Textbox example.

This commit is contained in:
2025-11-08 17:26:25 -06:00
parent 0a83175b66
commit db589b7d91
13 changed files with 184 additions and 18 deletions

View File

@@ -8,6 +8,7 @@ target_sources(${DUSK_TARGET_NAME}
PRIVATE PRIVATE
rpg.c rpg.c
rpgcamera.c rpgcamera.c
rpgtextbox.c
) )
# Subdirs # Subdirs

View File

@@ -12,8 +12,10 @@
void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) { void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
switch(item->type) { switch(item->type) {
case CUTSCENE_ITEM_TYPE_TEXT: { case CUTSCENE_ITEM_TYPE_TEXT: {
strncpy(data->text.buffer, item->text, CUTSCENE_TEXT_BUFFER); rpgTextboxShow(
data->text.length = strlen(data->text.buffer); item->text.position,
item->text.text
);
break; break;
} }
@@ -36,6 +38,11 @@ void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data) { void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
switch(item->type) { switch(item->type) {
case CUTSCENE_ITEM_TYPE_TEXT:
if(rpgTextboxIsVisible()) return;
cutsceneSystemNext();
break;
case CUTSCENE_ITEM_TYPE_WAIT: case CUTSCENE_ITEM_TYPE_WAIT:
data->wait -= TIME.fixedDelta; data->wait -= TIME.fixedDelta;
if(data->wait <= 0) cutsceneSystemNext(); if(data->wait <= 0) cutsceneSystemNext();

View File

@@ -27,12 +27,11 @@ typedef struct cutsceneitem_s {
cutscenetext_t text; cutscenetext_t text;
cutscenecallback_t callback; cutscenecallback_t callback;
cutscenewait_t wait; cutscenewait_t wait;
cutscenecutscene_t cutscene; const cutscenecutscene_t cutscene;
}; };
} cutsceneitem_t; } cutsceneitem_t;
typedef union { typedef union {
cutscenetextdata_t text;
cutscenewaitdata_t wait; cutscenewaitdata_t wait;
} cutsceneitemdata_t; } cutsceneitemdata_t;

View File

@@ -6,14 +6,9 @@
*/ */
#pragma once #pragma once
#include "dusk.h" #include "rpg/rpgtextbox.h"
#define CUTSCENE_TEXT_BUFFER 256 typedef struct {
char_t text[RPG_TEXTBOX_MAX_CHARS];
typedef const char_t *cutscenetext_t; rpgtextboxpos_t position;
} cutscenetext_t;
typedef struct cutscenetextdata_s {
char_t buffer[CUTSCENE_TEXT_BUFFER];
uint8_t length;
uint8_t scroll;
} cutscenetextdata_t;

View File

@@ -9,9 +9,9 @@
#include "rpg/cutscene/cutscenesystem.h" #include "rpg/cutscene/cutscenesystem.h"
static const cutsceneitem_t TEST_CUTSCENE_ONE_ITEMS[] = { 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_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 = { 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[] = { static const cutsceneitem_t TEST_CUTSCENE_TWO_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f }, { .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 = { static const cutscene_t TEST_CUTSCENE = {

View File

@@ -9,6 +9,7 @@
#include "assert/assert.h" #include "assert/assert.h"
#include "rpg/cutscene/scene/testcutscene.h" #include "rpg/cutscene/scene/testcutscene.h"
#include "rpg/rpgtextbox.h"
void npcInit(entity_t *entity) { void npcInit(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL"); assertNotNull(entity, "Entity pointer cannot be NULL");
@@ -24,5 +25,7 @@ bool_t npcInteract(entity_t *player, entity_t *npc) {
cutsceneSystemStartCutscene(&TEST_CUTSCENE); cutsceneSystemStartCutscene(&TEST_CUTSCENE);
// rpgTextboxShow(RPG_TEXTBOX_POS_BOTTOM, "Hello World!");
return false; return false;
}; };

View File

@@ -11,6 +11,7 @@
#include "rpg/cutscene/cutscenesystem.h" #include "rpg/cutscene/cutscenesystem.h"
#include "time/time.h" #include "time/time.h"
#include "rpgcamera.h" #include "rpgcamera.h"
#include "rpgtextbox.h"
#include "util/memory.h" #include "util/memory.h"
errorret_t rpgInit(void) { errorret_t rpgInit(void) {
@@ -22,8 +23,8 @@ errorret_t rpgInit(void) {
// Init the world. // Init the world.
worldInit(); worldInit();
// Initialize the camera
rpgCameraInit(); rpgCameraInit();
rpgTextboxInit();
// TEST: Create some entities. // TEST: Create some entities.
entity_t *ent; entity_t *ent;

39
src/rpg/rpgtextbox.c Normal file
View File

@@ -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;
}

52
src/rpg/rpgtextbox.h Normal file
View File

@@ -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();

View File

@@ -10,4 +10,5 @@ target_sources(${DUSK_TARGET_NAME}
uitext.c uitext.c
uifps.c uifps.c
uiframe.c uiframe.c
uitextbox.c
) )

View File

@@ -11,6 +11,7 @@
#include "util/memory.h" #include "util/memory.h"
#include "display/tileset/tileset_minogram.h" #include "display/tileset/tileset_minogram.h"
#include "display/screen.h" #include "display/screen.h"
#include "ui/uitextbox.h"
ui_t UI; ui_t UI;
@@ -31,6 +32,8 @@ void uiUpdate(void) {
UI.camera.orthographic.right = SCREEN.width; UI.camera.orthographic.right = SCREEN.width;
UI.camera.orthographic.top = 0; UI.camera.orthographic.top = 0;
UI.camera.orthographic.bottom = SCREEN.height; UI.camera.orthographic.bottom = SCREEN.height;
uiTextboxUpdate();
} }
void uiRender(void) { void uiRender(void) {
@@ -39,6 +42,7 @@ void uiRender(void) {
// Render UI elements here // Render UI elements here
if(UI.fontTexture.width > 0) { if(UI.fontTexture.width > 0) {
uiFPSRender(UI.fontTileset, &UI.fontTexture); uiFPSRender(UI.fontTileset, &UI.fontTexture);
uiTextboxRender();
} }
cameraPopMatrix(); cameraPopMatrix();

45
src/ui/uitextbox.c Normal file
View File

@@ -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);
}

19
src/ui/uitextbox.h Normal file
View File

@@ -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();