Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 104 additions & 101 deletions docs/RouteEngine.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@ The RouteEngine is the core runtime for RVN visual novels. It manages state, pro
## Creating an Engine Instance

```js
import createRouteEngine from 'rvn-temp';
import createRouteEngine from "rvn-temp";

const engine = createRouteEngine({
handlePendingEffects: (effects) => {
// Process side effects (render, timers, etc.)
effects.forEach(effect => {
switch(effect.name) {
case 'render':
effects.forEach((effect) => {
switch (effect.name) {
case "render":
renderToScreen(engine.selectRenderState());
break;
case 'handleLineActions':
case "handleLineActions":
engine.handleLineActions();
break;
// ... handle other effects
}
});
}
},
});
```

Expand All @@ -35,30 +35,35 @@ Initializes the engine with project data and global settings.
engine.init({
initialState: {
projectData: {
resources: { /* images, audio, etc */ },
resources: {
/* images, audio, etc */
},
story: {
initialSceneId: 'scene1',
initialSceneId: "scene1",
scenes: {
scene1: {
initialSectionId: 'section1',
initialSectionId: "section1",
sections: {
section1: {
initialLineId: 'line1', // optional, otherwise first line is used
lines: [ /* section lines */ ]
}
}
}
}
}
}
}
initialLineId: "line1", // optional, otherwise first line is used
lines: [
/* section lines */
],
},
},
},
},
},
},
},
});
```

Localization is not implemented in the current runtime. The planned
patch-based l10n model is documented in [L10n.md](./L10n.md).

The engine will:

1. Create the system store with initial state
2. Append a `render` effect
3. Execute any actions on the initial line
Expand Down Expand Up @@ -99,13 +104,13 @@ Dispatches a single action to the system store.

```js
// Advance to next line
engine.handleAction('nextLine');
engine.handleAction("nextLine");

// Jump to specific section
engine.handleAction('sectionTransition', { sectionId: 'chapter_2' });
engine.handleAction("sectionTransition", { sectionId: "chapter_2" });

// Toggle auto mode
engine.handleAction('toggleAutoMode');
engine.handleAction("toggleAutoMode");
```

### `handleActions(actions, eventContext?)`
Expand All @@ -117,17 +122,14 @@ Dispatches multiple actions from an object. Optionally accepts event context for
engine.handleActions({
setNextLineConfig: {
manual: { enabled: false },
auto: { enabled: true, trigger: 'fromComplete', delay: 2000 }
auto: { enabled: true, trigger: "fromComplete", delay: 2000 },
},
markLineCompleted: {}
markLineCompleted: {},
});

// With event context (for slider/input events)
// Bindings like _event.value in action payloads get resolved
engine.handleActions(
payload.actions,
{ _event: payload._event }
);
engine.handleActions(payload.actions, { _event: payload._event });
```

#### Event Templating
Expand All @@ -151,7 +153,7 @@ Invalid `_event.*` bindings fail fast with an explicit error.
operations:
- variableId: volume
op: set
value: '_event.value' # Resolved to slider's current value
value: "_event.value" # Resolved to slider's current value
```

The integration layer should pass event context when handling events:
Expand All @@ -161,10 +163,10 @@ eventHandler: (eventName, payload) => {
if (payload.actions) {
engine.handleActions(
payload.actions,
payload._event ? { _event: payload._event } : undefined
payload._event ? { _event: payload._event } : undefined,
);
}
}
};
```

#### Layout Resource References
Expand Down Expand Up @@ -290,12 +292,12 @@ Processes actions attached to the current line. Called automatically on line cha
```js
// Line data structure
const line = {
id: 'line_1',
id: "line_1",
actions: {
background: { resourceId: 'bg_school' },
dialogue: { characterId: 'protagonist', content: [{ text: 'Hello!' }] },
bgm: { resourceId: 'music_1' }
}
background: { resourceId: "bg_school" },
dialogue: { characterId: "protagonist", content: [{ text: "Hello!" }] },
bgm: { resourceId: "music_1" },
},
};
```

Expand Down Expand Up @@ -329,104 +331,105 @@ const presentationState = engine.selectPresentationState();

### Navigation Actions

