# 局地戦略イベント記述用スクリプト

とりあえず最小限の構成でスクリプトを実装しました。 もちろん機能が足りないので色々追加していくつもりです。 複数のマップにまたがるキャンペーンモードなどをどのように実現するかは未定です。 あまりファイルの種類が増えるとまずいので多分スクリプト機能の一部になると思いますが。

スクリプトエンジンの中身はほとんど、やねうらおさんの公開されている yaneuraoGameScript のyaneuraoGameScript 2000 for yaneGameSDK2nd(通称ygs2kEx)のソースを流用したものです。 yaneuraoGameScript の内、コンパイラと仮想マシンを使って、ＲＰＧなどのイベントをスクリプトで記述し、 これを解釈実行するためのスクリプトエンジンを作成しました。
とりあえずは局地戦略のために開発しますが、そのうち独立したスクリプトエンジンとしてソースと共に公開したいと思います。

include
標準で用意されているスクリプトファイル
標準で用意されている関数
  デバッグのための設定
  イベントドリブン
  イベントウィンドウ
  ゲームの状況を得る
  ユニットの情報を得る／設置する／削除する
  地形の情報を得る／設定する
  陣営の情報を得る／設定する
  陣営同士の関係を得る／設定する
  変数の保存

スクリプトＱ＆Ａ...

## include

スクリプトの最初に（最初でなくてもいいですが）例えば 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)
アルファ値を含む色を表す整数を返します。値は (a<<24) | (b<<16) | (g<<8) | r です。
アルファ値が0なら不透明、255なら完全な透明になります。

## 標準で用意されている関数

スクリプトがロードされた状態で、既に使用可能な関数です。 戻り値が 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 にすると制限を加えません。 ステップ数はＶＭで実行される仮想コードの数（分からなければ気にしないでください）です。
デフォルトでは 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 になります。
なお「クリック」とはウィンドウ上でボタンを押した後、同じウィンドウ上でボタンを離す操作を言います。

long ks_window_mouse_left_downed()
long window_mouse_left_downed()
ウィンドウ上でマウスの左ボタンが押されたならば 1 を返します。 そうでなければ 0 を返します。 ウィンドウが表示された、またはこの関数を前回呼び出した時から１度でもボタンが押されていれば戻り値は 1 になります。

long ks_window_mouse_xy(long *x, long *y)
long window_mouse_xy(long *x, long *y)
ウィンドウ上にマウスカーソルがある時、その座標を得ます。その場合戻り値は０です。 マウスカーソルがウィンドウ外にあるならば戻り値が -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() 関数が １を返したら、この関数で座標を得るという使い方をします。

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() 関数が １を返したら、この関数で座標を得るという使い方をします。

### ゲームの状況を得る

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("ライフが少なくてピンチ");

現在定義されているインデックスは以下の通りです。
UNITSTATUS_ID : ユニットＩＤです。
UNITSTATUS_X : X座標。
UNITSTATUS_Y : Y座標。
UNITSTATUS_LIFE : ライフ。
UNITSTATUS_LIFEMAX : 最大ライフ。将来最大ライフを変更できるようになる可能性があります。
UNITSTATUS_GUN_n : 弾数。n は 0 or 1 です。将来火器の種類が増える……かも？
UNITSTATUS_GUNMAX_n : 最大弾数。

long ks_unit_append(long x, long y, long side, long id)
long unit_append(long x, long y, long side, long 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)の地形は××だ");

現在定義されているインデックスは以下の通りです。
GROUNDSTATUS_ID : 地形ＩＤです。
GROUNDSTATUS_LIFE : 施設の場合は耐久度です。施設以外の場合の値は未定義です。

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("資金が少ないなあ");

現在定義されているインデックスは以下の通りです。
SIDESTATUS_INCOME : 収入。
SIDESTATUS_MONEY : 現在の資金。
SIDESTATUS_UNITCOUNT : ユニットの数。ユニットインデックスは 0 から この値-1 までになります。

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("友好国");

現在定義されているインデックスは以下の通りです。
SIDERELATION_FRIENDLY : 友好度。零以下なら敵。思考ルーチンに影響する。 値が大きいと優先的に攻撃するかも？（現在は正が不戦、零が敵、負は零と同じ(^^;）
注意： ks_side_relation_get(side1,side2,..)とks_side_relation_get(side2,side1,..)の結果は同一ではありません。

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が、文字列なら空文字列が返ります。 文字列の場合はポインタを返そうかとも考えたのですが、文字列を指すポインタを渡してそれに 値を入れるという形式にしました。
注：存在しない変数を得ようとすると、その変数が新たに登録されてしまいます。