NSLaおよびこのマニュアルは、素のNScripterに関する知識(Luaと狭義のシステムカスタマイズは含みません)を暗黙の前提としています。言い換えると、読み手が以下のどちらかの状態にあることを想定しています。
@NScripterは分かるがLuaは分からない
ANScripterもLuaも分かる
この暗黙の前提に基づき、NSLa独自の文法を解説する際に「NScripterではmov $0,""
のように値を代入しますが…」といった書き方をせず、「従来の命令に加え…」とだけ書くかもしれません。
その上で@・A両方の層に対応するために、本マニュアルは以下のような記述形態を採ることにします。
「できることならLuaの知識を踏まえて読んで欲しい解説」には青色のマーカーを用います。
たとえば、「NSLaではローカル変数を利用できます」といった具合です。
「Luaは一切分からない」という人は青色マーカーの記述を読み飛ばしてください。
「オプションから変更できる挙動、およびNScripter目線(配布状態の動作サンプル)では正しいがLua目線(およびNSLaの実際の仕様)としては間違っている解説」には緑色のマーカーを用います。
たとえば、「NSLaは000.txt〜255.txtを読み込みます」といった具合です。
NSLaの実体は配列に読み込んだスクリプトを実行する(NSExec()やloadstring()のような)関数です[3.1](実際、関数としてLuaから実行できます)が、
混乱を避ける目的から、ある項目(追加/変更される文法や機能)についてNScripterの延長で解説できる限り、NScripter目線で記述することにします。
★は思いっきり書きかけの項目に付けている目印です。あと一か月くらいかかりそうです…
「素のNScripter」「NScripter本体」と書いた場合、NSLaを導入していない本来のNScripter(における文法ルール)を指します。
このマニュアル執筆時点ではバージョン3.03が最新版です。
「生の文字」と書いた場合、原則として""
で括られた文字列を意味します。「生の数字」と書いた場合も、同様に、変数記述ではない数字そのものを意味します。
「命令」と書いた場合、NScripterの命令全般を指します(Luaに命令という概念は(誤解を恐れずに言えば)存在しません)。
「引数」と書いた場合、命令または関数に渡される文字や数字などの値を意味します。
lsp 1,"foo.png",0,0
なら4つの引数がlsp
に渡されており、alert("text","title")
なら2つの引数がalert
に渡されています。n番目の引数は一般に第n引数と呼ばれます。
「評価する」と書いた場合、「(該当する文字列を)処理・実行する」と読み替えて構いません。
たとえば「“$0,$1=$1,$0”の右辺を評価し、それから代入する」という文章は、
@右辺の「$1,$0」が具体的な値に置き換わる
A$0に$1の中身が、$1に$0の中身が代入される(→$0,$1の値が入れ替わる)
という二段階の操作を意味します。
「深度」または「gosub深度」と書いた場合、「(スクリプトの開始位置を基準に)何重にgosubしているか」を表す数値を指します。
一度もgosubしていない状態は深度1、3回gosubしていれば深度4です。
「あと何回returnすればスクリプトを抜け出せるか」と言い換えることもできます。
「ネスト」または「ifネスト」と書いた場合、「(該当深度の開始位置を基準に)何重にifが記述されているか」を表す数値を指します。
NSLaにおけるifの文法に関しては当該項目を参照してください[2.7]。(NSLaではLua方式のifを使用できます。)
Lua変数のうち、関数型については文中でhoge()
のように表記することがあります。これは純粋に視認性を目的とした表記であり、当該関数が引数を持たないこと・関数呼び出しであることを即座に意味するものではありません。
$0
に文字$1
は含まれているか?」を確かめるために、検索&抽出を行いたいとします。Luaにはstring.matchという関数(≒命令)が用意されています。--【Lua側】 function NSCOM_match() local num, str, key = NSGetStrRef(), NSGetPopInt(NSPopComma()), NSPopStr(NSPopComma()) NSSetStrValue(num, (str:match(key) or '×')) end NSExec("luasub match") ;【NScripter側(00.txt)】 match $0,$0,$1
「標準関数を一つ呼びたいだけなのに別ファイルを用意し、標準関数を呼ぶだけの関数を作り、luasubする」という手順は、作業効率の上でも見かけの上でもスマートではありません。サウンドノベル用途に限れば文字列操作をする機会は皆無ですが、そうでない用途においては不便さが残ります。;【拡張スクリプト(000.txt)】 $0=^^string.match("文字列","チェックするキーワード") or '×'^^
★
「語頭が『NSNS』であるLuaグローバル変数」を予約させてください。
NSLaの挙動を設定するオプションについては、全てNSNS〜という名前で定義されています[4.1]。それ以外の変数は内部処理に用いています。
たとえば拡張スクリプトはNSNSgyo
に格納されており、NSNSsaveon
がsaveon状態か否かを管理しています。
既にLuaスクリプトを使用している等の理由からグローバル変数を好まず、かつrequire等で調整出来る人は必要に応じて調整してください(名前が衝突する可能性は低いと思いますが…)
NSNSでは始まらない変数として、以下の関数を定義しています。
string.split() alert() ensafe()
これらは内部動作に使用していないため、必要に応じて上書きしても構いません。
デフォルトのLua関数のうち、error()のみ上書きしています(拡張スクリプトにおけるエラー発生箇所を表示するため)。 内部ではNSNSerror()の形で処理しているため、不都合なら再上書きしても構いません。
上記以外に、NSLaの実行そのものが定義された関数として以下を使用しています。
nsload() nsloadfile() ns() nsfile() nsboot()
詳細については拡張スクリプトの動的な読み込みと実行[3.2]を参照してください。
Lua変数NSNSSaveHensu
で指定した番号の文字変数(通常変数が望ましい)を1つ使用します。
デフォルトではNSNSSaveHensu=199
と定義されていますが、極力通常変数末尾に近い(使わない)番号を指定してあげてください。
この変数は内部情報(ロード時戻り行など)の記録場所として利用され、一部の内部処理でも予約されています。
NScripter変数のローカル化機能(localalias/setvar命令)[2.5]を利用する場合、指定した範囲の文字・数字変数がNSLa側の処理で使用されます。これも通常変数を指定してください。同梱の000.txtでは以下のように設定されています。
localalias 1000,1255 setvar 1256,1511
defsub
やluasub
は出来なくなっています。
defsub mov
if elseif else end outif killif
for next break continue
do while repeat until
switch case default fallthrough
reset nsbreak nsend
goto gosub return tablegoto skip jumpf jumpb
trap rtrap lrtrap
select selgosub--selnumは可
local var
main domain
english
savedir savegame savepoint saveon saveoff loadgame
systemcall
setwindow setwindow2 setwindow3
puttext clickstr linepage
trap r_trap lr_trap rmode
loadgosub textcolor textclear
numalias stralias
automode_time
--新旧ボタン関連全て セーブデータに用いるログ取り用途・環境の記録
btnwait bclear btndef spbtn cellcheckspbtn btnex btncellcheck exbtn btnarea
getcursor getenter getfunction getinsert getmclick getpage gettab getzxc spclclk
bsp bcursor bdown btrans btndown bdef btime btntime btntime2 exbtn_d
--
kidokumode filelog labelexist
ld cl bg
textspeed textspeeddefault indent
texton textoff texthide textshow
bgm bgmstop
nsnsaliasnakami--デフォルト命令ではない(上に内部的にも必然性はない(古い)が許して)
localalias --デフォルト命令ではない。通常変数内部にローカルエイリアス領域を予約する
setvar --デフォルト命令ではない。通常変数内部にvar変数領域を予約する
NScall_animation()
NScall_load()
NScall_end()
NScall_savepoint()
|
内部からそれぞれ
NSNScall_animation()
NSNScall_load()
NSNScall_end()
NSNScall_savepoint()
を呼び出してください(つまりNSを一つ余分に書いたものを呼びます)。たとえばNSLaデフォルトのloadコールバックは
function NSCALL_load()
NSNSCALL_load()
end
と定義されています。
|
NScall_close() | 終了前にNSNSsafeend=true と代入しておいてください。 |
NSCALL_text0() |
文頭で呼び出されます。 文字列を返すとテキストがすり替わり、falseを返すとテキスト表示を中止します。 |
NSCALL_tag() |
文頭で呼び出されます。 NSCALL_tagにラベル名または関数が代入されていた場合、 pretextgosub/zenkakko命令相当の処理を期待してタグ内部のテキストが渡されます。 ※Nスク本来の挙動(クリック待ち毎の判定)と異なるため注意してください。 |
NSCALL_textは使用できません。入力待ちをカスタマイズする関数としてNSNSclickwait()
が用意されています[2.6.6]。
NSLua主体のプロジェクトにNSLaを組み込む手順に関しては、[3.1.1]を参照してください。
「NScripter風に動く」スクリプトを新規に作成する場合は、同梱ファイルをそのまま利用できます。
動作に最低限必要なファイルは以下の通りです。
既存のスクリプトをNSLaに対応させたい場合、NScripterと仕様が異なる部分を修正する必要があります。
コンバートツールでは自動変換されない内容について、手作業で確認する必要があります。
公式同梱のdll以外については、NScripterプラグインに関するまとめ[別ページ]を参照してください。
以下のdllは同梱していませんが、利用(exeと同じ階層またはdllフォルダに存在する状態)を想定したコードを含んでいます。
NSLA.lua
NSLaの実体です。同梱のsystem.luaからdofileされています。
関数等の定義はこのファイルで行われています。
system.lua
NSLaを呼び出しています。
同梱のsystem.luaには「NScripterであるかのように」動かすためのコードが書かれてあります[3.1.2]。
00.txt
NSLAの処理そのものには基本的に用いられません。
現在のNScripterは00.txt〜99.txt抜きでも(system.luaのみで)動作しますが、
その状態ではセーブ処理やrmenuが不安定になるようです。
また、処理順の関係から上部メニュー設定(insertmenu/resetmenu等)のみは00.txtで処理する必要があります。
そうした理由から、NSLaでは(実際の動作上はほとんど無関係であるにも関わらず)00.txtの存在を推奨しています。
同梱サンプルでは1行目の初期定義と上部メニュー設定、NSLaの開始処理(NSCOM_main()の呼び出し処理)のみ行っています。
000.txt〜255.txt
ここに拡張スクリプトを記述します。同梱したsystem.luaでは起動時に「能動的に」000.txt〜255.txtを読み込み、その後*defineと*startから実行しています。
具体的にはNSNSinitializing()関数によって連番スクリプトを読み込み[3.2.1]、
その後nsboot("*define")とnsboot("*start")を実行しています[3.2.3]。
読み込むファイルや実行するラベルは任意に変更可能です。したがってLua視点においてこれらのファイルは必須ではありません。
適用したいスクリプトが以下の条件にあてはまっていないか確認してください。
★コンバートツールの警告検知機能をもう少し更新する
★リンク貼る
システムカスタマイズ(※)している
(※…クリック待ちの乗っ取り)NSLaではクリック待ちの乗っ取り方が異なります。
若干量を手作業で修正する必要があります。既存の命令をdefsubしている
予約語やluasub命令との衝突を確認する必要があります。 既にNSLuaを利用している
コールバックやluasub命令の衝突を確認する必要があります。 自動変換できない非互換性を含む
手作業でスクリプトを修正する必要があるかもしれません。 NSLa非対応文法(※)を含む
(※…主にテキストボタン)NSLaを適用することはできません。 2.1.2 - 同梱dllフォルダの中身について(前提プラグイン)
nslua.dll
NScripter公式サイトで提供されています。NSLa以前にLuaの利用で必須です。
nspng.dll
nsogg2.dll
NScripter公式サイトで提供されています。NScripter本体に付属するdllです。
deffontd.dll
dpshadow.dll
サンプルコード(ノベル向け追加コード)内部でテキスト表示乗っ取りに使用しています。
NSLa本体で直接使用はしていません。
NScripterDS.dll
ogg形式のファイルを高度に再生することが出来ます。オートモード時のボイス待ちは「このプラグインかつラッパーのnds関数」または「NSOgg2.dll」を前提にしています。
fileutil.dll
「フォルダ作成・削除、ファイル読み書き名前変更プラグイン」という名称で配布されています。NSLaはセーブ・ログ用のフォルダが存在しない場合にこのプラグインを使って自動作成しようとします。
複数の行をまとめてコメントアウトできます。
;[[
ここから
ここまで
;]]
入れ子にすることもできます。
;[[
入れ子にする場合は
;[=[
このように任意個のイコールを挟むことで
;]=]
多重に記述できます。
;]]
Luaの書き方(--、ハイフン2つ)も利用できます。
--このように
--[[
まったくのLua風な表記も
--[==[
通るように
--]==]
なっています。
--]]
ただし、
%0--
と記述した場合のみdec %0
と認識されます[2.3.4]。
一括代入処理は、内部的にはNSNSmov()関数への文字列渡しです。
代入の略記が用意されています。
以下はmov %0,10
の略記です。
%0=10
以下は%0=10 :$0="あいうえお"
の略記です。
%0,$0=10,"あいうえお"
変数の範囲指定も行えます。以下は%0=0 :%1=1 :%2=2
の略記です(複合ボタンの記述を思い出してください)。
%0-2=0,1,2
右辺で範囲指定を用いる場合は「-」の代わりに「#」を使います(右辺には式が来るかもしれず、-は使えないため)。
%0-2=%10#12
同じ型の変数を連続で指定する場合は変数記号($%)
を省略しても構いません。
%0-2,4,10,$15=0,1,2,4,10,"15"
左右の辺で型が合わない場合は変換を試みます。
%0,%1,$2="123","あいうえお",456 -->%0=123 %1=0(変換失敗) $2="456"
右辺が不足している(項が左辺より少ない)場合、自動的に0または""が補われます。
%0,%1,%2,$3=0,1 -->%0=0 %1=1 %2=0 $3=""
反対に右辺が余った(項が左辺より多い)場合、余分な項は用いられません。
%0,%1=0,1,2,3 -->%0=0 %1=1 2,3は破棄
文字変数に全角文字を代入する場合のみ、文字を括る""を省略できます。
$0=あいうえお
右辺に式を記述しても構いませんが、文字列に計算式を結合する場合は()
で括らないとエラーになります。
$0="あいう"+(15-1) -->"あいう14"
$0="あいう"+15-1 -->エラー!
代入略記における文字列の結合には、+(NScripter式)
と..(Lua式)
のどちらを用いても構いません。
$0="あ"+"い".."う" -->"あいう"
代入略記以外のほとんどの箇所ではNScripter本来の結合表記(+
での結合)のみを認めていますが、例外としてifの条件式[2.7]とfor[2.8]に限ってはLua風の表記(..
)を用いる必要があります(他の記号を機械的に置き換えてLuaに判定を投げているため)。
文法としては完全に一貫性を欠きますが許してください。
右辺に値や変数ではなく「*」を指定すると、当該変数の初期化(0または"")を意味します。
右辺不足時に実際に補われているのはこの記号です。
%0,$0=*,*
右辺の変数/値/*の直後には、特別な末尾オプションを付加できます。
%0! | 左辺の残った項すべてに同じ値(%0 )を代入します。 |
%0@ | 左辺の当該項以降に同じ型(数値変数)が続く限り、同じ値(%0 )を代入します。 |
%0@数字 | 指定した個数分の項に一括代入します。"あ"@3 は"あ","あ","あ" と等価です。 |
# @ ! 表記いずれかの後に追加のオプション「?(数字)」を指定できます。
「?(数字)」を指定すると、連続して代入される値が(数字)または1
ずつ増減します。負の値を指定しても構いません。
0@3?10 は 0,10,20 と等価です。
文字に対してこのオプションが指定されると、末尾に半角で番号を付与します。
【"ぬ"@3?】 は 【"ぬ1","ぬ2","ぬ3"】 と等価です。
【"ぬ"@3?4】 は 【"ぬ4","ぬ8","ぬ12"】 と等価です。
# @ ! を記述せずに?(数字)のみ指定した場合、「@?(数字)」の略記と見なします。
(左辺の型が変わるまで連続代入+(数字)ずつ増加or連番付与)
同様に、!(数字)は!?(数字)の略記と見なされます。
(左辺の残り全てに連続代入+加算または連番付与)
add,sub,mul,div,mod
に略記が用意されています。
%0+=100
はadd %0,100
の略記です。
%0-=100
はsub %0,100
の略記です。
%0*=100
はmul %0,100
の略記です。
%0/=100
はdib %0,100
の略記です。
%0%=100
はmod %0,100
の略記です(見づらいため注意)。
このうち+=
のみは文字変数に対しても適用できます。他はエラーを返します。
剰余(割ったあまりの数、mod)を%
で表すのはLua風の表記です(実際のところ、計算はLuaに投げています)。ところが%
は数字変数の語頭でもあるため、NSLaでは剰余と数字変数の記号が衝突しています。
この衝突は、以下のような記述(数字変数による変数番号の指定)において、意図に反した動作を招くおそれがあります。
;【問題1】
%0=3
if(10 % %0==1) -->10 % 3==1
;条件を満たす
end
if(10%%0==1) -->103==1
;条件満たさず
end
;【問題2】
%3=5
if(10 % 3==1)
;条件を満たす
end
if(10%3==1) -->105==1
;条件満たさず
end
これはif/for条件節が内部的にLuaスクリプトとして処理される仕様に由来する問題です[2.16.5]。
「NScripter変数を生の文字や数字に変換→Luaで判定」の順に処理されるため、「剰余と数字(変数)」がくっついて記述されていると、本来の意図がどうであれ「数字変数」や「数字変数で番号指定された数字変数」として解釈されてしまいます。
動作オプションの設定[4.1]次第では「` (Windowsキーボードではshift+@)」
を「(数字変数ではない)%」
として解釈させる回避手段も用意されていますが、剰余を扱う際は極力半角スペースやタブを間に挿入すべきです。
一方代入略記[2.3.1]では独自に項を切り分けて処理されるため、剰余の直後に数字変数を記述しても正しく解釈されます。
%1=10%%0 -->%1=1
%0++
はinc %0
の略記です。%0--
はdec %0
の略記です。
%0-- %0--;あいうえお %0----あいうえお
| 「%0 を1 減少させる(+注釈)」を表します |
%0 -- %0--あいうえお %0 --あいうえお
| 「%0 というテキスト(+注釈)」を表します |
実際はLuaのグローバル変数(テーブル型)を埋め込んで使用しているだけです。[2.16.2]したがって以下の記述は読み飛ばして問題ありません。
NSLaで配列を用いるには、
foo={}
のように定義を行う必要があります。このとき使用する名前は原則としてエイリアスと重複しないようにしてください。
配列に値を代入する場合は
foo[1]="bar"
のように表記します。代入する値の型は文字でも数字でも構いません。
命令の引数として値を呼ぶ場合はlsp 1,&foo[1],0,0
のように&
を付けて表記します。
この例ではfooという配列変数の1番に代入された値を呼び出しています。
添え字(上記例でいう1
の部分です)に文字列を使うこともできます。&foo["あいうえお"]
のように記述します。一般に、これは連想配列と呼ばれます。
★多次元配列の定義方法 dimの場合★
★値の初期値は0ではない(nilである)点
★1オリジンにすべきである点
★保存方法
$$0 %$0
のように変数を記述することができます。つまり、エイリアス名を文字変数で指定することができます。$%0 %%0
つまり変数による変数番号指定もそのまま使えます。
/@
に続いてエイリアス名を記述します。 if($0==1)
/@hoge
$hoge="あいうえお"
値は「$hoge」です。 -->値は「あいうえお」です。
end
値は「$hoge」です。 -->未定義エラー!
/@hoge,fuga
--$hoge,$fuga,%hoge,%fugaが用意される
/@hoge,fuga=15,"あいうえお"
--$hoge="15",$fuga="あいうえお",%hoge=15,%fuga=0が用意される
--ここは深度1です
gosub *label1
値は「$hoge」です。 -->未定義エラー!
----------------------------------------------------
*label1
--ここは深度2です
if(true)
var hoge="あいうえお"
end
値は「$hoge」です。 -->値は「あいうえお」です。
gosub *label2
return
*label2
--ここは深度3です
値は「$hoge」です。 -->未定義エラー!
return
local $0
NSNSclickwait(frommyselnum,voicewaiting,notfromtextwait)
この関数は俗にシステムカスタマイズと呼ばれる処理のうち、
「クリック待ち時の動作を変更する」
に相当します。
つまり、「テキスト中のクリック待ち(@¥)」または「自作selnum」から呼び出され、以下の処理を担う関数です。
@(必要であれば)常駐ボタンを定義する
A(bexec命令で)クリックやキーやボタン押下等の「入力」を検知する
B結果(何が入力されたのか)をNSLaに返す
NSLaデフォルトの関数は実装例としてのサンプルを兼ねています。
NSLaでは、単に常駐ボタンを実装するだけなら常駐ボタン設置関数[2.6.5]を使用して配置・取得する事ができます。
その場合クリック待ち関数を修正・自作する必要はありません。つまり、この関数を改変する場合は
「有効なキー入力を追加・変更したい(Zキーで読み進めたい、F12リセットは要らない等)」
「ゲームパッド主体のジャンルにおいて、テキスト表示時にゲームパッドの入力を検知したい」
どちらかの状況にある事が想定されます。
■引数について
第一引数(bclearを行わずtrapを発火させないフラグ)
自作selnum等から呼び出して使う場合はtrueを渡してNSNSclickwait(true)とします。これはtrapの発火を防ぐためです。
第二引数(オートモードのボイス待ちミリ秒)
オートモード[2.12]中であり、かつボイス待ちを行う(クリック待ち関数を再帰的に呼び出す)場合に次回確認までの待ち時間(ミリ秒)を渡します。デフォルト関数では100ミリ秒ごとにボイス再生の有無を判定しています。
外部からの呼び出し時は(何か特別な演出を用いるのでない限り)使用しません。
第三引数(常駐ボタンを起動しないフラグ)
判定に常駐ボタンを用いたくない場合はtrueを渡します。これは自作バックログからの呼び出し等を想定しています。
これらの引数は全てデフォルト関数内で利用しています。したがって、関数を自作する場合はこの限りではありません。
第四引数(テキストクリック待ちから呼ばれた場合にクリック待ち記号が渡される)
NSLaデフォルトのオプション環境では「@ \ @ ¥」の四種類いずれかが渡されます。
一部のテキスト系命令で自動挿入されるクリック待ちについてはその機能に準じます。
(つまり ("@" または "@")で一致判定を行えばとりあえず問題ありません。)
これはispage命令相当の機能ですが、標準で定義している関数では特に利用していません。
■NSLaデフォルトの関数で行っている処理
NSLaにおいて、デフォルトの「クリック待ち」が担う処理は以下の3つです。
@(必要であれば)常駐ボタンを定義する | デフォルトの関数では常駐ボタン設置関数[2.6.5]を(定義されていれば)自動で呼び出しますが、 そちらで定義したくない特別な理由があればここで直接定義しても構いません。 | A(bexecを用いて)クリックやボタン等の入力を取得する | bexecそのものです。特別な理由があれば代わりに旧ボタンを用いても構いませんが、様々な理由(反応速度の差、常駐ボタン設置関数との兼ね合い等)から推奨はしません。NSLaはテキストボタンを使用できない[2.18.2]点にも注意してください。 | BNSLaに結果を返す | bexecの結果に応じて起こすべきアクションを決めます。以下で詳しく解説※します。 |
●(常駐)ボタンを押したか?
常駐ボタン設置関数を呼び出してチェックしています。
特別な理由がない限りは常駐ボタン関数に任せてよいはずですが、必要であれば関数内に直接記述しても構いません。
以下に該当箇所のコードを(若干書き換えつつ)引用します。
--100番を押していた場合 if(bkekka=="S100")then --●何らかの入力を検知させたい場合 --右クリック扱いしたい場合 --bkekka="RCLICK" --ホイール上入力扱いしたい場合 --bkekka="WHEELUP" --スキップしたい場合のみsystemcallではなくこちらで行います --bkekka="SKIP" --●systemcallしたい場合(標準メニューを呼ぶ等) --NSNSrclickmove_systemcall="windowerase" --※倍角テキストモード[2.6.8]中にデフォルト回想を呼ぶとエラーを招くため、 -- lookbackのみは以下のように書く必要がある -- if(not NSNSbsmm_mul)then NSNSrclickmove_systemcall="lookback" end --●任意の命令で割り込みたい場合 (基本的には自作selnumからの呼び出しで発火させるべきではない) -- NSNSdotrap=NSNStrap --trapの発火 -- NSNSdotrap=NSNSrtrap --r_trapの発火 -- NSNSdotrap=NSNSlrtrap --lr_trapの発火 -- NSNSdotrap='caption "常駐ぼたんくりっく"' --割り込む命令を直接指定する例 -- NSNSdotrap='押 し た な ?' --テキストも通る(使い所はやや乏しい) -- NSNSdotrap='%20=57' --NSLa特有の略記やLuaコードも通る -- NSNSdotrap='*何らかのラベル' --ラベルを記述するとgotoジャンプする -- NSNSdotrap='gosub *trapによるgosubジャンプ' --[[ 【割り込む際の注意】 ※ボタンウェイトおよび標準selnumの実行中はtrapが発火しない(本来の挙動と同様)。 ※trapで割り込む命令にgosubを指定した場合のみ、やや特殊な挙動になる @「飛び先からreturnで戻るまでずっと」「trap発火中」と見なされる。 A「trap発火中は右クリックメニューが発火しない」 「独自右クリックメニュー[2.11.4](※1)の中ではtrapが発火しない」 というセーフティ機能が存在する。 詳しくはtrapによる命令割り込み[2.11]を参照。 --]] --200番を押していた場合 elseif(bkekka=="S200")then end --常駐ボタン関数[2.6.5]がセットされていたらそちらでも判定する if(not notfromtextwait and type(NSNSpermanentbuttons)=="function")then local newkekka=NSNSpermanentbuttons("check",resnum) if(newkekka)then bkekka=newkekka end end
●オートモード関連の処理
オートモード[2.12]中であるか、オートモードの切り替わりが発生するかどうかを判定します。NSLaにおけるオートモードの扱いは当該項目を参照してください。
if(bkekka=="AUTO" or (bkekka=="TIMEOUT" and NSNSautomode))then if(bkekka=="AUTO" and NSNSautomode)then NSNSautomode=false else bkekka="WHEELDOWN" NSSystemCall("automode") --「ボイス鳴ってる間は待つ」の処理(自身を再帰的に呼び出す) --NScripterDS.dllまたはNSOgg2.dllが前提、前者はろだのラッパー(nds関数)利用が前提 if((type(NSNSexistogg2)=="string" and type(nds)=="function" and nds("isplaying",NSNSexistogg2)==1) or (NSNSexistogg2 and NSOggIsPlaying(0)))then return NSNSclickwait(frommyselnum,100) end end end
●デフォルトおよびNSLa追加分の入力判定
以下の判定を行っています(コードは省略)。
クリック(相当のキー)
右クリック(相当のキー)
ホイール上(回想呼び出し)
F11(デバッグコンソール呼び出し)
F12(リセット)
単に特定のキーをクリック扱いする場合などはここに加筆すると良いでしょう。
ツクール系列に揃えたZキー読み進めなどを足しておくと便利かもしれません。
●オートモードを止める入力だった場合はskipoffする
●結果をreturnする
bkekka(文字),resnum(数字)
の二つを返しています。
以上がデフォルト関数で行われている処理です。
大多数を占めるであろうノベル用途では、デフォルト関数(と常駐ボタン設置関数[2.6.5]の調整)でおよそ事足りると思います。
■関数を改変すべき場合
ここで想定するシチュエーションは
「ゲームパッド主体のジャンルにおいて、ゲームパッドの入力をテキスト送りに使いたい」
というものです。
たとえばNScripterで弾幕STGを制作するならば、当然プラグインを用いてゲームパッドに対応するべきです。
しかしデフォルトのクリック待ち(およびbexec)はパッドの入力を検知出来ません、標準テキストの利用に問題が生じます。
その場合標準テキストを諦めるか、検知可能なbexecを自作するか、この関数を改変または自作する必要があります。
繰り返しになりますが、この関数を自作する場合、最低限行うべき処理は以下の3点です。
@(必要であれば)常駐ボタンを定義する
Aクリックやキーやボタン押下等の「入力」を検知する
B結果(何が入力された事にするか)を返す
入力検知、つまりAの箇所を改変する場合、
btimeを短く設定した上でbexecとプラグイン側の入力チェックを同時に行う
bexecを使わず自力で入力を取得する
といった方法が考えられます。
text0 | テキストの先頭で呼ばれ、これから処理する予定の文字列が引数として渡されます。文字列を返せばそのテキストにすり替え、falseを返せばテキスト表示を中止します。 ★zenkakko、pretextgosubとの兼ね合い |
text | 呼ばれません ※クリック待ち時の挙動はNSNSclickwait()[2.6.6]で処理されます。 |
puttext |
非推奨です(テキストも: で区切れるため意味がない&テキストに伴うべき機能が一部働かない)
|
NSLaにおけるifの最も基本的な形は以下の通りです。
if(条件式)
条件を満たした場合の処理
end
「満たした場合の処理を複数行に分けて書ける」のが素のNScripterとの大きな差です。従来は以下のように記述する必要がありました。
;パターン1
IF(条件式の否定):skip 2 ;(以下の処理をちょうど飛ばすだけの行数)
条件を満たした場合の処理
;パターン2
IF(条件式の否定):goto *label
条件を満たした場合の処理
*label
;パターン3
IF(条件式の否定):jumpf
条件を満たした場合の処理
~
条件式の直後には、必ず「改行」「:
」「then」のうちいずれかを記述する必要があります。
従来のNScripterでは:
を省略可能でしたが、NSLaでは省略できません。この非互換性はコンバートツールによって補正されます[2.17.1]。
したがって、従来通り単一の行に記述したい場合、以下のような表記をすることになります。
if(条件式) :(命令) :end
この表記を醜く思う場合、従来と同じ動作をする大文字のIF[2.7.4]の使用を検討できます。
条件式にはNScripterと同じ演算記号を使用できます。
(実際は条件式の記号を内部で置換し、Luaとして判定しています。)
【>】 【>=】 【<】 【<=】 | 「大小(または辞書順)の判定」 |
【==】 | 「等しい」 |
【~=】 【!=】 【<>】 | 「等しくない」 |
【and】 【&&】 | 「かつ」 |
【or】 【||】 | 「または」 |
【() 】 | 「括弧内の判定結果(真偽)を一つの項と見なす)」 (多重に記述可能) |
【not hoge】 | 「否定(項hogeが偽である)」 |
endは従来のNScripterにおいて「本体の終了」を意味する命令でしたが、NSLaにおいてはifを閉じるための予約語として機能しています。
従来のendに相当する命令は「nsend
」および「nsbreak
」です[2.15]。
この非互換性はコンバートツールによって補正されます[2.17.1]。
「else
」は「条件を満たさなかった場合に以下を処理する」を意味する予約語です。ifと組み合わせて使用します。
if(条件式)
満たした場合の処理
else
満たさなかった場合の処理
end
「elseif
」は「直前までの条件式を満たさなかった場合に判定」を意味する予約語です。ifやelseと組み合わせて使用します。
if(条件式A)
Aを満たした場合の処理
elseif(条件式B)
Aを満たさず、Bを満たした場合の処理
elseif(条件式C)
A・Bを満たさず、Cを満たした場合の処理
else
どれも満たさなかった場合の処理
end
if
ネストの中に別のif
を記述しても構いません(入れ子にすることができます)。
if(条件式A)
Aを満たした場合の処理
if(条件式B)
A・Bを満たした場合の処理
end
end
NSLaのifネストには、
「内部にラベルを記述してはいけない」
「skipの飛び先は同一ネストでなければならない」
という二重の制約が課されています。詳しくはラベルの記述位置に関する注意[2.9.4]を参照してください。
そうした制限下で小規模なネスト内ジャンプに対応するために、以下の予約語が作られています。
何らかの理由からifネストの外まですぐさま離脱したい場合、「outif
」命令を利用できます。
これはforにおけるbreak
のように働く予約語です。
;ネスト0
if(条件式A)
;ネスト1
Aを満たした場合の処理
if(条件式B)
;ネスト2
outif
end
end
--outifを呼ぶとここまで離脱する
そうではなくネストを部分的に離脱したい場合、「killif
」命令を利用できます。
killifが呼ばれると、直後の引数に指定した数(省略時は2個)だけネストを離脱します。
;ネスト0
if(条件式A)
;ネスト1
Aを満たした場合の処理
if(条件式B)
;ネスト2
if(条件式C)
;ネスト3
killif 2
end
ここは飛ばされる
end
--ここまで離脱する
Aを満たした場合の処理の続き
end
原則として小文字で命令を記述するNSLaにおいて、唯一の例外となっているのがIFです。
IF(条件式) :満たせば以下を実行、満たさなければ次行へ移動
見ての通り互換用の命令です(「満たさなければ次行へ」という命令ですが、必要に応じて利用しても構いません。
また、IF以下はネストと見なされません(ネストが加算されません)。
この仕様は前項のネスト部分離脱処理[2.7.3]と組み合わせる際に便利かもしれません。
;ネスト0
if(条件式A)
;ネスト1
Aを満たした場合の処理
if(条件式B)
;ネスト2
Bを満たした場合の処理
IF(条件式C):killif 1
Bを満たした場合の処理の続き
end
;Cを満たした場合ここに離脱する
end
Luaのdo(ブロック要素)と似ています。
ローカル変数[2.5.1]や名前ありローカル変数(ローカルエイリアス)[2.5.2]のために存在する制御文で、
ネストを1増加させる他は何も行いません。
役割上は「if(true)」と同義です。
;ネスト0
do
;ネスト1
end
switch文はNScripterにもLuaに備わっていない構文ですが、少なからぬ言語で採用されています。 「最初に[switchの値]==[caseの値]を満たしたcase文」の直下のみを実行します。 条件分岐のための構文であり、if〜elseif〜else〜endのif節きわめて似た機能を持ちます。
以下にswitchによる条件分岐とifによる条件分岐の比較を示します。
switch(%0) case 0 %0==0であった場合に読まれます。 case 1 case 2 複数のcaseを一つの実行分に紐付けることもできます(条件式のorに相当)。 この場合は%0==1または%0==2であった場合に読まれます。 case %1 %0==%1であった場合に読まれます。 case "あいうえお" %0=="あいうえお"であった場合に読まれます。 case あいうえお 生の全角文字に限り、""を省略しても構いません。 case abc %0==abc(半角文字はLua変数と見なされます)であった場合に読まれます。 default 条件を満たすcaseが一つもなかった場合に読まれます(elseに相当)。 default節は省略可能です。 caseとdefaultの記述順は自由です。 つまり、何れかのcaseとセットで書いても構いません。 end
if(%0==0) 0なら読まれます。 elseif(%0==1 or %0==2) 1または2なら読まれます。 elseif(%0==%1) %1なら読まれます。 elseif(%0<=10) 10以下なら読まれます。 elseif(%0=="あいうえお") "あいうえお"なら読まれます。 --elseif(%0==あいうえお) -- ※これはエラーです elseif(%0==abc) %0==abc(半角文字はLua変数と見なされます)であった場合に読まれます。 else 他に条件を満たすif節が一つもなかった場合に読まれます。 defaultと異なり、記述位置や組み合わせの自由度はありません。 end
case内部の処理を途中で終えたい場合、breakでswitch末尾のendまで離脱できます(forなどのループ中にswitchを書く場合は注意してください)。 ★case節末尾の自動breakとfallthrough命令 ★case直下はネスト0と見なされるためラベルやローカルラベルを記述できますが、
forの管理変数は暗黙的に(疑似)ローカル化され、for離脱時に元の値へ戻ります[2.5.4]。
NScripterや多くの言語同様に、breakによってループを離脱できます。
;NScripter変数で管理
for %0=1,5
ループ内部の処理
next
--Lua変数で管理(離脱時には元の値に戻る一方、内部的にはグローバル変数を利用する点に注意してください!)
for i=1,5 do
ループ内部の処理
end
「to」「step」ではなくカンマで区切ります。ステップは省略できます。
★もっといろいろ省略できる
ループ末尾はnext(NScripter風の表記)でもend(Lua風の表記)でも構いません。nextを用いれば対応関係が厳密になり相対的に記述ミスを検出しやすくなります(endはifネスト[2.7.1]の末尾にも用いられるため)が、Luaに慣れたユーザーにとってはend表記の方が自然に使えるかもしれません。
条件式の直後には、必ず「改行」「:
」「do」のうちいずれかを記述する必要があります。
管理変数にはNScripter変数、Lua変数どちらを使っても構いません。
for i in tab do
alert(tab[i]) -->変数tabの中身を次々表示する(順番は保証されない)
end
for k,v in tab do
--何らかの処理
end
for k,v in pairs(tab) do
--何らかの処理
end
--pairs以外のイテレータも使用できます。 str="1=Tanaka,2=Saitou,3=Gotou,44=松尾芭蕉" for num,value in string.gmatch(str,"(%d+)=([^,]*)") do alert(num) -->"1"→"2"→"3"→"44" alert(value) -->"Tanaka"→"Saitou"→"Gotou"→"松尾芭蕉" end ★ただし管理変数は2つまでしか記述できず、 出てくる値は事前に全て評価される(ループ途中での変更は無視される) ★困るようなら複数行埋め込みLua使ってね
--10回繰り返される %0=0 while(%0<=10)do %0++ %0回目のループです end --中身は読まれない %0=0 while(-1>999)--doはあってもなくてもいい -- end --無限ループ %0=0 while(true)do %0++ %0回目のループです end
--10回繰り返される %0=0 repeat %0++ %0回目のループです until(%0>=10) --一度だけ実行される repeat 一度だけ実行されます until(true) --無限ループ repeat %0++ %0回目のループです until(false)
jumpf/jumpb
の飛び先に名前を付けることができます。 jumpf テスト
~
ここには飛ばない
~テスト
ここに飛ぶ
goto
と拘束の弱いラベルであるかのように動作するため、NSLaでは~(チルダ)
をローカルラベルと呼ぶことにします。
例外的な挙動として、jumpf/jumpb
側で名前を指定しなかった場合、名前を問わず、最も近い場所に見つけたローカルラベルにジャンプします。
「無名のローカルラベルに絞り込んで飛ぶ」という動作ではないため注意が必要です。
jumpf
~あいうえお
ここに飛ぶ
~
ここではない
(goto/gosub/jumpf/jumpb)
は飛び先がネスト外(if
の外部)であることを期待するため、
続けてendを見つけるとデフォルトではエラーを吐きます。
この制約は、ネスト内部でスクリプト位置を移動したい場合において影響してきます。
部分的にネストを飛び出すにはkillif命令[2.7.3]を使ってください。
同一ネスト内でジャンプを行う必要がある場合、skipを用いてください。skipは同ネストへの移動を期待します。
動作オプションをNSNSjumptozeronest=nil
と設定すると、
「jumpf/jumpb
の飛び先は同じネストであることを期待する」と挙動を変更できます。
そうした場合は「ネスト内ジャンプにskip/jumpf
、ifネストからの離脱にkillif/outif
」と役割分担できますが、
一方でjumpf時にネストがずれてしまってもその場でエラーが出ず、skip同様にバグの特定が難しくなります。
以上の問題から、デフォルトではtrue(jumpfにはネスト外ジャンプを期待する)が設定されています。
gosub *テスト("あいうえお",15)
gosub *テスト "あいうえお",15
-------------------
*テスト(hoge,fuga)
$hoge,%fugaが渡されました。@
return
var hoge,fuga="あいうえお",15
gosub *テスト("あいうえお",15)
getret($0-1)
「$0」「$1」が戻ってきました。
-------------------
*テスト(hoge,fuga)
/@foo=$hoge+"かきくけこ"
return $foo,%fuga*3
trap *ラベル
NScripter命令・Luaコード・テキスト等、「NSLaにおいて単一の命令と解釈される文字列」も仕込む事が出来ます。
trap[bg "foo.png",0]
trap[$0="あいうえお"]
trap[alert("あいうえお") local a=12 alert(a)]
trap[$0="あいうえお"]
これらが発火した場合、原則として「(本来実行されるはずだった)次の命令の直前」に割り込まれます。
例外として、テキスト表示中(クリック待ち)に発火した場合は、割り込んだ命令を実行後に残りのテキストを表示します。
仕込んだ文字列の変数は(trapを定義した瞬間ではなく)発火した瞬間に評価されます。
$0="あいうえお"
trap[gosub *テスト($0)]
$0="かきくけこ"
trapさせます→@しました¥
----------------------
*テスト(str)
$strが渡されました。@/ -->かきくけこが渡されました。
return
trap "〜"
r_trap "〜"
lr_trap "〜"
"
」を含む命令に問題が出るため、対策として以下の3つが用意されています。 trap[〜]
rtrap[〜]
lrtrap[〜]
off,stop,resume
も使用できます。 trap off
trap stop
trap resume
現時点でtrap2とlr_trap2には対応していません。これはNSLaにおける非互換性の一つです。
【注】
※ボタンウェイトおよび標準selnumの実行中はtrapが発火しない(本来の挙動と同様)。
※trapで割り込む命令にgosubを指定した場合のみ、やや特殊な挙動になる
@「飛び先からreturnで戻るまでずっと」「trap発火中」と見なされる。
A「trap発火中は右クリックメニューが発火しない」
「独自右クリックメニュー(※1)の中ではtrapが発火しない」
というセーフティ機能が存在する。
※1…独自右クリックメニュー
rgosubに近い機能。
設定例は以下。
NSNSrclickmove="*hoge" --*hogeにgosub
NSNSrclickmove="rmenu" --systemcall rmenuに相当
NSNSrclickmove="" --何もしない
NSNSrclickmove=hoge() --関数hoge()を呼ぶ。【 (関数を定義してから設定する必要があります) 】
ただし、ラベルへのgosubを設定する場合はselnum命令自作が推奨される。
標準selnumでは複数の問題を起こす(※2)。
※2…独自メニューにラベルgosubを設定し、標準のselnumを使用した場合に起きる問題
selnum内で独自右クリックメニューを発火させた上で、
・飛び先ラベルでsystemcallを呼んでも反応しない
・飛び先ラベルでloadgameするとバグる(実行中だったselnumが消せずに残ってしまう)
・飛び先ラベルで多重にselnumすると元の選択肢が化ける
・飛び先ラベルでテキストを表示すると選択肢に重なって表示される
2番目の問題が比較的致命的であるため、selnumの自作を推奨することになる。
fchk("ファイル名")
が基本
lchk("*ラベル名")
が基本入力欄が空 | デバッグモードを終わります。 |
NScripter変数 |
変数の中身をウインドウ(タイトル欄)に表示します。$hoge -->$hogeの中身を表示 カンマ( , )で区切ることで複数の変数を表示できます。$foo,$bar -->$foo,$barの中身を表示 |
変数=値 | その場でNScripter変数に値を代入します。文法は代入の略記[2.3.1]に準じます。 |
ラベル名 | ラベルが存在すれば位置を、存在しなければその旨を教えてくれます。 |
Luaとして認識される文字列 | 入力内容をその場で実行します。 デフォルトではNスク変数らしき文字を変換します。 不要な場合は埋め込みLua同様「//」に続けてください。 |
「@」で始まる文字列 |
@以降の文字で命令割り込みを予約します。 入力内容はデバッグモードを抜けた瞬間に発火します。 また、特殊な制御コマンドとして 「@」現在の発火予定を表示 「@@」発火予定の消去 「@@@」実行予定の復元(デバッグモードに入る前の内容に戻す) 「@@@@」前回の入力内容を繰り返す が用意されています。 |
上記以外 | @を省略した記述(命令の割り込み予約)と見なします。 |
「F12キーでリセット」はRPGツクール2000などに見られるショートカットキーです。クイックセーブ・ロード主体であろうサウンドノベル用途での必要性はさておき、NSLa標準の処理ではF12キーをリセットに割り振っています。
具体的には
※NSLaに限らず、NSLuaを利用する際は「リセット」に気を使う必要があります。 *start
h={} --resetする度に再定義≒初期化される
h.hoge="hoge"
*start
bg black,1
alert()
このように :alert("「次のコロンまで」がコードの範囲と見なされます。") :local a,b a=1 b=2
/.
./複数行埋め込むこともできる
埋め込んだLua内部に拡張スクリプトを記述することもできる
テキストに埋め込む事もできます。
「アニメーションコールバックまたはクリック待ち処理(NSNSclickwait[2.6.6])でF12を検知する」
「連続実行防止用にNSNSF12osippa=true
とした上でresetの実行予約で割り込む」
「拡張スクリプトがresetを実行していたら自動で必要な処理(拡張スクリプトの位置調節等)を行う」
と処理されています。
したがって埋め込んだLua関数内部でリセットを検知する場合、検知した瞬間速やかに抜け出すべきです。
(Lua変数はreset命令で変動しない=自動では初期化されないため)
NSLaにおいてリセット時にLua変数を初期化したい場合、
@拡張スクリプト内部で使う変数を単一のテーブルに格納する
A実行節先頭で定義する(ことで疑似的にLua変数を初期化する)
このような手順が考えられます。
2.15 - 拡張スクリプトの終了(nsbreak/nsend)
従来のend命令に相当します(NSLaにおいて、"end"はifネスト[2.7.1]やfor[2.8]を閉じる予約語です)。
nsbreakは現在のns関数のみ終了(離脱)し、nsendは多重実行中の全ns関数を終了させようとします[3.2.5]。
NScripter目線においては両者の違いはありません。
2.16 - スクリプトにLuaを埋め込む
★
NScripter命令の内部に埋め込む(基本的には引数として)方法と、コードを直接埋め込んで命令のように実行する方法が用意されています。
2.16.1 - 命令であるかのようにLuaコードを記述する
★
通常のスクリプトにおける命令であるかのように、Luaコードを記述することができます。
NSLa側で「Luaコードか、NScripter命令か」を自動的に解釈し、前者ならloadstring()越しに実行します。
2.16.3 - 命令にLua変数/関数を埋め込む(&hoge)
★&foo
テーブル・関数も可能だがキーに注意
2.16.2 - 命令にLuaコードを埋め込む(^^〜^^)
(単一の変数ではなく)複雑な値をNScriptr命令の引数に用いたい場合、そのようにLuaコードを記述できます。
bg ^^hoge^^,1
bg ^^foo..bar^^,1
bg ^^hoge="foo" return hoge^^,1
^^〜^^
内部には
@「(そのままreturn可能な)変数」
A「(そのままreturn可能な)式」
B「最終的に値をreturnする関数の中身」
何れかを記述してください。
あいうえお^^kakikukeko^^さしすせそ@
2.16.4 - 「:」「%」に関する注意
★「:」は命令区切りが優先されてしまう
★「`」を%と見なすオプションが有効
2.16.5 - if/forの真の姿
★条件式内部はLuaとして処理している話
2.17 - 既存スクリプトのコンバート
★@ツールでコンバートできる A手で直す必要がある(が代替手段は用意されている) B完全に対応していない(移植できない) で分けた方が良いのではないか
2.17.1 - コンバートツールで自動変換される内容
2.17.2 - 非互換性(手で書き換える必要が生じるスクリプト)
2.17.3 - 内部で管理変数を書き換えているfor
2.17.4 - 引数を持つdefsub命令
2.17.5 - 既にシステムカスタマイズしている場合
2.17.6. - NSLaが用いるluasub済み命令と衝突する場合
2.17.7 - コールバックを利用している場合
2.18 - 非対応の命令・機能
2.18.1 - definereset
2.18.2 - テキストボタン
2.18.3 - テキスト直埋めの変数操作(そういうものがあります)
2.18.4 - システムカスタマイズ(クリック待ち)専用の命令群