*defineは、*startの後に

NScripterの開発について、思ったことをつらつら書いていく。
と言っても、タイトルで述べていることは、NScripterの入門サイトのどれかで書かれていたことで、特段珍しいことではない。
NScripterスクリプトを組む時、何も考えていないとこのようになる。

*define
game
*start
end

こうするとスクリプトは上から下に流れるように実行されるので、直観的である。
しかし、*define節と*start節は順序が逆になってもきちんと実行される。

*start
end
*define
game

これでも全く問題はない。
だが、人間の直観に逆らってこうすることにどんなメリットがあるのか。それは開発に必要な手間の軽減にある。
仮に、シナリオとプログラマの二人が組んでノベルゲームを作るプロジェクトを想定する。大雑把に言って、シナリオライターは*start節を書き、プログラマは*define節を書くと言う分担だ。
シナリオライターは早々にシナリオを組み上げ、演出をちりばめてプログラマーに渡してきた。プログラマーは努力していたが、システムカスタマイズに若干手間取っているとする。たとえば、ある場面での特殊な演出のプログラムがうまく動かない、とか。
そのシーンが冒頭ならともかく、終盤のどこかだったりすると、開発途中ではあるが、その直前あたりでセーブをしてから何度もトライアンドエラーをするのが普通だろう。
NScripterのセーブシステムは、変数の状態と、どの行から実行するかの情報を記録している。この「どの行から」と言うのが曲者であって、もし、「*define→*start」のスクリプト構造だと、プログラマがいじる度にそれに釣られて本文の行番号も変わってしまうのだ。その場合でもセーブデータをロードすることはできるが、それが正しく動作すると言う保証はどこにもなくなってしまう。
シナリオが完成した以上、*start節は変更しないと考えれば、「*start→*define」の順の方がセーブデータの使いまわしができて開発速度に寄与すると考えられる。

実装

私の使っているテンプレートはこのようになっている。

ファイル名 内容
00.txt *start
01.txt〜49.txt ; シナリオを記述する。
50.txt end
*define
mov %0,100
51.txt〜74.txt ; 主にnumaliasとdefsubを記述する。
75.txt game
76.txt〜98.txt ; システムカスタマイズなどで使うサブルーチン群を記述する。
99.txt mesbox "おかしな部分を実行しています。","警告"
end

サブルーチン群には、呼び出されなければ行けないように、*define節によって区切りをする意味もある。

追記で補足

コメント欄より

40.txtと99.txtにendが二つあるのは何か理由があるのですか?

endが二つあるのはエラートラップ。
実は、*define節は*start節の中から普通に呼び出したりできる。なので、50.txtのendには、ルーズなシナリオライターがシナリオの最後にendをつけ忘れたり、ループし損ねたりした時に、*define節が実行されないようにする意味がある。
76.txt〜98.txtには、ラベルとreturnの組み合わせしかないはずだが、万が一プログラマがreturnをつけ忘れたりしたような時に99.txtのendと直前のメッセージはそれを知らせるために必要。