code:
//=============================================================================
// Keke_LoopBalloon - ループ・フキダシアイコン
// バージョン: 1.0.4
//=============================================================================
// Copyright (c) 2022 ケケー
// Released under the MIT license
// http://opensource.org/licenses/mit-license.php
//=============================================================================

/*:
 * @target MZ
 * @plugindesc フキダシアイコンをループ表示させる
 * @author ケケー
 * @url https://kekeelabo.com
 *
 * @help
 * 【ver.1.0.4】
 * フキダシアイコンをループ表示させる
 * プラグインコマンドか注釈を使って表示できる
 *
 *
 * 【機能1】プラグインコマンドでフキダシアイコンを表示
 * => プラグインコマンド → フキダシアイコンの表示
 * 　対象キャラ・フキダシアイコンIDを入力
 *
 *
 * 【機能2】プラグインコマンドでフキダシアイコンを消去
 * => プラグインコマンド → フキダシアイコンの消去
 * 　対象キャラを入力
 *
 *
 * 【機能3】注釈でフキダシアイコンを表示
 * => イベントコマンドの『注釈』を使う
 * ★注釈の置き場所は「現在のページの一番上」か「1ページ目の一番上」
 * 　まず現在のページのがあればそれを使い、なければ1ページ目のを使う
 * 　注釈は複数つなげることも可能
 * 置いた注釈の中に
 * <balloon: フキダシアイコンID, (ループウェイト), (ループ回数)>
 * 　※ループウェイトとループ回数は省略可能
 * 例)
 * <balloon: 1>
 * 　びっくりのフキダシアイコンを表示
 * <balloon: 0>
 * 　フキダシアイコンを表示しない
 * <balloon: 2, 30, 5>
 * 　はてなのフキダシアイコンを表示
 * 　ループ間に 30フレーム のウェイトを挟み、
 * 　5回 表示したら終了
 *
 *
 * 【付属】標準のアイコンID
 * 1:  びっくり
 * 2:  はてな
 * 3:  音符
 * 4:  ハート
 * 5:  怒り
 * 6:  汗
 * 7:  くしゃくしゃ
 * 8:  沈黙
 * 9:  電球
 * 10: Zzz
 * 11: ユーザー定義1
 * 12: ユーザー定義2
 * 13: ユーザー定義3
 * 14: ユーザー定義4
 * 15: ユーザー定義5
 *
 *
 * ● 利用規約 ●
 * MITライセンスのもと、自由に使ってくれて大丈夫です
 *
 *
 *
 * @command フキダシアイコンの表示
 * @desc フキダシアイコンをループ表示する
 *
 * @arg 対象キャラ-名前
 * @desc 名前で対象指定。***:イベント検索、セルフ:イベント自身、プレイヤー:同、全:全キャラ、\v[***]:変数。, で複数指定
 *
 * @arg 対象キャラ-ID
 * @desc IDで対象指定。。1〜:イベントID。0:イベント自身、-1:プレイヤー、-2:全キャラ、\v[***]:変数, で複数、~ でまとめて指定
 *
 * @arg フキダシアイコンID
 * @desc ループ表示するフキダシアイコンのID
 * @type number
 *
 * @arg ループウェイト
 * @desc ループの間に挟むウェイト。5 なら 5フレーム。0~5 なら 0 から 5 の間でランダム
 *
 * @arg ループ回数
 * @desc ループ表示する回数。5 なら 5回 表示。0~5 なら 0 から 5 の間でランダム。空欄なら無制限
 *
 *
 *
 * @command フキダシアイコンの消去
 * @desc 表示中のフキダシアイコンを消去する
 *
 * @arg 対象キャラ-名前
 * @desc 名前で対象指定。***:イベント検索、セルフ:イベント自身、プレイヤー:同、全:全キャラ、\v[***]:変数。, で複数指定
 *
 * @arg 対象キャラ-ID
 * @desc IDで対象指定。。1〜:イベントID。0:イベント自身、-1:プレイヤー、-2:全キャラ、\v[***]:変数, で複数、~ でまとめて指定
 */

(() => {
  //- プラグイン名
  const pluginName = document.currentScript.src.match(/^.*\/(.*).js$/)[1];

  //==================================================
  //--  プラグインコマンド
  //==================================================

  //- フキダシアイコンの表示
  PluginManager.registerCommand(
    pluginName,
    "フキダシアイコンの表示",
    (args) => {
      // バルーンIDを取得
      const balloonId = Number(convertVariable(args["フキダシアイコンID"]));
      if (!balloonId) {
        return;
      }
      // 対象キャラを取得
      const names = convertVariable(args["対象キャラ-名前"]);
      const ids = convertVariable(args["対象キャラ-ID"]);
      const charas = [...getCharasByName(names), ...getCharasById(ids)];
      if (!charas || !charas.length) {
        return;
      }
      // 各パラメータを取得
      const loopWait = convertVariable(args["ループウェイト"]);
      const loopNum = randomise(convertVariable(args["ループ回数"]));
      // ループバルーンをセット
      charas.forEach((chara) => {
        // バルーンスプライトの消去
        delBalloonSprite(chara);
        chara._loopBalloonKe = { id: balloonId, wait: loopWait, num: loopNum };
      });
    }
  );

  //- フキダシアイコンの消去
  PluginManager.registerCommand(
    pluginName,
    "フキダシアイコンの消去",
    (args) => {
      // 対象キャラを取得
      const names = convertVariable(args["対象キャラ-名前"]);
      const ids = convertVariable(args["対象キャラ-ID"]);
      const charas = [...getCharasByName(names), ...getCharasById(ids)];
      if (!charas || !charas.length) {
        return;
      }
      // ループバルーンを消去
      charas.forEach((chara) => {
        // バルーンスプライトの消去
        delBalloonSprite(chara);
        chara._loopBalloonKe = null;
      });
    }
  );

  //==================================================
  //--  ループバルーン
  //==================================================

  //- ゲームキャラクターベース・更新(コア追加)
  const _Game_CharacterBase_update = Game_CharacterBase.prototype.update;
  Game_CharacterBase.prototype.update = function () {
    _Game_CharacterBase_update.apply(this);
    // ループバルーンの更新
    updateLoopBalloon(this);
  };

  //- ループバルーンの更新
  function updateLoopBalloon(chara) {
    // 注釈からのバルーン取得
    getBalloonByComment(chara);
    const balloon = chara._loopBalloonKe || chara._loopBallonCommentKe;
    // バルーンデータがなければリターン
    if (!balloon) {
      return;
    }
    // バルーン表示中はリターン
    if (chara._balloonPlaying) {
      return;
    }
    // ウェイトを適用
    if (balloon.waitDura) {
      balloon.waitDura--;
      return;
    }
    // バルーンを表示
    $gameTemp.requestBalloon(chara, balloon.id);
    // ウェイトをセット
    balloon.waitDura = randomise(balloon.wait);
  }

  //- 注釈からのバルーン取得
  function getBalloonByComment(event) {
    // イベント以外はリターン
    if (!event._eventId) {
      return;
    }
    // 前回のバルーンを保存
    const preBalloon = event._loopBallonCommentKe;
    // ページ切り替わったら初期化
    initAtPageChange(event);
    // すでに取得済みならリターン
    if (event._loopBallonCommentKe) {
      return;
    }
    // 最初の注釈を取得
    const comment = getFirstComment(event);
    if (!comment) {
      return null;
    }
    const p = getParameter(comment);
    if (p) {
      event._loopBallonCommentKe = p;
    }
    // バルーンが変更されたら
    if (preBalloon && preBalloon.id != p.id) {
      // バルーンスプライトの消去
      delBalloonSprite(event);
    }
  }

  //- ページが切り替わったら初期化
  function initAtPageChange(event) {
    // ページが切り替わったら
    if (event._currentPageKeLpbl != event._pageIndex) {
      // パラムスを初期化
      event._loopBallonCommentKe = null;
    }
    // 現在のページを保存
    event._currentPageKeLpbl = event._pageIndex;
  }

  //- パラメータの取得
  function getParameter(note = "") {
    // ノートから設定部分を検索
    let desc = "";
    if (note) {
      let match = note.match(/<balloon:\s*([^>]+)>/i);
      if (!match || !match[1]) {
        return null;
      }
      desc = match[1];
    }
    // パラメータを取得
    const descs = desc.replace(/\s/g, "").split(",");
    const p = {};
    p.id = Number(descs[0]);
    p.wait = descs[1];
    p.num = randomise(descs[2]);
    return p;
  }

  //- ゲームキャラクターベース・バルーンの終了(コア追加)
  const _Game_CharacterBase_endBalloon =
    Game_CharacterBase.prototype.endBalloon;
  Game_CharacterBase.prototype.endBalloon = function () {
    _Game_CharacterBase_endBalloon.apply(this);
    // バルーンの回数カウント
    countBalloonNum(this);
  };

  //- バルーンの回数カウント
  function countBalloonNum(chara) {
    const balloon = chara._loopBalloonKe;
    if (!balloon || !balloon.num) {
      return;
    }
    balloon.num--;
    // 終了
    if (balloon.num <= 0) {
      chara._loopBalloonKe = null;
    }
  }

  //- バルーンスプライトの消去
  function delBalloonSprite(chara) {
    const spriteset = SceneManager._scene._spriteset;
    const balloons = spriteset._balloonSprites;
    if (!balloons || !balloons.length) {
      return;
    }
    balloons.forEach((balloon) => {
      if (balloon.targetObject == chara) {
        spriteset.removeBalloon(balloon);
      }
    });
  }

  //==================================================
  //--  プラグインコマンド基本 /ベーシック
  //==================================================

  let plcmPreter = null;

  //- プラグインコマンド呼び出しプリターを保存(コア追加)
  const _PluginManager_callCommand = PluginManager.callCommand;
  PluginManager.callCommand = function (self, pluginName, commandName, args) {
    plcmPreter = self;
    _PluginManager_callCommand.apply(this, arguments);
    plcmPreter = null;
  };

  //- プラグインコマンド呼び出しイベントを取得
  function getPlcmEvent() {
    return getPreterEvent(plcmPreter);
  }

  //- インタープリターのイベント取得
  function getPreterEvent(preter) {
    if (!preter) {
      return null;
    }
    return preter.character(preter.eventId());
  }

  //- 名前でのキャラリスト取得
  function getCharasByName(names, self) {
    if (!names) {
      return [];
    }
    const nameList = names.replace(/\s/g, "").split(",");
    let charas = [];
    let match = null;
    for (const name of nameList) {
      // イベントを取得
      $gameMap.events().forEach((event) => {
        const note = event.event().name + " " + event.event().note;
        if (note.includes(name)) {
          charas.push(event);
        }
      });
      // セルフを取得
      if (name.match(/^(セルフ|自分|自身|self)$/)) {
        self =
          self ||
          getPlcmEvent() ||
          (charaSpritePlcm ? charaSpritePlcm._character : null) ||
          $gamePlayer;
        if (self) {
          charas.push(self);
        }
      }
      // プレイヤーを取得
      if (name.match(/^(プレイヤー|操作キャラ|player)$/)) {
        charas = [...charas, $gamePlayer];
      }
      // フォロワーを取得
      match = name.match(
        /^(フォロワー|フォロアー|隊列|隊列キャラ|follower)(\d*)$/
      );
      if (match) {
        const id = match[2] ? Number(match[2]) - 1 : 0;
        charas =
          id != null
            ? [...charas, $gamePlayer._followers._data[id]]
            : [...charas, ...$gamePlayer._followers._data];
      }
      // パーティを取得
      if (name.match(/^(パーティ|味方|味方全員|味方全体|party)$/)) {
        charas = [...charas, $gamePlayer, ...$gamePlayer._followers._data];
      }
      // 乗り物を取得
      match = name.match(/^(乗り物|乗物|乗機|vehicle)(\d*)$/);
      if (match) {
        const id = match[2] ? Number(match[2]) - 1 : 0;
        charas = id
          ? [...charas, $gameMap._vehicles[id]]
          : [...charas, ...$gameMap._vehicles];
      }
      // 全て取得
      if (name.match(/^(全て|すべて|全部|全体|all)$/)) {
        charas = [
          ...$gameMap.events(),
          $gamePlayer,
          ...$gamePlayer._followers._data,
          ...$gameMap._vehicles,
        ];
      }
      // 選択なし
      if (name.match(/^(なし|無し|none)$/)) {
      }
    }
    charas = charas.filter((chara) => chara);
    return charas;
  }

  //- IDでのキャラリスト取得
  function getCharasById(ids, self) {
    if (!ids) {
      return [];
    }
    const idList = !ids ? [] : strToNumList(ids.toString());
    let charas = [];
    for (const id of idList) {
      // イベントを取得
      if (id > 0) {
        charas.push($gameMap.event(id));
      }
      // セルフを取得
      if (id == 0) {
        self =
          self ||
          getPlcmEvent() ||
          (charaSpritePlcm ? charaSpritePlcm._character : null) ||
          $gamePlayer;
        if (self && !idList.includes(self._eventId)) {
          charas.push(self);
        }
      }
      // プレイヤーを取得
      if (id == -1) {
        charas = [...charas, $gamePlayer];
      }
      // フォロワーを取得
      if (id <= -10 && id >= -99) {
        charas =
          id == -10
            ? [...charas, ...$gamePlayer._followers._data]
            : [...charas, $gamePlayer._followers._data[Math.abs(id) - 11]];
      }
      // 乗り物を取得
      if (id <= -100) {
        charas =
          id == -100
            ? [...charas, ...$gameMap._vehicles]
            : [...charas, $gameMap._vehicles[Math.abs(id) - 101]];
      }
      // 全て取得
      if (id == -2) {
        charas = [
          ...$gameMap.events(),
          $gamePlayer,
          ...$gamePlayer._followers._data,
          ...$gameMap._vehicles,
        ];
      }
    }
    charas = charas.filter((chara) => chara);
    return charas;
  }

  //==================================================
  //-  その他 /ベーシック
  //==================================================

  //- 最初の注釈を取得
  function getFirstComment(event) {
    let page = event.page();
    let list = page ? event.page().list : [];
    let commandFirst = list[0] || {};
    if (commandFirst.code != 108) {
      page = event.event().pages[0];
      list = page ? page.list : [];
      commandFirst = list[0] || {};
      if (commandFirst.code != 108) {
        return;
      }
    }
    // 注釈を全て読み込み
    let comment = "";
    let i = 0;
    while (true) {
      const command = list[i];
      if (!command || !(command.code == 108 || command.code == 408)) {
        break;
      }
      comment += command.parameters[0] + "\n";
      i++;
    }
    return comment;
  }

  //- 変数の置換
  function convertVariable(str) {
    if (!str) {
      return str;
    }
    const regExp = /[\x1b\\]v\[(\d+)\]/gi;
    while (true) {
      const match = regExp.exec(str);
      if (!match) {
        break;
      }
      const val = $gameVariables.value(Number(match[1]));
      str = str.replace(match[0], val);
    }
    return str;
  }

  //- ランダマイズ
  function randomise(val) {
    if (typeof val != "string" || !val.includes("~")) {
      return val;
    }
    const vals = val.replace(/\s/g, "").split("~");
    const calcs = [Number(vals[0]), Number(vals[1])];
    calcs.sort((a, b) => a - b);
    return Math.round(calcs[0] + Math.random() * (calcs[1] - calcs[0]));
  }

  //- スプライトの検索-キャラクター
  function searchSpriteChara(chara) {
    let result = null;
    const sprites = SceneManager._scene._spriteset._characterSprites;
    for (let sprite of sprites) {
      if (isSameChara(sprite._character, chara)) {
        result = sprite;
        break;
      }
    }
    return result;
  }
})();


