Ink나 Yarn에서 오셨다면
Ink와 Yarn Spinner는 흐름(flow)입니다: 텍스트가 위에서 아래로 흐르고, divert나 jump가 위쪽을 포함해 어디로든 보냅니다. Lute는 그 일을 둘로 나눕니다:
- 장면 안에서 텍스트는 앞으로 흐르며, 되돌아가는 길은 횟수가 정해진
::jump … max루프 하나뿐입니다. 메뉴는<branch>(한 번 묻습니다)와<hub>(플레이어가 떠날 때까지 다시 묻습니다)이고, 상태에 따라 줄을 고르는 것은<match>와when=입니다. - 장면 사이에서는 아무것도 divert하지 않습니다. 엔진이 계기(occasion)(“다음 장”, “플레이어가 틸리에게
말을 건다”)를 발생시키고, 각 장면은 자기가 어느 계기에 응답하는지 말합니다. 순서는
after:,priority:, 매니페스트의chapters:가 정합니다. 장면을 이야기로 잇기가 차근차근 보여 줍니다.
모든 루프는 횟수가 정해져 있고 잘 중첩되며 그 밖에는 뒤로 뛰는 것이 없으므로, 검사기는 장면을 지나는 모든 경로를 따라갈 수 있고, 그래서 어느 경로도 막다른 길이 아님을 증명할 수 있습니다.
대응표
섹션 제목: “대응표”| Ink | Yarn Spinner | Lute |
|---|---|---|
=== knot === | 노드(title: … ===) | 장면: 자기 .lute 파일 하나, 프런트매터에 kind: scene과 id: |
= stitch | — | 장면 안의 ## 제목 섹션(섹션은 점프 대상이 아닙니다) |
이야기의 다음 부분으로 가는 -> knot | <<jump Node>> | 계기(on:)에 응답하는 다른 장면. 순서는 after:나 chapters: |
플레이어가 돌아오는 메뉴로 가는 -> knot | 허브 노드로 돌아가는 <<jump>> | <hub>: 선택지 하나가 끝날 때마다 다시 묻고, exit 선택지에서 끝납니다 |
아래쪽의 이름 붙은 gather로 가는 -> label | — | 아래쪽 ::label{name="label"}로 가는 ::jump{to="label"} |
앞선 gather로 되돌아가는 반복되는 -> label | 재시도의 시작으로 되돌아가는 <<jump>> | 앞선 ::label로 가는 ::jump{to="label" max="3"}. 최대 max번 실행됩니다(0.51). 루프와 뒤로 가는 divert 참고 |
한 번만 고르는 * [choice] | -> option <<once>> | 허브의 <choice … once>. <branch>는 어차피 한 번만 묻습니다 |
계속 남는 + [choice] | -> option | <hub> 안의 평범한 <choice> |
- gather | 선택지 다음 줄들 | </branch> 다음 줄들. 어느 선택지를 골랐든 실행됩니다 |
VAR oil = 1 | <<declare $oil = 1>> | 스키마나 프런트매터의 state: 아래 run.oil: { type: int, default: 1 } |
CONST MAX = 5 | — | defs: 아래의 def. @MAX로 읽습니다 |
LIST mood = calm, stormy | — | enum 타입 경로: run.mood: { type: { enum: [calm, stormy] }, default: calm } |
~ oil = oil + 2 | <<set $oil to $oil + 2>> | ::set{run.oil += 2} |
텍스트 안의 {oil} | 텍스트 안의 {$oil} | {{run.oil}} |
{oil > 0: text} | <<if $oil > 0>> | 가드를 단 줄: @narrator{when="run.oil > 0"}: text |
{x: a | b} | <<if>> … <<else>> | 가드를 단 줄 두 개, 또는 <otherwise> 갈래가 있는 <match> |
{a | b | c}, {&a | b}, {!a | b} | <<once>> … <<else>> | scene.* 카운터와 그것을 읽는 <match>(아래) |
{~a | b} | 줄 그룹(=>), dice(), random() | Lute에는 무작위 선택이 없습니다(아래) |
방문 횟수 {knot}, {knot > 2} | visited_count("Node") | 직접 ::set{… += 1} 하는 int 경로. visited('<id>')는 참/거짓뿐입니다 |
-> DONE | <<stop>> | ::end, 또는 그냥 장면의 끝 |
-> END | 이야기의 끝 | ::set이 참으로 만드는 스키마의 terminal: 조건(아래) |
터널 -> knot -> | — | 컴포넌트: ::use{component="…"} |
INCLUDE | 여러 .yarn 파일 | 프로젝트: lute.project.yaml 아래의 모든 .lute 파일. 공유 상태는 uses:로 가져오는 스키마에 |
| — | 노드 그룹(when: 헤더) | 한 계기에 응답하는 여러 장면, 각자 자기 when:. 자격 있는 장면 중 priority:가 가장 높은 것이 재생됩니다(Beats) |
| — | when: once, when: always | once: 키: once: run(기본값), once: user, once: false |
# tag | #tag | 자유 형식 줄 태그는 없습니다(아래) |
| — | 현지화 줄 id #line:… | 콘텐츠 줄의 code=: @narrator{code="0010"}: …. lute tag가 채워 넣고, 컴파일된 lineId는 이것으로 만들어집니다(Dialogue & cast) |
knot 하나를 옮기기
섹션 제목: “knot 하나를 옮기기”어느 Ink 이야기의 등대 램프실입니다: 플레이어가 계속 돌아오는 메뉴, 장부를 읽을 때마다 바뀌는 텍스트, 한 번만 고르는 선택지, 인라인 조건이 있습니다.
VAR oil = 1
=== lamp_room ==={lamp_room == 1: The lamp room smells of cold brass.|The lamp room again. The dark is closer.}+ [Read the ledger] -> ledger* [Go down to the stores] -> stores+ [Wait for dark] -> dusk
=== ledger ==={ledger:- 1: The last entry is three weeks old. "Oil low. Ship due."- 2: You read it again. The handwriting shakes toward the end.- else: The words have stopped changing.}-> lamp_room
=== stores ===You find two more cans of oil.~ oil = oil + 2-> lamp_room
=== dusk ==={oil >= 3: The lamp catches.|The wick sputters.}-> ENDLute에서는 knot 네 개가 장면 하나입니다. lamp.lute로 저장하세요:
---kind: sceneid: lamptitle: The Lamp Roomstate: run.oil: { type: int, default: 1 } scene.ledgerReads: { type: int, default: 0 }---
## The Lamp Room
@narrator: The lamp room smells of cold brass.<hub id="lamp"> <return> @narrator: The lamp room again. The dark is closer. </return> <choice id="ledger" text="Read the ledger"> ::set{scene.ledgerReads += 1} <match subject="scene.ledgerReads"> <when is="1"> @narrator: The last entry is three weeks old. "Oil low. Ship due." </when> <when is="2"> @narrator: You read it again. The handwriting shakes toward the end. </when> <otherwise> @narrator: The words have stopped changing. </otherwise> </match> </choice> <choice id="stores" text="Go down to the stores" once> @narrator: You find two more cans of oil. ::set{run.oil += 2} </choice> <choice id="dusk" text="Wait for dark" exit> @narrator{when="run.oil >= 3"}: The lamp catches. @narrator{when="run.oil < 3"}: The wick sputters. </choice></hub>- 플레이어가 돌아오는 knot이
<hub>입니다. 딸린 knot 하나하나가 허브의 선택지이고, 각 knot 끝의-> lamp_room은 적지 않아도 됩니다: 선택지의 줄이 실행되고 나면 허브가 다시 묻습니다.exit선택지만 허브를 떠납니다. +는 평범한 선택지,*는once입니다.once선택지는 한 번 고르면 메뉴에서 빠집니다.- 처음 들어올 때의 텍스트는
<hub>앞의 줄이며 한 번만 재생됩니다. 돌아올 때마다의 텍스트는<return>블록입니다:exit이 아닌 선택지가 끝날 때마다, 메뉴가 다시 나오기 전에 실행됩니다. 마지막once선택지가 메뉴를 비울 때도, 허브가 닫히기 직전에 실행됩니다. - 방문 횟수
{ledger: - 1 … - 2 … - else …}는 선택지가 직접 세는 숫자와 그것을 읽는<match>입니다. 허브도 고른 것을scene.visited.lamp.ledger로 기록하지만, 그 값은 참/거짓뿐이고 선택지 자신의 줄 안에서는 이미 참입니다: 고른 것은 그 줄들이 실행되기 전에 기록됩니다. - 인라인 조건
{oil >= 3: …|…}는 각자when=을 단 두 줄입니다. run.oil은 이 파일이 혼자서 검사되도록 이 장면에서 선언했습니다. 프로젝트에서는 다른 장면도 읽으므로 모든 장면이 가져오는 스키마에 둡니다.
lute trace는 장면을 지나는 경로 하나를 재생합니다. --choice는 허브에서 고를 것을 순서대로 적습니다:
$ lute trace lamp.lute --choice lamp=ledger,ledger,stores,ledger,dusktrace: lamp.lute (seeds: 0 paths, 0 facts; 5 selections) ## The Lamp Room @narrator The lamp room smells of cold brass. <hub lamp> eligible: ledger, stores, dusk -> ledger ::set scene.ledgerReads = 1 <match scene.ledgerReads> -> arm 1 (is="1") @narrator The last entry is three weeks old. "Oil low. Ship due." -- return (hub lamp) -- @narrator The lamp room again. The dark is closer. <hub lamp> eligible: ledger, stores, dusk -> ledger ::set scene.ledgerReads = 2 <match scene.ledgerReads> -> arm 2 (is="2") @narrator You read it again. The handwriting shakes toward the end. -- return (hub lamp) -- @narrator The lamp room again. The dark is closer. <hub lamp> eligible: ledger, stores, dusk -> stores @narrator You find two more cans of oil. ::set run.oil = 3 -- return (hub lamp) -- @narrator The lamp room again. The dark is closer. <hub lamp> eligible: ledger, dusk -> ledger ::set scene.ledgerReads = 3 <match scene.ledgerReads> -> otherwise @narrator The words have stopped changing. -- return (hub lamp) -- @narrator The lamp room again. The dark is closer. <hub lamp> eligible: ledger, dusk -> dusk guard `run.oil >= 3`: taken @narrator The lamp catches. guard `run.oil < 3`: skippedtrace complete: 10 decisions; choices 3/3 (lamp), arms 3/3 (scene.ledgerReads @19:5), guard `run.oil >= 3` @36:5: taken, guard `run.oil < 3` @37:5: skippedstores는 한 번 고른 뒤 메뉴에서 빠집니다. 나머지 허브 규칙은 Choices & hubs에
있습니다. 검사기가 모든 허브에 요구하는 규칙도 거기 있습니다: 허브는 끝날 수 있어야 합니다(가드 없는
exit 선택지, 또는 once 선택지만).
바뀌는 텍스트
섹션 제목: “바뀌는 텍스트”Lute에는 인라인 대안이 없습니다. Ink의 {a|b|c}와 Yarn의 <<once>> … <<else>>는 <match>가 고르는 온전한
줄이 되고, <match>가 읽는 숫자는 직접 관리합니다.
- 시퀀스(
{a|b|c}, 또는{stopping: …}): 숫자를 올리고1,2, 그리고 마지막 것은<otherwise>로 맞춥니다. 위의 장부가 그렇게 합니다. - 한 번만(
{!a|b}): 같은 방법에<otherwise>를 비워 둡니다. - 순환(
{&a|b|c}): 센 숫자를 줄 수로 나눈 나머지를 def로 만들어 맞춥니다:
---kind: sceneid: harbortitle: The Harborstate: scene.looks: { type: int, default: 0 }defs: weather: { type: int, cel: "scene.looks % 3" }---
## The Harbor
<hub id="harbor"> <choice id="look" text="Look at the sea"> ::set{scene.looks += 1} <match subject="@weather"> <when is="1"> @narrator: Fog on the water. </when> <when is="2"> @narrator: Mist over the rocks. </when> <otherwise> @narrator: Rain, sideways. </otherwise> </match> </choice> <choice id="leave" text="Walk inland" exit> @narrator: You turn your back on the sea. </choice></hub>- 섞기(
{~a|b}, Yarn의dice()): Lute는 무작위로 고르지 않습니다. 그래야 한 경로의 trace와 play가 언제나 같은 것을 출력합니다. 주사위는 엔진이 굴리게 하세요: 엔진이 쓰는 경로(스키마에서owner: engine)를 선언하고 그 경로에<match>를 겁니다.
scene.* 카운터는 장면이 제시될 때마다 처음부터 다시 셉니다. 런 동안 유지하려면 run.*에, 영영
유지하려면 user.*에 세세요.
장면 사이를 옮겨 가기
섹션 제목: “장면 사이를 옮겨 가기”뒤쪽 knot으로 가는 divert는 두 번째 장면이 되고, 엔진이 그 장면이 응답하는 계기를 발생시킵니다.
매니페스트의 chapters:는 장면 목록에 순서를 줍니다: 계기를 발생시킬 때마다 다음 장면이 제시됩니다.
Ink의 {cond: -> a | -> b}는 같은 계기에 응답하는 장면 두 개이고, 각자 when:을 답니다. 두 조건이 동시에
성립할 수 없으면 검사기는 둘을 같은 priority로 받아들입니다. 장면을 이야기로
잇기가 바로 이것을 만듭니다.
장면 안에서 ::jump{to="…"}는 같은 문서의 ::label{name="…"}로 뜁니다. max가 없으면 앞으로만 갑니다.
플레이어가 돌아오는 메뉴는 <hub>이고, 다시 재생되는 장면은 자기 계기에 다시 응답합니다.
## 제목 섹션은 점프 대상이 아닙니다 — {#id} 접미사를 단 섹션도 마찬가지입니다. 그 id는 안정적인 식별
정보일 뿐이므로, 뛰어가고 싶은 제목 아래에 ::label을 두세요. 선택지 id(Ink의 이름 붙은 선택지
* (inside))도 대상이 아닙니다. 선택지 id는 고른 것의 이름이지 텍스트 안의 자리가 아닙니다.
루프와 뒤로 가는 divert
섹션 제목: “루프와 뒤로 가는 divert”0.51부터 앞선 gather로 되돌아가는 Ink의 -> label과 재시도의 시작으로 되돌아가는 Yarn의
<<jump Node>>에 쓸 표기가 생겼습니다. 앞선 ::label로 가는 ::jump{to="…" max="N"}이며, 최대 N번(1부터
99까지의 리터럴) 실행되고 그 뒤에는 제어가 다음으로 이어집니다:
::label{name="again"}@narrator: The latch is stuck.<branch id="try"> <choice id="pull" text="Pull it"> ::jump{to="again" max="3"} </choice> <choice id="leave" text="Leave it"> @narrator: You leave it. </choice></branch>이 한계가 Lute와 자유로운 divert의 차이입니다. 루프는 언제나 횟수가 정해져 있습니다. 조건이 바뀔 때까지
반복하는 divert는 쓸 표기가 없는데, 한계가 1부터 99까지의 리터럴이기 때문입니다(@ref, 0, 더 큰 수는
E-JUMP-MAX). 라벨은 점프와 같은 본문이나 그것을 감싸는 본문에 있어야 하고, 사이에는 <choice>와 <match>
arm 본문만 둘 수 있으므로 루프는 <hub> 옵션, <on>이나 <objective>를 벗어날 수 없습니다
(E-JUMP-LOOP-SCOPE). 문서의 최상위 ## 섹션들은 하나의 본문이므로 루프가 섹션을 넘을 수 있습니다(0.53).
루프는 라벨에서만 진입하고(E-JUMP-INTO-LOOP), 루프끼리는 중첩되되 겹치지 않습니다
(E-JUMP-LOOP-CROSS). 다른 장면으로 되돌아가는 것은 여전히 점프가 아니라 계기입니다. “내가 떠날 때까지 다시
물어라”처럼 플레이어가 멈출 때를 정한다면 <hub>를 쓰세요. 허브와 루프를 참고하세요.
visited('<scene id>')는 장면이 한 번이라도 제시되었는지 묻습니다. 횟수가 아니라 참/거짓이고, 세이브
전체에 걸칩니다: 새 런이 시작되어도 참으로 남습니다.
이야기 끝내기
섹션 제목: “이야기 끝내기”Ink에는 끝이 두 가지 있고, Lute도 그렇습니다.
-> DONE은 이 흐름을 끝냅니다. Lute에서는::end, 또는 장면의 끝입니다: 제시가 끝나고, 다음 계기가 다른 장면을 제시할 수 있습니다.-> END는 이야기를 끝냅니다. Lute에서는 스키마의 조건terminal:입니다. 이 조건이 성립하면 새 런이 시작될 때까지 엔진은 더는 계기를 발생시키지 않습니다(타이틀 화면처럼outsideRun: true로 선언한 계기는 예외입니다). 장면은 평범한::set으로 조건을 성립시킵니다.
state: run.fate: { type: { enum: [open, drowned] }, default: open }terminal: "run.fate == 'drowned'"<choice id="leap" text="Climb the rail toward the light" once> @narrator: The rail is wet. The light is very far away. ::set{run.fate = "drowned"} ::end</choice>lute play가 둘 다 보여 줍니다: ::end는 장면을 닫고, 그 뒤로 게임은 끝난 상태입니다.
@narrator: The rail is wet. The light is very far away. set run.fate = "drowned"::end (this presentation ends) note: the game is over — `terminal: run.fate == 'drowned'` holds, so the engine raises no occasion from here (`occasion:` / `advance:` steps are refused; `newRun: true` starts a new run)── end: terminal — `terminal: run.fate == 'drowned'` holds ──────────────플레이 스크립트는 expect: { end: terminal }로 이것을 확인합니다(스토리 플레이).
주석과 태그
섹션 제목: “주석과 태그”// 주석은 자기 줄에만(또는 디렉티브 뒤에) 쓰고, /* … */는 여러 줄에 걸칩니다. 줄의 텍스트 뒤에 오는
//와 #은 텍스트의 일부입니다: 플레이어에게 보이고, 검사기가 경고합니다(W-TEXT-COMMENT-LIKE).
Lute에는 자유 형식 태그가 없습니다. Ink나 Yarn 태그가 엔진에 알리던 것은 대신 검사되는 자리가 있습니다:
- 줄을 어떻게 전달할지: 줄 속성,
@keeper{emotion="tired"}: …(Dialogue & cast); - 효과음, 음악, 카메라: 자기 줄에 쓰는 디렉티브,
::sfx{…},::music{…}(Core directives); - 문서 자체의 태그(Yarn의
tags:헤더): 프런트매터의extra: { tags: [...] }.
검사기가 하는 말
섹션 제목: “검사기가 하는 말”.lute 파일 안의 Ink나 Yarn 문법은 Lute에서 쓸 형태를 알려 주는 오류나 경고가 됩니다. Ink 줄로 된 파일:
$ lute check ink.luteink.lute:10:1: error [E-UNCLASSIFIED] unrecognized line: `=== lamp_room ===` is an Ink knot; a Lute scene is its own `.lute` file (`kind: scene` and `id: lamp_room` in its frontmatter), and a section inside a scene is a `## lamp_room` headingink.lute:11:1: error [E-UNCLASSIFIED] unrecognized line: `= stitch` is an Ink stitch; a section inside a scene is a `## stitch` headingink.lute:12:1: error [E-UNCLASSIFIED] unrecognized line: `VAR` declares an Ink global; Lute declares state under `state:` in the frontmatter (`run.oil: { type: int, default: 1 }`) and writes it with `::set{…}`ink.lute:13:1: error [E-UNCLASSIFIED] unrecognized line: `~ run.oil = run.oil + 2` is Ink logic; Lute writes state with `::set{run.oil = run.oil + 2}`, and the path is declared under `state:` in the frontmatterink.lute:14:1: error [E-UNCLASSIFIED] unrecognized line: a content line needs a speaker: narration is `@narrator: …`, dialogue `@<speaker>: …`ink.lute:15:1: error [E-UNCLASSIFIED] unrecognized line: `* [Read the ledger]` is an Ink choice; Lute choices are `<choice id="…" text="…">` blocks inside a `<branch>` (asked once) or a `<hub>` (asked again until an `exit` choice); Ink's once-only `*` in a loop is a `<hub>` choice with the `once` flagink.lute:16:1: error [E-UNCLASSIFIED] unrecognized line: `+ [Wait for dark]` is an Ink choice; Lute choices are `<choice id="…" text="…">` blocks inside a `<branch>` (asked once) or a `<hub>` (asked again until an `exit` choice); Ink's sticky `+` is a plain `<hub>` choiceink.lute:17:1: error [E-UNCLASSIFIED] unrecognized line: `- gather` is an Ink gather; Lute has no gathers: after a `<branch>` or `<hub>` closes, the lines below it run whichever choice was taken, so write the gathered text there as an ordinary line (`@narrator: …`)ink.lute:18:1: error [E-UNCLASSIFIED] unrecognized line: `-> ledger` is an Ink divert; Lute has no diverts: `::jump{to="ledger"}` jumps forward to a `::label{name="ledger"}` later in this document, a `<hub>` repeats its choices until an `exit` choice, and another scene is reached through the occasion it answers (`on:` in its frontmatter)ink.lute:19:1: error [E-UNCLASSIFIED] unrecognized line: `-> END` is an Ink divert; it ends the whole story, which in Lute is the schema's `terminal:` condition: a scene makes it hold with an ordinary `::set{…}` (`::end` is Ink's `-> DONE`: it ends only this scene)failed: ink.lute (10 error(s), 0 warning(s))Yarn도 마찬가지입니다:
$ lute check yarn.luteyarn.lute:10:1: error [E-UNCLASSIFIED] unrecognized line: `<<declare $oil = 1>>` is a Yarn declaration; Lute declares state under `state:` in the frontmatter (`run.oil: { type: int, default: 1 }`) and writes it with `::set{…}`yarn.lute:11:1: error [E-UNCLASSIFIED] unrecognized line: `<<set $oil to 3>>` is a Yarn command; Lute writes state with `::set{run.oil = 3}`, and the path is declared under `state:` in the frontmatteryarn.lute:12:1: error [E-UNCLASSIFIED] unrecognized line: `<<if $oil > 2>>` is a Yarn conditional; Lute chooses between lines with `<match subject="…">` and its `<when is="…">`/`<when test="…">` arms, or guards one line: `@narrator{when="run.oil > 2"}: …`yarn.lute:13:1: error [E-UNCLASSIFIED] unrecognized line: `<<jump Lamp_Room>>` is a Yarn jump; Lute has no diverts: `::jump{to="Lamp_Room"}` jumps forward to a `::label{name="Lamp_Room"}` later in this document, a `<hub>` repeats its choices until an `exit` choice, and another scene is reached through the occasion it answers (`on:` in its frontmatter)yarn.lute:14:1: error [E-UNCLASSIFIED] unrecognized line: `-> Read the ledger` is a Yarn option; Lute choices are `<choice id="…" text="…">` blocks inside a `<branch>` (asked once) or a `<hub>` (asked again until an `exit` choice)failed: yarn.lute (5 error(s), 0 warning(s))다른 언어의 마크업으로 쓴 텍스트는 문자 그대로 쓴 것일 수도 있으므로 오류가 아니라 경고입니다. 한 겹 중괄호, 줄 끝의 주석이나 태그, 대괄호를 쓴 선택지 텍스트:
$ lute check text.lutetext.lute:10:21: warning [W-TEXT-SINGLE-BRACE] `{run.oil}` is literal line text: single braces are not read, so the player sees them — interpolation is `{{run.oil}}`text.lute:11:12: warning [W-TEXT-SINGLE-BRACE] `{run.oil > 0: A can of oil sits by the…}` is literal line text: single braces are not read, so the player sees them — Lute has no inline conditional text; guard the whole line instead (`@narrator{when="run.oil > 0"}: …`), or choose between lines with `<match>`text.lute:12:12: warning [W-TEXT-SINGLE-BRACE] `{~Fog|Mist|Rain}` is literal line text: single braces are not read, so the player sees them — Lute has no inline alternatives (Ink's `{~…|…}` shuffle, `{&…|…}` cycle, `{!…|…}` once-only, `{…|…}` sequence); choose between whole lines with `<match>` or guarded lines (`when="…"`)text.lute:13:17: warning [W-TEXT-SINGLE-BRACE] `{$oil}` is literal line text: single braces are not read, so the player sees them — Yarn's `{$oil}` is an interpolation of a declared state path in Lute, `{{run.oil}}`text.lute:14:41: warning [W-TEXT-COMMENT-LIKE] `# mood:cold` is part of the line text, so the player sees it — Lute has no line tags; keep a note as a `// …` comment on a line of its owntext.lute:15:33: warning [W-TEXT-COMMENT-LIKE] `// TODO darker` is part of the line text, so the player sees it — a comment is `// …` on a line of its own (or after a directive), never after texttext.lute:17:25: warning [W-TEXT-BRACKET-LABEL] choice text `[Go inside]` shows its brackets on the button — choice text is shown exactly as written (Lute has no Ink-style bracket suppression); write `text="Go inside"`ok: text.lute (7 warning(s))디렉티브처럼 쓴 divert, 뒤로 가는 점프, 점프 대상으로 쓴 섹션 제목, 조건 안의 Ink·Yarn 습관:
$ lute check flow.luteflow.lute:12:1: error [E-UNKNOWN-DIRECTIVE] unknown directive `::goto` — did you mean `::jump{to="…"}`? It jumps forward to a `::label{name="…"}`flow.lute:13:12: error [E-JUMP-BACKWARD] `::jump` targets label `top`, which is not forward of this `::jump` in document order — `::jump` only jumps forward unless it carries `max`: `::jump{to="top" max="3"}` repeats at most 3 times; to offer choices again, use a `<hub>` (it asks until an `exit` choice is taken)flow.lute:18:12: error [E-JUMP-UNDEFINED] `::jump` targets `Gallery`, which no `::label` in this document declares — `Gallery` is the `## Gallery` heading, not a label; a `::jump` target is a `::label{name="…"}` later in this document, so put `::label{name="Gallery"}` under that headingfailed: flow.lute (3 error(s), 0 warning(s))$ lute check cond.lutecond.lute:5:7: error [E-CEL-PROFILE] `once` is not a condition: how often a beat plays is its own key, `once` — `once: run` (once per run, the default), `once: user` (once ever) or `once: false` (every time); on a `<beat>` or `<entry>` it is `once="run"` — drop this `when`cond.lute:12:17: error [E-CEL-TYPE] `visited('gallery') > 2`: `>` compares numbers (int/double), and `visited('gallery')` is a bool — `visited('gallery')` is a bool, whether the scene was ever presented, not how often; count visits in an `int` path you `::set` (for example `::set{run.visits += 1}`)cond.lute:13:17: error [E-CEL-PROFILE] `$oil`: a state path takes no `$` (`$` alone is the `<match>` subject) — write the path with its tier — did you mean `run.oil`?cond.lute:14:17: error [E-CEL-TYPE] `run.oil == true` compares an int with a bool, so it is never truefailed: cond.lute (4 error(s), 0 warning(s))Ink의 첫 방문 검사를 허브의 기록으로 옮긴 경우도 잡힙니다: 선택지 자신의 줄 안에서는 기록이 이미 참이므로, 거짓을 요구하는 갈래는 결코 실행되지 않습니다.
first.lute:11:7: error [E-ARM-DEAD] arm can never fire: its pattern `false` never matches here — `scene.visited.lamp.ledger` is `true` in its option's own arm — the visit record is set when the choice is picked, before its arm runs모든 코드는 진단 코드에 있고, lute --explain <CODE>가 그 항목을 출력합니다.