システムカスタマイズについてダラダラやるよーその5

システムカスタマイズについてダラダラやるよーその4 - 永字八法の続き。
NScripterのシステムカスタマイズについて、1から話をしてみようと思う。その第5回目。

セーブ・ロードの仕組みについて

NScripterでは、あるタイミングで変数の状態などのスナップショットを作成して取っておき、save(or save2)命令でそれをセーブデータとして出力します。このセーブデータはload命令で読み出せます。

いつセーブするか。

これは、*define節で、autosaveoffを実行しているかいないかで変わって来ます。

autosaveoffをしていない時

saveon/saveoff命令が重要になって来ます。
saveon命令を実行すると、その行以降、全ての行でスナップショットが作成されます。for-nextなどでスプライトを動かしていたりしても、それぞれの行でスナップショットが作成されるため、動作が非常に遅くなります。
saveoffを実行すると、その行以降、自動でのスナップショット作成が起こらなくなります。なので、動作は最速になりますが、スクリプトの任意の場所で、savepoint命令を使ってスナップショットを作成しなければなりません。

autosaveoffをしている時

saveon/saveoff命令が無効化します。
そのかわりに、文章表示行の先頭で、自動的にsavepoint命令が(内部的に)呼ばれ、スナップショットを作成します。

どちらのモードがよいのか。

大部分が好みの問題ですが、完全にコントロールしたいのであれば、autosaveoffを使わず、*start節の先頭でsaveoffして、随所でsavepointではないでしょうか。(本稿ではこっちを採用します)

何がスナップショットに含まれないか。

この二つは特に致命的なので、それぞれ専用の対策を必要とする。それぞれ、専用の項目を設ける予定。

よくわからないと言う人に。

以下、簡単なluaによる検証スクリプトを作成した。
nslua.dllがあるのを確認し、system.luaファイルを作成し、その中に下記のスクリプトをコピペすること。
そうすると、実行時にセーブポイント(自動・手動問わず)に差し掛かる度に、ポップアップが出てセーブポイントに来たことを告げてくれる。
それで、多すぎず少なすぎずにスナップショットが作られていれば、よしとしましょう。

現在のスクリプト

*start

;	saveoff ; 完全手動セーブ宣言!
	kidokumode 1 ; 既読スキップモードを使用可能にする。
	gosub *if_in_advance ; 一度は実行しておく。
	gosub *text_lb_skip_start ; 一度は実行しておく。
	skipoff

あ\
い\
う\
え\
お\

end

*define

	; メニューバーを全消し
	deletemenu

	mov %0,100 ; numalias定義用一時変数

	; bexecの結果を受け取る専用変数を宣言
	numalias bexec,%0:inc %0

	; システムカスタマイズの宣言
	textgosub *text_lb

	; 既読スキップ有りで。
	kidokuskip
	
	; isskipの結果を受け取る専用変数を宣言
	numalias isskip,%0:inc %0

	; ifの代替命令群の定義
	defsub if_str_goto
	defsub if_str_gosub
	defsub if_int_goto
	defsub if_int_gosub
	defsub notif_str_goto
	defsub notif_str_gosub
	defsub notif_int_goto
	defsub notif_int_gosub
	
	numalias if_a,%0:inc %0
	numalias if_b,%0:inc %0
	numalias if_label,%0:inc %0
	
	; 自動記録なし
	autosaveoff

game