code:
//=============================================================================
//  Keke_TimingCommon - タイミングコモン
// バージョン: 1.2.1
//=============================================================================
// Copyright (c) 2022 ケケー
// Released under the MIT license
// http://opensource.org/licenses/mit-license.php
//=============================================================================

/*:
 * @target MZ
 * @plugindesc 特定のタイミングでコモンを差し込む
 * @author ケケー
 * @url https://kekeelabo.com
 *
 * @help
 * 【ver.1.2.1】
 * 特定のタイミングでコモンイベントを自動で差し込む
 * ◎ニューゲーム
 * ◎データロード
 * ◎場所移動
 * ◎メニュー
 * ◎バトル開始
 * ◎バトル勝利
 * ◎バトル逃走
 * ◎バトル敗北
 * ◎ゲームオーバー
 * ◎味方アクション
 * ◎敵アクション
 *
 *
 * ● 使い方 ●
 * プラグインパラメータで、各タイミングごとの実行コモンを設定する
 * ゲーム中に無効化/有効化したり、
 * 実行コモンを変更したい場合はプラグインコマンドで
 *
 * 【注釈】アクションコモン変数
 * アクションコモンは戦闘中、バトラーのアクション前後に実行するコモン
 * その際、特定の変数に行動者ID、スキルID、アイテムIDを自動で入れられる
 * 行動者IDには味方の場合はアクターIDが、敵の場合はエネミーIDが入る
 * 行動がスキルの場合はスキルIDが、アイテムの場合はアイテムIDが入る
 *
 *
 * ● 利用規約 ●
 * MITライセンスのもと、自由に使ってくれて大丈夫です
 *
 *
 *
 * Automatically insert common events at specific timings
 * ◎ New Game
 * ◎ Data loading
 * ◎ Move location
 * ◎ Menu
 * ◎ Battle begins
 * ◎ Battle Victory
 * ◎ Battle Escape
 * ◎ Battle defeat
 * ◎ Game Over
 * ◎ Friendly action
 * ◎ Enemy actions
 *
 *
 * ● How to use ●
 * Set the execution common for each timing with the plug-in parameter
 * disable/enable in-game,
 * If you want to change the execution common, use the plugin command
 *
 * [Note] Action common variable
 * Action commons are commons that are executed before
 *   and after the battler's action during battle.
 * At that time, you can automatically put the actor ID, skill ID,
 *   and item ID into a specific variable
 * Actor ID includes Actor ID for allies and Enemy ID for enemies
 * If the action is a skill, enter the skill ID.
 *   If the action is an item, enter the item ID.
 *
 *
 * ● Terms of Use ●
 * Feel free to use it under the MIT license.
 *
 *
 *
 * @param コモン-ニューゲーム
 * @desc ニューゲーム時に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-データロード
 * @desc データロード時に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-場所移動-前
 * @desc 場所移動前に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-場所移動-後
 * @desc 場所移動後に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-メニュー開く
 * @desc メニューを開く時に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-メニュー閉じ
 * @desc メニューを閉じる時に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-バトル開始-前
 * @desc バトル開始前に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-バトル開始-後
 * @desc バトル開始後に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-バトル勝利-前
 * @desc バトル勝利時の遷移前に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-バトル勝利-後
 * @desc バトル勝利時の遷移後に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-バトル逃走-前
 * @desc バトル逃走時の遷移前に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-バトル逃走-後
 * @desc バトル逃走時の遷移後に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-バトル敗北-前
 * @desc バトル敗北時の遷移前に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-バトル敗北-後
 * @desc バトル敗北時の遷移後に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @param コモン-ゲームオーバー
 * @desc ゲームオーバー時に実行するコモンイベント。全滅してゲームオーバーになった場合、回復しないとコモンを繰り返すので注意
 * @type common_event
 * @default
 *
 * @param コモン-味方アクション-前
 * @desc 戦闘中、味方がスキル/アイテムを使う直前に実行するコモンイベントの有効/無効
 * @type common_event
 * @default
 *
 * @param コモン-味方アクション-後
 * @desc 戦闘中、味方がスキル/アイテムを使った直後に実行するコモンイベントの有効/無効
 * @type common_event
 * @default
 *
 * @param コモン-敵アクション-前
 * @desc 戦闘中、敵がスキル/アイテムを使う直前に実行するコモンイベントの有効/無効
 * @type common_event
 * @default
 *
 * @param コモン-敵アクション-後
 * @desc 戦闘中、敵がスキル/アイテムを使った直後に実行するコモンイベントの有効/無効
 * @type common_event
 * @default
 *
 * @param アクションコモン変数
 *
 * @param 変数-行動者ID
 * @parent アクションコモン変数
 * @desc アクションコモン時の行動者のIDを入れる変数。アクターならアクターID、エネミーならエネミーID
 * @type variable
 * @default
 *
 * @param 変数-スキルID
 * @parent アクションコモン変数
 * @desc アクションコモン時の使用スキルのIDを入れる変数
 * @type variable
 * @default
 *
 * @param 変数-アイテムID
 * @parent アクションコモン変数
 * @desc アクションコモン時の使用アイテムのIDを入れる変数
 * @type variable
 * @default
 *
 * @param 変数-対象が敵か味方か
 * @parent アクションコモン変数
 * @desc アクションコモン時の行動対象が敵か味方かを入れる変数。敵なら 0、味方なら 1
 * @type variable
 * @default
 *
 * @param 変数-対象ID
 * @parent アクションコモン変数
 * @desc アクションコモン時の行動対象のID。アクターならアクターID、エネミーならエネミーID
 * @type variable
 * @default
 *
 * @param 変数-対象全て
 * @parent アクションコモン変数
 * @desc アクションコモン時の行動対象全てのIDを配列として入れる変数
 * @type variable
 * @default
 *
 *
 *
 * @command タイミングコモン有効/無効
 * @desc 各タイミングコモンを有効/無効を切り替える
 *
 * @arg コモン-ニューゲーム
 * @desc ニューゲーム時に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-データロード
 * @desc データロード時に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-場所移動-前
 * @desc 場所移動前に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-場所移動-後
 * @desc 場所移動後に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-メニュー開く
 * @desc メニューを開く時に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-メニュー閉じ
 * @desc メニューを閉じる時に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-バトル開始-前
 * @desc バトル開始前に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-バトル開始-後
 * @desc バトル開始後に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-バトル勝利-前
 * @desc バトル勝利時の遷移前に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-バトル勝利-後
 * @desc バトル勝利時の遷移後に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-バトル逃走-前
 * @desc バトル逃走時の遷移前に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-バトル逃走-後
 * @desc バトル逃走時の遷移後に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-バトル敗北-前
 * @desc バトル敗北時の遷移前に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-バトル敗北-後
 * @desc バトル敗北時の遷移後に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-ゲームオーバー
 * @desc ゲームオーバー時に実行するコモンイベントの有効/無効
 * @type boolean
 * @default
 *
 * @arg コモン-味方アクション-前
 * @desc 戦闘中、味方がスキル/アイテムを使う直前に実行するコモンイベントの有効/無効
 * @type common_event
 * @default
 *
 * @arg コモン-味方アクション-後
 * @desc 戦闘中、味方がスキル/アイテムを使った直後に実行するコモンイベントの有効/無効
 * @type common_event
 * @default
 *
 * @arg コモン-敵アクション-前
 * @desc 戦闘中、敵がスキル/アイテムを使う直前に実行するコモンイベントの有効/無効
 * @type common_event
 * @default
 *
 * @arg コモン-敵アクション-後
 * @desc 戦闘中、敵がスキル/アイテムを使った直後に実行するコモンイベントの有効/無効
 * @type common_event
 * @default
 *
 *
 *
 * @command タイミングコモン全無効
 * @desc タイミングコモンを全て無効にする。個別の有効/無効より優先される
 *
 *
 *
 * @command タイミングコモン全無効解除
 * @desc タイミングコモンの全無効を解除する
 *
 *
 *
 * @command タイミングコモン変更
 * @desc 各タイミングコモンの使用コモンイベントを変更する
 *
 * @arg コモン-ニューゲーム
 * @desc ニューゲーム時に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-データロード
 * @desc データロード時に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-場所移動-前
 * @desc 場所移動前に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-場所移動-後
 * @desc 場所移動後に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-メニュー開く
 * @desc メニューを開く時に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-メニュー閉じ
 * @desc メニューを閉じる時に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-バトル開始-前
 * @desc バトル開始前に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-バトル開始-後
 * @desc バトル開始後に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-バトル勝利-前
 * @desc バトル勝利時の遷移前に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-バトル勝利-後
 * @desc バトル勝利時の遷移後に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-バトル逃走-前
 * @desc バトル逃走時の遷移前に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-バトル逃走-後
 * @desc バトル逃走時の遷移後に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-バトル敗北-前
 * @desc バトル敗北時の遷移前に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-バトル敗北-後
 * @desc バトル敗北時の遷移後に実行するコモンイベント
 * @type common_event
 * @default
 *
 * @arg コモン-ゲームオーバー
 * @desc ゲームオーバー時に実行するコモンイベント。全滅した場合、回復しないとコモンを繰り返すので注意
 * @type common_event
 * @default
 */

