Liminalia

既定のシステム画面

作品のプラグインがシステム画面を差し替えないときに使う、エンジンの既定のシステム画面(packages/web/src/system/)。 プレイヤーから見た操作と画面をまとめる。見た目は default.css の CSS 変数(--lm-*)、文言は strings.ts(英語は strings-en.ts)。 作品ごとの見た目と操作は liminalia.json の ui で変える(下の「ゲームの設定」。Studio の「ゲームの設定」のタブで見本を見ながら編集できる)。

流れ

「クリックで開始」(指で触る端末は「タップで開始」。デスクトップ版は出さない)→ タイトル → ゲーム。終わったらタイトルへ戻る。

タイトル画面のボタンは、はじめから、つづきから(セーブデータがあれば)、おまけ(何か一つでも解放したら)、コンフィグ、終了(デスクトップ版だけ)。

操作

マウス、キーボード指ゲームパッド
送るクリック、Enter、Space、ホイール下タップA
一つ戻る、進め直すPageUp、PageDown(ホイール下は、戻ったところからは進め直す)—LB、LT
メニュー右クリック、Esc長押し、右上のメニューのボタンSTART、B
画面を閉じる右クリック、Esc閉じるボタンB
バックログホイール上下へなぞるY
ウィンドウを消すクイックメニューの「ウィンドウを消す」上へなぞるX
スキップCtrl を押している間—RB を押している間
コンフィグクイックメニューの「コンフィグ」メニューからSELECT
クイックセーブ/ロードS / Lメニューから—
前の選択肢へ、次の選択肢へメニューから(キーは ui.keys で割り当てる)メニューから—
ボタンを選ぶ矢印キー—十字キー、左スティック
フルスクリーンAlt+Enter、F11(デスクトップ版)、コンフィグの「画面モード」コンフィグの「画面モード」—
  • オート、スキップ中の送りは、止めるだけ。選択肢でもオート、スキップは止まる。ウィンドウを消している間は、どの操作もウィンドウを戻すだけ
  • 矢印キー、十字キーは、開いている画面、タイトル、選択肢のボタンを、向きに近い順に選ぶ(選んでいるボタンに枠線が出る)。 コンフィグのスライダーは左右で値が変わる
  • 指で触る端末では、クイックメニューの代わりに右上のメニューボタンを出す。横長の作品を縦に持つと、横にするよう知らせる
  • Android 版の戻るボタンは Esc と同じ
  • 次の選択肢へ: 次の選択肢へ飛ぶ(文字送りはせず、ロードと同じように選択肢の時点を組み立て直す。飛ばした本文はバックログに入り、 CG や章の解放などスキップで通っても起きることは起きる。既読にはならない。この先に選択肢が無ければ知らせるだけ)。 未読はコンフィグのスキップの設定に従い、「既読のみ」なら選択肢の手前の最初の未読のページへ飛んで止まる。 ui.nextChoice.readOnly: true にすると、次の選択肢まで既読のときだけ飛ぶ(未読があれば知らせるだけ)。 先に画面なしで流して確認し、この先に選択肢が無ければ知らせて飛ばさない
  • 巻き戻し: 一つずつ戻る(確認しない)。戻ったページの文字は一度に出す。今のページの直前に選択肢を出していれば、その選択肢へ戻る。 戻ったところからは進め直せ(前に選んだ選択肢の先へ)、読み進めても同じところなら続けて進め直せる。選び直すと進め直す先は消える。 戻れるのは新しいページから 100 ページまで。既読、おまけの解放、設定は戻さない。BGM は戻った先のものになる。 ui.rollback.fixChoices: true で戻ったあとは前に選んだものしか選べなくし、ui.rollback.enabled: false で使えなくする。 ui.input.wheelUp: "rollback" でホイール上をバックログの代わりに一つ戻る操作にする。クイックメニューには書いたときだけ置ける(rollback、rollforward)
  • 次の未読へ: 既読のページを飛ばし、未読のページで止まる(コンフィグのスキップの対象が「すべて」でも止まる。選択肢でも止まる)。 先に画面なしで流して確認し、終わりまで既読なら知らせて飛ばさない。クイックメニューのボタンとキーは書いたときだけ(next-unread、ui.keys.nextUnread)
  • 既読の選択肢: ジャンプ先の最初のページ(ジャンプ先の頭の @jump は三つまでたどる)を読んだことのある選択肢は、少し薄く出す (ui.choice.readColor で色を決めると、薄くせずにその色にする)。シナリオからジャンプ先を割り出すので、作品は何も書かなくてよい
  • スクリーンショット(メニュー。クイックメニューのボタンとキーは書いたときだけ: screenshot、ui.keys.screenshot): デスクトップ版はウィンドウを そのまま撮って写真のフォルダの「<題名>」に置く。ブラウザ版、スマホ版はゲーム画面(メッセージウィンドウとシステム画面を除く)を PNG で保存させる
  • バックログを検索: バックログの画面の上の欄で、話者と本文を検索する(当たったページだけ並べる。入力中の Esc は文字を消すだけ)
  • 前の選択肢へ: 前に出た選択肢から選び直す(選んでいる最中なら一つ前の選択肢。確認してから。以降の進行状況は失われる)。 戻れるのはこのプレイで出た新しい 50 個まで。セーブには新しい 10 個を入れ、ロードした後もそこから戻れる (セーブの容量が足りなければ、選択肢の控えから諦める)
  • クイックメニューには書いたときだけ置ける(ui.menuBar.items に prev-choice、next-choice)。プラグインからは system.perform("prev-choice") など

