とりあえず最小限の構成でスクリプトを実装しました。 もちろん機能が足りないので色々追加していくつもりです。 複数のマップにまたがるキャンペーンモードなどをどのように実現するかは未定です。 あまりファイルの種類が増えるとまずいので多分スクリプト機能の一部になると思いますが。
スクリプトエンジンの中身はほとんど、やねうらおさんの公開されている
yaneuraoGameScript のyaneuraoGameScript 2000 for yaneGameSDK2nd(通称ygs2kEx)のソースを流用したものです。
yaneuraoGameScript の内、コンパイラと仮想マシンを使って、RPGなどのイベントをスクリプトで記述し、
これを解釈実行するためのスクリプトエンジンを作成しました。
とりあえずは局地戦略のために開発しますが、そのうち独立したスクリプトエンジンとしてソースと共に公開したいと思います。
スクリプトの最初に(最初でなくてもいいですが)例えば
include "ksapi.c"
と書くと、指定したファイル ksapi.c の内容がその位置に展開されます。
なお頭に#は付けません。
include されるスクリプトファイルは、まずマップと同じフォルダから探され、
見つからなければ 局地戦略インストールフォルダ\System\Script が探されます。
ksapi.c
局地戦略用イベントスクリプトで本体の機能を呼び出すための関数をラッピング(使いやすくするなどの目的で包むという意味)した関数が書いてあります。
本体の機能を呼び出す関数は、ks_window_open などのように、頭に"ks_"が付いています。
これを直接呼び出してもいいのですが、スクリプトエンジンには外部関数の型チェック(引数の数が正しいかなど)
を行う機能がないので、 ksapi.c ではこれらの関数をラッピングして、関数の呼び出し方が間違っていれば文法エラーになるようにしました。
また、スクリプトの実行を中断して制御をいったん本体に戻すための"halt"という命令があり、
ks_window_msg_out()などの関数を実行した後に"halt"する必要があります。
ksapi.c の関数は、こういった関数を適切に呼び出すようになっています。
color.c
rgb(r,g,b)
色を表す整数を返します。色を表す整数は (b<<16) | (g<<8) | r です。
R,G,Bを各8bitで指定します。
例:color_red = rgb(255,0,0);argb(a,r,g,b)
スクリプトがロードされた状態で、既に使用可能な関数です。 戻り値が long の関数で、特に説明がない場合は原則的に、 戻り値は正常終了が0,何かエラーが起きると-1です。
多くの関数は、ksapi.c でラッピングされています。
例えば ks_window_open() を呼び出す代わりに window_open() を呼び出すことを推奨します。
ただし本体の側から呼び出されるイベントドリブン関数はそのまま
ks_on_newgame() { ... }"ks_"が付いた名前で書いてください。
long ks_debugmode(long mode)
long debugmode(long mode)
スクリプトの実行時に、引数が足りないなどのエラーが起きたとき、エラーメッセージを表示するかどうかを設定します。
mode=0ならエラーが起きても何も表示しません。
mode=1ならエラー内容を_scripterror.txtに出力してエラーメッセージを表示します。
いちいちダイアログを表示しないのは、無限ループが起きたときに面倒なことになるためです。
ただしスクリプトに文法的な間違い(セミコロンが抜けているなど)があった場合はこの設定に関わらずエラーメッセージをダイアログで出力します。
long ks_set_script_step(long step)
long set_script_step(long step)
スクリプトが実行された時の最大ステップ数を指定します。
無限ループが起きた時に本体が止まらないようにするためのものです。0 にすると制限を加えません。
ステップ数はVMで実行される仮想コードの数(分からなければ気にしないでください)です。
デフォルトでは 100000 になっています。
スクリプトは、イベントドリブンで実行されます。イベントドリブンとは、例えばボタンが押されたとか、音声を再生し終わったなどの 「イベント」が起きたときに、あらかじめ決められていた処理が呼び出される方式です。 局地戦略では、例えば新規ゲームが開始された、フェイズが始まった、ユニットが動いたなどのタイミングで スクリプトを実行することになります。
void ks_on_newgame()
新規ゲームが開始された時に呼び出されます。
マップの初期化処理などが終わり、プレイヤーが操作を始める直前のタイミングです。
void ks_on_phasestart()
ある陣営のフェイズが始まった時に呼び出されます。その陣営のプレイヤーが操作を始める直前のタイミングです。
新規ゲーム開始時は、ks_on_newgame() の後に呼び出されます。
long ks_window_show(str title)
long window_show(str title)
メッセージなどを表示するためのイベントウィンドウを表示します。
例: window_show("イベント");
long ks_window_hide()
long window_hide()
イベントウィンドウを閉じます。この関数を使わなくても、スクリプトの実行が終了した時点で自動的に閉じられます。
long ks_window_size_set(long width, long height)
long window_size_set(long width, long height)
ウィンドウの大きさを変更します。
ウィンドウの描画内容は破棄されます。
long ks_window_size_get(long *width, long *height)
long window_size_get(long *width, long *height)
ウィンドウの大きさを取得します。
例: long w,h; window_size_get(&w,&h);
long ks_window_msg_out(str msg)
long window_msg_out(str msg)
イベントウィンドウにメッセージを表示します。上から下に表示され、はみ出しそうになるとスクロールします。
long ks_window_msg_clear()
long window_msg_clear()
イベントウィンドウ上のメッセージを消去します。
long ks_window_msg_view(long x1, long y1, long x2, long y2)
long window_msg_view(long x1, long y1, long x2, long y2)
イベントウィンドウ上のメッセージ表示領域を矩形(x1,y1)-(x2,y2)で指定します。
戻り値は正常終了が0,座標の指定が正しくないなどのエラーが起きると-1です。
long ks_window_clear(long color)
long window_clear(long color)
イベントウィンドウ全体を color で塗りつぶします。
long ks_window_boxfill(long x1, long y1, long x2, long y2, long pencolor, long brushcolor)
long window_boxfill(long x1, long y1, long x2, long y2, long pencolor, long brushcolor)
イベントウィンドウ上の矩形(x1,y1)-(x2,y2)を描画色で塗りつぶします。
pencolor は外枠の色、brushcolor は内側の色です。
long ks_window_img_load(str filename)
long window_img_load(str filename)
画像をメモリに読み込みます。戻り値は画像番号です。
ks_window_img_unload() で画像を破棄するとき、この画像番号を渡します。
なお画像は16枚まで読み込めます(0-15)。
ファイルが見つからない、読み込めないフォーマットだった、空きの画像番号が無い
などの理由でエラーになった場合の戻り値は -1 です。
long ks_window_img_unload(long image_number)
long window_img_unload(long image_number)
ks_window_img_load() でメモリに読み込んだ画像をメモリ上から破棄します。
画像番号が足りない、メモリを節約したいなどの理由があるときに使用します。
なお読み込んだ画像は、局地戦略本体が終了する時に自動的に破棄されます。
long ks_window_img_copy(long dest_image, long destx, long desty, long src_image, long srcx1, long srcy1, long srcx2, long srcy2)
long window_img_copy(long dest_image, long destx, long desty, long src_image, long srcx1, long srcy1, long srcx2, long srcy2)
画像同士で矩形(長方形)領域のコピーを行います。
dest_image,src_image はそれぞれ、コピー先画像とコピー元画像の画像番号です。
コピー先画像に IMAGE_WINDOW を指定するとイベントウィンドウの画面を指定したことになります。
領域(srcx1,srcy1)-(srcx2,srcy2)を(destx,desty)にコピーします。
※領域の指定方法は Windows の習慣に従い、実際には(srcx2-1, srcy2-1)までがコピーされます。
例:"face.png"を読み込み、(64,64)-(128,128)をイベントウィンドウの(0,0)に表示する。 long imgnum; imgnum = window_img_load("face.png"); if(imgnum != -1) window_img_copy(IMAGE_WINDOW, 0, 0, imgnum, 64, 64, 128, 128);
long ks_window_str(long x, long y, str s, long color)
long window_str(long x, long y, str s, long color)
イベントウィンドウ上の(x,y)座標を左上とする文字列を出力します。
color は文字色です。背景は透過します。
まだマウス操作だけです。 イベントドリブンにしようかとも思ったのですが、とりあえず状態を得る関数を作ってみました。 通常、halt を含むループの中で呼び出す形になるでしょう。
long ks_window_mouse_left_clicked()
long window_mouse_left_clicked()
ウィンドウ上でマウスの左ボタンがクリックされたならば 1 を返します。
そうでなければ 0 を返します。
ウィンドウが表示された、またはこの関数を前回呼び出した時から1度でもクリックされていれば戻り値は 1 になります。
なお「クリック」とはウィンドウ上でボタンを押した後、同じウィンドウ上でボタンを離す操作を言います。
long ks_window_mouse_left_downed()
long window_mouse_left_downed()
ウィンドウ上でマウスの左ボタンが押されたならば 1 を返します。
そうでなければ 0 を返します。
ウィンドウが表示された、またはこの関数を前回呼び出した時から1度でもボタンが押されていれば戻り値は 1 になります。
long ks_window_mouse_xy(long *x, long *y)
long window_mouse_xy(long *x, long *y)
ウィンドウ上にマウスカーソルがある時、その座標を得ます。その場合戻り値は0です。
マウスカーソルがウィンドウ外にあるならば戻り値が -1 になります。
クリックされた位置を得るためにこの関数を使わないで下さい。実際にクリックした位置とずれることがあります。
代わりに ks_window_mouse_left_clicked_xy() などを使います。
例: long x,y; if(window_mouse(&x,&y) == -1) window_msg_out("マウスがウィンドウ上にない");
long ks_window_mouse_left_clicked_xy(long *x, long *y)
long window_mouse_left_clicked_xy(long *x, long *y)
最後にクリックされた位置の座標を返します。ks_window_mouse_left_clicked() 関数が
1を返したら、この関数で座標を得るという使い方をします。
long ks_window_mouse_left_downed_xy(long *x, long *y)
long window_mouse_left_clicked_xy(long *x, long *y)
最後にマウスの左ボタンが押された位置の座標を返します。ks_window_mouse_left_downed() 関数が
1を返したら、この関数で座標を得るという使い方をします。
long ks_game_status_get(long *array)
long game_status_get(long *array)
定義されている陣営の数などを得ます。
陣営情報が定義されていても、実際にマップ上に存在しているとは限らないことに注意してください。
現在定義されているインデックスは以下の通りです。
GAMESTATUS_SIDECOUNT : 定義されている陣営の数。
GAMESTATUS_TURN : 現在のターン数。
スクリプトにおいて、ユニットは陣営番号とシリアルで識別されます。 シリアルは本体が一意に付けた数値で、スクリプトが付け替えることはできません。
long ks_unit_side_on(long x, long y)
long unit_side_on(long x, long y)
(x,y)座標にいるユニットの陣営番号を返します。
いなければ-1を返します。
long ks_unit_serial_on(long x, long y)
long unit_serial_on(long x, long y)
(x,y)座標にいるユニットのシリアルを返します。
いなければ-1を返します。
long ks_unit_status_get(long side, long serial, long *array)
long unit_status_get(long side, long serial, long *array)
ユニットのステータス情報を得ます。serial は ks_unit_serial_on() の戻り値を使います。
例:
long unit_status[UNITSTATUS_MAX]; unit_status_get(0, 0, &unit_status); if(unit_status[UNITSTATUS_LIFE] < 50) window_msg_out("ライフが少なくてピンチ");現在定義されているインデックスは以下の通りです。
long ks_unit_append(long x, long y, long side, long id)
long unit_append(long x, long y, long side, long id)
陣営とユニットIDを指定して新しいユニットを設置します。
動作確認が不十分なため、テキサクモードと相性が悪いかもしれません。
陣営のユニット数が上限に達しているか、定義されていないユニットIDの場合は、ユニットは設置されず戻り値が-1になります。
long ks_unit_remove(long x, long y)
long unit_remove(long x, long y)
(x,y)座標にいるユニットを削除します。
動作確認が不十分なため、テキサクモードと相性が悪いかもしれません。
(x,y)座標にユニットがいない場合は戻り値が-1になります。
long ks_ground_status_get(long x, long y, long *array)
long ground_status_get(long x, long y, long *array)
(x,y)座標の地形のステータスを得ます。
例:
long gnd_status[GROUNDSTATUS_MAX]; ground_status_get(3, 4, &gnd_status); if(gnd_status[GROUNDSTATUS_ID] == 10) window_msg_out("座標(3,4)の地形は××だ");現在定義されているインデックスは以下の通りです。
long ks_ground_status_set(long x, long y, long *array)
long ground_status_set(long x, long y, long *array)
(x,y)座標の地形のステータスを変更します。
例:
long gnd_status[GROUNDSTATUS_MAX]; ground_status_get(3, 4, &gnd_status); gnd_status[GROUNDSTATUS_ID] = 10; ground_status_set(3, 4, &gnd_status); // (3,4)の地形IDを変更するこの関数を実行すると、ゲーム内部でデータが書き換わります。 実際に画面上の地形が更新されるのは、halt したかスクリプトの実行が終了した時になります。
long ks_side_status_get(long side, long *array)
long side_status_get(long side, long *array)
陣営のステータスを得ます。sideは 0 から始まります。
例:
long side_status[SIDESTATUS_MAX]; side_status_get(&side_status); if(side_status[SIDESTATUS_MONEY] < 10000) message("資金が少ないなあ");現在定義されているインデックスは以下の通りです。
long ks_side_status_set(long side, long *array)
long side_status_set(long side, long *array)
陣営のステータスを設定します。sideは 0 から始まります。
ユニットの数は変更できません。
例:
long side_status[SIDESTATUS_MAX]; side_status_get(&side_status); if(side_status[SIDESTATUS_MONEY] < 10000){ message("資金が10000支給された!"); side_status[SIDESTATUS_MONEY] += 10000; side_status_set(&side_status); }
long ks_side_relation_get(long side1, long side2, long *array)
long side_relation_get(long side1, long side2, long *array)
陣営同士の関係情報を得ます。sideは 0 から始まります。
例:
long side_relation[SIDERELATION_SIZE]; side_relation_get(side1,side2,&side_relation); if(side_relation[SIDERELATION_FRIENDLY] > 0) window_msg_out("友好国");現在定義されているインデックスは以下の通りです。
long ks_side_relation_set(long side1, long side2, long *array)
long side_relation_set(long side1, long side2, long *array)
陣営同士の関係情報を設定します。
例:side2 の陣営を side1 の敵にします。
long side_relation[SIDERELATION_SIZE]; side_relation_get(side1,side2,&side_relation); side_relation[SIDERELATION_FRIENDLY] = -1; side_relation_set(side1,side2,&side_relation);
long ks_var_set_int(str name, long var)
long ks_var_set_str(str name, str var)
long var_set_int(str name, long var)
long var_set_str(str name, str var)
セーブファイルに保存される変数を登録します。
このスクリプトでは文字列を、文字列へのポインタのlong型整数表現で扱うため
関数の型は同じです。しかし区別できないと困るので別々の関数になっています。
登録できる変数の数に限度は特にありませんが、セーブファイルに保存されるので数が多いと動作が重くなるかも?
long ks_var_get_int(str name)
void ks_var_get_str(str name, str var)
long var_get_int(str name)
void var_get_str(str name, str var)
ks_var_set で登録した変数の値を得ます。戻り値が変数の値です。
変数が存在しない場合は整数なら0が、文字列なら空文字列が返ります。
文字列の場合はポインタを返そうかとも考えたのですが、文字列を指すポインタを渡してそれに
値を入れるという形式にしました。
注:存在しない変数を得ようとすると、その変数が新たに登録されてしまいます。