(() => {
  //- プラグイン名
  const pluginName = document.currentScript.src.match(/^.*\/(.*).js$/)[1];

  //==================================================
  //-- ワードコンバート
  //==================================================

  const words = [
    "NewGame",
    "DataLoad",
    "TransfarPre",
    "TransfarAfter",
    "MenuOpen",
    "MenuClose",
    "BattleStartPre",
    "BattleStartAfter",
    "BattleVictoryPre",
    "BattleVictoryAfter",
    "BattleEscapePre",
    "BattleEscapeAfter",
    "BattleLosePre",
    "BattleLoseAfter",
    "GameOver",
    "actorActionPre",
    "actorActionAfter",
    "enemyActionPre",
    "enemyActionAfter",
  ];

  const jpns = [
    "ニューゲーム",
    "データロード",
    "場所移動-前",
    "場所移動-後",
    "メニュー開く",
    "メニュー閉じ",
    "バトル開始-前",
    "バトル開始-後",
    "バトル勝利-前",
    "バトル勝利-後",
    "バトル逃走-前",
    "バトル逃走-後",
    "バトル敗北-前",
    "バトル敗北-後",
    "ゲームオーバー",
    "味方アクション-前",
    "味方アクション-後",
    "敵アクション-前",
    "敵アクション-後",
  ];

  function toWord(jpn) {
    const index = jpns.indexOf(jpn);
    if (index < 0) {
      return;
    }
    return words[index];
  }

  function toJpn(word) {
    const index = words.indexOf(word);
    if (index < 0) {
      return;
    }
    return jpns[index];
  }

  //==================================================
  //--  パラメータ受け取り
  //==================================================

  //- 真偽化
  function toBoolean(str) {
    if (!str) {
      return false;
    }
    const str2 = str.toString().toLowerCase();
    if (str2 == "true" || str2 == "on") {
      return true;
    }
    if (str2 == "false" || str2 == "off") {
      return false;
    }
    return Number(str);
  }

  const parameters = PluginManager.parameters(pluginName);

  keke_varSubjectId = Number(parameters["変数-行動者ID"]);
  keke_varSkillId = Number(parameters["変数-スキルID"]);
  keke_varItemId = Number(parameters["変数-アイテムID"]);
  keke_varTargetIs = Number(parameters["変数-対象が敵か味方か"]);
  keke_varTargetId = Number(parameters["変数-対象ID"]);
  keke_varTargetAll = Number(parameters["変数-対象全て"]);

  //==================================================
  //--  プラグインコマンド
  //==================================================

  //- タイミングコモン有効/無効
  PluginManager.registerCommand(
    pluginName,
    "タイミングコモン有効/無効",
    (args) => {
      jpns.forEach((jpn) => {
        const param = args["コモン-" + jpn];
        if (param) {
          $gameMap._timingCommonInvalidsKe[toWord(jpn)] = !toBoolean(param);
        }
      });
    }
  );

  //- タイミングコモン全無効
  PluginManager.registerCommand(
    pluginName,
    "タイミングコモン全無効",
    (args) => {
      $gameMap._noTimingCommonKe = true;
    }
  );

  //- タイミングコモン全無効解除
  PluginManager.registerCommand(
    pluginName,
    "タイミングコモン全無効解除",
    (args) => {
      $gameMap._noTimingCommonKe = false;
    }
  );

  //- タイミングコモン変更
  PluginManager.registerCommand(pluginName, "タイミングコモン変更", (args) => {
    jpns.forEach((jpn) => {
      const param = args["コモン-" + jpn];
      if (param) {
        $gameMap._timingCommonsKe[toWord(jpn)] = Number(param);
      }
    });
  });

  //==================================================
  //--  共通開始
  //==================================================

  //- スプライトセット・マップ開始(コア追加)
  const _Spriteset_Map_initialize = Spriteset_Map.prototype.initialize;
  Spriteset_Map.prototype.initialize = function () {
    // タイミングコモンの初期化
    initTimingCommon();
    // マップ開始時コモン
    commonMapStart();
    _Spriteset_Map_initialize.apply(this);
  };

  //- スプライトセット・バトル開始(コア追加)
  const _Spriteset_Battle_initialize = Spriteset_Battle.prototype.initialize;
  Spriteset_Battle.prototype.initialize = function () {
    // タイミングコモンの初期化
    initTimingCommon();
    _Spriteset_Battle_initialize.apply(this);
  };

  //- シーン開始(コア追加)
  const _Scene_Map_start = Scene_Map.prototype.start;
  Scene_Map.prototype.start = function () {
    // マップ開始時コモン_2
    commonMapStart_2();
    _Scene_Map_start.apply(this);
  };

  //==================================================
  //--  共通更新
  //==================================================

  //- シーンベース更新(コア追加)
  const _Scene_Base_update = Scene_Base.prototype.update;
  Scene_Base.prototype.update = function () {
    _Scene_Base_update.apply(this);
    // コモンプリターの更新
    updateCommonPreter($gameMap);
    // コモンハンドラの更新
    updateCommonHandler($gameMap);
  };

  //==================================================
  //--  コモンの実行
  //==================================================

  //- タイミングコモンの初期化
  function initTimingCommon() {
    if ($gameMap._timingCommonsKe) {
      return;
    }
    const commons = {};
    words.forEach((word) => {
      commons[word] = Number(parameters["コモン-" + toJpn(word)]);
    });
    $gameMap._timingCommonsKe = commons;
    $gameMap._timingCommonInvalidsKe = {};
  }

  //- コモンイベントの呼び出し
  function callCommonEvent(word) {
    const id = getTimingCommon(word);
    if (!id) {
      return;
    }
    return doCommonEvent(id, $gameMap, word);
  }

  //- タイミングコモンの取得
  function getTimingCommon(word) {
    if ($gameMap._noTimingCommonKe) {
      return;
    }
    if (
      !$gameMap._timingCommonInvalidsKe ||
      $gameMap._timingCommonInvalidsKe[word]
    ) {
      return;
    }
    return $gameMap._timingCommonsKe[word];
  }

  //- コモンイベントの実行
  function doCommonEvent(id, body, word) {
    // コモンイベントを取得
    const commonEvent = $dataCommonEvents[id];
    if (!commonEvent) {
      return;
    }
    // 通常プリターの停止
    stopNormalPreter();
    // インタープリターを作成
    const preter = new Game_Interpreter(0);
    preter.tCommonType = word;
    if (!body._tCommonPretersKe) {
      body._tCommonPretersKe = [];
    }
    body._tCommonPretersKe.push(preter);
    // セットアップ
    preter.setup(commonEvent.list);
    // コモンプリターの更新
    updateCommonPreter(body);
    return true;
  }

  //- コモンプリターの更新
  function updateCommonPreter(body) {
    // ウェイトを更新
    if (body._tCommonPreterWaitKe) {
      body._tCommonPreterWaitKe--;
    }
    if (!body._tCommonPretersKe || !body._tCommonPretersKe.length) {
      return;
    }
    // プリターを更新
    let del = false;
    const preter = body._tCommonPretersKe[0];
    preter.update();
    if (!preter.isRunning()) {
      body._tCommonPretersKe.shift();
    }
    // 終了
    if (!body._tCommonPretersKe.length) {
      // 通常プリターの再開
      runNormalPreter();
      // ドット移動の停止を解除
      $gamePlayer._noDotMoveKe = false;
    }
  }

  //- コモンハンドラの更新
  function updateCommonHandler(body) {
    if (!body._tCommonHandlerKe) {
      return;
    }
    if (body._tCommonPretersKe && body._tCommonPretersKe.length) {
      return;
    }
    ignoreCommon = true;
    body._tCommonHandlerKe();
    body._tCommonHandlerKe = null;
    ignoreCommon = false;
  }

  //- タイミングコモン中か(公開)
  Game_Map.prototype.inTimmingCommonKe = function () {
    if (!this._tCommonPretersKe) {
      return false;
    }
    return this._tCommonPretersKe.some((preter) => preter.isRunning());
  };

  //- ゲームオーバコモン中か
  function inGameOverCommon() {
    const gm = $gameMap;
    if (!gm._tCommonPretersKe) {
      return false;
    }
    return gm._tCommonPretersKe.some(
      (preter) => preter.tCommonType == "GameOver"
    );
  }

  //- イベント中条件にタイミングコモンを追加
  const _Game_Map_isEventRunning = Game_Map.prototype.isEventRunning;
  Game_Map.prototype.isEventRunning = function () {
    let result = _Game_Map_isEventRunning.apply(this);
    result = result || this.inTimmingCommonKe();
    return result;
  };

  //- 通常プリターの停止
  function stopNormalPreter() {
    if (!$gameMap) {
      return;
    }
    $gameMap._interpreter._stopByTimingCommonKe = true;
    $gameTroop._interpreter._stopByTimingCommonKe = true;
  }

  //- 通常プリターの再開
  function runNormalPreter() {
    if (!$gameMap) {
      return;
    }
    $gameMap._interpreter._stopByTimingCommonKe = false;
    $gameTroop._interpreter._stopByTimingCommonKe = false;
  }

  //- 通常プリターの停止差し込み
  const _Game_Interpreter_updateChild = Game_Interpreter.prototype.updateChild;
  Game_Interpreter.prototype.updateChild = function () {
    if (this._stopByTimingCommonKe) {
      return true;
    }
    return _Game_Interpreter_updateChild.apply(this);
  };

  //==================================================
  //--  各タイミングでのコモン呼び出し
  //==================================================

  let ignoreCommon = false;
  let newGameAfter = false;
  let loadAfter = false;
  let gameOverAfter = false;
  let inGameOver = false;

  //- ニューゲーム後フラグ
  const _DataManager_setupNewGame = DataManager.setupNewGame;
  DataManager.setupNewGame = function () {
    _DataManager_setupNewGame.apply(this);
    newGameAfter = true;
  };

  //- ロード後フラグ

  const _Scene_Load_onLoadSuccess = Scene_Load.prototype.onLoadSuccess;
  Scene_Load.prototype.onLoadSuccess = function () {
    _Scene_Load_onLoadSuccess.call(this);
    // ロード後フラグをオン
    loadAfter = true;
  };

  //- 場所移動-前(コア追加)
  const _Game_Interpreter_command201 = Game_Interpreter.prototype.command201;
  Game_Interpreter.prototype.command201 = function (params) {
    if (!ignoreCommon) {
      const gm = $gameMap;
      if (gm._tCommonHandlerKe) {
        return false;
      }
      const run = callCommonEvent("TransfarPre");
      if (run) {
        gm._tCommonHandlerKe = this.executeCommand.bind(this);
        return false;
      }
    }
    return _Game_Interpreter_command201.apply(this, arguments);
  };

  //- メニュー開く(コア追加)
  const _Scene_Map_callMenu = Scene_Map.prototype.callMenu;
  Scene_Map.prototype.callMenu = function () {
    if (!ignoreCommon) {
      const gm = $gameMap;
      if (gm._tCommonHandlerKe) {
        return;
      }
      const run = callCommonEvent("MenuOpen");
      if (run) {
        gm._tCommonHandlerKe = this.callMenu.bind(this);
        return;
      }
    }
    _Scene_Map_callMenu.apply(this);
  };

  //- バトル開始-前(コア追加)
  const _Game_Interpreter_command301 = Game_Interpreter.prototype.command301;
  Game_Interpreter.prototype.command301 = function (params) {
    if (!ignoreCommon) {
      const gm = $gameMap;
      if (gm._tCommonHandlerKe) {
        return false;
      }
      const run = callCommonEvent("BattleStartPre");
      if (run) {
        gm._tCommonHandlerKe = this.executeCommand.bind(this);
        return false;
      }
    }
    return _Game_Interpreter_command301.apply(this, arguments);
  };

  //- バトル開始-後(コア追加)
  const _Scene_Battle_start = Scene_Battle.prototype.start;
  Scene_Battle.prototype.start = function () {
    _Scene_Battle_start.apply(this);
    setTimeout(callCommonEvent, 0, "BattleStartAfter");
  };

  //- 戦闘結果を保存
  let battleResult = "";

  const _BattleManager_processVictory = BattleManager.processVictory;
  BattleManager.processVictory = function () {
    _BattleManager_processVictory.apply(this);
    battleResult = "Victory";
  };

  const _BattleManager_processAbort = BattleManager.processAbort;
  BattleManager.processAbort = function () {
    _BattleManager_processAbort.apply(this);
    battleResult = "Escape";
  };

  const _BattleManager_processDefeat = BattleManager.processDefeat;
  BattleManager.processDefeat = function () {
    _BattleManager_processDefeat.apply(this);
    battleResult = "Lose";
  };

  //- バトル終了-前(コア追加)
  const _BattleManager_updateBattleEnd = BattleManager.updateBattleEnd;
  BattleManager.updateBattleEnd = function () {
    if (!ignoreCommon) {
      const gm = $gameMap;
      if (gm._tCommonHandlerKe) {
        return true;
      }
      const run = callCommonEvent("Battle" + battleResult + "Pre");
      if (run) {
        gm._tCommonHandlerKe = BattleManager.updateBattleEnd.bind(this);
        return true;
      }
    }
    _BattleManager_updateBattleEnd.apply(this);
  };

  //- ゲームオーバー
  const _SceneManager_goto = SceneManager.goto;
  SceneManager.goto = function (sceneClass) {
    if (sceneClass == Scene_Gameover) {
      if (getTimingCommon("GameOver")) {
        if (gameOverAfter || inGameOverCommon()) {
          return;
        }
        // マップ以外ならマップ移行後コモン
        if (this._scene.constructor.name != "Scene_Map") {
          sceneClass = Scene_Map;
          gameOverAfter = true;
          // マップならすぐにコモン
        } else {
          callCommonEvent("GameOver");
          return;
        }
      }
    }
    _SceneManager_goto.apply(this, arguments);
  };

  //- マップ開始時コモン
  function commonMapStart() {
    // ニューゲーム
    if (newGameAfter) {
      callCommonEvent("NewGame");
      newGameAfter = false;
    }
    // データロード
    if (loadAfter) {
      callCommonEvent("DataLoad");
      loadAfter = false;
    }
    // メニュー閉じ
    if (SceneManager.isPreviousScene(Scene_Menu)) {
      callCommonEvent("MenuClose");
    }
    // バトル終了-後
    if (SceneManager.isPreviousScene(Scene_Battle)) {
      callCommonEvent("Battle" + battleResult + "After");
    }
    // ゲームオーバー
    if (gameOverAfter) {
      callCommonEvent("GameOver");
      gameOverAfter = false;
    }
  }

  //- マップ開始時コモン_2
  function commonMapStart_2() {
    // 場所移動-後
    if (SceneManager.isPreviousScene(Scene_Map)) {
      callCommonEvent("TransfarAfter");
    }
  }

  //- アクションコモン-前
  let didPreActionCommon = false;
  let inPreActionCommon = false;

  const _BattleManager_startAction = BattleManager.startAction;
  BattleManager.startAction = function () {
    if (!$gameParty._inReactionKe) {
      const gm = $gameMap;
      if (didPreActionCommon && gm._tCommonHandlerKe) {
        return;
      }
      if (!didPreActionCommon) {
        didPreActionCommon = true;
        if (gm._tCommonHandlerKe) {
          return;
        }
        // アクションデータの保存
        const isEnemy = saveActionData(this);
        const subject = isEnemy ? "enemy" : "actor";
        const run = callCommonEvent(subject + "ActionPre");
        if (run) {
          inPreActionCommon = true;
          return;
        }
      }
    }
    _BattleManager_startAction.apply(this);
    if (inPreActionCommon && !$gameParty._inReactionKe) {
      inPreActionCommon = false;
      //removeCurrentAction(this._subject);
    }
  };

  // アクションコモン-後
  const _BattleManager_endAction = BattleManager.endAction;
  BattleManager.endAction = function () {
    // アクションデータの保存
    if (!$gameParty._inReactionKe) {
      const isEnemy = saveActionData(this, true);
      const subject = isEnemy ? "enemy" : "actor";
      callCommonEvent(subject + "ActionAfter");
      // 前アクションコモンフラグをオフ
      didPreActionCommon = false;
    }
    _BattleManager_endAction.apply(this);
  };

  //- アクションデータの保存
  function saveActionData(bm, isEnd) {
    // 行動者ID
    const subject = bm._subject;
    const actIsEnemy = subject._enemyId;
    const subjectId = actIsEnemy ? subject._enemyId : subject._actorId;
    // スキルID/アイテムID
    const action = subject.currentAction();
    if (!action) {
      return actIsEnemy;
    }
    const item = action._item;
    const obje = action.item();
    if (!obje) {
      return actIsEnemy;
    }
    const isItem = item.isItem();
    const skillId = isItem ? 0 : obje.id;
    const itemId = isItem ? obje.id : 0;
    // ターゲットID
    const targets = isEnd ? bm._targets : action.makeTargets();
    const target = targets[0];
    const tageIsActor = target && target._actorId ? 1 : 0;
    const targetId = target
      ? tageIsActor
        ? target._actorId
        : target._enemyId
      : 0;
    const targetIds = targets.map((t) =>
      t._enemyId ? t._enemyId : t._actorId
    );
    if (keke_varSubjectId) {
      $gameVariables.setValue(keke_varSubjectId, subjectId);
    }
    if (keke_varSkillId) {
      $gameVariables.setValue(keke_varSkillId, skillId);
    }
    if (keke_varItemId) {
      $gameVariables.setValue(keke_varItemId, itemId);
    }
    if (keke_varTargetIs) {
      $gameVariables.setValue(keke_varTargetIs, tageIsActor);
    }
    if (keke_varTargetId) {
      $gameVariables.setValue(keke_varTargetId, targetId);
    }
    if (keke_varTargetAll) {
      $gameVariables.setValue(keke_varTargetAll, targetIds);
    }
    return actIsEnemy;
  }

  // カレントアクション削除の遅延
  const _Game_Battler_removeCurrentAction =
    Game_Battler.prototype.removeCurrentAction;
  Game_Battler.prototype.removeCurrentAction = function () {
    if (inPreActionCommon) {
      return;
    }
    _Game_Battler_removeCurrentAction.apply(this);
  };

  function removeCurrentAction(subject) {
    if (subject._actions) {
      subject.removeCurrentAction();
    }
  }
})();

