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
15 changes: 15 additions & 0 deletions docs/Concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,13 @@ pointers: {
- `trigger`: When to advance (`'fromStart'` or `'fromComplete'`)
- `delay`: Milliseconds to wait before advancing

Global playback modes use a different timing model:

- Global `autoMode` starts its delay after the current line is completed.
- In practice, completion is driven by Route Graphics `renderComplete`, so text reveal and other tracked render work finish first.
- Global `skipMode` does not wait for completion; it advances aggressively on its own short timer.
- `nextLineConfig.auto` is the only built-in auto-like behavior that can intentionally start from line start via `trigger: "fromStart"`.

## Dialogue Modes

### ADV Mode (Adventure)
Expand Down Expand Up @@ -303,6 +310,14 @@ Tracks content the player has seen:
- **sections**: Array of `{ sectionId, lastLineId }` entries
- **resources**: Array of `{ resourceId }` entries

For lines, this is intentionally a section-level frontier model:

- `lastLineId` means the furthest seen line reached within that section.
- Any line at or before that frontier is treated as seen.
- This assumes section flow is effectively linear, which matches the engine's current use of seen-lines for skip behavior and progress tracking.

The frontier is updated when the current line is completed and also when advancing away from the current line. That keeps the final completed line in a section marked as seen even if there is no later line to move to.

Used for:

- Skip mode (skip only viewed content)
Expand Down
14 changes: 14 additions & 0 deletions docs/RouteEngine.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,13 @@ const presentationState = engine.selectPresentationState();
| `stopSkipMode` | - | Disable skip mode |
| `toggleSkipMode` | - | Toggle skip mode |

Playback timing semantics:

- Global `autoMode` waits for the current line to complete before starting its `_autoForwardTime` delay.
- That completion is driven by Route Graphics `renderComplete`, so revealing text and other tracked render work finish first.
- Global `skipMode` does not use that completion gate; it advances on its own fast timer.
- `nextLineConfig.auto` is separate and may use `trigger: "fromStart"` or `trigger: "fromComplete"` depending on authored behavior.

### UI Actions

| Action | Payload | Description |
Expand All @@ -390,6 +397,13 @@ const presentationState = engine.selectPresentationState();
| `addViewedResource` | `{ resourceId }` | Mark resource as viewed |
| `addToHistory` | `{ item }` | Add entry to history sequence |

Seen-line semantics:

- The engine stores seen progress per section as a single frontier: `{ sectionId, lastLineId }`.
- The frontier line itself counts as seen.
- Any earlier line in the same section also counts as seen.
- The frontier is updated when a line is completed and when progression moves away from the current line.

### Save System Actions

| Action | Payload | Description |
Expand Down
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.3.7",
"version": "0.3.8",
"description": "A lightweight Visual Novel engine built in JavaScript for creating interactive narrative games with branching storylines",
"repository": {
"type": "git",
Expand Down
18 changes: 18 additions & 0 deletions spec/system/actions/markLineCompleted.spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,30 @@ exportName: markLineCompleted
case: mark line as completed
in:
- state:
contexts:
- currentPointerMode: "read"
pointers:
read:
sectionId: "section1"
lineId: "1"
global:
isLineCompleted: false
viewedRegistry:
sections: []
pendingEffects: []
out:
contexts:
- currentPointerMode: "read"
pointers:
read:
sectionId: "section1"
lineId: "1"
global:
isLineCompleted: true
viewedRegistry:
sections:
- sectionId: "section1"
lastLineId: "1"
pendingEffects:
- name: render
---
Expand Down
18 changes: 14 additions & 4 deletions spec/system/actions/nextLine.spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ in:
- state:
global:
isLineCompleted: false
viewedRegistry:
sections: []
nextLineConfig:
manual:
enabled: true
Expand All @@ -88,6 +90,10 @@ in:
out:
global:
isLineCompleted: true
viewedRegistry:
sections:
- sectionId: "section1"
lastLineId: "1"
nextLineConfig:
manual:
enabled: true
Expand Down Expand Up @@ -150,7 +156,6 @@ out:
manual:
enabled: true
pendingEffects:
- name: "render"
- name: "handleLineActions"
viewedRegistry:
sections:
Expand Down Expand Up @@ -223,7 +228,6 @@ out:
delay: 900
applyMode: "persistent"
pendingEffects:
- name: "render"
- name: "handleLineActions"
- name: "nextLineConfigTimer"
payload:
Expand Down Expand Up @@ -296,7 +300,6 @@ out:
enabled: false
applyMode: "persistent"
pendingEffects:
- name: "render"
- name: "clearNextLineConfigTimer"
- name: "handleLineActions"
viewedRegistry:
Expand Down Expand Up @@ -649,7 +652,6 @@ out:
manual:
enabled: true
pendingEffects:
- name: "render"
- name: "handleLineActions"
viewedRegistry:
sections:
Expand Down Expand Up @@ -723,6 +725,10 @@ out:
payload:
delay: 2000
- name: "render"
viewedRegistry:
sections:
- sectionId: "section1"
lastLineId: "1"
projectData:
story:
scenes:
Expand Down Expand Up @@ -786,6 +792,10 @@ out:
pendingEffects:
- name: "clearNextLineConfigTimer"
- name: "render"
viewedRegistry:
sections:
- sectionId: "section1"
lastLineId: "1"
projectData:
story:
scenes:
Expand Down
24 changes: 24 additions & 0 deletions spec/system/actions/nextLineFromSystem.spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ in:
- state:
global:
isLineCompleted: true
viewedRegistry:
sections: []
nextLineConfig:
auto: { enabled: false }
pendingEffects: []
Expand All @@ -79,6 +81,10 @@ in:
out:
global:
isLineCompleted: false
viewedRegistry:
sections:
- sectionId: "section1"
lastLineId: "1"
nextLineConfig:
auto: { enabled: false }
pendingEffects:
Expand All @@ -104,6 +110,8 @@ in:
- state:
global:
isLineCompleted: true
viewedRegistry:
sections: []
nextLineConfig:
auto:
enabled: true
Expand All @@ -129,6 +137,10 @@ in:
out:
global:
isLineCompleted: false
viewedRegistry:
sections:
- sectionId: "section1"
lastLineId: "1"
nextLineConfig:
auto:
enabled: true
Expand Down Expand Up @@ -161,6 +173,8 @@ in:
- state:
global:
isLineCompleted: true
viewedRegistry:
sections: []
nextLineConfig:
auto:
enabled: true
Expand All @@ -187,6 +201,10 @@ in:
out:
global:
isLineCompleted: false
viewedRegistry:
sections:
- sectionId: "section1"
lastLineId: "1"
nextLineConfig:
manual:
enabled: true
Expand Down Expand Up @@ -225,6 +243,8 @@ in:
trigger: "fromComplete"
delay: 2000
pendingEffects: []
viewedRegistry:
sections: []
projectData:
story:
scenes:
Expand All @@ -251,6 +271,10 @@ out:
delay: 2000
pendingEffects:
- name: "handleLineActions"
viewedRegistry:
sections:
- sectionId: "section1"
lastLineId: "1"
projectData:
story:
scenes:
Expand Down
6 changes: 3 additions & 3 deletions spec/system/selectors/selectIsLineViewed.spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ in:
lastLineId: "10"
- sectionId: "abc"
lineId: "10"
out: false
out: true
---
case: lineId before lastLineId (numeric comparison)
in:
Expand Down Expand Up @@ -142,7 +142,7 @@ in:
actions: {}
- sectionId: "intro"
lineId: "3"
out: false
out: true
---
case: lineId after lastLineId with projectData
in:
Expand Down Expand Up @@ -297,4 +297,4 @@ in:
lastLineId: undefined
- sectionId: "notfound"
lineId: "5"
out: false
out: false
5 changes: 1 addition & 4 deletions src/createEffectsHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ const handleLineActions = (
const handledLineActions = engine.handleLineActions();

const renderDispatchCountAfter = getRenderDispatchCount?.() ?? 0;
if (
renderDispatchCountAfter === renderDispatchCountBefore &&
(!handledLineActions || renderDispatchCountBefore === 0)
) {
if (renderDispatchCountAfter === renderDispatchCountBefore) {
render({ engine, routeGraphics, trackRenderDispatch }, payload);
}
};
Expand Down
Loading
Loading