Docs
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user