| Action | Payload | Description |
|--------|---------|-------------|
| `nextLine` | - | Advance to the next line (respects `nextLineConfig.manual`) |
| `prevLine` | `{ sectionId }` | Navigate to previous line (enters history mode) |
| `jumpToLine` | `{ sectionId?, lineId }` | Jump to specific line |
| `sectionTransition` | `{ sectionId }` | Transition to a different section |
| Action | Payload | Description |
| ------------------- | ------------------------ | ----------------------------------------------------------- |
| `nextLine` | - | Advance to the next line (respects `nextLineConfig.manual`) |
| `prevLine` | `{ sectionId }` | Navigate to previous line (enters history mode) |
| `jumpToLine` | `{ sectionId?, lineId }` | Jump to specific line |
| `sectionTransition` | `{ sectionId }` | Transition to a different section |

### Playback Mode Actions

| Action | Payload | Description |
|--------|---------|-------------|
| `startAutoMode` | - | Enable auto-advance mode |
| `stopAutoMode` | - | Disable auto-advance mode |
| `toggleAutoMode` | - | Toggle auto-advance mode |
| `startSkipMode` | - | Enable skip mode |
| `stopSkipMode` | - | Disable skip mode |
| `toggleSkipMode` | - | Toggle skip mode |
| Action | Payload | Description |
| ---------------- | ------- | ------------------------- |
| `startAutoMode` | - | Enable auto-advance mode |
| `stopAutoMode` | - | Disable auto-advance mode |
| `toggleAutoMode` | - | Toggle auto-advance mode |
| `startSkipMode` | - | Enable skip mode |
| `stopSkipMode` | - | Disable skip mode |
| `toggleSkipMode` | - | Toggle skip mode |

### UI Actions

| Action | Payload | Description |
|--------|---------|-------------|
| `showDialogueUI` | - | Show the dialogue UI |
| `hideDialogueUI` | - | Hide the dialogue UI |
| `toggleDialogueUI` | - | Toggle dialogue UI visibility |
| Action | Payload | Description |
| ------------------ | ------- | ----------------------------- |
| `showDialogueUI` | - | Show the dialogue UI |
| `hideDialogueUI` | - | Hide the dialogue UI |
| `toggleDialogueUI` | - | Toggle dialogue UI visibility |

### State Management Actions

| Action | Payload | Description |
|--------|---------|-------------|
| `markLineCompleted` | - | Mark current line as completed |
| `setNextLineConfig` | `{ manual?, auto? }` | Configure line advancement |
| `updateProjectData` | `{ projectData }` | Replace project data |
| Action | Payload | Description |
| ------------------- | -------------------- | ------------------------------ |
| `markLineCompleted` | - | Mark current line as completed |
| `setNextLineConfig` | `{ manual?, auto? }` | Configure line advancement |
| `updateProjectData` | `{ projectData }` | Replace project data |

### Registry Actions

| Action | Payload | Description |
|--------|---------|-------------|
| `addViewedLine` | `{ sectionId, lineId }` | Mark line as viewed |
| `addViewedResource` | `{ resourceId }` | Mark resource as viewed |
| `addToHistory` | `{ item }` | Add entry to history sequence |
| Action | Payload | Description |
| ------------------- | ----------------------- | ----------------------------- |
| `addViewedLine` | `{ sectionId, lineId }` | Mark line as viewed |
| `addViewedResource` | `{ resourceId }` | Mark resource as viewed |
| `addToHistory` | `{ item }` | Add entry to history sequence |

### Save System Actions

| Action | Payload | Description |
|--------|---------|-------------|
| Action | Payload | Description |
| ----------------- | --------------------------------- | ----------------- |
| `replaceSaveSlot` | `{ slotKey, date, image, state }` | Save game to slot |

### Effect Actions

| Action | Payload | Description |
|--------|---------|-------------|
| `appendPendingEffect` | `{ name, ...options }` | Queue a side effect |
| `clearPendingEffects` | - | Clear the effect queue |
| Action | Payload | Description |
| --------------------- | ---------------------- | ---------------------- |
| `appendPendingEffect` | `{ name, ...options }` | Queue a side effect |
| `clearPendingEffects` | - | Clear the effect queue |

## Available Selectors

The system store exposes these selectors (called internally):

