2008年1月 9日 (水): 吉里吉里プロフェッショナル版
2.目次
3.利用規約
利用規約は、吉里吉里ヒント集の規約に準じます。
4.価格
フリー公開しますので、こちらも無料でご利用頂けます。
5.謝辞
これらの追加機能ヒントを作成するにあたって、以下のサイトを参考にさせていただきました。
- 吉里吉里情報局
- Aqua Place&おさかな定食
- 清水恵のデータ工房内「吉里吉里2/KAG3覚え書き」
- Project Lips「KAGチョイ技集」
- Outfocus Wiki Inside KAG3
6.プロフェッショナル版
これらの機能は、CircleMebius製作「月下之煌(げっかのきらめき)」にて実装されているものを、さらにバージョンアップさせて洗練させたものです。
![]()
実際の製品としての動作実績がありますので、安心してご利用下さい。
体験版でどんな動作なのかご確認することもできます。
また、機能概要を説明したサンプルをご用意しています。
サンプルはこちらからダウンロードして下さい。
初期状態の吉里吉里から導入したサンプルを入れています。
画像など、こちらからサイズやファイル名を参考にして、コピーしてお使い下さい。
なお、ソースの追加部分には全て"mebius"というコメントを入れていますので、初期状態からの変更部分はこの文字列を検索することですぐに発見できます。
6.1 サンプル一覧
以下に、サンプルの一覧を掲載しています。
解説もありますので、一度ご覧下さい。
総合サンプルに全ての解説が入っていますので、こちらが便利です。
また、画像など、こちらのサンプルをダウンロードして参考にして画像生成などをしていただければ、ファイル名などのミスもなくスムーズにできると思います。
| 内容 | 実行サンプル | 画像・ボイスデータ サンプル |
| 総合サンプル (下記全てを含みます) |
ダウンロード | ダウンロード |
| 簡易メニューのみ | ダウンロード | ダウンロード |
| 文字のフェード表示のみ | ダウンロード | - |
| ボイスシステムのみ | ダウンロード | ダウンロード |
| セーブ・ロード画面のみ | ダウンロード | ダウンロード |
6.2 簡易メニュー
機能