作品がシステム画面を差し替えても、指、ゲームパッド、矢印キーの操作は bindPlayerInput(docs/plugins.md)で同じものを使える。

開発中の道具

  • 変数の監視(開発サーバーだけ。F9 で出し入れ): 遊びながら game、temp、system の変数を見る。名前で絞り込め、☆で留めたものは上に 並ぶ(このブラウザに覚える)。値を押すと JSON の書き方で書き換えられる(今のプレイの変数に入れるだけで、セーブはしない)。書き出しには入らない
  • 重さの計測(開発サーバーだけ。F10 で出し入れ): 左上に、フレームレート(直近 1 秒)、長いフレーム(50ms を超えたもの)の数と一番長いもの、 長いタスクの数(Long Tasks API のあるブラウザ)、JS のメモリ(performance.memory のある Chromium だけ。無ければ「取得できません」)、 DOM の要素の数、読み込んだ素材の数と大きさ(画像、音、動画、フォント、そのほか)、読み込みに時間のかかった素材を出す。 フレームは表示している間だけ数え、「リセット」でフレームの数を 0 に戻す。素材は Resource Timing から数え、開発サーバーは素材を毎回確かめ直すので、 書き出したゲームより多く出ることがある。書き出しには入らない
  • lim profile: 上の計測を、Chromium でプレイヤーを開いて決めたページ数を進めながら取り、レポートにする(下)
