Chain cutscenes and rendering text.

This commit is contained in:
2025-10-29 09:54:03 -05:00
parent a48fa50a3e
commit e05a3e751c
16 changed files with 181 additions and 38 deletions

View File

@@ -6,6 +6,8 @@
# Sources
target_sources(microrpg PRIVATE
cutscene.c
cutsceneitem.c
cutscenemode.c
)
)
# Subdirectories
add_subdirectory(item)

View File

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

View File

@@ -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();
void cutsceneTick();
/**
* Get the current cutscene item.
*
* @return Pointer to the current cutscene item.
*/
const cutsceneitem_t * cutsceneGetCurrentItem();

View File

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

View File

@@ -8,4 +8,6 @@
#pragma once
#include "microrpg.h"
typedef const char *cutscenetext_t;
typedef struct cutscene_s cutscene_t;
typedef cutscene_t* cutscenecutscene_t;

View File

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

View File

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

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 "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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File