repo_name: dazed/translations

license: unknown


code:
//=============================================================================
// RPGツクールMZ - LL_ExGaugeDrawing.js v1.1.0
//-----------------------------------------------------------------------------
// ルルの教会 (Lulu's Church)
// https://nine-yusha.com/
//
// URL below for license details.
// https://nine-yusha.com/plugin/
//=============================================================================

/*:
 * @target MZ
 * @plugindesc ゲージ描画をカスタマイズします。
 * @author ルルの教会
 * @url https://nine-yusha.com/plugin-exgauge/
 *
 * @help LL_ExGaugeDrawing.js
 *
 * ゲージ描画処理を拡張します。
 *   ・ゲージ枠の描画、ゲージ色を立体感のある描画へ変更
 *   ・各種ゲージ色の変更
 *   ・ゲージ高さの変更
 *   ・残り少ない時に点滅させる
 *   ・HP・MPの最大値を表示
 *   ・ラベルや数値の位置・サイズを調整
 *   ・ゲージなしにして数値のみにすることも可能
 *
 * プラグインコマンドはありません。
 *
 * 利用規約:
 *   ・著作権表記は必要ございません。
 *   ・利用するにあたり報告の必要は特にございません。
 *   ・商用・非商用問いません。
 *   ・R18作品にも使用制限はありません。
 *   ・ゲームに合わせて自由に改変していただいて問題ございません。
 *   ・プラグイン素材としての再配布（改変後含む）は禁止させていただきます。
 *
 * 作者: ルルの教会
 * 作成日: 2021/9/22
 *
 * @param gaugeHeight
 * @text ゲージの高さ
 * @desc 描画されるゲージの高さです。(初期値: 10)
 * @type number
 * @default 10
 *
 * @param solidGradation
 * @text ゲージに立体感をつける
 * @desc OFFにすると通常のゲージ描画になります。
 * @type boolean
 * @default true
 *
 * @param gaugeBackColor
 * @text ゲージの背景色
 * @desc ゲージの背景色をCSSカラーコードで指定します。
 * @default #202040
 * @type string
 *
 * @param gaugeOutlineColor
 * @text ゲージの枠色
 * @desc ゲージの枠色をCSSカラーコードで指定します。
 * 枠を付けたくない場合は、背景色と同色にしてください。
 * @default #fff
 * @type string
 *
 * @param labelY
 * @text ラベルのY座標
 * @desc ラベルの縦位置を調整します。 (初期値: 4)
 * プラスにすると下へ、マイナスにすると上へ移動します。
 * @default 4
 * @type number
 * @min -100
 * @max 100
 *
 * @param labelFontSize
 * @text ラベルのフォントサイズ
 * @desc ラベルのフォントサイズを調整します。 (初期値: -8)
 * 標準フォントサイズからどれだけ小さくするか設定。
 * @default -8
 * @type number
 * @min -100
 * @max 100
 *
 * @param valueAdjustY
 * @text 現在値のY座標
 * @desc 現在値の縦位置を調整します。 (初期値: 0)
 * プラスにすると下へ、マイナスにすると上へ移動します。
 * @default 0
 * @type number
 * @min -100
 * @max 100
 *
 * @param valueFontSize
 * @text 現在値のフォントサイズ
 * @desc 現在値のフォントサイズを調整します。 (初期値: -10)
 * 標準フォントサイズからどれだけ小さくするか設定。
 * @default -10
 * @type number
 * @min -100
 * @max 100
 *
 * @param maxValueWidth
 * @text 最大値の横幅
 * @desc 最大値の横幅を調整します。 (初期値: 32)
 * この数値を大きくすると、値と最大値の距離が広がります。
 * @default 32
 * @type number
 * @min -100
 * @max 100
 *
 * @param maxValueAdjustY
 * @text 最大値のY座標
 * @desc 最大値の縦位置を調整します。 (初期値: 2)
 * プラスにすると下へ、マイナスにすると上へ移動します。
 * @default 2
 * @type number
 * @min -100
 * @max 100
 *
 * @param maxValueFontSize
 * @text 最大値のフォントサイズ
 * @desc 最大値のフォントサイズを標準のフォントサイズから
 * どれだけ小さくするかで設定します。 (初期値: -14)
 * @default -14
 * @type number
 * @min -100
 * @max 100
 *
 * @param hpGauge
 * @text HPゲージの設定
 * @desc HPゲージの設定です。
 * @default {"gaugeColor1":"#ff784c","gaugeColor2":"#ffffa0","lowPercentage":"25","gaugeAlertColor1":"#ff2020","gaugeAlertColor2":"#ff784c","lowAlert":"battle","maxValueEnable":"true","gaugeX":"24","gaugeHidden":"false"}
 * @type struct<settings>
 *
 * @param mpGauge
 * @text MPゲージの設定
 * @desc MPゲージの設定です。
 * @default {"gaugeColor1":"#4080c0","gaugeColor2":"#99ccff","lowPercentage":"25","gaugeAlertColor1":"#ff2020","gaugeAlertColor2":"#ff784c","lowAlert":"none","maxValueEnable":"true","gaugeX":"24","gaugeHidden":"false"}
 * @type struct<settings>
 *
 * @param tpGauge
 * @text TPゲージの設定
 * @desc TPゲージの設定です。
 * @default {"gaugeColor1":"#00a040","gaugeColor2":"#80ff80","lowPercentage":"0","gaugeAlertColor1":"#ff2020","gaugeAlertColor2":"#ff784c","lowAlert":"none","maxValueEnable":"false","gaugeX":"24","gaugeHidden":"false"}
 * @type struct<settings>
 *
 * @param timeGauge
 * @text TIMEゲージの設定
 * @desc TIMEゲージ(タイムプログレスバー)の設定です。
 * @default {"gaugeColor1":"#a060e0","gaugeColor2":"#ccc0ff","lowPercentage":"0","gaugeAlertColor1":"#ff2020","gaugeAlertColor2":"#ff784c","lowAlert":"none","maxValueEnable":"false","gaugeX":"0","gaugeHidden":"false"}
 * @type struct<settings>
 */

/*~struct~settings:
 *
 * @param gaugeColor1
 * @text ゲージの色1
 * @desc ゲージの色1をCSSカラーコードで指定します。
 * @type string
 *
 * @param gaugeColor2
 * @text ゲージの色2
 * @desc ゲージの色2をCSSカラーコードで指定します。
 * @type string
 *
 * @param lowPercentage
 * @text ゲージを赤くする％
 * @desc 指定％以下になったときにゲージを赤くします。
 * 無効にしたい場合は0を入力してください。
 * @type number
 * @min 0
 * @max 100
 *
 * @param gaugeAlertColor1
 * @text ゲージ赤の色1
 * @desc 赤くなった時のゲージ色1をCSSカラーコードで指定します。
 * @type string
 *
 * @param gaugeAlertColor2
 * @text ゲージ赤の色2
 * @desc 赤くなった時のゲージ色2をCSSカラーコードで指定します。
 * @type string
 *
 * @param lowAlert
 * @text 点滅アラート
 * @desc ゲージが赤くなった時の動作を選択してください。
 * @type select
 * @option 点滅しない
 * @value none
 * @option 戦闘中のみ点滅
 * @value battle
 * @option 常に点滅
 * @value always
 *
 * @param maxValueEnable
 * @text 最大値を表示
 * @desc 最大値を表示します。
 * @type boolean
 * @default true
 *
 * @param gaugeX
 * @text 始点X座標
 * @desc ゲージの始点X座標を指定します。
 * 0にするとラベルと重なります。
 * @type number
 *
 * @param gaugeHidden
 * @text ゲージを非表示
 * @desc ONにするとゲージが非表示になります。
 * @type boolean
 * @default false
 */