lim profile [プロジェクト] [ファイル[#ラベル]] [--pages 30] [--wait 500] [--choose 0,1] [--out フォルダ] [--json]
  • 始める位置を省くと liminalia.json の entry から。ページを出し切ってから --wait ミリ秒待って数値を取り、次へ送る (行の途中のクリック待ちも送る。同じページの数値は一つにまとめる)。選択肢は --choose の番号(0 始まり)を順に選び、使い切ったら最初の選択肢
  • exports/profile(--out で変える)に 計測.md(要約、時間のかかった素材、ページごとのフレームレート、メモリ、DOM の要素、素材の数)と 計測.json を書き、要約を出す。--json は JSON を出すだけ
  • Chromium は lim shot と同じもの(LIMINALIA_CHROMIUM か Playwright の chrome-headless-shell)

画面

  • セーブ、ロード: サムネイルと本文の冒頭つきのセーブスロット。クイックセーブは別のタブ。使っているスロットには「メモ」(一行のメモを付ける)、 「保護」(上書き、削除、移動先にできない)、「コピー」と「移動」(押してから写し先のスロットを選ぶ。ほかのページへも。保護は移動だけが持っていく)、「削除」がある
  • バックログ: 話者(キャラクターの名前の色)と本文(ルビ、装飾、改行込み)。ボイスのあるページは再生ボタンで鳴らし直せる。 新しい 100 ページまでは「ここに戻る」でそのページまで戻れる(確認してから。以降の進行状況は失われる)
  • コンフィグ: 「テキスト」のタブにテキスト速度、オートの待ち時間、スキップの対象(既読のみ/すべて)、言語(作品が多言語のとき)と、 テキスト速度のプレビュー。「画面」のタブにウィンドウの不透明度(メッセージウィンドウの背景の不透明度。文字は薄くしない)、文字サイズ(80〜150%。選択肢にも効く)、 フォント(標準/明朝/ゴシック/丸ゴシック/UD。UD は読みやすさのために作られた書体で、無ければゴシック)、画面効果(「控えめ」で画面と立ち絵の揺れ、振動、文字の動き、@effect の動きを止める。 OS で視差効果を減らしていれば、はじめから控えめ)と、フルスクリーンに切り替えられる環境では画面モード(ウィンドウ/フルスクリーン。デスクトップ版は次の起動でも同じ)。 「サウンド」のタブに BGM、効果音、ボイスの音量、聞いた話者ごとの声の音量(まだ声を聞いていない人物は 出さない)、次へ進んだときにボイスを止めるか、ボイス再生中に BGM を下げるか(既定は下げる)。 「画面」のタブには、テキスト読み上げ(ページを出し終えたら話者と本文を端末の声で読む。ボイスのある台詞は読まない。選択肢と、キーやゲームパッドで選んだ 画面のボタンも読む)と高コントラスト(メッセージウィンドウを不透明な黒に、文字を白に、選択肢とシステム画面の縁をくっきり。OS でコントラストを上げていれば はじめから)、デスクトップ版のウィンドウの大きさ(標準と、ゲームの大きさの 0.5〜2 倍のうち今の画面に収まるもの)、確認のメッセージ (オフにすると、タイトルへ、終了、ロード、上書き、ジャンプの確認を省く。セーブの削除、読み込み、引き継ぎ、記録を送るは省かない)、 動かさないマウスのカーソルを隠す(ゲーム中、3 秒動かさなければ隠す)、色覚の補正(1 型、2 型、3 型。見分けにくい色の差を見分けやすい色の差へ 移す色の行列を、文字や画面のボタンも含めた舞台全体にかける)、効果音の字幕(ui.captions と @se caption=)もある。 「操作」のタブでは、キーの割り当て(操作ごとに「変更」を押してからキーを押す。作品の ui.keys と既定の上に重なる)と、ゲームパッドのボタンの 入れ替え(役目ごとに「変更」を押してからボタンを押す。そのボタンのこれまでの役目は、前にその役目だったボタンへ移る)を変えられる。本文は一字ずつの要素で組むので、スクリーンリーダーには、ページを出し終えた一続きの文を見えない場所(aria-live)で渡す。 作品の CSS は --window-opacity、--text-scale、--player-font と舞台の lm-reduce-motion、lm-high-contrast で、作品の部品を合わせられる。 下に「セーブデータをエクスポート」「セーブデータをインポート」(セーブデータ、既読情報、コンフィグの設定、システム変数を一つの .json にする。バックアップと、別の コンピューターやブラウザへの移し替えに使う。別のゲームのものはインポートしない)
  • おまけ: CG(解放した差分だけを、クリックで送って見る)、回想(再生し、終わるとおまけへ戻る。回想の間はセーブできない)、 音楽、実績。一覧は liminalia.json の extra(format.md §5)
  • 音楽室(extra.music: { file, title?, artist?, description? } の配列。@bgm で一度鳴らした曲だけ選べる): 下の再生の欄に今の曲、 アーティスト、説明、シークバー、再生と一時停止、前後の曲、リピート(全曲、一曲、なし)、シャッフル。全曲とリピートなしは 曲を繰り返さずに鳴らし、終わったら次の聞いた曲へ進む(リピートなしは最後の曲で止まる)。一曲リピートは埋め込みのループの位置で繰り返す
  • 用語集(extra.glossary: { id, term, reading?, aliases?, description, group?, link? } の配列): 本文に term、aliases が出たら 解放して知らせ、本文の中でその語に点線の下線を付ける(押すと説明が出て、本文は送らない。link: false は下線を付けずに解放だけ。 縦書きの数字のまとまりなど、一字と並びがずれるところには下線を付けない)。@glossary-unlock id でも解放する。おまけの「用語集」に読みの順で並べる
  • 章選択(extra.chapters: { id, title, target, synopsis?, thumb? } の配列): target の最初のページにはじめて着いたら解放し、 その時点を控える。おまけの「章」から、題、あらすじ、画像を見て、その章から読み直せる(確認してから)
  • フローチャート(extra.flowchart を true か { "titles": { "ファイル#ラベル": "題" } } にすると使える。タイトル、メニューから開く): シナリオからシーンのつながりを作る(作品は何も書かなくてよい)。はじめ、選択肢のジャンプ先、合流するところでシーンを区切り、一本道や カスタムコマンドのジャンプ先(調べる箇所など)は来たシーンにまとめる。読んだシーンだけ題(最初の本文か titles)を出し、まだのシーンは伏せる。 はじめて着いたシーンはその時点を控え(全セーブ共通)、そこから読み直せる(確認してから)。上に既読率(本文のページのうち読んだもの)と エンディングの数、「エンディング」のタブに終わりの一覧(@title-screen の ending か「エンディング n」)

既読(ページの位置)、設定、システム変数(おまけの解放の記録を含む)は全セーブ共通で保存する。 liminalia.json の saves.protect が true なら、どれも読めない形で書き、書き換えたものは無いものとして扱う。 afterLoad(ファイル#ラベル)があれば、ロードの直後にそこを呼び出してから(@return で)ロードしたページへ戻る (セーブに入らない temp. を計算し直すなど)。 セーブはページの位置と一緒に、そのページの目印(行と、本文、選択肢の文から作る鍵)を持つ。作品を更新してシナリオの行がずれても、 ロードで同じ文のページを探して再開する(見つからなければ元の行にいちばん近いページ。どちらも診断 save-relocated で知らせる)。

音

  • イントロ付きループ: OGG(Vorbis、Opus)の曲に埋め込んだ LOOPSTART と LOOPLENGTH(または LOOPEND。どちらも標本の数)を読み、 イントロのあとの区間を繰り返す(ゲーム音楽で広く使われている書き方)。@bgm の loop-start、loop-end で書けば、そちらが勝つ
  • クロスフェード: @bgm 曲 crossfade=2s は、新しい曲を読み込んでから、今の曲を消しつつ新しい曲を入れる(読み込みの間も今の曲は鳴り続ける)
  • 定位と再生速度: @bgm、@se の pan(-100 で左、100 で右)と rate(1 で等速。音の高さも変わる)。セーブとロード、一時停止のあとも保つ
  • システムボイス(ui.systemVoice): title(タイトル画面を出したとき。タイトルコール)、actions(操作したとき。タイトルの start、load、extra、flowchart、config、exit と、メニューの操作 save、load、auto、skip、backlog など)、confirm(確認のダイアログ)に、 voice フォルダの音の候補を並べる。候補はファイルか { "file": …, "if": "system.cleared", "character": "A" } で、if が真のものから一つを選ぶ (クリア後だけのタイトルコールなど)。character を書くと、コンフィグの「サウンド」に「システムボイス」(ランダム、各キャラクター、オフ)が出る。 volume(0〜100)は全体の音量。ボイスの音量に従い、シナリオのボイスは止めない

言語

作品が多言語(liminalia.json の languages)なら、コンフィグで言語を選べる。選んでいなければ、ブラウザ(端末)の言語に 合うもの、無ければ原文の言語で始める。システム画面の文言は日本語と英語を組み込みで持ち、ほかの言語は翻訳ファイルの ui で 上書きする(format.md §5 の多言語)。

ゲームの設定(liminalia.json の ui)

どれも省略でき、省略は上の既定の見た目と操作。形は @liminalia/lang の UiConfig(lim check と Studio のチェックが確認する)。 長さはゲーム画面の px、色は CSS の色。画像は image、背景は bgimage、BGM は bgm、フォントのファイルは others のフォルダからのパス。

項目中身
text本文のフォント(font)、フォントのファイル(fontFile)、大きさ、色、行の高さ、飾り(decoration: none / shadow / edge と decorationColor)、既読のページの色(readColor)、揃え(align: left / center / right。中央でも文字を出す途中で行が動かない)、日本語の組み方の数値(ルビの大きさ rubySize(親文字の倍率。既定 0.45)、ルビと親文字の間 rubyGap(親文字の倍率。既定 0。縦書きは右との間)、一行の字数 lineChars(縦書きは一列。一字を 1em として数えるので、縦書きではフォントが縦書きの字送り幅を持つこと。持たないフォントのファイルは lim check が知らせる)、禁則の強さ lineBreak(strict / normal / loose / anywhere)、行末の句読点のぶら下げ hangingPunctuation(Safari、iOS だけ)、NVL の段落の間 paragraphGap(文字サイズの倍率。既定 0.5、0 で一行ぶんだけ進む))
messageメッセージウィンドウの位置と大きさ(x y width height。一つでも書くと左上と大きさで置く)、内側の余白、背景の色、画像、枠線の色、角の丸み、scroll(収まらない本文を改ページせずにメッセージウィンドウの中でスクロールさせ、文字に合わせて下へ送る。本文の上のホイールは読み返しに使う)、vertical(縦書き。シナリオの @vertical でも切り替えられる)
name名前欄の位置(メッセージウィンドウの左上から)、大きさ、色、背景、画像、枠線の色
face顔ウィンドウ。話している人物の顔をメッセージウィンドウの中に出し、本文をその分ずらす。show(always: 常に。offstage: その人物の立ち絵がないときだけ)、side(left / right)、width height(既定 180)。顔の絵はキャストの face(docs/design/format.md §5)。縦書き、NVL、吹き出しのあいだは出さない
help操作説明の画面(メニューの「操作説明」と @open-screen help)。操作の表は今の割り当て(遊ぶ人の割り当て、keys、既定、ゲームパッドの入れ替え)から作る。pages は [{ "title": …, "image": …, "text": … }] でタブを足し(遊び方の説明など)、title: true ならタイトル画面にも出す
captions効果音と BGM の字幕(コンフィグの「効果音の字幕」をオンにした人に、画面の右上に少しのあいだ出す)。sounds は効果音のファイル → 字幕(@se の caption= が優先)、music: true なら BGM が替わったときにおまけの音楽の一覧の題名を出す
tones立ち絵の色調(@tone)の名前 → { color, amount, saturation, brightness, contrast }。color を amount(0〜1)だけ掛け合わせ、彩度、明るさ、コントラストは 1 でそのまま。組み込み(sepia、mono、night、evening、dark)と同じ名前なら置き換える。シナリオに無い名前を書くと lim check が知らせる
glyphクリック待ちアイコンの文字か画像、色、動き(bounce / blink / none)、大きさ size(本文の倍率。既定 0.7)、本文との間 gap(アイコンの大きさの倍率。既定 0.3。縦書きは上下)
choice選択肢のボタンの幅、文字サイズ、間、色、背景、マウスオーバー時の背景、枠線の色、画像、マウスオーバー時の画像
systemシステム画面の配色(dark / light)、アクセントカラー、フォント
titleタイトル画面の背景、BGM、題名の画像(logo)、題名の文字を出すか(showName)。variants は [{ "if": "(system.cleared)", "background": …, "bgm": …, "logo": … }] のように、全セーブ共通の変数の条件が真の最初のものが書いた項目だけ置き換える(クリア後に変わるタイトル)
menuBarクイックメニューに置くボタンと並び(items。ほかに prev-choice、next-choice、持ち物の items も置ける)、位置(position: bottom-right / bottom-left / bottom-center / top-right / top-left / hidden)、指で触れる端末のメニューボタン(touchButton)
cursorマウスのカーソルの画像と押す点。animated なら画像を舞台に描いてマウスに付けて動かす(GIF、APNG が動く)
tapタップエフェクトの画像(image)、大きさ(size)、消えるまで(time)と鳴らす効果音(se。sound から)
screenfullscreen を false にするとフルスクリーンにさせない(コンフィグの「画面モード」、Alt+Enter、F11、@fullscreen が効かない)。pauseScenario を true にすると、標準の画面(コンフィグ、セーブ、ロード、バックログ、確認など)を開いている間、シナリオの次のコマンドを止める(@wait の残りや時間で進む演出が裏で進まない。既定は止めない)
saveSlotsセーブスロットの数(8 の倍数に切り上げ、8〜200。既定 48)
backlogバックログに残すページの数(10〜2000。既定 300)
defaultsはじめてプレイする人のコンフィグの値(テキスト速度、オートの待ち時間、BGM、効果音、ボイスの音量、スキップで未読も飛ばすか、ウィンドウの不透明度 windowOpacity、文字サイズ textScale、ボイス再生中に BGM を下げるか duckBgm)
keysキーの割り当て(advance、menu、backlog、hide、auto、skip、quickSave、quickLoad、save、load、config、prevChoice、nextChoice → KeyboardEvent.key の名前の配列)。書いた操作だけ既定を置き換える

見た目(文字、メッセージウィンドウ、名前欄、クリック待ちアイコン、選択肢、カーソル)はプレイヤーがゲーム画面に当て(DomHost.setUi)、差し替えたシステム画面でも効く。 セーブスロット、バックログ、コンフィグの初期値、キー、タイトル画面、クイックメニューは既定のシステム画面が読む(差し替えたシステム画面は SystemEnv.project.ui から読める)。 開発サーバーでは、ui を書き換えると開いているプレイヤーにそのまま効く(起動し直さない)。

素材とシナリオの暗号化(デスクトップ版)

liminalia.json の build.encrypt を true にすると(制作画面のプロジェクトの設定の「素材とシナリオを暗号化する」)、デスクトップ版の 素材、シナリオ、データ、キャスト、訳、フローチャートと、書き出しに入れるパックを暗号化(AES-256-CTR)して入れ、アプリが読むときに戻す (音、動画の飛ばし読みにも答える)。まとめたファイル(asar)を開いても、そのままでは使えない。鍵はアプリの中に入れるしかないので、 手間をかければ破れる(どのエンジンも同じ)。

  • 鍵は作品ごとに一つで、.liminalia/encrypt-key に控える(どの OS の書き出しも同じ鍵)。パッチ(lim patch)は前の書き出しのアプリの中の鍵で 中身を戻して比べ、パッチも同じ鍵で暗号化する
  • ブラウザ版とスマホ版は暗号化しない(プレイヤーの端末へ届ける以上、隠し切れないため)
  • 書き出しの画面に、暗号化、セーブデータの保存先、保護の今の設定を出す(押すとプロジェクトの設定のその欄を開く)

フォントのサブセット化

liminalia.json の build.subsetFonts に { "files": ["others/fonts/main.otf"], "text": "足す字" } を書くと、書き出すときにそのフォントを 作品で使う字だけにサブセット化する(ASCII、かな、全角の英数字と記号はいつも残し、シナリオ、data、翻訳、liminalia.json、作品のプラグインのソース、 エンジンのシステム画面の文言から字を集める)。名前の入力など、ほかから字が出るなら text に足す。形式(ttf、otf、woff、woff2)は保つ。

Steam(デスクトップ版)

  • クラウドセーブ: デスクトップ版のセーブ、既読、設定は鍵ごとのファイルで置く(前のバージョンが localStorage に置いたものは、はじめて 起動したときに移す)。Steamworks の Auto-Cloud では、Windows、Linux(既定の実行ファイルの横)はルートを「App Install Directory」、 サブディレクトリを savedata、パターンを *.json にする。Mac(標準の場所)はルートを「Mac Application Support」、サブディレクトリを <題名>/saves にする(saves.location を替えたときは、置いた場所に合わせる)
  • 在席表示(Rich Presence): @presence "第一章 星を待つ丘" で出し、空で消す。extra.chapters の章に着いたら章の題を自動で出し、 タイトルに戻ったら消す。Steamworks の在席表示の訳に "#Status" を "%status%" で用意する
  • Steam Deck、ゲームパッド: 文字の入力(@text-input)は、Steam Deck で遊んでいるか、ゲームパッドがつながっているとき、Steam の画面の キーボードを出す(打ったら決める。やめたらゲームの欄で打てる)
  • ワークショップ(build.steam.workshop): 購読したアイテムをパックとして読み込む
  • Steam Input(build.steam.input): アクションをゲームパッドのボタンやシステム画面の操作に結ぶ
  • DLC: build.store.products の steam(DLC の App ID)で @purchase と system.purchases が使える。 ワークショップ、Steam Input、DLC の設定は distribution.md

クラッシュレポート

書き出したゲームは、捕まえていない例外を、バージョン、プレイしていた位置、環境と一緒に一つ残す(デスクトップ版は保存先の logs/ にもファイルで残し、 画面のプロセスが落ちたら記録して開き直す)。liminalia.json の build.crashReport.url を書くと、次に起動したときにプレイヤーに尋ね、 「はい」ならクラッシュレポートを JSON で POST する(project、message、stack、at、version、userAgent、date)。

注意書き、表現の切り替え、エディション

  • 注意書き(ui.contentWarning: { title?, text, button?, always? }): 起動したとき、タイトルの前に出す。同意したら次からは出さない (文を変えたら出し直す。always ならいつも)。デスクトップ版は「終了」で閉じる
  • 表現を控えめにする(ui.contentFilter: { label?, default?, assets? }): コンフィグの「画面」のタブに項目を出す。シナリオは system.content_filter で分け、assets("bgimage/a.png": "a_safe.png" のように種類のフォルダからのパスへ)で素材を差し替える。 変えたらゲーム中の画面を組み直す
  • エディション(build.variants と lim build … --variant 名前): エディションごとに forbid(入っていたら止める。全年齢版に成人向けの素材が 入らないように)、exclude(入れない)を足し、contentFilter: true なら控えめにしたまま変えさせない。シナリオからは system.variant で エディションの名前を見る。書き出し先はエディションごとに分かれる

体験版からの引き継ぎ

liminalia.json の saves.carryFrom に、引き継げる作品(体験版など)の id → 題名を書く({ "my_game_demo": "わたしの物語 体験版" })。

  • はじめて起動したとき、この作品にまだセーブが無く、その作品のセーブが見つかれば、引き継ぐかを一度だけ尋ねる。ブラウザ版は同じ保存先 (同じ場所に置いたもの)から、デスクトップ版はその題名のアプリの保存先から読む。読めない形(saves.protect)のセーブも引き継げる
  • コンフィグの「セーブデータをインポート」でも、その作品がエクスポートしたセーブデータを読み込める

追加の中身、修正(パック)

作品のフォルダの packs/<id>/ に pack.json(id、title、version、requires(必要な本体のバージョン)、order(読み込む順)、extra)と scenario/、data/、assets/<種類>/ を置くと、lim build が書き出しの packs/ に入れる。後から配るときは lim pack packs/<id> で 書き出し、ゲームの packs/(デスクトップ版は Resources/packs/。Steam の DLC のデポなど)に置く。

  • 起動したときに読み込み、シナリオ、データ、素材は同じ名前のものを本体より先に使う(修正の差し替え)。新しい名前のものは足す
  • extra(CG、回想、音楽、実績、用語集、章)は本体の一覧の後ろに足す。フローチャートはパックに重ねたもの(lim pack が作る)を使う
  • シナリオからは system.packs.<id>(値はバージョン)で入っているかを見られる(@if (system.packs.dlc1))
  • requires が本体のバージョン(liminalia.json の version)と違うパックは読み込まない。開発サーバーでは読み込まない
  • デスクトップ版は、保存先の packs/(アップデートで入れたもの)、Resources/packs/、Steam ワークショップで購読したもの(build.steam.workshop)、 書き出しに入れたものの順に探し、同じ id は前にあるものを使う

アップデート(デスクトップ版)

liminalia.json の build.update.url に、lim update-manifest が作るマニフェストの URL を書くと、デスクトップ版は起動したとき(checkOnStartup: false なら しない)と、メニューの「アップデートを確認」(シナリオからは @check-update)で、新しいパックや本体があるかを確かめる。

  • 起動したときの確認は待たずに行い、タイトル画面でほかの画面が開いていないときに尋ねる。断ったものは、次の起動からは尋ねない(メニューからは尋ねる)
  • パック: 「はい」なら取り寄せて sha256 を確かめ、保存先の packs/<id>/ に入れて、再起動するかを尋ねる
  • 本体: 「はい」なら、その OS のファイルがあれば取り寄せて確かめ、Windows のインストーラーなら起動してゲームを終了する(ほかは開く)。 ファイルが無ければダウンロードのページを開く
  • 尋ねるのは標準の確認のダイアログ(作品が screens.confirm を差し替えていればそれ。種類は update と update-restart)。 作り方とマニフェストの形は distribution.md