From db589b7d9157339954b4921b6b62a5cbd7baae10 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Sat, 8 Nov 2025 17:26:25 -0600 Subject: [PATCH] Textbox example. --- src/rpg/CMakeLists.txt | 1 + src/rpg/cutscene/item/cutsceneitem.c | 11 ++++-- src/rpg/cutscene/item/cutsceneitem.h | 3 +- src/rpg/cutscene/item/cutscenetext.h | 15 +++----- src/rpg/cutscene/scene/testcutscene.h | 6 ++-- src/rpg/entity/npc.c | 3 ++ src/rpg/rpg.c | 3 +- src/rpg/rpgtextbox.c | 39 ++++++++++++++++++++ src/rpg/rpgtextbox.h | 52 +++++++++++++++++++++++++++ src/ui/CMakeLists.txt | 1 + src/ui/ui.c | 4 +++ src/ui/uitextbox.c | 45 +++++++++++++++++++++++ src/ui/uitextbox.h | 19 ++++++++++ 13 files changed, 184 insertions(+), 18 deletions(-) create mode 100644 src/rpg/rpgtextbox.c create mode 100644 src/rpg/rpgtextbox.h create mode 100644 src/ui/uitextbox.c create mode 100644 src/ui/uitextbox.h diff --git a/src/rpg/CMakeLists.txt b/src/rpg/CMakeLists.txt index d1b7fd3..1a08b8b 100644 --- a/src/rpg/CMakeLists.txt +++ b/src/rpg/CMakeLists.txt @@ -8,6 +8,7 @@ target_sources(${DUSK_TARGET_NAME} PRIVATE rpg.c rpgcamera.c + rpgtextbox.c ) # Subdirs diff --git a/src/rpg/cutscene/item/cutsceneitem.c b/src/rpg/cutscene/item/cutsceneitem.c index 5e615f5..08d004a 100644 --- a/src/rpg/cutscene/item/cutsceneitem.c +++ b/src/rpg/cutscene/item/cutsceneitem.c @@ -12,8 +12,10 @@ void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) { switch(item->type) { case CUTSCENE_ITEM_TYPE_TEXT: { - strncpy(data->text.buffer, item->text, CUTSCENE_TEXT_BUFFER); - data->text.length = strlen(data->text.buffer); + rpgTextboxShow( + item->text.position, + item->text.text + ); break; } @@ -36,6 +38,11 @@ void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) { void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data) { switch(item->type) { + case CUTSCENE_ITEM_TYPE_TEXT: + if(rpgTextboxIsVisible()) return; + cutsceneSystemNext(); + break; + case CUTSCENE_ITEM_TYPE_WAIT: data->wait -= TIME.fixedDelta; if(data->wait <= 0) cutsceneSystemNext(); diff --git a/src/rpg/cutscene/item/cutsceneitem.h b/src/rpg/cutscene/item/cutsceneitem.h index 39b9818..9c8a3a3 100644 --- a/src/rpg/cutscene/item/cutsceneitem.h +++ b/src/rpg/cutscene/item/cutsceneitem.h @@ -27,12 +27,11 @@ typedef struct cutsceneitem_s { cutscenetext_t text; cutscenecallback_t callback; cutscenewait_t wait; - cutscenecutscene_t cutscene; + const cutscenecutscene_t cutscene; }; } cutsceneitem_t; typedef union { - cutscenetextdata_t text; cutscenewaitdata_t wait; } cutsceneitemdata_t; diff --git a/src/rpg/cutscene/item/cutscenetext.h b/src/rpg/cutscene/item/cutscenetext.h index dfb9f3c..3ad07f2 100644 --- a/src/rpg/cutscene/item/cutscenetext.h +++ b/src/rpg/cutscene/item/cutscenetext.h @@ -6,14 +6,9 @@ */ #pragma once -#include "dusk.h" +#include "rpg/rpgtextbox.h" -#define CUTSCENE_TEXT_BUFFER 256 - -typedef const char_t *cutscenetext_t; - -typedef struct cutscenetextdata_s { - char_t buffer[CUTSCENE_TEXT_BUFFER]; - uint8_t length; - uint8_t scroll; -} cutscenetextdata_t; \ No newline at end of file +typedef struct { + char_t text[RPG_TEXTBOX_MAX_CHARS]; + rpgtextboxpos_t position; +} cutscenetext_t; \ No newline at end of file diff --git a/src/rpg/cutscene/scene/testcutscene.h b/src/rpg/cutscene/scene/testcutscene.h index 9bf776f..bcdffb2 100755 --- a/src/rpg/cutscene/scene/testcutscene.h +++ b/src/rpg/cutscene/scene/testcutscene.h @@ -9,9 +9,9 @@ #include "rpg/cutscene/cutscenesystem.h" static const cutsceneitem_t TEST_CUTSCENE_ONE_ITEMS[] = { - { .type = CUTSCENE_ITEM_TYPE_TEXT, .text = "This is a test cutscene." }, + { .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "This is a test cutscene.", .position = RPG_TEXTBOX_POS_BOTTOM } }, { .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 2.0f }, - { .type = CUTSCENE_ITEM_TYPE_TEXT, .text = "It has multiple lines of text.\nAnd waits in between." }, + { .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "It has multiple lines of text.\nAnd waits in between.", .position = RPG_TEXTBOX_POS_TOP } }, }; static const cutscene_t TEST_CUTSCENE_ONE = { @@ -21,7 +21,7 @@ static const cutscene_t TEST_CUTSCENE_ONE = { static const cutsceneitem_t TEST_CUTSCENE_TWO_ITEMS[] = { { .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f }, - // { .type = CUTSCENE_ITEM_TYPE_CUTSCENE, .cutscene = &TEST_CUTSCENE_ONE }, + { .type = CUTSCENE_ITEM_TYPE_CUTSCENE, .cutscene = &TEST_CUTSCENE_ONE }, }; static const cutscene_t TEST_CUTSCENE = { diff --git a/src/rpg/entity/npc.c b/src/rpg/entity/npc.c index 3ab78b5..ec6548b 100644 --- a/src/rpg/entity/npc.c +++ b/src/rpg/entity/npc.c @@ -9,6 +9,7 @@ #include "assert/assert.h" #include "rpg/cutscene/scene/testcutscene.h" +#include "rpg/rpgtextbox.h" void npcInit(entity_t *entity) { assertNotNull(entity, "Entity pointer cannot be NULL"); @@ -23,6 +24,8 @@ bool_t npcInteract(entity_t *player, entity_t *npc) { assertNotNull(npc, "NPC entity pointer cannot be NULL"); cutsceneSystemStartCutscene(&TEST_CUTSCENE); + + // rpgTextboxShow(RPG_TEXTBOX_POS_BOTTOM, "Hello World!"); return false; }; \ No newline at end of file diff --git a/src/rpg/rpg.c b/src/rpg/rpg.c index 1d6b5cc..e6aeace 100644 --- a/src/rpg/rpg.c +++ b/src/rpg/rpg.c @@ -11,6 +11,7 @@ #include "rpg/cutscene/cutscenesystem.h" #include "time/time.h" #include "rpgcamera.h" +#include "rpgtextbox.h" #include "util/memory.h" errorret_t rpgInit(void) { @@ -22,8 +23,8 @@ errorret_t rpgInit(void) { // Init the world. worldInit(); - // Initialize the camera rpgCameraInit(); + rpgTextboxInit(); // TEST: Create some entities. entity_t *ent; diff --git a/src/rpg/rpgtextbox.c b/src/rpg/rpgtextbox.c new file mode 100644 index 0000000..6fba985 --- /dev/null +++ b/src/rpg/rpgtextbox.c @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "rpgtextbox.h" +#include "util/memory.h" +#include "util/string.h" +#include "assert/assert.h" + +rpgtextbox_t RPG_TEXTBOX; + +void rpgTextboxInit() { + memoryZero(&RPG_TEXTBOX, sizeof(rpgtextbox_t)); +} + +void rpgTextboxShow( + const rpgtextboxpos_t position, + const char_t *text +) { + RPG_TEXTBOX.position = position; + RPG_TEXTBOX.visible = true; + + stringCopy( + RPG_TEXTBOX.text, + text, + RPG_TEXTBOX_MAX_CHARS + ); +} + +void rpgTextboxHide() { + RPG_TEXTBOX.visible = false; +} + +bool_t rpgTextboxIsVisible() { + return RPG_TEXTBOX.visible; +} \ No newline at end of file diff --git a/src/rpg/rpgtextbox.h b/src/rpg/rpgtextbox.h new file mode 100644 index 0000000..02e8cd9 --- /dev/null +++ b/src/rpg/rpgtextbox.h @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +#define RPG_TEXTBOX_MAX_CHARS 256 + +typedef enum { + RPG_TEXTBOX_POS_TOP, + RPG_TEXTBOX_POS_BOTTOM, +} rpgtextboxpos_t; + +typedef struct { + rpgtextboxpos_t position; + bool_t visible; + char_t text[RPG_TEXTBOX_MAX_CHARS]; +} rpgtextbox_t; + +extern rpgtextbox_t RPG_TEXTBOX; + +/** + * Initializes the RPG textbox. + */ +void rpgTextboxInit(); + +/** + * Shows the RPG textbox at a specified position. + * + * @param position The position to show the textbox at. + * @param text The text to display in the textbox (copied). + */ +void rpgTextboxShow( + const rpgtextboxpos_t position, + const char_t *text +); + +/** + * Hides the RPG textbox. + */ +void rpgTextboxHide(); + +/** + * Checks if the RPG textbox is currently visible. + * + * @return true if the textbox is visible, false otherwise. + */ +bool_t rpgTextboxIsVisible(); \ No newline at end of file diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 482d62e..a877b34 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -10,4 +10,5 @@ target_sources(${DUSK_TARGET_NAME} uitext.c uifps.c uiframe.c + uitextbox.c ) \ No newline at end of file diff --git a/src/ui/ui.c b/src/ui/ui.c index 6ef2c6e..44c9aa0 100644 --- a/src/ui/ui.c +++ b/src/ui/ui.c @@ -11,6 +11,7 @@ #include "util/memory.h" #include "display/tileset/tileset_minogram.h" #include "display/screen.h" +#include "ui/uitextbox.h" ui_t UI; @@ -31,6 +32,8 @@ void uiUpdate(void) { UI.camera.orthographic.right = SCREEN.width; UI.camera.orthographic.top = 0; UI.camera.orthographic.bottom = SCREEN.height; + + uiTextboxUpdate(); } void uiRender(void) { @@ -39,6 +42,7 @@ void uiRender(void) { // Render UI elements here if(UI.fontTexture.width > 0) { uiFPSRender(UI.fontTileset, &UI.fontTexture); + uiTextboxRender(); } cameraPopMatrix(); diff --git a/src/ui/uitextbox.c b/src/ui/uitextbox.c new file mode 100644 index 0000000..47b0a87 --- /dev/null +++ b/src/ui/uitextbox.c @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "uitextbox.h" +#include "ui/ui.h" +#include "ui/uitext.h" +#include "rpg/rpgtextbox.h" +#include "display/screen.h" +#include "display/spritebatch.h" +#include "input/input.h" + +void uiTextboxUpdate() { + if(!rpgTextboxIsVisible()) return; + + if(inputPressed(INPUT_ACTION_ACCEPT)) { + rpgTextboxHide(); + } +} + +void uiTextboxRender() { + if(!rpgTextboxIsVisible()) return; + + const char_t *text = RPG_TEXTBOX.text; + int32_t textWidth, textHeight; + + uiTextMeasure(text, UI.fontTileset, &textWidth, &textHeight); + + float_t y = 0; + if(RPG_TEXTBOX.position == RPG_TEXTBOX_POS_BOTTOM) { + y = SCREEN.height - (float_t)textHeight; + } + + spriteBatchPush( + NULL, + 0.0f, y, + (float_t)SCREEN.width, (float_t)(y + textHeight), + COLOR_BLACK, + 0.0f, 0.0f, 1.0f, 1.0f + ); + uiTextDraw(0, y, text, COLOR_RED, UI.fontTileset, &UI.fontTexture); +} \ No newline at end of file diff --git a/src/ui/uitextbox.h b/src/ui/uitextbox.h new file mode 100644 index 0000000..09b2823 --- /dev/null +++ b/src/ui/uitextbox.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +/** + * Updates the RPG textbox state. + */ +void uiTextboxUpdate(); + +/** + * Draws the RPG textbox if it is visible. + */ +void uiTextboxRender(); \ No newline at end of file