(() => {
  "use strict";
  const pluginName = "LL_ExGaugeDrawing";

  const parameters = PluginManager.parameters(pluginName);
  const gaugeHeight = Number(parameters["gaugeHeight"] || 10);
  const solidGradation = eval(parameters["solidGradation"] || "true");
  const gaugeBackColor = String(parameters["gaugeBackColor"] || "#202040");
  const gaugeOutlineColor = String(parameters["gaugeOutlineColor"] || "#fff");
  const labelY = Number(parameters["labelY"] || 4);
  const labelFontSize = Number(parameters["labelFontSize"] || -8);
  const valueAdjustY = Number(parameters["valueAdjustY"] || 0);
  const valueFontSize = Number(parameters["valueFontSize"] || -10);
  const maxValueWidth = Number(parameters["maxValueWidth"] || 32);
  const maxValueAdjustY = Number(parameters["maxValueAdjustY"] || 2);
  const maxValueFontSize = Number(parameters["maxValueFontSize"] || -14);

  const hpGaugeSettings = JSON.parse(parameters["hpGauge"] || "null");
  const mpGaugeSettings = JSON.parse(parameters["mpGauge"] || "null");
  const tpGaugeSettings = JSON.parse(parameters["tpGauge"] || "null");
  const timeGaugeSettings = JSON.parse(parameters["timeGauge"] || "null");
  //const normalGaugeSettings = JSON.parse(parameters["normalGauge"] || "null");
  const normalGaugeSettings = {
    gaugeColor1: "#fff",
    gaugeColor2: "#fff",
    lowPercentage: "0",
    gaugeAlertColor1: "#ff2020",
    gaugeAlertColor2: "#ff784c",
    lowAlert: "none",
    maxValueEnable: "false",
    gaugeX: "0",
    gaugeHidden: "false",
  };

  // MZ for v1.3.3
  Sprite_Gauge.prototype.textHeight = function () {
    return 24;
  };

  Sprite_Gauge.prototype.gaugeHeight = function () {
    return gaugeHeight;
  };

  Sprite_Gauge.prototype.gaugeX = function () {
    if (this._statusType == "hp") return Number(hpGaugeSettings.gaugeX);
    if (this._statusType == "mp") return Number(mpGaugeSettings.gaugeX);
    if (this._statusType == "tp") return Number(tpGaugeSettings.gaugeX);
    if (this._statusType == "time") return Number(timeGaugeSettings.gaugeX);
    return Number(normalGaugeSettings.gaugeX);
  };

  Sprite_Gauge.prototype.labelY = function () {
    return labelY;
  };

  Sprite_Gauge.prototype.labelFontSize = function () {
    return $gameSystem.mainFontSize() + labelFontSize;
  };

  Sprite_Gauge.prototype.gaugeBackColor = function () {
    return gaugeBackColor;
  };

  Sprite_Gauge.prototype.gaugeOutlineColor = function () {
    return gaugeOutlineColor;
  };

  Sprite_Gauge.prototype.valueFontSize = function () {
    return $gameSystem.mainFontSize() + valueFontSize;
  };

  Sprite_Gauge.prototype.labelOutlineWidth = function () {
    return 3;
  };

  Sprite_Gauge.prototype.valueOutlineWidth = function () {
    return 3;
  };

  Sprite_Gauge.prototype.gaugeColor1 = function () {
    const rate = this.gaugeRate();
    switch (this._statusType) {
      case "hp":
        if (rate < Number(hpGaugeSettings.lowPercentage) / 100) {
          return hpGaugeSettings.gaugeAlertColor1;
        }
        return hpGaugeSettings.gaugeColor1;
      case "mp":
        if (rate < Number(mpGaugeSettings.lowPercentage) / 100) {
          return mpGaugeSettings.gaugeAlertColor1;
        }
        return mpGaugeSettings.gaugeColor1;
      case "tp":
        if (rate < Number(tpGaugeSettings.lowPercentage) / 100) {
          return tpGaugeSettings.gaugeAlertColor1;
        }
        return tpGaugeSettings.gaugeColor1;
      case "time":
        if (rate < Number(timeGaugeSettings.lowPercentage) / 100) {
          return timeGaugeSettings.gaugeAlertColor1;
        }
        return timeGaugeSettings.gaugeColor1;
      default:
        if (rate < Number(normalGaugeSettings.lowPercentage) / 100) {
          return normalGaugeSettings.gaugeAlertColor1;
        }
        return normalGaugeSettings.gaugeColor1;
    }
  };

  Sprite_Gauge.prototype.gaugeColor2 = function () {
    const rate = this.gaugeRate();
    switch (this._statusType) {
      case "hp":
        if (rate < Number(hpGaugeSettings.lowPercentage) / 100) {
          return hpGaugeSettings.gaugeAlertColor2;
        }
        return hpGaugeSettings.gaugeColor2;
      case "mp":
        if (rate < Number(mpGaugeSettings.lowPercentage) / 100) {
          return mpGaugeSettings.gaugeAlertColor2;
        }
        return mpGaugeSettings.gaugeColor2;
      case "tp":
        if (rate < Number(tpGaugeSettings.lowPercentage) / 100) {
          return tpGaugeSettings.gaugeAlertColor2;
        }
        return tpGaugeSettings.gaugeColor2;
      case "time":
        if (rate < Number(timeGaugeSettings.lowPercentage) / 100) {
          return timeGaugeSettings.gaugeAlertColor2;
        }
        return timeGaugeSettings.gaugeColor2;
      default:
        if (rate < Number(normalGaugeSettings.lowPercentage) / 100) {
          return normalGaugeSettings.gaugeAlertColor2;
        }
        return normalGaugeSettings.gaugeColor2;
    }
  };

  Sprite_Gauge.prototype.drawGaugeRect = function (x, y, width, height) {
    // ゲージ非表示オプション
    if (
      this._statusType === "hp" &&
      eval(hpGaugeSettings.gaugeHidden || "false")
    )
      return;
    if (
      this._statusType === "mp" &&
      eval(mpGaugeSettings.gaugeHidden || "false")
    )
      return;
    if (
      this._statusType === "tp" &&
      eval(tpGaugeSettings.gaugeHidden || "false")
    )
      return;
    if (
      this._statusType === "time" &&
      eval(timeGaugeSettings.gaugeHidden || "false")
    )
      return;

    const rate = this.gaugeRate();
    const fillW = Math.floor((width - 2) * rate);
    const fillH = height - 2;
    const color0 = this.gaugeOutlineColor();
    const color0b = this.gaugeBackColor();
    const color1 = this.gaugeColor1();
    const color2 = this.gaugeColor2();
    this.bitmap.fillRect(x, y, width, height, color0);
    this.bitmap.fillRect(x + 1, y + 1, width - 2, height - 2, color0b);
    this.bitmap.gradientFillRect(
      x + 1,
      y + 1,
      fillW,
      fillH,
      color1,
      color2,
      false
    );
    // Solid Gradation
    if (solidGradation) {
      this.bitmap.gradientFillRect(
        x + 1,
        y + 1,
        fillW,
        fillH / 3,
        "rgba(255, 255, 255, 0.1)",
        "rgba(255, 255, 255, 0.7)",
        true
      );
      this.bitmap.gradientFillRect(
        x + 1,
        y + fillH / 3 + 1,
        fillW,
        fillH / 2,
        "rgba(0, 0, 0, 0.2)",
        "rgba(0, 0, 0, 0)",
        true
      );
    }
  };

  Sprite_Gauge.prototype.updateFlashing = function () {
    this._flashingCount++;
    if (this._statusType === "time") {
      if (this._battler.isInputting()) {
        if (this._flashingCount % 30 < 15) {
          this.setBlendColor(this.flashingColor1());
        } else {
          this.setBlendColor(this.flashingColor2());
        }
        return;
      } else {
        this.setBlendColor([0, 0, 0, 0]);
      }
    }

    // Alert Flashing
    switch (this._statusType) {
      case "hp":
        this.updateLowAlert(hpGaugeSettings);
        break;
      case "mp":
        this.updateLowAlert(mpGaugeSettings);
        break;
      case "tp":
        this.updateLowAlert(tpGaugeSettings);
        break;
      case "time":
        this.updateLowAlert(timeGaugeSettings);
        break;
      default:
        this.updateLowAlert(normalGaugeSettings);
    }
  };

  Sprite_Gauge.prototype.updateLowAlert = function (gaugeSettings) {
    const rate = this.gaugeRate();
    if (String(gaugeSettings.lowAlert) != "none") {
      if (
        String(gaugeSettings.lowAlert) == "always" ||
        (String(gaugeSettings.lowAlert) == "battle" && $gameParty.inBattle())
      ) {
        if (
          !this._battler.isDead() &&
          rate < Number(gaugeSettings.lowPercentage) / 100
        ) {
          if (this._flashingCount % 30 < 15) {
            this.setBlendColor([255, 92, 92, 92]);
          } else {
            this.setBlendColor([0, 0, 0, 0]);
          }
        } else {
          this.setBlendColor([0, 0, 0, 0]);
        }
      }
    }
  };

  Sprite_Gauge.prototype.drawValue = function () {
    // Max Value
    switch (this._statusType) {
      case "hp":
        this.drawValueWithMax(hpGaugeSettings);
        break;
      case "mp":
        this.drawValueWithMax(mpGaugeSettings);
        break;
      case "tp":
        this.drawValueWithMax(tpGaugeSettings);
        break;
      case "time":
        this.drawValueWithMax(timeGaugeSettings);
        break;
      default:
        this.drawValueWithMax(normalGaugeSettings);
    }
  };

  Sprite_Gauge.prototype.drawValueWithMax = function (gaugeSettings) {
    const currentValue = this.currentValue();
    const maxValue = this.currentMaxValue();
    const width = this.bitmapWidth();
    const height = this.textHeight();
    this.setupValueFont();
    if (eval(gaugeSettings.maxValueEnable || "true")) {
      this.bitmap.drawText(
        currentValue + "/",
        maxValueWidth * -1,
        valueAdjustY,
        width,
        height,
        "right"
      );
      this.bitmap.fontSize = $gameSystem.mainFontSize() + maxValueFontSize;
      this.bitmap.drawText(
        maxValue,
        0,
        maxValueAdjustY,
        width,
        height,
        "right"
      );
      this.bitmap.fontSize = this.valueFontSize();
    } else {
      this.bitmap.drawText(
        currentValue,
        0,
        valueAdjustY,
        width,
        height,
        "right"
      );
    }
  };
})();


code:
//=============================================================================
// RPGツクールMZ - LL_GalgeChoiceWindow.js v2.0.1
//-----------------------------------------------------------------------------
// ルルの教会 (Lulu's Church)
// https://nine-yusha.com/
//
// URL below for license details.
// https://nine-yusha.com/plugin/
//=============================================================================

/*:
 * @target MZ
 * @plugindesc ノベルゲーム風選択肢ウィンドウプラグイン
 * @author ルルの教会
 * @url https://nine-yusha.com/plugin-galgechoicewindow/
 *
 * @help LL_GalgeChoiceWindow.js
 *
 * ノベルゲーム風の選択肢ウィンドウを表示します。
 * 独自のウィンドウ画像、ボタン画像を設定することもできます。
 * (画像はimg/systemフォルダに配置してください)
 *
 * プラグインコマンド:
 *   選択肢の表示: 選択肢を表示し、結果を変数で受け取ります。
 *
 * 利用規約:
 *   ・著作権表記は必要ございません。
 *   ・利用するにあたり報告の必要は特にございません。
 *   ・商用・非商用問いません。
 *   ・R18作品にも使用制限はありません。
 *   ・ゲームに合わせて自由に改変していただいて問題ございません。
 *   ・プラグイン素材としての再配布（改変後含む）は禁止させていただきます。
 *
 * 作者: ルルの教会
 * 作成日: 2022/12/9
 *
 * @command showChoice
 * @text 選択肢の表示
 * @desc 選択肢を表示し、結果を変数で受け取ります。
 *
 * @arg messageText
 * @text 質問メッセージ
 * @desc 質問メッセージです。 (2行以内推奨)
 * \V[n]、\N[n]、\P[n]、\Gの制御文字が使用できます。
 * @type multiline_string
 *
 * @arg choices
 * @text 選択肢リスト
 * @desc 選択肢として表示する文字列のリストです。
 * @default []
 * @type struct<choices>[]
 *
 * @arg maxCols
 * @text 選択肢列数
 * @desc 選択肢を何列で表示するかの設定です。
 * @default 1
 * @min 1
 * @max 20
 * @type number
 *
 * @arg variableNumber
 * @text 結果受け取り変数
 * @desc 選択結果を受け取る変数IDを指定します。
 * @type variable
 *
 * @arg cancelType
 * @text キャンセル許可
 * @desc キャンセルされた場合は-1が変数に代入されます。
 * @default true
 * @type boolean
 *
 * @arg iniPosition
 * @text 初期カーソル位置
 * @desc カーソルの初期位置です。(0にすると選択なし)
 * 数値を入力するか変数で指定することもできます。
 * @default 1
 * @type combo
 * @option $gameVariables.value(1)   // 変数ID:1の値
 * @option 0
 * @option 1
 * @option 2
 * @option 3
 * @option 4
 * @option 5
 * @option 6
 *
 * @arg windowWidth
 * @text ウィンドウ幅
 * @desc 選択肢ウィンドウの幅です。
 * 背景画像を使用する場合は横幅を調整してください。
 * @default 480
 * @min 0
 * @max 9999
 * @type number
 *
 * @arg windowBgImage
 * @text ウィンドウ画像
 * @desc ウィンドウ画像を使用する場合は画像を選択してください。
 * 標準のウィンドウ枠は非表示になります。
 * @dir img/system
 * @type file
 * @require 1
 *
 * @arg buttonBgImage
 * @text ボタン画像
 * @desc ボタン画像を使用する場合は画像を選択してください。
 * @dir img/system
 * @type file
 * @require 1
 *
 * @arg buttonFocusImage
 * @text ボタン画像(選択時)
 * @desc フォーカス時のボタン画像を選択してください。
 * ベースとなるボタン画像と大きさを必ず統一してください。
 * @dir img/system
 * @type file
 * @require 1
 *
 * @arg buttonImageAdjustX
 * @text ボタン画像X座標
 * @desc ボタン画像の表示位置(X)の調整値です。(初期値: 0)
 * ＋で右へ、－で左へ調整します。
 * @default 0
 * @min -9999
 * @max 9999
 * @type number
 *
 * @arg buttonImageAdjustY
 * @text ボタン画像Y座標
 * @desc ボタン画像の表示位置(Y)の調整値です。(初期値: 0)
 * ＋で下へ、－で上へ調整します。
 * @default 0
 * @min -9999
 * @max 9999
 * @type number
 *
 * @arg itemHeightAdjust
 * @text 選択肢項目高さ
 * @desc 選択肢項目の高さの調整値です。(初期値: 8)
 * 画像が重なってしまう場合はこの数値を大きくしてください。
 * @default 8
 * @min -9999
 * @max 9999
 * @type number
 */

/*~struct~choices:
 *
 * @param label
 * @text 選択肢文字列
 * @desc 選択肢として表示する文字列です。
 * \V[n]、\N[n]、\P[n]、\Gの制御文字が使用できます。
 * @type string
 *
 * @param switchId
 * @text スイッチID
 * @desc スイッチON時のみ選択肢を表示したい場合に設定します。
 * 通常は「なし」のままで問題ありません。
 * @type switch
 *
 * @param resultNumber
 * @text 結果返却値
 * @desc この選択肢が選ばれた時に変数に入力する値を指定できます。
 * 「0」にすると自動で選択肢番号が返却されます。
 * @default 0
 * @min 0
 * @max 99999999
 * @type number
 */

