RPGツクールMV製ゲームの回想部屋に直行するための汎用スクリプト集

# はじめに

ゲーム開始時に強制的に見せられる退屈なオープニングムービーや、
長い長いスキップ不可のチュートリアルや、めんどくさい回想部屋への移動フラグなど
すべての面倒ごとをすっ飛ばしてさっさと回想部屋に直行したい
せっかちさん向けのスクリプト集です。

テキストエディタの使い方が分かり、かつ、
ある程度はJSONデータが読める人を対象としています。

2022.01.22 追記
記事を大幅に書き換えました。
長らくHPの値を変える云々ってタイトルだったのに、
HP関連のスクリプトを全く書いてないことに気づいたので。

* 現在実行中のイベントを強制終了できるようにしました。
* 回想部屋に直行するための実用例を一番下に追記しました。

2021.10.28 追記

* マップ移動時に強制壁抜けできるようにしました。
* マップ移動時のスポーン地点座標を指定できるようにしました。
* 壁抜けをトグルできるようにしました。

# 導入方法

https://qiita.com/qwe001/items/cf5db359c912775b22e9

上記URLに従い、必要ファイルをダウンロードすれば半自動で導入できます。
以下の方法は、手動で導入する場合の手順です。

## 1. ゲームの実行ファイルがあるフォルダに移動する

多くの場合、`Game.exe` や `{ゲームのタイトル}.exe` がある場所がそれにあたります。

## 2. ゲームで読み込む `index.html` の場所に移動する

多くの場合、ゲームの実行ファイル `Game.exe` があるフォルダの下の `www/index.html` がそれにあたります。
時々、ゲームの実行ファイルと同階層に `index.html` がある場合もあります。

場所を特定したい時は、ゲームの実行ファイル `Game.exe` と同階層にある
`package.json` の内容を確認すればわかります。

![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/bef745e9-276a-d749-fcba-729de022f6e7.png)

`main` プロパティの値 `www/index.html` が読み込み先です。

`www` フォルダが見つからない、`index.html` ファイルが見つからない、
ゲームフォルダには `Game_boxed.exe` しかなくて、他のフォルダがない場合、どうしようもありません。

どうしようもなくはないですが、本稿と関係ない話なので、各自
<font color="White">EnigmaVBUnpackerで</font>
ググって何とかしてください。

## 3. `index.html` ファイル内の `script` タグの一番最後に以下の記述を書き込む

テキストエディタで `index.html` ファイルを開き、以下の記述をコピペして書き込みます

```javascript
<script>
// 自作スクリプトここから
var isThroughNow = false; // 壁抜けフラグ

document.onkeydown = function(event){
    if (!event) { event = window.event; };

    var shift = event.shiftKey;
    var ctrl = event.ctrlKey;
    var alt = event.altKey;
    var keyCode = event.keyCode;

    var actorId = 1; // 主人公
    var nowVal;

    if(alt && keyCode == 73){ // Alt+I
        var inputMapId = window.prompt("ここに移動先のマップIDを入力してください");
        var inputMapX = window.prompt("ここに移動先のX座標を入力してください", 1);
        var inputMapY = window.prompt("ここに移動先のY座標を入力してください", 1);

        var mapId = inputMapId;
        var mapX = parseInt(inputMapX, 10);
        var mapY = parseInt(inputMapY, 10);
        var playerDirection = 0; // 0:そのまま, 2:下, 4:左, 6:右, 8:上
        var fadeInId = 2; // 0:黒フェード, 1:白フェード, 2:なし

        $gamePlayer.reserveTransfer(mapId, mapX, mapY, playerDirection, fadeInId);

        alert('mapId:' + mapId + ' に移動します');
        //alert('デバッグモード起動中は、AltまたはCtrlキーを押しながら移動で壁抜けできます。移動不可能になったらお試しください');

        $gamePlayer.setThrough(true);
        alert('壁抜けを許可しました。終了するには「Alt+M」キーを押してください');
        isThroughNow = true;
    }

    if(alt && keyCode == 74){ // Alt+J
        SceneManager.push(Scene_Save);

        alert('セーブ画面を開きます');
    }

    if(alt && keyCode == 75){ // Alt+K
        var inputEventId = window.prompt("ここに再生したいコモンイベントIDを入力してください");

        $gameTemp.reserveCommonEvent(inputEventId);

        alert('コモンイベント:' + inputEventId + 'を再生します');
    }

    if(alt && keyCode == 76){ // Alt+L
        SceneManager.push(Scene_Debug);

        alert('デバッグ画面を開きます');
    }

    if(alt && keyCode == 77){ // Alt+M
        if(isThroughNow === true){
            alert('壁抜けを終了します。再開するには「Alt+M」キーを押してください');
            $gamePlayer.setThrough(false);
            isThroughNow = false;
        }
        else {
            alert('壁抜けを許可します。終了するには「Alt+M」キーを押してください');
            $gamePlayer.setThrough(true);
            isThroughNow = true;
        }
    }

    if(alt && keyCode == 78){ // Alt+N
        if($gameMap.isEventRunning()){
            alert('イベントを強制終了します。');

            $gameMap.events().forEach(function(event) {
                if (event.eventId() != this._eventId){
                    event.erase();
                }
            }, this);

            $gameMap._interpreter._index = $gameMap._interpreter._list.length;

            $gamePlayer.setTransparent(false); // show player to map
        }
        else {
            alert('現在実行中のイベントはありません');
        }
    }

    if(alt && keyCode == 79){ // Alt+O
        alert('立ち絵・黒背景を全消去します');
        $gameScreen.clearPictures();
    }
};
// 自作スクリプトここまで
</script>
```

