diff --git a/src/dusk/asset/loader/CMakeLists.txt b/src/dusk/asset/loader/CMakeLists.txt index 30f97bc1..44bb11b1 100644 --- a/src/dusk/asset/loader/CMakeLists.txt +++ b/src/dusk/asset/loader/CMakeLists.txt @@ -15,5 +15,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} add_subdirectory(display) add_subdirectory(locale) add_subdirectory(json) -add_subdirectory(chunk) add_subdirectory(dmf) \ No newline at end of file diff --git a/src/dusk/asset/loader/assetloader.c b/src/dusk/asset/loader/assetloader.c index ab1ba41b..6ce9494b 100644 --- a/src/dusk/asset/loader/assetloader.c +++ b/src/dusk/asset/loader/assetloader.c @@ -45,10 +45,4 @@ assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = { .loadAsync = assetJsonLoaderAsync, .dispose = assetJsonDispose }, - - [ASSET_LOADER_TYPE_CHUNK] = { - .loadSync = assetChunkLoaderSync, - .loadAsync = assetChunkLoaderAsync, - .dispose = assetChunkDispose - }, }; diff --git a/src/dusk/asset/loader/assetloader.h b/src/dusk/asset/loader/assetloader.h index a9edd1b1..5edae5ff 100644 --- a/src/dusk/asset/loader/assetloader.h +++ b/src/dusk/asset/loader/assetloader.h @@ -12,7 +12,6 @@ #include "asset/loader/display/assettilesetloader.h" #include "asset/loader/locale/assetlocaleloader.h" #include "asset/loader/json/assetjsonloader.h" -#include "asset/loader/chunk/assetchunkloader.h" typedef enum { ASSET_LOADER_TYPE_NULL, @@ -23,7 +22,6 @@ typedef enum { ASSET_LOADER_TYPE_TILESET, ASSET_LOADER_TYPE_LOCALE, ASSET_LOADER_TYPE_JSON, - ASSET_LOADER_TYPE_CHUNK, ASSET_LOADER_TYPE_COUNT } assetloadertype_t; @@ -35,7 +33,6 @@ typedef union { assettilesetloaderloading_t tileset; assetlocaleloaderloading_t locale; assetjsonloaderloading_t json; - assetchunkloaderloading_t chunk; } assetloaderloading_t; typedef union { @@ -45,7 +42,6 @@ typedef union { assettilesetoutput_t tileset; assetlocaleoutput_t locale; assetjsonoutput_t json; - assetchunkoutput_t chunk; } assetloaderoutput_t; typedef union { @@ -53,7 +49,6 @@ typedef union { assettilesetloaderinput_t tileset; assetlocaleloaderinput_t locale; assetjsonloaderinput_t json; - assetchunkloaderinput_t chunk; } assetloaderinput_t; typedef struct assetloading_s assetloading_t; diff --git a/src/dusk/asset/loader/chunk/CMakeLists.txt b/src/dusk/asset/loader/chunk/CMakeLists.txt deleted file mode 100644 index 64e16290..00000000 --- a/src/dusk/asset/loader/chunk/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - assetchunkloader.c -) diff --git a/src/dusk/asset/loader/chunk/assetchunkloader.c b/src/dusk/asset/loader/chunk/assetchunkloader.c deleted file mode 100644 index 4046ef12..00000000 --- a/src/dusk/asset/loader/chunk/assetchunkloader.c +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetchunkloader.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "util/endian.h" -#include "asset/loader/assetloading.h" -#include "asset/loader/assetentry.h" -#include "asset/loader/assetloader.h" -#include "asset/asset.h" - -errorret_t assetChunkLoaderAsync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertNotMainThread("Should be called from an async thread."); - - if(loading->loading.chunk.state != ASSET_CHUNK_LOADING_STATE_READ_FILE) { - errorOk(); - } - - assertNull(loading->loading.chunk.data, "Data already defined?"); - - assetfile_t *file = &loading->loading.chunk.file; - assetLoaderErrorChain(loading, - assetFileInit(file, loading->entry->name, NULL, NULL) - ); - - uint8_t *data = memoryAllocate(file->size); - assetLoaderErrorChain(loading, assetFileOpen(file)); - assetLoaderErrorChain(loading, assetFileRead(file, data, file->size)); - assetLoaderErrorChain(loading, assetFileClose(file)); - assetLoaderErrorChain(loading, assetFileDispose(file)); - assertTrue( - file->lastRead == file->size, - "Failed to read entire chunk file." - ); - - loading->loading.chunk.data = data; - loading->loading.chunk.state = ASSET_CHUNK_LOADING_STATE_PARSE; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; - errorOk(); -} - -errorret_t assetChunkLoaderSync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertTrue(loading->type == ASSET_LOADER_TYPE_CHUNK, "Invalid type."); - assertIsMainThread("Must be called from the main thread."); - - assetchunkoutput_t *out = &loading->entry->data.chunk; - - switch(loading->loading.chunk.state) { - case ASSET_CHUNK_LOADING_STATE_INITIAL: - loading->loading.chunk.state = ASSET_CHUNK_LOADING_STATE_READ_FILE; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC; - errorOk(); - - case ASSET_CHUNK_LOADING_STATE_PARSE: - break; - - case ASSET_CHUNK_LOADING_STATE_LOAD_MODELS: - while(loading->loading.chunk.modelIndex < out->meshCount) { - uint8_t m = loading->loading.chunk.modelIndex; - if(out->modelEntries[m] == NULL) { - out->modelEntries[m] = assetLock( - out->modelNames[m], ASSET_LOADER_TYPE_MODEL, NULL - ); - assertNotNull( - out->modelEntries[m], "Failed to lock model." - ); - loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; - errorOk(); - } - if(out->modelEntries[m]->state == ASSET_ENTRY_STATE_ERROR) { - assetLoaderErrorThrow( - loading, "Model failed to load: %s", out->modelNames[m] - ); - } - if(out->modelEntries[m]->state != ASSET_ENTRY_STATE_LOADED) { - loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; - errorOk(); - } - loading->loading.chunk.modelIndex++; - } - loading->entry->state = ASSET_ENTRY_STATE_LOADED; - errorOk(); - - default: - errorOk(); - } - - uint8_t *data = loading->loading.chunk.data; - assertNotNull(data, "Chunk data should have been loaded by now."); - - if(data[0] != 'D' || data[1] != 'C' || data[2] != 'F') { - memoryFree(data); - assetLoaderErrorThrow(loading, "Invalid chunk file header"); - } - - uint32_t version = endianLittleToHost32(*(uint32_t *)(data + 4)); - if(version != ASSET_CHUNK_FILE_VERSION) { - memoryFree(data); - assetLoaderErrorThrow( - loading, "Unsupported chunk file version %u", version - ); - } - - size_t offset = 8; - - size_t tileSize = CHUNK_TILE_COUNT * sizeof(tile_t); - out->tiles = memoryAllocate(tileSize); - memoryCopy(out->tiles, data + offset, tileSize); - offset += tileSize; - - for(size_t t = 0; t < CHUNK_TILE_COUNT; t++) { - uint32_t *shape = (uint32_t *)&out->tiles[t].shape; - *shape = endianLittleToHost32(*shape); - } - - out->meshCount = data[offset]; - offset += sizeof(uint8_t); - assertTrue( - out->meshCount <= CHUNK_MESH_COUNT_MAX, - "Chunk mesh count exceeds maximum." - ); - - for(uint8_t m = 0; m < out->meshCount; m++) { - uint8_t nameLen = 0; - while( - data[offset + nameLen] != '\0' && - nameLen < CHUNK_MESH_NAME_MAX - 1 - ) { - nameLen++; - } - memoryCopy(out->modelNames[m], data + offset, nameLen); - out->modelNames[m][nameLen] = '\0'; - offset += nameLen + 1; - - memoryCopy(out->meshOffsets[m], data + offset, sizeof(vec3)); - offset += sizeof(vec3); - out->meshOffsets[m][0] = endianLittleToHostFloat(out->meshOffsets[m][0]); - out->meshOffsets[m][1] = endianLittleToHostFloat(out->meshOffsets[m][1]); - out->meshOffsets[m][2] = endianLittleToHostFloat(out->meshOffsets[m][2]); - } - - memoryFree(data); - loading->loading.chunk.data = NULL; - - if(out->meshCount == 0) { - loading->entry->state = ASSET_ENTRY_STATE_LOADED; - errorOk(); - } - - loading->loading.chunk.state = ASSET_CHUNK_LOADING_STATE_LOAD_MODELS; - loading->loading.chunk.modelIndex = 0; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; - errorOk(); -} - -errorret_t assetChunkDispose(assetentry_t *entry) { - assertNotNull(entry, "Entry cannot be NULL"); - assertTrue(entry->type == ASSET_LOADER_TYPE_CHUNK, "Invalid type."); - assertIsMainThread("Must be called from the main thread."); - - assetchunkoutput_t *out = &entry->data.chunk; - - if(out->tiles != NULL) { - memoryFree(out->tiles); - out->tiles = NULL; - } - - for(uint8_t m = 0; m < out->meshCount; m++) { - if(out->modelEntries[m] == NULL) continue; - assetUnlockEntry(out->modelEntries[m]); - out->modelEntries[m] = NULL; - } - errorOk(); -} diff --git a/src/dusk/asset/loader/chunk/assetchunkloader.h b/src/dusk/asset/loader/chunk/assetchunkloader.h deleted file mode 100644 index db591a7f..00000000 --- a/src/dusk/asset/loader/chunk/assetchunkloader.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "asset/assetfile.h" -#include "rpg/overworld/chunk.h" - -#define ASSET_CHUNK_FILE_VERSION 4 - -typedef struct assetloading_s assetloading_t; -typedef struct assetentry_s assetentry_t; - -typedef struct { - void *nothing; -} assetchunkloaderinput_t; - -typedef enum { - ASSET_CHUNK_LOADING_STATE_INITIAL, - ASSET_CHUNK_LOADING_STATE_READ_FILE, - ASSET_CHUNK_LOADING_STATE_PARSE, - ASSET_CHUNK_LOADING_STATE_LOAD_MODELS, - ASSET_CHUNK_LOADING_STATE_DONE -} assetchunkloadingstate_t; - -typedef struct { - assetfile_t file; - assetchunkloadingstate_t state; - uint8_t *data; - uint8_t modelIndex; -} assetchunkloaderloading_t; - -typedef struct { - tile_t *tiles; - uint8_t meshCount; - char_t modelNames[CHUNK_MESH_COUNT_MAX][CHUNK_MESH_NAME_MAX]; - vec3 meshOffsets[CHUNK_MESH_COUNT_MAX]; - assetentry_t *modelEntries[CHUNK_MESH_COUNT_MAX]; -} assetchunkoutput_t; - -/** - * Asynchronous loader for chunk assets. Reads the raw DCF file bytes into - * the loading buffer so the sync phase can parse without blocking the - * main thread on I/O. - * - * @param loading Loading information for the asset being loaded. - * @return Error code indicating success or failure of the load operation. - */ -errorret_t assetChunkLoaderAsync(assetloading_t *loading); - -/** - * Synchronous loader for chunk assets. Validates the DCF binary previously - * read by the async phase and populates the output assetchunkoutput_t with - * tile data and model paths. - * - * @param loading Loading information for the asset being loaded. - * @return Error code indicating success or failure of the load operation. - */ -errorret_t assetChunkLoaderSync(assetloading_t *loading); - -/** - * Disposer for chunk assets. - * - * @param entry Asset entry containing the chunk data to dispose. - * @return Error code indicating success or failure of the dispose operation. - */ -errorret_t assetChunkDispose(assetentry_t *entry); diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index d3a32567..fdc786d2 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -10,7 +10,6 @@ #include "time/time.h" #include "input/input.h" #include "locale/localemanager.h" -#include "rpg/rpg.h" #include "display/display.h" #include "scene/scene.h" #include "asset/asset.h" @@ -44,7 +43,6 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { errorChain(localeManagerInit()); errorChain(displayInit()); errorChain(uiInit()); - errorChain(rpgInit()); errorChain(networkInit()); errorChain(sceneInit()); @@ -63,7 +61,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { consolePrint("Assertions real"); #endif - sceneSet(SCENE_TYPE_OVERWORLD); + // sceneSet(SCENE_TYPE_OVERWORLD); errorOk(); } @@ -85,7 +83,6 @@ errorret_t engineUpdate(void) { if(dialogType == SYSTEM_DIALOG_TYPE_NONE) { inputUpdate(); consoleUpdate(); - errorChain(rpgUpdate()); errorChain(sceneUpdate()); errorChain(assetUpdate()); errorChain(uiUpdate()); @@ -109,7 +106,6 @@ void engineExit(void) { errorret_t engineDispose(void) { errorChain(sceneDispose()); errorChain(networkDispose()); - errorChain(rpgDispose()); localeManagerDispose(); errorChain(uiDispose()); consoleDispose(); diff --git a/src/dusk/scene/CMakeLists.txt b/src/dusk/scene/CMakeLists.txt index 9444af7b..b5764583 100644 --- a/src/dusk/scene/CMakeLists.txt +++ b/src/dusk/scene/CMakeLists.txt @@ -6,8 +6,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC scene.c - scenetype.c -) - -# Subdirs -add_subdirectory(overworld) \ No newline at end of file +) \ No newline at end of file diff --git a/src/dusk/scene/overworld/CMakeLists.txt b/src/dusk/scene/overworld/CMakeLists.txt deleted file mode 100644 index 6038dc07..00000000 --- a/src/dusk/scene/overworld/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - sceneoverworld.c -) \ No newline at end of file diff --git a/src/dusk/scene/overworld/sceneoverworld.c b/src/dusk/scene/overworld/sceneoverworld.c deleted file mode 100644 index 4e42a149..00000000 --- a/src/dusk/scene/overworld/sceneoverworld.c +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "scene/scene.h" -#include "console/console.h" -#include "assert/assert.h" - -#include "display/display.h" -#include "display/displaystate.h" -#include "display/shader/shader.h" -#include "display/screen/screen.h" -#include "display/shader/shaderunlit.h" -#include "display/spritebatch/spritebatch.h" -#include "display/texture/texture.h" - -#include "rpg/overworld/map.h" -#include "rpg/entity/entity.h" -#include "rpg/rpgcamera.h" - -#include "asset/loader/assetloader.h" -#include "asset/loader/assetentry.h" - -#include "util/memory.h" - -errorret_t sceneOverworldInit(scenedata_t *sceneData) { - assertNotNull(sceneData, "Scene data cannot be null"); - errorOk(); -} - -errorret_t sceneOverworldUpdate(scenedata_t *sceneData) { - assertNotNull(sceneData, "Scene data cannot be null"); - errorOk(); -} - -errorret_t sceneOverworldRender(scenedata_t *sceneData) { - assertNotNull(sceneData, "Scene data cannot be null"); - - sceneoverworld_t *overworld = &sceneData->overworld; - sceneOverworldCullUpdate(overworld); - - - // Overworld camera - errorChain(shaderBind(&SHADER_UNLIT)); - errorChain(shaderSetMatrix( - &SHADER_UNLIT, SHADER_UNLIT_MODEL, SCENE.screenIdentity - )); - - rpgCameraUpdateProjection(); - errorChain(shaderSetMatrix( - &SHADER_UNLIT, SHADER_UNLIT_PROJECTION, RPG_CAMERA.projection - )); - - rpgCameraUpdateEye(); - errorChain(shaderSetMatrix( - &SHADER_UNLIT, SHADER_UNLIT_VIEW, RPG_CAMERA.eye - )); - - // Chunk tiles - errorChain(displaySetState((displaystate_t){ - .flags = DISPLAY_STATE_FLAG_DEPTH_TEST | DISPLAY_STATE_FLAG_CULL - })); - errorChain(sceneOverworldDrawChunksBase(overworld)); - - // Entities - errorChain(displaySetState((displaystate_t){ - .flags = DISPLAY_STATE_FLAG_CULL - })); - - for(uint8_t i = 0; i < ENTITY_COUNT; i++) { - entity_t *ent = &ENTITIES[i]; - if(ent->type == ENTITY_TYPE_NULL) continue; - errorChain(sceneOverworldDrawEntity(overworld, ent)); - } - - // Chunk props - errorChain(displaySetState((displaystate_t){ - .flags = DISPLAY_STATE_FLAG_DEPTH_TEST | DISPLAY_STATE_FLAG_CULL - })); - errorChain(sceneOverworldDrawChunksProps(overworld)); - - // Weather effects - // errorChain(shaderSetMatrix( - // &SHADER_UNLIT, SHADER_UNLIT_PROJECTION, SCENE.screenProj - // )); - // errorChain(shaderSetMatrix( - // &SHADER_UNLIT, SHADER_UNLIT_VIEW, SCENE.screenView - // )); - // errorChain(displaySetState((displaystate_t){ - // .flags = DISPLAY_STATE_FLAG_BLEND - // })); - - // spritebatchsprite_t skyboxSprite; - // memoryZero(&skyboxSprite, sizeof(spritebatchsprite_t)); - // skyboxSprite.max[0] = (float_t)(SCREEN.width / SCREEN.scaleUi); - // skyboxSprite.max[1] = (float_t)(SCREEN.height / SCREEN.scaleUi); - // shadermaterial_t skyboxMaterial = { - // .unlit = { .color = color(0xFF, 0x00, 0x00, 0xFF/2), .texture = NULL } - // }; - // errorChain(spriteBatchBuffer(&skyboxSprite, 1, &SHADER_UNLIT, skyboxMaterial)); - // errorChain(spriteBatchFlush()); - - errorOk(); -} - -void sceneOverworldCullUpdate(sceneoverworld_t *overworld) { - rpgCameraGetPosition(overworld->cullTarget); - - const float_t worldH = - (float_t)(SCREEN.height / SCREEN.scale3d) / TILE_SIZE_PIXELS; - const float_t worldW = worldH * SCREEN.aspect; - - overworld->cullHalfRangeX = - (worldW * 0.5f) + SCENE_OVERWORLD_CHUNK_CULL_SKIN_X; - overworld->cullHalfRangeY = - (worldH * 0.5f) + SCENE_OVERWORLD_CHUNK_CULL_SKIN_Y; -} - -bool_t sceneOverworldChunkShouldRender( - const sceneoverworld_t *overworld, - const chunk_t *chunk -) { - worldpos_t worldPos; - chunkPosToWorldPos(&chunk->position, &worldPos); - - const float_t chunkCenterX = (float_t)worldPos.x + (CHUNK_WIDTH * 0.5f); - const float_t chunkCenterY = (float_t)worldPos.y + (CHUNK_HEIGHT * 0.5f); - - if(fabsf(overworld->cullTarget[0] - chunkCenterX) > - overworld->cullHalfRangeX) return false; - if(fabsf(overworld->cullTarget[1] - chunkCenterY) > - overworld->cullHalfRangeY) return false; - - return true; -} - -bool_t sceneOverworldEntityShouldRender( - const sceneoverworld_t *overworld, - const entity_t *ent -) { - if(fabsf(overworld->cullTarget[0] - ent->renderPosition[0]) > - overworld->cullHalfRangeX) return false; - if(fabsf(overworld->cullTarget[1] - ent->renderPosition[1]) > - overworld->cullHalfRangeY) return false; - - return true; -} - -errorret_t sceneOverworldDrawEntity( - const sceneoverworld_t *overworld, - entity_t *ent -) { - if(!sceneOverworldEntityShouldRender(overworld, ent)) errorOk(); - - spritebatchsprite_t sprite; - glm_vec3_copy(ent->renderPosition, sprite.min); - glm_vec3_add(sprite.min, (vec3){ 0, -0.05f, 0.05f }, sprite.min);// Stop Fight - glm_vec3_copy(sprite.min, sprite.max); - glm_vec3_add(sprite.max, (vec3){ 1, 1, 0 }, sprite.max); - glm_vec2_copy((vec2){ 0, 0 }, sprite.uvMin); - glm_vec2_copy((vec2){ 1, 1 }, sprite.uvMax); - - color_t color; - switch(ent->direction) { - case ENTITY_DIR_NORTH: color = COLOR_YELLOW; break; - case ENTITY_DIR_EAST: color = COLOR_RED; break; - case ENTITY_DIR_SOUTH: color = COLOR_GREEN; break; - case ENTITY_DIR_WEST: color = COLOR_BLUE; break; - default: color = COLOR_CYAN; break; - } - - shadermaterial_t material = { - .unlit = { .color = color, .texture = NULL } - }; - errorChain(spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, material)); - errorChain(spriteBatchFlush()); - - errorOk(); -} - -errorret_t sceneOverworldDrawChunksBase(const sceneoverworld_t *overworld) { - for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) { - chunk_t *chunk = MAP.chunkOrder[i]; - if(chunk == NULL) continue; - if(!sceneOverworldChunkShouldRender(overworld, chunk)) continue; - if(chunk->meshCount == 0) continue; - if(chunk->modelEntries[0] == NULL) continue; - if(chunk->modelEntries[0]->state != ASSET_ENTRY_STATE_LOADED) continue; - - assetmodeloutput_t *model = &chunk->modelEntries[0]->data.model; - if(model->meshEntry == NULL) continue; - - texture_t *tex = model->texEntry != NULL - ? &model->texEntry->data.texture - : NULL; - - shadermaterial_t mat = { - .unlit = { .color = model->color, .texture = tex } - }; - errorChain(shaderSetMatrix( - &SHADER_UNLIT, SHADER_UNLIT_MODEL, chunk->meshModels[0] - )); - errorChain(shaderSetMaterial(&SHADER_UNLIT, &mat)); - errorChain(meshDraw( - &model->meshEntry->data.mesh.mesh, 0, -1 - )); - } - - // Restore identity model so subsequent renders (e.g. entities) are - // not affected by the last chunk transform. - mat4 identity; - glm_mat4_identity(identity); - errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, identity)); - - errorOk(); -} - -errorret_t sceneOverworldDrawChunksProps(const sceneoverworld_t *overworld) { - for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) { - chunk_t *chunk = MAP.chunkOrder[i]; - if(chunk == NULL) continue; - if(!sceneOverworldChunkShouldRender(overworld, chunk)) continue; - - for(uint8_t m = 1; m < chunk->meshCount; m++) { - if(chunk->modelEntries[m] == NULL) continue; - if(chunk->modelEntries[m]->state != ASSET_ENTRY_STATE_LOADED) continue; - - assetmodeloutput_t *model = &chunk->modelEntries[m]->data.model; - if(model->meshEntry == NULL) continue; - - texture_t *tex = model->texEntry != NULL - ? &model->texEntry->data.texture - : NULL; - - shadermaterial_t mat = { - .unlit = { .color = model->color, .texture = tex } - }; - errorChain(shaderSetMatrix( - &SHADER_UNLIT, SHADER_UNLIT_MODEL, chunk->meshModels[m] - )); - errorChain(shaderSetMaterial(&SHADER_UNLIT, &mat)); - errorChain(meshDraw( - &model->meshEntry->data.mesh.mesh, 0, -1 - )); - } - } - - // Restore identity model in case anything renders after props. - mat4 identity; - glm_mat4_identity(identity); - errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, identity)); - - errorOk(); -} - -errorret_t sceneOverworldDispose(scenedata_t *sceneData) { - assertNotNull(sceneData, "Scene data cannot be null"); - errorOk(); -} diff --git a/src/dusk/scene/overworld/sceneoverworld.h b/src/dusk/scene/overworld/sceneoverworld.h deleted file mode 100644 index 048e20d0..00000000 --- a/src/dusk/scene/overworld/sceneoverworld.h +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "scene/scenebase.h" -#include "rpg/overworld/chunk.h" -#include "rpg/entity/entity.h" - -typedef struct { - // Cached per-frame chunk culling bounds, refreshed by - // sceneOverworldCullUpdate() so sceneOverworldChunkShouldRender() - // does not need to recompute them for every chunk. - vec3 cullTarget; - float_t cullHalfRangeX; - float_t cullHalfRangeY; -} sceneoverworld_t; - -// Extra world-space margin, in tiles, added on top of the rough -// screen-size skin below - cheap insurance against the approximation -// culling a chunk that is actually still just in view. Y gets its own -// value since the camera's tilt makes the vertical check less exact. -#define SCENE_OVERWORLD_CHUNK_CULL_PADDING_X 8.0f -#define SCENE_OVERWORLD_CHUNK_CULL_PADDING_Y 4.0f - -// The camera is tilted between the ground-depth (Y) and height (Z) -// axes, so a chunk's on-screen vertical extent also depends on its Z -// layers. Rather than projecting every corner, fold that into a -// generous skin margin on the depth check. -#define SCENE_OVERWORLD_CHUNK_CULL_SKIN_X \ - ((CHUNK_WIDTH * 0.5f) + SCENE_OVERWORLD_CHUNK_CULL_PADDING_X) -#define SCENE_OVERWORLD_CHUNK_CULL_SKIN_Y \ - ((CHUNK_HEIGHT * 0.5f) + (CHUNK_DEPTH * WORLD_LAYER_HEIGHT) + \ - SCENE_OVERWORLD_CHUNK_CULL_PADDING_Y) - -/** - * Initialises the overworld scene. - * - * @param sceneData The scene data used for this scene. - * @return An error if the init failed, or errorOk() if it succeeded. - */ -errorret_t sceneOverworldInit(scenedata_t *sceneData); - -/** - * Updates the overworld scene. - * - * @param sceneData The scene data used for this scene. - * @return An error if the update failed, or errorOk() if it succeeded. - */ -errorret_t sceneOverworldUpdate(scenedata_t *sceneData); - -/** - * Refreshes the chunk culling bounds cached on the overworld scene data, - * from the current camera position and screen dimensions. Must be - * called once per frame before sceneOverworldChunkShouldRender(). - * - * @param overworld The overworld scene data to update. - */ -void sceneOverworldCullUpdate(sceneoverworld_t *overworld); - -/** - * Roughly checks whether a chunk falls within the visible screen area, - * based on chunk size and screen dimensions. Cheap approximation, not - * an exact frustum check - includes a generous skin margin to account - * for the camera's angle tilting height/depth onto the screen's - * vertical axis, so it may pass some chunks that are actually just out - * of view but will never wrongly cull one that is visible. - * - * @param overworld The overworld scene data holding the cached culling - * bounds, as refreshed by sceneOverworldCullUpdate(). - * @param chunk The chunk to check. - * @return true if the chunk should be rendered, false otherwise. - */ -bool_t sceneOverworldChunkShouldRender( - const sceneoverworld_t *overworld, - const chunk_t *chunk -); - -/** - * Draws the base (tile) mesh of every loaded chunk, with normal depth - * testing. Must be called before entities are rendered. - * - * @param overworld The overworld scene data holding the cached culling - * bounds, as refreshed by sceneOverworldCullUpdate(). - * @return An error if drawing failed, or errorOk() on success. - */ -errorret_t sceneOverworldDrawChunksBase(const sceneoverworld_t *overworld); - -/** - * Draws every loaded chunk's additional meshes (trees, buildings, etc), - * with normal depth testing so they correctly occlude entities standing - * beneath them. Must be called after entities are rendered. - * - * @param overworld The overworld scene data holding the cached culling - * bounds, as refreshed by sceneOverworldCullUpdate(). - * @return An error if drawing failed, or errorOk() on success. - */ -errorret_t sceneOverworldDrawChunksProps(const sceneoverworld_t *overworld); - -/** - * Roughly checks whether an entity falls within the visible screen - * area. Reuses the same cached chunk culling bounds, since an entity - * is just a point within that same space - deliberately cheap, a - * couple of subtractions and comparisons, since it runs per entity - * every frame and it is not worth spending more math than that to - * avoid drawing one that is slightly off-screen. - * - * @param overworld The overworld scene data holding the cached culling - * bounds, as refreshed by sceneOverworldCullUpdate(). - * @param ent The entity to check. - * @return true if the entity should be rendered, false otherwise. - */ -bool_t sceneOverworldEntityShouldRender( - const sceneoverworld_t *overworld, - const entity_t *ent -); - -/** - * Draws a single entity as a sprite, colored by its facing direction. - * Skips drawing (without error) if the entity should not render. - * - * @param overworld The overworld scene data holding the cached culling - * bounds, as refreshed by sceneOverworldCullUpdate(). - * @param ent The entity to draw. - * @return An error if drawing failed, or errorOk() on success. - */ -errorret_t sceneOverworldDrawEntity( - const sceneoverworld_t *overworld, - entity_t *ent -); - -/** - * Renders the overworld scene. - * - * @param sceneData The scene data used for this scene. - * @return An error if the render failed, or errorOk() if it succeeded. - */ -errorret_t sceneOverworldRender(scenedata_t *sceneData); - -/** - * Disposes the overworld scene. - * - * @param sceneData The scene data used for this scene. - * @return An error if the dispose failed, or errorOk() if it succeeded. - */ -errorret_t sceneOverworldDispose(scenedata_t *sceneData); \ No newline at end of file diff --git a/src/dusk/scene/scene.c b/src/dusk/scene/scene.c index 72c4e9d9..24d02416 100644 --- a/src/dusk/scene/scene.c +++ b/src/dusk/scene/scene.c @@ -23,98 +23,13 @@ errorret_t sceneInit(void) { } errorret_t sceneUpdate(void) { - // Handle scene change. - if(SCENE.next != SCENE_TYPE_NULL) { - if( - SCENE.current != SCENE_TYPE_NULL && - SCENE_TYPES[SCENE.current].dispose != NULL - ) { - errorChain(SCENE_TYPES[SCENE.current].dispose(&SCENE.data)); - } - - SCENE.current = SCENE.next; - SCENE.next = SCENE_TYPE_NULL; - - if( - SCENE.current != SCENE_TYPE_NULL && - SCENE_TYPES[SCENE.current].init != NULL - ) { - errorChain(SCENE_TYPES[SCENE.current].init(&SCENE.data)); - } - } - - #if DUSK_TIME_DYNAMIC - if(TIME.dynamicUpdate) errorOk(); - #endif - - if( - SCENE.current != SCENE_TYPE_NULL && - SCENE_TYPES[SCENE.current].update != NULL - ) { - errorChain(SCENE_TYPES[SCENE.current].update(&SCENE.data)); - } - errorOk(); } errorret_t sceneRender(void) { - // Setup screen matrices for 3D rendering. - glm_mat4_identity(SCENE.screenIdentity); - - glm_ortho( - 0.0f, (float_t)(SCREEN.width / SCREEN.scaleUi), - (float_t)(SCREEN.height / SCREEN.scaleUi), 0.0f, - 0.1f, 100.0f, - SCENE.screenProj - ); - - glm_lookat( - (vec3){ 0.0f, 0.0f, 1.0f }, - (vec3){ 0.0f, 0.0f, 0.0f }, - (vec3){ 0.0f, 1.0f, 0.0f }, - SCENE.screenView - ); - - // Scene rendering - if( - SCENE.current != SCENE_TYPE_NULL && - SCENE_TYPES[SCENE.current].render != NULL - ) { - errorChain(SCENE_TYPES[SCENE.current].render(&SCENE.data)); - } - - // UI Rendering - errorChain(shaderBind(&SHADER_UNLIT)); - errorChain(shaderSetMatrix( - &SHADER_UNLIT, SHADER_UNLIT_MODEL, SCENE.screenIdentity - )); - errorChain(shaderSetMatrix( - &SHADER_UNLIT, SHADER_UNLIT_PROJECTION, SCENE.screenProj - )); - errorChain(shaderSetMatrix( - &SHADER_UNLIT, SHADER_UNLIT_VIEW, SCENE.screenView - )); - - errorChain(displaySetState((displaystate_t){ - .flags = DISPLAY_STATE_FLAG_BLEND - })); - errorChain(uiRender()); - errorOk(); } -void sceneSet(const scenetype_t type) { - assertTrue(type < SCENE_TYPE_COUNT, "Invalid scene type."); - SCENE.next = type; -} - errorret_t sceneDispose(void) { - if( - SCENE.current != SCENE_TYPE_NULL && - SCENE_TYPES[SCENE.current].dispose != NULL - ) { - errorChain(SCENE_TYPES[SCENE.current].dispose(&SCENE.data)); - } - errorOk(); } diff --git a/src/dusk/scene/scene.h b/src/dusk/scene/scene.h index db138699..8673bed4 100644 --- a/src/dusk/scene/scene.h +++ b/src/dusk/scene/scene.h @@ -6,16 +6,10 @@ */ #pragma once -#include "dusk.h" -#include "scenetype.h" +#include "error/error.h" typedef struct { - scenetype_t current; - scenetype_t next; - scenedata_t data; - mat4 screenProj; - mat4 screenView; - mat4 screenIdentity; + void *nothing; } scene_t; extern scene_t SCENE; @@ -47,7 +41,7 @@ errorret_t sceneRender(void); * * @param type The scene type to change to. */ -void sceneSet(const scenetype_t type); +// void sceneSet(const scenetype_t type); /** * Disposes the active scene immediately. diff --git a/src/dusk/scene/scenebase.h b/src/dusk/scene/scenebase.h deleted file mode 100644 index 7846a9cc..00000000 --- a/src/dusk/scene/scenebase.h +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" - -typedef union scenedata_u scenedata_t; \ No newline at end of file diff --git a/src/dusk/scene/scenetype.c b/src/dusk/scene/scenetype.c deleted file mode 100644 index e2e76d7e..00000000 --- a/src/dusk/scene/scenetype.c +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "scenetype.h" - -scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT] = { - [SCENE_TYPE_NULL] = { 0 }, - - [SCENE_TYPE_OVERWORLD] = { - .init = sceneOverworldInit, - .update = sceneOverworldUpdate, - .render = sceneOverworldRender, - .dispose = sceneOverworldDispose - }, -}; - diff --git a/src/dusk/scene/scenetype.h b/src/dusk/scene/scenetype.h deleted file mode 100644 index 9e981678..00000000 --- a/src/dusk/scene/scenetype.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "scene/scenebase.h" -#include "scene/overworld/sceneoverworld.h" - -typedef union scenedata_u { - sceneoverworld_t overworld; -} scenedata_t; - -typedef errorret_t (*scenecallback_t)(scenedata_t *); - -typedef struct { - scenecallback_t init; - scenecallback_t update; - scenecallback_t render; - scenecallback_t dispose; -} scenecallbacks_t; - -typedef enum { - SCENE_TYPE_NULL, - - SCENE_TYPE_OVERWORLD, - - SCENE_TYPE_COUNT -} scenetype_t; - -extern scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT]; \ No newline at end of file diff --git a/src/dusk/ui/CMakeLists.txt b/src/dusk/ui/CMakeLists.txt index 26754f0d..d83ddcfc 100644 --- a/src/dusk/ui/CMakeLists.txt +++ b/src/dusk/ui/CMakeLists.txt @@ -7,7 +7,6 @@ add_subdirectory(debug) add_subdirectory(frame) add_subdirectory(focus) add_subdirectory(overlay) -add_subdirectory(rpg) add_subdirectory(transition) add_subdirectory(widget) diff --git a/src/dusk/ui/debug/CMakeLists.txt b/src/dusk/ui/debug/CMakeLists.txt index 74e31662..9d377628 100644 --- a/src/dusk/ui/debug/CMakeLists.txt +++ b/src/dusk/ui/debug/CMakeLists.txt @@ -7,5 +7,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC uiconsole.c uifps.c - uiplayerpos.c ) diff --git a/src/dusk/ui/debug/uiplayerpos.c b/src/dusk/ui/debug/uiplayerpos.c deleted file mode 100644 index a3b9f2d5..00000000 --- a/src/dusk/ui/debug/uiplayerpos.c +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "uiplayerpos.h" -#include "display/screen/screen.h" -#include "display/text/text.h" -#include "rpg/entity/entity.h" -#include "rpg/entity/entitytype.h" -#include "rpg/overworld/worldpos.h" - -errorret_t uiPlayerPosDraw() { - entity_t *player = NULL; - for(uint8_t i = 0; i < ENTITY_COUNT; i++) { - if(ENTITIES[i].type == ENTITY_TYPE_PLAYER) { - player = &ENTITIES[i]; - break; - } - } - if(!player) errorOk(); - - chunkpos_t chunkPos; - worldPosToChunkPos(&player->position, &chunkPos); - - char_t text[64]; - snprintf( - text, - sizeof(text), - "%d,%d,%d[%d,%d,%d]", - (int_t)player->position.x, - (int_t)player->position.y, - (int_t)player->position.z, - (int_t)chunkPos.x, - (int_t)chunkPos.y, - (int_t)chunkPos.z - ); - - float_t y = (float_t)SCREEN.scanY + - (float_t)FONT_DEFAULT.tileset->tileHeight; - errorChain(textDraw( - (float_t)SCREEN.scanX, y, text, COLOR_GREEN, &FONT_DEFAULT - )); - - char_t velocityText[64]; - snprintf( - velocityText, - sizeof(velocityText), - "vel %.2f,%.2f,%.2f", - player->body.velocity[0], - player->body.velocity[1], - player->body.velocity[2] - ); - - float_t velocityY = y + (float_t)FONT_DEFAULT.tileset->tileHeight; - errorChain(textDraw( - (float_t)SCREEN.scanX, velocityY, velocityText, COLOR_GREEN, &FONT_DEFAULT - )); - - errorOk(); -} diff --git a/src/dusk/ui/debug/uiplayerpos.h b/src/dusk/ui/debug/uiplayerpos.h deleted file mode 100644 index 88cde124..00000000 --- a/src/dusk/ui/debug/uiplayerpos.h +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" - -/** - * Draws the player world and chunk position on screen. - * Searches for the first ENTITY_TYPE_PLAYER and renders: - * WORLDX,WORLDY,WORLDZ[chunkx,chunky,chunkz] - * - * @return Any error that occurs. - */ -errorret_t uiPlayerPosDraw(); diff --git a/src/dusk/ui/frame/CMakeLists.txt b/src/dusk/ui/frame/CMakeLists.txt index fde94df5..dd6ef9f9 100644 --- a/src/dusk/ui/frame/CMakeLists.txt +++ b/src/dusk/ui/frame/CMakeLists.txt @@ -9,6 +9,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} uiconfirm.c ) -add_subdirectory(game) -add_subdirectory(settings) -add_subdirectory(backpack) +add_subdirectory(settings) \ No newline at end of file diff --git a/src/dusk/ui/frame/backpack/CMakeLists.txt b/src/dusk/ui/frame/backpack/CMakeLists.txt deleted file mode 100644 index 570abb53..00000000 --- a/src/dusk/ui/frame/backpack/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - uibackpack.c -) diff --git a/src/dusk/ui/frame/backpack/uibackpack.c b/src/dusk/ui/frame/backpack/uibackpack.c deleted file mode 100644 index 16a8f0d1..00000000 --- a/src/dusk/ui/frame/backpack/uibackpack.c +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "uibackpack.h" -#include "ui/frame/uiframe.h" -#include "rpg/item/backpack.h" -#include "util/memory.h" -#include "util/string.h" -#include "display/screen/screen.h" -#include "display/text/text.h" -#include "assert/assert.h" - -uibackpack_t UI_BACKPACK; - -void uiBackpackTabChanged( - const uimenu_t *menu, - const uint8_t index, - const uimenuitem_t *item -) { - const itemtype_t type = (itemtype_t)(index + 1); - const inventory_t *inventory = backpackGetInventory(type); - - errorCatch(uiItemListSetItemStacks( - &UI_BACKPACK.itemList, inventory->storage, inventory->storageSize - )); -} - -void uiBackpackTabSelected( - const uimenu_t *menu, - const uint8_t index, - const uimenuitem_t *item -) { - uiItemListOpen(&UI_BACKPACK.itemList); -} - -errorret_t uiBackpackInit(void) { - memoryZero(&UI_BACKPACK, sizeof(uibackpack_t)); - - MENU_BEGIN( - &UI_BACKPACK.tabsMenu, UI_BACKPACK.tabs, - uiBackpackTabSelected, NULL, uiBackpackTabChanged - ); - for(uint8_t i = 0; i < UI_BACKPACK_TAB_COUNT; i++) { - stringFormat( - UI_BACKPACK.tabLabels[i], UI_BACKPACK_TAB_LABEL_MAX - 1, - "Category %u", i + 1 - ); - MENU_TAB(UI_BACKPACK.tabLabels[i]); - } - MENU_END(UI_BACKPACK.tabs, menuIndex); - - uiItemListInit( - &UI_BACKPACK.itemList, - UI_BACKPACK_ITEM_LIST_COLUMNS, UI_BACKPACK_ITEM_LIST_ROWS, 1, - NULL, NULL, NULL, NULL - ); - - errorOk(); -} - -errorret_t uiBackpackDraw(void) { - if(!uiMenuIsActive(&UI_BACKPACK.tabsMenu)) errorOk(); - - const float_t width = SCREEN.width; - const float_t height = SCREEN.height; - const float_t x = (float_t)SCREEN.scanX + - ((float_t)SCREEN.scanWidth - width) * 0.5f; - const float_t y = (float_t)SCREEN.scanY + - ((float_t)SCREEN.scanHeight - height) * 0.5f; - - errorChain(uiFrameDraw(x, y, width, height)); - - const float_t contentX = x + UI_FRAME_START_X; - const float_t contentY = y + UI_FRAME_START_Y; - const float_t contentWidth = width - (UI_FRAME_START_X * 2); - const float_t contentHeight = height - (UI_FRAME_START_Y * 2); - - errorChain(uiMenuDraw( - &UI_BACKPACK.tabsMenu, - contentX, - contentY, - contentWidth, - contentHeight - )); - - const float_t tabsRowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight; - const float_t listY = contentY + tabsRowHeight + UI_FRAME_PADDING_Y; - - errorChain( - uiItemListDraw(&UI_BACKPACK.itemList, contentX, listY, contentWidth) - ); - - errorOk(); -} - -bool_t uiBackpackIsOpen(void) { - return uiMenuIsActive(&UI_BACKPACK.tabsMenu); -} - -void uiBackpackOpen(void) { - uiMenuOpen(&UI_BACKPACK.tabsMenu); -} - -void uiBackpackClose(void) { - uiMenuClose(&UI_BACKPACK.tabsMenu); -} - -errorret_t uiBackpackDispose(void) { - errorOk(); -} diff --git a/src/dusk/ui/frame/backpack/uibackpack.h b/src/dusk/ui/frame/backpack/uibackpack.h deleted file mode 100644 index bbd56ee0..00000000 --- a/src/dusk/ui/frame/backpack/uibackpack.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" -#include "ui/widget/uimenu.h" -#include "ui/widget/uiitemlist.h" -#include "rpg/item/item.h" - -#define UI_BACKPACK_TAB_COUNT (ITEM_TYPE_COUNT - 1) -#define UI_BACKPACK_TAB_LABEL_MAX 32 -#define UI_BACKPACK_ITEM_LIST_COLUMNS 4 -#define UI_BACKPACK_ITEM_LIST_ROWS 5 - -typedef struct { - uimenu_t tabsMenu; - uimenuitem_t tabs[UI_BACKPACK_TAB_COUNT]; - char_t tabLabels[UI_BACKPACK_TAB_COUNT][UI_BACKPACK_TAB_LABEL_MAX]; - - uiitemlist_t itemList; -} uibackpack_t; - -extern uibackpack_t UI_BACKPACK; - -/** - * Initializes the backpack panel and its item type tabs. - * - * @return Any error that occurs. - */ -errorret_t uiBackpackInit(void); - -/** - * Draws the backpack panel. No-op when not visible. - * - * @return Any error that occurs. - */ -errorret_t uiBackpackDraw(void); - -/** - * Returns true when the backpack panel is currently open. - * - * @returns True if open. - */ -bool_t uiBackpackIsOpen(void); - -/** - * Opens the backpack panel. No-op when already open. - */ -void uiBackpackOpen(void); - -/** - * Closes the backpack panel. No-op when already closed. - */ -void uiBackpackClose(void); - -/** - * Disposes of the backpack panel. - * - * @return Any error that occurs. - */ -errorret_t uiBackpackDispose(void); diff --git a/src/dusk/ui/frame/game/CMakeLists.txt b/src/dusk/ui/frame/game/CMakeLists.txt deleted file mode 100644 index 4b79eda6..00000000 --- a/src/dusk/ui/frame/game/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - uigamemenu.c -) diff --git a/src/dusk/ui/frame/game/uigamemenu.c b/src/dusk/ui/frame/game/uigamemenu.c deleted file mode 100644 index d1a531ca..00000000 --- a/src/dusk/ui/frame/game/uigamemenu.c +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "uigamemenu.h" -#include "ui/frame/uiframe.h" -#include "ui/frame/settings/uisettings.h" -#include "ui/frame/backpack/uibackpack.h" -#include "util/memory.h" -#include "display/screen/screen.h" -#include "assert/assert.h" -#include "locale/localemanager.h" -#include "asset/loader/locale/assetlocaleloader.h" - -#define UI_GAME_MENU_INDEX_CHARACTERS 0 -#define UI_GAME_MENU_INDEX_ITEMS 1 -#define UI_GAME_MENU_INDEX_SETTINGS 2 - -uigamemenu_t UI_GAME_MENU; - -void uiGameMenuSelected( - const uimenu_t *menu, - const uint8_t index, - const uimenuitem_t *item -) { - if(index == UI_GAME_MENU_INDEX_ITEMS) uiBackpackOpen(); - if(index == UI_GAME_MENU_INDEX_SETTINGS) uiSettingsOpen(); -} - -errorret_t uiGameMenuInit(void) { - memoryZero(&UI_GAME_MENU, sizeof(uigamemenu_t)); - - errorChain(assetLocaleGetString( - &LOCALE.entry->data.locale, - "ui.game_menu.characters", - 0, - UI_GAME_MENU.charactersLabel, - UI_GAME_MENU_LABEL_MAX - )); - errorChain(assetLocaleGetString( - &LOCALE.entry->data.locale, - "ui.game_menu.items", - 0, - UI_GAME_MENU.itemsLabel, - UI_GAME_MENU_LABEL_MAX - )); - errorChain(assetLocaleGetString( - &LOCALE.entry->data.locale, - "ui.game_menu.settings", - 0, - UI_GAME_MENU.settingsLabel, - UI_GAME_MENU_LABEL_MAX - )); - - MENU_BEGIN( - &UI_GAME_MENU.menu, UI_GAME_MENU.items, uiGameMenuSelected, NULL, NULL - ); - MENU_BUTTON(UI_GAME_MENU.charactersLabel); - MENU_BUTTON(UI_GAME_MENU.itemsLabel); - MENU_BUTTON(UI_GAME_MENU.settingsLabel); - - MENU_END(UI_GAME_MENU.items, 1); - - errorOk(); -} - -errorret_t uiGameMenuDraw(void) { - if(!uiMenuIsActive(&UI_GAME_MENU.menu)) errorOk(); - - const float_t width = UI_GAME_MENU_WIDTH; - const float_t height = (float_t)SCREEN.scanHeight; - const float_t x = (float_t)(SCREEN.scanX + SCREEN.scanWidth) - width; - const float_t y = (float_t)SCREEN.scanY; - - errorChain(uiFrameDraw(x, y, width, height)); - errorChain(uiMenuDraw( - &UI_GAME_MENU.menu, - x + UI_FRAME_START_X, - y + UI_FRAME_START_Y, - width - (UI_FRAME_START_X * 2), - height - (UI_FRAME_START_Y * 2) - )); - - errorOk(); -} - -bool_t uiGameMenuIsOpen(void) { - return uiMenuIsActive(&UI_GAME_MENU.menu); -} - -void uiGameMenuOpen() { - uiMenuOpen(&UI_GAME_MENU.menu); -} - -void uiGameMenuClose() { - uiMenuClose(&UI_GAME_MENU.menu); -} - -errorret_t uiGameMenuDispose(void) { - errorOk(); -} diff --git a/src/dusk/ui/frame/game/uigamemenu.h b/src/dusk/ui/frame/game/uigamemenu.h deleted file mode 100644 index ce6307fe..00000000 --- a/src/dusk/ui/frame/game/uigamemenu.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" -#include "ui/widget/uimenu.h" - -#define UI_GAME_MENU_ITEM_COUNT 3 -#define UI_GAME_MENU_WIDTH 150.0f -#define UI_GAME_MENU_LABEL_MAX 32 - -typedef struct { - uimenu_t menu; - uimenuitem_t items[UI_GAME_MENU_ITEM_COUNT]; - char_t charactersLabel[UI_GAME_MENU_LABEL_MAX]; - char_t itemsLabel[UI_GAME_MENU_LABEL_MAX]; - char_t settingsLabel[UI_GAME_MENU_LABEL_MAX]; -} uigamemenu_t; - -extern uigamemenu_t UI_GAME_MENU; - -/** - * Initializes the game menu panel. - * - * @return Any error that occurs. - */ -errorret_t uiGameMenuInit(void); - -/** - * Draws the game menu panel. No-op when not visible. - * - * @return Any error that occurs. - */ -errorret_t uiGameMenuDraw(void); - -/** - * Returns true when the game menu panel is currently open. - * - * @returns True if open. - */ -bool_t uiGameMenuIsOpen(void); - -/** - * Opens the game menu panel. No-op when already open. - */ -void uiGameMenuOpen(); - -/** - * Closes the game menu panel. No-op when already closed. - */ -void uiGameMenuClose(); - -/** - * Disposes of the game menu panel. - * - * @return Any error that occurs. - */ -errorret_t uiGameMenuDispose(void); diff --git a/src/dusk/ui/rpg/CMakeLists.txt b/src/dusk/ui/rpg/CMakeLists.txt deleted file mode 100644 index eb3441de..00000000 --- a/src/dusk/ui/rpg/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - uiemoji.c -) - - -add_subdirectory(textbox) \ No newline at end of file diff --git a/src/dusk/ui/rpg/textbox/CMakeLists.txt b/src/dusk/ui/rpg/textbox/CMakeLists.txt deleted file mode 100644 index e7787f2d..00000000 --- a/src/dusk/ui/rpg/textbox/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - uitextbox.c - uitextboxmain.c - uitextboxmini.c - uitextboxminilist.c -) diff --git a/src/dusk/ui/rpg/textbox/uitextbox.c b/src/dusk/ui/rpg/textbox/uitextbox.c deleted file mode 100644 index 272b7e89..00000000 --- a/src/dusk/ui/rpg/textbox/uitextbox.c +++ /dev/null @@ -1,242 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "uitextbox.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "util/string.h" -#include "time/time.h" -#include "display/text/text.h" -#include "display/color.h" -#include "display/spritebatch/spritebatch.h" -#include "display/shader/shaderunlit.h" -#include "ui/frame/uiframe.h" - -void uiTextboxInit( - uitextbox_t *box, - char_t *text, - const uint32_t maxLength, - uitextboxline_t *lines, - const uint32_t linesMax -) { - assertNotNull(box, "Textbox cannot be NULL"); - assertNotNull(text, "Text buffer cannot be NULL"); - assertTrue(maxLength >= 1, "maxLength must be at least 1"); - assertNotNull(lines, "Lines buffer cannot be NULL"); - assertTrue(linesMax >= 1, "linesMax must be at least 1"); - memoryZero(box, sizeof(uitextbox_t)); - box->text = text; - box->maxLength = maxLength; - box->lines = lines; - box->linesMax = linesMax; -} - -void uiTextboxSetText(uitextbox_t *box, const char_t *text) { - assertNotNull(box, "Textbox cannot be NULL"); - assertNotNull(text, "Text cannot be NULL"); - stringCopy(box->text, text, box->maxLength); - box->currentPage = 0; - box->scroll = 0; - box->layoutWidth = 0.0f; - box->layoutHeight = 0.0f; -} - -void uiTextboxBuildLayout( - uitextbox_t *box, - const float_t width, - const float_t height -) { - assertNotNull(box, "Textbox cannot be NULL"); - - box->layoutWidth = width; - box->layoutHeight = height; - box->lineCount = 0; - box->pageCount = 1; - - float_t fontW = (float_t)FONT_DEFAULT.tileset->tileWidth; - float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight; - - if(fontW <= 0.0f || fontH <= 0.0f) return; - - box->charsPerLine = (int32_t)(width / fontW); - box->linesPerPage = (int32_t)(height / (fontH + UI_TEXTBOX_LINE_SPACING)); - if(box->linesPerPage > UI_TEXTBOX_LINES_PER_PAGE_MAX) { - box->linesPerPage = UI_TEXTBOX_LINES_PER_PAGE_MAX; - } - - if(box->charsPerLine <= 0 || box->linesPerPage <= 0) return; - if(box->text[0] == '\0') return; - - char_t *src = box->text; - int32_t i = 0; - - while(src[i] != '\0' && box->lineCount < (int32_t)box->linesMax) { - if(src[i] == '\t') { - i++; - int32_t rem = box->lineCount % box->linesPerPage; - int32_t pad = rem > 0 ? box->linesPerPage - rem : 0; - while(pad > 0 && box->lineCount < (int32_t)box->linesMax) { - box->lines[box->lineCount].start = i; - box->lines[box->lineCount].count = 0; - box->lineCount++; - pad--; - } - continue; - } - - int32_t lineStart = i; - int32_t lineWidth = 0; - - while(src[i] != '\0') { - char_t c = src[i]; - - if(c == '\n') { i++; break; } - if(c == '\t') break; - - if(c == ' ') { - int32_t wordLen = 0; - int32_t j = i + 1; - while( - src[j] != ' ' && src[j] != '\n' && - src[j] != '\t' && src[j] != '\0' - ) { - wordLen++; - j++; - } - - if(lineWidth > 0 && lineWidth + 1 + wordLen > box->charsPerLine) { - i++; - break; - } - - lineWidth++; - i++; - } else { - if(lineWidth >= box->charsPerLine) break; - lineWidth++; - i++; - } - } - - box->lines[box->lineCount].start = lineStart; - box->lines[box->lineCount].count = lineWidth; - box->lineCount++; - } - - if(box->lineCount == 0) { - box->pageCount = 1; - } else { - box->pageCount = - (box->lineCount + box->linesPerPage - 1) / box->linesPerPage; - } -} - -errorret_t uiTextboxUpdate(uitextbox_t *box) { - assertNotNull(box, "Textbox cannot be NULL"); - -#ifdef DUSK_TIME_DYNAMIC - if(TIME.dynamicUpdate) errorOk(); -#endif - - if(!uiTextboxPageIsComplete(box)) { - box->scroll += UI_TEXTBOX_SCROLL_CHARS_PER_TICK; - } - - errorOk(); -} - -errorret_t uiTextboxDraw( - uitextbox_t *box, - const float_t x, - const float_t y, - const float_t width, - const float_t height -) { - assertNotNull(box, "Textbox cannot be NULL"); - - float_t startX = (float_t)UI_FRAME_START_X; - float_t startY = (float_t)UI_FRAME_START_Y; - float_t contentX = x + startX; - float_t contentY = y + startY; - float_t contentW = width - 2.0f * startX; - float_t contentH = height - 2.0f * startY; - - if(contentW != box->layoutWidth || contentH != box->layoutHeight) { - uiTextboxBuildLayout(box, contentW, contentH); - } - - errorChain(uiFrameDraw(x, y, width, height)); - - if(box->lineCount == 0 || box->text[0] == '\0') errorOk(); - - float_t fontW = (float_t)FONT_DEFAULT.tileset->tileWidth; - float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight; - - shadermaterial_t material = { - .unlit = { - .color = COLOR_WHITE, - .texture = FONT_DEFAULT.texture - } - }; - - int32_t pageFirst = box->currentPage * box->linesPerPage; - int32_t pageLast = pageFirst + box->linesPerPage; - if(pageLast > box->lineCount) pageLast = box->lineCount; - - int32_t charsLeft = box->scroll; - - for(int32_t li = pageFirst; li < pageLast && charsLeft > 0; li++) { - uitextboxline_t *line = &box->lines[li]; - int32_t visible = line->count < charsLeft ? line->count : charsLeft; - float_t lineY = contentY + - (float_t)(li - pageFirst) * (fontH + UI_TEXTBOX_LINE_SPACING); - - for(int32_t ci = 0; ci < visible; ci++) { - char_t c = box->text[line->start + ci]; - if(c == ' ') continue; - spritebatchsprite_t sprite = textGetSprite( - (vec2){ contentX + (float_t)ci * fontW, lineY }, - c, - &FONT_DEFAULT - ); - errorChain(spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, material)); - } - - charsLeft -= visible; - } - - errorOk(); -} - -int32_t uiTextboxGetPageCharCount(const uitextbox_t *box) { - assertNotNull(box, "Textbox cannot be NULL"); - int32_t first = box->currentPage * box->linesPerPage; - int32_t last = first + box->linesPerPage; - if(last > box->lineCount) last = box->lineCount; - int32_t total = 0; - for(int32_t i = first; i < last; i++) { - total += box->lines[i].count; - } - return total; -} - -bool_t uiTextboxPageIsComplete(const uitextbox_t *box) { - assertNotNull(box, "Textbox cannot be NULL"); - return box->scroll >= uiTextboxGetPageCharCount(box); -} - -bool_t uiTextboxHasNextPage(const uitextbox_t *box) { - assertNotNull(box, "Textbox cannot be NULL"); - return box->currentPage + 1 < box->pageCount; -} - -void uiTextboxNextPage(uitextbox_t *box) { - assertNotNull(box, "Textbox cannot be NULL"); - if(!uiTextboxHasNextPage(box)) return; - box->currentPage++; - box->scroll = 0; -} diff --git a/src/dusk/ui/rpg/textbox/uitextbox.h b/src/dusk/ui/rpg/textbox/uitextbox.h deleted file mode 100644 index 047e7e1f..00000000 --- a/src/dusk/ui/rpg/textbox/uitextbox.h +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" - -#define UI_TEXTBOX_LINES_PER_PAGE_MAX 4 -#define UI_TEXTBOX_SCROLL_CHARS_PER_TICK 1 -#define UI_TEXTBOX_LINE_SPACING 0.0f - -typedef struct { - int32_t start; - int32_t count; -} uitextboxline_t; - -typedef struct { - char_t *text; - uint32_t maxLength; - - uitextboxline_t *lines; - uint32_t linesMax; - int32_t lineCount; - int32_t charsPerLine; - int32_t linesPerPage; - int32_t pageCount; - - // last dimensions used for layout; rebuild triggers when these change - float_t layoutWidth; - float_t layoutHeight; - - int32_t currentPage; - int32_t scroll; -} uitextbox_t; - -/** - * Initializes a textbox, zeroing all state and binding it to caller-owned - * text and line storage. - * - * @param box The textbox to initialize. - * @param text Caller-owned buffer the textbox copies its text into. - * @param maxLength Capacity of text, in characters. - * @param lines Caller-owned buffer the textbox lays lines out into. - * @param linesMax Capacity of lines, in entries. - */ -void uiTextboxInit( - uitextbox_t *box, - char_t *text, - const uint32_t maxLength, - uitextboxline_t *lines, - const uint32_t linesMax -); - -/** - * Copies text into the textbox and marks layout as dirty. - * Resets currentPage and scroll to 0. - * - * @param box The textbox to update. - * @param text Null-terminated source string. - */ -void uiTextboxSetText(uitextbox_t *box, const char_t *text); - -/** - * Rebuilds word-wrap and page layout for the given draw dimensions. - * Called automatically by uiTextboxDraw when width or height changes. - * - * @param box The textbox to rebuild. - * @param width Available content width in pixels. - * @param height Available content height in pixels. - */ -void uiTextboxBuildLayout( - uitextbox_t *box, - const float_t width, - const float_t height -); - -/** - * Advances the typewriter scroll by UI_TEXTBOX_SCROLL_CHARS_PER_TICK. - * Skipped on dynamic ticks. - * - * @param box The textbox to update. - * @returns Any error that occurs. - */ -errorret_t uiTextboxUpdate(uitextbox_t *box); - -/** - * Draws the textbox frame and visible text. Rebuilds layout automatically - * if width or height differs from the last draw call. - * - * @param box The textbox to draw. - * @param x Screen x position. - * @param y Screen y position. - * @param width Draw width in pixels. - * @param height Draw height in pixels. - * @returns Any error that occurs. - */ -errorret_t uiTextboxDraw( - uitextbox_t *box, - const float_t x, - const float_t y, - const float_t width, - const float_t height -); - -/** - * Returns the total visible char count for the current page. - * - * @param box The textbox to query. - * @returns Total chars on the current page. - */ -int32_t uiTextboxGetPageCharCount(const uitextbox_t *box); - -/** - * Returns true when scroll has fully revealed the current page. - * - * @param box The textbox to query. - * @returns True if the current page is fully visible. - */ -bool_t uiTextboxPageIsComplete(const uitextbox_t *box); - -/** - * Returns true when there is at least one more page after the current one. - * - * @param box The textbox to query. - * @returns True if a next page exists. - */ -bool_t uiTextboxHasNextPage(const uitextbox_t *box); - -/** - * Advances to the next page and resets scroll to 0. - * Has no effect if already on the last page. - * - * @param box The textbox to advance. - */ -void uiTextboxNextPage(uitextbox_t *box); diff --git a/src/dusk/ui/rpg/textbox/uitextboxmain.c b/src/dusk/ui/rpg/textbox/uitextboxmain.c deleted file mode 100644 index f81cce8a..00000000 --- a/src/dusk/ui/rpg/textbox/uitextboxmain.c +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "uitextboxmain.h" -#include "ui/focus/uifocus.h" -#include "display/screen/screen.h" -#include "display/text/text.h" -#include "display/color.h" -#include "display/spritebatch/spritebatch.h" -#include "display/shader/shaderunlit.h" -#include "ui/frame/uiframe.h" - -uitextboxmain_t UI_TEXTBOX_MAIN; -static uifocusitem_t *focusItem = NULL; - -errorret_t uiTextboxMainInit(void) { - uiTextboxInit( - &UI_TEXTBOX_MAIN.box, - UI_TEXTBOX_MAIN.text, UI_TEXTBOX_MAIN_TEXT_MAX, - UI_TEXTBOX_MAIN.lines, UI_TEXTBOX_MAIN_LINES_MAX - ); - errorOk(); -} - -void uiTextboxMainSetText(const char_t *text) { - uiTextboxSetText(&UI_TEXTBOX_MAIN.box, text); - if(focusItem != NULL) return; - focusItem = uiFocusPush( - 1, 1, - uiTextboxMainFocusSelected, - NULL, - uiTextboxMainFocusClosed, - NULL, - NULL - ); -} - -errorret_t uiTextboxMainUpdate(void) { - if(focusItem == NULL) errorOk(); - return uiTextboxUpdate(&UI_TEXTBOX_MAIN.box); -} - -errorret_t uiTextboxMainDraw(void) { - if(focusItem == NULL) errorOk(); - float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight; - float_t h = (float_t)UI_TEXTBOX_MAIN_LINES * fontH + - (float_t)(UI_TEXTBOX_MAIN_LINES - 1) * UI_TEXTBOX_LINE_SPACING + - 2.0f * (float_t)UI_FRAME_START_Y; - float_t w = (float_t)SCREEN.scanWidth; - float_t x = (float_t)SCREEN.scanX; - float_t y = (float_t)(SCREEN.scanY + SCREEN.scanHeight) - h; - errorChain(uiTextboxDraw(&UI_TEXTBOX_MAIN.box, x, y, w, h)); - - if(!uiTextboxPageIsComplete(&UI_TEXTBOX_MAIN.box)) errorOk(); - - float_t fontW = (float_t)FONT_DEFAULT.tileset->tileWidth; - float_t contentX = x + (float_t)UI_FRAME_START_X; - float_t contentY = y + (float_t)UI_FRAME_START_Y; - float_t contentW = w - 2.0f * (float_t)UI_FRAME_START_X; - float_t contentH = h - 2.0f * (float_t)UI_FRAME_START_Y; - - shadermaterial_t material = { - .unlit = { - .color = COLOR_WHITE, - .texture = FONT_DEFAULT.texture - } - }; - - spritebatchsprite_t caret = textGetSprite( - (vec2){ - contentX + contentW - fontW, - contentY + contentH - fontH - }, - 'v', - &FONT_DEFAULT - ); - errorChain(spriteBatchBuffer(&caret, 1, &SHADER_UNLIT, material)); - - errorOk(); -} - -bool_t uiTextboxMainPageIsComplete(void) { - return uiTextboxPageIsComplete(&UI_TEXTBOX_MAIN.box); -} - -bool_t uiTextboxMainHasNextPage(void) { - return uiTextboxHasNextPage(&UI_TEXTBOX_MAIN.box); -} - -void uiTextboxMainNextPage(void) { - uiTextboxNextPage(&UI_TEXTBOX_MAIN.box); -} - -bool_t uiTextboxMainIsActive(void) { - return focusItem != NULL; -} - -bool_t uiTextboxMainFocusSelected(const uifocusitem_t *item) { - if(!uiTextboxPageIsComplete(&UI_TEXTBOX_MAIN.box)) { - UI_TEXTBOX_MAIN.box.scroll = - uiTextboxGetPageCharCount(&UI_TEXTBOX_MAIN.box); - return true; - } - - if(uiTextboxHasNextPage(&UI_TEXTBOX_MAIN.box)) { - uiTextboxNextPage(&UI_TEXTBOX_MAIN.box); - return true; - } - - uiFocusPopItem(focusItem); - return true; -} - -bool_t uiTextboxMainFocusClosed(const uifocusitem_t *item) { - focusItem = NULL; - return true; -} diff --git a/src/dusk/ui/rpg/textbox/uitextboxmain.h b/src/dusk/ui/rpg/textbox/uitextboxmain.h deleted file mode 100644 index e7eb83f6..00000000 --- a/src/dusk/ui/rpg/textbox/uitextboxmain.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "ui/rpg/textbox/uitextbox.h" -#include "ui/focus/uifocusitem.h" - -#define UI_TEXTBOX_MAIN_LINES 4 -#define UI_TEXTBOX_MAIN_TEXT_MAX 1024 -#define UI_TEXTBOX_MAIN_LINES_MAX 64 - -typedef struct { - uitextbox_t box; - char_t text[UI_TEXTBOX_MAIN_TEXT_MAX]; - uitextboxline_t lines[UI_TEXTBOX_MAIN_LINES_MAX]; -} uitextboxmain_t; - -extern uitextboxmain_t UI_TEXTBOX_MAIN; - -/** - * Initializes UI_TEXTBOX_MAIN. - * - * @returns Any error that occurs. - */ -errorret_t uiTextboxMainInit(void); - -/** - * Copies text into UI_TEXTBOX_MAIN and resets page and scroll. - * - * @param text Null-terminated source string. - */ -void uiTextboxMainSetText(const char_t *text); - -/** - * Advances the typewriter scroll for UI_TEXTBOX_MAIN. - * - * @returns Any error that occurs. - */ -errorret_t uiTextboxMainUpdate(void); - -/** - * Draws UI_TEXTBOX_MAIN full-width at the bottom of the screen. - * Position and size are derived from SCREEN each call. - * - * @returns Any error that occurs. - */ -errorret_t uiTextboxMainDraw(void); - -/** - * Returns true when the current page is fully scrolled in. - * - * @returns True if the current page is complete. - */ -bool_t uiTextboxMainPageIsComplete(void); - -/** - * Returns true when at least one more page follows the current one. - * - * @returns True if a next page exists. - */ -bool_t uiTextboxMainHasNextPage(void); - -/** - * Advances UI_TEXTBOX_MAIN to the next page and resets scroll. - * Has no effect if already on the last page. - */ -void uiTextboxMainNextPage(void); - -/** - * Returns true when UI_TEXTBOX_MAIN has focus (is visible and active). - * - * @returns True if the textbox is currently active. - */ -bool_t uiTextboxMainIsActive(void); - -/** - * Internal focus callback - skip scroll or advance page or dismiss. - * - * @param item The active focus item. - * @returns True. - */ -bool_t uiTextboxMainFocusSelected(const uifocusitem_t *item); - -/** - * Internal focus callback - clears the focus item pointer on dismiss. - * - * @param item The focus item being closed. - * @returns True. - */ -bool_t uiTextboxMainFocusClosed(const uifocusitem_t *item); diff --git a/src/dusk/ui/rpg/textbox/uitextboxmini.c b/src/dusk/ui/rpg/textbox/uitextboxmini.c deleted file mode 100644 index f04515c6..00000000 --- a/src/dusk/ui/rpg/textbox/uitextboxmini.c +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "uitextboxmini.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "time/time.h" -#include "rpg/rpgcamera.h" -#include "display/text/text.h" -#include "display/screen/screen.h" -#include "ui/frame/uiframe.h" - -void uiTextboxMiniInit(uitextboxmini_t *mini) { - assertNotNull(mini, "Mini textbox cannot be NULL"); - memoryZero(mini, sizeof(uitextboxmini_t)); - uiTextboxInit( - &mini->box, - mini->text, UI_TEXTBOX_MINI_TEXT_MAX, - mini->lines, UI_TEXTBOX_MINI_LINES_MAX - ); -} - -void uiTextboxMiniShow( - uitextboxmini_t *mini, - const char_t *text, - vec3 position, - const float_t duration, - uitextboxminiclosedcallback_t closed, - void *user -) { - assertNotNull(mini, "Mini textbox cannot be NULL"); - assertNotNull(text, "Text cannot be NULL"); - uiTextboxSetText(&mini->box, text); - glm_vec3_copy(position, mini->position); - - int32_t textWidth, textHeight; - textMeasure(text, &FONT_DEFAULT, &textWidth, &textHeight); - float_t width = (float_t)textWidth + 2.0f * (float_t)UI_FRAME_START_X; - mini->width = width < UI_TEXTBOX_MINI_WIDTH_MAX - ? width : UI_TEXTBOX_MINI_WIDTH_MAX; - - float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight; - float_t maxHeight = (float_t)UI_TEXTBOX_MINI_LINES_MAX * fontH + - (float_t)(UI_TEXTBOX_MINI_LINES_MAX - 1) * UI_TEXTBOX_LINE_SPACING + - 2.0f * (float_t)UI_FRAME_START_Y; - uiTextboxBuildLayout( - &mini->box, - mini->width - 2.0f * (float_t)UI_FRAME_START_X, - maxHeight - 2.0f * (float_t)UI_FRAME_START_Y - ); - int32_t lineCount = mini->box.lineCount > 0 ? mini->box.lineCount : 1; - mini->height = (float_t)lineCount * fontH + - (float_t)(lineCount - 1) * UI_TEXTBOX_LINE_SPACING + - 2.0f * (float_t)UI_FRAME_START_Y; - - mini->active = true; - mini->timer = duration; - mini->closed = closed; - mini->user = user; -} - -errorret_t uiTextboxMiniUpdate(uitextboxmini_t *mini) { - assertNotNull(mini, "Mini textbox cannot be NULL"); - if(!mini->active) errorOk(); - - errorChain(uiTextboxUpdate(&mini->box)); - - if(mini->timer != UI_TEXTBOX_MINI_DURATION_INFINITE) { - mini->timer -= TIME.delta; - if(mini->timer <= 0.0f) uiTextboxMiniClose(mini); - } - - errorOk(); -} - -errorret_t uiTextboxMiniDraw(uitextboxmini_t *mini) { - assertNotNull(mini, "Mini textbox cannot be NULL"); - if(!mini->active) errorOk(); - - vec2 screenPos; - rpgCameraToScreen(mini->position, screenPos); - - if( - mini->clamp == UI_TEXTBOX_MINI_CLAMP_X || - mini->clamp == UI_TEXTBOX_MINI_CLAMP_BOTH - ) { - float_t minX = (float_t)SCREEN.scanX; - float_t maxX = (float_t)(SCREEN.scanX + SCREEN.scanWidth) - mini->width; - if(screenPos[0] < minX) screenPos[0] = minX; - if(screenPos[0] > maxX) screenPos[0] = maxX; - } - - if( - mini->clamp == UI_TEXTBOX_MINI_CLAMP_Y || - mini->clamp == UI_TEXTBOX_MINI_CLAMP_BOTH - ) { - float_t minY = (float_t)SCREEN.scanY; - float_t maxY = (float_t)(SCREEN.scanY + SCREEN.scanHeight) - mini->height; - if(screenPos[1] < minY) screenPos[1] = minY; - if(screenPos[1] > maxY) screenPos[1] = maxY; - } - - return uiTextboxDraw( - &mini->box, screenPos[0], screenPos[1], mini->width, mini->height - ); -} - -bool_t uiTextboxMiniIsActive(const uitextboxmini_t *mini) { - assertNotNull(mini, "Mini textbox cannot be NULL"); - return mini->active; -} - -void uiTextboxMiniClose(uitextboxmini_t *mini) { - assertNotNull(mini, "Mini textbox cannot be NULL"); - if(!mini->active) return; - mini->active = false; - if(mini->closed != NULL) mini->closed(mini); -} diff --git a/src/dusk/ui/rpg/textbox/uitextboxmini.h b/src/dusk/ui/rpg/textbox/uitextboxmini.h deleted file mode 100644 index f173b3c5..00000000 --- a/src/dusk/ui/rpg/textbox/uitextboxmini.h +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "ui/rpg/textbox/uitextbox.h" - -#define UI_TEXTBOX_MINI_TEXT_MAX 128 -#define UI_TEXTBOX_MINI_LINES_MAX 2 -#define UI_TEXTBOX_MINI_WIDTH_MAX 160.0f - -// Magic duration value that keeps a mini textbox visible indefinitely -// instead of counting down and auto-closing. -#define UI_TEXTBOX_MINI_DURATION_INFINITE -1.0f - -typedef struct uitextboxmini_s uitextboxmini_t; -typedef void (*uitextboxminiclosedcallback_t)(const uitextboxmini_t *mini); - -/** - * Determines whether a mini textbox's screen position is clamped to stay - * fully on screen. Defaults to UI_TEXTBOX_MINI_CLAMP_NONE. - */ -typedef enum { - UI_TEXTBOX_MINI_CLAMP_NONE, - UI_TEXTBOX_MINI_CLAMP_X, - UI_TEXTBOX_MINI_CLAMP_Y, - UI_TEXTBOX_MINI_CLAMP_BOTH -} uitextboxminiclamp_t; - -typedef struct uitextboxmini_s { - uitextbox_t box; - char_t text[UI_TEXTBOX_MINI_TEXT_MAX]; - uitextboxline_t lines[UI_TEXTBOX_MINI_LINES_MAX]; - - vec3 position; - float_t width; - float_t height; - uitextboxminiclamp_t clamp; - bool_t active; - float_t timer; - - uitextboxminiclosedcallback_t closed; - void *user; -} uitextboxmini_t; - -/** - * Initializes a mini textbox, zeroing all state and binding its internal - * uitextbox_t to its own fixed-size text and line storage. - * - * @param mini The mini textbox to initialize. - */ -void uiTextboxMiniInit(uitextboxmini_t *mini); - -/** - * Shows a mini textbox with the given text at the given world position for - * the given duration. Resets the typewriter scroll, measures the text to - * size the box (width capped at UI_TEXTBOX_MINI_WIDTH_MAX, height derived - * from the resulting wrapped line count), and starts the visibility timer. - * - * @param mini The mini textbox to show. - * @param text Null-terminated source string. - * @param position World-space position the box is anchored to on screen. - * @param duration How long the mini textbox stays visible, in seconds, or - * UI_TEXTBOX_MINI_DURATION_INFINITE to never auto-close. - * @param closed Called once the timer elapses and the box closes. May be - * NULL. - * @param user Opaque pointer passed back through the closed callback. - */ -void uiTextboxMiniShow( - uitextboxmini_t *mini, - const char_t *text, - vec3 position, - const float_t duration, - uitextboxminiclosedcallback_t closed, - void *user -); - -/** - * Advances the typewriter scroll and counts down the visibility timer. - * Closes the mini textbox and fires its closed callback once the timer - * elapses. Has no effect if not active or if the duration was set to - * UI_TEXTBOX_MINI_DURATION_INFINITE. - * - * @param mini The mini textbox to update. - * @returns Any error that occurs. - */ -errorret_t uiTextboxMiniUpdate(uitextboxmini_t *mini); - -/** - * Draws the mini textbox at its text-measured size, anchored on screen to - * its world-space position via the RPG camera. Has no effect if not - * active. - * - * @param mini The mini textbox to draw. - * @returns Any error that occurs. - */ -errorret_t uiTextboxMiniDraw(uitextboxmini_t *mini); - -/** - * Returns true when the mini textbox is currently visible. - * - * @param mini The mini textbox to query. - * @returns True if active. - */ -bool_t uiTextboxMiniIsActive(const uitextboxmini_t *mini); - -/** - * Immediately closes the mini textbox and fires its closed callback. - * Has no effect if not active. - * - * @param mini The mini textbox to close. - */ -void uiTextboxMiniClose(uitextboxmini_t *mini); diff --git a/src/dusk/ui/rpg/textbox/uitextboxminilist.c b/src/dusk/ui/rpg/textbox/uitextboxminilist.c deleted file mode 100644 index 070bc817..00000000 --- a/src/dusk/ui/rpg/textbox/uitextboxminilist.c +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "uitextboxminilist.h" - -uitextboxmini_t UI_TEXTBOX_MINI_LIST[UI_TEXTBOX_MINI_LIST_COUNT]; -uint8_t UI_TEXTBOX_MINI_LIST_NEXT; - -errorret_t uiTextboxMiniListInit(void) { - for(uint8_t i = 0; i < UI_TEXTBOX_MINI_LIST_COUNT; i++) { - uiTextboxMiniInit(&UI_TEXTBOX_MINI_LIST[i]); - } - UI_TEXTBOX_MINI_LIST_NEXT = 0; - errorOk(); -} - -uint8_t uiTextboxMiniListGetNext(void) { - uint8_t index = UI_TEXTBOX_MINI_LIST_NEXT; - UI_TEXTBOX_MINI_LIST_NEXT = - (UI_TEXTBOX_MINI_LIST_NEXT + 1) % UI_TEXTBOX_MINI_LIST_COUNT; - return index; -} - -errorret_t uiTextboxMiniListUpdate(void) { - for(uint8_t i = 0; i < UI_TEXTBOX_MINI_LIST_COUNT; i++) { - errorChain(uiTextboxMiniUpdate(&UI_TEXTBOX_MINI_LIST[i])); - } - errorOk(); -} - -errorret_t uiTextboxMiniListDraw(void) { - for(uint8_t i = 0; i < UI_TEXTBOX_MINI_LIST_COUNT; i++) { - errorChain(uiTextboxMiniDraw(&UI_TEXTBOX_MINI_LIST[i])); - } - errorOk(); -} diff --git a/src/dusk/ui/rpg/textbox/uitextboxminilist.h b/src/dusk/ui/rpg/textbox/uitextboxminilist.h deleted file mode 100644 index f3040b2c..00000000 --- a/src/dusk/ui/rpg/textbox/uitextboxminilist.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "ui/rpg/textbox/uitextboxmini.h" - -#define UI_TEXTBOX_MINI_LIST_COUNT 4 - -extern uitextboxmini_t UI_TEXTBOX_MINI_LIST[UI_TEXTBOX_MINI_LIST_COUNT]; -extern uint8_t UI_TEXTBOX_MINI_LIST_NEXT; - -/** - * Initializes all UI_TEXTBOX_MINI_LIST slots and resets - * UI_TEXTBOX_MINI_LIST_NEXT. - * - * @returns Any error that occurs. - */ -errorret_t uiTextboxMiniListInit(void); - -/** - * Returns the index of the next UI_TEXTBOX_MINI_LIST slot to use, cycling - * through all slots in round-robin order via UI_TEXTBOX_MINI_LIST_NEXT. - * - * @returns The next slot index. - */ -uint8_t uiTextboxMiniListGetNext(void); - -/** - * Updates all UI_TEXTBOX_MINI_LIST slots. - * - * @returns Any error that occurs. - */ -errorret_t uiTextboxMiniListUpdate(void); - -/** - * Draws all active UI_TEXTBOX_MINI_LIST slots. - * - * @returns Any error that occurs. - */ -errorret_t uiTextboxMiniListDraw(void); diff --git a/src/dusk/ui/rpg/uiemoji.c b/src/dusk/ui/rpg/uiemoji.c deleted file mode 100644 index 387e873d..00000000 --- a/src/dusk/ui/rpg/uiemoji.c +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "uiemoji.h" -#include "display/spritebatch/spritebatch.h" -#include "time/time.h" -#include "util/memory.h" -#include "display/shader/shaderunlit.h" -#include "rpg/entity/entity.h" -#include "rpg/rpgcamera.h" - -uiemoji_t UI_EMOJI; - -errorret_t uiEmojiInit(void) { - memoryZero(&UI_EMOJI, sizeof(UI_EMOJI)); - errorOk(); -} - -void uiEmojiAdd( - const uint8_t entity, - const float_t duration, - const uiemojitype_t type -) { - uint8_t slot = 0xFF; - // Find an available slot if any - for(uint8_t i = 0; i < UI_EMOJI_ACTIVE_COUNT; i++) { - if(UI_EMOJI.items[i].time <= 0.0f) { - slot = i; - break; - } - - if(UI_EMOJI.items[i].entity == entity) { - slot = i; - break; - } - - if(UI_EMOJI.items[i].type == UI_EMOJI_NULL) { - slot = i; - break; - } - } - - if(slot == 0xFF) { - // Whichever ends soonest - float_t minTime = UI_EMOJI.items[0].time; - slot = 0; - for(uint8_t i = 1; i < UI_EMOJI_ACTIVE_COUNT; i++) { - if(UI_EMOJI.items[i].time >= minTime) continue; - minTime = UI_EMOJI.items[i].time; - slot = i; - } - } - - uiemojiitem_t *item = &UI_EMOJI.items[slot]; - item->entity = entity; - item->time = duration; - item->type = type; -} - -errorret_t uiEmojiUpdate(void) { - for(uint8_t i = 0; i < UI_EMOJI_ACTIVE_COUNT; i++) { - uiemojiitem_t *item = &UI_EMOJI.items[i]; - if(item->type == UI_EMOJI_NULL) continue; - - item->time -= TIME.delta; - - if(item->time <= 0.0f) { - item->type = UI_EMOJI_NULL; - item->entity = 0xFF; - } - } - - errorOk(); -} - -errorret_t uiEmojiDraw(void) { - spritebatchsprite_t sprites[UI_EMOJI_ACTIVE_COUNT]; - uint8_t count = 0; - - for(uint8_t i = 0; i < UI_EMOJI_ACTIVE_COUNT; i++) { - uiemojiitem_t *item = &UI_EMOJI.items[i]; - if(item->type == UI_EMOJI_NULL) continue; - - vec2 screenPos; - rpgCameraToScreen(ENTITIES[item->entity].renderPosition, screenPos); - - spritebatchsprite_t *sprite = &sprites[count++]; - sprite->min[0] = screenPos[0]; - sprite->min[1] = screenPos[1] - UI_EMOJI_SIZE; - sprite->min[2] = 0.0f; - sprite->max[0] = screenPos[0] + UI_EMOJI_SIZE; - sprite->max[1] = screenPos[1]; - sprite->max[2] = 0.0f; - sprite->uvMin[0] = 0.0f; - sprite->uvMin[1] = 0.0f; - sprite->uvMax[0] = 1.0f; - sprite->uvMax[1] = 1.0f; - } - - if(count == 0) errorOk(); - - shadermaterial_t mat = { - .unlit = { - .color = COLOR_WHITE, - .texture = NULL - } - }; - errorChain(spriteBatchBuffer(sprites, count, &SHADER_UNLIT, mat)); - errorOk(); -} - -errorret_t uiEmojiDispose(void) { - errorOk(); -} diff --git a/src/dusk/ui/rpg/uiemoji.h b/src/dusk/ui/rpg/uiemoji.h deleted file mode 100644 index 6dc8cc00..00000000 --- a/src/dusk/ui/rpg/uiemoji.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" - -#define UI_EMOJI_ACTIVE_COUNT 4 -#define UI_EMOJI_SIZE 32.0f - -typedef enum { - UI_EMOJI_NULL, - - UI_EMOJI_QUESTION_MARK, - UI_EMOJI_EXCLAMATION_MARK, - - UI_EMOJI_COUNT, -} uiemojitype_t; - -typedef struct { - uint8_t entity; - float_t time; - uiemojitype_t type; -} uiemojiitem_t; - -typedef struct { - uiemojiitem_t items[UI_EMOJI_ACTIVE_COUNT]; -} uiemoji_t; - -extern uiemoji_t UI_EMOJI; - -/** - * Initialize the UI emoji. - */ -errorret_t uiEmojiInit(void); - -/** - * Show a UI emoji for a specific entity. - * - * @param entity The entity to show the emoji for. - * @param duration The duration to display the emoji. - * @param type The type of emoji to display. - */ -void uiEmojiAdd( - const uint8_t entity, - const float_t duration, - const uiemojitype_t type -); - -/** - * Update the UI emoji. - */ -errorret_t uiEmojiUpdate(void); - -/** - * Draw the UI emoji. - */ -errorret_t uiEmojiDraw(void); - -/** - * Dispose of the UI emoji. - */ -errorret_t uiEmojiDispose(void); \ No newline at end of file diff --git a/src/dusk/ui/uielement.c b/src/dusk/ui/uielement.c index 74bda578..a16ac836 100644 --- a/src/dusk/ui/uielement.c +++ b/src/dusk/ui/uielement.c @@ -12,17 +12,11 @@ #include "engine/engine.h" #include "ui/overlay/uifullbox.h" #include "ui/overlay/uiloading.h" -#include "ui/debug/uiplayerpos.h" #include "ui/overlay/uicrop.h" #include "ui/transition/uitransition.h" #include "ui/debug/uiconsole.h" #include "ui/frame/settings/uisettings.h" -#include "ui/frame/game/uigamemenu.h" -#include "ui/frame/backpack/uibackpack.h" #include "ui/frame/uiconfirm.h" -#include "ui/rpg/textbox/uitextboxmain.h" -#include "ui/rpg/textbox/uitextboxminilist.h" -#include "ui/rpg/uiemoji.h" uielement_t UI_ELEMENTS[] = { { @@ -37,27 +31,6 @@ uielement_t UI_ELEMENTS[] = { .draw = uiFullboxUnderDraw }, - // in world stuff - { - .init = uiEmojiInit, - .update = uiEmojiUpdate, - .draw = uiEmojiDraw, - .dispose = uiEmojiDispose - }, - - // Ingame menus - { - .init = uiGameMenuInit, - .draw = uiGameMenuDraw, - .dispose = uiGameMenuDispose - }, - - { - .init = uiBackpackInit, - .draw = uiBackpackDraw, - .dispose = uiBackpackDispose - }, - { .init = uiSettingsInit, .update = uiSettingsUpdate, @@ -72,18 +45,6 @@ uielement_t UI_ELEMENTS[] = { .dispose = uiConfirmDispose }, - { - .init = uiTextboxMainInit, - .update = uiTextboxMainUpdate, - .draw = uiTextboxMainDraw - }, - - { - .init = uiTextboxMiniListInit, - .update = uiTextboxMiniListUpdate, - .draw = uiTextboxMiniListDraw - }, - { .init = uiTransitionInit, .update = uiTransitionUpdate, @@ -111,7 +72,6 @@ uielement_t UI_ELEMENTS[] = { // Debug items { .draw = uiConsoleDraw, .dispose = uiConsoleDispose }, { .draw = uiFPSDraw }, - { .draw = uiPlayerPosDraw }, { 0 } // Null terminator }; diff --git a/src/dusk/ui/widget/CMakeLists.txt b/src/dusk/ui/widget/CMakeLists.txt index 2e3eaf06..77cd1ebd 100644 --- a/src/dusk/ui/widget/CMakeLists.txt +++ b/src/dusk/ui/widget/CMakeLists.txt @@ -11,7 +11,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} uislider.c uidropdown.c uiscrolling.c - uiitem.c - uiitemlist.c uimenu.c ) diff --git a/src/dusk/ui/widget/uiitem.c b/src/dusk/ui/widget/uiitem.c deleted file mode 100644 index 9e73b676..00000000 --- a/src/dusk/ui/widget/uiitem.c +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "uiitem.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "util/string.h" -#include "display/text/text.h" -#include "display/color.h" - -#define UI_ITEM_LABEL_MAX 48 - -errorret_t uiItemInit( - uiitem_t *item, - const itemid_t itemId, - const uint8_t quantity -) { - assertNotNull(item, "Item cannot be NULL"); - memoryZero(item, sizeof(uiitem_t)); - item->item = itemId; - item->quantity = quantity; - - if(itemId == ITEM_ID_NULL) errorOk(); - - errorChain(itemGetName(itemId, item->nameLabel, UI_ITEM_NAME_LABEL_MAX)); - - errorOk(); -} - -itemid_t uiItemGetItem(const uiitem_t *item) { - assertNotNull(item, "Item cannot be NULL"); - return item->item; -} - -uint8_t uiItemGetQuantity(const uiitem_t *item) { - assertNotNull(item, "Item cannot be NULL"); - return item->quantity; -} - -void uiItemSetQuantity(uiitem_t *item, const uint8_t quantity) { - assertNotNull(item, "Item cannot be NULL"); - item->quantity = quantity; -} - -bool_t uiItemIsHighlighted(const uiitem_t *item) { - assertNotNull(item, "Item cannot be NULL"); - return item->highlighted; -} - -void uiItemSetHighlighted(uiitem_t *item, const bool_t highlighted) { - assertNotNull(item, "Item cannot be NULL"); - item->highlighted = highlighted; -} - -errorret_t uiItemDraw( - const uiitem_t *item, - const float_t x, - const float_t y -) { - assertNotNull(item, "Item cannot be NULL"); - if(item->item == ITEM_ID_NULL) errorOk(); - - const color_t color = item->highlighted ? COLOR_RED : COLOR_WHITE; - - char_t text[UI_ITEM_LABEL_MAX]; - stringFormat( - text, UI_ITEM_LABEL_MAX - 1, - "%s x%u", item->nameLabel, item->quantity - ); - - errorChain(textDraw(x, y, text, color, &FONT_DEFAULT)); - errorOk(); -} diff --git a/src/dusk/ui/widget/uiitem.h b/src/dusk/ui/widget/uiitem.h deleted file mode 100644 index 787884c8..00000000 --- a/src/dusk/ui/widget/uiitem.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" -#include "rpg/item/item.h" - -#define UI_ITEM_NAME_LABEL_MAX 32 - -typedef struct { - itemid_t item; - uint8_t quantity; - bool_t highlighted; - char_t nameLabel[UI_ITEM_NAME_LABEL_MAX]; -} uiitem_t; - -/** - * Initializes an item widget, resolving the item's localized name. - * - * @param item The item widget to initialize. - * @param itemId The item ID to display. ITEM_ID_NULL renders as an - * empty slot. - * @param quantity The stack quantity to display. - * @return Any error that occurs. - */ -errorret_t uiItemInit( - uiitem_t *item, - const itemid_t itemId, - const uint8_t quantity -); - -/** - * Returns the item ID this widget displays. - * - * @param item The item widget to query. - * @returns The item ID. - */ -itemid_t uiItemGetItem(const uiitem_t *item); - -/** - * Returns the stack quantity this widget displays. - * - * @param item The item widget to query. - * @returns The quantity. - */ -uint8_t uiItemGetQuantity(const uiitem_t *item); - -/** - * Sets the stack quantity this widget displays. - * - * @param item The item widget to update. - * @param quantity The new quantity. - */ -void uiItemSetQuantity(uiitem_t *item, const uint8_t quantity); - -/** - * Returns whether the item widget is highlighted. - * - * @param item The item widget to query. - * @returns True if highlighted. - */ -bool_t uiItemIsHighlighted(const uiitem_t *item); - -/** - * Sets the highlighted state of the item widget. - * - * @param item The item widget to update. - * @param highlighted The new highlighted state. - */ -void uiItemSetHighlighted(uiitem_t *item, const bool_t highlighted); - -/** - * Draws the item widget at the given screen position: item name and - * quantity. No-op for an empty (ITEM_ID_NULL) slot. - * - * @param item The item widget to draw. - * @param x Screen x position. - * @param y Screen y position. - * @return Any error that occurs. - */ -errorret_t uiItemDraw( - const uiitem_t *item, - const float_t x, - const float_t y -); diff --git a/src/dusk/ui/widget/uiitemlist.c b/src/dusk/ui/widget/uiitemlist.c deleted file mode 100644 index 2ab2f62a..00000000 --- a/src/dusk/ui/widget/uiitemlist.c +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "uiitemlist.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "display/text/text.h" - -void uiItemListInit( - uiitemlist_t *list, - const uint8_t columns, - const uint8_t rows, - const uint8_t itemColumns, - uiitemlistselectedcallback_t selected, - uiitemlistchangedcallback_t changed, - uiitemlistclosedcallback_t closed, - uiitemlistcolumncallback_t columnDraw -) { - assertNotNull(list, "Item list cannot be NULL"); - assertTrue(columns > 0, "Item list columns must be > 0"); - assertTrue(rows > 0, "Item list rows must be > 0"); - assertTrue(itemColumns > 0, "Item list itemColumns must be > 0"); - - memoryZero(list, sizeof(uiitemlist_t)); - list->columns = columns; - list->rows = rows; - list->itemColumns = itemColumns; - list->selected = selected; - list->changed = changed; - list->closed = closed; - list->columnDraw = columnDraw; - - uiScrollingInit(&list->scroll); -} - -void uiItemListSetItems( - uiitemlist_t *list, - const uiitem_t *items, - const uint8_t itemCount -) { - assertNotNull(list, "Item list cannot be NULL"); - assertTrue( - itemCount <= UI_ITEM_LIST_CAPACITY_MAX, "Too many items for list" - ); - - memoryCopy(list->items, items, sizeof(uiitem_t) * itemCount); - list->itemCount = itemCount; -} - -errorret_t uiItemListSetItemStacks( - uiitemlist_t *list, - const inventorystack_t *stacks, - const uint8_t stackCount -) { - assertNotNull(list, "Item list cannot be NULL"); - assertTrue( - stackCount <= UI_ITEM_LIST_CAPACITY_MAX, "Too many items for list" - ); - - for(uint8_t i = 0; i < stackCount; i++) { - errorChain( - uiItemInit(&list->items[i], stacks[i].item, stacks[i].quantity) - ); - } - list->itemCount = stackCount; - - errorOk(); -} - -void uiItemListOpen(uiitemlist_t *list) { - assertNotNull(list, "Item list cannot be NULL"); - if(list->focusItem != NULL) return; - - list->focusItem = uiFocusPush( - list->columns, list->rows, - uiItemListFocusSelected, - uiItemListFocusChanged, - uiItemListFocusClosed, - NULL, - list - ); -} - -void uiItemListClose(uiitemlist_t *list) { - assertNotNull(list, "Item list cannot be NULL"); - if(list->focusItem == NULL) return; - uiFocusPopItem(list->focusItem); - list->focusItem = NULL; -} - -bool_t uiItemListIsActive(const uiitemlist_t *list) { - assertNotNull(list, "Item list cannot be NULL"); - return list->focusItem != NULL; -} - -errorret_t uiItemListDraw( - const uiitemlist_t *list, - const float_t x, - const float_t y, - const float_t width -) { - assertNotNull(list, "Item list cannot be NULL"); - if(list->itemCount == 0) errorOk(); - - const float_t colStep = width / (float_t)list->columns; - const float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight; - const float_t itemColStep = colStep / (float_t)list->itemColumns; - - const uint8_t visibleMax = list->columns * list->rows; - const uint8_t drawCount = - list->itemCount < visibleMax ? list->itemCount : visibleMax; - - for(uint8_t i = 0; i < drawCount; i++) { - const uint8_t col = i % list->columns; - const uint8_t row = i / list->columns; - - const float_t ix = x + (float_t)col * colStep; - const float_t iy = y + (float_t)row * rowHeight; - - errorChain(uiItemDraw(&list->items[i], ix, iy)); - - for(uint8_t c = 1; c < list->itemColumns; c++) { - if(list->columnDraw == NULL) continue; - errorChain(list->columnDraw( - list, &list->items[i], c, ix + (float_t)c * itemColStep, iy - )); - } - } - - errorOk(); -} - -bool_t uiItemListFocusSelected(const uifocusitem_t *focusItem) { - assertNotNull(focusItem, "Focus item cannot be NULL"); - assertNotNull(focusItem->user, "Focus item user cannot be NULL"); - uiitemlist_t *list = (uiitemlist_t *)focusItem->user; - if(list->selected == NULL) return true; - - const uint8_t index = focusItem->y * list->columns + focusItem->x; - if(index >= list->itemCount) return true; - - list->selected(list, index, &list->items[index]); - return true; -} - -bool_t uiItemListFocusChanged(const uifocusitem_t *focusItem) { - assertNotNull(focusItem, "Focus item cannot be NULL"); - assertNotNull(focusItem->user, "Focus item user cannot be NULL"); - uiitemlist_t *list = (uiitemlist_t *)focusItem->user; - - const uint8_t focusIndex = focusItem->y * list->columns + focusItem->x; - for(uint8_t i = 0; i < list->itemCount; i++) { - uiItemSetHighlighted(&list->items[i], i == focusIndex); - } - - if(list->changed == NULL) return true; - if(focusIndex >= list->itemCount) return true; - - list->changed(list, focusIndex, &list->items[focusIndex]); - return true; -} - -bool_t uiItemListFocusClosed(const uifocusitem_t *focusItem) { - assertNotNull(focusItem, "Focus item cannot be NULL"); - assertNotNull(focusItem->user, "Focus item user cannot be NULL"); - uiitemlist_t *list = (uiitemlist_t *)focusItem->user; - list->focusItem = NULL; - - for(uint8_t i = 0; i < list->itemCount; i++) { - uiItemSetHighlighted(&list->items[i], false); - } - - if(list->closed != NULL) list->closed(list); - return true; -} diff --git a/src/dusk/ui/widget/uiitemlist.h b/src/dusk/ui/widget/uiitemlist.h deleted file mode 100644 index 26b3e350..00000000 --- a/src/dusk/ui/widget/uiitemlist.h +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" -#include "ui/widget/uiitem.h" -#include "ui/widget/uiscrolling.h" -#include "ui/focus/uifocus.h" -#include "rpg/item/inventory.h" - -#define UI_ITEM_LIST_CAPACITY_MAX 40 - -typedef struct uiitemlist_s uiitemlist_t; - -typedef void (*uiitemlistselectedcallback_t)( - const uiitemlist_t *list, - const uint8_t index, - const uiitem_t *item -); - -typedef void (*uiitemlistchangedcallback_t)( - const uiitemlist_t *list, - const uint8_t index, - const uiitem_t *item -); - -typedef void (*uiitemlistclosedcallback_t)(const uiitemlist_t *list); - -/** - * Called to draw an extra per-item info column (columnIndex >= 1) - * beyond the item's own default uiItemDraw rendering -- e.g. a shop - * menu drawing a price alongside the item name. - * - * @param list The item list being drawn. - * @param item The item slot being drawn. - * @param columnIndex The info column being drawn (>= 1). - * @param x Screen x position for this column. - * @param y Screen y position for this column. - * @return Any error that occurs. - */ -typedef errorret_t (*uiitemlistcolumncallback_t)( - const uiitemlist_t *list, - const uiitem_t *item, - const uint8_t columnIndex, - const float_t x, - const float_t y -); - -struct uiitemlist_s { - uiitem_t items[UI_ITEM_LIST_CAPACITY_MAX]; - uint8_t itemCount; - - // Grid layout: how many item slots wide/tall the list displays. - uint8_t columns; - uint8_t rows; - - // How many info columns are rendered per item slot. 1 means only the - // item's own default name/quantity is drawn; anything beyond that is - // rendered via columnDraw. - uint8_t itemColumns; - - uiscrolling_t scroll; - uifocusitem_t *focusItem; - - uiitemlistselectedcallback_t selected; - uiitemlistchangedcallback_t changed; - uiitemlistclosedcallback_t closed; - uiitemlistcolumncallback_t columnDraw; - - void *user; -}; - -/** - * Initializes an item list. - * - * @param list The item list to initialize. - * @param columns Number of item slots per row. - * @param rows Number of item slot rows. - * @param itemColumns Number of info columns rendered per item slot. - * @param selected Called when an item slot is selected. - * @param changed Called when the focused item slot changes. - * @param closed Called when the item list is closed. - * @param columnDraw Called to draw each extra info column (index >= 1); - * may be NULL if itemColumns is 1. - */ -void uiItemListInit( - uiitemlist_t *list, - const uint8_t columns, - const uint8_t rows, - const uint8_t itemColumns, - uiitemlistselectedcallback_t selected, - uiitemlistchangedcallback_t changed, - uiitemlistclosedcallback_t closed, - uiitemlistcolumncallback_t columnDraw -); - -/** - * Sets the items displayed by the list directly, copying them into - * the list's own storage. - * - * @param list The item list to update. - * @param items The items to display. - * @param itemCount Number of entries in items. Must be <= - * UI_ITEM_LIST_CAPACITY_MAX. - */ -void uiItemListSetItems( - uiitemlist_t *list, - const uiitem_t *items, - const uint8_t itemCount -); - -/** - * Sets the items displayed by the list from an array of item stacks, - * internally creating a uiitem_t for each stack. - * - * @param list The item list to update. - * @param stacks The item stacks to display. - * @param stackCount Number of entries in stacks. Must be <= - * UI_ITEM_LIST_CAPACITY_MAX. - * @return Any error that occurs. - */ -errorret_t uiItemListSetItemStacks( - uiitemlist_t *list, - const inventorystack_t *stacks, - const uint8_t stackCount -); - -/** - * Pushes the item list onto the UI focus stack, making it navigable. - * No-op if already open. - * - * @param list The item list to open. - */ -void uiItemListOpen(uiitemlist_t *list); - -/** - * Pops the item list from the UI focus stack. No-op if already closed. - * - * @param list The item list to close. - */ -void uiItemListClose(uiitemlist_t *list); - -/** - * Returns whether the item list is currently on the UI focus stack. - * - * @param list The item list to query. - * @returns True if active. - */ -bool_t uiItemListIsActive(const uiitemlist_t *list); - -/** - * Draws the item list's grid of item slots at the given position. - * Only the first columns * rows items are drawn. - * - * @param list The item list to draw. - * @param x Screen x position. - * @param y Screen y position. - * @param width Content width, used to lay out columns. - * @return Any error that occurs. - */ -errorret_t uiItemListDraw( - const uiitemlist_t *list, - const float_t x, - const float_t y, - const float_t width -); - -/** - * Internal focus callback -- forwards selection to the list's selected - * handler. - * - * @param focusItem The active focus item; user field must point to - * uiitemlist_t. - * @returns True. - */ -bool_t uiItemListFocusSelected(const uifocusitem_t *focusItem); - -/** - * Internal focus callback -- updates item highlights and fires - * changed. - * - * @param focusItem The active focus item; user field must point to - * uiitemlist_t. - * @returns True. - */ -bool_t uiItemListFocusChanged(const uifocusitem_t *focusItem); - -/** - * Internal focus callback -- clears focusItem and fires the closed - * handler. - * - * @param focusItem The active focus item; user field must point to - * uiitemlist_t. - * @returns True. - */ -bool_t uiItemListFocusClosed(const uifocusitem_t *focusItem);