(() => {
  "use strict";
  const pluginName = "LL_GalgeChoiceWindow";

  const parameters = PluginManager.parameters(pluginName);

  // Padding
  const gcwWindowPadding = 32;
  // キャンセル許可時のタッチUI戻るボタンの表示
  const gcwCancelButtonEnabled = false;
  // フェードイン効果フレーム
  const gcwFadeInFrame = 32;

  // 設定保存用変数定義
  let messageTextLists = [];
  let choiceLists = [];
  let maxCols = 1;
  let variableNumber = 0;
  let cancelType = true;
  let iniPosition = 1;
  let gcwWindowWidth = 480;
  let backgroundImage = "";
  let gcwButtonImage = "";
  let gcwButtonFocusImage = "";
  let gcwButtonAdjustX = 0;
  let gcwButtonAdjustY = 0;
  let gcwItemHeightAdjust = 8;

  PluginManager.registerCommand(pluginName, "showChoice", function (args) {
    // 設定を取得
    const messageText = String(args.messageText).split("\n");
    const choices = JSON.parse(args.choices || "null");
    maxCols = Number(args.maxCols || 1);
    variableNumber = Number(args.variableNumber);
    cancelType = String(args.cancelType) === "true" ? true : false;
    iniPosition = eval(args.iniPosition);
    gcwWindowWidth = Number(args.windowWidth || 480);
    backgroundImage = String(args.windowBgImage || "");
    gcwButtonImage = String(args.buttonBgImage || "");
    gcwButtonFocusImage = String(args.buttonFocusImage || "");
    gcwButtonAdjustX = Number(args.buttonImageAdjustX || 0);
    gcwButtonAdjustY = Number(args.buttonImageAdjustY || 0);
    gcwItemHeightAdjust = Number(args.itemHeightAdjust || 8);

    messageTextLists = [];
    if (messageText != "") {
      messageText.forEach((elm) => {
        messageTextLists.push(String(elm));
      });
    }

    // 選択肢を定義
    choiceLists = [];
    if (choices) {
      choices.forEach((elm) => {
        if (
          Number(JSON.parse(elm).switchId) === 0 ||
          $gameSwitches.value(Number(JSON.parse(elm).switchId))
        ) {
          choiceLists.push(JSON.parse(elm));
        }
      });
    }

    // 選択肢が空の場合、終了
    if (choiceLists.length == 0) {
      console.log(
        pluginName +
          ": 有効な選択肢が存在しなかったため、コマンドがスキップされました"
      );
      return;
    }

    if (!$gameMessage.isBusy()) {
      $gameMessage.setGalgeChoices(choiceLists);
      this.setWaitMode("message");
    }
  });

  //-----------------------------------------------------------------------------
  // Game_Message
  //

  const _Game_Message_clear = Game_Message.prototype.clear;
  Game_Message.prototype.clear = function () {
    _Game_Message_clear.apply(this, arguments);

    this._galgeChoices = [];
  };

  Game_Message.prototype.galgeChoices = function () {
    return this._galgeChoices;
  };

  Game_Message.prototype.isGalgeChoice = function () {
    return this._galgeChoices.length > 0;
  };

  Game_Message.prototype.setGalgeChoices = function (choices) {
    this._galgeChoices = choices;
  };

  const _Game_Message_isBusy = Game_Message.prototype.isBusy;
  Game_Message.prototype.isBusy = function () {
    return _Game_Message_isBusy.apply(this, arguments) || this.isGalgeChoice();
  };

  //-----------------------------------------------------------------------------
  // Scene_Message
  //

  const _Scene_Message_createAllWindows =
    Scene_Message.prototype.createAllWindows;
  Scene_Message.prototype.createAllWindows = function () {
    this.createGalgeChoiceListWindow();
    _Scene_Message_createAllWindows.apply(this, arguments);
  };

  Scene_Message.prototype.createGalgeChoiceListWindow = function () {
    this._galgeChoiceListWindow = new Window_GalgeChoiceList();
    this.addWindow(this._galgeChoiceListWindow);
  };

  const _Scene_Message_associateWindows =
    Scene_Message.prototype.associateWindows;
  Scene_Message.prototype.associateWindows = function () {
    _Scene_Message_associateWindows.apply(this, arguments);

    const messageWindow = this._messageWindow;
    messageWindow.setGalgeChoiceListWindow(this._galgeChoiceListWindow);
    if (this._galgeChoiceListWindow)
      this._galgeChoiceListWindow.setMessageWindow(messageWindow);
  };

  //-----------------------------------------------------------------------------
  // Window_Message
  //

  const _Window_Message_initMembers = Window_Message.prototype.initMembers;
  Window_Message.prototype.initMembers = function () {
    _Window_Message_initMembers.apply(this, arguments);

    this._galgeChoiceListWindow = null;
  };

  const _Window_Message_startInput = Window_Message.prototype.startInput;
  Window_Message.prototype.startInput = function () {
    if ($gameMessage.isGalgeChoice()) {
      this._galgeChoiceListWindow.start();
      return true;
    }

    return _Window_Message_startInput.apply(this, arguments);
  };

  Window_Message.prototype.setGalgeChoiceListWindow = function (
    galgeChoiceListWindow
  ) {
    this._galgeChoiceListWindow = galgeChoiceListWindow;
  };

  const _Window_Message_isAnySubWindowActive =
    Window_Message.prototype.isAnySubWindowActive;
  Window_Message.prototype.isAnySubWindowActive = function () {
    if (this._galgeChoiceListWindow) {
      return (
        _Window_Message_isAnySubWindowActive.apply(this, arguments) ||
        this._galgeChoiceListWindow.active
      );
    }

    return _Window_Message_isAnySubWindowActive.apply(this, arguments);
  };

  //-----------------------------------------------------------------------------
  // Window_GalgeChoiceList
  //
  // ノベルゲーム風選択肢ウィンドウを定義します。

  function Window_GalgeChoiceList() {
    this.initialize(...arguments);
  }

  Window_GalgeChoiceList.prototype = Object.create(Window_Command.prototype);
  Window_GalgeChoiceList.prototype.constructor = Window_GalgeChoiceList;

  Window_GalgeChoiceList.prototype.initialize = function () {
    Window_Command.prototype.initialize.call(this, new Rectangle());
    this.createCancelButton();
    this.openness = 0;
    this.deactivate();
    this._background = 0;
    this._canRepeat = false;
    // フォーカスアニメーション判定
    this._originalCursorSpriteFade = true;
  };

  Window_GalgeChoiceList.prototype.setMessageWindow = function (messageWindow) {
    this._messageWindow = messageWindow;
  };

  Window_GalgeChoiceList.prototype.createCancelButton = function () {
    if (ConfigManager.touchUI) {
      this._cancelButton = new Sprite_Button("cancel");
      this._cancelButton.visible = false;
      this.addChild(this._cancelButton);
    }
  };

  Window_GalgeChoiceList.prototype.start = function () {
    this.updatePlacement();
    this.createCancelButton();
    this.placeCancelButton();
    this.createContents();
    this.refresh();
    this.setQuestionText();
    this.setSymbol();
    this.createOriginalBackground();
    this.open();
    this.activate();
  };

  const _Window_GalgeChoiceList_refresh =
    Window_GalgeChoiceList.prototype.refresh;
  Window_GalgeChoiceList.prototype.refresh = function () {
    this.removeChild(this._bgSprite);
    this._bgSprite = null;
    this.removeChild(this._bgDummy);
    this._bgDummy = null;
    this.removeChild(this._originalCursorSprite);
    this._originalCursorSprite = null;
    this.removeChild(this._originalButtonSprite);
    this._originalButtonSprite = null;

    // ウィンドウ枠、カーソルを初期化
    this.setCursorRect(0, 0, 0, 0);
    this.opacity = 255;

    _Window_GalgeChoiceList_refresh.apply(this, arguments);
  };

  Window_GalgeChoiceList.prototype.updatePlacement = function () {
    const wh = this.calcWindowHeight();
    const ww = gcwWindowWidth;
    const wx = (Graphics.boxWidth - ww) / 2;
    const wy = (Graphics.boxHeight - wh) / 2;

    this.width = ww;
    this.height = wh;
    this.x = wx;
    this.y = wy;
  };

  Window_GalgeChoiceList.prototype.calcWindowHeight = function () {
    // ウィンドウの高さを計算
    const itemCols = Math.ceil(choiceLists.length / maxCols);
    let height = this.lineHeight() * messageTextLists.length;
    if (messageTextLists.length > 0) height += gcwWindowPadding;
    height += this.itemHeight() * itemCols;
    height += gcwWindowPadding * 2; // padding
    return height;
  };

  Window_GalgeChoiceList.prototype.createCancelButton = function () {
    if (ConfigManager.touchUI) {
      this._cancelButton = new Sprite_Button("cancel");
      this._cancelButton.visible = false;
      this.addChild(this._cancelButton);
    }
  };

  Window_GalgeChoiceList.prototype.createOriginalBackground = function () {
    if (backgroundImage != "") {
      this._bgSprite = new Sprite();
      this._bgSprite.bitmap = ImageManager.loadSystem(backgroundImage);
      this._bgSprite.bitmap.addLoadListener(
        function () {
          this._bgSprite.x = this.width / 2 - this._bgSprite.width / 2;
          this._bgSprite.y = this.height / 2 - this._bgSprite.height / 2;
        }.bind(this)
      );
      this._bgSprite.opacity = 0;
      this.addChildToBack(this._bgSprite);
      // ウィンドウ枠を消去
      this.opacity = 0;
    }
  };

  Window_GalgeChoiceList.prototype.drawAllItems = function () {
    this.createOriginalCursor();
    this.createOriginalButton();
    Window_Selectable.prototype.drawAllItems.call(this);
  };

  Window_GalgeChoiceList.prototype.createOriginalCursor = function () {
    if (gcwButtonFocusImage != "") {
      this._originalCursorSprite = null;
      this._originalCursorSprite = new Sprite();
      this._originalCursorSprite.bitmap =
        ImageManager.loadSystem(gcwButtonFocusImage);
      this._originalCursorSprite.opacity = 128;
      this._originalCursorSprite.visible = true;
      this.addChildToBack(this._originalCursorSprite);
    }
  };

  Window_GalgeChoiceList.prototype.createOriginalButton = function () {
    if (gcwButtonImage != "") {
      this._originalButtonSprite = null;
      this._originalButtonSprite = new Sprite();
      this._originalButtonSprite.visible = false;
      this.addChildToBack(this._originalButtonSprite);
    }
  };

  Window_GalgeChoiceList.prototype.drawBackgroundRect = function (rect) {
    const c1 = ColorManager.itemBackColor1();
    const c2 = ColorManager.itemBackColor2();
    const x = rect.x;
    const y = rect.y;
    const w = rect.width;
    const h = rect.height;
    if (gcwButtonImage != "") {
      const originalButtonSprite = new Sprite();
      originalButtonSprite.bitmap = ImageManager.loadSystem(gcwButtonImage);
      originalButtonSprite.x = x + gcwButtonAdjustX + gcwWindowPadding;
      originalButtonSprite.y = y + gcwButtonAdjustY + gcwWindowPadding;
      this._originalButtonSprite.addChild(originalButtonSprite);
    } else {
      this.contentsBack.gradientFillRect(x, y, w, h, c1, c2, true);
      this.contentsBack.strokeRect(x, y, w, h, c1);
    }
  };

  Window_GalgeChoiceList.prototype.refreshCursor = function () {
    if (this._cursorAll) {
      this.refreshCursorForAll();
    } else if (this.index() >= 0) {
      const rect = this.itemRect(this.index());
      if (gcwButtonFocusImage != "") {
        if (this._originalCursorSprite) {
          this._originalCursorSprite.x =
            rect.x + gcwButtonAdjustX + gcwWindowPadding;
          this._originalCursorSprite.y =
            rect.y + gcwButtonAdjustY + gcwWindowPadding;
        }
      } else {
        this.setCursorRect(rect.x, rect.y, rect.width, rect.height);
      }
    } else {
      this.setCursorRect(0, 0, 0, 0);
    }
  };

  Window_GalgeChoiceList.prototype.refreshCursorForAll = function () {
    const maxItems = this.maxItems();
    if (maxItems > 0) {
      const rect = this.itemRect(0);
      rect.enlarge(this.itemRect(maxItems - 1));
      if (gcwButtonFocusImage != "") {
        if (this._originalCursorSprite) {
          this._originalCursorSprite.x = rect.x + gcwButtonAdjustX;
          this._originalCursorSprite.y = rect.y + gcwButtonAdjustY;
        }
      } else {
        this.setCursorRect(rect.x, rect.y, rect.width, rect.height);
      }
    } else {
      this.setCursorRect(0, 0, 0, 0);
    }
  };

  Window_GalgeChoiceList.prototype.update = function () {
    Window_Command.prototype.update.call(this);
    this.updateCancelButton();
    this.updateOriginalButton();
    this.updateOriginalCursorButton();
    if (this._bgSprite) this._bgSprite.opacity = this.openness;
  };

  Window_GalgeChoiceList.prototype.updateOriginalButton = function () {
    if (this._originalButtonSprite) {
      if (this.isOpen()) {
        this._originalButtonSprite.visible = true;
      } else {
        this._originalButtonSprite.visible = false;
      }
    }
  };

  Window_GalgeChoiceList.prototype.updateOriginalCursorButton = function () {
    if (this._originalCursorSprite) {
      if (this.isOpen() && this._index > -1) {
        this._originalCursorSprite.visible = true;
        if (this._originalCursorSprite.opacity >= 255)
          this._originalCursorSpriteFade = false;
        if (this._originalCursorSprite.opacity <= 128)
          this._originalCursorSpriteFade = true;
        this._originalCursorSprite.opacity += this._originalCursorSpriteFade
          ? 6
          : -6;
      } else {
        this._originalCursorSprite.visible = false;
      }
    }
  };

  Window_GalgeChoiceList.prototype.updateOpen = function () {
    if (this._opening) {
      this.openness += gcwFadeInFrame;
      if (this.isOpen()) {
        this._opening = false;
      }
    }
  };

  Window_GalgeChoiceList.prototype.updateCancelButton = function () {
    if (this._cancelButton) {
      this._cancelButton.visible =
        this.needsCancelButton() && this.isOpen() && gcwCancelButtonEnabled;
    }
  };

  Window_GalgeChoiceList.prototype.needsCancelButton = function () {
    return cancelType;
  };

  Window_GalgeChoiceList.prototype.placeCancelButton = function () {
    if (this._cancelButton) {
      const spacing = 8;
      const button = this._cancelButton;
      const right = this.x + this.width;
      if (right < Graphics.boxWidth - button.width + spacing) {
        button.x = this.width + spacing;
      } else {
        button.x = -button.width - spacing;
      }
      button.y = this.height / 2 - button.height / 2;
    }
  };

  Window_GalgeChoiceList.prototype.maxCols = function () {
    return maxCols;
  };

  Window_GalgeChoiceList.prototype.setQuestionText = function () {
    messageTextLists.forEach((text, index) => {
      this.drawText(
        this.convertEscapeCharacters(text),
        0,
        this.lineHeight() * index,
        this.innerWidth,
        "center"
      );
    }, this);
  };

  Window_GalgeChoiceList.prototype.makeCommandList = function () {
    choiceLists.forEach(function (elm, index) {
      this.addCommand(
        String(elm.label),
        Number(elm.resultNumber || 0) !== 0
          ? Number(elm.resultNumber)
          : index + 1
      );
    }, this);
  };

  Window_GalgeChoiceList.prototype.updatePadding = function () {
    this.padding = gcwWindowPadding;
  };

  Window_GalgeChoiceList.prototype.scrollBaseY = function (index) {
    let margin = this.lineHeight() * messageTextLists.length;
    if (messageTextLists.length > 0) margin += gcwWindowPadding;
    return this._scrollBaseY - margin;
  };

  Window_GalgeChoiceList.prototype.setSymbol = function () {
    if (iniPosition > 0) {
      this.selectSymbol(iniPosition);
    } else {
      this.deselect();
    }
  };

  Window_GalgeChoiceList.prototype.itemHeight = function () {
    return (
      Window_Scrollable.prototype.itemHeight.call(this) + gcwItemHeightAdjust
    );
  };

  Window_GalgeChoiceList.prototype.drawItem = function (index) {
    const rect = this.itemLineRect(index);
    const align = this.itemTextAlign();
    this.resetTextColor();
    this.changePaintOpacity(this.isCommandEnabled(index));
    this.drawText(
      this.convertEscapeCharacters(this.commandName(index)),
      rect.x,
      rect.y,
      rect.width,
      align
    );
  };

  Window_GalgeChoiceList.prototype.isCancelEnabled = function () {
    return cancelType;
  };

  Window_GalgeChoiceList.prototype.isOkTriggered = function () {
    return Input.isTriggered("ok");
  };

  Window_GalgeChoiceList.prototype.callOkHandler = function () {
    $gameVariables.setValue(variableNumber, this.currentSymbol());
    this._messageWindow.terminateMessage();
    this.close();
  };

  Window_GalgeChoiceList.prototype.callCancelHandler = function () {
    $gameVariables.setValue(variableNumber, -1);
    this._messageWindow.terminateMessage();
    this.close();
  };
})();