- 簡易メニュー(下が命令、右が保存、左が設定、上がシステム)
- マウスが指定領域内に入ったら表示、領域から出たら消去(常に表示・常に非表示も可能)
- キーボード操作対応
- ツールチップヒントの表示機能
- 簡易メニュー内でスライダーの使用も可能
- オートセーブ機能付き(オートセーブ内容は、新しい順に表示されます)
この簡易メニューは自信を持ってご提供できる機能ですので、是非お役立て下さい。
なお、特に制約を受けそうな制限は以下の通りです。
- 現状では、オートセーブに対応した記述になっています。0~99のスロットまで(プレイ画面表記上では1~100まで)が通常の保存用スロットで、100~109まで(プレイ画面表記上では自動1~自動10まで)が自動保存用スロットです。
これについては修正は可能ですが、変数で一括に処理とかはしておらず、ソース中の条件値や値を一つ一つ修正していかなければなりません。なので、この項目を修正する場合は少々時間がかかる場合があります。(とはいえ、構造が分かれば数日ぐらいで修正できる程度だとは思いますが)
それでは、以下に実装方法を記します。
Config.tjsでの処理
- サンプルでは「画面サイズ」は800x600を想定しています。もし違うサイズで実装する場合、個別に調整願います。("800"とか"600"とかの文字列で検索すれば、修正箇所はすぐに見つかります)
- メッセージレイヤの数を変更します。簡易メニューだけでも5枚のメッセージレイヤを用います。それに合うようにnumMessageLayersの数を増やします。
- もし初期設定のままなら、「メッセージレイヤの色と不透明度」でframeOpacity = 0とします。でないと文字枠が出てしまいます。文字枠は全景レイヤーに用意するか、もしくはmessage0などに画像を指定し直して下さい。
- セーブ画像を使う場合、サムネイルを保存するように設定しておきます。(saveThumbnail = trueに)
- サムネイルの保存サイズも適切なものにします。デフォルトのサンプルのまま用いるのであれば、横118pixelで保存します。(thumbnailWidth = 118に)。大きさはどの数値でも問題ありませんので、適宜合うように調整して下さい。
- サムネイルの画質をよくするため、サムネイル保存モードで、24ビットにしておきます。(thumbnailDepth = 24)
- 利用可能な栞の数(numBookMarks)を適切な数値にしておきます。(ここでは110個と想定してます)
Mainwindow.tjs
まずは簡易メニューの変数定義を追加します。
Mainwindow.tjsのfunction KAGWindow()直前の変数定義に、以下の命令を追加します。
var holdPeriodEventQueue = []; // 保留にされたムービーのピリオドイベントキュー
var isLeavePeriodEvent = false; // ムービーのピリオドイベントを保留にするかどうか
var isWaitPeriodEvent = false; // ムービーのピリオドイベント待ち状態かどうか
var waitedPeriodEventStorageName = void; // ピリオドイベント待ちをコールしたストレージ名
//↓--------------------------------------- ここから追加部分--
//mebius:変数追加分
var autosaveLoadedFlag = false;
var timermove_qexec;//簡易メニュー表示用タイマー
var timermove_qsave;
var timermove_qconf;
var timermove_qsys;
var movemode_qexec = 0;
var movemode_qsave = 0;
var movemode_qconf = 0;
var movemode_qsys = 0;
var qmenuExec_LoadButton;//簡易メニューボタン
var qmenuExec_SaveButton;
var qmenuExec_ConfigButton;
var qmenuExec_HistryButton;
var qmenuExec_VoiceButton;
var qmenuExec_SkipButton;
var qmenuExec_AutoButton;
var qmenuExec_EraseButton;
var qmenuExec_MenuButton;
var qmenuSave_UpButton;
var qmenuSave_DownButton;
var qmenuSave_EntryButton = [];
var qmenuConfig_FullScrButton;
var qmenuConfig_WindowButton;
var qmenuConfig_2ndReadButton;
var qmenuSystem_ReturnTopButton;
var qmenuSystem_ExitButton;
var qmenuLayer_Exec;//簡易メニューのレイヤナンバーを保持する変数
var qmenuLayer_Save;
var qmenuLayer_Config;
var qmenuLayer_System;
var qmenuLayer_Hint;
//↑--------------------------------------- ここまで追加部分--
//------------------------------------------------------ コンストラクタ --
function KAGWindow(ismain = true, width = 0, height = 0)
{
// コンストラクタ
// 引数 : ismain : メインウィンドウとして作成されるのかどうか
super.Window(); // 親クラスのコンストラクタを呼ぶ
// コンフィギュレーション
isMain = ismain;
if(ismain)
{
コンストラクタ内部に簡易メニューの部分を記述します。
function KAGWindow()の中に、以下の命令を追加します。
//------------------------------------------------------ コンストラクタ --
function KAGWindow(ismain = true, width = 0, height = 0)
{
// コンストラクタ
// 引数 : ismain : メインウィンドウとして作成されるのかどうか
super.Window(); // 親クラスのコンストラクタを呼ぶ
// コンフィギュレーション
// (中略)
// ウィンドウサイズの調整
if(width != 0 && height != 0)
{
// 与えられたサイズを適用
scWidth = width;
scHeight = height;
}
setInnerSize(scWidth, scHeight);
// quake 用タイマの作成
quakeTimer = new Timer(onQuakeTimerInterval, '');
add(quakeTimer);
quakeTimer.interval = 50;
//↓--------------------------------------- ここから追加部分--
//mebius:簡易メニュー用タイマー定義
timermove_qexec = new Timer(onTimerMoveQExec, '');
timermove_qexec.interval = 10;
timermove_qexec.enabled = false;
timermove_qsave = new Timer(onTimerMoveQSave, '');
timermove_qsave.interval = 10;
timermove_qsave.enabled = false;
timermove_qconf = new Timer(onTimerMoveQConfig, '');
timermove_qconf.interval = 10;
timermove_qconf.enabled = false;
timermove_qsys = new Timer(onTimerMoveQSystem, '');
timermove_qsys.interval = 10;
timermove_qsys.enabled = false;
//↑--------------------------------------- ここまで追加部分--
// 背景レイヤの作成
fore.messages = [];
back.messages = [];
fore.layers = [];
back.layers = [];
fore.base = new BaseLayer(this, null, "表-背景");
add(fore.base);
fore.base.setImageSize(scWidth, scHeight);
fore.base.setSizeToImageSize();
back.base = new BaseLayer(this, fore.base, "裏-背景");
add(back.base);
セーブ時のサムネイルに、簡易メニューを入れないようにします。
function lockSnapshot()に、以下の命令を追加します。
function lockSnapshot()
{
// スナップショットをロックする
// 初めてスナップショットがロックされた時点での画面を保存する
if(snapshotLockCount == 0)
{
//↓--------------------------------------- ここから追加部分--
//mebius:簡易メニュー類はスナップショットから外す。
var mes2 = false;
var mes3 = false;
var mes4 = false;
var mes5 = false;
var mes6 = false;
var slider_object_show_temp = false;
//↑--------------------------------------- ここまで追加部分--
if(snapshotLayer === void)
snapshotLayer = new Layer(this, primaryLayer);
snapshotLayer.setImageSize(scWidth, scHeight);
snapshotLayer.face = dfAlpha;
//↓--------------------------------------- ここから追加部分--
//mebius:簡易メニュー類はスナップショットから外す。
if (kag.fore.messages[qmenuLayer_Exec].visible)
{
mes2 = true;
kag.fore.messages[qmenuLayer_Exec].visible = false;
}
if (kag.fore.messages[qmenuLayer_Save].visible)
{
mes3 = true;
kag.fore.messages[qmenuLayer_Save].visible = false;
}
if (kag.fore.messages[qmenuLayer_Config].visible)
{
mes4 = true;
kag.fore.messages[qmenuLayer_Config].visible = false;
if (slider_object.foreControlLayer.Sliders[0].visible)//簡易メニューのスライダーが表示中なら消去
{
slider_object_show_temp = true;
slider_object.setOptions(%[forevisible:false]);
}
}
if (kag.fore.messages[qmenuLayer_System].visible)
{
mes5 = true;
kag.fore.messages[qmenuLayer_System].visible = false;
}
if (kag.fore.messages[qmenuLayer_Hint].visible)
{
mes6 = true;
kag.fore.messages[qmenuLayer_Hint].visible = false;
}
//↑--------------------------------------- ここまで追加部分--
snapshotLayer.piledCopy(0, 0, kag.fore.base, 0, 0, scWidth, scHeight);
//↓--------------------------------------- ここから追加部分--
//mebius:簡易メニュー類はスナップショットから外す。
if (mes2)
{
kag.fore.messages[qmenuLayer_Exec].visible = true;
}
if (mes3)
{
kag.fore.messages[qmenuLayer_Save].visible = true;
}
if (mes4)
{
kag.fore.messages[qmenuLayer_Config].visible = true;
if (slider_object_show_temp)
slider_object.setOptions(%[forevisible:true]);
}
if (mes5)
{
kag.fore.messages[qmenuLayer_System].visible = true;
}
if (mes6)
{
kag.fore.messages[qmenuLayer_Hint].visible = true;
}
//↑--------------------------------------- ここまで追加部分--
}
snapshotLockCount ++;
}
オートセーブ機能に対応させておきます。
オートセーブ機能を使わない場合は、この部分は入れなくても大丈夫です。(入れておいても問題ありません)
function loadBookMark()、function saveBookMarkWithAsk()、function loadBookMarkWithAsk()に、以下の命令を追加します。
2箇所ほど、元々ある行の位置を変更する行(else文の中に入れる行)があるので注意して下さい。
function loadBookMark(num, loaduser = true)
{
// 栞番号 num からデータを読み出す
//↓--------------------------------------- ここから追加部分--
autosaveLoadedFlag = true;//mebius:オートセーブ読み込み直後に再び保存しない対策。
//↑--------------------------------------- ここまで追加部分--
return loadBookMarkFromFile(getBookMarkFileNameAtNum(num), loaduser);
}
//↓--------------------------------------- ここから追加部分--
function saveBookMarkWithAsk(num,autoon,autostr)//mebius:引数追加(autoon,autostr)
//↑--------------------------------------- ここまで追加部分--
{
// 栞番号 num に栞を設定する
// そのとき、設定するかどうかをたずねる
if(readOnlyMode) return false;
if(bookMarkProtectedStates[num]) return false;
var prompt = "栞 ";
//↓--------------------------------------- ここから追加部分--
//mebius:autoonがtrueならstrを番号として使う。
if(autoon)
prompt += autostr;
else
if(num < numBookMarks) prompt += (num+1);//元々あるこの行は、elseの中に入れます。
//↑--------------------------------------- ここまで追加部分--
if(bookMarkDates[num] != "") // bookMarkDates が空文字の場合は栞は存在しない
prompt += "「" + bookMarkNames[num] + "」";
prompt += "に「"+ pcflags.currentPageName + "」をはさみますか?";
var result = askYesNo(prompt);
if(result) return saveBookMark(num);
return false;
}
//↓--------------------------------------- ここから追加部分--
function loadBookMarkWithAsk(num,autoon,autostr)//mebius:引数追加(autoon,autostr)
//↑--------------------------------------- ここまで追加部分--
{
// 栞番号 num から栞を読み出す
// そのとき、読み出すかどうかをたずねる
if(num < numBookMarks && bookMarkDates[num] == "") // bookMarkDates が空文字の場合は栞は存在しない
return false;
var prompt = "栞 ";
//↓--------------------------------------- ここから追加部分--
//mebius:autoonがtrueならstrを番号として使う。
if(autoon)
prompt += autostr;
else
if(num < numBookMarks) prompt += (num+1);//元々あるこの行は、elseの中に入れます。
//↑--------------------------------------- ここまで追加部分--
prompt += "「"+ bookMarkNames[num] + "」をたどりますか?";
var result = askYesNo(prompt);
if(result) return loadBookMark(num);
return false;
}
マウスが該当エリアに入ったら、簡易メニューを表示する命令を追加します。
それぞれの座標は個別に修正して下さい。
ウィンドウモード(全画面表示でない)場合、簡易メニューを表示させたままマウスカーソルをウィンドウから外した場合、簡易メニューが表示されたままになります。そのため、上下左右の端に表示しない領域を少し確保しておく方がいいかと思います。
なお、座標(800,600)にマウスカーソルがある場合、何も表示しないように注意して下さい。理由は次のキーボード対応の項目の影響によるものです。
function onMouseDown(x, y)の後に、以下の命令を追加します。
function onMouseDown(x, y)
{
lastMouseDownX = x;
lastMouseDownY = y;
super.onMouseDown(...);
}
//↓--------------------------------------- ここから追加部分--
//mebius:マウス移動時に簡易メニューの表示判定
function onMouseMove(x, y, shift)
{
//マウスが簡易メニュー(下:命令)表示領域なら、簡易メニュー(下:命令)を表示する。
if (sf.qmenu_exec_mode == 1 && f.qmenu_enabled)
{
if (x > 160 && x < 725 && y > 565 && y < 600)
showQExecMenu('scroll');
else
hideQExecMenu('scroll');
}
//マウスが簡易メニュー(右:セーブ)表示領域なら、簡易メニュー(右:セーブ)を表示する。
if (sf.qmenu_save_mode == 1 && f.qmenu_enabled)
{
if (x > 760 && y > 5 && y < 553)
{
showQSaveMenu('scroll');
}
else
{
hideQSaveMenu('scroll');
}
}
//マウスが簡易メニュー(左:設定)表示領域なら、簡易メニュー(左:設定)を表示する。
if (sf.qmenu_config_mode == 1 && f.qmenu_enabled)
{
if (x <130 && y > 5 && y < 440)
{
showQConfigMenu('scroll');
}
else
{
hideQConfigMenu('scroll');
}
}
//マウスが簡易メニュー(上:システム)表示領域なら、簡易メニュー(上:システム)を表示する。
if (sf.qmenu_system_mode == 1 && f.qmenu_enabled)
{
if (x > 250 && x < 750 && y >= 0 && y < 25)
showQSystemMenu('scroll');
else
hideQSystemMenu('scroll');
}
super.onMouseMove(...);
}
//↑--------------------------------------- ここまで追加部分--
//------------------------------------------------------- キーボード操作 --
function processKeys(key, shift)
{
キーボード対応させます。
「移動表示」の場合、上下左右キーで各簡易メニューを表示します。
キャンセルはESCキーで、ESCキーを押すと座標(800,600)にマウスカーソルを移動させています。
function processKeys(key, shift)に、以下の命令を追加します。
//------------------------------------------------------- キーボード操作 --
function processKeys(key, shift)
{
if(checkProceedingKey(key, shift)) return;
if(key == #'F')
{
// 次の選択肢/未読まで進む
skipToNextStopByKey();
return;
}
// (中略)
if(key == #'R' || (key == VK_UP && (shift & ssShift)))
{
// メッセージ履歴を表示
showHistoryByKey();
return;
}
//↓--------------------------------------- ここから追加部分--
//mebius:キーボード対応。PageUP、PageDownで簡易メニューのページ変更。
if(key == VK_PRIOR || key == VK_NEXT)
{
//簡易セーブの場合、ページ変更
if (kag.fore.messages[qmenuLayer_Save].visible == true)
{
if (key == VK_PRIOR)
{
if (sf.saveload_page == 0)
sf.saveload_page = 10;
else
sf.saveload_page--;
kag.redrawQSaveMenu();
}
else if (key == VK_NEXT)
{
if (sf.saveload_page == 10)
sf.saveload_page = 0;
else
sf.saveload_page++;
kag.redrawQSaveMenu();
}
return;
}
}
//↑--------------------------------------- ここまで追加部分--
if(key == VK_ESCAPE)
{
//↓--------------------------------------- ここから追加部分--
//mebius:キーボード対応
//簡易メニュー(下:命令)、簡易メニュー(右:セーブ)上なら非表示にする
if(f.qmenu_enabled && kag.fore.messages[qmenuLayer_Exec].visible == true && sf.qmenu_exec_mode > 0)
{
if (sf.qmenu_exec_mode == 2)
{
kag.fore.base.setCursorPos(800,600);
kag.focusedLayer = null;
return;
}
else if (sf.qmenu_exec_mode == 1)
{
hideQExecMenu('nowait');
kag.fore.base.setCursorPos(800,600);
kag.focusedLayer = null;
return;
}
}
else if (f.qmenu_enabled && fore.messages[qmenuLayer_Save].visible == true)
{
if (sf.qmenu_save_mode == 2)
{
kag.fore.base.setCursorPos(800,600);
kag.focusedLayer = null;
return;
}
else if (sf.qmenu_save_mode == 1)
{
hideQSaveMenu('nowait');
hideQMenuHint();
kag.fore.base.setCursorPos(800,600);
kag.focusedLayer = null;
return;
}
}
else if (f.qmenu_enabled && fore.messages[qmenuLayer_Config].visible == true)
{
if (sf.qmenu_config_mode == 2)
{
kag.fore.base.setCursorPos(800,600);
kag.focusedLayer = null;
return;
}
else if (sf.qmenu_config_mode == 1)
{
hideQConfigMenu('nowait');
hideQMenuHint();
kag.fore.base.setCursorPos(800,600);
kag.focusedLayer = null;
return;
}
}
else if (f.qmenu_enabled && fore.messages[qmenuLayer_System].visible == true)
{
if (sf.qmenu_system_mode == 2)
{
kag.fore.base.setCursorPos(800,600);
kag.focusedLayer = null;
return;
}
else if (sf.qmenu_system_mode == 1)
{
hideQSystemMenu('nowait');
hideQMenuHint();
kag.fore.base.setCursorPos(800,600);
kag.focusedLayer = null;
return;
}
}
//↑--------------------------------------- ここまで追加部分--
// メッセージを消す
if(typeof this.rightClickMenuItem != "undefined" &&
rightClickMenuItem.enabled)
{
rightClickMenuItem.click(); // クリックをエミュレート
return;
}
}
//↓--------------------------------------- ここから追加部分--
//mebius:キーボード対応
if(f.qmenu_enabled)
{
if(key == VK_UP && fore.messages[qmenuLayer_System].visible == false)
{
if (sf.qmenu_system_mode == 1)
showQSystemMenu('nowait');
return;
}
if(key == VK_LEFT && fore.messages[qmenuLayer_Config].visible == false)
{
if (sf.qmenu_config_mode == 1)
showQConfigMenu('nowait');
return;
}
if(key == VK_DOWN && kag.fore.messages[qmenuLayer_Exec].visible == false)
{
if (sf.qmenu_exec_mode == 1)
showQExecMenu('nowait');
return;
}
if(key == VK_RIGHT && fore.messages[qmenuLayer_Save].visible == false)
{
if (sf.qmenu_save_mode == 1)
showQSaveMenu('nowait');
/*
if (qmenuSave_UpButton !== void)
{
kag.focusedLayer = qmenuSave_UpButton;
//kag.fore.base.setCursorPos(768,14);
//qmenuSave_UpButton.focus();
tf.qsavefocus = true;
}
*/
return;
}
}
//↑--------------------------------------- ここまで追加部分--
}
マウスホイールに対応させます。
function onMouseWheel(shift, delta, x, y)に、以下の命令を追加します。
function onMouseWheel(shift, delta, x, y)
{
// ホイールが回転した
super.onMouseWheel(...);
//↓--------------------------------------- ここから追加部分--
//mebius:マウスが簡易メニュー(右:セーブ)表示領域なら、ページ変更する。
if (sf.qmenu_save_mode >= 1 && f.qmenu_enabled && fore.messages[qmenuLayer_Save].visible == true)
{
if (x > 760 && y > 5 && y < 570)
{
if (delta > 0)
upPageQSaveMenuClick();
else if (delta < 0)
downPageQSaveMenuClick();
}
return;
}
//↑--------------------------------------- ここまで追加部分--
if(!historyLayer.visible)
{
if(delta > 0)
showHistoryByKey(); // メッセージ履歴を表示
else if(System.getTickCount() - lastHistoryHiddenTick > 150)
onPrimaryClick(); // クリックをエミュレート
// ↑ tick を比較しているのは、メッセージ履歴を隠す操作とホイールを
// 手前に回す操作が連続した場合に勝手に読み進むのをある程度防ぐ仕掛け
}
else
{
// メッセージ履歴にイベントを垂れ流す
historyLayer.windowMouseWheel(shift, delta, x, y);
}
}
簡易メニューの本体部分の命令です。
ここで各ボタンの座標や画像、処理方法などを記します。
個別に対応するように修正して下さい。
ボタンを押した後の処理も、個別に追加・修正して下さい。(function ~Click()という命令に記述します)
クイックセーブ・クイックロードボタンは入れていませんが、命令を参考にしてボタンを増やして、処理させればすぐにできると思います。
MainWindow.tjsの最後の方にある「タグハンドラ群の終わり」の後ぐらいに、以下の命令を追加します。
waittrig : function(elm)
{
// トリガを待つ
return waitTrigger(elm);
} incontextof this,
//----------------------------------------------- タグハンドラ群の終わり --
interrupt : function(elm) { return -2; } incontextof this ];
}
//↓--------------------------------------- ここから追加部分--
//--------------------------------------- mebius:命令群 --
//----簡易メニュー共通---------------------------------------------------------
//簡易メニューのレイヤー定義
function defineQMenuLayers()
{
//定義されたシステム変数から、メッセージレイヤナンバーを抜き出して保持します。
var exec_layer = sf.qmenu_exec_layer;
var save_layer = sf.qmenu_save_layer;
var config_layer = sf.qmenu_config_layer;
var system_layer = sf.qmenu_system_layer;
var hint_layer = sf.qmenu_hint_layer;
qmenuLayer_Exec = +exec_layer.substr(7);
qmenuLayer_Save = +save_layer.substr(7);
qmenuLayer_Config = +config_layer.substr(7);
qmenuLayer_System = +system_layer.substr(7);
qmenuLayer_Hint = +hint_layer.substr(7);
}
//----簡易メニュー(下:命令)---------------------------------------------------------
//簡易メニュー(下:命令)を作成
function makeQExecMenu(backmode)
{
var destlayer;
if (backmode == 'back')
destlayer = back.messages[qmenuLayer_Exec];
else
destlayer = fore.messages[qmenuLayer_Exec];
destlayer.face = dfBoth;
//読込ボタン
if (qmenuExec_LoadButton === void)
{
var qtxtElm = %[];
qtxtElm.graphic = "qmenu_exec01";
qtxtElm.hint = '';
qtxtElm.visible = true;
qtxtElm.onenter = "kag.makeQMenuHint(158,550,' 読込','qmenu_hint_base01')";
qtxtElm.onleave = "kag.hideQMenuHint()";
qtxtElm.exp = "kag.loadQExecMenuClick()";
qtxtElm.rc = "";
//qtxtElm.enterse ='';
//qtxtElm.entersebuf = '';
//qtxtElm.leavese = '';
//qtxtElm.leavesebuf = '';
qmenuExec_LoadButton = new LinkButtonLayer(this, destlayer);
qmenuExec_LoadButton.showFocusImage = false;
destlayer.locate(180,577);
destlayer.addButton(qtxtElm);
}
//保存ボタン
if (qmenuExec_SaveButton === void)
{
var qtxtElm = %[];
qtxtElm.graphic = "qmenu_exec02";
qtxtElm.hint = '';
qtxtElm.visible = true;
qtxtElm.onenter = "kag.makeQMenuHint(229,550,' 保存','qmenu_hint_base01')";
qtxtElm.onleave = "kag.hideQMenuHint()";
qtxtElm.exp = "kag.saveQExecMenuClick()";
qtxtElm.rc = "";
//qtxtElm.enterse ='';
//qtxtElm.entersebuf = '';
//qtxtElm.leavese = '';
//qtxtElm.leavesebuf = '';
qmenuExec_SaveButton = new LinkButtonLayer(this, destlayer);
qmenuExec_SaveButton.showFocusImage = false;
destlayer.locate(250,577);
destlayer.addButton(qtxtElm);
}
//設定ボタン
if (qmenuExec_ConfigButton === void)
{
var qtxtElm = %[];
qtxtElm.graphic = "qmenu_exec11";
qtxtElm.hint = '';
qtxtElm.visible = true;
qtxtElm.onenter = "kag.makeQMenuHint(388,550,' 設定','qmenu_hint_base01')";
qtxtElm.onleave = "kag.hideQMenuHint()";
qtxtElm.exp = "kag.configQExecMenuClick()";
qtxtElm.rc = "";
//qtxtElm.enterse ='';
//qtxtElm.entersebuf = '';
//qtxtElm.leavese = '';
//qtxtElm.leavesebuf = '';
qmenuExec_ConfigButton = new LinkButtonLayer(this, destlayer);
qmenuExec_ConfigButton.showFocusImage = false;
destlayer.locate(410,577);
destlayer.addButton(qtxtElm);
}
//履歴ボタン
if (qmenuExec_HistryButton === void)
{
var qtxtElm = %[];
qtxtElm.graphic = "qmenu_exec03";
qtxtElm.hint = '';
qtxtElm.visible = true;
qtxtElm.onenter = "kag.makeQMenuHint(448,550,' 履歴','qmenu_hint_base01')";
qtxtElm.onleave = "kag.hideQMenuHint()";
qtxtElm.exp = "kag.historyQExecMenuClick()";
qtxtElm.rc = "";
//qtxtElm.enterse ='';
//qtxtElm.entersebuf = '';
//qtxtElm.leavese = '';
//qtxtElm.leavesebuf = '';
qmenuExec_HistryButton = new LinkButtonLayer(this, destlayer);
qmenuExec_HistryButton.showFocusImage = false;
destlayer.locate(470,577);
destlayer.addButton(qtxtElm);
}
//ボイス再生ボタン
if (qmenuExec_VoiceButton === void)
{
var qtxtElm = %[];
qtxtElm.graphic = "qmenu_exec09";
qtxtElm.hint = '';
qtxtElm.visible = true;
qtxtElm.onenter = "kag.makeQMenuHint(501,550,' 音声再生','qmenu_hint_base01')";
qtxtElm.onleave = "kag.hideQMenuHint()";
qtxtElm.exp = "kag.voiceQExecMenuClick()";
qtxtElm.rc = "";
//qtxtElm.enterse ='';
//qtxtElm.entersebuf = '';
//qtxtElm.leavese = '';
//qtxtElm.leavesebuf = '';
qmenuExec_VoiceButton = new LinkButtonLayer(this, destlayer);
qmenuExec_VoiceButton.showFocusImage = false;
destlayer.locate(520,577);
destlayer.addButton(qtxtElm);
}
//スキップボタン
if (qmenuExec_SkipButton === void)
{
var qtxtElm = %[];
qtxtElm.graphic = "qmenu_exec04";
qtxtElm.hint = '';
qtxtElm.visible = true;
qtxtElm.onenter = "kag.makeQMenuHint(547,550,' 早送り','qmenu_hint_base01')";
qtxtElm.onleave = "kag.hideQMenuHint()";
qtxtElm.exp = "kag.skipQExecMenuClick()";
qtxtElm.rc = "";
//qtxtElm.enterse ='';
//qtxtElm.entersebuf = '';
//qtxtElm.leavese = '';
//qtxtElm.leavesebuf = '';
qmenuExec_SkipButton = new LinkButtonLayer(this, destlayer);
qmenuExec_SkipButton.showFocusImage = false;
destlayer.locate(570,577);
destlayer.addButton(qtxtElm);
}
//自動再生ボタン
if (qmenuExec_AutoButton === void)
{
var qtxtElm = %[];
qtxtElm.graphic = "qmenu_exec05";
qtxtElm.hint = '';
qtxtElm.visible = true;
qtxtElm.onenter = "kag.makeQMenuHint(596,550,' 自動再生','qmenu_hint_base01')";
qtxtElm.onleave = "kag.hideQMenuHint()";
qtxtElm.exp = "kag.autoQExecMenuClick()";
qtxtElm.rc = "";
//qtxtElm.enterse ='';
//qtxtElm.entersebuf = '';
//qtxtElm.leavese = '';
//qtxtElm.leavesebuf = '';
qmenuExec_AutoButton = new LinkButtonLayer(this, destlayer);
qmenuExec_AutoButton.showFocusImage = false;
destlayer.locate(620,577);
destlayer.addButton(qtxtElm);
}
//消去ボタン
if (qmenuExec_EraseButton === void)
{
var qtxtElm = %[];
qtxtElm.graphic = "qmenu_exec06";
qtxtElm.hint = '';
qtxtElm.visible = true;
qtxtElm.onenter = "kag.makeQMenuHint(644,550,' 文字枠消去','qmenu_hint_base01')";
qtxtElm.onleave = "kag.hideQMenuHint()";
qtxtElm.exp = "kag.eraseQExecMenuClick()";
qtxtElm.rc = "";
//qtxtElm.enterse ='';
//qtxtElm.entersebuf = '';
//qtxtElm.leavese = '';
//qtxtElm.leavesebuf = '';
qmenuExec_EraseButton = new LinkButtonLayer(this, destlayer);
qmenuExec_EraseButton.showFocusImage = false;
destlayer.locate(670,577);
destlayer.addButton(qtxtElm);
}
//メニューボタン
/*
if (qmenuExec_MenuButton === void)
{
var qtxtElm = %[];
qtxtElm.graphic = "qmenu_exec07";
qtxtElm.hint = '';
qtxtElm.visible = true;
qtxtElm.onenter = "kag.makeQMenuHint(644,550,'メニュー表示','qmenu_hint_base01')";
qtxtElm.onleave = "kag.hideQMenuHint()";
qtxtElm.exp = "kag.menuQExecMenuClick()";
qtxtElm.rc = "";
//qtxtElm.enterse ='';
//qtxtElm.entersebuf = '';
//qtxtElm.leavese = '';
//qtxtElm.leavesebuf = '';
qmenuExec_MenuButton = new LinkButtonLayer(this, destlayer);
qmenuExec_MenuButton.showFocusImage = false;
destlayer.locate(670,577);
destlayer.addButton(qtxtElm);
}
*/
}
//簡易メニュー(下:命令)を表示
function showQExecMenu(mvmode,backmode)
{
var destlayer;
if(skipMode && sf.qmenu_exec_mode != 2) return;
if (backmode == "back")
destlayer = back.messages[qmenuLayer_Exec];
else
destlayer = fore.messages[qmenuLayer_Exec];
if (!movemode_qexec)
{
if (destlayer.visible == false)
makeQExecMenu();
if (mvmode == "scroll") //"scroll"なら移動表示
{
destlayer.visible = true;
movemode_qexec = 1;
timermove_qexec.enabled = true;
}
else //"nowait"なら瞬間表示
{
destlayer.visible = true;
destlayer.top = 0;
}
}
}
//簡易メニュー(下:命令)の消去
function hideQExecMenu(mvmode)
{
if (!movemode_qexec)
{
if (mvmode == "scroll") //"scroll"なら移動表示
{
if (f.qmenu_enabled && fore.messages[qmenuLayer_Exec].visible == true)
//kag.fore.messages[qmenuLayer_Exec].visible = true;
movemode_qexec = 2;
timermove_qexec.enabled = true;
}
else //"nowait"なら瞬間消去
{
fore.messages[qmenuLayer_Exec].top = sf.qmenu_exec_movepixel;
fore.messages[qmenuLayer_Exec].visible = false;
deleteQExecMenu();
}
}
}
//簡易メニュー(下:命令)を消去
function deleteQExecMenu()
{
if(qmenuExec_LoadButton !== void)
{
invalidate qmenuExec_LoadButton;
qmenuExec_LoadButton = void;
}
if(qmenuExec_SaveButton !== void)
{
invalidate qmenuExec_SaveButton;
qmenuExec_SaveButton = void;
}
if(qmenuExec_ConfigButton !== void)
{
invalidate qmenuExec_ConfigButton;
qmenuExec_ConfigButton = void;
}
if(qmenuExec_HistryButton !== void)
{
invalidate qmenuExec_HistryButton;
qmenuExec_HistryButton = void;
}
if(qmenuExec_VoiceButton !== void)
{
invalidate qmenuExec_VoiceButton;
qmenuExec_VoiceButton = void;
}
if(qmenuExec_SkipButton !== void)
{
invalidate qmenuExec_SkipButton;
qmenuExec_SkipButton = void;
}
if(qmenuExec_AutoButton !== void)
{
invalidate qmenuExec_AutoButton;
qmenuExec_AutoButton = void;
}
if(qmenuExec_EraseButton !== void)
{
invalidate qmenuExec_EraseButton;
qmenuExec_EraseButton = void;
}
if(qmenuExec_MenuButton !== void)
{
invalidate qmenuExec_MenuButton;
qmenuExec_MenuButton = void;
}
fore.messages[qmenuLayer_Exec].clear();
}
//簡易メニュー(下:命令)の表示用タイマ
function onTimerMoveQExec() {
var movespeed = sf.qmenu_exec_movespeed;//動く速度
switch (movemode_qexec) {
case 0://ストップ
timermove_qexec.enabled = false;
break;
case 1://表示
if (fore.messages[qmenuLayer_Exec].top > 0) {
fore.messages[qmenuLayer_Exec].top -= movespeed;
} else {
timermove_qexec.enabled = false;
movemode_qexec = 0;
}
break;
case 2://消去
if (fore.messages[qmenuLayer_Exec].top < sf.qmenu_exec_movepixel) {
fore.messages[qmenuLayer_Exec].top += movespeed;
} else {
timermove_qexec.enabled = false;
movemode_qexec = 0;
kag.fore.messages[qmenuLayer_Exec].visible = false;
deleteQExecMenu();
}
break;
}
}
function checkTextDrawing()//エラー回避のため、文字表示中は処理させないための判定用。
{
if ((!clickWaiting && !inSleep) || kag.skipMode>0 || kag.autoMode)
return true;
else
return false;
}
function loadQExecMenuClick()//ロードボタン処理
{
if (checkTextDrawing()) return;//エラー回避
kag.hideQMenuHint();
if (sf.qmenu_exec_mode != 2) kag.hideQExecMenu();
kag.callExtraConductor('qmenu.ks','*load');
}
function saveQExecMenuClick()//セーブボタン処理
{
if (checkTextDrawing()) return;//エラー回避
kag.hideQMenuHint();
if (sf.qmenu_exec_mode != 2) kag.hideQExecMenu();
kag.callExtraConductor('qmenu.ks','*save');
}
function configQExecMenuClick()//コンフィグボタン処理
{
if (checkTextDrawing()) return;//エラー回避
kag.hideQMenuHint();
if (sf.qmenu_exec_mode != 2) kag.hideQExecMenu();
kag.callExtraConductor('qmenu.ks','*config');
}
function historyQExecMenuClick()//履歴ボタン処理
{
if (checkTextDrawing()) return;//エラー回避
kag.hideQMenuHint();
if (sf.qmenu_exec_mode != 2) kag.hideQExecMenu();
kag.callExtraConductor('qmenu.ks','*history');
}
function voiceQExecMenuClick()//ボイス再生ボタン処理
{
if (checkTextDrawing()) return;//エラー回避
kag.hideQMenuHint();
//if (sf.qmenu_exec_mode != 2) kag.hideQExecMenu();
kag.callExtraConductor('qmenu.ks','*playvoice');
}
function skipQExecMenuClick()//スキップボタン処理
{
if (checkTextDrawing()) return;//エラー回避
kag.hideQMenuHint();
//if (sf.qmenu_exec_mode != 2) kag.hideQExecMenu();
kag.callExtraConductor('qmenu.ks','*skipmode');
}
function autoQExecMenuClick()//自動再生ボタン処理
{
if (checkTextDrawing()) return;//エラー回避
kag.hideQMenuHint();
//if (sf.qmenu_exec_mode != 2) kag.hideQExecMenu();
kag.callExtraConductor('qmenu.ks','*autoplay');
}
function eraseQExecMenuClick()//文字枠消去ボタン処理
{
if (checkTextDrawing()) return;//エラー回避
kag.hideQMenuHint();
if (sf.qmenu_exec_mode != 2) kag.hideQExecMenu();
kag.callExtraConductor('qmenu.ks','*erasetextwindow');
}
function menuQExecMenuClick()//メニューボタン処理
{
if (checkTextDrawing()) return;//エラー回避
kag.hideQMenuHint();
if (sf.qmenu_exec_mode != 2) kag.hideQExecMenu();
kag.callExtraConductor('qmenu.ks','*openmenu');
}
//----簡易メニュー(右:セーブ)---------------------------------------------------------
//簡易メニュー(右:セーブ)を作成
function makeQSaveMenu(backmode)
{
var destlayer;
if (backmode == 'back')
destlayer = back.messages[qmenuLayer_Save];
else
destlayer = fore.messages[qmenuLayer_Save];
destlayer.face = dfBoth;
//上ボタン
if (qmenuSave_UpButton === void)
{
var qsaveElm = %[];
qsaveElm.graphic = "qmenu_save_changepage01";
qsaveElm.hint = '';
qsaveElm.visible = true;
if (sf.saveload_page == 0)
qsaveElm.onenter = "kag.makeQMenuHint(573,17,' 最後のページに移動します。','qmenu_hint_base02')";
else
qsaveElm.onenter = "kag.makeQMenuHint(573,17,' 前のページに移動します。','qmenu_hint_base02')";
qsaveElm.onleave = "kag.hideQMenuHint()";
qsaveElm.exp = "kag.upPageQSaveMenuClick()";
qsaveElm.rc = "";
//qsaveElm.enterse ='';
//qsaveElm.entersebuf = '';
//qsaveElm.leavese = '';
//qsaveElm.leavesebuf = '';
qmenuSave_UpButton = new LinkButtonLayer(this, destlayer);
qmenuSave_UpButton.showFocusImage = false;
destlayer.locate(767,13);
destlayer.addButton(qsaveElm);
}
//各エントリ作成
for (var i=0;i<10;i++)
{
if (qmenuSave_EntryButton[i] === void)
{
var qsaveElm = %[];
var entry = i;
if (sf.saveload_page == 10)//オートセーブの場合、表示位置を修正
{
entry = sf.autosave_entry - i-1;
if (entry < 0) entry+=10;//負になった場合、+10して整数にする
}
if (sf.saveload_page == 10)
qsaveElm.graphic = "qmenu_save_highlight01";
else
qsaveElm.graphic = "qmenu_save_highlight01";
qsaveElm.hint = '';
qsaveElm.visible = true;
qsaveElm.onenter = "kag.makeQSaveMenuHint("+i+"+sf.saveload_page*10,420,44+48*"+entry+")";
qsaveElm.onleave = "kag.hideQMenuHint()";
qsaveElm.exp = "kag.lEntryQSaveMenuClick("+i+")";
qsaveElm.rc = "kag.rEntryQSaveMenuClick("+i+")";
//qsaveElm.enterse ='';
//qsaveElm.entersebuf = '';
//qsaveElm.leavese = '';
//qsaveElm.leavesebuf = '';
qmenuSave_EntryButton[i] = new LinkButtonLayer(this, destlayer);
qmenuSave_EntryButton[i].showFocusImage = false;
destlayer.locate(767,44+48*entry);
destlayer.addButton(qsaveElm);
//----------エントリ内容を画像で表示する場合----------
if (sf.saveload_page == 10)//オートセーブの場合
{
//ベース画像読み込み
var tmplayer = new Layer(this, destlayer);
tmplayer.loadImages("qmenu_save_entry02");
destlayer.copyRect(767,44+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight);
invalidate tmplayer;
//「自動」文字描画
var tmplayer = new Layer(this, destlayer);
tmplayer.loadImages("qmenu_save_mozi_auto");
destlayer.operateRect(767, 50+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight,omPsNormal,255);
invalidate tmplayer;
//数値
var tmplayer = new Layer(this, destlayer);
if (entry < 9)//1桁の場合
{
switch (entry) {
case 0: tmplayer.loadImages("qmenu_save_mozi01");break;
case 1: tmplayer.loadImages("qmenu_save_mozi02");break;
case 2: tmplayer.loadImages("qmenu_save_mozi03");break;
case 3: tmplayer.loadImages("qmenu_save_mozi04");break;
case 4: tmplayer.loadImages("qmenu_save_mozi05");break;
case 5: tmplayer.loadImages("qmenu_save_mozi06");break;
case 6: tmplayer.loadImages("qmenu_save_mozi07");break;
case 7: tmplayer.loadImages("qmenu_save_mozi08");break;
case 8: tmplayer.loadImages("qmenu_save_mozi09");break;
}
destlayer.operateRect(777, 65+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight,omPsNormal,255);
}
else//2桁の場合
{
//オートセーブのスロットを10より多く作る場合、個別に修正して下さい。
tmplayer.loadImages("qmenu_save_mozi01");
destlayer.operateRect(774, 65+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight,omPsNormal,255);
tmplayer.loadImages("qmenu_save_mozi00");
destlayer.operateRect(781, 65+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight,omPsNormal,255);
}
invalidate tmplayer;
}
else//通常セーブの場合
{
//ベース画像読み込み
var tmplayer = new Layer(this, destlayer);
tmplayer.loadImages("qmenu_save_entry01");
destlayer.copyRect(767,44+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight);
invalidate tmplayer;
//表示位置調整
var num = i+1+sf.saveload_page*10;
if (num < 10)//1桁の場合
{
var tmplayer = new Layer(this, destlayer);
switch (num) {
case 1: tmplayer.loadImages("qmenu_save_mozi01");break;
case 2: tmplayer.loadImages("qmenu_save_mozi02");break;
case 3: tmplayer.loadImages("qmenu_save_mozi03");break;
case 4: tmplayer.loadImages("qmenu_save_mozi04");break;
case 5: tmplayer.loadImages("qmenu_save_mozi05");break;
case 6: tmplayer.loadImages("qmenu_save_mozi06");break;
case 7: tmplayer.loadImages("qmenu_save_mozi07");break;
case 8: tmplayer.loadImages("qmenu_save_mozi08");break;
case 9: tmplayer.loadImages("qmenu_save_mozi09");break;
}
destlayer.operateRect(777, 65+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight,omPsNormal,255);
invalidate tmplayer;
}
else if (num == 100)//3桁の場合(ここでは100のみ)
{
var tmplayer = new Layer(this, destlayer);
tmplayer.loadImages("qmenu_save_mozi01");
destlayer.operateRect(769, 65+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight,omPsNormal,255);
tmplayer.loadImages("qmenu_save_mozi00");
destlayer.operateRect(776, 65+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight,omPsNormal,255);
tmplayer.loadImages("qmenu_save_mozi00");
destlayer.operateRect(783, 65+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight,omPsNormal,255);
invalidate tmplayer;
}
else//2桁の場合
{
var tmplayer = new Layer(this, destlayer);
var num_10 = num\10;
var num_1 = num%10;
switch (num_10) {//10の位
case 1: tmplayer.loadImages("qmenu_save_mozi01");break;
case 2: tmplayer.loadImages("qmenu_save_mozi02");break;
case 3: tmplayer.loadImages("qmenu_save_mozi03");break;
case 4: tmplayer.loadImages("qmenu_save_mozi04");break;
case 5: tmplayer.loadImages("qmenu_save_mozi05");break;
case 6: tmplayer.loadImages("qmenu_save_mozi06");break;
case 7: tmplayer.loadImages("qmenu_save_mozi07");break;
case 8: tmplayer.loadImages("qmenu_save_mozi08");break;
case 9: tmplayer.loadImages("qmenu_save_mozi09");break;
}
destlayer.operateRect(774, 65+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight,omPsNormal,255);
invalidate tmplayer;
var tmplayer = new Layer(this, destlayer);
switch (num_1) {//1の位
case 0: tmplayer.loadImages("qmenu_save_mozi00");break;
case 1: tmplayer.loadImages("qmenu_save_mozi01");break;
case 2: tmplayer.loadImages("qmenu_save_mozi02");break;
case 3: tmplayer.loadImages("qmenu_save_mozi03");break;
case 4: tmplayer.loadImages("qmenu_save_mozi04");break;
case 5: tmplayer.loadImages("qmenu_save_mozi05");break;
case 6: tmplayer.loadImages("qmenu_save_mozi06");break;
case 7: tmplayer.loadImages("qmenu_save_mozi07");break;
case 8: tmplayer.loadImages("qmenu_save_mozi08");break;
case 9: tmplayer.loadImages("qmenu_save_mozi09");break;
}
destlayer.operateRect(781, 65+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight,omPsNormal,255);
invalidate tmplayer;
}
//最新の場合、「最新」を追加
if (num == sf.new_savedata+1)
{
var tmplayer = new Layer(this, destlayer);
tmplayer.loadImages("qmenu_save_mozi_new");
destlayer.operateRect(767, 50+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight,omPsNormal,255);
invalidate tmplayer;
}
}
//----------エントリ内容をフォントで表示する場合----------
/*
//番号色
var txtcolor = 0x000000;
var autotxtcolor = 0x550000;
var shadowcolor = 0xffffff;
if (sf.saveload_page == 10)//オートセーブの場合
{
var str;
//ベース画像読み込み
var tmplayer = new Layer(this, destlayer);
tmplayer.loadImages("qmenu_save_entry02");
destlayer.copyRect(767,44+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight);
invalidate tmplayer;
if (entry == 9)
str = entry+1;
else
str = " " + (entry+1);
destlayer.drawText(770, 55+48*entry, "自動", autotxtcolor,255,kag.chDefaultAntialiased,64,shadowcolor,0,1,1);
destlayer.drawText(777, 67+48*entry, str, autotxtcolor,255,kag.chDefaultAntialiased,64,shadowcolor,0,1,1);
}
else//通常セーブの場合
{
//表示位置調整
var num = i+1+sf.saveload_page*10;
//ベース画像読み込み
var tmplayer = new Layer(this, destlayer);
tmplayer.loadImages("qmenu_save_entry01");
destlayer.copyRect(767,44+48*entry, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight);
invalidate tmplayer;
if (num < 10)
destlayer.drawText(780, 57+48*entry, num, txtcolor,255,kag.chDefaultAntialiased,64,shadowcolor,0,1,1);
else if (num == 100)
destlayer.drawText(773, 57+48*entry, num, txtcolor,255,kag.chDefaultAntialiased,64,shadowcolor,0,1,1);
else
destlayer.drawText(776, 57+48*entry, num, txtcolor,255,kag.chDefaultAntialiased,64,shadowcolor,0,1,1);
//最新の場合、「最新」を追加
if (num == sf.new_savedata+1)
destlayer.drawText(770, 72+48*entry, "最新", 0xff0000,255,kag.chDefaultAntialiased,64,shadowcolor,0,1,1);
}
*/
}
}
//下ボタン
if (qmenuSave_DownButton === void)
{
var qsaveElm = %[];
qsaveElm.graphic = "qmenu_save_changepage02";
qsaveElm.hint = '';
qsaveElm.visible = true;
if (sf.saveload_page == 10)
qsaveElm.onenter = "kag.makeQMenuHint(573,526,' 最初のページに戻ります。','qmenu_hint_base02')";
else
qsaveElm.onenter = "kag.makeQMenuHint(573,526,' 次のページに移動します。','qmenu_hint_base02')";
qsaveElm.onleave = "kag.hideQMenuHint()";
qsaveElm.exp = "kag.downPageQSaveMenuClick()";
qsaveElm.rc = "";
//qsaveElm.enterse ='';
//qsaveElm.entersebuf = '';
//qsaveElm.leavese = '';
//qsaveElm.leavesebuf = '';
qmenuSave_DownButton = new LinkButtonLayer(this, destlayer);
qmenuSave_DownButton.showFocusImage = false;
destlayer.locate(767,524);
destlayer.addButton(qsaveElm);
}
}
//簡易メニュー(右:セーブ)を消去
function deleteQSaveMenu()
{
if(qmenuSave_UpButton !== void)
{
invalidate qmenuSave_UpButton;
qmenuSave_UpButton = void;
}
if(qmenuSave_DownButton !== void)
{
invalidate qmenuSave_DownButton;
qmenuSave_DownButton = void;
}
for (var i=0;i<10;i++)
{
if(qmenuSave_EntryButton[i] !== void)
{
invalidate qmenuSave_EntryButton[i];
qmenuSave_EntryButton[i] = void;
}
}
fore.messages[qmenuLayer_Save].clear();
}
//簡易メニュー(右:セーブ)を表示
function showQSaveMenu(mvmode,backmode)
{
var destlayer;
if(skipMode && sf.qmenu_save_mode != 2) return;
if (backmode == "back")
destlayer = back.messages[qmenuLayer_Save];
else
destlayer = fore.messages[qmenuLayer_Save];
if (!movemode_qsave)
{
if (destlayer.visible == false)
makeQSaveMenu();
if (mvmode == "scroll") //"scroll"なら移動表示
{
destlayer.visible = true;
movemode_qsave = 1;
timermove_qsave.enabled = true;
}
else //"nowait"なら瞬間表示
{
destlayer.visible = true;
destlayer.left = 0;
}
}
}
//簡易メニュー(右:セーブ)を再描画
function redrawQSaveMenu()
{
deleteQSaveMenu();
fore.messages[qmenuLayer_Save].clear();
makeQSaveMenu();
}
//簡易メニュー(右:セーブ)を隠す
function hideQSaveMenu(mvmode)
{
if (!movemode_qsave)
{
if (mvmode == "scroll") //"scroll"なら移動表示
{
if (f.qmenu_enabled && fore.messages[qmenuLayer_Save].visible == true)
//fore.messages[qmenuLayer_Save].visible = true;
movemode_qsave = 2;
timermove_qsave.enabled = true;
}
else //"nowait"なら瞬間表示
{
fore.messages[qmenuLayer_Save].visible = false;
fore.messages[qmenuLayer_Save].left = sf.qmenu_save_movepixel;
deleteQSaveMenu();
}
}
}
//簡易メニュー(右:セーブ)の表示用タイマ
function onTimerMoveQSave() {
var movespeed = sf.qmenu_save_movespeed;//動く速度
switch (movemode_qsave) {
case 0://ストップ
timermove_qsave.enabled = false;
break;
case 1://表示
if (fore.messages[qmenuLayer_Save].left > 0) {
fore.messages[qmenuLayer_Save].left -= movespeed;
} else {
timermove_qsave.enabled = false;
movemode_qsave = 0;
}
break;
case 2://消去
if (fore.messages[qmenuLayer_Save].left < sf.qmenu_save_movepixel) {
fore.messages[qmenuLayer_Save].left += movespeed;
} else {
timermove_qsave.enabled = false;
movemode_qsave = 0;
fore.messages[qmenuLayer_Save].visible = false;
deleteQSaveMenu();
}
break;
}
}
//簡易メニュー(右:セーブ)のツールチップヒント作成
function makeQSaveMenuHint(num,x,y)
{
if(skipMode) return;
var txtcolor = 0x000000;//文字色
var text = kag.getBookMarkPageName(num);
kag.fore.messages[qmenuLayer_Hint].face = dfBoth;
//ベース画像の読み込み
var tmplayer = new Layer(this, kag.fore.messages[qmenuLayer_Hint]);
tmplayer.loadImages("qmenu_save_hint_base");
kag.fore.messages[qmenuLayer_Hint].copyRect(0, 0, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight);
invalidate tmplayer;
// サムネイル画像の読み込み
var tmplayer = new Layer(this, kag.fore.messages[qmenuLayer_Hint]);
var tnname = kag.getBookMarkFileNameAtNum(num);
if(Storages.isExistentStorage(tnname) && kag.bookMarkDates[num] != '')
{
tmplayer.loadImages(tnname);
}
else
{
// サムネイル画像が手動で削除されたときの対処
if(kag.bookMarkDates[num] != '')
{
kag.bookMarkNames[num] = ''; // 栞名
kag.bookMarkDates[num] = ''; // 保存年月日
if(kag.scflags.bookMarkComments !== void)
kag.scflags.bookMarkComments[num] = ''; // コメント
kag.setBookMarkMenuCaptions();
}
tmplayer.loadImages("dummy"); // No Data
}
kag.fore.messages[qmenuLayer_Hint].copyRect(3, 3, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight);
invalidate tmplayer;
kag.fore.messages[qmenuLayer_Hint].left = x;
kag.fore.messages[qmenuLayer_Hint].top = y;
kag.fore.messages[qmenuLayer_Hint].setSize(344, 94);
kag.fore.messages[qmenuLayer_Hint].font.height = 12;
// 番号を表示
var str = string (num + 1);
var ty = kag.fore.messages[qmenuLayer_Hint].font.getTextHeight(str);
if (sf.saveload_page == 10)//オートセーブの場合
{
var entry = sf.autosave_entry - num + 100;
if (entry <= 0) entry+=10;
if (entry == 10)
str = "自動" + string (entry); //自動セーブデータは「自動」と表示する
else
str = "自動 " + string (entry); //位置調整のため半角スペースを追加
kag.fore.messages[qmenuLayer_Hint].drawText(301, 4, str, txtcolor);
}
else
kag.fore.messages[qmenuLayer_Hint].drawText(322, 4, str, txtcolor);
// 栞の保存名を表示
var str = kag.bookMarkNames[num];
if(str == '') str = '情報なし';
if (sf.saveload_page == 10) str = "(自動)"+ str; //オートセーブデータは「自動」を追加する
//シナリオ中の日付表示をする場合、ラベルに':'(半角コロン)の区切り記号をつけたら、
//その区切り記号より前を「シナリオ中の日付」、それより後を「サブタイトル」と設定することができます。
//セーブ・ロード画面でもこの機能を使う場合、
//saveload.ksのfunction SaveDataItemLayer(window, parent, num)内部も同様に変更して下さい。
//var sepnum = str.indexOf(":");//詳細情報
//シナリオ中の日付表示
/*
if (sepnum != -1)
{
kag.fore.messages[6].drawText(129, 25, str.substring(0,sepnum), txtcolor);
str = str.substring(sepnum+1);
}
*/
//サブタイトル表示
kag.fore.messages[qmenuLayer_Hint].drawText(143, 45, str, txtcolor);
//上書き禁止の表示
if (kag.bookMarkProtectedStates[num])
{
kag.fore.messages[qmenuLayer_Hint].drawText(217, 5, "(上書き禁止)", 0xff0000);
}
// 日付を表示
if(kag.bookMarkDates[num] == '')
{
str = " ----/--/-- --:--";
}
else
{
if (sf.new_savedata == num)//最新のものは"最新"を追加
kag.fore.messages[qmenuLayer_Hint].drawText(187, 77, "(最新)", 0xff0000);
if (num == ((sf.autosave_entry == 0)? 10:sf.autosave_entry) + 99)//オートセーブの最新のものには(新)を追加
kag.fore.messages[qmenuLayer_Hint].drawText(187, 77, "(新)", 0xff0000);
if (num == (sf.autosave_entry + 100))//オートセーブの最古のものには(古)を追加
kag.fore.messages[qmenuLayer_Hint].drawText(187, 77, "(古)", 0xff0000);
str = kag.bookMarkDates[num];
}
kag.fore.messages[qmenuLayer_Hint].drawText(233, 77, str, txtcolor);
kag.fore.messages[qmenuLayer_Hint].visible = true;
}
//簡易メニューなどのツールチップヒント作成
function makeQMenuHint(x,y,str,imgfile,destlayer)//imgtypeは画像ファイル名
{
if(skipMode) return;
if (sf.qmenu_hint)
{
if (destlayer == '')
destlayer = kag.fore.messages[qmenuLayer_Hint];
destlayer.face = dfBoth;
//ベース画像の読み込み
var tmplayer = new Layer(this, destlayer);
if (imgfile != '')
tmplayer.loadImages(imgfile);
destlayer.copyRect(0, 0, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight);
invalidate tmplayer;
destlayer.left = x;
destlayer.top = y;
destlayer.drawText(8, 6, str, 0x000000);
destlayer.visible = true;
}
}
//簡易メニューのツールチップヒントを隠す
function hideQMenuHint(destlayer)
{
if (destlayer == '')
destlayer = kag.fore.messages[qmenuLayer_Hint];
destlayer.visible = false;
destlayer.clear();
}
//ページUPボタン処理
function upPageQSaveMenuClick()
{
if (checkTextDrawing()) return;//エラー回避
if (sf.saveload_page == 0)
sf.saveload_page = 10;
else
sf.saveload_page--;
kag.redrawQSaveMenu();
//showQSaveMenu(0);
}
//各セーブエントリボタン処理(左クリック(セーブ))
function lEntryQSaveMenuClick(num)
{
if (checkTextDrawing()) return;//エラー回避
if (sf.saveload_page < 10)
kag.saveBookmarkAutoMode(num+sf.saveload_page*10);
}
//各セーブエントリボタン処理(右クリック(ロード))
function rEntryQSaveMenuClick(num)
{
if (checkTextDrawing()) return;//エラー回避
kag.loadBookmarkAutoMode(num+sf.saveload_page*10);
}
//ページDOWNボタン処理
function downPageQSaveMenuClick()
{
if (checkTextDrawing()) return;//エラー回避
if (sf.saveload_page == 10)
sf.saveload_page = 0;
else
sf.saveload_page++;
kag.redrawQSaveMenu();
//showQSaveMenu(0);
}
//----簡易メニュー(左:設定)---------------------------------------------------------
//簡易メニュー(左:設定)を作成
function makeQConfigMenu(backmode)
{
var destlayer;
if (backmode == 'back')
destlayer = back.messages[qmenuLayer_Config];
else
destlayer = fore.messages[qmenuLayer_Config];
destlayer.face = dfBoth;
var tmplayer = new Layer(this, destlayer);
tmplayer.loadImages("qmenu_config_base");
destlayer.copyRect(0, 0, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight);
invalidate tmplayer;
if (fullScreened)//フルスクリーンの場合
{
//全画面ボタン
var tmplayer = new Layer(this, destlayer);
tmplayer.loadImages("qmenu_config01on");
destlayer.copyRect(17, 79, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight);
invalidate tmplayer;
//ウィンドウボタン
if (qmenuConfig_WindowButton === void)
{
var qconfElm = %[];
qconfElm.graphic = "qmenu_config02";
qconfElm.hint = '';
qconfElm.visible = true;
qconfElm.onenter = "kag.makeQMenuHint(110,101,' 窓表示にします。','qmenu_hint_base02')";
qconfElm.onleave = "kag.hideQMenuHint()";
qconfElm.exp = "kag.hideQMenuHint(),kag.windowQConfigMenuClick()";
qconfElm.rc = "";
//qconfElm.enterse ='';
//qconfElm.entersebuf = '';
//qconfElm.leavese = '';
//qconfElm.leavesebuf = '';
qmenuConfig_WindowButton = new LinkButtonLayer(this, destlayer);
qmenuConfig_WindowButton.showFocusImage = false;
destlayer.locate(17,104);
destlayer.addButton(qconfElm);
}
}
else//ウィンドウモードの場合
{
//全画面ボタン
if (qmenuConfig_FullScrButton === void)
{
var qconfElm = %[];
qconfElm.graphic = "qmenu_config01";
qconfElm.hint = '';
qconfElm.visible = true;
qconfElm.onenter = "kag.makeQMenuHint(110,75,' 全画面表示にします。','qmenu_hint_base02')";
qconfElm.onleave = "kag.hideQMenuHint()";
qconfElm.exp = "kag.hideQMenuHint(),kag.fullScrQConfigMenuClick()";
qconfElm.rc = "";
//qconfElm.enterse ='';
//qconfElm.entersebuf = '';
//qconfElm.leavese = '';
//qconfElm.leavesebuf = '';
qmenuConfig_FullScrButton = new LinkButtonLayer(this, destlayer);
qmenuConfig_FullScrButton.showFocusImage = false;
destlayer.locate(17,79);
destlayer.addButton(qconfElm);
}
//ウィンドウボタン
var tmplayer = new Layer(this, destlayer);
tmplayer.loadImages("qmenu_config02on");
destlayer.copyRect(17, 104, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight);
invalidate tmplayer;
}
if (sf.readtxt_fast)//既読文章瞬間表示オンの場合
{
//無ボタン
if (qmenuConfig_2ndReadButton === void)
{
var qconfElm = %[];
qconfElm.graphic = "qmenu_config03on";
qconfElm.hint = '';
qconfElm.visible = true;
qconfElm.onenter = "kag.makeQMenuHint(110,283,' 既読文章を通常速度にします。','qmenu_hint_base02')";
qconfElm.onleave = "kag.hideQMenuHint()";
qconfElm.exp = "kag.hideQMenuHint(),kag.readtxtOffQConfigMenuClick()";
qconfElm.rc = "";
//qconfElm.enterse ='';
//qconfElm.entersebuf = '';
//qconfElm.leavese = '';
//qconfElm.leavesebuf = '';
qmenuConfig_2ndReadButton = new LinkButtonLayer(this, destlayer);
qmenuConfig_2ndReadButton.showFocusImage = false;
destlayer.locate(40,283);
destlayer.addButton(qconfElm);
}
}
else//既読文章瞬間表示オフの場合
{
//有ボタン
if (qmenuConfig_2ndReadButton === void)
{
var qconfElm = %[];
qconfElm.graphic = "qmenu_config03";
qconfElm.hint = '';
qconfElm.visible = true;
qconfElm.onenter = "kag.makeQMenuHint(110,283,' 既読文章を瞬間表示にします。','qmenu_hint_base02')";
qconfElm.onleave = "kag.hideQMenuHint()";
qconfElm.exp = "kag.hideQMenuHint(),kag.readtxtOnQConfigMenuClick()";
qconfElm.rc = "";
//qconfElm.enterse ='';
//qconfElm.entersebuf = '';
//qconfElm.leavese = '';
//qconfElm.leavesebuf = '';
qmenuConfig_2ndReadButton = new LinkButtonLayer(this, destlayer);
qmenuConfig_2ndReadButton.showFocusImage = false;
destlayer.locate(40,283);
destlayer.addButton(qconfElm);
}
}
}
//簡易メニュー(左:設定)を消去
function deleteQConfigMenu()
{
if(qmenuConfig_FullScrButton !== void)
{
invalidate qmenuConfig_FullScrButton;
qmenuConfig_FullScrButton = void;
}
if(qmenuConfig_WindowButton !== void)
{
invalidate qmenuConfig_WindowButton;
qmenuConfig_WindowButton = void;
}
if(qmenuConfig_2ndReadButton !== void)
{
invalidate qmenuConfig_2ndReadButton;
qmenuConfig_2ndReadButton = void;
}
fore.messages[qmenuLayer_Config].clear();
}
//簡易メニュー(左:設定)を表示
function showQConfigMenu(mvmode,backmode)
{
var destlayer;
if(skipMode && sf.qmenu_config_mode != 2) return;
if (backmode == "back")
destlayer = back.messages[qmenuLayer_Config];
else
destlayer = fore.messages[qmenuLayer_Config];
if (!movemode_qconf)
{
if (destlayer.visible == false)
{
makeQConfigMenu();
}
if (mvmode == "scroll") //"scroll"なら移動表示
{
destlayer.visible = true;
movemode_qconf = 1;
timermove_qconf.enabled = true;
}
else //"nowait"なら瞬間表示
{
destlayer.visible = true;
destlayer.left = 0;
//スライダー表示
var sliderelm = %[];
sliderelm.forevisible = true;
sliderelm.page = "qmenu";
slider_object.setOptions(sliderelm);
}
}
}
//簡易メニュー(左:設定)を隠す
function hideQConfigMenu(mvmode)
{
if (!movemode_qconf)
{
//スライダー消去
var sliderelm = %[];
sliderelm.forevisible = false;
//sliderelm.backvisible = false;
slider_object.setOptions(sliderelm);
if (mvmode == "scroll") //"scroll"なら移動表示
{
if (f.qmenu_enabled && fore.messages[qmenuLayer_Config].visible == true)
//fore.messages[qmenuLayer_Config].visible = false;
movemode_qconf = 2;
timermove_qconf.enabled = true;
}
else //"nowait"なら瞬間表示
{
fore.messages[qmenuLayer_Config].visible = false;
fore.messages[qmenuLayer_Config].left = -sf.qmenu_config_movepixel;
deleteQConfigMenu();
}
}
}
//簡易メニュー(左:設定)の表示用タイマ
function onTimerMoveQConfig() {
var movespeed = sf.qmenu_config_movespeed;//動く速度
switch (movemode_qconf) {
case 0://ストップ
timermove_qconf.enabled = false;
break;
case 1://表示
if (fore.messages[qmenuLayer_Config].left < 0) {
fore.messages[qmenuLayer_Config].left += movespeed;
} else {
//スライダー表示
var sliderelm = %[];
sliderelm.forevisible = true;
sliderelm.page = "qmenu";
slider_object.setOptions(sliderelm);
timermove_qconf.enabled = false;
movemode_qconf = 0;
}
break;
case 2://消去
if (fore.messages[qmenuLayer_Config].left > -sf.qmenu_config_movepixel) {
fore.messages[qmenuLayer_Config].left -= movespeed;
} else {
timermove_qconf.enabled = false;
movemode_qconf = 0;
fore.messages[qmenuLayer_Config].visible = false;
deleteQConfigMenu();
}
break;
}
}
function fullScrQConfigMenuClick()//フルスクリーンボタン処理
{
if (checkTextDrawing()) return;//エラー回避
kag.hideQMenuHint();
kag.onFullScreenMenuItemClick();
deleteQConfigMenu();
makeQConfigMenu();
}
function windowQConfigMenuClick()//窓モードボタン処理
{
if (checkTextDrawing()) return;//エラー回避
kag.hideQMenuHint();
kag.onWindowedMenuItemClick();
deleteQConfigMenu();
makeQConfigMenu();
}
function readtxtOnQConfigMenuClick()//既読瞬間表示オンボタン処理
{
if (checkTextDrawing()) return;//エラー回避
sf.readtxt_fast = true;
kag.userCh2ndSpeed = 0;
setUserSpeed();
deleteQConfigMenu();
makeQConfigMenu();
}
function readtxtOffQConfigMenuClick()//既読瞬間表示オフボタン処理
{
if (checkTextDrawing()) return;//エラー回避
sf.readtxt_fast = false;
kag.userCh2ndSpeed = -1;
setUserSpeed();
deleteQConfigMenu();
makeQConfigMenu();
}
//----簡易メニュー(上:システム)---------------------------------------------------------
//簡易メニュー(上:システム)を作成
function makeQSystemMenu(backmode)
{
var destlayer;
if (backmode == 'back')
destlayer = back.messages[qmenuLayer_System];
else
destlayer = fore.messages[qmenuLayer_System];
destlayer.face = dfBoth;
//最初に戻るボタン
if (qmenuSystem_ReturnTopButton === void)
{
var qsysElm = %[];
qsysElm.graphic = "qmenu_sys_menu01";
qsysElm.hint = '';
qsysElm.visible = true;
qsysElm.onenter = "kag.makeQMenuHint(500,32,' 最初に戻ります。','qmenu_hint_base02')";
qsysElm.onleave = "kag.hideQMenuHint()";
qsysElm.exp = "kag.hideQMenuHint(),kag.returnTopQSystemMenuClick()";
qsysElm.rc = "";
//qsysElm.enterse ='';
//qsysElm.entersebuf = '';
//qsysElm.leavese = '';
//qsysElm.leavesebuf = '';
qmenuSystem_ReturnTopButton = new LinkButtonLayer(this, destlayer);
qmenuSystem_ReturnTopButton.showFocusImage = false;
destlayer.locate(590,0);
destlayer.addButton(qsysElm);
}
//終了ボタン
if (qmenuSystem_ExitButton === void)
{
var qsysElm = %[];
qsysElm.graphic = "qmenu_sys_menu02";
qsysElm.hint = '';
qsysElm.visible = true;
qsysElm.onenter = "kag.makeQMenuHint(550,32,' 終了します。','qmenu_hint_base02')";
qsysElm.onleave = "kag.hideQMenuHint()";
qsysElm.exp = "kag.hideQMenuHint(),kag.exitQSystemMenuClick()";
qsysElm.rc = "";
//qsysElm.enterse ='';
//qsysElm.entersebuf = '';
//qsysElm.leavese = '';
//qsysElm.leavesebuf = '';
qmenuSystem_ExitButton = new LinkButtonLayer(this, destlayer);
qmenuSystem_ExitButton.showFocusImage = false;
destlayer.locate(640,0);
destlayer.addButton(qsysElm);
}
}
//簡易メニュー(上:システム)を表示
function showQSystemMenu(mvmode,backmode)
{
var destlayer;
if(skipMode && sf.qmenu_system_mode != 2) return;
if (backmode == "back")
destlayer = back.messages[qmenuLayer_System];
else
destlayer = fore.messages[qmenuLayer_System];
if (!movemode_qsys)
{
if (destlayer.visible == false)
makeQSystemMenu();
if (mvmode == "scroll") //"scroll"なら移動表示
{
destlayer.visible = true;
movemode_qsys = 1;
timermove_qsys.enabled = true;
}
else //"nowait"なら瞬間表示
{
destlayer.visible = true;
destlayer.top = 0;
}
}
}
//簡易メニュー(上:システム)の消去
function hideQSystemMenu(mvmode)
{
if (!movemode_qsys)
{
if (mvmode == "scroll") //"scroll"なら移動表示
{
if (f.qmenu_enabled && fore.messages[qmenuLayer_System].visible == true)
//kag.fore.messages[qmenuLayer_System].visible = true;
movemode_qsys = 2;
timermove_qsys.enabled = true;
}
else //"nowait"なら瞬間表示
{
fore.messages[qmenuLayer_System].top = -sf.qmenu_system_movepixel;
fore.messages[qmenuLayer_System].visible = false;
deleteQSystemMenu();
}
}
}
//簡易メニュー(上:システム)を消去
function deleteQSystemMenu()
{
if(qmenuSystem_ReturnTopButton !== void)
{
invalidate qmenuSystem_ReturnTopButton;
qmenuSystem_ReturnTopButton = void;
}
if(qmenuSystem_ExitButton !== void)
{
invalidate qmenuSystem_ExitButton;
qmenuSystem_ExitButton = void;
}
fore.messages[qmenuLayer_System].clear();
}
//簡易メニュー(上:システム)の表示用タイマ
function onTimerMoveQSystem() {
var movespeed = sf.qmenu_system_movespeed;//動く速度
switch (movemode_qsys) {
case 0://ストップ
timermove_qsys.enabled = false;
break;
case 1://表示
if (fore.messages[qmenuLayer_System].top < 0) {
fore.messages[qmenuLayer_System].top += movespeed;
} else {
timermove_qsys.enabled = false;
movemode_qsys = 0;
}
break;
case 2://消去
if (fore.messages[qmenuLayer_System].top > -sf.qmenu_system_movepixel) {
fore.messages[qmenuLayer_System].top -= movespeed;
} else {
timermove_qsys.enabled = false;
movemode_qsys = 0;
kag.fore.messages[qmenuLayer_System].visible = false;
deleteQSystemMenu();
}
break;
}
}
function returnTopQSystemMenuClick()//最初に戻るボタン処理
{
if (checkTextDrawing()) return;//エラー回避
kag.hideQMenuHint();
kag.callExtraConductor('qmenu.ks','*returntop');
}
function exitQSystemMenuClick()//終了ボタン処理
{
if (checkTextDrawing()) return;//エラー回避
kag.hideQMenuHint();
kag.callExtraConductor('qmenu.ks','*exit_game');
}
//----オートセーブ用---------------------------------------------------------
//オートセーブ対応セーブコマンド
function saveBookmarkAutoMode(num)
{
var result;
if(sf.save_ask)//確認をする場合
if (sf.saveload_page == 10)
{
var autonum = (sf.autosave_entry-num+100);
if (autonum<=0) autonum+=10;
result = kag.saveBookMarkWithAsk(num,true,"自動"+autonum);
}
else
result = kag.saveBookMarkWithAsk(num);
else//確認をしない場合
if (sf.saveload_page == 10)
result = kag.saveBookMark(num,true,"自動"+(sf.autosave_entry-num));
else
result = kag.saveBookMark(num);
if (result)
{
sf.new_savedata = num;
if(sf.qmenu_save_mode) kag.redrawQSaveMenu();
}
return result;
}
//オートセーブ対応ロードコマンド
function loadBookmarkAutoMode(num)
{
var result;
if(sf.save_ask)//確認をする場合
if (sf.saveload_page == 10)
{
var autonum = (sf.autosave_entry-num+100);
if (autonum<=0) autonum+=10;
result = kag.loadBookMarkWithAsk(num,true,"自動"+autonum);
}
else
result = kag.loadBookMarkWithAsk(num);
else//確認をしない場合
if (sf.saveload_page == 10)
result = kag.loadBookMark(num,true,"自動"+(sf.autosave_entry-num));
else
result = kag.loadBookMark(num);
return result;
}
//↑--------------------------------------- ここまで追加部分--
}
// TJS スクリプトはここで終わり
MessageLayer.tjs
右クリック対応のボタンを定義しておきます。
MessageLayer.tjsのclass LinkButtonLayerにあるfunction onMouseUp()の中に、以下の命令を追加します。
function onMouseUp(x, y, button, shift)
{
if(enabled && button == mbLeft && !parent.selProcessLock) parent.onButtonClick(linkNum);
//↓--------------------------------------- ここから追加部分--
else if(enabled && button == mbRight && !parent.selProcessLock) parent.onRightButtonClick(linkNum); // mebius:右クリック
//↑--------------------------------------- ここまで追加部分--
if(this isvalid) super.onMouseUp(...);
}
function addButton(elm)の中に、以下の命令を追加します。
一行ほど修正する行があるので、注意して下さい。
function addButton(elm)
{
// グラフィカルボタンを配置
// (中略)
links[numLinks] = %[
type : ltButton,
graphic : elm.graphic,
graphickey : elm.graphickey,
storage : elm.storage,
target : elm.target,
exp : createSoundExpression(elm.exp, elm.clickse, elm.clicksebuf),
countPage : (elm.countpage === void) ? true : +elm.countpage,
object : object,
onenter : object.onenter,
onleave : object.onleave,
x : [x],
y : [y],
w : [object.width],
h : [object.height],
fixed : [true],
//↓--------------------------------------- ここから追加部分--
lineCount : 1, // mebius:最後にコンマを追加
rc: elm.rc // mebius:右クリック用の処理
//↑--------------------------------------- ここまで追加部分--
];
numLinks++;
focusable = true; // フォーカスを受け取れるように
setSelProcessLock(false); // 選択ロック解除
}
右クリック対応のプロセスを定義します。
function processLink(n)の後に、以下の命令を追加します。
function processLink(n)
{
// リンク番号 n を処理する
var ln = links[n];
if(ln === void) return;
// 裏画面のハイライトを非表示
if(comp !== void) comp.highlightLayer.visible = false;
// 実行
Scripts.eval(ln.exp) if ln.exp != '';
if(ln.storage != '' || ln.target != '')
{
window.lockMessageLayerSelProcess(); // 選択をロック
if(window.getKeyState(VK_RETURN) || window.getKeyState(VK_SPACE))
window.hideMouseCursor();
// キーボードによる操作の場合はマウスカーソルを隠す
window.process(ln.storage, ln.target, ln.countPage);
}
}
//↓--------------------------------------- ここから追加部分--
//mebius:右クリック処理用
function processLink2(n)
{
// リンク番号 n を処理する
var ln = links[n];
if(ln === void) return;
// 裏画面のハイライトを非表示 ※これ必要ないかも...?
if(comp !== void) comp.highlightLayer.visible = false;
// 右クリック用の処理を実行
Scripts.eval(ln.rc) if ln.rc != '';
}
function onRightButtonClick(num)
{
// 番号 num のグラフィカルボタンが右クリックされた
processLink2(num);
}
//↑--------------------------------------- ここまで追加部分--
キーボード対応させます。左クリックはリターンで、右クリックはスペースに割り当てています。
もし他のキーに割り当てたい場合、key == VK_SPACEの部分を他のキーに修正して下さい。
function onKeyDown(key, shift)の中に、以下の命令を追加します。
function onKeyDown(key, shift)
{
// キーが押された
if(window.preProcessKeys(key, shift)) return;
if(!focusable || !numLinks) { return super.onKeyDown(...); }
// (中略)
else if(!selProcessLock && (key == VK_DOWN || key == r || (key == VK_TAB && !(shift & ssShift))))
{
selClickLock = false;
if(keyLink == -1 || keyLink == numLinks -1)
{
var l = focusNext();
if(l !== null) return;
keyLink = 0;
}
else
{
keyLink ++;
}
var obj = setFocusToLink(keyLink);
if(obj !== void) obj.focus();
}
else if(key == VK_SPACE || key == VK_RETURN)
{
if(selProcessLock || keyLink == -1)
window.checkProceedingKey(key, shift);
//↓--------------------------------------- ここから追加部分--
else//mebius:右クリックはスペースで代用。
{
if (key == VK_SPACE && links[keyLink].rc !='')
processLink2(keyLink);
else
processLink(keyLink);
}
//else//ここは消去
// processLink(keyLink);
//↑--------------------------------------- ここまで追加部分--
}
else
{
window.processKeys(key, shift); // window に処理をまかせる
}
}
スライダー関係
簡易メニュー(左:設定)ではスライダーを用いています。
スライダーを用いずにボタンのみで実現する場合、この項目は不要です。
ここでは、らんかさん作の「スライダー機能実装プラグイン(+画像タブ+トランジション対応)」を利用しています。もし別のスライダープラグインを使っている場合、スライダーの該当箇所を修正して下さい。
どの場所を修正したのかは、DF辺りの比較用ツールを使って比較すると分かりやすいかと思います。
実際は、サンプル内にあるSliderLayer2.tjsとslider.ksのファイルをご利用下さい。
元のファイルとの差分は、ファイル中にコメントで記されています。必要であれば文字列"mebius"で検索して、該当修正箇所をチェックして下さい。
SliderLayer2.tjsの内容は以下の通りです。
/* ---
スライダーレイヤ
SliderLayer.tjs 改造 : つまみに画像を使うバージョン
※つまみに読み込む画像の縦幅はスライダーの縦幅に合わせてください。
横幅については 10 がデフォルトです。
変更したい場合は Slider_tabWidth の数値を書き換えてください。
画像は以下の二種類が要ります(グラフィカルボタンではないので注意)
tab1 ... 通常の状態
tab2 ... スライダーにマウスカーソルが乗っている状態
画像ファイル名を変更したいときは setSliderTab メソッド内の該当
部分を書き換えればOKです。
2004/04/27 - Ranka
--- */
/* --
2008/07/07 CircleMebius修正版
-- */
class SliderLayer extends Layer
{
var Slider_min = 0; // 最小値
var Slider_max = 0; // 最大値
var Slider_position = 0; // 位置
var Slider_tabWidth = 10; // つまみサイズ
var Slider_dragging = false; // ドラッグ中かどうか
var Slider_dragOriginX; // ドラッグ開始 X 位置
var Slider_mouseOn = false; // マウスが領域内にあるかどうか
var SliderTab; // つまみ用オブジェクト
function SliderLayer(win, par)
{
super.Layer(win, par);
focusable = true; // フォーカスを得られる
hitType = htMask;
hitThreshold = 0;
}
function finalize()
{
invalidate SliderTab if SliderTab !== void;
super.finalize(...);
}
function assign(src)
{
// src の情報をこのオブジェクトにコピー
Slider_min = src.Slider_min;
Slider_max = src.Slider_max;
Slider_position = src.Slider_position;
}
function setSliderTab(x)
{
// スライダーレイヤ上にマウスカーソルがあるかないかで
// 表示させる画像を変える
//mebius:タブのファイル名修正
if(Slider_mouseOn || focused)//←この行も修正しています。
SliderTab.loadImages('slider_tab02'); // カーソルが乗っている
else
SliderTab.loadImages('slider_tab01'); // カーソルが乗っていない
// 画像の表示位置をセット
SliderTab.left = x;
}
function onPaint()
{
// onPaint イベント
// レイヤの内容を描画する
super.onPaint(...);
// よく使う値をローカル変数に用意する
var imw = imageWidth, imh = imageHeight;
var tabw = Slider_tabWidth;
var htabw = tabw >> 1;
// 下敷きを塗る
fillRect(0, 0, imw, imh, 0);
//mebius:コメントアウト
/*
if(focused)
{
// フォーカスがあるのでハイライトする
colorRect(0, 0, width-1, 1, clHighlight, 128);
colorRect(0, 1, 1, height-2, clHighlight, 128);
colorRect(0, height-1, width, 1, clHighlight, 128);
colorRect(width-1, 0, 1, height-1, clHighlight, 128);
}
// 中央のへこみ線
{
var himh = imh >> 1;
var right = imw - tabw;
fillRect(htabw, himh - 1, right, 1, 0x80000000);
fillRect(htabw, himh , right, 1, 0x80ffffff);
}
*/
// タブ
var pos_x = int(
(Slider_position-Slider_min) * (imw - tabw - 2) /
(Slider_max - Slider_min)) + htabw + 1;
var x_htabw = pos_x - htabw + this.left;
if(SliderTab === void)
{
SliderTab = new global.Layer(window, parent);
SliderTab.face = dfBoth;
// 表示する画像と位置をセット
setSliderTab(x_htabw);
SliderTab.top = this.top;
SliderTab.setSizeToImageSize();
SliderTab.hitType = htMask;
SliderTab.hitThreshold = 256; // 全域透過
SliderTab.visible = true; // 表示
}
else
{
// 表示する画像と位置をセット
setSliderTab(x_htabw);
}
}
function onKeyDown(key, shift, process)
{
// キーが押された
if(process)
{
if(key == VK_LEFT)
{
// 左
if(shift & ssAlt)
position = Slider_position - 1;
else
position = Slider_position - int((Slider_max - Slider_min)/ (Slider_tabWidth-2) / 2);
super.onKeyDown(key, shift, false); // 処理をしたのでprocessにfalseを渡す
}
else if(key == VK_RIGHT)
{
// 左
if(shift & ssAlt)
position = Slider_position + 1;
else
position = Slider_position + int((Slider_max - Slider_min)/ (Slider_tabWidth-2) / 2);
super.onKeyDown(key, shift, false); // 処理をしたのでprocessにfalseを渡す
}
else
{
super.onKeyDown(...);
}
}
else
{
// process が false の場合は処理は行わない
super.onKeyDown(...);
}
}
function onMouseDown(x, y, button)
{
// マウスボタンが押された
focus();
super.onMouseDown(...);
var tabw = Slider_tabWidth;
var htabw = tabw >> 1;
var pos_x = int(
(Slider_position-Slider_min) * (imageWidth - tabw - 2)/(Slider_max - Slider_min)) +
htabw + 1;
if(pos_x - htabw > x)
{
// タブより左
position = Slider_position - int((Slider_max - Slider_min)/ (tabw-2));
}
else if(pos_x + htabw < x)
{
// タブより右
position = Slider_position + int((Slider_max - Slider_min)/ (tabw-2));
}
else
{
// タブ
// タブのドラッグを開始
Slider_dragging = true;
Slider_dragOriginX = x - pos_x;
}
}
function onMouseUp(x, y, button)
{
// マウスボタンが離された
super.onMouseUp(...);
Slider_dragging = false;
}
function onMouseMove(x, y)
{
// マウスが移動した
super.onMouseMove(...);
if(Slider_dragging)
{
// タブをドラッグ
position = int(
(x - Slider_dragOriginX - (Slider_tabWidth >> 1)) * (Slider_max - Slider_min) /
(imageWidth - Slider_tabWidth - 2) + Slider_min);
}
}
function onMouseEnter()
{
// マウスがレイヤ領域内に入った
update();
Slider_mouseOn = true;
super.onMouseEnter(...);
}
function onMouseLeave()
{
// マウスがレイヤ領域から出ていった
update();
Slider_mouseOn = false;
Slider_dragging = false;
super.onMouseLeave(...);
}
function onFocus()
{
// フォーカスを得た
super.onFocus(...);
update();
}
function onBlur()
{
// フォーカスを失った
super.onBlur(...);
update();
}
function onNodeDisabled()
{
// レイヤのノードが不可になった
super.onNodeDisabled(...);
update();
}
function onNodeEnabled()
{
// レイヤのノードが有効になった
super.onNodeEnabled(...);
update();
}
property width
{
setter(x)
{
super.width = x;
imageWidth = x;
update();
}
getter
{
return super.width;
}
}
property height
{
setter(x)
{
super.height = x;
imageHeight = x;
update();
}
getter
{
return super.height;
}
}
property max
{
setter(x)
{
Slider_max = x;
update();
}
getter
{
return Slider_max;
}
}
property min
{
setter(x)
{
Slider_min = x;
update();
}
getter
{
return Slider_min;
}
}
property position
{
setter(x)
{
if(x < Slider_min) x = Slider_min;
if(x > Slider_max) x = Slider_max;
Slider_position = x;
update();
onChange(Slider_position);
}
getter
{
return Slider_position;
}
}
function onChange(pos)
{
// onChange
window.action(%[target:this, type:'onChange', position:pos]);
}
}
slider.ksの内容は以下の通りです。
@if exp="typeof(global.slider_object) == 'undefined'"
@iscript
// スライダー機能実装プラグイン
// transviewer 改造 : TJS 版
// +画像タブ
// +トランジション対応
/* --
2008/07/07 CircleMebius修正版
-- */
Scripts.execStorage("SliderLayer2.tjs");
class LSliderLayer extends SliderLayer
{
function LSliderLayer()
{
super.SliderLayer(...);
}
function finalize()
{
super.finalize(...);
}
function onChange(pos)
{
// 親 ( ContolLayer ) にもイベントを通知
parent.sliderLayerChange(this, pos);
}
}
class ControlLayer extends Layer // コントロールレイヤクラス
{
var Sliders = []; // スライダーオブジェクト
var PosText = []; // 現在値の単位
var owner; // SliderPlugin オブジェクトへの参照
//mebius:修正
var controlLeft = 0; // 左からの表示位置
var controlTop = 0; // 上からの表示位置
var controlWidth = 800; // 表示領域(横幅)
var controlHeight = 600; // 表示領域(縦幅)
var bgColor = 0x00000000; // 背景色 ( 0xAARRGGBB )
var changingByFunction = false;//ユーザーが値を直接変更した時にsliderLayerChange()を実行させないためのフラグ。
var changingByLayerChange = false;//描画変更時にsliderLayerChange()を実行させないためのフラグ。
// ▽ドラッグ可能にしたいときはコメントの行を消してね
/* ---
var dragOriginX;
var dragOriginY;
var dragging = false; // ドラッグ中かどうか
--- */
function ControlLayer(win, par, owner)
{
super.Layer(win, par);
this.owner = owner;
// コントロールレイヤを作成
setPos(controlLeft, controlTop);
setImageSize(controlWidth, controlHeight);
setSizeToImageSize();
fillRect(0, 0, width, height, bgColor);
var obj;
// 表示する文字の大きさ
font.height = 14;
//mebius:以下の部分は個別に。
/* --- スライダー設定 0:文字速度(簡易メニュー) --- */
//drawText(15, 50, "", 0xffffff, 255, true);
obj = new LSliderLayer(window, this);
Sliders[0] = obj;
obj.left = 15; // 左からの表示位置
obj.top = 180; // 上からの表示位置
obj.width = 75; // 横幅
obj.height = 20; // 縦幅
obj.min = 0; // 最小値
obj.max = f.txtspeed_max; // 最大値
PosText[0] = "%";
obj.position = f.txtspeed_max-sf.txt_vol;//ポジション
//↑ここのポジションは、kag.userChSpeedからではなく、システム変数で保持している速度の値を代入しています。
// 以下のポジションでも同様。各自環境に合うように修正して下さい。
obj.visible = false;
obj.cursor = crArrow;
/* --- スライダー設定 1:自動的に読み進める速度(簡易メニュー) --- */
//drawText(15, 50, "", 0xffffff, 255, true);
obj = new LSliderLayer(window, this);
Sliders[1] = obj;
obj.left = 15; // 左からの表示位置
obj.top = 359; // 上からの表示位置
obj.width = 75; // 横幅
obj.height = 20; // 縦幅
obj.min = 0; // 最小値
obj.max = f.autoreadspeed_max; // 最大値
PosText[1] = "%";
obj.position = f.autoreadspeed_max - sf.autoread_vol;//ポジション
obj.visible = false;
obj.cursor = crArrow;
/* --- スライダー設定 2:文字速度(設定画面用) --- */
//drawText(15, 50, "", 0xffffff, 255, true);
obj = new LSliderLayer(window, this);
Sliders[2] = obj;
obj.left = 619; // 左からの表示位置
obj.top = 118; // 上からの表示位置
obj.width = 110; // 横幅
obj.height = 20; // 縦幅
obj.min = 0; // 最小値
obj.max = f.txtspeed_max; // 最大値
PosText[2] = "%";
obj.position = f.txtspeed_max-sf.txt_vol;
obj.visible = false;
obj.cursor = crArrow;
/* --- スライダー設定 3:自動的に読み進める速度(設定画面用) --- */
//drawText(15, 50, "", 0xffffff, 255, true);
obj = new LSliderLayer(window, this);
Sliders[3] = obj;
obj.left = 619; // 左からの表示位置
obj.top = 197; // 上からの表示位置
obj.width = 110; // 横幅
obj.height = 20; // 縦幅
obj.min = 0; // 最小値
obj.max = f.autoreadspeed_max; // 最大値
PosText[3] = "%";
obj.position = f.autoreadspeed_max - sf.autoread_vol;
obj.visible = false;
obj.cursor = crArrow;
}
function finalize()
{
super.finalize(...);
}
function sliderLayerChange(obj, pos)
{
// スライダーの位置が変更されたとき
for (var i = 0; i < Sliders.count; i++)
{
if((obj == Sliders[i]) && !changingByFunction)//mebius:ユーザーからの変更時は実行しない
{
// 現在の選択数値と単位をスライダーの右に表示
/*
var lf = obj.left + obj.width;
var wh = width - lf - 5;
var txt = pos + PosText[i];
fillRect(lf, obj.top, wh, obj.height, bgColor);
drawText(lf + 5, obj.top, txt, 0, 255, true);
*/
// 現在の選択数値を一時変数に入れる
tf.SliderPosition[i] = pos;
// スライダー変更時のアクションがあれば呼ぶ
// mebius:設定画面のスライダーはここで処理してOKですが、
// 簡易メニュー部分はここでは処理させない方がいいです。(ファイル名が特定できないので)
if (owner.setting && tf.sliders[i] != '')
kag.callExtraConductor('', tf.sliders[i]);
//mebius:ここでスライダー変更時の処理を記述します。
if (!changingByLayerChange)//レイヤー描画時には処理させない判定
{
switch (i){
case 0: sf.txt_vol = f.txtspeed_max-tf.SliderPosition[i];
kag.userChSpeed=sf.txt_vol;
if (!sf.readtxt_fast) kag.userCh2ndSpeed=-1;//既読文字速度のスライダーを使わない場合
kag.setUserSpeed();
break;
case 1: sf.autoread_vol = f.autoreadspeed_max-tf.SliderPosition[i];
kag.autoModePageWait=sf.autoread_vol+f.autoreadspeed_min;//改ページウェイト
//kag.autoModeLineWait=(sf.autoread_vol+f.autoreadspeed_min)\4;//行クリック待ちウェイト
//autoModeLineWait
//kag.setUserSpeed();
break;
case 2: sf.txt_vol = f.txtspeed_max-tf.SliderPosition[i];
kag.userChSpeed=sf.txt_vol;
if (!sf.readtxt_fast) kag.userCh2ndSpeed=-1;//既読文字速度のスライダーを使わない場合
kag.setUserSpeed();
break;
case 3: sf.autoread_vol = f.autoreadspeed_max-tf.SliderPosition[i];
kag.autoModePageWait=sf.autoread_vol+f.autoreadspeed_min;//改ページウェイト
//kag.autoModeLineWait=(sf.autoread_vol+f.autoreadspeed_min)\4;//行クリック待ちウェイト
//kag.setUserSpeed();
break;
}
}
break;
}
}
}
// ▽ドラッグ可能にしたいときはコメントの行を消してね
/* ---
function onMouseMove(x, y)
{
// マウスが移動した
if(dragging)
{
var px = parent.cursorX;
var py = parent.cursorY;
var l = px - dragOriginX;
var t = py - dragOriginY;
setPos(l, t);
}
if(y < 10)
cursor = crSizeAll;
else
cursor = crDefault;
}
function onMouseDown(x, y, button)
{
// マウスボタンが押された
if(y < 10)
{
dragging = true;
dragOriginX = x;
dragOriginY = y;
}
}
function onMouseUp(x, y, button)
{
// マウスボタンが離された
dragging = false;
}
function onMouseLeave()
{
// マウスがレイヤ領域から出ていった
super.onMouseLeave(...);
}
--- */
//mebius:キーボード処理対応。
function onKeyDown(key, shift)
{
// キーが押された
super.onKeyDown(...);
if(key == VK_ESCAPE)
{
if (f.config_showing)//コンフィグ画面なら
{
// 右クリック
kag.onPrimaryRightClick(); // クリックをエミュレート
return;
}
else //簡易メニューなら
{
kag.processKeys(key, shift);
return;
}
}
}
}
class SliderPlugin extends KAGPlugin // スライダープラグインクラス
{
var window; // ウィンドウへの参照
var foreControlLayer; // 表画面のコントロールレイヤ
var backControlLayer; // 裏画面のコントロールレイヤ
var foreSeen = false; // 表画面のコントロールレイヤが可視か
var backSeen = false; // 裏画面のコントロールレイヤが可視か
var setting = false; // エラー回避(^^;
function SliderPlugin(window)
{
super.KAGPlugin(); // スーパークラスのコンストラクタを呼ぶ
this.window = window; // window への参照を保存する
// コントロールレイヤを作成
if(foreControlLayer === void)
foreControlLayer = new ControlLayer(window, kag.fore.base, this);
if(backControlLayer === void)
backControlLayer = new ControlLayer(window, kag.back.base, this);
setting = true;
// 非表示に
foreControlLayer.visible = foreSeen = false;
backControlLayer.visible = backSeen = false;
}
function finalize()
{
// コントロールレイヤを破棄
invalidate foreControlLayer if foreControlLayer !== void;
invalidate backControlLayer if backControlLayer !== void;
super.finalize(...);
}
function show()
{
// 親を再設定
foreControlLayer.parent = window.fore.base;
backControlLayer.parent = window.back.base;
// コントロールレイヤを表示
foreControlLayer.visible = foreSeen = true;
backControlLayer.visible = backSeen = true;
}
function hide()
{
// コントロールレイヤを閉じる
foreControlLayer.visible = foreSeen = false;
backControlLayer.visible = backSeen = false;
}
function setOptions(elm)
{
//mebius:表示するスライダーの選択
//ページごとに、どのスライダーを表示するのかをここで選択します。
if (elm.page !== void)
{
if (elm.page == "qmenu")//簡易メニューで表示の場合
{
for (var i=0;foreControlLayer.Sliders[i] !== void;i++)
{
if (i==0 || i==1)//表示するスライダー番号(ここを個別に修正します)
{
foreControlLayer.Sliders[i].visible = true;
backControlLayer.Sliders[i].visible = true;
foreControlLayer.Sliders[i].SliderTab.visible = true;
backControlLayer.Sliders[i].SliderTab.visible = true;
}
else
{
foreControlLayer.Sliders[i].visible = false;
backControlLayer.Sliders[i].visible = false;
foreControlLayer.Sliders[i].SliderTab.visible = false;
backControlLayer.Sliders[i].SliderTab.visible = false;
}
}
}
else if (elm.page == "config")//設定画面で表示の場合
{
for (var i=0;foreControlLayer.Sliders[i] !== void;i++)
{
if (i==2 || i==3)//表示するスライダー番号(ここを個別に修正します)
{
foreControlLayer.Sliders[i].visible = true;
backControlLayer.Sliders[i].visible = true;
foreControlLayer.Sliders[i].SliderTab.visible = true;
backControlLayer.Sliders[i].SliderTab.visible = true;
}
else
{
foreControlLayer.Sliders[i].visible = false;
backControlLayer.Sliders[i].visible = false;
foreControlLayer.Sliders[i].SliderTab.visible = false;
backControlLayer.Sliders[i].SliderTab.visible = false;
}
}
}
else//その他の場合
{
for (var i=0;foreControlLayer.Sliders[i] !== void;i++)
{
foreControlLayer.Sliders[i].visible = false;
backControlLayer.Sliders[i].visible = false;
foreControlLayer.Sliders[i].SliderTab.visible = false;
backControlLayer.Sliders[i].SliderTab.visible = false;
}
}
}
// オプションを設定
if(elm.forevisible !== void)
foreControlLayer.visible = foreSeen = +elm.forevisible;
if(elm.backvisible !== void)
backControlLayer.visible = backSeen = +elm.backvisible;
}
//mebius:スライダーの位置設定命令
function setSliderPosition(elm)
{
if(elm.position !== void && elm.target !== void)
{
foreControlLayer.changingByFunction = true;
foreControlLayer.Sliders[int(elm.target)].position=int(elm.position);
foreControlLayer.changingByFunction = false;
backControlLayer.changingByFunction = true;
backControlLayer.Sliders[int(elm.target)].position=int(elm.position);
backControlLayer.changingByFunction = false;
}
}
// KAGPlugin のメソッドのオーバーライド
function onStore(f, elm)
{
// 栞を保存するとき
var dic = f.sliderControl = %[];
dic.foreVisible = foreSeen;
dic.backVisible = backSeen; // 各情報を辞書配列に記録
}
function onRestore(f, clear, elm)
{
// 栞を読み出すとき
var dic = f.sliderControl;
if(dic === void)
{
// sliderControl の情報が栞に保存されていない
foreControlLayer.visible = foreSeen = false;
backControlLayer.visible = backSeen = false;
}
else
{
// sliderControl の情報が栞に保存されている
setOptions(%[
forevisible : dic.foreVisible,
backvisible : dic.backVisible // オプションを設定
]);
}
}
function onStableStateChanged(stable)
{
// 「安定」( s l p の各タグで停止中 ) か、
// 「走行中」 ( それ以外 ) かの状態が変わったときに呼ばれる
// 走行中は無効にする
// mebius: 常時変更可能に問題があれば、以下のコメントアウトを取り払って下さい。
//foreControlLayer.enabled = stable;
//backControlLayer.enabled = stable;
}
function onMessageHiddenStateChanged(hidden)
{
// メッセージレイヤがユーザの操作によって隠されるとき、
// 現れるときに呼ばれる
if(hidden)
{
// メッセージレイヤと一緒に非表示にする
if (foreControlLayer !== void) hide();
}
else
{
// 再表示する
if (foreControlLayer !== void) show();
}
}
function onCopyLayer(toback)
{
// レイヤの表←→裏の情報のコピー
// backlay タグやトランジションの終了時に呼ばれる
if(toback)
{
// 表→裏
var fore = foreControlLayer;
var back = backControlLayer;
back.visible = fore.visible;
backSeen = foreSeen;
setting = false;
for (var i = 0; i < back.Sliders.count; i++)
{
back.changingByLayerChange = true;//mebius:レイヤ変更によるフラグ立て
back.Sliders[i].position = fore.Sliders[i].position;
back.changingByLayerChange = false;
}
setting = true;
}
else
{
// 裏→表
var fore = foreControlLayer;
var back = backControlLayer;
fore.visible = back.visible;
foreSeen = backSeen;
setting = false;
for (var i = 0; i < fore.Sliders.count; i++)
{
fore.changingByLayerChange = true;//mebius:レイヤ変更によるフラグ立て
fore.Sliders[i].position = back.Sliders[i].position;
fore.changingByLayerChange = false;
}
setting = true;
}
}
function onExchangeForeBack()
{
// 裏と表の管理情報を交換
// children = true のトランジションでは、トランジション終了時に
// 表画面と裏画面のレイヤ構造がそっくり入れ替わるので、
// それまで 表画面だと思っていたものが裏画面に、裏画面だと思って
// いたものが表画面になってしまう。ここのタイミングでその情報を
// 入れ替えれば、矛盾は生じないで済む。
var tmp;
tmp = backControlLayer;
backControlLayer = foreControlLayer;
foreControlLayer = tmp;
tmp = backSeen;
backSeen = foreSeen;
foreSeen = tmp;
}
}
tf.SliderPosition = []; // スライダーの位置
kag.addPlugin(global.slider_object = new SliderPlugin(kag));
// プラグインオブジェクトを作成し、登録する
@endscript
@endif
;
; コントロールレイヤの表示切り換えマクロ
;
; @slider forevisible=true で表画面のスライダーを表示
; @slider forevisible=false で表画面のスライダーを非表示
;
; @slider backvisible=true で裏画面のスライダーを表示
; @slider backvisible=false で裏画面のスライダーを非表示
;
;mebius:どのスライダーを表示するかを選ぶ命令
; @slider page="qmenu" で簡易メニュー用スライダーのみ表示
; @slider page="config" でコンフィグ用スライダーのみ表示
;
@macro name="slider"
@eval exp="slider_object.setOptions(mp)"
@endmacro
;mebius:位置調整用命令
@macro name="setSliderPosition"
@eval exp="slider_object.setSliderPosition(mp)"
@endmacro
@return
;------------------
; 2006/01/06 Ranka
;------------------
以下のような命令を入れることで、表示するスライダーを選択することができます。
[slider page="qmenu"]
[slider page="config"]
実際に使用するときは、以下のようなbackvisibleやforevisible属性と一緒に記述して、トランジションで表示させるといいでしょう。
[slider backvisible=true page="config"]
次のような命令で、スライダーの位置(値)を変更できます。(マクロはすぐ下の部分で定義しています)
ここの例では、Slider[2]のスライダーの位置(値)にSlider[0]の位置(tf.SliderPosition[0])を入れています。
[setSliderPosition target=2 position=&tf.SliderPosition[0]]
マクロなどの定義
簡易メニューに関するマクロや変数定義などを行います。
first.ksなどのゲームに入る前に、以下の命令を追加します。
まずはスライダーを用いている場合、スライダープラグインを定義するよりも前の場所に、以下の命令を定義しておきます。
文字速度などの変数名は、もし既に使っているものがあれば、Devasなどのツールを用いて一括置換しておいて下さい。
速度の値や上限値なども適当に修正して下さい。
;----------------------------------------------------------------------------- ;■slider.ksよりも前に定義するもの ;----------------------------------------------------------------------------- ;初期値。設定の初期化をする時にもこの値を使います。 [eval exp="f.init_text = 30"] [eval exp="f.init_readtext = 30"] [eval exp="f.init_autoread = 1000"] @iscript // この定義は、sliderプラグイン定義よりも前に記述しておきます。 // デフォルトのテキスト速度 sf.txt_vol = f.init_text if sf.txt_vol === void; // デフォルトの自動で読む速度用 sf.autoread_vol = f.init_autoread if sf.autoread_vol === void; //文字速度最大値(最も遅い) f.txtspeed_max = 100; //自動で読む速度最大値(最も遅い) f.autoreadspeed_max = 5000; //自動で読む速度最小値(最も速い) f.autoreadspeed_min = 200; @endscript
上記の命令を追加した後に、スライダープラグインを定義します。
[call storage="slider.ks"]
first.ksなどのゲームに入る前に、以下の命令を追加します。
こちらは、スライダープラグインのコールの後に記述します。
オートセーブが不要であれば、該当箇所は消しても大丈夫だと思います。(そのままでも問題ありません)
;-----------------------------------------------------------------------------
;■簡易メニュー関連の定義
;-----------------------------------------------------------------------------
@iscript
//簡易メニュー表示可能/不可能フラグ 0:表示不可、1:表示許可
f.qmenu_enabled = 0;
//レイヤー定義
//sf.qmenu_exec_layer:簡易実行メニュー下(命令用)
//sf.qmenu_save_layer:簡易セーブメニュー右(セーブ用)
//sf.qmenu_config_layer:簡易設定メニュー左(設定用)
//sf.qmenu_system_layer:簡易システムメニュー上(システム用)
//sf.qmenu_hint_layer:ツールチップヒント表示用
sf.qmenu_exec_layer = "message2";
sf.qmenu_save_layer = "message3";
sf.qmenu_config_layer = "message4";
sf.qmenu_system_layer = "message5";
sf.qmenu_hint_layer = "message6";
//上記システム変数を元にレイヤーナンバーを内部で定義します。
//現状ではメッセージレイヤーは0~9まで(1桁のみ)有効。
//2桁を使いたければ、defineQMenuLayers()内部を2桁取得に修正して下さい。
kag.defineQMenuLayers();
//セーブ・ロードのページ数
sf.saveload_page = 0 if sf.saveload_page === void;
//最新の栞番号を記録しておくシステム変数
sf.new_savedata = -1 if sf.new_savedata === void;
//オートセーブ 0:なし 1:あり
sf.autosave = 1 if sf.autosave === void;
//オートセーブ用先頭エントリ 0から9まで
sf.autosave_entry = 0 if sf.autosave_entry === void;
//sf.save_ask 0:セーブ・ロード時に確認メッセージを出さない、1:確認メッセージを出す
sf.save_ask = 1 if sf.save_ask === void;
//sf.readtxt_fast 0:既読文章は瞬間表示しない 1:既読文章は瞬間表示
sf.readtxt_fast = 0 if sf.readtxt_fast === void;
//簡易メニュー(下:命令)の表示設定 0:常に非表示、1:領域に入った時だけ表示、2:常に表示
sf.qmenu_exec_mode = 1 if sf.qmenu_exec_mode === void;
//簡易メニュー(右:セーブ)の表示設定 0:常に非表示、1:領域に入った時だけ表示、2:常に表示
sf.qmenu_save_mode = 1 if sf.qmenu_save_mode === void;
//簡易メニュー(左:設定)の表示設定 0:常に非表示、1:領域に入った時だけ表示、2:常に表示
sf.qmenu_config_mode = 1 if sf.qmenu_config_mode === void;
//簡易メニュー(上:システム)の表示設定 0:常に非表示、1:領域に入った時だけ表示、2:常に表示
sf.qmenu_system_mode = 1 if sf.qmenu_system_mode === void;
//ツールチップヒントの表示設定 0:なし、1:あり
sf.qmenu_hint = 1 if sf.qmenu_hint === void;
//簡易メニューの移動する距離(=表示する高さor幅)の定義(pixel)
sf.qmenu_exec_movepixel = 20;
sf.qmenu_save_movepixel = 35;
sf.qmenu_config_movepixel = 110;
sf.qmenu_system_movepixel = 24;
//簡易メニューの動く速度定義(値が大きければ速くなります)
sf.qmenu_exec_movespeed = 2;
sf.qmenu_save_movespeed = 3;
sf.qmenu_config_movespeed = 10;
sf.qmenu_system_movespeed = 2;
@endscript
; スライダー変更時の処理用ラベルの指定
; スライダーを設置した順に書いてくださいね
; ※設置数とラベル指定の数が合わないと不具合が起きるので、
; 処理なし(設定終了時にフラグを立てる等)のスライダーは
; tf.sliders = [ '*test1', '', '*test3' ];
; というように '' だけを書いておいてください
@eval exp="tf.sliders = [ '', '', '', '']"
; tf.SliderPositionの要素を全て-1で初期化します。
@eval exp="tf.SliderPosition[0] = -1"
@eval exp="tf.SliderPosition[1] = -1"
@eval exp="tf.SliderPosition[2] = -1"
@eval exp="tf.SliderPosition[3] = -1"
[layopt layer=&sf.qmenu_exec_layer visible=false page=fore]
[layopt layer=&sf.qmenu_exec_layer visible=false page=back]
[layopt layer=&sf.qmenu_save_layer visible=false page=fore]
[layopt layer=&sf.qmenu_save_layer visible=false page=back]
[layopt layer=&sf.qmenu_config_layer visible=false page=fore]
[layopt layer=&sf.qmenu_config_layer visible=false page=back]
[layopt layer=&sf.qmenu_system_layer visible=false page=fore]
[layopt layer=&sf.qmenu_system_layer visible=false page=back]
[layopt layer=&sf.qmenu_hint_layer visible=false page=fore]
[layopt layer=&sf.qmenu_hint_layer visible=false page=back]
@position layer=&sf.qmenu_exec_layer frame="" left=0 top=0 width=800 height=600 marginl=0 margint=0 marginr=0 marginb=0 page=fore
@position layer=&sf.qmenu_exec_layer frame="" left=0 top=0 width=800 height=600 marginl=0 margint=0 marginr=0 marginb=0 page=back
@position layer=&sf.qmenu_save_layer frame="" left=0 top=0 width=800 height=600 marginl=0 margint=0 marginr=0 marginb=0 page=fore
@position layer=&sf.qmenu_save_layer frame="" left=0 top=0 width=800 height=600 marginl=0 margint=0 marginr=0 marginb=0 page=back
@position layer=&sf.qmenu_config_layer frame="" left=0 top=0 width=800 height=600 marginl=0 margint=0 marginr=0 marginb=0 page=fore
@position layer=&sf.qmenu_config_layer frame="" left=0 top=0 width=800 height=600 marginl=0 margint=0 marginr=0 marginb=0 page=back
@position layer=&sf.qmenu_system_layer frame="" left=0 top=0 width=800 height=600 marginl=0 margint=0 marginr=0 marginb=0 page=fore
@position layer=&sf.qmenu_system_layer frame="" left=0 top=0 width=800 height=600 marginl=0 margint=0 marginr=0 marginb=0 page=back
@position layer=&sf.qmenu_hint_layer frame="" left=0 top=0 width=800 height=600 marginl=0 margint=0 marginr=0 marginb=0 page=fore
@position layer=&sf.qmenu_hint_layer frame="" left=0 top=0 width=800 height=600 marginl=0 margint=0 marginr=0 marginb=0 page=back
;-----------------------------------------------------------------------------
;■簡易メニュー表示許可 [qmenu enabled=true][qmenu enabled=false]
;-----------------------------------------------------------------------------
[macro name=qmenu]
@if exp="mp.enabled"
[eval exp="f.qmenu_enabled = 1"]
@else
[eval exp="f.qmenu_enabled = 0"]
@endif
[endmacro]
;-----------------------------------------------------------------------------
;■簡易メニューの描画
; マウス移動で表示の場合(fore画面に描画) [init_qmenu page="fore"]
; 常に表示の場合(back画面に描画) [init_qmenu page="back"]
;(注意)「常に表示([init_qmenu])」の場合、back画面に描画します。
;トランジション命令などを用いて、文字枠読み込みなどと同時にこの命令を挿入します。
;[init_qmenu page="fore"]は必ずトランジション完了後に行います。
; 例:以下のような順番
; [backlay][文字枠読み込み命令][init_qmenu page="back"][trans ...][wt][init_qmenu page="fore"]
;-----------------------------------------------------------------------------
[macro name=init_qmenu]
@if exp="mp.page == 'fore'"
@if exp="sf.qmenu_exec_mode ==1"
;■各メニュー
@current layer=&sf.qmenu_exec_layer page=fore
@er
[layopt layer=&sf.qmenu_exec_layer page=fore visible=false left=0 top=&sf.qmenu_exec_movepixel]
@eval exp="kag.deleteQExecMenu()"
@endif
@if exp="sf.qmenu_save_mode == 1"
@current layer=&sf.qmenu_save_layer page=fore
@er
[layopt layer=&sf.qmenu_save_layer page=fore visible=false left=&sf.qmenu_save_movepixel top=0]
@eval exp="kag.deleteQSaveMenu()"
@endif
@if exp="sf.qmenu_config_mode == 1"
@current layer=&sf.qmenu_config_layer page=fore
@er
[layopt layer=&sf.qmenu_config_layer page=fore visible=false left=&"-sf.qmenu_config_movepixel" top=0]
@eval exp="kag.deleteQConfigMenu()"
@endif
@if exp="sf.qmenu_system_mode == 1"
@current layer=&sf.qmenu_system_layer page=fore
@er
[layopt layer=&sf.qmenu_system_layer page=fore visible=false left=0 top=&"-sf.qmenu_system_movepixel"]
@eval exp="kag.deleteQSystemMenu()"
@endif
@endif
@if exp="mp.page == 'back'"
@if exp="sf.qmenu_exec_mode == 2"
;■各メニュー
@current layer=&sf.qmenu_exec_layer page=back
@er
[layopt layer=&sf.qmenu_exec_layer page=back visible=true left=0 top=0]
@eval exp="kag.deleteQExecMenu()"
@eval exp="kag.makeQExecMenu('back')"
@eval exp="kag.showQExecMenu('nowait','back')"
@endif
@if exp="sf.qmenu_save_mode == 2"
@current layer=&sf.qmenu_save_layer page=back left=0 top=0
@er
[layopt layer=&sf.qmenu_save_layer page=back visible=true]
@eval exp="kag.deleteQSaveMenu()"
@eval exp="kag.makeQSaveMenu('back')"
@eval exp="kag.showQSaveMenu('nowait','back')"
@endif
@if exp="sf.qmenu_config_mode == 2"
@current layer=&sf.qmenu_config_layer page=back left=0 top=0
@er
[layopt layer=&sf.qmenu_config_layer page=back visible=true]
@eval exp="kag.deleteQConfigMenu()"
@eval exp="kag.makeQConfigMenu('back')"
@eval exp="kag.showQConfigMenu('nowait','back')"
@slider page="qmenu" forevisible=false backvisible=true
@endif
@if exp="sf.qmenu_system_mode == 2"
@current layer=&sf.qmenu_system_layer page=back left=0 top=0
@er
[layopt layer=&sf.qmenu_system_layer page=back visible=true]
@eval exp="kag.deleteQSystemMenu()"
@eval exp="kag.makeQSystemMenu('back')"
@eval exp="kag.showQSystemMenu('nowait','back')"
@endif
@endif
@current layer=message0
[endmacro]
;-----------------------------------------------------------------------------
;■簡易メニュー消去 [erase_qmenu]
;-----------------------------------------------------------------------------
[macro name=erase_qmenu]
[layopt layer=&sf.qmenu_exec_layer visible=false page=back]
[layopt layer=&sf.qmenu_save_layer visible=false page=back]
[layopt layer=&sf.qmenu_config_layer visible=false page=back]
[layopt layer=&sf.qmenu_system_layer visible=false page=back]
;スライダー表示中ならスライダー消去
@if exp="slider_object.foreControlLayer.Sliders[0].visible"
@slider backvisible=false
@endif
[endmacro]
;-----------------------------------------------------------------------------
;■オートセーブ [autosave]
;-----------------------------------------------------------------------------
[macro name=autosave]
;直前のラベルでオートセーブされます
@if exp="sf.autosave && !kag.autosaveLoadedFlag"
@eval exp="kag.autosaveLoadedFlag = 0"
@save place=&"sf.autosave_entry+100" ask=no
@eval exp="sf.autosave_entry += 1"
@eval exp="sf.autosave_entry -= 10" cond="sf.autosave_entry >= 10"
@endif
[endmacro]
ここで定義したマクロの使い方を以下で説明します。
簡易メニューの表示許可、不許可命令:
[qmenu enabled=false]
[qmenu enabled=true]
簡易メニューの描画命令:
(重要)コメントにあるように、[init_qmenu page="fore"]は必ずトランジション完了後に行います。
なお、現状ではこのinit_qmenu命令を行った後のカレントメッセージの場所は、message0になります。
もし問題がある場合、init_qmenuマクロ内の@current layer=message0の部分を修正しておいて下さい。
[init_qmenu page="fore"]
[init_qmenu page="back"]
簡易メニューの消去命令:
back画面から消去します。
文字枠消去命令の中にこの命令を入れておくといいでしょう。
[erase_qmenu]
オートセーブ命令:
オートセーブを用いる場合、こちらをご利用下さい。
[autosave]
コンフィグ画面での処理
別にコンフィグ画面などでスライダーがある場合、簡易メニューのスライダーの値とコンフィグ画面でのスライダーの値との整合性に注意する必要があります。
コンフィグ画面を開く時と閉じる時の2箇所で、整合性を合わせる命令を追加すればOKです。
コンフィグ画面に入った直後に設置する命令を以下に示します。
スライダーの数ほど増やしたりした場合、個別に値が同一になるように調整して下さい。
こちらも、サンプルのconfig.ksを参考にしていただければと思います。
;簡易メニューとは別にコンフィグ画面などでスライダーがある場合、値の整合性を取ります。 ;表示する前に同期するスライダーの値(変更済みのもののみ)を読み込んで、位置を反映させます。 ;tf.SliderPositionの該当箇所は、変更していたら変更後の数値が、未変更なら-1もしくは未変更の値が入っているようにします。 ;(サンプルのslider.ksはトランジションをした時にもonChangeを呼ぶので、-1が入ってることはまれですが......) ;設定画面のスライダーと簡易メニューのスライダーの値を同じにするために、以下の命令を追加します。 ;(設定画面を開くとき) [qmenu enabled=false] ;値の同期(簡易メニューの値が修正されていたら、コンフィグ画面のスライダーに値を代入) @setSliderPosition target=2 position=&tf.SliderPosition[0] cond="tf.SliderPosition[0]!=-1" @setSliderPosition target=3 position=&tf.SliderPosition[1] cond="tf.SliderPosition[1]!=-1" ;変更されたものを初期化(簡易メニューのスライダーポジションを-1にします) @eval exp="tf.SliderPosition[0] = -1" @eval exp="tf.SliderPosition[1] = -1" ;表示するスライダーの変更 @slider page="config"
コンフィグ画面でスライダーを表示・非表示する命令は、以下のような命令で可能です。
;~~~設定画面の処理をここで記述~~~ ;スライダーを表示する命令は以下の通り。(fore画面に表示する場合はforevisible=trueでOKです) ;@slider backvisible=true ;@slider backvisible=false
コンフィグ画面から出るとき、以下の命令を設置します。
;(設定画面を閉じるとき) [qmenu enabled=true] ;値の同期(コンフィグ画面のスライダーの値が修正されていたら、簡易メニューのスライダーに値を代入) @setSliderPosition target=0 position=&tf.SliderPosition[2] cond="tf.SliderPosition[2]!=-1" @setSliderPosition target=1 position=&tf.SliderPosition[3] cond="tf.SliderPosition[3]!=-1" ;変更されたものを初期化(コンフィグ画面で設定したスライダーポジションを-1にします) @eval exp="tf.SliderPosition[2] = -1" @eval exp="tf.SliderPosition[3] = -1" ;表示するスライダーの変更 @slider page="qmenu"
命令の処理例
実際に、個別のボタンを押したときに実行する命令の処理例を示しておきます。
必要であれば参考にして下さい。サンプルのqmenu.ksと同じものです。
;-----------------------------------------------------------------------------
;■クイックセーブ
;-----------------------------------------------------------------------------
*q_save
@save ask=false place=99
@return
;-----------------------------------------------------------------------------
;■クイックロード
;-----------------------------------------------------------------------------
*q_load
[if exp="sf.save_ask"]
[if exp="askYesNo('クイックセーブした栞を読み込みますか?') "]
@load ask=false place=99
[endif]
[else]
@load ask=false place=99
[endif]
@return
;-----------------------------------------------------------------------------
;■セーブ
;-----------------------------------------------------------------------------
*save
@eval exp="f.rclickmode = 2, kag.callExtraConductor('saveload.ks','*saveload')"
@return
;-----------------------------------------------------------------------------
;■ロード
;-----------------------------------------------------------------------------
*load
@eval exp="f.rclickmode = 1, kag.callExtraConductor('saveload.ks','*saveload')"
@return
;-----------------------------------------------------------------------------
;■設定
;-----------------------------------------------------------------------------
*config
@call storage="config.ks" target="*start-config"
@return
;-----------------------------------------------------------------------------
;■履歴
;-----------------------------------------------------------------------------
*history
@eval exp="kag.showHistory()"
@return
;-----------------------------------------------------------------------------
;■音声再生
;-----------------------------------------------------------------------------
*playvoice
@pv_playpart char=&f.pv_char voice=&f.pv_voice colorchange=false
@return
;-----------------------------------------------------------------------------
;■早送り
;-----------------------------------------------------------------------------
*skipmode
@eval exp="tf.autoMode=1,menuCheckTimer.enabled=true"
@return
;-----------------------------------------------------------------------------
;■自動再生
;-----------------------------------------------------------------------------
*autoplay
@eval exp="tf.autoMode=2,menuCheckTimer.enabled=true"
@return
;-----------------------------------------------------------------------------
;■文字消去
;-----------------------------------------------------------------------------
*erasetextwindow
;文字枠消去命令を追加します。
@eval exp="tf.qmenu_enabled_temp = f.qmenu_enabled"
[qmenu enabled=false]
@layopt layer=message0 page=fore visible=false
@layopt layer=0 page=fore visible=false
@waitclick
@layopt layer=message0 page=fore visible=true
@layopt layer=0 page=fore visible=true
@if exp="tf.qmenu_enabled_temp"
[qmenu enabled=true]
@endif
@return
;-----------------------------------------------------------------------------
;■メニュー表示
;-----------------------------------------------------------------------------
*openmenu
;メニューにジャンプ
;@eval exp="kag.callExtraConductor('rclick.ks','*rclick')"
@eval exp="kag.hideQMenuHint()"
@call storage="rclick.ks" target="*rclick"
@return
;-----------------------------------------------------------------------------
;■最初に戻る(システムメニュー)
;-----------------------------------------------------------------------------
*returntop
@gotostart ask=true
@return
;-----------------------------------------------------------------------------
;■終了(システムメニュー)
;-----------------------------------------------------------------------------
*exit_game
@close ask=true
@return
もし、上記qmenu.ksの内部で用いられている早送り、自動再生を使用する場合、以下の命令をfirst.ksなどのマクロ定義部分で記述しておきます。
;-----------------------------------------------------------------------------
;■早送り、自動再生用
;-----------------------------------------------------------------------------
@iscript
var menuCheckTimer = new Timer (menuCheck, '');
menuCheckTimer.interval = 20;
function menuCheck {
if (tf.autoMode==1)kag.skipToNextStopMenuItem.click();
if (tf.autoMode==2)kag.autoModeMenuItem.click();
menuCheckTimer.enabled = false; // タイマー停止
}
@endscript
画像ファイル
画像をサンプルファイルからコピーして下さい。
imageフォルダにボタン画像などがあります。
テスト実行例
テスト用の実行例として、以下のような例を挙げておきます。
サンプルに入っているものと似たようなものです。
;----------------------------------------------------------------------------- ;■テスト実行 ;----------------------------------------------------------------------------- @rclick call=true target="*rclick" enabled=true *restart_point|簡易メニュー [cm] @eval exp="sf.qmenu_exec_mode = 1" @eval exp="sf.qmenu_save_mode = 1" @eval exp="sf.qmenu_config_mode = 1" @eval exp="sf.qmenu_system_mode = 1" @backlay @image storage="black" layer=base page=back visible=true @trans layer=base time=50 method=crossfade @wt *test_point|簡易メニュー概要 [autosave] [qmenu enabled=true] @backlay @image storage="ima_1" layer=base page=back visible=true @image storage="textwindow" layer=0 page=back visible=true [init_qmenu page="back"] @trans layer=base time=500 method=crossfade @wt [init_qmenu page="fore"] [er]こちらは、簡易メニューのサンプルです。[r][l] 簡易メニューは上下左右にそれぞれボタンを配置することによって、プレイヤーが非常に簡単に命令の実行や設定変更ができるようになる機能です。[r][l] 実際にマウスを上下左右に移動させてみると、表示されます。[r][l] ボタンの上にマウスを持って行くと、ツールチップヒントも表示できるようになっていますので、記号だけの表示でも分かりやすくなっています。[r][l] 下が命令系、右がセーブ・ロード系、左が設定系、上がシステム系です。[r][l] 上下左右の簡易メニューとツールチップヒントは、それぞれ個別に表示させたり表示させないように設定することも可能です。[r][l] 例えば、上のシステム系命令だけは使わないということも可能です。[p] [er]セーブ機能、設定機能、システム機能については基本的な動作はするようになっていますが、命令系のボタンについてはシステムごとに違うと思われますので、個別に作り込んで下さい。[r][l] ま、サンプルとして簡単なものはご提供していますので、そちらを参考にして頂ければと思います。[r][l] なお、キーボード操作にも対応しています。[r][l] 上下左右の方向キーでそれぞれのメニューが表示され、ESCキーでキャンセルできます。[r][l] セーブ・ロード部分では、リターンキーがセーブ、スペースキーがロードに割り当てられています。こちらもソースをいじれば変更可能です。[r][p] [er]それでは実際に、右側のセーブ系でセーブ・ロードを試してみて下さい。[r][l] 左クリックでセーブ、右クリックでロード。マウスホイールでページ移動できます。[r][l] お次は左側の設定部分もどうぞ。[r][l] 文字速度も変更されます。[r][l] 現状では自動再生の速度は、ページ末のウェイトのみ([p]タグ)のみ変更するようになっています。クリック待ちウェイト([l]タグ)も変更したい場合、スライダーのslider.ksファイル中にあるソースの該当箇所のコメントを外せば対応できます。[r][l] なお、文字表示中は簡易メニューの各ボタンは操作できないようになっています。文字表示中に実行すると文字表示が変になる可能性があるためですね。[r][l] 上のシステム系命令も動かしてみましょう。ま、別に最初に戻ったり終了する必要はありません。[r][l] 効果音などは、適宜個別に追加して下さい。[p] [er]現在はマウスカーソルで移動したら表示される形式ですが、常に表示をさせることも可能です。[r][l] 実際に変更してみましょう。[p] [qmenu enabled=false] @backlay @image storage="black" layer=base page=back visible=true [erase_qmenu] @trans layer=base time=500 method=crossfade @wt @eval exp="sf.qmenu_exec_mode = 2" @eval exp="sf.qmenu_save_mode = 2" @eval exp="sf.qmenu_config_mode = 2" @eval exp="sf.qmenu_system_mode = 2" @wait time=500 @backlay @image storage="engawa_1" layer=base page=back visible=true [qmenu enabled=true] [init_qmenu page="back"] @trans layer=base time=500 method=crossfade @wt [init_qmenu page="fore"] *test_point2|「常に表示」のサンプル [autosave] [er]このように、常に表示をすることができます。[r][l] 下の命令系メニューのみ常に表示、などもできます。[r][l] あまり使わないかもしれませんが、設定で変更できるようにしてもいいかと思います。[p] [qmenu enabled=false] @backlay @image storage="black" layer=base page=back visible=true [erase_qmenu] @trans layer=base time=500 method=crossfade @wt @eval exp="sf.qmenu_exec_mode = 1" @eval exp="sf.qmenu_save_mode = 1" @eval exp="sf.qmenu_config_mode = 1" @eval exp="sf.qmenu_system_mode = 1" @wait time=500 @backlay @image storage="engawa_1" layer=base page=back visible=true [init_qmenu page="back"] @trans layer=base time=500 method=crossfade @wt [init_qmenu page="fore"] *test_point3|使わない場合のサンプル [autosave] [er]今度は簡易メニューを使えなくしてみました。[r][l] [qmenu enabled=false]というタグ一つで、簡易メニューを使わないということもできます。[r][l] オープニングやエンディング直前では使わないようにするといいかもしれません。[r][l] また、文字枠を消して背景を消す時は、簡易メニューは表示させないようにする方が見た目にも美しくなると思います。[r][l] [qmenu enabled=true] 簡易メニューを使えるようにしました。マウスでの移動表示モードにしています。[r][l] [er]なお、おまけとしてオートセーブにも対応しています。右側の簡易メニューのオートセーブのページをご覧下さい。[r][l] ここまで見ている段階で、既にいくつか、オートセーブされていると思います。[r][l] オートセーブは新しい順に表示されるようになっています。[p] [er]なお、全画面表示にした場合、ウィンドウメニューを表示しようとすると、上側の簡易メニューが隠れてしまいます。[r][l] そのため、ウィンドウメニューを使う場合は、上側の簡易メニューを使わないようにするか、もしくは表示位置をずらすなどの対処をしましょう。[p] *test_point4|その他注意事項 [autosave] [er]なお、簡易メニューでスライダーを使う場合、コンフィグ画面で設定した値と同期する必要があります。[r][l] 実装方法を見て、同期する命令を追加するのを忘れないようにしましょう。[r][l] 実際に設定画面にジャンプしてみます。値を変更してみて、設定画面に共に反映されていることを確認して下さい。[p] @call storage="config.ks" [er]後は、画像のデザインを個別にして、効果音を追加すれば簡易メニューの導入ができると思います。[r][l] なお、文字枠消去命令や、右クリックメニュー、コンフィグ画面などに移行した前後で、画面の整合性が取れているかのチェックを念入りにしましょう。[r][l] きちんと常に表示されるか、変なゴミが残らないかですね。[p] [er]この簡易メニューは非常に使いやすい機能となる、自信を持ってご提供できる機能です。[r][l] 少しでもお役に立てれば嬉しいです。[r][l] 説明は以上です。最初に戻ります。[p] [jump target="*restart_point"][s] *rclick @call storage="qmenu.ks" target="*erasetextwindow" @return
残作業のメモ
残作業を以下にざっと説明します。メモ程度なので他にもいろいろとあるでしょうが、参考までに。
- 簡易メニューの画像デザインをして、画像ファイルの用意をして、座標を確定します。
- 画像ファイルはサンプルと同じファイル名で保存するといいでしょう。MainWindow.tjsのfunction makeQExecMenu()などのメソッド内部で座標を記述します。
- ボタンを押した後に処理する部分を作ります。
- MainWindow.tjsのfunction loadQExecMenuClick()メソッドなどから、それぞれの処理部分にジャンプさせるか、そのメソッド内に処理を記述します。
- ボタンを押したときの効果音など、細かい部分は個別に作り込んで下さい。(注記:function defineQMenuLayers()内部のボタン要素に効果音を定義した場合、エラーが出て正常に動かないことがありました。なので、Click後(function loadQExecMenuClick()などの内部)もしくはClick後にジャンプした後に効果音命令を別途処理すると安全に動作すると思います)
- 他にもボタンが必要な場合、どんどん増産していって下さい。
- 簡易メニューの「マウス移動で表示」と「常に表示」モードの切り替え時の動作チェックはしっかり行いましょう。
その他注意事項
- 画像生成時、メッセージレイヤに画像を表示する場合、吉里吉里付属の画像フォーマットコンバータ(krkrtpc.exe)を用いて、ltAddAlpha形式に変換しなければ半透明にならないので注意しましょう。(ボタン画像はそのままでOKだったと思います)
また、既にltAddAlpha形式になっている画像を再度ltAddAlpha形式に変換したら、アルファ情報が壊れてしまう場合があるようですので注意しましょう。
- 簡易メニュー(右:セーブ)ボタン画像について、ボタン画像は下の画像より常に上に配置されますので、ボタン部分に画像を配置した場合、数値や文字などが下に隠れてしまい見えなくなってしまいます。そのため、ボタン画像はハイライトのみにして、それぞれの模様は下の画像部分に入れています。(サンプル参照)
- ボタン画像の座標設定は、全て「常に表示」にしたときに800x600でどこに配置するかの座標を入れます。
- ウィンドウ上部にメニューバーがある場合、フルスクリーン表示すると、マウスを上部に持って行った場合に簡易メニュー(上:システム)の部分がウィンドウのメニューバーによって一部隠れてしまいます。そのため、ウィンドウのメニューバーを入れたい場合は簡易メニュー(上:システム)の座標をもう少し下に配置するか、簡易メニュー(上:システム)を使わないようにしましょう。
- 文字描画中に簡易メニューの命令を実行すると文字表示がおかしくなる場合がありますので、クリック待ちの状態でなければ一部ボタンは機能しないようになっています。それ以外の場面で簡易メニューを実行したい場合は、function
checkTextDrawing()内でのclickWaiting部分を削除して修正して下さい。
- もしオートセーブ機能が不要であれば、function saveBookmarkAutoMode(num)とfunction loadBookmarkAutoMode(num)、もしくはその呼び出し元のfunction lEntryQSaveMenu(num)とrEntryQSaveMenu(num)を書き換えればいいでしょう。
- セーブ方式については、sf.autosave_entryに最新のエントリポイントを保持していて、オートセーブをする度に+1されます。表示する際に新しい順番に100から表示されるように場所を入れ替えているだけです。
- スライダーでは、自動読み進め(オートプレイ)の速度は、改ページ時のウェイト(autoModePageWait、いわゆる[p]タグ)のみ変更しています。行クリック待ち時のウェイト(autoModeLineWait、いわゆる[l]タグ)の速度は変更していませんので、必要に応じて調整して下さい。(コメントアウトを外して調整して下さい)
- [cm]タグなど、全メッセージレイヤを消去する命令は使わないようにしましょう。簡易メニューまで消されてしまいます。
よくありそうなトラブル
- 簡易メニューを移動表示にしているけど、初めて表示エリアにマウスカーソルを入れたときに、移動ではなく瞬間的に表示されてしまう。
→[init_qmenu page="fore"]命令がトランジションの前にあるために起こります。[trans][wt]命令の後に記述すれば正常に表示されるようになります。
- ゲーム中にコンフィグ画面で「マウス移動で表示」から「常に表示」に変更して、ゲームに戻ってもすぐに反映してくれず、次のトランジションが起こったときから「常に表示」が反映される。
→コンフィグ画面が終わったとき、再度文字枠を含めて簡易メニューをトランジションで描画しなおすことで解決できます。
素材
月下之煌で用いた画像の素材を置いておきます。
必要であればどうぞ。
こちらからダウンロードして下さい。
6.3 文字のフェード表示
機能
こんな感じで文字をフェードで表示する機能です。
NScripterにはあるみたいですが、吉里吉里ではありませんでしたので製作しました。

- 1文字ずつ透明度を上げながら表示することによって、フェードっぽく見えます。
- ノーウェイトを含む、ある一定速度より速く表示される速度の場合、フェード表示を無効にします。
- CPUにあまり負荷がかからないので、追加しても軽く動作します。
実装方法を以下に示します。
Config.tjs
- Config.tjsのメッセージレイヤにおけるフォントの設定で、「縁取り」にします。(defaultEdge = trueに)。影付けでも、共に処理なしでも動作上は全く問題ありませんが、縁取りの方が美しく表示されます。
MessageLayer.tjs
MessageLayer.tjsのfunction MessageLayer()の直前に、以下の命令を追加します。
フェードの速さやタイミング、フェード表示させない速度の閾値などは、これらの数値を調整して下さい。
(注意)6文字をフェードさせながら表示させますので、あまりにゆっくりなフェード速度だと違和感が出る可能性があります。
// リンクタイプ
var ltNormal = 1;
var ltButton = 2;
var ltEdit = 3;
var ltCheckBox = 4;
//↓--------------------------------------- ここから追加部分--
//mebius:文字フェード用変数
var drawtimer = [];
var timercount = 0;
var chelm = %[];
var timer_interval = 40;//フェード状態更新の頻度(ms)
var timer_addopa = 65;//1回のフェードでどれだけ透明度を上げるか(最高255)
var first_opa = 50;//最初に描画する時の透明度(最高255)
var nonfade_limit = 10;//これ以下の値の(表示が速い)文字速度(ms)では、フェード表示させない上限値。
//↑--------------------------------------- ここまで追加部分--
function MessageLayer(owner, parent, name, id, do_config)
{
// MessageLayer コンストラクタ
// owner : オーナー KAG Window
// parent : 親レイヤ
同じくMessageLayer.tjsのfunction MessageLayer()の最後に、以下の命令を追加します。
function MessageLayer(owner, parent, name, id, do_config)
{
//(中略)
// リンクをハイライト表示するためのレイヤ
highlightLayer = new global.KAGLayer(window, this);
highlightLayer.type = layerType;
highlightLayer.face = dfAuto;
highlightLayer.hitType = htProvince;
// 領域画像で当たり判定を行う
//↓--------------------------------------- ここから追加部分--
//mebius:文字フェード用タイマー定義
drawtimer[0] = new Timer(onDrawTimer0, '');
drawtimer[0].interval = timer_interval;
drawtimer[0].enabled = false;
drawtimer[1] = new Timer(onDrawTimer1, '');
drawtimer[1].interval = timer_interval;
drawtimer[1].enabled = false;
drawtimer[2] = new Timer(onDrawTimer2, '');
drawtimer[2].interval = timer_interval;
drawtimer[2].enabled = false;
drawtimer[3] = new Timer(onDrawTimer3, '');
drawtimer[3].interval = timer_interval;
drawtimer[3].enabled = false;
drawtimer[4] = new Timer(onDrawTimer4, '');
drawtimer[4].interval = timer_interval;
drawtimer[4].enabled = false;
drawtimer[5] = new Timer(onDrawTimer5, '');
drawtimer[5].interval = timer_interval;
drawtimer[5].enabled = false;
chelm.opa = [];
chelm.dx = [];
chelm.dy = [];
chelm.ch = [];
chelm.chColor = [];
chelm.ll = [];
//↑--------------------------------------- ここまで追加部分--
}
function finalize()
{
同じくMessageLayer.tjsのfunction finalize()の次に、以下の命令を追加します。
function finalize()
{
// invalidateLinkObjects(); // リンクに結びつけられたオブジェクトの無効化
invalidateLinkObjects();
invalidate highlightLayer;
invalidate lineLayer;
super.finalize();
}
//↓--------------------------------------- ここから追加部分--
//mebius:文字フェード用。
function onDrawTimer0()
{
chelm.opa[0] += timer_addopa;
if (chelm.opa[0] >= 255)
{
chelm.opa[0] = 255;
drawtimer[0].enabled = false;
}
if(edge)
chelm.ll[0].drawText(chelm.dx[0], chelm.dy[0], chelm.ch[0], chelm.chColor[0], chelm.opa[0],
antialiased, edgeEmphasis, edgeColor, edgeExtent, 0, 0);
else if(shadow)
chelm.ll[0].drawText(chelm.dx[0], chelm.dy[0], chelm.ch[0], chelm.chColor[0], chelm.opa[0],
antialiased, 255, shadowColor, 0, 2, 2);
else
chelm.ll[0].drawText(chelm.dx[0], chelm.dy[0], chelm.ch[0], chelm.chColor[0], chelm.opa[0],
antialiased);
}
function onDrawTimer1()
{
chelm.opa[1] += timer_addopa;
if (chelm.opa[1] >= 255)
{
chelm.opa[1] = 255;
drawtimer[1].enabled = false;
}
if(edge)
chelm.ll[1].drawText(chelm.dx[1], chelm.dy[1], chelm.ch[1], chelm.chColor[1], chelm.opa[1],
antialiased, edgeEmphasis, edgeColor, edgeExtent, 0, 0);
else if(shadow)
chelm.ll[1].drawText(chelm.dx[1], chelm.dy[1], chelm.ch[1], chelm.chColor[1], chelm.opa[1],
antialiased, 255, shadowColor, 0, 2, 2);
else
chelm.ll[1].drawText(chelm.dx[1], chelm.dy[1], chelm.ch[1], chelm.chColor[1], chelm.opa[1],
antialiased);
}
function onDrawTimer2()
{
chelm.opa[2] += timer_addopa;
if (chelm.opa[2] >= 255)
{
chelm.opa[2] = 255;
drawtimer[2].enabled = false;
}
if(edge)
chelm.ll[2].drawText(chelm.dx[2], chelm.dy[2], chelm.ch[2], chelm.chColor[2], chelm.opa[2],
antialiased, edgeEmphasis, edgeColor, edgeExtent, 0, 0);
else if(shadow)
chelm.ll[2].drawText(chelm.dx[2], chelm.dy[2], chelm.ch[2], chelm.chColor[2], chelm.opa[2],
antialiased, 255, shadowColor, 0, 2, 2);
else
chelm.ll[2].drawText(chelm.dx[2], chelm.dy[2], chelm.ch[2], chelm.chColor[2], chelm.opa[2],
antialiased);
}
function onDrawTimer3()
{
chelm.opa[3] += timer_addopa;
if (chelm.opa[3] >= 255)
{
chelm.opa[3] = 255;
drawtimer[3].enabled = false;
}
if(edge)
chelm.ll[3].drawText(chelm.dx[3], chelm.dy[3], chelm.ch[3], chelm.chColor[3], chelm.opa[3],
antialiased, edgeEmphasis, edgeColor, edgeExtent, 0, 0);
else if(shadow)
chelm.ll[3].drawText(chelm.dx[3], chelm.dy[3], chelm.ch[3], chelm.chColor[3], chelm.opa[3],
antialiased, 255, shadowColor, 0, 2, 2);
else
chelm.ll[3].drawText(chelm.dx[3], chelm.dy[3], chelm.ch[3], chelm.chColor[3], chelm.opa[3],
antialiased);
}
function onDrawTimer4()
{
chelm.opa[4] += timer_addopa;
if (chelm.opa[4] >= 255)
{
chelm.opa[4] = 255;
drawtimer[4].enabled = false;
}
if(edge)
chelm.ll[4].drawText(chelm.dx[4], chelm.dy[4], chelm.ch[4], chelm.chColor[4], chelm.opa[4],
antialiased, edgeEmphasis, edgeColor, edgeExtent, 0, 0);
else if(shadow)
chelm.ll[4].drawText(chelm.dx[4], chelm.dy[4], chelm.ch[4], chelm.chColor[4], chelm.opa[4],
antialiased, 255, shadowColor, 0, 2, 2);
else
chelm.ll[4].drawText(chelm.dx[4], chelm.dy[4], chelm.ch[4], chelm.chColor[4], chelm.opa[4],
antialiased);
}
function onDrawTimer5()
{
chelm.opa[5] += timer_addopa;
if (chelm.opa[5] >= 255)
{
chelm.opa[5] = 255;
drawtimer[5].enabled = false;
}
if(edge)
chelm.ll[5].drawText(chelm.dx[5], chelm.dy[5], chelm.ch[5], chelm.chColor[5], chelm.opa[5],
antialiased, edgeEmphasis, edgeColor, edgeExtent, 0, 0);
else if(shadow)
chelm.ll[5].drawText(chelm.dx[5], chelm.dy[5], chelm.ch[5], chelm.chColor[5], chelm.opa[5],
antialiased, 255, shadowColor, 0, 2, 2);
else
chelm.ll[5].drawText(chelm.dx[5], chelm.dy[5], chelm.ch[5], chelm.chColor[5], chelm.opa[5],
antialiased);
}
function drawDrawTimer(timernum)
{
if (drawtimer[timernum].enabled == true)
{
drawtimer[timernum].enabled = false;
chelm.ll[timernum].drawText(chelm.dx[timernum], chelm.dy[timernum],
chelm.ch[timernum], chelm.chColor[timernum], 255,
antialiased, edgeEmphasis, edgeColor, edgeExtent, 0, 0);
}
}
function addDrawTimer(lineLayer,dx,dy,ch,chColor)
{
if (drawtimer[timercount] !== void)
{
if (drawtimer[timercount].enabled == true)//使われている場合は、強制描画
{
drawDrawTimer(timercount);
}
drawtimer[timercount].enabled = true;
}
chelm.ll[timercount] = lineLayer;
chelm.dx[timercount] = dx;
chelm.dy[timercount] = dy;
chelm.ch[timercount] = ch;
chelm.chColor[timercount] = chColor;
chelm.opa[timercount] = first_opa;//最初に描画する透明度
timercount++;
if (timercount >= 6)
timercount = 0;
}
function drawAllDrawTimer()
{
for (var i=0;i<6;i++)
{
drawDrawTimer(i);
}
}
function stopAllDrawTimer()
{
for (var i=0;i<6;i++)
{
if (drawtimer[i].enabled == true)
{
drawtimer[i].enabled = false;
}
}
}
//↑--------------------------------------- ここまで追加部分--
function setCompLayer(lay) { comp = lay; }
function clearLayer()
{
同じくMessageLayer.tjsのfunction clearLayer()の最初に、以下の命令を追加します。
function clearLayer()
{
//↓--------------------------------------- ここから追加部分--
//mebius:タイマーを全てオフにする。
stopAllDrawTimer();
//↑--------------------------------------- ここまで追加部分--
// レイヤをクリア
window.updateBeforeCh = 1;
cancelDrag();
if(imageModified)
{
同じくMessageLayer.tjsのfunction reline()の最初の方に、以下の命令を追加します。
function reline()
{
// 改行
// ページを越える場合は true, 越えないで改行できる場合は false
var condition;
//↓--------------------------------------- ここから追加部分--
//mebius:改行時には全て描画
if (sf.textfade)
{
drawAllDrawTimer();
}
//↑--------------------------------------- ここまで追加部分--
if(vertical)
{
condition= lineLayerOriginX + getLineLayerLeftOffset() - lineSpacing -
lineSize <= marginL;
}
同じくMessageLayer.tjsのfunction processCh(ch)の以下の部分を、以下の命令に変更します。
function processCh(ch)
{
// 文字 ch を描画する
// 改行が行われ、かつそれがページ末端を越える場合は true を返す
// それ以外は false
var vert = vertical;
if((vert ? y >= relinexpos : x >= relinexpos ) && autoReturn)
{
if(((lastDrawnCh=="" || wwLeading.indexOf(lastDrawnCh)==-1) &&
wwFollowing.indexOf(ch)==-1) ||
(lastDrawnCh!="" && wwFollowingWeak.indexOf(lastDrawnCh)!=-1 &&
wwFollowingWeak.indexOf(ch)!=-1))
{
// 最後に描画したのが行末禁則文字でない場合
// しかもこれから描画するのが行頭禁則文字でない
// 場合
// または弱禁則文字が連続していない場合
if(reline()) return autoReturn;
}
else if(vert ? ( y>imageHeight ) : (x>imageWidth))
{
// これから描画するのが強禁則文字ではなくて、
// 確実に 右端を越える場合
// ( この場合は余白は考えない )
if(reline()) return autoReturn;
}
}
changeLineSize() if sizeChanged;
var inlink = inLink != -1;
beginLinkLine() if inlink;
var ll = lineLayer;
var llfont = ll.font;
var cw = llfont.getTextWidth(ch);
var dx , dy;
if(vert)
dx = int(lineLayerBase+(fontSize>>1)), dy = int(lineLayerPos);
else
dx = int(lineLayerPos), dy = int(lineLayerBase-fontSize);
//↓--------------------------------------- ここから追加部分--
//↓元々ある以下の部分をコメントアウト、もしくは削除します。
//if(edge)
// ll.drawText(dx, dy, ch, chColor, 255, antialiased, edgeEmphasis, edgeColor, edgeExtent, 0, 0); // 文字
//else if(shadow)
// ll.drawText(dx, dy, ch, chColor, 255, antialiased, 255, shadowColor, 0, 2, 2); // 文字
//else
// ll.drawText(dx, dy, ch, chColor, 255, antialiased); // 文字
//mebius:文字フェード表示。
//縁取り以外(影付けもしくはノーマル描画)では、重ね合わせて描画されるのでアンチエイリアスが崩れます。
//そのため、不要であれば影付けとノーマル描画は元のままでも構いません。
if(edge)
{
if (kag.skipMode == 0 && sf.textfade && ((!kag.getCurrentRead() && (kag.userChSpeed > nonfade_limit)) || (kag.getCurrentRead() && ((kag.userCh2ndSpeed > nonfade_limit) || ((kag.userCh2ndSpeed == -1) && (kag.userChSpeed > nonfade_limit))))) && f.textfade_enabled)
{
ll.drawText(dx, dy, ch, chColor, first_opa, antialiased, edgeEmphasis, edgeColor, edgeExtent, 0, 0); // 文字
addDrawTimer(lineLayer,dx,dy,ch,chColor);
}
else
{
ll.drawText(dx, dy, ch, chColor, 255, antialiased, edgeEmphasis, edgeColor, edgeExtent, 0, 0); // 文字
}
}
else if(shadow)
{
if (kag.skipMode == 0 && sf.textfade && ((!kag.getCurrentRead() && (kag.userChSpeed > nonfade_limit)) || (kag.getCurrentRead() && ((kag.userCh2ndSpeed > nonfade_limit) || ((kag.userCh2ndSpeed == -1) && (kag.userChSpeed > nonfade_limit))))) && f.textfade_enabled)
{
ll.drawText(dx, dy, ch, chColor, first_opa, antialiased, 255, shadowColor, 0, 2, 2); // 文字
addDrawTimer(lineLayer,dx,dy,ch,chColor);
}
else
{
ll.drawText(dx, dy, ch, chColor, 255, antialiased, 255, shadowColor, 0, 2, 2); // 文字
}
}
else
{
if (kag.skipMode == 0 && sf.textfade && ((!kag.getCurrentRead() && (kag.userChSpeed > nonfade_limit)) || (kag.getCurrentRead() && ((kag.userCh2ndSpeed > nonfade_limit) || ((kag.userCh2ndSpeed == -1) && (kag.userChSpeed > nonfade_limit))))) && f.textfade_enabled)
{
ll.drawText(dx, dy, ch, chColor, first_opa, antialiased);
addDrawTimer(lineLayer,dx,dy,ch,chColor);
}
else
{
ll.drawText(dx, dy, ch, chColor, 255, antialiased);
}
}
//↑--------------------------------------- ここまで追加部分--
if(currentRuby != "")
{
// ルビがある
var cw = llfont.getTextWidth(ch);
var orgsize = llfont.height;
first.ksでの処理
シナリオファイルの最初の方で、フェードで用いる変数の初期化とマクロを定義しておきます。
@iscript //sf.textfade 0:テキストをフェードで表示しない(瞬間表示)、1:フェードで表示する sf.textfade = 1 if sf.textfade === void; //テキストのフェード命令用 f.textfade_enabled = 0; @endscript ;----------------------------------------------------------------------------- ;■文字フェード有り [textfade enabled=true] ;■文字フェードなし [textfade enabled=false] ;----------------------------------------------------------------------------- [macro name=textfade] @if exp="mp.enabled == 'false'" @eval exp="f.textfade_enabled = 0" @else @eval exp="f.textfade_enabled = 1" @endif [endmacro] ;-----------------------------------------------------------------------------
命令の記述方法
以下の命令で、フェードのオン・オフが可能です。
ユーザーのフェード表示オン・オフ設定を保持する変数:
こちらはコンフィグ画面などで設定する値です。
[eval exp="sf.textfade = 1"]
[eval exp="sf.textfade = 0"]
システム的にフェード表示を許可・不許可する命令:
[textfade enabled=true]
[textfade enabled=false]
備考:
- 文字を重ねて描画していますので、Config.tjsにおいてフォントを「縁取り(defaultEdgeの値をtrue)」にすることを強くオススメします。それ以外はフォントが太く見えたり、アンチエイリアスが崩れる場合があります。
- 同じ理由で、文を描画途中にマウスクリックなどで文字を瞬間的に表示すると、マウスクリックした後からの縁取りの色が少し薄く見えます。これは文字枠を少し黒っぽくしたり、文字枠の色に縁取りの色を近づけることで、見た目の違和感をほとんどなくすことができます。
- sf.smooth_textはユーザーが決めるもので、f.textfade_enabledはシステム的(演出側)が用いるものです。
- [unable_textfade]によってf.textfade_enabledをオフにしておいたら、ユーザーがフェード表示をオンにしていても、フェードで描画されません。フェードで表示させたくない部分は[unable_textfade]命令を使って下さい。
テスト実行例
テスト用の実行例として、以下のような例を挙げておきます。
サンプルに入っているものと似たようなものです。(サンプルでは、背景画像などを含んで表示しています)
*restart_point_txtfade|文字フェードサンプル [cm] [er]こちらは、文字のフェード表示機能のサンプルです。[r][l] 現在は通常状態で、一文字ずつ瞬間表示しています。[r][l] それでは、実際に文字のフェード機能をオンにして表示してみましょう。[r][l] [textfade enabled=true] 文字フェード機能がオンになりました。[r][l] このように、文字をフェードしながら表示します。[r][l] ウィンドウのメニューで文字速度を「遅い」で表示してみましょう。[r][l] すると、一文字ずつフェードで表示しているのがはっきりと分かると思います。[r][l] 文字速度が一定以上速くなると(デフォルトでは一文字の描画間隔が10ms以下になると)、自動的にオフになります。これは見た目ではよく分からなくなるためですね。[r][l] 文字速度を「速い」に設定してみると、フェード機能も自動的にオフにされます。なので、瞬間表示でも綺麗に表示されます。[r][l] [textfade enabled=false] フェードをオフにしたら、このように表示されます。[r][l] カクカクッという表示と、スムーズな表示の違いが分かると思います。[p] [textfade enabled=true] [er]フェード機能をオンにしました。[r][l] 文字は重ねて描画していますので、文字の境界が通常よりも濃く表示されます。[r][l] そのため、アンチエイリアスが崩れてしまう場合がありますので、文字は「縁取り」で描画することをオススメします。そうすることで、綺麗に描画されます。[r][l] 文字を表示している途中でマウスクリックして、文字を最後まで瞬間描画してみて下さい。[r][l] 分かりにくいかもしれませんが、よく見ると、途中の文字から少しだけ黒い縁が薄くなっているのが分かると思います。これも重ねて描画していることによるものです。[r][l] これは文字枠を少し黒っぽいものを用いることで、見た目にはほとんど分からなくすることができます。[r][l] ですので、文字枠は少し明るさを落としたものを用いると、違和感なく表示できるでしょう。[p] [er]フェードの速度などは、ソースにある値を変更することで調整可能です。[r][l] お好みに合わせて調整して下さい。[p] [er]説明は以上です。[r][l] 最初に戻ります。[p] [jump target="*restart_point_txtfade"][s]
6.4 ボイスシステム
機能
- ボイスの再生、停止、フェードアウトなどの基本機能
- ページ変更時のボイス消去有無
- ボイス再生時にBGM、ループ効果音音量を下げる設定有無
- セリフ部分のテキストフォントの色を、キャラごとに変更可能
- 複数ボイスの同時再生も可能
- BGM、効果音などの音に関する基本的命令も一通り揃っています
Config.tjsでの処理
- サウンドバッファの数を十分に足りるように確保します。(numSEBuffersの値を確保)
- デフォルトではBGMはMIDI再生になっているので、Oggなどを再生できるようにWaveに変更しておきます。。function BGM_config()で、type = "Wave"に変更しておきます。
MainWindow.tjsでの処理
MainWindow.tjsのfunction onSESoundBufferStop(id)に、以下の命令を追加します。
function onSESoundBufferStop(id)
{
// 効果音の再生が終了した
conductor.trigger('sestop' + id);
//↓--------------------------------------- ここから追加部分--
//mebius:追加分
if (sf.voice_bgm_reduce_onoff && id>=f.cv_buf_min && id<=f.cv_buf_max && bgm.userchange!=1)
{
var seelm = %[];
seelm.volume = 100;
seelm.time = 500;
bgm.fade(seelm);
}
if (sf.voice_bgm_reduce_onoff && id>=f.cv_buf_min && id<=f.cv_buf_max && se[f.se_loop_buf].userchange!=1)
{
var seelm = %[];
seelm.volume = 100;
seelm.time = 500;
se[f.se_loop_buf].fade(seelm);
}
//↑--------------------------------------- ここまで追加部分--
}
BGM.tjsでの処理
BGM.tjsのclass BGMの変数定義部分に、以下の命令を追加します。
class BGM
{
var owner; // このオブジェクトを保持する KAGWindow クラスのオブジェクト
var type = "Wave"; // BGM タイプ
var cdVolume = "xxxx"; // 演奏に使用する CD の ボリュームラベル
var doubleBuffered = true; // クロスフェードを行うか
var midiInitialMessage = <% %>; // MIDI 初期化メッセージ
var buf1; // バッファ 1
var buf2; // バッファ 2
var currentBuffer; // buf1 or buf2
var looping = true; // ループ中かどうか
var volume = 100000; // ボリューム
var playingStorage; // 演奏中のストレージ
var currentStorage; // 見かけの演奏中のストレージ
//↓--------------------------------------- ここから追加部分--
var userchange = 0;//mebius:追加分
//↑--------------------------------------- ここまで追加部分--
var _enabled; // 有効かどうか
function BGM(owner)
{
this.owner = owner;
function onSoundBufferFadeCompleted(source)内部に、以下の命令を追加します。
function onSoundBufferFadeCompleted(source)
{
if(currentBuffer == source)
{
// フェードが終了した
owner.onBGMFadeCompleted();
//↓--------------------------------------- ここから追加部分--
userchange = 0;//mebius:追加分
//↑--------------------------------------- ここまで追加部分--
}
}
SE.tjsでの処理
SE.tjsのclass SESoundBufferの変数定義部分に、以下の命令を追加します。
class SESoundBuffer extends WaveSoundBuffer
{
// 効果音クラス ( WaveSoundBuffer を継承 )
var owner; // オーナー
var id; // 効果音バッファID
var inFading; // フェード中かどうか
var prevstatus = "unload"; // 直前のステータス
var currentStorage = ""; // 見かけの演奏中のストレージ
var currentVolume = 100; // 見かけのボリューム
var inFadeAndStop = false; // フェード終了時に停止するか
//↓--------------------------------------- ここから追加部分--
var userchange = 0;//mebius:追加分
//↑--------------------------------------- ここまで追加部分--
function SESoundBuffer(owner, id)
{
// コンストラクタ
super.WaveSoundBuffer(owner);
function onFadeCompleted()の内部に、以下の命令を追加します。
function onFadeCompleted()
{
// onFadeCompleted オーバーライド
inFading = false;
if(inFadeAndStop)
{
try
{
super.stop(); // 再生停止
}
catch(e)
{
dm("効果音の停止に失敗しました(実行は続行できます) : " + e.message);
}
inFadeAndStop = false;
}
super.onFadeCompleted(...);
owner.onSESoundBufferFadeCompleted(id); // フェード終了
//↓--------------------------------------- ここから追加部分--
userchange = 0;//mebius:追加分
//↑--------------------------------------- ここまで追加部分--
}
first.ksでの処理
Oggなどを使う場合、プラグインを登録しておきます。
first.ksなどの最初の方でプラグインとして宣言します。
プラグインの宣言方法:
[loadplugin module="wuvorbis.dll"]
プラグイン宣言後に、以下の変数・マクロなどを定義します。
;(注意)BGM、効果音において、volume値はフェードイン・アウトなどの
; システム内部で使いますので、基本的に変更できない仕様です。
; ユーザー・もしくは演出側が変更できるのはgvolumeの値だけです。
;初期値。設定の初期化をする時にもこの値を使います。
[eval exp="f.init_bgm = 80"]
[eval exp="f.init_se = 100"]
[eval exp="f.init_cv = 100"]
[eval exp="f.init_sysse = 80"]
;BGM・効果音関連
[eval exp="f.bgm_fadetime = 4000"]
[eval exp="f.bgm_fadetime_fast = 1500"]
[eval exp="f.bgm_crossfadetime = 3000"]
[eval exp="f.se_fadetime = 2000"]
[eval exp="f.cv_fadetime = 200"]
;ボイス再生時の音量減少量
[eval exp="f.voice_bgm_reduce_s = 25"]
;現在再生中のBGM、効果音(ループのみ)
[eval exp="f.now_bgm = ''"]
[eval exp="f.now_se_loop = ''"]
;ボイスをボタンで再度再生用の保持する変数
[eval exp="f.pv_char = ''"]
[eval exp="f.pv_voice = ''"]
;ここでは、以下のように割り当てることを想定しています。
;必要に応じて、適宜値を調整して下さい。
;ループ用効果音バッファに通常効果音を流しても大丈夫です。
;ボイス再生中にBGM音量を下げる設定をしていたら、ループ用効果音バッファも同時に音量が下げられます。
;通常効果音のバッファ数を増やす場合、0,1,2...というように
;ループ用効果音バッファよりも値が小さくなるようにします。
;buf:0 通常効果音
;buf:1 ループ用効果音
;buf:2 システム用効果音(ボタンの音、決定・キャンセル音などのシステム用)
;buf:3~ ボイス用効果音
[eval exp="f.se_loop_buf = 1"]
[eval exp="f.se_sys_buf = 2"]
[eval exp="f.cv_buf_min = 3"]
[eval exp="f.cv_buf_max = 13"]
;-----------------------------------------------------------------------------
;■各キャラクター使用サウンドバッファ
;個別に、各キャラクター使用サウンドバッファ、各キャラクターセリフのフォント色を割り当てて下さい。
;注意:同時に再生されるキャラは、同じバッファを割り当てないようにします。
;ここではサンプルを記述しています。キャラごとに修正しておいて下さい。
;-----------------------------------------------------------------------------
[eval exp="f.cv_sb_shisui = 3"]
[eval exp="f.cv_sb_yue = 4"]
[eval exp="f.cv_sb_syao = 5"]
[eval exp="f.cv_sb_renfa = 6"]
[eval exp="f.cv_sb_ui_sai = 7"]
[eval exp="f.cv_sb_roushi = 8"]
[eval exp="f.cv_sb_ishin = 9"]
[eval exp="f.cv_sb_outei = 10"]
[eval exp="f.cv_sb_youma = 11"]
[eval exp="f.cv_sb_sonota_f = 12"]
[eval exp="f.cv_sb_sonota_m = 13"]
[eval exp="f.cv_sb_hist = 3"]
[eval exp="tf.voicenoend = false"]
;-----------------------------------------------------------------------------
;■各キャラクターセリフのフォント色
;-----------------------------------------------------------------------------
[eval exp="f.cv_color_shisui = 0xffffff"]
[eval exp="f.cv_color_yue = 0xffd0d0"]
[eval exp="f.cv_color_syao = 0xd0ffe0"]
[eval exp="f.cv_color_renfa = 0xffd0ff"]
[eval exp="f.cv_color_ui = 0xe0ffe0"]
[eval exp="f.cv_color_sai = 0xe0e0ff"]
[eval exp="f.cv_color_roushi = 0xfff0d0"]
[eval exp="f.cv_color_ishin = 0xf0d0ff"]
[eval exp="f.cv_color_outei = 0xffc0e0"]
[eval exp="f.cv_color_kyuki = 0xffc0e0"]
[eval exp="f.cv_color_syokuin = 0xe0c0ff"]
[eval exp="f.cv_color_kou = 0xe0d0c0"]
[eval exp="f.cv_color_byakko = 0xffffff"]
[eval exp="f.cv_color_youma = 0xffffff"]
[eval exp="f.cv_color_yasai = 0xf0fff0"]
[eval exp="f.cv_color_sakana = 0xf0f0ff"]
[eval exp="f.cv_color_saisyou = 0xe0c0c0"]
[eval exp="f.cv_color_seiyou = 0xf0f0ff"]
[eval exp="f.cv_color_nenpai = 0xd0e0e0"]
[eval exp="f.cv_color_sonota_f = 0xffffff"]
[eval exp="f.cv_color_sonota_m = 0xffffff"]
;-----------------------------------------------------------------------------
;■変数定義
;-----------------------------------------------------------------------------
@iscript
//BGM、効果音、ボイスの有無。 1:再生可能、0:再生不可
//効果音とシステム効果音を別物として扱うこともできます。
//効果音とシステム効果音を分ける場合、"sf.sysse_on"で検索して、該当部分をコメントアウトしたり適宜調整願います。
sf.bgm_on = 1 if sf.bgm_on === void;
sf.se_on = 1 if sf.se_on === void;
sf.voice_on = 1 if sf.voice_on === void;
//sf.sysse_on = 1 if sf.sysse_on === void;
//それぞれ初期値を代入
sf.bgm_vol = f.init_bgm if sf.bgm_vol === void; // BGM 音量用
sf.se_vol = f.init_se if sf.se_vol === void; // SE 音量用
sf.cv_vol = f.init_cv if sf.cv_vol === void; // ボイス 音量用
sf.sysse_vol = f.init_sysse if sf.sysse_vol === void; // システムSE 音量用
//ボイス対応:01:止水、02:ユエ、03:暁、04:姐さん、05:右伊・左伊、06:老師、07:以心、08:應帝、09:妖魔、10:その他女性、11:その他男性
//設定画面で用いるための変数。各キャラのボイスあり、なしの変数。
sf.voice_01 = 1 if sf.voice_01 === void;
sf.voice_02 = 1 if sf.voice_02 === void;
sf.voice_03 = 1 if sf.voice_03 === void;
sf.voice_04 = 1 if sf.voice_04 === void;
sf.voice_05 = 1 if sf.voice_05 === void;
sf.voice_06 = 1 if sf.voice_06 === void;
sf.voice_07 = 1 if sf.voice_07 === void;
sf.voice_08 = 1 if sf.voice_08 === void;
sf.voice_09 = 1 if sf.voice_09 === void;
sf.voice_10 = 1 if sf.voice_10 === void;
sf.voice_11 = 1 if sf.voice_11 === void;
//各キャラボイス音量変数
sf.voice_vol_01 = 100 if sf.voice_vol_01 === void;
sf.voice_vol_02 = 100 if sf.voice_vol_02 === void;
sf.voice_vol_03 = 100 if sf.voice_vol_03 === void;
sf.voice_vol_04 = 100 if sf.voice_vol_04 === void;
sf.voice_vol_05 = 100 if sf.voice_vol_05 === void;
sf.voice_vol_06 = 100 if sf.voice_vol_06 === void;
sf.voice_vol_07 = 100 if sf.voice_vol_07 === void;
sf.voice_vol_08 = 100 if sf.voice_vol_08 === void;
sf.voice_vol_09 = 100 if sf.voice_vol_09 === void;
sf.voice_vol_10 = 100 if sf.voice_vol_10 === void;
sf.voice_vol_11 = 100 if sf.voice_vol_11 === void;
//文末ボイス終了(次の文に行ってもボイスを止めるかどうか) 0:文末でボイスを終了しない、1:終了する
sf.voice_cut = 1 if sf.voice_cut === void;
//BGM再生時の音量を下げるかどうか 0:下げない、1:下げる
sf.voice_bgm_reduce_onoff = 1 if sf.voice_bgm_reduce_onoff === void;
//BGM再生時の音量を下げる量
sf.voice_bgm_reduce = f.voice_bgm_reduce_s if sf.voice_bgm_reduce === void;
@endscript
;-----------------------------------------------------------------------------
;■初期化
;使用バッファだけ増やして下さい。
;-----------------------------------------------------------------------------
@bgmopt gvolume="&sf.bgm_vol"
@seopt buf=0 gvolume="&sf.se_vol"
@seopt buf=1 gvolume="&sf.se_vol"
@seopt buf=2 gvolume="&sf.se_vol"
;sf.sysse_onを用いる場合、以下のものに変更。
;@seopt buf=2 gvolume="&sf.sysse_vol"
@seopt buf=3 gvolume="&sf.cv_vol"
@seopt buf=4 gvolume="&sf.cv_vol"
@seopt buf=5 gvolume="&sf.cv_vol"
@seopt buf=6 gvolume="&sf.cv_vol"
@seopt buf=7 gvolume="&sf.cv_vol"
@seopt buf=8 gvolume="&sf.cv_vol"
@seopt buf=9 gvolume="&sf.cv_vol"
@seopt buf=10 gvolume="&sf.cv_vol"
@seopt buf=11 gvolume="&sf.cv_vol"
@seopt buf=12 gvolume="&sf.cv_vol"
@seopt buf=13 gvolume="&sf.cv_vol"
;-----------------------------------------------------------------------------
;■BGM再生 [splaybgm storage="BGMファイル"]
;■BGM再生(クロスフェード) [sxchgbgm storage="BGMファイル"]
;■BGM停止 [sstopbgm]
;■BGMフェードアウト [sfadeoutbgm]
;引数は全て[playbgm][xchgbgm][fadeoutbgm]と同じです。
;-----------------------------------------------------------------------------
[macro name=splaybgm]
[eval exp="kag.bgm.userchange = 0"]
[playbgm * cond="sf.bgm_on"]
[bgmopt volume=100]
[eval exp="f.now_bgm = mp.storage"]
[endmacro]
[macro name=sxchgbgm]
[eval exp="kag.bgm.stopFade()"]
[eval exp="kag.bgm.userchange = 1" cond="!kag.skipMode"]
[xchgbgm cond="sf.bgm_on" * time=&f.bgm_crossfadetime overlap=&f.bgm_crossfadetime]
[eval exp="f.now_bgm = mp.storage"]
[endmacro]
[macro name=sstopbgm]
[stopbgm]
[eval exp="f.now_bgm = ''"]
[endmacro]
[macro name=sfadeoutbgm]
[eval exp="kag.bgm.stopFade()"]
[eval exp="kag.bgm.userchange = 1"]
[fadeoutbgm *]
[eval exp="f.now_bgm = ''"]
[endmacro]
;-----------------------------------------------------------------------------
;■効果音再生 [splayse storage="SEファイル" buf="再生バッファ"]
;■効果音停止 [sstopse]
;■効果音フェードアウト [sfadeoutse buf=&f.se_loop_buf time=&f.se_fadetime]
;引数は[playse][stopse][fadeoutse]と同じです。
;-----------------------------------------------------------------------------
[macro name=splayse]
[if exp="(mp.buf == f.se_loop_buf) && (mp.loop == true)"]
[eval exp="kag.se[f.se_loop_buf].stopFade()"]
[eval exp="kag.se[f.se_loop_buf].userchange = 1"]
[endif]
[if exp="kag.skipMode<=1"]
[playse * cond="sf.se_on"]
;[playse * cond="sf.sysse_on && mp.buf == 2"]
[else]
;スキップ時はループ音のみ再生する。
[playse * cond="sf.se_on && (mp.buf == f.se_loop_buf) && (mp.loop == 'true')"]
[endif]
[eval exp="f.now_se_loop = mp.storage" cond="(mp.buf == f.se_loop_buf) && (mp.loop == 'true')"]
[endmacro]
[macro name=sstopse]
[stopse *]
[if exp="mp.buf == f.se_loop_buf"]
[eval exp="f.now_se_loop = ''"]
[endif]
[endmacro]
[macro name=sfadeoutse]
[if exp="mp.buf <= f.se_loop_buf"]
[eval exp="kag.se[+mp.buf].userchange = 1"]
[eval exp="kag.se[+mp.buf].stopFade()"]
[endif]
[fadeoutse *]
[if exp="mp.buf == f.se_loop_buf"]
[eval exp="f.now_se_loop = ''"]
[endif]
[endmacro]
;-----------------------------------------------------------------------------
;■ボイス用 [pv b=バッファ s=ファイル名]台詞[sv]
;[pv]命令で再生、[sv]命令でボイス終了判定をします。
;通常は、[pv char="登録キャラ名" voice="ボイスファイル名"]で使います。
;nostop=true: 重複再生用。この属性値があるボイスが再生したとしても、
; 再生中のボイス(前の命令のボイス)をストップしません。
; ま、正確にはnostop属性に何か値があれば勝手にtrueと判定します。
;(同時再生の使用例)
;同時再生時には、delayで文字速度を瞬間表示にして使用します。
;[er][delay speed=nowait][pv char="ui" voice="ui_syao_a04_008"]右伊「あ!」[rs]
;[pv char="sai" voice="sai_syao_a04_009" nostop=true]左伊「!」[delay speed=user][ps]
;-----------------------------------------------------------------------------
;下準備
@iscript
function stopAllVoices()
{
// f.cv_buf_min ~ f.cv_buf_max のすべての効果音を停止する
for(var i = f.cv_buf_min; i <= f.cv_buf_max; i++) kag.se[i].stop();
}
function playVoice(buf, storage)
{
// 効果音バッファ buf にて storage を再生する
// KAG がスキップ処理中の場合は処理を行わない
if(!kag.skipMode)
{
if (!tf.voicenoend)
stopAllVoices();
else
tf.voicenoend = false;
kag.se[buf].play(%[ storage : storage ]);
}
}
function createHistoryActionExp(buf, storage)
{
// メッセージ履歴をクリックしたときに実行する TJS 式を生成する
return "stopAllVoices(), kag.se[" + buf +"].play(%[ storage : '" + storage + "' ])";
}
function waitVoiceStop()
{
for (var i=f.cv_buf_min;i<=f.cv_buf_max;i++)
{
kag.waitSEStop(%[buf:i,canskip:true]);
}
}
function fadeoutAllVoices()
{
for (var i=f.cv_buf_min;i<=f.cv_buf_max;i++)
{
kag.se[i].fadeOut(%[time:f.cv_fadetime]);
}
}
@endscript
;ボイス再生
;
@macro name=pv
[eval exp="f.pv_char = mp.char"]
[eval exp="f.pv_voice = mp.voice"]
@eval exp="tf.voicenoend = true" cond="mp.nostop != ''"
@pv_playpart *
@if exp="mp.voice != ''"
@hact exp="&createHistoryActionExp(f.cv_sb_hist, mp.voice)"
@endif
@endmacro
@macro name=pv_playpart
;それぞれのキャラごとに、色やバッファを分けて再生します。
;記述形式解説は以下の通り。
;@if exp="mp.char=='shisui(←キャラ名)' && sf.voice_01(←ボイスオン・オフの番号)==true"
;@if exp="mp.colorchange == ''"
;@font color=&f.cv_color_shisui(←フォントカラー)
;;@endif
;@eval exp="f.cv_sb_hist = f.cv_sb_shisui(←再生バッファ)"
;@if exp="sf.voice_on && mp.voice != ''"
;@seopt buf=&f.cv_sb_shisui(←再生バッファ) volume=&sf.voice_vol_01(←ボリューム)
;@eval exp="playVoice(f.cv_sb_shisui(←再生バッファ), mp.voice(←ボイスファイル名))"
;@reduce_bgmvol
;@endif
;@endif
;(ここでの例)ボイス対応:01:止水、02:ユエ、03:暁、04:姐さん、05:右伊・左伊、06:老師、07:以心、08:應帝、09:妖魔、10:その他女性、11:その他男性
;以下はキャラごとに個別に修正して下さい。
@if exp="mp.char=='shisui' && sf.voice_01==true"
@if exp="mp.colorchange == ''"
@font color=&f.cv_color_shisui
@endif
@eval exp="f.cv_sb_hist = f.cv_sb_shisui"
@if exp="sf.voice_on && mp.voice != ''"
@seopt buf=&f.cv_sb_shisui volume=&sf.voice_vol_01
@eval exp="playVoice(f.cv_sb_shisui, mp.voice)"
@reduce_bgmvol
@endif
@endif
@if exp="mp.char=='yue' && sf.voice_02==true"
@if exp="mp.colorchange == ''"
@font color=&f.cv_color_yue
@eval exp="f.cv_sb_hist = f.cv_sb_yue"
@endif
@if exp="sf.voice_on && mp.voice != ''"
@seopt buf=&f.cv_sb_yue volume=&sf.voice_vol_02
@eval exp="playVoice(f.cv_sb_yue, mp.voice)"
@reduce_bgmvol
@endif
@endif
@if exp="mp.char=='syao' && sf.voice_03==true"
@if exp="mp.colorchange == ''"
@font color=&f.cv_color_syao
@endif
@eval exp="f.cv_sb_hist = f.cv_sb_syao"
@if exp="sf.voice_on && mp.voice != ''"
@seopt buf=&f.cv_sb_syao volume=&sf.voice_vol_03
@eval exp="playVoice(f.cv_sb_syao, mp.voice)"
@reduce_bgmvol
@endif
@endif
@if exp="mp.char=='renfa' && sf.voice_04==true"
@if exp="mp.colorchange == ''"
@font color=&f.cv_color_renfa
@endif
@eval exp="f.cv_sb_hist = f.cv_sb_renfa"
@if exp="sf.voice_on && mp.voice != ''"
@seopt buf=&f.cv_sb_renfa volume=&sf.voice_vol_04
@eval exp="playVoice(f.cv_sb_renfa, mp.voice)"
@reduce_bgmvol
@endif
@endif
@if exp="(mp.char=='ui' || mp.char=='sai')&& sf.voice_05==true"
@if exp="mp.colorchange == ''"
@font color=&f.cv_color_ui cond="mp.char=='ui'"
@font color=&f.cv_color_sai cond="mp.char=='sai'"
@endif
@eval exp="f.cv_sb_hist = f.cv_sb_ui_sai"
@if exp="sf.voice_on && mp.voice != ''"
@seopt buf=&f.cv_sb_ui_sai volume=&sf.voice_vol_05
@eval exp="playVoice(f.cv_sb_ui_sai, mp.voice)"
@reduce_bgmvol
@endif
@endif
@if exp="mp.char=='roushi' && sf.voice_06==true"
@if exp="mp.colorchange == ''"
@font color=&f.cv_color_roushi
@endif
@eval exp="f.cv_sb_hist = f.cv_sb_roushi"
@if exp="sf.voice_on && mp.voice != ''"
@seopt buf=&f.cv_sb_roushi volume=&sf.voice_vol_06
@eval exp="playVoice(f.cv_sb_roushi, mp.voice)"
@reduce_bgmvol
@endif
@endif
@if exp="mp.char=='ishin' && sf.voice_07==true"
@if exp="mp.colorchange == ''"
@font color=&f.cv_color_ishin
@endif
@eval exp="f.cv_sb_hist = f.cv_sb_ishin"
@if exp="sf.voice_on && mp.voice != ''"
@seopt buf=&f.cv_sb_ishin volume=&sf.voice_vol_07
@eval exp="playVoice(f.cv_sb_ishin, mp.voice)"
@reduce_bgmvol
@endif
@endif
@if exp="mp.char=='outei' && sf.voice_08==true"
@if exp="mp.colorchange == ''"
@font color=&f.cv_color_outei
@endif
@eval exp="f.cv_sb_hist = f.cv_sb_outei"
@if exp="sf.voice_on && mp.voice != ''"
@seopt buf=&f.cv_sb_outei volume=&sf.voice_vol_08
@eval exp="playVoice(f.cv_sb_outei, mp.voice)"
@reduce_bgmvol
@endif
@endif
@if exp="(mp.char=='kyuki' || mp.char=='syokuin' || mp.char=='kou' || mp.char=='byakko' || mp.char=='youma') && sf.voice_09==true"
@if exp="mp.colorchange == ''"
@font color=&f.cv_color_kyuki cond="mp.char=='kyuki'"
@font color=&f.cv_color_syokuin cond="mp.char=='syokuin'"
@font color=&f.cv_color_kou cond="mp.char=='kou'"
@font color=&f.cv_color_byakko cond="mp.char=='byakko'"
@font color=&f.cv_color_youma cond="mp.char=='youma'"
@endif
@eval exp="f.cv_sb_hist = f.cv_sb_youma"
@if exp="sf.voice_on && mp.voice != ''"
@seopt buf=&f.cv_sb_youma volume=&sf.voice_vol_09
@eval exp="playVoice(f.cv_sb_youma, mp.voice)"
@reduce_bgmvol
@endif
@endif
@if exp="mp.char=='sonota_f' && sf.voice_10==true"
@if exp="mp.colorchange == ''"
@font color=&f.cv_color_sonota_f
@endif
@eval exp="f.cv_sb_hist = f.cv_sb_sonota_f"
@if exp="sf.voice_on && mp.voice != ''"
@seopt buf=&f.cv_sb_sonota_f volume=&sf.voice_vol_10
@eval exp="playVoice(f.cv_sb_sonota_f, mp.voice)"
@reduce_bgmvol
@endif
@endif
@if exp="(mp.char=='sonota_m' || mp.char=='yasai' || mp.char=='sakana' || mp.char=='saisyou' || mp.char=='seiyou' || mp.char=='nenpai') && sf.voice_11==true"
@if exp="mp.colorchange == ''"
@font color=&f.cv_color_sonota_m cond="mp.char=='kyuki'"
@font color=&f.cv_color_yasai cond="mp.char=='yasai'"
@font color=&f.cv_color_sakana cond="mp.char=='sakana'"
@font color=&f.cv_color_saisyou cond="mp.char=='saisyou'"
@font color=&f.cv_color_seiyou cond="mp.char=='seiyou'"
@font color=&f.cv_color_nenpai cond="mp.char=='nenpai'"
@endif
@eval exp="f.cv_sb_hist = f.cv_sb_sonota_m"
@if exp="sf.voice_on && mp.voice != ''"
@seopt buf=&f.cv_sb_sonota_m volume=&sf.voice_vol_11
@eval exp="playVoice(f.cv_sb_sonota_m, mp.voice)"
@reduce_bgmvol
@endif
@endif
@endmacro
;BGM音量を下げる
@macro name=reduce_bgmvol
@if exp="sf.voice_bgm_reduce_onoff"
@eval exp="kag.bgm.fade(%[time:1000,volume:100-sf.voice_bgm_reduce])" cond="!kag.skipMode && (kag.bgm.userchange != 1)"
@eval exp="kag.se[f.se_loop_buf].fade(%[time:1000,volume:100-sf.voice_bgm_reduce])" cond="!kag.skipMode && (kag.se[f.se_loop_buf].userchange != 1)"
@endif
@endmacro
;ボイス終了処理
@macro name=sv
@eval exp="waitVoiceStop()" cond="kag.autoMode"
@eval exp="fadeoutAllVoices()" cond="sf.voice_cut"
[eval exp="f.pv_char = ''"]
[eval exp="f.pv_voice = ''"]
@endmacro
;-----------------------------------------------------------------------------
;■ボイス終了兼クリック待ち命令 [ps]
;クリック待ち命令の[p]命令を拡張したもので、[p]命令の代わりに用います。
;■ボイス終了兼改行命令 [rs]
;改行命令の[r]命令を拡張したもので、ボイスの同時再生時に用います。
;-----------------------------------------------------------------------------
[macro name=ps]
@endhact
@font color=default
[p]
[sv]
[endmacro]
[macro name=rs]
@endhact
@font color=default
[r]
[endmacro]
命令の記述方法
以下に命令の記述形式を示します。
BGM再生・クロスフェード、停止、フェードアウト命令:
(引数は全て[playbgm][xchgbgm][fadeoutbgm]と同じです)
[splaybgm storage="BGMファイル"]
[sxchgbgm storage="BGMファイル"]
[sstopbgm]
[sfadeoutbgm]
効果音再生・停止・フェードアウト命令:
(引数は[playse][stopse][fadeoutse]と同じです)
通常サウンドはバッファ0、ループ系サウンドはバッファ1、システム音はバッファ2で再生するようにします。(変数で定義してます)
[splayse storage="SEファイル" buf="再生バッファ"]
[sstopse]
[sfadeoutse buf=&f.se_loop_buf time=&f.se_fadetime]
ボイス再生命令:
[pv char="登録キャラ名" voice="ボイスファイル名"]
nostop=true属性を追加すれば、同時再生可能です。
重複再生用。この属性値があるボイスが再生したとしても、
再生中のボイス(前の命令のボイス)をストップしません。
ま、正確にはnostop属性に何か値があれば勝手にtrueと判定します。
(同時再生の使用例)
同時再生時には、delayで文字速度を瞬間表示にして使用します。
[er][delay speed=nowait][pv char="ui" voice="ui_syao_a04_008"]右伊「あ!」[rs]
[pv char="sai" voice="sai_syao_a04_009" nostop=true]左伊「!」[delay
speed=user][ps]
ボイス停止命令:
[sv]
ボイス再生・停止は
[pv char="登録キャラ名" voice="ボイスファイル名"]台詞[sv]
このように記述します。
詳細はサンプルをご覧下さい。
ボイス終了兼クリック待ち命令:
クリック待ち命令の[p]命令を拡張したもので、[p]命令の代わりに用います。
[ps]
ボイス終了兼改行命令:
改行命令の[r]命令を拡張したもので、ボイスの同時再生時に用います。
キャラの発言が終了して、かつ改行させたい時に使用します。
[rs]
[sv]は[ps]に含んでいますので、実際に使用する場合、以下のような記述形式になります。
これを参考にして下さい。
[er][pv char="yue" voice="yue_syao_a03_016"]ユエ「あ、ありがとう......その、無茶はしないから、安心してくれ......」[ps] [er]そしてユエは少し頬を赤くして、こう言った。[ps] [er][pv char="yue" voice="yue_syao_a03_017"]ユエ「でも、止水が私のことを心配してくれるのは、とても嬉し――」[ps]
残作業のメモ
- 各キャラクター使用サウンドバッファ、フォント色を各ゲームごとに割り当てます。
- 変数定義で、使用するバッファ分ほど、設定画面で用いるための変数を追加します。
- マクロ[pv_playpart]の定義内部に、各キャラごとにフォントや再生バッファを決める部分を記述しています。ここをキャラ分ほど修正します。
テスト実行例
テスト用の実行例として、以下のような例を挙げておきます。
サンプルに入っているものと似たようなものです。(サンプルでは、背景画像などを含んで表示しています)
*restart_point_voice|ボイス機能テスト [eval exp="sf.voice_bgm_reduce = 40"] [eval exp="sf.voice_bgm_reduce_onoff = 0"] [splaybgm storage="04_mitikusa"] [cm]こちらは、ボイス表示機能のサンプルです。[ps][r] この機能では、キャラごとにバッファを分けたボイスの再生、停止などの基本的な機能の他に、ボイス再生中にBGMやループ系効果音の音量を下げたり、キャラのフォント色を変えたり、文末でボイスを終了させるかどうかの設定をすることができます。[ps][r] また、効果音やBGM系の命令も含んでいますので、ボイスだけでなく効果音やBGMなどの音に関する基本的命令も含まれています。[ps][r] [er]さて、それでは実際に主な機能を見てみましょう。[ps][r] まずは、通常ボイス再生。[ps][r] [pv char="yue" voice="yue_syao_a03_016"]ユエ「あ、ありがとう......その、無茶はしないから、安心してくれ......」[ps][r] ボイス再生中にはBGMやループ系効果音の音量を下げる設定にして、再生します。[ps][r] [eval exp="sf.voice_bgm_reduce_onoff = 1"] [pv char="yue" voice="yue_syao_a03_017"]ユエ「でも、止水が私のことを心配してくれるのは、とても嬉し――」[ps][r] [pv char="yue" voice="yue_syao_a03_019"]ユエ「......ふ、ふふふふ......」[ps][r] このように、フェードでゆっくりと違和感なく音量の上げ下げをしてくれます。[ps][r] ここでは音量が下がっているのがはっきり分かるように、ちょっと大げさに音量を下げています。[ps][r] 音量の下げ幅も、変数に下げ幅の値を入れるだけで設定できます。[ps][r] BGM音量の下げ幅については、BGMの音量次第で適切な下げ幅も変わりますので、コンフィグ画面などで下げ幅を決めるボタンもしくはスライダーを設置してもいいかと思います。[ps][r] [er]ボイスを同時に再生することもできます。[ps][r] [delay speed=nowait][pv char="yue" voice="yue_syao_a03_020"]ユエ「どうやら、化け物の身の心配をする前に、己の身の心配をする方がいいみたいだな......」[rs] [pv char="syao" voice="syao_syao_b06_2_022" nostop=true]暁「この鏡の力できっと人間になれるはずなんだ。だけど、どうやって使えばいいのかボクには分からないの」[delay speed=user][ps][r] 文字表示を瞬間表示にして使うことを想定しています。(瞬間表示でなければ、一人分のセリフを描画し終えてから次のセリフが表示するために見栄えがしないのと、システム的にも問題が出ますので、瞬間表示をするようにしましょう)[ps][r] また、先に終わったボイスに合わせてBGMの音量下げも元に戻るので、二人ぐらいが同じセリフを言うぐらいの長さに調整するようにしましょう。[ps][r] [er]それでは、ループ系効果音を同時に流しながらボイスを再生してみましょう。[ps][r] [splayse storage="digging3_loop" loop=true buf=&f.se_loop_buf] [pv char="syao" voice="syao_syao_b06_2_023"]暁「だから、止水君もボクが人間になるの、手伝ってくれるよね?」[ps][r] ループ系効果音も同時に音量が下げられています。[ps][r] 判定は、f.se_loop_bufという変数で指定したバッファナンバーで再生されるループ音は、BGMと同じく音量を下げるようになります。[ps][r] [splayse storage="ask01" buf=&f.se_sys_buf] [pv char="syao" voice="syao_syao_b06_2_024"]暁「うわぁっ、ありがとう止水君っ」[ps][r] ループ系の効果音の音量は下がっても、通常の効果音の音量は下がりません。[ps][r] [er]ループ系効果音のみフェードアウトで消します。[ps][r] [sfadeoutse buf=&f.se_loop_buf time=&f.se_fadetime] ループ系効果音が消えていってます。[ps][r] [pv char="sai" voice="sai_yue_z02_009"]左伊「あたし達の仕事は、世国に来る妖魔に対処することです」[ps][r] BGMや効果音をフェードアウトなどでフェードしている最中は、ボイス再生によるBGMやループ系効果音の音量を下げる命令は無視します。[ps][r] なので、フェードアウトによるBGM音量を戻す「時間差」があったとしても、音量の整合性は取れていますのでご安心下さい。(←ここの整合性を取るのが結構苦労した部分です)[ps][r] [er]最後に連続でボイスを流すサンプルでも。[ps][r] [splayse storage="ask02" buf=&f.se_sys_buf] [pv char="sai" voice="sai_yue_z02_010"]左伊「でも......やっぱり今も、仕事はほとんどないです」[ps][r] BGMをフェードアウトしながらボイスを再生します。[ps][r] [sfadeoutbgm time=&f.bgm_fadetime] [pv char="ui" voice="ui_yue_z02_006"]右伊「わざわざここを襲ってくる妖魔など、もう皆無と言っていいですからね」[ps][r] [pv char="ui" voice="ui_yue_z02_007"]右伊「平和なのはいいことですが、やはりこうしていると剣が錆びる気がします」[ps][r] [er]説明は以上です。最初に戻ります[ps][r] [jump target="*restart_point_voice"][s]
6.5 セーブ・ロード画面
機能

- セーブ・ロード画面テンプレートとして利用可能
- オートセーブ機能付き(オートセーブ内容は、新しい順に表示されます)
- コメント、データ保護機能、データ削除機能有り
- 100個のセーブエントリと、10個のオートセーブエントリ
- キーボード操作対応
Config.tjsでの処理
初期状態から導入する場合、Config.tjsで修正する必要のある場所を以下に示します。
なお、これらは全て簡易メニューと重複しています。簡易メニューを導入されている場合はこちらは不要です。
- セーブ画像を使う場合、サムネイルを保存するように設定しておきます。(saveThumbnail = trueに)
- サムネイルの保存サイズも適切なものにします。デフォルトのサンプルのまま用いるのであれば、横118pixelで保存します。(thumbnailWidth = 118に)。大きさはどの数値でも問題ありませんので、適宜合うように調整して下さい。
- サムネイルの画質をよくするため、サムネイル保存モードで、24ビットにしておきます。(thumbnailDepth = 24)
- 利用可能な栞の数(numBookMarks)を適切な数値にしておきます。(ここでは110個と想定してます)
MainWindow.tjsでの処理
MainWindow.tjsの変数定義部分に以下の命令を追加します。
いくつか簡易メニューと重複している命令がありますので、簡易メニューを導入されている場合は不要な場合があります。
(なお、この命令は簡易メニューと重複しています。簡易メニューを導入されている場合はこちらは不要です)
var holdPeriodEventQueue = []; // 保留にされたムービーのピリオドイベントキュー
var isLeavePeriodEvent = false; // ムービーのピリオドイベントを保留にするかどうか
var isWaitPeriodEvent = false; // ムービーのピリオドイベント待ち状態かどうか
var waitedPeriodEventStorageName = void; // ピリオドイベント待ちをコールしたストレージ名
//↓--------------------------------------- ここから追加部分--
//mebius:変数追加分
var autosaveLoadedFlag = false;
//↑--------------------------------------- ここまで追加部分--
//------------------------------------------------------ コンストラクタ --
function KAGWindow(ismain = true, width = 0, height = 0)
{
function loadBookMark()、function saveBookMarkWithAsk()、function loadBookMarkWithAsk()に、以下の命令を追加します。
2箇所ほど、元々ある行の位置を変更する行(else文の中に入れる行)があるので注意して下さい。
オートセーブに対応します。
(なお、この命令は簡易メニューと重複しています。簡易メニューを導入されている場合はこちらは不要です)
function loadBookMark(num, loaduser = true)
{
// 栞番号 num からデータを読み出す
//↓--------------------------------------- ここから追加部分--
autosaveLoadedFlag = true;//mebius:オートセーブ読み込み直後に再び保存しない対策。
//↑--------------------------------------- ここまで追加部分--
return loadBookMarkFromFile(getBookMarkFileNameAtNum(num), loaduser);
}
//↓--------------------------------------- ここから追加部分--
function saveBookMarkWithAsk(num,autoon,autostr)//mebius:引数追加(autoon,autostr)
//↑--------------------------------------- ここまで追加部分--
{
// 栞番号 num に栞を設定する
// そのとき、設定するかどうかをたずねる
if(readOnlyMode) return false;
if(bookMarkProtectedStates[num]) return false;
var prompt = "栞 ";
//↓--------------------------------------- ここから追加部分--
//mebius:autoonがtrueならstrを番号として使う。
if(autoon)
prompt += autostr;
else
if(num < numBookMarks) prompt += (num+1);//元々あるこの行は、elseの中に入れます。
//↑--------------------------------------- ここまで追加部分--
if(bookMarkDates[num] != "") // bookMarkDates が空文字の場合は栞は存在しない
prompt += "「" + bookMarkNames[num] + "」";
prompt += "に「"+ pcflags.currentPageName + "」をはさみますか?";
var result = askYesNo(prompt);
if(result) return saveBookMark(num);
return false;
}
//↓--------------------------------------- ここから追加部分--
function loadBookMarkWithAsk(num,autoon,autostr)//mebius:引数追加(autoon,autostr)
//↑--------------------------------------- ここまで追加部分--
{
// 栞番号 num から栞を読み出す
// そのとき、読み出すかどうかをたずねる
if(num < numBookMarks && bookMarkDates[num] == "") // bookMarkDates が空文字の場合は栞は存在しない
return false;
var prompt = "栞 ";
//↓--------------------------------------- ここから追加部分--
//mebius:autoonがtrueならstrを番号として使う。
if(autoon)
prompt += autostr;
else
if(num < numBookMarks) prompt += (num+1);//元々あるこの行は、elseの中に入れます。
//↑--------------------------------------- ここまで追加部分--
prompt += "「"+ bookMarkNames[num] + "」をたどりますか?";
var result = askYesNo(prompt);
if(result) return loadBookMark(num);
return false;
}
マウスホイールに対応させます。
function onMouseWheel(shift, delta, x, y)に、以下の命令を追加します。
function onMouseWheel(shift, delta, x, y)
{
// ホイールが回転した
super.onMouseWheel(...);
//↓--------------------------------------- ここから追加部分--
//mebius:セーブ・ロード画面の場合
if (saveload_object.showing == true)
{
if (delta > 0)
{
if (sf.saveload_page == 0)
sf.saveload_page = 10;
else
sf.saveload_page--;
saveload_object.foreConfig.changePage(sf.saveload_page);
saveload_object.foreConfig.makePageButtons();
}
else if (delta < 0)
{
if (sf.saveload_page == 10)
sf.saveload_page = 0;
else
sf.saveload_page++;
saveload_object.foreConfig.changePage(sf.saveload_page);
saveload_object.foreConfig.makePageButtons();
}
return;
}
//↑--------------------------------------- ここまで追加部分--
if(!historyLayer.visible)
{
if(delta > 0)
showHistoryByKey(); // メッセージ履歴を表示
else if(System.getTickCount() - lastHistoryHiddenTick > 150)
onPrimaryClick(); // クリックをエミュレート
// ↑ tick を比較しているのは、メッセージ履歴を隠す操作とホイールを
// 手前に回す操作が連続した場合に勝手に読み進むのをある程度防ぐ仕掛け
}
else
{
// メッセージ履歴にイベントを垂れ流す
historyLayer.windowMouseWheel(shift, delta, x, y);
}
}
MainWindow.tjsの最後の方にある「タグハンドラ群の終わり」の後ぐらいに、以下の命令を追加します。
(なお、この命令は簡易メニューと重複しています。簡易メニューを導入されている場合はこちらは不要です)
//--------------------------------------- mebius:命令群 --
//----オートセーブ用---------------------------------------------------------
//オートセーブ対応セーブコマンド
function saveBookmarkAutoMode(num)
{
var result;
if(sf.save_ask)//確認をする場合
if (sf.saveload_page == 10)
{
var autonum = (sf.autosave_entry-num+100);
if (autonum<=0) autonum+=10;
result = kag.saveBookMarkWithAsk(num,true,"自動"+autonum);
}
else
result = kag.saveBookMarkWithAsk(num);
else//確認をしない場合
if (sf.saveload_page == 10)
result = kag.saveBookMark(num,true,"自動"+(sf.autosave_entry-num));
else
result = kag.saveBookMark(num);
if (result)
{
sf.new_savedata = num;
if(sf.qmenu_save_mode) kag.redrawQSaveMenu();
}
return result;
}
//オートセーブ対応ロードコマンド
function loadBookmarkAutoMode(num)
{
var result;
if(sf.save_ask)//確認をする場合
if (sf.saveload_page == 10)
{
var autonum = (sf.autosave_entry-num+100);
if (autonum<=0) autonum+=10;
result = kag.loadBookMarkWithAsk(num,true,"自動"+autonum);
}
else
result = kag.loadBookMarkWithAsk(num);
else//確認をしない場合
if (sf.saveload_page == 10)
result = kag.loadBookMark(num,true,"自動"+(sf.autosave_entry-num));
else
result = kag.loadBookMark(num);
return result;
}
saveload.ksの導入
以下のプラグインをsaveload.ksとして保存します。
これはサンプルにあるファイルと内容は同一です。
内容はコメントで記述していますので、座標や色、画像などを個別に修正してご利用下さい。
なお、本プラグインを作る際に、らんかさん作の「右クリックでの設定画面を TJS2 で実現するサンプル」をベースに使わせていただきました。
@if exp="typeof(global.saveload_object) == 'undefined'"
@iscript
// 右クリックでの設定画面を TJS2 で実現するサンプル
// 2002/6/14 版 改造 :
// システムメニュー実装
// +セーブデータ削除
// +トランジション対応バージョン
/* --
2008/07/08 CircleMebius修正版
-- */
//オートセーブについては、100~109のセーブエントリを使っています。
//オートセーブは、セーブ内容をコピーしたりするのではなく、100から順にセーブしてゆき、
//109を保存し終わったら100に戻るようになっています。
//sf.autosave_entryに最新のエントリポイントを保持していて、オートセーブをする度に+1されます。
//オートセーブ命令はマクロ内部[autosave]にて。
class RButtonLayer extends ButtonLayer
// parent に onClick イベントを送るようにしたボタンレイヤ
{
var tag;
function RButtonLayer(window, parent)
{
super.ButtonLayer(window, parent);
focusable = false;
}
function finalize()
{
super.finalize(...);
}
function onKeyDown(key, shift)
{
parent.onKeyDown(...);
}
function onClick()
{
// トランジション中ならスキップ
if(kag.transCount != 0)
{
kag.stopAllTransitions();
return;
}
super.onClick(...);
if(enabled)
parent.onButtonClick(this);
}
}
class RCheckBoxLayer extends CheckBoxLayer
// parent にイベントを送るようにしたチェックボックス
{
function RCheckBoxLayer(window, parent)
{
super.CheckBoxLayer(window, parent);
}
function finalize()
{
super.finalize(...);
}
function onMouseDown()
{
// マウスでクリックされた
super.onMouseDown(...);
if(enabled)
parent.onCheckBoxClick(this);
}
function onKeyDown(key, shift, process)
{
// キーが押された
if(process)
{
// スペースキーまたはエンターキー
if(key == VK_RETURN || key == VK_SPACE)
{
super.onKeyDown(...);
if(enabled)
parent.onCheckBoxClick(this);
}
else
{
parent.onKeyDown(...);
}
}
else
{
// process が false の場合は処理は行わない
parent.onKeyDown(...);
}
}
}
class SaveDataItemLayer extends KAGLayer
{
// 栞一個一個に対応するレイヤ
var num; // 栞番号
var bgColor = 0xff502013; // 背景色 ( 0xAARRGGBB )
var focusedColor = 0xffffffff;
var backImage;
var commentEdit; // コメントのエディット
var protectCheckBox; // 「データ保護」チェックボックス
var focused_cursolpos_x = 2;//フォーカスを受けた時のマウスカーソル位置
var focused_cursolpos_y = 2;
var quicksave = false;//クイックセーブの時はTrueになる
var autosave = false;//オートセーブの時はTrueになる
var data_exist = true;//データがない場合はfalseになる
var se;//効果音用。kagのseは用いずに、この内部で用いる場合に使用。
var se_buf;
var loadmode = true;
function SaveDataItemLayer(window, parent, num)
{
super.KAGLayer(window, parent);
var txtcolor = 0x000000;//文字色
se = new WaveSoundBuffer(this);
se.volume = sf.sysse_vol * 1000;
this.num = num;
if (num == -1) quicksave = true;//クイックセーブモードのエントリを挿入。-1の時はクイックセーブなし。
else if (num > 99) autosave = true;//100番目以降ならオートセーブモードにする
else quicksave = false;
setImageSize(340, 90); // サイズ設定
face = dfBoth;
//背景画像読み込み
var tmplayer = new global.Layer(window, parent);
if (autosave)//オートセーブのアイテム背景画像
tmplayer.loadImages("saveload_itembase_a");
else//通常のアイテム背景画像
tmplayer.loadImages("saveload_itembase");
copyRect(0, 0, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight);
invalidate tmplayer;
//fillRect(0, 0, imageWidth, imageHeight, bgColor);
setSizeToImageSize();
hitType = htMask;
hitThreshold = 0; // 全域不透過
cursor = kag.cursorPointed;
focusable = true; // フォーカスは受け取れる
protectCheckBox = new CheckBoxLayer(window, this);
protectCheckBox.width = 16;
protectCheckBox.height = 16;
protectCheckBox.color = 0xffffff;
protectCheckBox.opacity = 64;
protectCheckBox.textColor = txtcolor;
protectCheckBox.setPos(320, 73);
protectCheckBox.visible = true;
if (quicksave)//クイックセーブデータはチェックボックスは使用できない
{
protectCheckBox.enabled = false;
protectCheckBox.color = txtcolor;
}
if (autosave)//オートセーブデータはチェックボックスは使用できない
{
protectCheckBox.enabled = false;
protectCheckBox.color = txtcolor;
}
//コメント欄
commentEdit = new EditLayer(window, this);
commentEdit.setPos(165, 73);
commentEdit.width = 105;
commentEdit.height = 16;
commentEdit.color = 0xffffff;
commentEdit.opacity = 64;
commentEdit.textColor = txtcolor;
commentEdit.visible = true;
font.height = 12;
//font.face = "MS Pゴシック";
face = dfBoth;
//drawText(420, 35, "データ保護", txtcolor);
// エントリ番号を表示
var str = string (num + 1);
var ty = font.getTextHeight(str);
//drawText(6, ( imageHeight - ty ) \ 2, str, 0);
if (quicksave)
{
str = "仮"; //クイックセーブデータは「仮」と表示する
drawText(320, 2, str, 0);
}
else if (autosave)
{
var entry = sf.autosave_entry - num + 100;
if (entry <= 0) entry+=10;
if (entry == 10)
str = "自動" + string (entry); //自動セーブデータは「自動」と表示する
else
str = "自動 " + string (entry); //位置調整のため半角スペースを追加
drawText(301, 2, str, 0);
}
else
drawText(320, 2, str, 0);
// サムネイル画像を読み込む
var tmplayer = new global.Layer(window, parent);
var tnname = kag.getBookMarkFileNameAtNum(num);
if(Storages.isExistentStorage(tnname) && kag.bookMarkDates[num] != '')
{
tmplayer.loadImages(tnname);
protectCheckBox.checked = kag.bookMarkProtectedStates[num];
if(kag.scflags.bookMarkComments !== void)
commentEdit.text = kag.scflags.bookMarkComments[num];
}
else
{
// サムネイル画像が手動で削除されたときの対処
if(kag.bookMarkDates[num] != '')
{
kag.bookMarkNames[num] = ''; // 栞名
kag.bookMarkDates[num] = ''; // 保存年月日
if(kag.scflags.bookMarkComments !== void)
kag.scflags.bookMarkComments[num] = ''; // コメント
kag.setBookMarkMenuCaptions();
}
tmplayer.loadImages("dummy"); // No Data
//データ無しの場合の表示
//tmplayer.setImageSize(118, 89);
//var str = "データなし";
//var tx = tmplayer.font.getTextWidth(str);
//tmplayer.drawText((tmplayer.imageWidth - tx) \ 2, 40,
// str, 0xffffff);
}
copyRect(1, 1, tmplayer, 0, 0, tmplayer.imageWidth, tmplayer.imageHeight);
invalidate tmplayer;
// 栞の保存名を表示
font.height = 12;
var str = kag.bookMarkNames[num];
if(str == '') str = '情報なし';
if (quicksave) str = "(仮保存)"+ str; //クイックセーブデータは「仮保存」を追加する
if (autosave) str = "(自動)"+ str; //オートセーブデータは「自動」を追加する
//シナリオ中の日付表示をする場合、ラベルに':'(半角コロン)の区切り記号をつけたら、
//その区切り記号より前を「シナリオ中の日付」、それより後を「サブタイトル」と設定することができます。
//簡易メニューでもこの機能を使う場合、
//MainWindow.tjsのfunction makeQSaveMenuHint(num,x,y)内部も同様に変更して下さい。
//var sepnum = str.indexOf(":");
//シナリオ中の日付表示
/*
if (sepnum != -1)
{
drawText(127, 18, str.substring(0,sepnum), txtcolor);
str = str.substring(sepnum+1);
}
*/
//サブタイトル表示
drawText(141, 35, str, txtcolor);
// 日付を表示
if(kag.bookMarkDates[num] == '')
{
str = " ----/--/-- --:--", commentEdit.enabled = false;
data_exist = false;
}
else
{
if (sf.new_savedata == num && sf.saveload_page != 10)//最新のものは"最新"を追加
drawText(185, 51, "(最新)", 0xff0000);
if (num == ((sf.autosave_entry == 0)? 10:sf.autosave_entry) + 99)//オートセーブの最新のものには(新)を追加
drawText(185, 51, "(新)", 0xff0000);
if (num == (sf.autosave_entry + 100))//オートセーブの最古のものには(古)を追加
drawText(185, 51, "(古)", 0xff0000);
str = kag.bookMarkDates[num];
}
// コメント :
drawText(231, 51, str, txtcolor);
//drawText(180, 63, "Comments :", txtcolor);
}
function finalize()
{
invalidate commentEdit;
invalidate protectCheckBox;
invalidate se;
super.finalize(...);
}
function saveToSystemVariable()
{
// 状態をシステム変数に記録する
if(kag.scflags.bookMarkComments === void)
kag.scflags.bookMarkComments = [];
kag.scflags.bookMarkComments[num] = commentEdit.text;
kag.bookMarkProtectedStates[num] = protectCheckBox.checked;
}
function onPaint()
{
super.onPaint(...);
// update() が呼ばれた後、描画の直前に呼ばれる
face = dfBoth;
if(focused)
{
//フォーカスを得たら、マウスカーソルを移動させる
//setCursorPos(focused_cursolpos_x, focused_cursolpos_y);
fillRect(0, 0, imageWidth, 1, focusedColor);
fillRect(0, 0, 1, imageHeight - 1, focusedColor);
fillRect(imageWidth - 1, 1, 1, imageHeight - 1, focusedColor);
fillRect(0, imageHeight - 1, imageWidth - 2, 1, focusedColor);
}
else
{
fillRect(0, 0, imageWidth, 1, bgColor);
fillRect(0, 0, 1, imageHeight - 1, bgColor);
fillRect(imageWidth - 1, 1, 1, imageHeight - 1, bgColor);
fillRect(0, imageHeight - 1, imageWidth - 2, 1, bgColor);
}
}
function onFocus()
{
// フォーカスを得た
super.onFocus(...);
update();
}
function onBlur()
{
// フォーカスを失った
super.onBlur(...);
update();
}
function onHitTest(x, y, process)
{
if(process)
{
// 右ボタンが押されていたときにイベントを透過
if(System.getKeyState(VK_RBUTTON))
super.onHitTest(x, y, false);
else
super.onHitTest(x, y, true);
}
}
function onKeyDown(key, shift, process)
{
// トランジション中ならスキップ
if(kag.transCount != 0)
{
kag.stopAllTransitions();
return;
}
// キーが押された
if(process && key == VK_RETURN || key == VK_SPACE)
{
// スペースキーまたはエンターキー
parent.onKeyDown(key, shift, false);
saveToSystemVariable();
parent.onLoadOrSave(num);
}
else
{
// process が false の場合は処理は行わない
parent.onKeyDown(...);
}
}
function onMouseDown(x, y, button, shift)
{
// トランジション中ならスキップ
if(kag.transCount != 0)
{
kag.stopAllTransitions();
return;
}
super.onMouseDown(...);
if(button == mbLeft)
{
focus();
saveToSystemVariable();
parent.onLoadOrSave(num);
}
}
function onMouseEnter(x, y, button, shift)
{
// トランジション中ならスキップ
if(kag.transCount != 0)
{
kag.stopAllTransitions();
return;
}
super.onMouseEnter(...);
if ((loadmode && data_exist) || (!loadmode && !protectCheckBox.checked))
{
if (sf.sysse_on)
{
se.open("onenter03.ogg");
se.play("onenter03.ogg");
}
fillRect(0, 0, imageWidth, 1, focusedColor);
fillRect(0, 0, 1, imageHeight - 1, focusedColor);
fillRect(imageWidth - 1, 1, 1, imageHeight - 1, focusedColor);
fillRect(0, imageHeight - 1, imageWidth - 2, 1, focusedColor);
}
}
function onMouseLeave(x, y, button, shift)
{
// トランジション中ならスキップ
if(kag.transCount != 0)
{
kag.stopAllTransitions();
return;
}
super.onMouseLeave(...);
fillRect(0, 0, imageWidth, 1, bgColor);
fillRect(0, 0, 1, imageHeight - 1, bgColor);
fillRect(imageWidth - 1, 1, 1, imageHeight - 1, bgColor);
fillRect(0, imageHeight - 1, imageWidth - 2, 1, bgColor);
}
}
class RClickConfigLayer extends Layer // 設定画面レイヤ
{
var saveDataItems;
var state = -1; // 0 = メインメニュー 1 = ロード画面 2 = セーブ画面
var owner; // RClickConfigPlugin オブジェクトへの参照
//var currentPage = 0; // セーブデータ選択中に表示中のページ
var deleteButton; // データ削除 ボタン
var returnButton; // もどる ボタン
var pageButtons; // セーブデータのページボタン
var askButtonT; // 確認有り ボタン
var askButtonF; // 確認有り ボタン
function RClickConfigLayer(win, par, owner)
{
super.Layer(win, par);
this.owner = owner;
// レイヤの状態を初期化
setImageSize(800, 600);
clearBase();
setSizeToImageSize();
setPos(0, 0);
hitType = htMask;
hitThreshold = 0; // 全域不透過
}
function finalize()
{
clear();
invalidate deleteButton if deleteButton !== void;
invalidate returnButton if returnButton !== void;
invalidate pageButtons if pageButtons !== void;
invalidate askButtonT if askButtonT !== void;
invalidate askButtonF if askButtonF !== void;
super.finalize(...);
}
function clearBase()
{
// 背景画像の読み込み
var file;
if(state == 1) file = "saveload_back_load"; // ロード画面
else if(state == 2) file = "saveload_back_save"; // セーブ画面
else if(state >= 10) file = "saveload_back_delete"; // 削除画面
else if(state == -1) file = ""; // 初期状態の時は何もしない
else file = ""; // メニュー画面
if(file != "")
{
loadImages(file);
face = dfBoth;
}
}
function makeMainMenu()
{
// メインメニューの表示
if(state != 0)
{
state = 0;
clear();
}
}
function clearSaveDataItems()
{
// セーブデータ表示のクリア
if(saveDataItems !== void)
{
for(var i = 0; i < saveDataItems.count; i++)
{
saveDataItems[i].saveToSystemVariable();
invalidate saveDataItems[i];
}
saveDataItems = void;
kag.setBookMarkMenuCaptions();
}
}
function makeSaveDataItems()
{
// セーブデータの表示
clearSaveDataItems();
saveDataItems = [];
//ここでセーブデータの表示をしています。座標などはここを修正します。
for(var i = 0; i < 10; i++)
{
var obj = new SaveDataItemLayer(window, this, sf.saveload_page * 10 + i);
saveDataItems[i] = obj;
var entry;
if (sf.saveload_page == 10)//オートセーブの場合、表示位置を修正
{
entry = sf.autosave_entry - i-1;
if (entry < 0) entry+=10;//負になった場合、+10して整数にする
}
else
entry = i;
if (entry<5)
{
obj.setPos(31, 87 + entry*92);//左側に表示する各エントリの座標
}
else
{
obj.setPos(372, 87 + (entry-5)*92);//右側に表示する各エントリの座標
}
obj.visible = true;
obj.loadmode = true if state == 1;
obj.loadmode = false if state == 2;
}
}
function clearPageButtons()
{
// ページボタンのクリア
if(pageButtons !== void)
{
for(var i = 0; i < pageButtons.count; i++)
{
invalidate pageButtons[i];
}
pageButtons = void;
}
}
function clearAskButton()
{
// 確認ボタンのクリア
if(askButtonT !== void)
{
invalidate askButtonT;
askButtonT = void;
}
if(askButtonF !== void)
{
invalidate askButtonF;
askButtonF = void;
}
}
function clearDeleteButton()
{
// 削除ボタンのクリア
if(deleteButton !== void)
{
invalidate deleteButton;
deleteButton = void;
}
}
function makePageButtons()
{
// ページボタンを作成する
clearPageButtons();
pageButtons = [];
for(var i = 0; i < 11; i++)
{
var obj = new RButtonLayer(window, this);
pageButtons[i] = obj;
obj.showFocusImage = true;
// 現在選択されているボタンは画像を変える
var file = sf.saveload_page == i ? "saveload_page%02dfon".sprintf(i+1) : "saveload_page%02df".sprintf(i+1);
obj.loadImages(file);
obj.top = i * 35 + 78;
obj.left = 730;
obj.focusable = true;
// 現在選択されているボタンはクリック不可
obj.enabled = sf.saveload_page != i;
obj.visible = true;
obj.tag = 'page';
obj.page = i;
}
}
function changePage(newpage)
{
// ページを変更するとき
if(pageButtons !== void)
{
pageButtons[sf.saveload_page].loadImages("saveload_page%02df".sprintf(sf.saveload_page+1));
pageButtons[sf.saveload_page].enabled = true;
sf.saveload_page = newpage;
pageButtons[newpage].loadImages("saveload_page%02dfon".sprintf(sf.saveload_page+1));
pageButtons[newpage].enabled = false;
// 裏画面に変更後の情報をコピー
owner.backCopyConfig();
makeSaveDataItems();
}
//ページが書き変わると、フォーカスを初期化
kag.focusedLayer = returnButton;
}
function changeAskMode(newpage)
{
// ページを変更するとき
if(pageButtons !== void)
{
if(sf.save_ask) sf.save_ask = false;
else sf.save_ask = true;
//画像書き換え
clearAskButton();
clearBase();
makeAskButton();
// 裏画面に変更後の情報をコピー
owner.backCopyConfig();
}
}
function makeDeleteButton()
{
// 「データ削除」ボタンを作成
if(deleteButton === void)
{
deleteButton = new RButtonLayer(window, this);
deleteButton.showFocusImage = true;
deleteButton.loadImages("saveload_menu03f");
deleteButton.left = 31;
deleteButton.top = 567;
deleteButton.focusable = true;
}
deleteButton.visible = true;
}
function makeReturnButton()
{
// 「戻る」ボタンを作成
if(returnButton === void)
{
returnButton = new RButtonLayer(window, this);
returnButton.showFocusImage = true;
returnButton.loadImages("saveload_menu00f");
returnButton.left = 692;
returnButton.top = 35;
returnButton.focusable = true;
}
returnButton.visible = true;
}
function makeAskButton()
{
// 「メッセージ確認有り」ボタンを作成
if(askButtonT === void && askButtonF === void)
{
var strT;
var strF;
if(sf.save_ask)//確認有りの場合
{
strT = "saveload_menu01onf";
strF = "saveload_menu02f";
}
else//確認無しの場合
{
strT = "saveload_menu01f";
strF = "saveload_menu02onf";
}
askButtonT = new RButtonLayer(window, this);
askButtonT.showFocusImage = true;
askButtonT.loadImages(strT);
askButtonT.left = 586;
askButtonT.top = 567;
askButtonT.enabled = !sf.save_ask;
askButtonT.focusable = !sf.save_ask;
askButtonF = new RButtonLayer(window, this);
askButtonF.showFocusImage = true;
askButtonF.loadImages(strF);
askButtonF.left = 648;
askButtonF.top = 567;
askButtonF.enabled = sf.save_ask;
askButtonF.focusable = sf.save_ask;
}
askButtonT.visible = true;
askButtonF.visible = true;
}
function makeLoadMenu()
{
// 「ロード」メニュー
if(state != 1)
{
state = 1;
clear();
makeReturnButton();
makeSaveDataItems(sf.saveload_page);
makeAskButton();
makeDeleteButton();
makePageButtons();
font.height = 24;
//drawText(30, 30, "ロード", 0xffffff);
}
}
function makeSaveMenu()
{
// 「セーブ」メニュー
if(state != 2)
{
state = 2;
clear();
makeReturnButton();
makeSaveDataItems(sf.saveload_page);
makeAskButton();
makeDeleteButton();
makePageButtons();
//font.height = 24;
//drawText(30, 30, "セーブ", 0xffffff);
}
}
function makeDeleteMenu()
{
// 「データ削除」メニュー
if(state < 10)
{
/*効果音を鳴らす場合
if (sf.sysse_on)
{
var sedata =%[];
sedata.storage = "menu_in03";
kag.se[se_buf].play(sedata);
}
*/
state *= 10;
clear();
makeReturnButton();
makeSaveDataItems(sf.saveload_page);
makeAskButton();
makePageButtons();
//font.height = 24;
//drawText(30, 30, "削 除", 0xffffff);
}
}
function clear()
{
// 画面上のボタン類をすべて非表示にするか、無効化する
clearBase();
clearSaveDataItems();
clearPageButtons();
clearAskButton();
clearDeleteButton();
}
function saveToSystemVariable()
{
// システム変数にデータを保存する必要があるとき
if(saveDataItems !== void)
{
for(var i = 0; i < saveDataItems.count; i++)
saveDataItems[i].saveToSystemVariable();
kag.setBookMarkMenuCaptions();
}
}
function onButtonClick(sender)
{
// ボタンが押されたとき
switch(sender)
{
/*
case saveButton: // セーブ ボタン
owner.backCopyConfig();
owner.backConfig.makeSaveMenu();
kag.process('', '*saveload_2');
break;
case loadButton: // ロード ボタン
owner.backCopyConfig();
owner.backConfig.makeLoadMenu();
kag.process('', '*saveload_2');
break;
*/
case deleteButton: // 削除モード ボタン
owner.backCopyConfig();
owner.backConfig.makeDeleteMenu();
kag.process('', '*saveload_2');
break;
case returnButton: // 戻る ボタン
returnMenu();
break;
case askButtonT:
case askButtonF:
changeAskMode();
break;
default:
if(sender.tag == 'page')
{
// page ボタン
changePage(sender.page);
}
}
}
function onCheckBoxClick(sender)
{
// チェックボックスが変更されたとき
switch(sender)
{
case chCheckBox: // ページ末まで(通常)
kag.chNonStopToPageBreakItem.click();
break;
case ch2ndCheckBox: // ページ末まで(既読)
kag.ch2ndNonStopToPageBreakItem.click();
break;
case chAntialiasCheckBox: // アンチエイリアス
kag.chAntialiasMenuItem.click();
}
}
function delBookMark(num)
{
// 栞の削除確認ダイアログ
// bookMarkDates が空の場合は栞は存在しない
if(num < kag.numBookMarks && kag.bookMarkDates[num] == "")
return false;
if(kag.bookMarkProtectedStates[num]) return false;
var prompt = "記録 ";
if(num < kag.numBookMarks) prompt += (num + 1);
prompt += " を削除しますか?";
var result;
if(!sf.save_ask) result = 1;
else
{
/*効果音を鳴らす場合
if (sf.sysse_on)
{
var sedata =%[];
sedata.storage = "ask02.ogg";
kag.se[se_buf].play(sedata);
}
*/
result = askYesNo(prompt);
}
if(result) // 「YES」のとき
{
/*効果音を鳴らす場合
if (sf.sysse_on)
{
var sedata =%[];
sedata.storage = "decide02.ogg";
kag.se[se_buf].play(sedata);
}
*/
kag.bookMarkNames[num] = ""; // 栞名を消去
kag.bookMarkDates[num] = ""; // 保存年月日を消去
kag.setBookMarkMenuCaptions();
if (sf.new_savedata == num)//最新マークだった場合、最新の情報だということを削除
{
sf.new_savedata = -1;
}
return true;
}
return false;
}
function onLoadOrSave(num)
{
// 番号 num をセーブまたはロード
if(state == 1)
{
// ロード
var result;
result = kag.loadBookmarkAutoMode(num);
}
else if(state == 2)
{
// セーブ
var result;
result = kag.saveBookmarkAutoMode(num);
if(result)
{
sf.new_savedata = num;
clearSaveDataItems();
if(kag.scflags.bookMarkComments !== void)
kag.scflags.bookMarkComments[num] = ''; // コメントをクリア
makeSaveDataItems(); // 表示を更新
// 裏画面に変更後の情報をコピー
owner.backCopyConfig();
}
}
else
{
// 削除
if(delBookMark(num))
{
clearSaveDataItems();
if(kag.scflags.bookMarkComments !== void)
kag.scflags.bookMarkComments[num] = ''; // コメントをクリア
makeSaveDataItems(); // 表示を更新
// 裏画面に変更後の情報をコピー
owner.backCopyConfig();
}
}
}
function show()
{
// レイヤを表示する
visible = true;
setMode();
focus();
kag.focusedLayer = returnButton;
}
function hide(opt)
{
// レイヤを隠す
if (opt) removeMode(); // 表画面のモーダルを解除
visible = false;
clear();
state = -1;
}
function returnMenu()
{
// 右クリック, ESC キー,「戻る」ボタンでの戻り先判定
owner.backCopyConfig();
if(state > 9)
{
// 削除画面が表示されている場合は元の画面へ戻る
state \= 10;
if (state == 1) owner.backConfig.makeLoadMenu();
else owner.backConfig.makeSaveMenu();
kag.process('', '*saveload_2');
}
else if(!state || f.rclickmode)
{
// メインメニューが表示されているときか、
// セーブ・ロード画面を直接呼び出した場合は閉じる
owner.onConfigClose();
}
else
{
// 直接呼び出されておらず、セーブ・ロード画面が
// 表示されている場合はメインメニューへ戻る
owner.backConfig.makeMainMenu();
kag.process('', '*saveload_2');
}
}
function onKeyDown(key, shift)
{
// トランジション中
if(kag.transCount != 0)
{
// Enter キーまたはスペースキーが押されたらスキップ
if(key == VK_RETURN || key == VK_SPACE)
kag.stopAllTransitions();
return;
}
if (key == VK_PRIOR)
{
if (sf.saveload_page == 0)
sf.saveload_page = 10;
else
sf.saveload_page--;
owner.foreConfig.changePage(sf.saveload_page);
owner.foreConfig.makePageButtons();
return;
}
else if (key == VK_NEXT)
{
if (sf.saveload_page == 10)
sf.saveload_page = 0;
else
sf.saveload_page++;
owner.foreConfig.changePage(sf.saveload_page);
owner.foreConfig.makePageButtons();
return;
}
// ESC キーが押されたら戻り先判定
if(key == VK_ESCAPE) returnMenu();
super.onKeyDown(...);
}
function onMouseDown(x, y, button, shift)
{
// トランジション中
if(kag.transCount != 0)
{
// 左クリックされたらスキップ
if(button == mbLeft) kag.stopAllTransitions();
return;
}
// 右クリックされたら戻り先判定
if(button == mbRight) returnMenu();
}
}
class RClickConfigPlugin extends KAGPlugin // 「右クリック設定」プラグインクラス
{
var window; // ウィンドウへの参照
var foreConfig; // 設定レイヤ(表画面)
var backConfig; // 設定レイヤ(裏画面)
var hmes = false; // 「メッセージを消す」用フラグ
var qmenu_enabled_temp = false;
var showing = false;
function RClickConfigPlugin(window)
{
// RClickPlugin コンストラクタ
super.KAGPlugin(); // スーパークラスのコンストラクタを呼ぶ
this.window = window; // window への参照を保存する
}
function finalize()
{
invalidate foreConfig if foreConfig !== void;
invalidate backConfig if backConfig !== void;
super.finalize(...);
}
function show()
{
// 表画面と裏画面に設定レイヤを作成
if(foreConfig === void)
foreConfig = new RClickConfigLayer(window, kag.fore.base, this);
if(backConfig === void)
backConfig = new RClickConfigLayer(window, kag.back.base, this);
// 重ね合わせ順序はメッセージ履歴よりも奥
// ※雪プラグインとか使っている場合は
// 重ならないように指定数値を調整したほうが良いかも
foreConfig.absolute = 2000000-1;
backConfig.absolute = foreConfig.absolute;
// まだ非表示にしておく
foreConfig.visible = false;
backConfig.visible = false;
if(f.rclickmode == 1)
{
// 直接「ロード」メニューを呼ぶ
foreConfig.makeLoadMenu();
backConfig.makeLoadMenu();
}
else if(f.rclickmode == 2)
{
// 直接「セーブ」メニューを呼ぶ
foreConfig.makeSaveMenu();
backConfig.makeSaveMenu();
}
//簡易メニューは使用できないようにする。
qmenu_enabled_temp = f.qmenu_enabled;
f.qmenu_enabled = false;
showing = true;
}
function setConfigTrans(opt)
{
// 画面を表示するか非表示にするかの設定
backConfig.visible = opt;
foreConfig.removeMode(); // 表画面のモーダルを解除
}
function backCopyConfig()
{
// 表画面のセーブデータの状態を保存
foreConfig.saveToSystemVariable();
// 必要な表画面の情報を裏画面にコピーする
backConfig.visible = foreConfig.visible;
backConfig.state = foreConfig.state;
//backConfig.currentPage = foreConfig.currentPage;
var fc = foreConfig.saveDataItems;
var bc = backConfig.saveDataItems;
// 裏画面を更新する
if(fc !== void)
{
if(backConfig.state == 1) backConfig.makeLoadMenu();
else if(backConfig.state == 2) backConfig.makeSaveMenu();
else backConfig.makeDeleteMenu();
}
if(fc !== void && bc !== void)
{
for(var i = 0; i < fc.count; i++)
{
bc[i].commentEdit.text = fc[i].commentEdit.text;
bc[i].protectCheckBox.checked = fc[i].protectCheckBox.checked;
bc[i].num = fc[i].num;
}
}
}
function foreCopyConfig()
{
// 必要な裏画面の情報を表画面にコピーする
foreConfig.visible = backConfig.visible;
foreConfig.state = backConfig.state;
//foreConfig.currentPage = backConfig.currentPage;
var fc = foreConfig.saveDataItems;
var bc = backConfig.saveDataItems;
// 表画面を更新する
if(bc !== void)
{
if(foreConfig.state == 1) foreConfig.makeLoadMenu();
else if(foreConfig.state == 2) foreConfig.makeSaveMenu();
else foreConfig.makeDeleteMenu();
}
if(bc !== void && fc !== void)
{
for(var i = 0; i < bc.count; i++)
{
fc[i].commentEdit.text = bc[i].commentEdit.text;
fc[i].protectCheckBox.checked = bc[i].protectCheckBox.checked;
fc[i].num = bc[i].num;
}
}
}
function onConfigClose()
{
// 設定レイヤを閉じる準備
f.rclickmode = 0; // 念のため呼び出しフラグを初期化
f.qmenu_enabled = qmenu_enabled_temp;//簡易メニューの使用状態を元に戻す
setConfigTrans(false); // 裏画面を非表示
window.trigger('config'); // 'config'トリガを発動する
}
function closeConfig()
{
// 設定レイヤを完全に閉じる
foreConfig.hide(true) if foreConfig !== void;
backConfig.hide(false) if backConfig !== void;
showing = false;
}
// 以下、KAGPlugin のメソッドのオーバーライド
function onStore(f, elm)
{
}
function onRestore(f, clear, elm)
{
// 栞を読み出すとき
closeConfig();
}
function onStableStateChanged(stable)
{
}
function onMessageHiddenStateChanged(hidden)
{
}
function onCopyLayer(toback)
{
// レイヤの表←→裏の情報のコピー
// backlay タグやトランジションの終了時に呼ばれる
if(toback)
if(foreConfig !== void) backCopyConfig(); // 表→裏
else
if(backConfig !== void) foreCopyConfig(); // 裏→表
}
function onExchangeForeBack()
{
// 裏と表の管理情報を交換
if (foreConfig !== void)
{
var tmp;
tmp = backConfig;
backConfig = foreConfig;
foreConfig = tmp;
}
}
function onSaveSystemVariables()
{
// セーブデータの情報を保存
if(foreConfig !== void) foreConfig.saveToSystemVariable();
}
}
kag.addPlugin(global.saveload_object = new RClickConfigPlugin(kag));
/* --- 「メッセージを消す」でちらつかせないための処理 --- */
kag.hideMessageLayerByUser2 = kag.hideMessageLayerByUser;
kag.hideMessageLayerByUser = function()
{
// 通常処理
hideMessageLayerByUser2();
// 最後に設定レイヤを消す
with(.saveload_object) if(.hmes) .closeConfig();
}
incontextof kag;
kag.showMessageLayerByUser2 = kag.showMessageLayerByUser;
kag.showMessageLayerByUser = function()
{
// 先に設定レイヤを表示する
with(.saveload_object)
{
if(messageLayerHiding && .hmes)
{
.show();
.foreConfig.show();
.hmes = false;
}
}
// 通常処理
showMessageLayerByUser2();
}
incontextof kag;
@endscript
@endif
@return
*saveload
; 右クリックで呼ばれるサブルーチン
; 直接「ロード」や「セーブ」の画面を呼び出す場合もここを呼ぶ
@locklink
@locksnapshot
;---
; 設定レイヤを作成
@eval exp="saveload_object.show()"
@backlay
;簡易メニューを設置する場合、簡易メニューを消すためにコメントを解いて下さい。
;@if exp="f.qmenu_enabled"
;@eval exp="tf.qmenu_enabled_temp = 1"
;[qmenu enabled=false]
;[layopt layer=&sf.qmenu_exec_layer visible=false page=back]
;[layopt layer=&sf.qmenu_save_layer visible=false page=back]
;[layopt layer=&sf.qmenu_config_layer visible=false page=back]
;[layopt layer=&sf.qmenu_system_layer visible=false page=back]
;@endif
;---
*saveload_2
;---
; トランジションで表示させる
@eval exp="saveload_object.setConfigTrans(true)"
@trans method=crossfade time=500
@wt
;---
; 表画面の表示設定
@eval exp="saveload_object.foreConfig.show()"
;---
*saveload_3
;---
; 'config'トリガが発動されるまで待機
@waittrig name="config"
;---
*saveload_4
;---
; トランジションで閉じる
;@if exp="tf.qmenu_enabled_temp"
;[qmenu enabled=true]
;[init_qmenu page="back"]
;@endif
@trans method=crossfade time=500
@wt
;@if exp="tf.qmenu_enabled_temp"
;@eval exp="tf.qmenu_enabled_temp = 0"
;[init_qmenu page="fore"]
;@endif
;
;---
; 設定レイヤの中身を消去
@eval exp="saveload_object.closeConfig()"
;---
@unlocksnapshot
@unlocklink
@return
;------------------
; 2005/12/01 Ranka
;------------------
変数宣言など
first.ksなどの最初の方でプラグインとして宣言します。
プラグインの宣言方法:
[call storage="saveload.ks"]
プラグイン宣言後に、以下の変数・マクロなどを定義します。
(なお、この命令は簡易メニューと重複しています。簡易メニューを導入されている場合はこちらは不要です)
@iscript //セーブ・ロードのページ数 sf.saveload_page = 0 if sf.saveload_page === void; //最新の栞番号を記録しておくシステム変数 sf.new_savedata = -1 if sf.new_savedata === void; //オートセーブ 0:なし 1:あり sf.autosave = 1 if sf.autosave === void; //オートセーブ用先頭エントリ 0から9まで sf.autosave_entry = 0 if sf.autosave_entry === void; //sf.save_ask 0:セーブ・ロード時に確認メッセージを出さない、1:確認メッセージを出す sf.save_ask = 1 if sf.save_ask === void; @endscript ;----------------------------------------------------------------------------- ;■オートセーブ [autosave] ;----------------------------------------------------------------------------- [macro name=autosave] ;直前のラベルでオートセーブされます @if exp="sf.autosave && !kag.autosaveLoadedFlag" @eval exp="kag.autosaveLoadedFlag = 0" @save place=&"sf.autosave_entry+100" ask=no @eval exp="sf.autosave_entry += 1" @eval exp="sf.autosave_entry -= 10" cond="sf.autosave_entry >= 10" @endif [endmacro]
セーブ・ロード画面の呼び出し方法は以下の通りです。
セーブ画面の呼び出し方法:
@eval exp="f.rclickmode = 2, kag.callExtraConductor('saveload.ks','*saveload')"
ロード画面の呼び出し方法:
@eval exp="f.rclickmode = 1, kag.callExtraConductor('saveload.ks','*saveload')"
画像形式
画像をサンプルファイルからコピーして下さい。
bgimageとimageフォルダにそれぞれあります。
このセーブ・ロード機能を使う際、ボタン画像は通常の3つ並べた画像とは違う形式で作ります。
基本的に、画像を4つ並べたもの(今までのものに、一番右にフォーカスがある画像を追加したもの)を用います。
ボタンがオンになっているものについても、横サイズは4倍にしたものを使います。
サンプルの画像をよく見て、画像生成をして下さい。
- ボタンは4つ並べたものを用います。一番右はキーボード対応のための、フォーカスを得た画像です。
- オン状態(押された状態で選択不可)のボタンは、横サイズが4倍のものを用います。

↑透明なので分かりにくいですが、反転させて画像サイズを確認して下さい。 - ページ選択ボタンなども同様です。(反転させて画像サイズを確認して下さい)
なお、背景画像は透過画像でも大丈夫です。
その場合、セーブ・ロード画面を表示した時に、後ろの文字や不要なものを表示しないように注意して下さい。
また、簡易メニューを導入する場合、セーブ・ロード画面に入る前に簡易メニューを表示させないように処理させて下さい。(saveload.ksの最後の方にあるコメントアウト部分にその参考を示しています)
残作業のメモ
残作業を以下にざっと説明します。メモ程度なので他にもいろいろとあるでしょうが、参考までに。
- 画面デザインをして、それぞれの画像を生成、座標を決めて追加します。
- 必要であれば、セーブアイテム内に表示する内容をいろいろと追加して下さい。
ゲーム内日時を追加したい場合、「シナリオ中の日付表示」でsaveload.ks内を検索して、該当部分を修正して下さい。 - 実際に設定や簡易メニューなどの命令を追加して、表示させます。
- 不要な全景やメッセージレイヤーを非表示にしたり、終了する時に戻したりして、表示の整合性を取ります。
ありそうな問題
- トランジションで画面推移をしていますので、簡易メニューから呼び出した時と右クリックメニューなどから呼び出した時の整合性を取るように注意して下さい。
どちらの画面から呼び出されたのかを判別する部分を追加すれば、すぐにできると思います。
テスト実行例
テスト用の実行例として、以下のような例を挙げておきます。
サンプルに入っているものと似たようなものです。(サンプルでは、背景画像などを含んで表示しています)
*restart_point_saveload|セーブ・ロード機能テスト
[cm]
[er]こちらは、セーブ・ロード画面テンプレートのサンプルです。[r][l]
このテンプレートを用いることで、既にある程度できあがった状態からセーブ・ロード画面を導入することができますので、新規に作るコストや時間をカットすることができます。[l][r]
通常のセーブ・ロード機能、コメント、データ保護、データ削除、キーボード対応などの基本機能に加えて、オートセーブにも対応しています。[p]
[er]現状では100個のセーブエントリと10個のオートセーブエントリを想定しています。[l][r]
セーブエントリ数を増やすことも可能です。ただ、その場合は少々ソースをいじる必要がありますが、数値を入れ替えて条件式を修正するだけなのでそれほど大変ではないと思います。[l][r]
オートセーブは簡易メニューと共通のものですので、簡易メニューと高い親和性があります。[p]
[er]前置きはこれぐらいにして、実際に動作させてみましょう。[p]
*saveload_link_point
[er]
[delay speed=nowait]
[link exp="f.rclickmode = 2, kag.callExtraConductor('saveload.ks','*saveload')"]セーブ画面を呼び出す[endlink][r]
[link exp="f.rclickmode = 1, kag.callExtraConductor('saveload.ks','*saveload')"]ロード画面を呼び出す[endlink][r]
[link target=*saveload_next]説明の続きを読む[endlink][r]
[delay speed=user]
[s]
*saveload_next|セーブ・ロード説明後半
[autosave]
[er]あとは、画面のデザインをして画像を生成すれば、すぐに使えると思います。[l][r]
必要に応じて、効果音命令を追加していって下さい。[l][r]
なお、このテンプレートで使う画像形式は、これまでの3つ並んだ形式ではなく4つ並んだ少し特殊な画像を作る必要がありますので、注意して下さい。詳細は実装方法をご覧下さい。[l][r]
[r]
この機能が、お役に立てれば嬉しいです。[l][r]
説明は以上です。最初に戻ります[p]
[jump target="*restart_point_saveload"][s]
8.更新履歴
- 2009/04/01:フリー公開
- 2009/02/18:価格改定
- 2008/11/18:規約修正(支払期限にマスターアップまでを追加)
同人価格を追加 - 2008/07/10:公開(作成:江本あやえもん)

