自前で既読ログを作成管理する方法の考察

迷走か進化か - 永字八法の続き。
シナリオライターには、半角英数の命令を使用させず、実質テキストのみでシナリオを記述させることが前提。
こうすると、luacall textが呼び出された時=シナリオの現在行がカウントアップされるとみなすことができる。

概要

仮に、現在の実行行番号を保存する変数を、%scenalio_cursorとする。
これの値は必ずしも実際の実行行数とは一致しない。単純にNSCALL_text()が呼び出される度に内部でインクリメントされる変数である。
また、これとは現在実行しているシナリオ名を保存する変数も作る。仮に、$scenalio_nameとでもする。実質、ここにはラベルが入ると思う。
そして、既読管理システムを作る。これは以下の命令を持つとする。

  1. 現在これから実行するシナリオ名を指定し、%scenalio_cursorをリセットする命令。
  2. シナリオ名と行番号を与えると、それが未読か既読かを返す命令。
  3. シナリオ名と行番号を与えると、それを実行済み(あるいは未読)にする命令。
  4. シナリオ名を指定して(あるいは全てのシナリオに対して)全て未読もしくは既読にする命令。(これはほぼ開発専用命令になるかも)
  5. シナリオを終了させる命令。(行毎の既読未読判定の他に、シナリオ毎の既読未読記録があれば、シーン回想が楽になる。)

細かいシーケンス

function NSCALL_text() endの中でどのようなことが発生するか。
※%scenalio_cursorは初期化されると0になるが、luaでは1オリジンの方が色々都合がよい。

  • NSCALL_text()が実行される。
  • 現在、シナリオが実行中であれば
    • %scenalio_cursorをインクリメント
    • その行の既読未読情報を取得
  • シナリオを実行
    • その行を既読にする。
  • 処理を終える。

これが基本だが、さらに細かい処理が入る。具体的には、シナリオライターによる細かい分岐を容易にするための処理である。
たとえば、あるキャラクターのセリフに対し、主人公の返答が主人公の性格パラメータによって変わるとする。変わるのはセリフだけで、ストーリーは分岐後再び一つに集約される。
生のNScripterのシナリオでは、このようになる。

*scenalio001
(中略)
ライバル「○○○?」

; 性格による分岐
tablegoto %propaty,*scenalio001_1_1,*scenalio001_1_2,*scenalio001_1_3

*scenalio001_1_1
主人公「性格1の回答」
goto *scenalio001_1_end

*scenalio001_1_2
主人公「性格2の回答」
goto *scenalio001_1_end

*scenalio001_1_3
主人公「性格3の回答」
goto *scenalio001_1_end

*scenalio001_1_end
ライバル「そうなのか」
(中略)
*scenalio001end
return

面倒臭い。やってられるかボケ。ラベル名書き間違えてミスとかしそうでストレスだ。
それをこのように書き換えられるかやってみる。

*scenalio001
(中略)
ライバル「○○○?」
【性格分岐・1】
主人公「性格1の回答」
【性格分岐・2】
主人公「性格2の回答」
【性格分岐・3】
主人公「性格3の回答」
【性格分岐・終了】
ライバル「そうなのか」
(中略)
【シナリオ終了】
return

注意すべきなのは、性格分岐をした場合、選ばれなかった性格のシナリオは、未読のままにしなければならないことだ。既読スキップを行っている場合、これは重要な仕様になる。
そこで、シーケンスを次のように書きかえる。
$scenalio_branchを設定する。これは、分岐の名前を設定する。
1.NSCALL_text()が実行される。
2.現在、シナリオが実行中であれば
2.1.%scenalio_cursorをインクリメント
2.2.その行の既読未読情報を取得
3.シナリオを実行する前に、$scenalio_branchが""でないなら
3.1.$scenalio_branchの内容によって、そのシナリオ行を実行するかしないかを決める。しないのであれば6まで飛ぶ。
4.シナリオを実行
4.1.実行した内容が「性格分岐」だった場合、
4.1.1.次のタグが「終了」なら、$scenalio_branchを""に。
4.1.2.それ以外なら、$scenalio_branchをタグと同じにする。
5.その行を既読にする。
6.NSCALL_text()を終了させる。

選択による分岐

また、これを応用すれば、選択肢も簡単に記述できることがわかる。

*scenalio001
(中略)
ライバル「○○○?」
【質問・選択肢1・選択肢2・選択肢3】
【分岐・1】
主人公「選択肢1の回答」
【分岐・2】
主人公「選択肢2の回答」
【分岐・3】
主人公「選択肢3の回答」
【分岐・終了】
ライバル「そうなのか」
(中略)
【シナリオ終了】
return

【質問】タグで選択肢を表示、プレイヤーに一つを選択させ、後は分岐と同じように処理ができる。
注意しなければならない点は、選択肢を表示する時に、既読の選択肢の色は変えたいこと。
これをどうやって実装するかは現在考え中。