*text_lb

	; 今がスキップモードなら、さっさと次に回す。
	isskip %isskip
	if_int_goto %isskip,1,*text_lb_end

	; 進む機能を持つキーが押されていないことをチェックする。
	checkkey %bexec,"LCLICK"   :skip (0-%bexec) * 1
	checkkey %bexec,"WHEELDOWN":skip (0-%bexec) * 2
	checkkey %bexec,"SPACE"    :skip (0-%bexec) * 3
	checkkey %bexec,"RETURN"   :skip (0-%bexec) * 4
	checkkey %bexec,"K"        :skip (0-%bexec) * 5
	; CTRLは除く。CTRL押しっぱなしで進むのは、最近のエンジンではよくある仕様だから。

	*text_lb_init_loop

	bclear

	*text_lb_loop

	bexec $bexec,%bexec

	; Sにセーブを、Lにロードを割り当てる。
	if_str_goto $bexec,"S",*save_routine
	if_str_goto $bexec,"L",*load_routine

	; Kに既読スキップモードを割り当てる。
	if_str_goto $bexec,"K",*text_lb_skip_start

	; マウスの左クリックかホイールダウン、キーボードのスペースキーかエンターキーかコントロールキーで次へ。
	if_str_goto $bexec,"LCLICK",*text_lb_end
	if_str_goto $bexec,"WHEELDOWN",*text_lb_end
	if_str_goto $bexec,"SPACE",*text_lb_end
	if_str_goto $bexec,"RETURN",*text_lb_end
	if_str_goto $bexec,"CTRL",*text_lb_end

	; それ以外の入力であれば、ループする
	goto *text_lb_loop

	; スキップモード開始用
	*text_lb_skip_start
	systemcall skip

	; クリック待ちの終了処理。
	*text_lb_end

	texec

return

; セーブルーチン
*save_routine
	savegame 0
	mesbox "セーブしました。","save game"
goto *text_lb
; ロードルーチン
*load_routine
	loadgame 0

; if 代替命令群本体ルーチン

*if_str_goto
	getparam $if_a,$if_b,$if_label
	if $if_a=$if_b return $if_label
return

*if_str_gosub
	getparam $if_a,$if_b,$if_label
	if $if_a=$if_b goto $if_label
return

*if_int_goto
	getparam %if_a,%if_b,$if_label
	if %if_a=%if_b return $if_label
return

*if_int_gosub
	getparam %if_a,%if_b,$if_label
	if %if_a=%if_b goto $if_label
return

*notif_str_goto
	getparam $if_a,$if_b,$if_label
	notif $if_a=$if_b return $if_label
return

*notif_str_gosub
	getparam $if_a,$if_b,$if_label
	notif $if_a=$if_b goto $if_label
return

*notif_int_goto
	getparam %if_a,%if_b,$if_label
	notif %if_a=%if_b return $if_label
return

*notif_int_gosub
	getparam %if_a,%if_b,$if_label
	notif %if_a=%if_b goto $if_label
return

*if_in_advance
	; 定義した命令を、一度は実行しておくためのルーチン。
	; 起動時に一回だけやっておけばよい。
	if_str_goto ""," ",*dummy
	if_str_gosub ""," ",*dummy
	if_int_goto 0,1,*dummy
	if_int_gosub 0,1,*dummy
	notif_str_goto "","",*dummy
	notif_str_gosub "","",*dummy
	notif_int_goto 0,0,*dummy
	notif_int_gosub 0,0,*dummy

	if_str_gosub "","",*if_in_advance_dummy
	if_str_goto "","",*if_in_advance1

	*if_in_advance1
	if_int_gosub 0,0,*if_in_advance_dummy
	if_int_goto 0,0,*if_in_advance2

	*if_in_advance2
	notif_str_gosub ""," ",*if_in_advance_dummy
	notif_str_goto ""," ",*if_in_advance3

	*if_in_advance3
	notif_int_gosub 0,1,*if_in_advance_dummy
	notif_int_goto 0,1,*if_in_advance4

	*if_in_advance4
return

*if_in_advance_dummy
	; 何もしないよ。
return

なお、現在のスクリプトでは、実際のセーブとロードは決め打ちで*save_routineと*load_routineに飛ばしているだけです。
リッチな、スクリーンショットや注意書きのついたセーブ・ロード画面が必要ならば、*save_routineや*load_routineの部分を好きに作りなおしてください。自作する場合は、*text_lbにreturnするようにしてください。