unfinished ui textbox

This commit is contained in:
2025-06-21 23:14:24 -05:00
parent 907d3234a9
commit 0ed65d3703
22 changed files with 553 additions and 11 deletions

View File

@ -1,5 +1,5 @@
{ {
"activeFile": "overworld.tsx", "activeFile": "map.tmj",
"expandedProjectPaths": [ "expandedProjectPaths": [
".", ".",
"templates" "templates"
@ -14,7 +14,7 @@
}, },
"map.tmj": { "map.tmj": {
"scale": 3, "scale": 3,
"selectedLayer": 0, "selectedLayer": 2,
"viewCenter": { "viewCenter": {
"x": 6520.166666666666, "x": 6520.166666666666,
"y": 6836.833333333333 "y": 6836.833333333333
@ -35,8 +35,8 @@
"project": "map project.tiled-project", "project": "map project.tiled-project",
"property.type": "int", "property.type": "int",
"recentFiles": [ "recentFiles": [
"map.tmj",
"overworld.tsx", "overworld.tsx",
"map.tmj",
"entities.tsx" "entities.tsx"
], ],
"tileset.lastUsedFilter": "Tiled tileset files (*.tsx *.xml)", "tileset.lastUsedFilter": "Tiled tileset files (*.tsx *.xml)",

View File

@ -425,14 +425,14 @@
"type":"player_spawn", "type":"player_spawn",
"visible":true, "visible":true,
"width":16, "width":16,
"x":6607.58333333333, "x":6384.58333333333,
"y":6944.25 "y":6846.91666666667
}, },
{ {
"id":7, "id":7,
"template":"templates\/NPC.tx", "template":"templates\/NPC.tx",
"x":6700.25, "x":6383.25,
"y":6838.75 "y":6880.08333333333
}, },
{ {
"id":8, "id":8,

View File

@ -25,6 +25,8 @@ target_sources(${DUSK_TARGET_NAME}
add_subdirectory(assert) add_subdirectory(assert)
add_subdirectory(display) add_subdirectory(display)
add_subdirectory(entity) add_subdirectory(entity)
add_subdirectory(item)
add_subdirectory(physics) add_subdirectory(physics)
add_subdirectory(ui)
add_subdirectory(util) add_subdirectory(util)
add_subdirectory(world) add_subdirectory(world)

View File

@ -15,4 +15,5 @@
typedef bool bool_t; typedef bool bool_t;
typedef int int_t; typedef int int_t;
typedef float float_t; typedef float float_t;
typedef char char_t;

View File

@ -7,6 +7,8 @@
#include "npc.h" #include "npc.h"
#include "ui/uitextbox.h"
void npcInit(entity_t *entity) { void npcInit(entity_t *entity) {
} }
@ -16,5 +18,5 @@ void npcUpdate(entity_t *entity) {
} }
void npcInteract(entity_t *player, entity_t *self) { void npcInteract(entity_t *player, entity_t *self) {
printf("I am being interacted with!\n"); uiTextboxSetText("Hello World");
} }

View File

@ -12,6 +12,10 @@
#include "world/world.h" #include "world/world.h"
#include "physics/physics.h" #include "physics/physics.h"
#include "ui/uitextbox.h"
inventory_t PLAYER_INVENTORY;
void playerInit() { void playerInit() {
entity_t *ent = &ENTITIES[0]; entity_t *ent = &ENTITIES[0];
@ -20,6 +24,8 @@ void playerInit() {
ent->x = WORLD_PLAYER_SPAWN_X; ent->x = WORLD_PLAYER_SPAWN_X;
ent->y = WORLD_PLAYER_SPAWN_Y; ent->y = WORLD_PLAYER_SPAWN_Y;
inventoryInit(&PLAYER_INVENTORY, INVENTORY_SIZE_MAX);
} }
void playerNPCInit(entity_t *entity) { void playerNPCInit(entity_t *entity) {
@ -31,6 +37,11 @@ void playerNPCUpdate(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL"); assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity type must be PLAYER"); assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity type must be PLAYER");
if(UI_TEXTBOX.visible) {
entity->vx = entity->vy = 0;
return;
}
if(inputIsDown(INPUT_BIND_UP)) { if(inputIsDown(INPUT_BIND_UP)) {
if(inputIsDown(INPUT_BIND_LEFT)) { if(inputIsDown(INPUT_BIND_LEFT)) {
entity->vx = -PLAYER_MOVE_SPEED_XY; entity->vx = -PLAYER_MOVE_SPEED_XY;

View File

@ -8,6 +8,7 @@
#pragma once #pragma once
#include "dusk.h" #include "dusk.h"
#include "util/fixed.h" #include "util/fixed.h"
#include "item/inventory.h"
typedef struct _entity_t entity_t; typedef struct _entity_t entity_t;
@ -23,6 +24,8 @@ typedef struct {
) )
#define PLAYER_INTERACT_ANGLE ((fixed248_t)175) #define PLAYER_INTERACT_ANGLE ((fixed248_t)175)
extern inventory_t PLAYER_INVENTORY;
/** /**
* Initializes the player and all player-related entities. * Initializes the player and all player-related entities.
*/ */

View File

@ -0,0 +1,10 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
inventory.c
)

68
src/dusk/item/inventory.c Normal file
View File

@ -0,0 +1,68 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "inventory.h"
#include "util/memory.h"
#include "assert/assert.h"
void inventoryInit(inventory_t *inventory, const uint8_t size) {
assertNotNull(inventory, "inventory must not be NULL");
assertTrue(size <= INVENTORY_SIZE_MAX, "size exceeding INVENTORY_SIZE_MAX");
assertTrue(size > 0, "size must be greater than 0");
memoryZero(inventory, sizeof(inventory_t));
inventory->size = size;
}
uint8_t inventoryItemIndexByType(
const inventory_t *inventory,
const itemtype_t type
) {
assertNotNull(inventory, "inventory must not be NULL");
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
uint8_t item = inventory->itemCount;
while(item--) {
if(inventory->items[item].type == type) return item;
}
return INVENTORY_SIZE_MAX;
}
uint8_t inventoryItemCount(
const inventory_t *inventory,
const itemtype_t type
) {
assertNotNull(inventory, "inventory must not be NULL");
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
const uint8_t index = inventoryItemIndexByType(inventory, type);
if(index == INVENTORY_SIZE_MAX) return 0;
return inventory->items[index].count;
}
void inventoryItemSet(
inventory_t *inventory,
const itemtype_t type,
const uint8_t count
) {
assertNotNull(inventory, "inventory must not be NULL");
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
assertTrue(count > 0, "count must be greater than 0");
const uint8_t index = inventoryItemIndexByType(inventory, type);
if(index == INVENTORY_SIZE_MAX) {
// Item does not exist, add it
assertTrue(inventory->itemCount < inventory->size, "inventory is full");
inventory->items[inventory->itemCount].type = type;
inventory->items[inventory->itemCount].count = count;
inventory->itemCount++;
} else {
// Item exists, update the count
inventory->items[index].count = count;
}
}

63
src/dusk/item/inventory.h Normal file
View File

@ -0,0 +1,63 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "itemstack.h"
#define INVENTORY_SIZE_MAX UINT8_MAX
typedef struct {
itemstack_t items[INVENTORY_SIZE_MAX];
uint8_t itemCount;
uint8_t size;
} inventory_t;
/**
* Initializes an inventory with a specified size.
*
* @param inventory Pointer to the inventory to initialize.
* @param size The size of the inventory (maximum is INVENTORY_SIZE_MAX).
*/
void inventoryInit(inventory_t *inventory, const uint8_t size);
/**
* Finds the index of the item of a specified type in the inventory.
*
* @param inventory Pointer to the inventory to search.
* @param type The type of item to find.
* @return The index of the item, or INVENTORY_SIZE_MAX if not found.
*/
uint8_t inventoryItemIndexByType(
const inventory_t *inventory,
const itemtype_t type
);
/**
* Gets the count of items of a specified type in the inventory.
*
* @param inventory Pointer to the inventory to check.
* @param type The type of item to count.
* @return The count of items of the specified type.
*/
uint8_t inventoryItemCount(
const inventory_t *inventory,
const itemtype_t type
);
/**
* Sets the count of items of a specified type in the inventory.
* If the item does not exist, it will be added.
*
* @param inventory Pointer to the inventory to modify.
* @param type The type of item to set.
* @param count The count of items to set.
*/
void inventoryItemSet(
inventory_t *inventory,
const itemtype_t type,
const uint8_t count
);

14
src/dusk/item/itemstack.h Normal file
View File

@ -0,0 +1,14 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "itemtype.h"
typedef struct {
itemtype_t type;
uint8_t count;
} itemstack_t;

24
src/dusk/item/itemtype.h Normal file
View File

@ -0,0 +1,24 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef enum {
ITEM_TYPE_NULL = 0,
// MEDICINE
ITEM_TYPE_POTION,
// INGREDIENTS
ITEM_TYPE_ONION,
ITEM_TYPE_SWEET_POTATO,
ITEM_TYPE_CARROT,
// COOKED FOOD
ITEM_TYPE_BAKED_SWEET_POTATO,
} itemtype_t;

View File

@ -12,10 +12,13 @@
#include "world/overworld.h" #include "world/overworld.h"
#include "input.h" #include "input.h"
#include "ui/uitextbox.h"
// Press F5 to compile and run the compiled game.gb in the Emulicous Emulator/Debugger // Press F5 to compile and run the compiled game.gb in the Emulicous Emulator/Debugger
void main(void) { void main(void) {
renderInit(); renderInit();
inputInit(); inputInit();
uiTextboxInit();
overworldInit(); overworldInit();
@ -27,6 +30,8 @@ void main(void) {
overworldUpdate(); overworldUpdate();
uiTextboxUpdate();
// Update input for next frame. // Update input for next frame.
inputUpdate(); inputUpdate();
} }

View File

@ -0,0 +1,10 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
uitextbox.c
)

18
src/dusk/ui/font.h Normal file
View File

@ -0,0 +1,18 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#define FONT_HEIGHT 8
#define FONT_LINE_SPACING 1
#define FONT_LINE_HEIGHT ( \
FONT_HEIGHT + FONT_LINE_SPACING \
)
//

128
src/dusk/ui/uitextbox.c Normal file
View File

@ -0,0 +1,128 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uitextbox.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "input.h"
uitextbox_t UI_TEXTBOX;
void uiTextboxInit() {
memoryZero(&UI_TEXTBOX, sizeof(uitextbox_t));
}
void uiTextboxUpdate() {
if(UI_TEXTBOX.visible == false) return;
if(UI_TEXTBOX.charsRevealed < UI_TEXTBOX.pageChars[UI_TEXTBOX.page]) {
if((RENDER_FRAME % 4) == 0) {
// printf("Revealing char\n");
UI_TEXTBOX.charsRevealed++;
} else if(inputIsDown(INPUT_BIND_ACTION)) {
UI_TEXTBOX.charsRevealed++;
}
} else {
if(inputPressed(INPUT_BIND_ACTION)) {
if(UI_TEXTBOX.page <= UI_TEXTBOX.pageCount - 1) {
uitextbox_t *textbox = &UI_TEXTBOX;
printf("next page?\n");
UI_TEXTBOX.page++;
UI_TEXTBOX.charsRevealed = 0;
} else {
printf("Closing textbox\n");
// Close the textbox
UI_TEXTBOX.visible = false;
UI_TEXTBOX.page = 0;
UI_TEXTBOX.charsRevealed = 0;
}
}
}
}
void uiTextboxSetText(const char_t *text) {
assertNotNull(text, "Text pointer cannot be NULL, call uiTextboxClose()");
memoryZero(UI_TEXTBOX.text, sizeof(UI_TEXTBOX.text));
memoryZero(UI_TEXTBOX.lineLengths, sizeof(UI_TEXTBOX.lineLengths));
memoryZero(UI_TEXTBOX.pageChars, sizeof(UI_TEXTBOX.pageChars));
UI_TEXTBOX.pageCount = 0;
UI_TEXTBOX.totalChars = 0;
UI_TEXTBOX.page = 0;
UI_TEXTBOX.charsRevealed = 0;
UI_TEXTBOX.visible = true;
char_t c;
uint16_t i = 0;
uint16_t j = 0;
uint8_t line = 0;
while((c = text[i++]) != '\0') {
assertTrue(c != '\r', "Carriage return characters not allowed.");
assertTrue(c != '\t', "Tab characters not allowed.");
if(c == '\n') {
goto newline;
} else {
goto character;
}
newline: {
line++;
if(line >= UI_TEXTBOX_LINE_COUNT) {
goto newPage;
}
continue;
}
character: {
UI_TEXTBOX.text[j] = c;
UI_TEXTBOX.totalChars++;
UI_TEXTBOX.lineLengths[line]++;
UI_TEXTBOX.pageChars[UI_TEXTBOX.pageCount]++;
}
newPage: {
assertTrue(
UI_TEXTBOX.pageCount < UI_TEXTBOX_PAGE_COUNT_MAX,
"Exceeded maximum number of pages in textbox."
);
UI_TEXTBOX.pageCount++;
line = 0;
continue;
// j = 0; // Reset character index for new page
}
assertUnreachable("Code should not reach here, all cases handled.");
}
// Hard coded testing values
// char_t buffer[UI_TEXTBOX_CHARS_PER_LINE];
// uint8_t line;
// for(line = 0; line < UI_TEXTBOX_LINE_COUNT; line++) {
// // Set the text for each line
// sprintf(buffer, "HC Line %d", line);
// UI_TEXTBOX.lineLengths[line] = strlen(text); // Set line length for each line
// UI_TEXTBOX.totalChars += UI_TEXTBOX.lineLengths[line];
// UI_TEXTBOX.pageChars[UI_TEXTBOX.pageCount] += UI_TEXTBOX.lineLengths[line];
// memoryCopy(
// UI_TEXTBOX.text + line * (UI_TEXTBOX_CHARS_PER_LINE),
// buffer,
// UI_TEXTBOX.lineLengths[line]
// );
// if(line == 3 || line == 7 || line == 11 || line == 15) {
// // Increment page count every 4 lines
// UI_TEXTBOX.pageCount++;
// }
// }
}

70
src/dusk/ui/uitextbox.h Normal file
View File

@ -0,0 +1,70 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/renderbase.h"
#include "ui/font.h"
#define UI_TEXTBOX_LINES_PER_PAGE 4
#define UI_TEXTBOX_WIDTH RENDER_WIDTH
#define UI_TEXTBOX_HEIGHT_INNER ( \
FONT_LINE_HEIGHT * UI_TEXTBOX_LINES_PER_PAGE \
)
#define UI_TEXTBOX_BORDER_WIDTH 1
#define UI_TEXTBOX_BORDER_HEIGHT 1
#define UI_TEXTBOX_PADDING_X 1
#define UI_TEXTBOX_PADDING_Y 1
#define UI_TEXTBOX_WIDTH_INNER ( \
UI_TEXTBOX_WIDTH - (UI_TEXTBOX_BORDER_WIDTH * 2) - \
(UI_TEXTBOX_PADDING_X * 2) \
)
#define UI_TEXTBOX_HEIGHT ( \
UI_TEXTBOX_HEIGHT_INNER + (UI_TEXTBOX_BORDER_HEIGHT * 2) + \
(UI_TEXTBOX_PADDING_Y * 2) \
)
#define UI_TEXTBOX_CHARS_PER_LINE UI_TEXTBOX_WIDTH_INNER
#define UI_TEXTBOX_PAGE_CHARS ( \
UI_TEXTBOX_CHARS_PER_LINE * UI_TEXTBOX_LINES_PER_PAGE \
)
#define UI_TEXTBOX_PAGE_COUNT_MAX 6
#define UI_TEXTBOX_LINE_COUNT ( \
UI_TEXTBOX_LINES_PER_PAGE * UI_TEXTBOX_PAGE_COUNT_MAX \
)
#define UI_TEXTBOX_CHARS_MAX ( \
UI_TEXTBOX_PAGE_CHARS * UI_TEXTBOX_PAGE_COUNT_MAX \
)
typedef struct {
char_t text[UI_TEXTBOX_CHARS_MAX];
uint8_t lineLengths[UI_TEXTBOX_LINE_COUNT];
uint8_t pageCount;
uint8_t pageChars[UI_TEXTBOX_PAGE_COUNT_MAX];
uint16_t totalChars;
uint8_t page;
uint8_t charsRevealed;
bool_t visible;
} uitextbox_t;
extern uitextbox_t UI_TEXTBOX;
/**
* Initializes the UI textbox.
*/
void uiTextboxInit(void);
/**
* Updates the UI textbox, handling text input and scrolling.
*/
void uiTextboxUpdate(void);
void uiTextboxSetText(
const char_t *text
);

View File

@ -8,4 +8,5 @@ target_sources(${DUSK_TARGET_NAME}
PRIVATE PRIVATE
drawscene.c drawscene.c
drawoverworld.c drawoverworld.c
drawui.c
) )

View File

@ -47,7 +47,6 @@ void drawOverworldDraw(void) {
}; };
uint8_t colorCount = sizeof(colors) / sizeof(Color); uint8_t colorCount = sizeof(colors) / sizeof(Color);
BeginBlendMode(BLEND_ALPHA_PREMULTIPLY);
do { do {
// Base layer // Base layer
for(uint8_t i = 0; i < CHUNK_TILE_COUNT; i++) { for(uint8_t i = 0; i < CHUNK_TILE_COUNT; i++) {
@ -99,7 +98,6 @@ void drawOverworldDraw(void) {
chunk++; chunk++;
} while(chunk < CHUNK_MAP.chunks + CHUNK_MAP_COUNT); } while(chunk < CHUNK_MAP.chunks + CHUNK_MAP_COUNT);
EndBlendMode();
// Entities // Entities
entity_t *entity = ENTITIES; entity_t *entity = ENTITIES;

View File

@ -0,0 +1,83 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "drawui.h"
#include "ui/uitextbox.h"
#include "util/memory.h"
#include "assert/assert.h"
void drawUIInit(void) {
}
void drawUI() {
drawUITextbox();
}
void drawUITextbox() {
if(!UI_TEXTBOX.visible) return;
// Semi-transparent dark blue
Color background = (Color){ 0, 0, 128, 128 };
DrawRectangle(
0, RENDER_HEIGHT - UI_TEXTBOX_HEIGHT,
UI_TEXTBOX_WIDTH, UI_TEXTBOX_HEIGHT,
background
);
BeginBlendMode(BLEND_ALPHA);
if(UI_TEXTBOX.charsRevealed > 0) {
char_t buffer[UI_TEXTBOX_CHARS_PER_LINE + 1];// +1 for null term
uint8_t charsRendered = 0;
// For each line
for(uint8_t i = 0; i < UI_TEXTBOX_LINES_PER_PAGE; i++) {
// Get count of chars in the line
uint8_t lineLength = UI_TEXTBOX.lineLengths[i];
if(lineLength == 0) continue;
// Determine how many chars left to render
uint8_t lineChars = UI_TEXTBOX.charsRevealed - charsRendered;
// Don't render more than in line
if(lineChars > lineLength) lineChars = lineLength;
assertTrue(lineChars > 0, "Line chars must be greater than 0");
// Update how many rendered
charsRendered += lineChars;
// Copy string from VN Textbox...
memoryCopy(
buffer,
UI_TEXTBOX.text + (UI_TEXTBOX.page * UI_TEXTBOX_PAGE_CHARS) +
(i * UI_TEXTBOX_CHARS_PER_LINE),
lineChars
);
// Null term string
buffer[lineChars] = '\0'; // Null-terminate the string
// Render the text
DrawText(
buffer,
UI_TEXTBOX_PADDING_X + UI_TEXTBOX_BORDER_WIDTH,
(
(RENDER_HEIGHT - UI_TEXTBOX_HEIGHT) +
UI_TEXTBOX_PADDING_Y + UI_TEXTBOX_BORDER_HEIGHT +
(i * FONT_LINE_HEIGHT)
),
FONT_HEIGHT,
WHITE
);
// Check if we're done rendering text
if(UI_TEXTBOX.charsRevealed - charsRendered == 0) break;
}
}
EndBlendMode();
}

View File

@ -0,0 +1,24 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskraylib.h"
/**
* Initializes the UI drawing system.
*/
void drawUIInit(void);
/**
* Draws all the UI elements to the screen in the correct order.
*/
void drawUI(void);
/**
* Draws the UI textbox to the screen.
*/
void drawUITextbox(void);

View File

@ -8,6 +8,7 @@
#include "render.h" #include "render.h"
#include "display/draw/drawscene.h" #include "display/draw/drawscene.h"
#include "display/draw/drawoverworld.h" #include "display/draw/drawoverworld.h"
#include "display/draw/drawui.h"
#include "assert/assert.h" #include "assert/assert.h"
RenderTexture2D RENDER_SCREEN_TEXTURE; RenderTexture2D RENDER_SCREEN_TEXTURE;
@ -32,14 +33,17 @@ void renderInit(void) {
RENDER_ENTITIES_TEXTURE = LoadTexture("../data/entities.png"); RENDER_ENTITIES_TEXTURE = LoadTexture("../data/entities.png");
drawOverworldInit(); drawOverworldInit();
drawUIInit();
} }
void renderDraw(void) { void renderDraw(void) {
// Draw the actual game. // Draw the actual game.
BeginBlendMode(BLEND_ALPHA_PREMULTIPLY);
BeginTextureMode(RENDER_SCREEN_TEXTURE); BeginTextureMode(RENDER_SCREEN_TEXTURE);
ClearBackground(BLACK); ClearBackground(BLACK);
drawScene(); drawScene();
drawUI();
EndTextureMode(); EndTextureMode();
@ -69,7 +73,10 @@ void renderDraw(void) {
0.0f, 0.0f,
WHITE WHITE
); );
EndDrawing(); EndDrawing();
EndBlendMode();
} }
void renderDispose(void) { void renderDispose(void) {