From 1ee605c02e02439e1d1b57477478889bcda809db Mon Sep 17 00:00:00 2001 From: han4wluc Date: Mon, 18 May 2026 21:53:45 +0800 Subject: [PATCH] fix: reserve choice render layer --- docs/RouteEngine.md | 7 ++--- package.json | 2 +- spec/projectDataSchema.test.js | 18 +++++++++++-- spec/renderLayers.test.js | 26 +++++++++++++++++++ .../constructPresentationState.spec.yaml | 4 +-- spec/system/constructRenderState.spec.yaml | 13 +++++++++- spec/system/renderState/addVisuals.spec.yaml | 8 +++--- src/renderLayers.js | 3 ++- src/schemas/presentationActions.yaml | 2 +- vt/reference/visual/layers-01.webp | 4 +-- vt/reference/visual/layers-02.webp | 4 +-- vt/specs/visual/layers.yaml | 20 +++++++++++--- vt/specs/visual/opacity-blur.yaml | 2 +- vt/specs/visual/transform-overrides.yaml | 2 +- 14 files changed, 91 insertions(+), 24 deletions(-) create mode 100644 spec/renderLayers.test.js diff --git a/docs/RouteEngine.md b/docs/RouteEngine.md index 7494b84..2a75e72 100644 --- a/docs/RouteEngine.md +++ b/docs/RouteEngine.md @@ -698,9 +698,10 @@ behavior. | `40` | `CHARACTER` | Engine character layer | | `50` | `VISUAL_BEHIND_DIALOGUE` | Before dialogue | | `60` | `DIALOGUE` | Engine dialogue/UI layer | -| `70` | `VISUAL_FOREGROUND` | Above story UI/layouts | +| `70` | `CHOICE` | Engine choice UI layer | +| `80` | `VISUAL_FOREGROUND` | Above choice/UI/layouts | -Visual items can use `10`, `30`, `50`, or `70`. Layer `70` is still below +Visual items can use `10`, `30`, `50`, or `80`. Layer `80` is still below screen transitions, overlay stack entries, and confirm dialogs. JavaScript callers can use the exported `RENDER_LAYER`, `VISUAL_LAYER`, and `DEFAULT_VISUAL_LAYER` constants when generating project data. @@ -793,7 +794,7 @@ visual: - id: vignette resourceId: vignette transformId: fullscreen - layer: 70 + layer: 80 opacity: 0.8 ``` diff --git a/package.json b/package.json index 471b48a..3735aa0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "route-engine-js", - "version": "1.20.0", + "version": "1.20.1", "description": "A lightweight Visual Novel engine built in JavaScript for creating interactive narrative games with branching storylines", "repository": { "type": "git", diff --git a/spec/projectDataSchema.test.js b/spec/projectDataSchema.test.js index 264481a..95d1a50 100644 --- a/spec/projectDataSchema.test.js +++ b/spec/projectDataSchema.test.js @@ -475,7 +475,7 @@ describe("projectData schema", () => { id: "vignette", resourceId: "vignette", transformId: "fullscreen", - layer: 70, + layer: 80, }, ], }, @@ -484,7 +484,7 @@ describe("projectData schema", () => { expect(validatePresentationActions.errors).toBeNull(); }); - it("rejects arbitrary visual layer values", () => { + it("rejects non-visual layer values", () => { expect( validatePresentationActions({ visual: { @@ -498,6 +498,20 @@ describe("projectData schema", () => { }, }), ).toBe(false); + + expect( + validatePresentationActions({ + visual: { + items: [ + { + id: "vignette", + resourceId: "vignette", + layer: 70, + }, + ], + }, + }), + ).toBe(false); }); it("accepts animation playback continuity in presentation action payloads", () => { diff --git a/spec/renderLayers.test.js b/spec/renderLayers.test.js new file mode 100644 index 0000000..c844bf2 --- /dev/null +++ b/spec/renderLayers.test.js @@ -0,0 +1,26 @@ +import { describe, expect, it } from "vitest"; + +import { + DEFAULT_VISUAL_LAYER, + RENDER_LAYER, + VISUAL_LAYER, + VISUAL_LAYER_VALUES, +} from "../src/renderLayers.js"; + +describe("render layer constants", () => { + it("reserves choice above dialogue and keeps foreground visuals above choice", () => { + expect(RENDER_LAYER).toMatchObject({ + VISUAL_BEHIND_BACKGROUND: 10, + BACKGROUND: 20, + VISUAL_BEHIND_CHARACTER: 30, + CHARACTER: 40, + VISUAL_BEHIND_DIALOGUE: 50, + DIALOGUE: 60, + CHOICE: 70, + VISUAL_FOREGROUND: 80, + }); + expect(VISUAL_LAYER.FOREGROUND).toBe(80); + expect(VISUAL_LAYER_VALUES).toEqual([10, 30, 50, 80]); + expect(DEFAULT_VISUAL_LAYER).toBe(50); + }); +}); diff --git a/spec/system/constructPresentationState.spec.yaml b/spec/system/constructPresentationState.spec.yaml index 1193bdf..6623aeb 100644 --- a/spec/system/constructPresentationState.spec.yaml +++ b/spec/system/constructPresentationState.spec.yaml @@ -681,7 +681,7 @@ in: layer: 30 - id: "vignette" resourceId: "effect-vignette" - layer: 70 + layer: 80 out: visual: items: @@ -690,7 +690,7 @@ out: layer: 30 - id: "vignette" resourceId: "effect-vignette" - layer: 70 + layer: 80 --- case: character item opacity and blur persist across omitted character action in: diff --git a/spec/system/constructRenderState.spec.yaml b/spec/system/constructRenderState.spec.yaml index 4256fe0..31c1310 100644 --- a/spec/system/constructRenderState.spec.yaml +++ b/spec/system/constructRenderState.spec.yaml @@ -134,7 +134,7 @@ in: - id: "foreground" resourceId: "foregroundImage" transformId: "visualTransform" - layer: 70 + layer: 80 - id: "defaultBehindDialogue" resourceId: "defaultImage" transformId: "visualTransform" @@ -156,6 +156,11 @@ in: resourceId: "dialogueUi" content: - text: "Layered" + choice: + resourceId: "choiceUi" + items: + - id: "go" + content: "Go" layout: resourceId: "storyLayout" resources: @@ -210,6 +215,10 @@ in: elements: - id: "dialogue-box" type: "container" + choiceUi: + elements: + - id: "choice-box" + type: "container" storyLayout: elements: - id: "story-layout" @@ -308,6 +317,8 @@ out: scaleY: 1 - id: "dialogue-box" type: "container" + - id: "choice-box" + type: "container" - id: "layout-storyLayout" type: "container" x: 0 diff --git a/spec/system/renderState/addVisuals.spec.yaml b/spec/system/renderState/addVisuals.spec.yaml index ae789c4..403ca8e 100644 --- a/spec/system/renderState/addVisuals.spec.yaml +++ b/spec/system/renderState/addVisuals.spec.yaml @@ -1933,7 +1933,7 @@ in: - id: "foreground" resourceId: "foregroundImage" transformId: "transform1" - layer: 70 + layer: 80 - id: "defaultLayer" resourceId: "defaultImage" transformId: "transform1" @@ -2055,7 +2055,7 @@ out: value: 0 audio: [] --- -case: invalid visual layer should throw clear error +case: reserved choice layer should throw clear error for visuals in: - elements: - id: "story" @@ -2071,7 +2071,7 @@ in: - id: "fog" resourceId: "fogImage" transformId: "transform1" - layer: 20 + layer: 70 resources: images: fogImage: @@ -2085,4 +2085,4 @@ in: rotation: 0 scaleX: 1 scaleY: 1 -throws: 'Visual item "fog" layer must be one of: 10, 30, 50, 70.' +throws: 'Visual item "fog" layer must be one of: 10, 30, 50, 80.' diff --git a/src/renderLayers.js b/src/renderLayers.js index 63993b4..c4d4834 100644 --- a/src/renderLayers.js +++ b/src/renderLayers.js @@ -5,7 +5,8 @@ export const RENDER_LAYER = Object.freeze({ CHARACTER: 40, VISUAL_BEHIND_DIALOGUE: 50, DIALOGUE: 60, - VISUAL_FOREGROUND: 70, + CHOICE: 70, + VISUAL_FOREGROUND: 80, }); export const VISUAL_LAYER = Object.freeze({ diff --git a/src/schemas/presentationActions.yaml b/src/schemas/presentationActions.yaml index cfd6d16..0ef48ad 100644 --- a/src/schemas/presentationActions.yaml +++ b/src/schemas/presentationActions.yaml @@ -318,7 +318,7 @@ properties: - 10 - 30 - 50 - - 70 + - 80 description: Numeric render layer for the visual item. Defaults to 50. Same-layer items preserve array order. opacity: type: number diff --git a/vt/reference/visual/layers-01.webp b/vt/reference/visual/layers-01.webp index c751db8..8e8e7c9 100644 --- a/vt/reference/visual/layers-01.webp +++ b/vt/reference/visual/layers-01.webp @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:142f022456c972f2bf2eff398c197be966f8e97efe47ce4d9565d98ab9c9988c -size 3482 +oid sha256:ddac4efec5247fba6ba19a53a0fb2f4dfaa2a850cc2d408d8eb6def2da61f6c1 +size 3574 diff --git a/vt/reference/visual/layers-02.webp b/vt/reference/visual/layers-02.webp index 737b1f7..8e8e7c9 100644 --- a/vt/reference/visual/layers-02.webp +++ b/vt/reference/visual/layers-02.webp @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df0439e8c51e538069ffd266f26d93e7ca72e967990bf382e51a6d1cd87730a6 -size 2740 +oid sha256:ddac4efec5247fba6ba19a53a0fb2f4dfaa2a850cc2d408d8eb6def2da61f6c1 +size 3574 diff --git a/vt/specs/visual/layers.yaml b/vt/specs/visual/layers.yaml index 16b5a80..f99b4b5 100644 --- a/vt/specs/visual/layers.yaml +++ b/vt/specs/visual/layers.yaml @@ -7,7 +7,7 @@ description: | - layer 10 visuals render before the background layer - layer 30 visuals render between background and character - layer 50 visuals render between character and dialogue - - layer 70 visuals render above dialogue and normal story layouts + - layer 80 visuals render above choice, dialogue, and normal story layouts - same-layer visuals preserve authored item order skipInitialScreenshot: true steps: @@ -142,6 +142,15 @@ resources: width: 1680 height: 240 colorId: fg10 + choiceUi: + elements: + - id: choice-panel + type: rect + x: 320 + y: 860 + width: 1280 + height: 90 + colorId: fg30 storyLayout: elements: - id: layout-panel @@ -253,7 +262,7 @@ story: - id: foreground resourceId: foregroundLayer transformId: fullScreen - layer: 70 + layer: 80 - id: behindDialogueOne resourceId: behindDialogueLayerOne transformId: fullScreen @@ -275,6 +284,11 @@ story: resourceId: dialogueUi content: - text: Layered visuals + choice: + resourceId: choiceUi + items: + - id: confirm + content: Confirm layout: resourceId: storyLayout - id: characterOverlapVisuals @@ -293,7 +307,7 @@ story: - id: overlapForeground resourceId: overlapForeground transformId: fullScreen - layer: 70 + layer: 80 - id: overlapBehindDialogueFirst resourceId: overlapBehindDialogueFirst transformId: fullScreen diff --git a/vt/specs/visual/opacity-blur.yaml b/vt/specs/visual/opacity-blur.yaml index 51edc2e..939b490 100644 --- a/vt/specs/visual/opacity-blur.yaml +++ b/vt/specs/visual/opacity-blur.yaml @@ -62,7 +62,7 @@ story: - id: glow resourceId: visual-sprite transformId: visual-center - layer: 70 + layer: 80 opacity: 0.45 blur: x: 6 diff --git a/vt/specs/visual/transform-overrides.yaml b/vt/specs/visual/transform-overrides.yaml index bec08e8..885619f 100644 --- a/vt/specs/visual/transform-overrides.yaml +++ b/vt/specs/visual/transform-overrides.yaml @@ -56,7 +56,7 @@ story: - id: logo resourceId: logo transformId: visual-left - layer: 70 + layer: 80 - id: visualTransformPatched actions: visual: