[MZ] QuickSaveSlot — メニューを開かずにワンキーでクイックセーブ/ロード

[MZ] QuickSaveSlot — メニューを開かずにワンキーでクイックセーブ/ロード

みなさん、こんにちは!

このたび、RPG Maker MZ 用の小さなクオリティオブライフ(QoL)プラグイン QuickSaveSlot をリリースしました。これは、セーブ/ロードメニューを開くことなく、ワンキーでクイックセーブやクイックロードを行えるようにするプラグインです。

機能概要

  • 専用スロットへの即時セーブ/ロード — 通常のセーブデータやオートセーブとは独立しているため、それらを上書きすることがありません。
  • 2つの設定可能なホットキー — デフォルトでは F5–F10 が割り当てられており、それぞれクイックセーブとクイックロードに対応しています。
  • マップ画面限定の設計 — メニュー、戦闘中、イベントやメッセージ表示中などはトリガーされません。
  • セーブ無効条件の尊重$gameSystem.disableSave() の状態を正しく反映します。
  • 画面上のトースト通知 — (オプション)確認メッセージを表示します。テキストは完全にカスタマイズ可能で、標準のセーブ/ロード音も再生されます。
  • オプションのスイッチゲート — ストーリーの分岐フラグや設定切替スイッチによって、機能の有効/無効を切り替えることができます。
  • プラグインコマンドの搭載 — イベントからトリガーするための Quick Save / Quick Load コマンドが含まれています。

開発の経緯

チェックポイントでセーブするために毎回メニューを開くのは、ゲームのリズムを壊してしまうため、私は dedicated(専用)のクイックセーブスロットをホットキーに割り当てたいと考えました。このスロットは安全に保管され、戦闘中やイベント中に誤って発動することはありませんし、プレイヤーの実際のセーブデータには一切干渉しません。

ダウンロード

GitHub - LeonneBrito/rpmakermz-plugins · GitHub

— Takki

「いいね!」 1

いいですね。saveGame における Promise の扱いが、よく間違われがちなポイントなので、then と catch がすでに設定されているのは良いことです。

読みながら気づいた小さな点が2つあります。

  1. savefileId の復元が、プレイヤーがキーを連打すると固まってしまう可能性があります。executeQuickSave は previousSavefileId を取得し、スロットを設定し、保存してから then 内で復元を行います。最初の Promise が解決する前に2回目の押下が行われると、previousSavefileId はクイックスロット自体として読み込まれてしまいます。その結果、復元処理はクイックスロットに戻してそこで停止してしまいます。これを防ぐには、進行中のフラグ(in-flight flag)でクリアする必要があります。
let saving = false;
const executeQuickSave = () => {
    if (saving || !canQuickSave()) { /* toast + return */ }
    saving = true;
    ...
    .finally(() => { saving = false; });
};
  1. もう1つは、ロード時のバージョン不整合のブランチです。標準の MZ では、そこでは5つの引数が渡されます。
$gamePlayer.reserveTransfer(mapId, x, y, d, 0);

あなたのコードでは3つの引数しか渡されていないため、fadeType が undefined となり、fadeInForTransfer は 0 または 1 の場合にのみ有効になります。リロード自体は問題ありませんが、通常はフェードイン/アウトがあるところ、フェードなしで即座に表示されます。5番目の引数に 0 を渡すことで、通常のフェード動作が戻ります。

なお、4番目の引数を省略しても問題ありません。performTransfer はそれをそのまま setDirection に渡しますが、setDirection は値が真値である場合にのみガードします。

Game_CharacterBase.prototype.setDirection = function(d) {
    if (!this.isDirectionFixed() && d) {
        this._direction = d;
    }
    this.resetStopCount();
};

したがって、undefined は通過してしまい、プレイヤーは既存の向きを維持します。実際に問題になるのは5番目の引数だけです。

確かに、その通りですね。両方とも見逃しにくいポイントを見事に指摘されました。

saving による実行中のガードは、クイックセーブの競合状態に対する確かに正しい修正です。これを設定しないと、2回目の押下で既に上書きされた savefileId が取得され、間違ったスロットが復元されてしまいます。

また、バージョン不一致の分岐についてもその通りです。第五引数の reserveTransfer を見落としていました。0 を渡すことで、通常のフェード処理が復元されます:

$gamePlayer.reserveTransfer(mapId, x, y, d, 0);

第四引数に関するご指摘もその通りです。setDirectionif (d) でガードされているため、undefined は実質的に害がなく、既存の方向が保持されます。

これら2つの変更を行います:実行中のセーブガードと、バージョン不一致時のリロードにおける fadeType = 0 です。