完成イメージはこんな感じです

```javascript:/path/to/project/www/index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
        <meta name="viewport" content="user-scalable=no">
        <link rel="icon" href="icon/icon.png" type="image/png">
        <link rel="apple-touch-icon" href="icon/icon.png">
        <link rel="stylesheet" type="text/css" href="fonts/gamefont.css">
        <title>ゲームのタイトル</title>
    </head>
    <body style="background-color: black">
        <script type="text/javascript" src="js/libs/pixi.js"></script>
        <script type="text/javascript" src="js/libs/pixi-tilemap.js"></script>
        <script type="text/javascript" src="js/libs/pixi-picture.js"></script>
        <script type="text/javascript" src="js/libs/fpsmeter.js"></script>
        <script type="text/javascript" src="js/libs/lz-string.js"></script>
        <script type="text/javascript" src="js/libs/iphone-inline-video.browser.js"></script>
        <script type="text/javascript" src="js/rpg_core.js"></script>
        <script type="text/javascript" src="js/rpg_managers.js"></script>
        <script type="text/javascript" src="js/rpg_objects.js"></script>
        <script type="text/javascript" src="js/rpg_scenes.js"></script>
        <script type="text/javascript" src="js/rpg_sprites.js"></script>
        <script type="text/javascript" src="js/rpg_windows.js"></script>
        <script type="text/javascript" src="js/plugins.js"></script>
        <script type="text/javascript" src="js/main.js"></script>

<script>
// 自作スクリプトここから
var isThroughNow = false; // 壁抜けフラグ

document.onkeydown = function(event){
    if (!event) { event = window.event; };

    var shift = event.shiftKey;
    var ctrl = event.ctrlKey;
    var alt = event.altKey;
    var keyCode = event.keyCode;

    var actorId = 1; // 主人公
    var nowVal;

    if(alt && keyCode == 73){ // Alt+I
        var inputMapId = window.prompt("ここに移動先のマップIDを入力してください");
        var inputMapX = window.prompt("ここに移動先のX座標を入力してください", 1);
        var inputMapY = window.prompt("ここに移動先のY座標を入力してください", 1);

        var mapId = inputMapId;
        var mapX = parseInt(inputMapX, 10);
        var mapY = parseInt(inputMapY, 10);
        var playerDirection = 0; // 0:そのまま, 2:下, 4:左, 6:右, 8:上
        var fadeInId = 2; // 0:黒フェード, 1:白フェード, 2:なし

        $gamePlayer.reserveTransfer(mapId, mapX, mapY, playerDirection, fadeInId);

        alert('mapId:' + mapId + ' に移動します');
        //alert('デバッグモード起動中は、AltまたはCtrlキーを押しながら移動で壁抜けできます。移動不可能になったらお試しください');

        $gamePlayer.setThrough(true);
        alert('壁抜けを許可しました。終了するには「Alt+M」キーを押してください');
        isThroughNow = true;
    }

    if(alt && keyCode == 74){ // Alt+J
        SceneManager.push(Scene_Save);

        alert('セーブ画面を開きます');
    }

    if(alt && keyCode == 75){ // Alt+K
        var inputEventId = window.prompt("ここに再生したいコモンイベントIDを入力してください");

        $gameTemp.reserveCommonEvent(inputEventId);

        alert('コモンイベント:' + inputEventId + 'を再生します');
    }

    if(alt && keyCode == 76){ // Alt+L
        SceneManager.push(Scene_Debug);

        alert('デバッグ画面を開きます');
    }

    if(alt && keyCode == 77){ // Alt+M
        if(isThroughNow === true){
            alert('壁抜けを終了します。再開するには「Alt+M」キーを押してください');
            $gamePlayer.setThrough(false);
            isThroughNow = false;
        }
        else {
            alert('壁抜けを許可します。終了するには「Alt+M」キーを押してください');
            $gamePlayer.setThrough(true);
            isThroughNow = true;
        }
    }

    if(alt && keyCode == 78){ // Alt+N
        if($gameMap.isEventRunning()){
            alert('イベントを強制終了します。');

            $gameMap.events().forEach(function(event) {
                if (event.eventId() != this._eventId){
                    event.erase();
                }
            }, this);

            $gameMap._interpreter._index = $gameMap._interpreter._list.length;

            $gamePlayer.setTransparent(false); // show player to map
        }
        else {
            alert('現在実行中のイベントはありません');
        }
    }

    if(alt && keyCode == 79){ // Alt+O
        alert('立ち絵・黒背景を全消去します');
        $gameScreen.clearPictures();
    }
};
// 自作スクリプトここまで
</script>

    </body>
</html>
```