| Selector | Parameters | Returns |
|----------|------------|---------|
| `selectPendingEffects` | - | Array of pending effects |
| `selectCurrentPointer` | - | `{ currentPointerMode, pointer }` |
| `selectCurrentLine` | - | Current line object |
| `selectSection` | `{ sectionId }` | Section object |
| `selectAutoMode` | - | Boolean |
| `selectSkipMode` | - | Boolean |
| `selectDialogueUIHidden` | - | Boolean |
| `selectIsLineViewed` | `{ sectionId, lineId }` | Boolean |
| `selectIsResourceViewed` | `{ resourceId }` | Boolean |
| `selectNextLineConfig` | - | Config object |
| `selectSaveSlots` | - | Save slots object |
| `selectSaveSlot` | `{ slotKey }` | Save slot data |
| Selector | Parameters | Returns |
| ------------------------ | ----------------------- | --------------------------------- |
| `selectPendingEffects` | - | Array of pending effects |
| `selectCurrentPointer` | - | `{ currentPointerMode, pointer }` |
| `selectCurrentLine` | - | Current line object |
| `selectSection` | `{ sectionId }` | Section object |
| `selectAutoMode` | - | Boolean |
| `selectSkipMode` | - | Boolean |
| `selectDialogueUIHidden` | - | Boolean |
| `selectIsLineViewed` | `{ sectionId, lineId }` | Boolean |
| `selectIsResourceViewed` | `{ resourceId }` | Boolean |
| `selectNextLineConfig` | - | Config object |
| `selectSaveSlots` | - | Save slots object |
| `selectSaveSlot` | `{ slotKey }` | Save slot data |

## Pending Effects

Effects queued by actions for external handling:

| Effect | Description |
|--------|-------------|
| `render` | Re-render the current state |
| Effect | Description |
| ------------------- | ------------------------------ |
| `render` | Re-render the current state |
| `handleLineActions` | Process current line's actions |

## Line Actions (Presentation)

Actions that can be attached to lines to control presentation:

| Action | Properties | Description |
|--------|------------|-------------|
| `base` | `{ resourceId }` | Set base layout |
| `background` | `{ resourceId, animations? }` | Set background/CG |
| `dialogue` | `{ characterId?, character?, content, mode?, ui?, clear? }` | Display dialogue |
| `character` | `{ items }` | Display character sprites. Each item can have optional `x` and `y` to override transform position |
| `visual` | `{ items }` | Display visual elements |
| `bgm` | `{ resourceId, loop?, volume?, delay? }` | Play background music |
| `sfx` | `{ items }` | Play sound effects |
| `voice` | `{ fileId, volume?, loop? }` | Play voice audio |
| `animation` | `{ ... }` | Apply animations |
| `layout` | `{ resourceId }` | Display layout |
| `choice` | `{ resourceId, items }` | Display choice menu |
| `cleanAll` | `true` | Clear all presentation state |
| Action | Properties | Description |
| ------------ | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `base` | `{ resourceId }` | Set base layout |
| `background` | `{ resourceId, animations? }` | Set background/CG |
| `dialogue` | `{ characterId?, character?, content, mode?, ui?, clear? }` | Display dialogue |
| `character` | `{ items }` | Display character sprites. Each item can have optional `x` and `y` to override transform position |
| `visual` | `{ items }` | Display visual elements |
| `bgm` | `{ resourceId, loop?, volume?, delay? }` | Play background music |
| `sfx` | `{ items }` | Play sound effects |
| `voice` | `{ fileId, volume?, loop? }` | Play voice audio |
| `animation` | `{ ... }` | Apply animations |
| `layout` | `{ resourceId }` | Display layout |
| `control` | `{ resourceId }` | Activate control bindings and control UI |
| `choice` | `{ resourceId, items }` | Display choice menu |
| `cleanAll` | `true` | Clear all presentation state |
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "route-engine-js",
"version": "0.2.2",
"version": "0.3.0",
"description": "A lightweight Visual Novel engine built in JavaScript for creating interactive narrative games with branching storylines",
"repository": {
"type": "git",
Expand Down
54 changes: 54 additions & 0 deletions spec/system/actions/nextLine.spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,60 @@ suites: [nextLine]
suite: nextLine
exportName: nextLine
---
case: show dialogue UI instead of completing or advancing when hidden
in:
- state:
global:
isLineCompleted: true
dialogueUIHidden: true
nextLineConfig:
manual:
enabled: true
pendingEffects: []
projectData:
story:
scenes:
scene1:
sections:
section1:
lines:
- id: "1"
actions: {}
- id: "2"
actions: {}
contexts:
- currentPointerMode: "read"
pointers:
read:
sectionId: "section1"
lineId: "1"
out:
global:
isLineCompleted: true
dialogueUIHidden: false
nextLineConfig:
manual:
enabled: true
pendingEffects:
- name: "render"
projectData:
story:
scenes:
scene1:
sections:
section1:
lines:
- id: "1"
actions: {}
- id: "2"
actions: {}
contexts:
- currentPointerMode: "read"
pointers:
read:
sectionId: "section1"
lineId: "1"
---
case: complete line instantly when not completed (click during animation)
in:
- state:
Expand Down
Loading
Loading