code:
//=============================================================================
// RPGツクールMZ - LL_InfoPopupWIndow.js v1.1.1
//-----------------------------------------------------------------------------
// ルルの教会 (Lulu's Church)
// https://nine-yusha.com/
//
// URL below for license details.
// https://nine-yusha.com/plugin/
//=============================================================================

/*:
 * @target MZ
 * @plugindesc ポップアップでインフォメッセージを表示します。
 * @author ルルの教会
 * @url https://nine-yusha.com/plugin-infowindow/
 *
 * @help LL_InfoPopupWIndow.js
 *
 * ポップアップでインフォメッセージを表示します。
 * アイテム入手メッセージや、各種イベントメッセージなどに使えます。
 * ウィンドウを表示するにはプラグインコマンドを実行してください。
 *
 * プラグインコマンド:
 *   メッセージを表示: インフォメッセージを表示します。
 *
 * フォントサイズを変更する場合:
 *   \FS["サイズ"] の制御文字を使用してください。 【例】\FS[16]
 *
 * 利用規約:
 *   ・著作権表記は必要ございません。
 *   ・利用するにあたり報告の必要は特にございません。
 *   ・商用・非商用問いません。
 *   ・R18作品にも使用制限はありません。
 *   ・ゲームに合わせて自由に改変していただいて問題ございません。
 *   ・プラグイン素材としての再配布（改変後含む）は禁止させていただきます。
 *
 * 作者: ルルの教会
 * 作成日: 2022/3/6
 *
 * @command showMessage
 * @text メッセージを表示
 * @desc ポップアップでインフォメッセージを表示します。
 *
 * @arg messageText
 * @text メッセージ内容
 * @desc 表示するメッセージ内容を入力します。
 * 各種制御文字が使用可能です。複数行表示はできません。
 * @type string
 *
 * @arg showCount
 * @text 表示時間
 * @desc ウィンドウの表示時間です。 (1/60秒)
 * -1と入力するとシーンが切り替わるまで表示し続けます。
 * @default 180
 * @max 999
 * @min -1
 * @type number
 *
 * @arg windowX
 * @text X座標
 * @desc ウィンドウの表示位置(X)です。(autoと入力すると中央表示)
 * 横幅は文字数に合わせて自動調整されます。
 * @default 0
 * @type string
 *
 * @arg windowY
 * @text Y座標
 * @desc ウィンドウの表示位置(Y)です。(autoと入力すると中央表示)
 * @default 60
 * @type string
 *
 * @arg seName
 * @text 効果音
 * @desc ウィンドウを表示した時の効果音です。
 * @dir audio/se
 * @type file
 * @require 1
 *
 * @arg seVolume
 * @text 効果音の音量
 * @desc 効果音の音量(0～100%)です。 (初期値: 90%)
 * @default 90
 * @max 100
 * @min 0
 * @type number
 *
 * @arg sePitch
 * @text 効果音のピッチ
 * @desc 効果音のピッチ(50～150%)です。 (初期値: 100%)
 * @default 100
 * @max 150
 * @min 50
 * @type number
 *
 * @arg sePan
 * @text 効果音の位相
 * @desc 効果音の位相(-100～100)です。 (初期値: 0)
 * @default 0
 * @max 100
 * @min -100
 * @type number
 *
 * @arg windowBackground
 * @text ウィンドウ背景
 * @desc ウィンドウの背景を指定してください。
 * @type select
 * @default 0
 * @option ウィンドウ
 * @value 0
 * @option 暗くする
 * @value 1
 * @option 透明
 * @value 2
 *
 * @command hideMessage
 * @text メッセージを消去
 * @desc 表示中のインフォメッセージを消去します。
 */

(() => {
  "use strict";
  const pluginName = "LL_InfoPopupWIndow";
  const parameters = PluginManager.parameters(pluginName);

  // インフォメッセージ表示
  PluginManager.registerCommand(pluginName, "showMessage", (args) => {
    const x = args.windowX === "auto" ? "auto" : Number(args.windowX || 0);
    const y = args.windowY === "auto" ? "auto" : Number(args.windowY || 60);

    exInfoWindowText = String(args.messageText || "");
    exInfoWindowShowCount = Number(args.showCount || 180);
    exInfoWindowPosition = { x: x, y: y };
    exInfoWindowSe = {
      name: String(args.seName || ""),
      volume: Number(args.seVolume || 90),
      pitch: Number(args.sePitch || 100),
      pan: Number(args.sePan || 0),
    };
    exInfoWindowBackground = Number(args.windowBackground || 0);
    exInfoWindowShow = true;
  });

  // インフォメッセージ消去
  PluginManager.registerCommand(pluginName, "hideMessage", (args) => {
    exInfoWindowHide = true;
  });

  // ウィンドウの表示状態保持用
  let exInfoWindowText = "";
  let exInfoWindowShowCount = 180;
  let exInfoWindowPosition = {};
  let exInfoWindowSe = {};
  let exInfoWindowBackground = 0;
  let exInfoWindowShow = false;
  let exInfoWindowHide = false;

  //-----------------------------------------------------------------------------
  // Scene_Map
  //

  const _Scene_Map_stop = Scene_Map.prototype.stop;
  Scene_Map.prototype.stop = function () {
    this._exInfoWindow.close();
    _Scene_Map_stop.apply(this, arguments);
  };

  const _Scene_Map_terminate = Scene_Map.prototype.terminate;
  Scene_Map.prototype.terminate = function () {
    _Scene_Map_terminate.apply(this, arguments);
    if (!SceneManager.isNextScene(Scene_Battle)) {
      this._exInfoWindow.hide();
    }
  };

  const _Scene_Map_createAllWindows = Scene_Map.prototype.createAllWindows;
  Scene_Map.prototype.createAllWindows = function () {
    _Scene_Map_createAllWindows.apply(this, arguments);
    this.createExInfoWindow();
  };

  Scene_Map.prototype.createExInfoWindow = function () {
    this._exInfoWindow = new Window_ExInfoWindow();
    this.addWindow(this._exInfoWindow);
  };

  const _Scene_Map_callMenu = Scene_Map.prototype.callMenu;
  Scene_Map.prototype.callMenu = function () {
    _Scene_Map_callMenu.apply(this, arguments);
    this._exInfoWindow.hide();
  };

  //-----------------------------------------------------------------------------
  // Scene_Battle
  //

  const _Scene_Battle_stop = Scene_Battle.prototype.stop;
  Scene_Battle.prototype.stop = function () {
    this._exInfoWindow.close();
    _Scene_Battle_stop.apply(this, arguments);
  };

  const _Scene_Battle_terminate = Scene_Battle.prototype.terminate;
  Scene_Battle.prototype.terminate = function () {
    _Scene_Battle_terminate.apply(this, arguments);
    this._exInfoWindow.hide();
  };

  const _Scene_Battle_createAllWindows =
    Scene_Battle.prototype.createAllWindows;
  Scene_Battle.prototype.createAllWindows = function () {
    _Scene_Battle_createAllWindows.apply(this, arguments);
    this.createExInfoWindow();
  };

  Scene_Battle.prototype.createExInfoWindow = function () {
    this._exInfoWindow = new Window_ExInfoWindow();
    this.addWindow(this._exInfoWindow);
  };

  //-----------------------------------------------------------------------------
  // Window_ExInfoWindow
  //
  // インフォメッセージを表示するウィンドウです。

  function Window_ExInfoWindow() {
    this.initialize(...arguments);
  }

  Window_ExInfoWindow.prototype = Object.create(Window_Base.prototype);
  Window_ExInfoWindow.prototype.constructor = Window_ExInfoWindow;

  Window_ExInfoWindow.prototype.initialize = function () {
    Window_Base.prototype.initialize.call(this, new Rectangle());
    this.openness = 0;
    this._showCount = 0;
  };

  Window_ExInfoWindow.prototype.start = function () {
    this._showCount = exInfoWindowShowCount;
    this.playSe();
    this.updatePlacement();
    this.updateBackground();
    this.createContents();
    this.refresh();
  };

  Window_ExInfoWindow.prototype.playSe = function () {
    if (exInfoWindowSe.name) AudioManager.playSe(exInfoWindowSe);
  };

  Window_ExInfoWindow.prototype.update = function () {
    Window_Base.prototype.update.call(this);
    // ウィンドウ表示判定
    if (exInfoWindowShow) {
      this.start();
      exInfoWindowShow = false;
    }
    // ウィンドウ消去判定
    if (exInfoWindowHide) {
      this.close();
      exInfoWindowHide = false;
    }
    if (this._showCount !== 0) {
      this.updateFadeIn();
      if (this._showCount > 0) {
        this._showCount--;
      }
    } else {
      this.updateFadeOut();
    }
  };

  Window_ExInfoWindow.prototype.updateFadeIn = function () {
    this.openness += 32;
  };

  Window_ExInfoWindow.prototype.updateFadeOut = function () {
    this.openness -= 32;
  };

  Window_ExInfoWindow.prototype.close = function () {
    this._showCount = 0;
  };

  Window_ExInfoWindow.prototype.updatePlacement = function () {
    this.width = this.windowWidth();
    this.height = this.windowHeight();

    if (exInfoWindowPosition.x === "auto") {
      this.x = (Graphics.boxWidth - this.windowWidth()) / 2;
    } else {
      this.x = exInfoWindowPosition.x;
    }
    if (exInfoWindowPosition.y === "auto") {
      this.y = (Graphics.boxHeight - this.windowHeight()) / 2;
    } else {
      this.y = exInfoWindowPosition.y;
    }
  };

  Window_ExInfoWindow.prototype.updateBackground = function () {
    this.setBackgroundType(exInfoWindowBackground);
  };

  Window_ExInfoWindow.prototype.windowWidth = function () {
    if (exInfoWindowText) {
      const textWidth = this.textSizeEx(exInfoWindowText).width;
      const padding = this.padding + this.itemPadding();
      const width = Math.ceil(textWidth) + padding * 2;
      return Math.min(width, Graphics.boxWidth);
    } else {
      return 0;
    }
  };

  Window_ExInfoWindow.prototype.windowHeight = function () {
    return this.fittingHeight(1);
  };

  Window_ExInfoWindow.prototype.refresh = function () {
    const rect = this.baseTextRect();
    this.contents.clear();
    this.drawTextEx(exInfoWindowText, rect.x, rect.y, rect.width);
  };
})();


code:
//=============================================================================
// RPGツクールMZ - LL_MenuScreenBase.js v1.1.0
//-----------------------------------------------------------------------------
// ルルの教会 (Lulu's Church)
// https://nine-yusha.com/
//
// URL below for license details.
// https://nine-yusha.com/plugin/
//=============================================================================

