diff --git a/src/cutscene/CMakeLists.txt b/src/cutscene/CMakeLists.txt index c37ed2e..b61e12b 100755 --- a/src/cutscene/CMakeLists.txt +++ b/src/cutscene/CMakeLists.txt @@ -6,6 +6,8 @@ # Sources target_sources(microrpg PRIVATE cutscene.c - cutsceneitem.c cutscenemode.c -) \ No newline at end of file +) + +# Subdirectories +add_subdirectory(item) \ No newline at end of file diff --git a/src/cutscene/cutscene.c b/src/cutscene/cutscene.c index 373d1d2..d9e2319 100644 --- a/src/cutscene/cutscene.c +++ b/src/cutscene/cutscene.c @@ -22,9 +22,7 @@ void cutsceneStart(const cutscene_t *cutscene) { void cutsceneTick() { if(GAME.cutsceneSystem.cutscene == NULL) return; - const cutsceneitem_t *item = ( - &GAME.cutsceneSystem.cutscene->items[GAME.cutsceneSystem.currentItem] - ); + const cutsceneitem_t *item = cutsceneGetCurrentItem(); cutsceneItemTick(item, &GAME.cutsceneSystem.data); } @@ -34,16 +32,23 @@ void cutsceneNext() { GAME.cutsceneSystem.currentItem++; // End of the cutscene? - if(GAME.cutsceneSystem.currentItem >= GAME.cutsceneSystem.cutscene->itemCount) { + if( + GAME.cutsceneSystem.currentItem >= GAME.cutsceneSystem.cutscene->itemCount + ) { GAME.cutsceneSystem.cutscene = NULL; - GAME.cutsceneSystem.currentItem = 0; + GAME.cutsceneSystem.currentItem = 0xFF; GAME.cutsceneSystem.mode = CUTSCENE_MODE_NONE; return; } // Start item. - const cutsceneitem_t *item = ( - &GAME.cutsceneSystem.cutscene->items[GAME.cutsceneSystem.currentItem] - ); + const cutsceneitem_t *item = cutsceneGetCurrentItem(); + memset(&GAME.cutsceneSystem.data, 0, sizeof(GAME.cutsceneSystem.data)); cutsceneItemStart(item, &GAME.cutsceneSystem.data); +} + +const cutsceneitem_t * cutsceneGetCurrentItem() { + if(GAME.cutsceneSystem.cutscene == NULL) return NULL; + + return &GAME.cutsceneSystem.cutscene->items[GAME.cutsceneSystem.currentItem]; } \ No newline at end of file diff --git a/src/cutscene/cutscene.h b/src/cutscene/cutscene.h index 54d2a38..b0001bb 100755 --- a/src/cutscene/cutscene.h +++ b/src/cutscene/cutscene.h @@ -6,10 +6,10 @@ */ #pragma once -#include "cutsceneitem.h" -#include "cutscenemode.h" +#include "cutscene/item/cutsceneitem.h" +#include "cutscene/cutscenemode.h" -typedef struct { +typedef struct cutscene_s { const cutsceneitem_t *items; uint8_t itemCount; } cutscene_t; @@ -43,4 +43,11 @@ void cutsceneNext(); /** * Update the cutscene system for one tick. */ -void cutsceneTick(); \ No newline at end of file +void cutsceneTick(); + +/** + * Get the current cutscene item. + * + * @return Pointer to the current cutscene item. + */ +const cutsceneitem_t * cutsceneGetCurrentItem(); \ No newline at end of file diff --git a/src/cutscene/item/CMakeLists.txt b/src/cutscene/item/CMakeLists.txt new file mode 100755 index 0000000..aaca3ac --- /dev/null +++ b/src/cutscene/item/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2025 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(microrpg PRIVATE + cutsceneitem.c +) \ No newline at end of file diff --git a/src/cutscene/cutscenecallback.h b/src/cutscene/item/cutscenecallback.h similarity index 100% rename from src/cutscene/cutscenecallback.h rename to src/cutscene/item/cutscenecallback.h diff --git a/src/cutscene/cutscenetext.h b/src/cutscene/item/cutscenecutscene.h similarity index 69% rename from src/cutscene/cutscenetext.h rename to src/cutscene/item/cutscenecutscene.h index 77afc50..e8f604f 100644 --- a/src/cutscene/cutscenetext.h +++ b/src/cutscene/item/cutscenecutscene.h @@ -8,4 +8,6 @@ #pragma once #include "microrpg.h" -typedef const char *cutscenetext_t; \ No newline at end of file +typedef struct cutscene_s cutscene_t; + +typedef cutscene_t* cutscenecutscene_t; \ No newline at end of file diff --git a/src/cutscene/cutsceneitem.c b/src/cutscene/item/cutsceneitem.c similarity index 58% rename from src/cutscene/cutsceneitem.c rename to src/cutscene/item/cutsceneitem.c index 5a3e548..e79f915 100644 --- a/src/cutscene/cutsceneitem.c +++ b/src/cutscene/item/cutsceneitem.c @@ -5,13 +5,16 @@ * https://opensource.org/licenses/MIT */ -#include "cutsceneitem.h" -#include "cutscene.h" +#include "cutscene/cutscene.h" +#include "input.h" void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) { switch(item->type) { - case CUTSCENE_ITEM_TEXT: + case CUTSCENE_ITEM_TEXT: { + strncpy(data->text.buffer, item->text, CUTSCENE_TEXT_BUFFER); + data->text.length = strlen(data->text.buffer); break; + } case CUTSCENE_ITEM_WAIT: data->wait = item->wait; @@ -21,6 +24,10 @@ void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) { if(item->callback != NULL) item->callback(); break; + case CUTSCENE_ITEM_CUTSCENE: + if(item->cutscene != NULL) cutsceneStart(item->cutscene); + break; + default: break; } @@ -28,8 +35,15 @@ void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) { void cutsceneItemTick(const cutsceneitem_t *item, cutsceneitemdata_t *data) { switch(item->type) { - case CUTSCENE_ITEM_TEXT: + // Scroll text, when finished scrolling, wait for A press. + case CUTSCENE_ITEM_TEXT: { + if(data->text.scroll < data->text.length) { + data->text.scroll++; + } else if(inputPressed(INPUT_ACTION_A)) { + cutsceneNext(); + } break; + } case CUTSCENE_ITEM_CALLBACK: break; diff --git a/src/cutscene/cutsceneitem.h b/src/cutscene/item/cutsceneitem.h similarity index 89% rename from src/cutscene/cutsceneitem.h rename to src/cutscene/item/cutsceneitem.h index bb80611..95ec7d8 100644 --- a/src/cutscene/cutsceneitem.h +++ b/src/cutscene/item/cutsceneitem.h @@ -9,11 +9,13 @@ #include "cutscenewait.h" #include "cutscenecallback.h" #include "cutscenetext.h" +#include "cutscenecutscene.h" #define CUTSCENE_ITEM_NULL 0 #define CUTSCENE_ITEM_TEXT 1 #define CUTSCENE_ITEM_CALLBACK 2 #define CUTSCENE_ITEM_WAIT 3 +#define CUTSCENE_ITEM_CUTSCENE 4 typedef struct cutsceneitem_s { uint8_t type; @@ -23,10 +25,12 @@ typedef struct cutsceneitem_s { cutscenetext_t text; cutscenecallback_t callback; cutscenewait_t wait; + cutscenecutscene_t cutscene; }; } cutsceneitem_t; typedef union { + cutscenetextdata_t text; cutscenewaitdata_t wait; } cutsceneitemdata_t; diff --git a/src/cutscene/item/cutscenetext.h b/src/cutscene/item/cutscenetext.h new file mode 100644 index 0000000..72a4aec --- /dev/null +++ b/src/cutscene/item/cutscenetext.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 "microrpg.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 diff --git a/src/cutscene/cutscenewait.h b/src/cutscene/item/cutscenewait.h similarity index 100% rename from src/cutscene/cutscenewait.h rename to src/cutscene/item/cutscenewait.h diff --git a/src/cutscene/scene/testcutscene.h b/src/cutscene/scene/testcutscene.h index 7794d4d..5c167f6 100755 --- a/src/cutscene/scene/testcutscene.h +++ b/src/cutscene/scene/testcutscene.h @@ -8,14 +8,23 @@ #pragma once #include "cutscene/cutscene.h" -static void testCutsceneTest() { -} +static const cutsceneitem_t TEST_CUTSCENE_ONE_ITEMS[] = { + { .type = CUTSCENE_ITEM_TEXT, .text = "This is a test cutscene." }, + { .type = CUTSCENE_ITEM_WAIT, .wait = 2 * GAME_TIME_TICKS_PER_SECOND }, + { .type = CUTSCENE_ITEM_TEXT, .text = "It has multiple lines of text.\nAnd waits in between." }, +}; -static const cutsceneitem_t TEST_CUTSCENE_ITEMS[] = { - { .type = CUTSCENE_ITEM_WAIT, .wait = 1 * GAME_TIME_TICKS_PER_SECOND } +static const cutscene_t TEST_CUTSCENE_ONE = { + .items = TEST_CUTSCENE_ONE_ITEMS, + .itemCount = sizeof(TEST_CUTSCENE_ONE_ITEMS) / sizeof(cutsceneitem_t) +}; + +static const cutsceneitem_t TEST_CUTSCENE_TWO_ITEMS[] = { + { .type = CUTSCENE_ITEM_WAIT, .wait = 1 * GAME_TIME_TICKS_PER_SECOND }, + { .type = CUTSCENE_ITEM_CUTSCENE, .cutscene = &TEST_CUTSCENE_ONE }, }; static const cutscene_t TEST_CUTSCENE = { - .items = TEST_CUTSCENE_ITEMS, - .itemCount = sizeof(TEST_CUTSCENE_ITEMS) / sizeof(cutsceneitem_t) + .items = TEST_CUTSCENE_TWO_ITEMS, + .itemCount = sizeof(TEST_CUTSCENE_TWO_ITEMS) / sizeof(cutsceneitem_t) }; \ No newline at end of file diff --git a/src/game.c b/src/game.c index 191fb16..a8c449b 100755 --- a/src/game.c +++ b/src/game.c @@ -15,19 +15,21 @@ void gameInit() { memset(&GAME, 0, sizeof(GAME)); cutsceneInit(); - - entityInit(&GAME.player, ENTITY_TYPE_PLAYER); - - entityInit(&GAME.overworld.map.entities[0], ENTITY_TYPE_SIGN); - GAME.overworld.map.entities[0].position.x = 5; - GAME.overworld.map.entities[0].position.y = 5; - GAME.overworld.map.entities[0].sign.cutscene = &TEST_CUTSCENE; + + entity_t *ent = &GAME.player; + entityInit(ent, ENTITY_TYPE_PLAYER); + + ent = &GAME.overworld.map.entities[0]; + entityInit(ent, ENTITY_TYPE_SIGN); + ent->position.x = 5; + ent->position.y = 5; + ent->sign.cutscene = &TEST_CUTSCENE; GAME.scene = SCENE_OVERWORLD; } void gameTick() { gameTimeTick(&GAME.time); - cutsceneTick(); sceneTick(); + cutsceneTick(); } \ No newline at end of file diff --git a/src/platform/CMakeLists.txt b/src/platform/CMakeLists.txt index 5549d1c..18e9e3f 100755 --- a/src/platform/CMakeLists.txt +++ b/src/platform/CMakeLists.txt @@ -3,16 +3,16 @@ # This software is released under the MIT License. # https://opensource.org/licenses/MIT -if(NOT DEFINED PLATFORM) - set(PLATFORM "raylib" CACHE STRING "Select the platform to build for (options: raylib, term, tty)") +if(NOT DEFINED RPG_PLATFORM) + set(RPG_PLATFORM "raylib" CACHE STRING "Select the platform to build for") endif() -if(PLATFORM STREQUAL "tty") +if(RPG_PLATFORM STREQUAL "tty") add_subdirectory(term) add_subdirectory(tty) -elseif(PLATFORM STREQUAL "raylib") +elseif(RPG_PLATFORM STREQUAL "raylib") add_subdirectory(raylib) add_subdirectory(term) else() - message(FATAL_ERROR "Unknown PLATFORM: ${PLATFORM}.") + message(FATAL_ERROR "Unknown RPG_PLATFORM: ${RPG_PLATFORM}.") endif() \ No newline at end of file diff --git a/src/platform/term/termoverworld.c b/src/platform/term/termoverworld.c index 43ea56e..1de3a06 100644 --- a/src/platform/term/termoverworld.c +++ b/src/platform/term/termoverworld.c @@ -30,6 +30,9 @@ void termDrawOverworld( termDrawEntity(start, chars, colors, w, h); start++; } + + // Draw UI + termDrawUI(chars, colors, w, h); } void termDrawEntity( @@ -68,4 +71,56 @@ void termDrawEntity( chars[index] = '!'; colors[index] = TERM_COLOR_YELLOW; } +} + +void termDrawUI( + char_t *chars, + termcolor_t *colors, + const int32_t w, + const int32_t h +) { + // Textbox. + const cutsceneitem_t *csItem = cutsceneGetCurrentItem(); + if(csItem != NULL && csItem->type == CUTSCENE_ITEM_TEXT) { + // Draw box + int32_t boxHeight = 5; + for(int32_t y = h - boxHeight; y < h; y++) { + for(int32_t x = 0; x < w; x++) { + size_t index = y * w + x; + if(y == h - boxHeight || y == h - 1) { + chars[index] = '-'; + } else if(x == 0 || x == w - 1) { + chars[index] = '|'; + } else { + chars[index] = ' '; + } + colors[index] = TERM_COLOR_WHITE; + } + } + + // Draw text + cutscenetextdata_t *data = &GAME.cutsceneSystem.data.text; + int boxRows = boxHeight - 2; + int boxCols = w - 2; + int row = 0, col = 0; + for(uint8_t i = 0; i < data->scroll; i++) { + char ch = data->buffer[i]; + if(ch == '\n') { + row++; + col = 0; + if(row >= boxRows) break; + continue; + } + if(col >= boxCols) { + row++; + col = 0; + if(row >= boxRows) break; + } + size_t boxIndex = (h - boxHeight + 1 + row) * w + 1 + col; + if(boxIndex >= (size_t)(h * w)) break; + chars[boxIndex] = ch; + colors[boxIndex] = TERM_COLOR_WHITE; + col++; + } + } } \ No newline at end of file diff --git a/src/platform/term/termoverworld.h b/src/platform/term/termoverworld.h index e7055ca..9137b51 100644 --- a/src/platform/term/termoverworld.h +++ b/src/platform/term/termoverworld.h @@ -39,4 +39,19 @@ void termDrawEntity( termcolor_t *colors, const int32_t w, const int32_t h +); + +/** + * Draw the UI to the terminal. + * + * @param chars Buffer of characters to write to. + * @param colors Buffer of colors to write to. + * @param w Width of the terminal in characters. + * @param h Height of the terminal in characters. + */ +void termDrawUI( + char_t *chars, + termcolor_t *colors, + const int32_t w, + const int32_t h ); \ No newline at end of file diff --git a/tools/embed-file/CMakeLists.txt b/tools/embed-file/CMakeLists.txt new file mode 100644 index 0000000..e69de29