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

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

既読ストップ問題

まあ、タイトルは造語なんですけど。
ノベルには、スキップモードがあるのは皆さんよくご存知のはず。例えば特定のキーを押しっぱなしで、あるいはボタンを一つ押して、絶対読めないだろって速さで文字が流れ、立ち絵がパタパタと動くアレです。
そのうち既読スキップモードと言うのが出てきまして、つまり既読、一度読んだ(見た)部分だけをスキップモードで流して、未読の部分が現れたらモードを終了させて普通のクリック待ちになるアレです。
NScripterでもそれは可能でして、するためには、*define節でkidokuskipと書くだけで、既読スキップが使えるようになります。
この既読か未読かは、スクリプトのそれぞれの行に対して実行したしないの情報を保存していく遣り方なのですが、システムカスタマイズをすると頻繁に何故か既読スキップモードが止まってしまう事態に出くわします。
なんでだろうとよくよく調べてみると、どうやら、ifのある行が未読と判定されているようです。
ifはご存知のとおり、条件式を判別して、条件を満たした場合にそれ以降の命令を実行する命令ですが、逆に条件式を満たさなかった場合は素通りするだけではなく、既読にしないと言う性質があったのです。
つまり、if(notifを含む)のある行は、それだけで既読ストップ問題を引き起こす潜在的可能性を持っていた訳です。
前回の最後のスクリプトを見ればわかりますが、やはりifを使っていますので、もしかしたら、既読ストップを起こす可能性があります。
まあこういう場合、問題になるかならないかわからない命令をドキドキしながら使うよりも、問題を起こさないことが保証されている別の命令を使って同じことをするのがセオリーですので、そのようにします。

既読スキップ問題の対処

単純で、既読スキップ問題を起こさない命令を作ることです。
そのため、ifに変わる何かを作成します。

; *define節に書きます。
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
; *define節にも*start節にもないところに書きます。
*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

たとえば、if_str_gotoは、二つの文字列とラベルを引数に取る命令で、二つの文字列が同じだった場合は、引数のラベルにgotoするものです。
同じように、notif_xxx_xxxxは一致しなかった時、xx_int_xxxxは数値を比較し、xx_xxx_gosubはgotoの代わりにgosubを実行します。
実はこんな命令作るよりも、nsluaに投げれば何も問題はないんだけど、まずはNScripterデフォルトでできるところまでやってみようと言うことで。
で、以下が、これを盛り込んだスクリプト

*start

	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

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

	; 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

; 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

これで、現状Kキーを押すと既読スキップモードに突入するようになった。
ちなみに既読と強制の切り替えは、コンフィグの領域なのでまた後で。

次回予告

さて、これで今後も既読ストップ問題は解決済みと言うことで。
次は、セーブデータの取り扱いについて。