/*:
 * @target MZ
 * @plugindesc メニュー画面立ち絵設定の共通ベースプラグインです。
 * @author ルルの教会
 * @url https://nine-yusha.com/plugin-menuscreen/
 *
 * @help LL_MenuScreenBase.js
 *
 * メニュー画面立ち絵設定の共通ベースプラグインです。
 * このプラグインでアクター毎の立ち絵リストを定義します。
 *
 * 下記のようにステート、スイッチ、変数条件で表示する立ち絵を複数定義できます。
 *   ・スイッチ1がONかつ毒状態の立ち絵
 *   ・変数1が10以上かつ毒状態の立ち絵
 *   ・スイッチ1がONの時の立ち絵
 *   ・毒状態の立ち絵
 *   ・スイッチ・ステート・変数条件なしの通常立ち絵 (最低限必要)
 *
 * 残りHP％で立ち絵を切り替える:
 *   まず「残りHP％」を「100」に設定した立ち絵リストを作成します。
 *   上記をコピーして「残りHP％」を「50」に変更し、立ち絵リストを複製します。
 *   これでHPが半分以下になった場合、「50」に設定した立ち絵が呼ばれます。
 *   残りHP％毎に、複数立ち絵を定義することも可能です。
 *
 * 画像ファイルの表示優先順:
 *   1. ステートID、スイッチID、変数条件全てに一致するもの
 *   2. ステートID、スイッチID両方に一致するもの
 *   3. ステートID、変数条件両方に一致するもの
 *   4. ステートIDのみ一致するもの
 *   5. スイッチID、変数条件両方に一致するもの
 *   6. スイッチIDのみ一致するもの
 *   7. 変数条件のみ一致するもの
 *   8. 条件なし (ステートID、スイッチID、変数条件全て設定なし)
 *   (上記の中で、残りHP％が最も低いものが優先して表示されます)
 *
 * 戦闘中立ち絵プラグイン連携:
 *   LL_StandingPictureBattle が導入されている場合は、
 *   戦闘中の立ち絵リストとそのまま連携させることも可能です。
 *
 * プラグインコマンドはありません。
 *
 * 利用規約:
 *   ・著作権表記は必要ございません。
 *   ・利用するにあたり報告の必要は特にございません。
 *   ・商用・非商用問いません。
 *   ・R18作品にも使用制限はありません。
 *   ・ゲームに合わせて自由に改変していただいて問題ございません。
 *   ・プラグイン素材としての再配布（改変後含む）は禁止させていただきます。
 *
 * 作者: ルルの教会
 * 作成日: 2022/3/7
 *
 * @param menuPictures
 * @text 立ち絵リスト
 * @desc メニュー画面に表示する立ち絵を定義します。
 * 特定ステート時、スイッチON時の立ち絵を複数定義できます。
 * @default []
 * @type struct<menuPictures>[]
 *
 * @param onSpbPlugin
 * @text 戦闘中立ち絵プラグイン連携
 * @desc ※この項目は使用しません
 *
 * @param onSpbPluginEnable
 * @text 立ち絵リストを連携
 * @desc LL_StandingPictureBattle の立ち絵リストと連携させます。
 * ONにするとこのプラグインの立ち絵リスト設定は無視されます。
 * @default false
 * @type boolean
 * @parent onSpbPlugin
 */

/*~struct~menuPictures:
 *
 * @param actorId
 * @text アクターID
 * @desc アクターIDです。立ち絵を定義するアクターを選択してください。
 * @type actor
 *
 * @param stateId
 * @text ステートID
 * @desc 特定ステートで立ち絵を変更したい場合に使用します。
 * 通常時の立ち絵は空白(なし)で設定ください。
 * @type state
 *
 * @param switchId
 * @text スイッチID
 * @desc スイッチONで立ち絵を変更したい場合に使用します。
 * 通常時の立ち絵は空白(なし)で設定ください。
 * @type switch
 *
 * @param variableCase
 * @text 変数条件
 * @desc 変数条件で立ち絵を変更したい場合に使用します。
 * @default
 * @type struct<variableCase>
 *
 * @param hpPercentage
 * @text 残りHP％
 * @desc 残りHP％で立ち絵を変更したい場合に使用します。
 * 通常時の立ち絵は100％で設定してください。
 * @default 100
 * @min 0
 * @max 100
 * @type number
 *
 * @param imageName
 * @text 画像ファイル名
 * @desc 立ち絵として表示する画像ファイルを選択してください。
 * @dir img/pictures
 * @type file
 * @require 1
 *
 * @param x
 * @text X座標
 * @desc 立ち絵の表示位置(X)の調整値です。
 * ＋で右へ、－で左へ調整します。 (初期値: 0)
 * @default 0
 * @min -9999
 * @max 9999
 * @type number
 *
 * @param y
 * @text Y座標
 * @desc 立ち絵の表示位置(Y)の調整値です。
 * ＋で下へ、－で上へ調整します。 (初期値: 0)
 * @default 0
 * @min -9999
 * @max 9999
 * @type number
 *
 * @param scaleX
 * @text X拡大率
 * @desc 立ち絵の拡大率(X)です。
 * @default 100
 * @min -2000
 * @max 2000
 * @type number
 *
 * @param scaleY
 * @text Y拡大率
 * @desc 立ち絵の拡大率(Y)です。
 * @default 100
 * @min -2000
 * @max 2000
 * @type number
 */

/*~struct~variableCase:
 *
 * @param id
 * @text 変数ID
 * @desc 条件に使用する変数IDです。
 * @type variable
 *
 * @param type
 * @text 変数条件
 * @desc 変数IDとの比較条件です。
 * @default equal
 * @type select
 * @option 一致する
 * @value equal
 * @option 以上
 * @value higher
 * @option 以下
 * @value lower
 *
 * @param value
 * @text 変数比較数値
 * @desc 変数IDと比較する数値です。
 * @default 0
 * @min -99999999
 * @max 99999999
 * @type number
 */

(() => {
  "use strict";
  const pluginName = "LL_MenuScreenBase";

  const parameters = PluginManager.parameters(pluginName);
  const paramJsonParse = function (key, value) {
    try {
      return JSON.parse(value);
    } catch (e) {
      return value;
    }
  };

  const menuPictures = String(parameters["menuPictures"] || "[]");
  const onSpbPluginEnable = eval(parameters["onSpbPluginEnable"] || "true");
  const menuPictureLists = JSON.parse(
    JSON.stringify(menuPictures, paramJsonParse)
  );

  //-----------------------------------------------------------------------------
  // 戦闘中立ち絵プラグインの立ち絵リストを取得
  // On LL_StandingPictureBattle Plugin
  //-----------------------------------------------------------------------------
  const spbPluginName = "LL_StandingPictureBattle";
  const spbParameters = PluginManager.parameters(spbPluginName);
  const spbCommandPictures = String(spbParameters["sbCommandPictures"] || "[]");
  const spbCommandPictureLists = JSON.parse(
    JSON.stringify(spbCommandPictures, paramJsonParse)
  );

  //-----------------------------------------------------------------------------
  // Ex Menu Screen Base Class
  //
  // メニュー画面立ち絵設定の独自クラスを追加定義します。

  class ExMenuScreenBase {
    //-----------------------------------------------------------------------------
    // 画像ファイル名を取得
    //-----------------------------------------------------------------------------
    static getImageName(actorId) {
      // 立ち絵リストを取得
      let pictureLists = this.getPictureLists();
      if (!pictureLists) return;

      // アクターのステート情報を取得
      let actorStates = [];
      if (actorId) actorStates = $gameActors.actor(actorId)._states;
      let specificPicture = null;

      // アクターIDが一致する立ち絵を検索
      pictureLists = pictureLists.filter(function (item) {
        if (Number(item.actorId) == actorId) {
          return true;
        }
      });

      // ステートにかかっているか？
      if (actorStates.length) {
        // ステートID・スイッチID・変数IDが有効な立ち絵リストを検索
        specificPicture = pictureLists.filter(function (item) {
          if (item.variableCase) {
            if (
              actorStates.indexOf(Number(item.stateId)) !== -1 &&
              $gameSwitches.value(Number(item.switchId)) &&
              ((String(item.variableCase.type) == "equal" &&
                $gameVariables.value(Number(item.variableCase.id)) ==
                  Number(item.variableCase.value)) ||
                (String(item.variableCase.type) == "higher" &&
                  $gameVariables.value(Number(item.variableCase.id)) >=
                    Number(item.variableCase.value)) ||
                (String(item.variableCase.type) == "lower" &&
                  $gameVariables.value(Number(item.variableCase.id)) <=
                    Number(item.variableCase.value)))
            ) {
              return true;
            }
          }
        });
        if (specificPicture.length)
          return this.checkHpPercentage(actorId, specificPicture);
        // ステートID・スイッチIDが有効な立ち絵リストを検索
        specificPicture = pictureLists.filter(function (item) {
          if (
            actorStates.indexOf(Number(item.stateId)) !== -1 &&
            $gameSwitches.value(Number(item.switchId)) &&
            !item.variableCase
          ) {
            return true;
          }
        });
        if (specificPicture.length)
          return this.checkHpPercentage(actorId, specificPicture);
        // ステートID・変数IDが有効な立ち絵リストを検索
        specificPicture = pictureLists.filter(function (item) {
          if (item.variableCase) {
            if (
              actorStates.indexOf(Number(item.stateId)) !== -1 &&
              (Number(item.switchId) === 0 || !item.switchId) &&
              ((String(item.variableCase.type) == "equal" &&
                $gameVariables.value(Number(item.variableCase.id)) ==
                  Number(item.variableCase.value)) ||
                (String(item.variableCase.type) == "higher" &&
                  $gameVariables.value(Number(item.variableCase.id)) >=
                    Number(item.variableCase.value)) ||
                (String(item.variableCase.type) == "lower" &&
                  $gameVariables.value(Number(item.variableCase.id)) <=
                    Number(item.variableCase.value)))
            ) {
              return true;
            }
          }
        });
        if (specificPicture.length)
          return this.checkHpPercentage(actorId, specificPicture);
        // ステートIDが有効な立ち絵リストを検索
        specificPicture = pictureLists.filter(function (item) {
          if (
            actorStates.indexOf(Number(item.stateId)) !== -1 &&
            (Number(item.switchId) === 0 || !item.switchId) &&
            !item.variableCase
          ) {
            return true;
          }
        });
        if (specificPicture.length)
          return this.checkHpPercentage(actorId, specificPicture);
      }

      // スイッチID・変数IDが有効な立ち絵リストを検索
      specificPicture = pictureLists.filter(function (item) {
        if (item.variableCase) {
          if (
            (Number(item.stateId) === 0 || !item.stateId) &&
            $gameSwitches.value(Number(item.switchId)) &&
            ((String(item.variableCase.type) == "equal" &&
              $gameVariables.value(Number(item.variableCase.id)) ==
                Number(item.variableCase.value)) ||
              (String(item.variableCase.type) == "higher" &&
                $gameVariables.value(Number(item.variableCase.id)) >=
                  Number(item.variableCase.value)) ||
              (String(item.variableCase.type) == "lower" &&
                $gameVariables.value(Number(item.variableCase.id)) <=
                  Number(item.variableCase.value)))
          ) {
            return true;
          }
        }
      });
      if (specificPicture.length)
        return this.checkHpPercentage(actorId, specificPicture);
      // スイッチIDが有効な立ち絵リストを検索
      specificPicture = pictureLists.filter(function (item) {
        if (
          (Number(item.stateId) === 0 || !item.stateId) &&
          $gameSwitches.value(Number(item.switchId)) &&
          !item.variableCase
        ) {
          return true;
        }
      });
      if (specificPicture.length)
        return this.checkHpPercentage(actorId, specificPicture);
      // 変数IDが有効な立ち絵リストを検索
      specificPicture = pictureLists.filter(function (item) {
        if (item.variableCase) {
          if (
            (Number(item.stateId) === 0 || !item.stateId) &&
            (Number(item.switchId) === 0 || !item.switchId) &&
            ((String(item.variableCase.type) == "equal" &&
              $gameVariables.value(Number(item.variableCase.id)) ==
                Number(item.variableCase.value)) ||
              (String(item.variableCase.type) == "higher" &&
                $gameVariables.value(Number(item.variableCase.id)) >=
                  Number(item.variableCase.value)) ||
              (String(item.variableCase.type) == "lower" &&
                $gameVariables.value(Number(item.variableCase.id)) <=
                  Number(item.variableCase.value)))
          ) {
            return true;
          }
        }
      });
      if (specificPicture.length)
        return this.checkHpPercentage(actorId, specificPicture);

      // 上記で見つからなかった場合、通常の立ち絵を検索
      let normalPicture = pictureLists.filter(function (item) {
        if (
          (Number(item.stateId) === 0 || !item.stateId) &&
          (Number(item.switchId) === 0 || !item.switchId) &&
          !item.variableCase
        )
          return true;
      });
      if (normalPicture.length)
        return this.checkHpPercentage(actorId, normalPicture);
    }

    static checkHpPercentage(actorId, pictureLists) {
      // アクターの残HP％を取得
      let hpRate = this.getHpRate(actorId);
      // 最もHP%が低い立ち絵を適用する
      let minHpRate = 100;
      let result = null;
      pictureLists.forEach(function (item) {
        if (
          hpRate <= Number(item.hpPercentage) &&
          minHpRate >= Number(item.hpPercentage)
        ) {
          result = item;
          minHpRate = Number(item.hpPercentage);
        } else if (!item.hpPercentage && minHpRate >= 100) {
          // プラグインパラメータが更新されていない場合、便宜的に100として扱う
          result = item;
          minHpRate = Number(item.hpPercentage);
        }
      });
      return result;
    }

    static getPictureLists() {
      return onSpbPluginEnable ? spbCommandPictureLists : menuPictureLists;
    }

    static onSpbPluginEnable() {
      return onSpbPluginEnable;
    }

    // アクターのHPレートを取得
    static getHpRate(actorId) {
      if (!$gameActors.actor(actorId)) return 0;
      return $gameActors.actor(actorId).mhp > 0
        ? ($gameActors.actor(actorId).hp / $gameActors.actor(actorId).mhp) * 100
        : 0;
    }
  }

  window.ExMenuScreenBase = ExMenuScreenBase;
})();