Files
dusk/.claude/rpg/cutscene.md
T
2026-06-18 14:59:21 -05:00

150 lines
4.5 KiB
Markdown

# 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:
```c
typedef struct {
errorret_t (*onStart)(void);
errorret_t (*onUpdate)(void);
errorret_t (*onEnd)(void);
} cutsceneevent_t;
```
API:
```c
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:
```c
typedef struct cutscene_s {
const cutsceneitem_t *items;
uint8_t itemCount;
} cutscene_t;
```
A `cutsceneitem_t` is a tagged union of all item types:
```c
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 active `cutscene_t`
- `currentItem` — index into `scene->items[]`
- `data` — per-item runtime data (`cutsceneitemdata_t`, currently just `cutscenewaitdata_t`)
- `mode` — the current `cutscenemode_t`
API:
```c
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:
```c
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`):
```c
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:
```c
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:
```c
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:
```c
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.