`index.html` ファイルへの書き込みが完了したら、ゲームを起動します。
既にゲームを起動中ならば、F5キーを押して更新すればスクリプト集が使用可能になります。

# 回想部屋への直行方法

## Step1. 回想部屋のマップIDを特定する

ゲームのあるフォルダから見て、以下のファイルを探します

```
www/data/MapInfos.json
```

多くのゲームは、このファイルに全マップの情報が定義されています。
このファイルをテキストエディタで開き、「回想」とか「Gallary」とかで検索して、回想部屋らしきマップを探します。
見つからなかったらGREP検索で`www/data/*.json`から探せばどうにかなるかもですが、説明が面倒なので割愛します。

![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/de9639e7-d040-d507-99f3-9a18ef4d5b3a.png)

見つけたら、その行の左端にある `id` の値を確認します。この数値が、マップのIDです。
今回の例だと「45」がそれにあたります。

## Step2. ゲームを起動し、キャラの操作が可能な状態にする

制作者が意匠を凝らして作ったスキップ不可の強制オープニングが始まったら、
`Alt + N` を押してください。強制的にイベントを終了します。

もし、主人公キャラが表示されない、立ち絵がずっと表示されて邪魔な場合は
`Alt + O` を押してください。立ち絵や、黒背景画像を全消去します。

なお、すぐにキャラの操作ができるなら、この手順はスキップしてください。
または、オープニングイベントがすぐに終わりそうだったり、
スキップ、または早送りの方法があらかじめ用意されている場合もこの手順をスキップしてください。

イベントを強制終了したり、立ち絵を消去しますので、
予期せぬ不具合が起きる可能性があるためです。

## Step3. 回想部屋に移動する

`Alt + I` を押すと、マップIDの入力を求められます。
ここに、Step1で特定した回想部屋のIDを入力して、ダイアログ内のOKボタンを押します
(ゲームによってはEnterキーが使用できる場合もあります)

![image3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/bfa12c63-127e-5cd6-b78f-b1c1ae9ca742.png)

マップ移動後の初期スポーン地点をどうするか聞かれます。
壁抜けできるようにしてありますので、気にせずOKボタンを二度押します

![image4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/f7788728-5a98-d9f3-5a48-54c889d0d996.png)

![image5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/0daad324-1288-fc60-c539-43efbd917fc1.png)


## Step4. 壁抜けをオフにしてセーブする

ゲームによっては、複数の部屋にわたって回想部屋がある場合もあり、
壁抜けしたままだと移動が不便になる場合もあります。

壁の内側に移動したら、`Alt + M` を押して壁抜けをオフにします。
(もう一度押せば再度壁抜けをオンにできます)

そして、`Alt + J` を押してセーブします。(セーブ不可のゲームもたまにあるので用意してます)
次回起動時にすぐ回想部屋に直行できるようにするためと、
このあとフラグをいじるので、おかしくならないためにセーブしておきます。

## Step5. 回想フラグを全部オンにする

回想部屋内に全開放スイッチ的なのがあれば、この手順はスキップしてください

`Alt + L` を押して、デバッグ画面を開きます。
ここで回想用のギャラリー開放に使いそうなフラグを片っ端からオンにしていきます。

よくあるのは「CG1」とか「回想 ○○1」とか「○○イベ1」みたいな名前のフラグです。

フラグをオンにするのが終わったらEscキーを押して画面を閉じます。

回想部屋のギャラリーが全開放されたら作業終了です。

`Alt + J` を押して、現在の状態をセーブします。あとはご自由に。

## おまけ. キーワードに一致するイベントを探して再生する

時には、ギャラリーが多すぎてお目当てのイベントが見つけづらい時もあると思います。
そんな時は、 `www/data/CommonEvents.json` をテキストエディタで開きます。
そして、自分の好きなキーワードで検索して、ヒットする行を探します。

![image6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/f288c621-fbdd-6f97-b189-b23d16fb7cb1.png)

ヒットしたら、その行の先頭のIDをメモします。

![image7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/0f6ebfcb-1c93-89b0-bc22-3ad0238c992d.png)

ゲームを起動し、操作可能状態になったら `Alt + K` を押して、
先ほどメモしたIDを入力してOKボタンを押します。

すると、お目当てのイベントが再生されます。
イベントの再生が終了すると、操作不能になることが多いので、
その場合はF5キーを押してゲームを再起動してください。

以上