4.5 KiB
Cutscenes
Two distinct layers: a low-level engine sequencer (src/dusk/cutscene/) and a higher-level RPG wrapper (src/dusk/rpg/cutscene/). Almost all game code works with the RPG layer.
Engine sequencer (src/dusk/cutscene/)
cutscene_t CUTSCENE is a minimal event runner with up to CUTSCENE_EVENT_COUNT_MAX (16) cutsceneevent_t slots. Each event has three callbacks:
typedef struct {
errorret_t (*onStart)(void);
errorret_t (*onUpdate)(void);
errorret_t (*onEnd)(void);
} cutsceneevent_t;
API:
cutscenePlay(events, count); // copy events array and start from index 0
cutsceneAdvance(); // end current event, start next (deactivates after last)
cutsceneStop(); // abort immediately
cutsceneIsActive(); // bool
This layer is primarily used by the RPG cutscene system — game code doesn't normally touch it directly.
RPG cutscene layer (src/dusk/rpg/cutscene/)
Data structures
A cutscene_t is just a pointer to an item array and a count:
typedef struct cutscene_s {
const cutsceneitem_t *items;
uint8_t itemCount;
} cutscene_t;
A cutsceneitem_t is a tagged union of all item types:
typedef struct cutsceneitem_s {
cutsceneitemtype_t type;
union {
cutscenetext_t text; // display text in textbox
cutscenecallback_t callback; // call a void(*)(void) function
cutscenewait_t wait; // pause for a fixed_t duration (seconds)
const cutscene_t *cutscene; // nest another cutscene
};
} cutsceneitem_t;
Item types
| Type constant | Payload | Behaviour |
|---|---|---|
CUTSCENE_ITEM_TYPE_TEXT |
cutscenetext_t — text[256] + rpgtextboxpos_t position |
Shows textbox; advances on player confirm input |
CUTSCENE_ITEM_TYPE_CALLBACK |
cutscenecallback_t (function pointer) |
Calls the function once, then immediately advances |
CUTSCENE_ITEM_TYPE_WAIT |
cutscenewait_t (a fixed_t in seconds) |
Counts down animTime each frame, then advances |
CUTSCENE_ITEM_TYPE_CUTSCENE |
const cutscene_t * |
Plays the nested cutscene before continuing |
Runtime state
cutscenesystem_t CUTSCENE_SYSTEM tracks:
scene— pointer to the activecutscene_tcurrentItem— index intoscene->items[]data— per-item runtime data (cutsceneitemdata_t, currently justcutscenewaitdata_t)mode— the currentcutscenemode_t
API:
cutsceneSystemStartCutscene(cutscene); // begin playing a cutscene
cutsceneSystemNext(); // advance to next item
cutsceneSystemUpdate(); // called each frame from rpgUpdate
cutsceneSystemGetCurrentItem(); // inspect active item
Cutscene mode (cutscenemode.h)
Each item can run in one of three modes:
CUTSCENE_MODE_NONE // no cutscene active
CUTSCENE_MODE_FULL_FREEZE // pause everything (not yet used)
CUTSCENE_MODE_INPUT_FREEZE // player input blocked (default: CUTSCENE_MODE_INITIAL)
CUTSCENE_MODE_GAMEPLAY // player can still move during cutscene
cutsceneModeIsInputAllowed() is checked by entityUpdate() before invoking the movement callback — the player cannot walk when in INPUT_FREEZE mode.
Defining a cutscene
Cutscenes are defined as static const arrays in header files under rpg/cutscene/scene/. Example (testcutscene.h):
static const cutsceneitem_t MY_CUTSCENE_ITEMS[] = {
{
.type = CUTSCENE_ITEM_TYPE_TEXT,
.text = { .text = "Hello!", .position = RPG_TEXTBOX_POS_BOTTOM }
},
{
.type = CUTSCENE_ITEM_TYPE_WAIT,
.wait = FIXED(1.5f)
},
{
.type = CUTSCENE_ITEM_TYPE_CUTSCENE,
.cutscene = &ANOTHER_CUTSCENE
},
};
static const cutscene_t MY_CUTSCENE = {
.items = MY_CUTSCENE_ITEMS,
.itemCount = sizeof(MY_CUTSCENE_ITEMS) / sizeof(cutsceneitem_t)
};
Attach to an NPC via its interact component:
entity->interact.type = ENTITY_INTERACT_CUTSCENE;
entity->interact.data.cutscene = &MY_CUTSCENE;
Textbox (src/dusk/rpg/rpgtextbox.h)
rpgtextbox_t RPG_TEXTBOX is the global textbox state:
typedef struct {
rpgtextboxpos_t position; // RPG_TEXTBOX_POS_TOP or RPG_TEXTBOX_POS_BOTTOM
bool_t visible;
char_t text[RPG_TEXTBOX_MAX_CHARS]; // 256 chars
} rpgtextbox_t;
API:
rpgTextboxShow(position, text); // copies text, sets visible = true
rpgTextboxHide(); // sets visible = false
rpgTextboxIsVisible(); // bool
The textbox state is read by ui/uitextbox.c during the UI render pass to draw the dialogue box on screen. rpgtextbox.c itself does no rendering.