code:
//=============================================================================
// NRP_BattleTargetCursor.js
//=============================================================================

/*:
 * @target MV MZ
 * @plugindesc v1.00 Display the cursor when selecting a target in battle.
 * @author Takeshi Sunagawa (http://newrpg.seesaa.net/)
 * @url http://newrpg.seesaa.net/article/482370647.html
 *
 * @help The cursor is displayed during battle
 * and when the target is selected.
 * Various adjustments can be made accordingly.
 *
 * By default, it is designed for side view.
 * It can also be used in front view by adjusting the settings.
 *
 * There are no advanced functions such as referring
 * to the positional relationship of the target
 * when the directional key is pressed
 * to determine the cursor's transition destination.
 * Pressing a key simply moves the cursor
 * to the target of the previous or next ID.
 *
 * ■Usage
 * Just introducing it will change the behavior.
 * The cursor image is not set by default,
 * so only the target name is displayed.
 *
 * A sample cursor is available on the introduction page.
 * Please refer to it for reference.
 *
 * After that, you can adjust the plugin parameters as you wish.
 *
 * Note that most of the actor's parameters can be left blank.
 * In that case, the parameters of the enemy side
 * will be automatically referenced.
 * ※The cursor image and X-coordinate adjustments
 *   will be flipped left and right.
 *
 * [Terms]
 * There are no restrictions.
 * Modification, redistribution freedom, commercial availability,
 * and rights indication are also optional.
 * The author is not responsible,
 * but will deal with defects to the extent possible.
 *
 * @param <EnemySide>
 *
 * @param HideEnemyNameList
 * @parent <EnemySide>
 * @type boolean
 * @default true
 * @desc Hides the default enemy name list.
 *
 * @param SwitchEnemyUpDown
 * @parent <EnemySide>
 * @type boolean
 * @default true
 * @desc Enables to switch the selection of enemies up&down. Only available when HideEnemyNameList is on.
 *
 * @param SwitchEnemyLeftRight
 * @parent <EnemySide>
 * @type select
 * @option Invalid @value
 * @option Left to Next @value left
 * @option Right to Next @value right
 * @default left
 * @desc Enables to switch the selection of enemies left&right. Only available when HideEnemyNameList is on.
 *
 * @param EnemyTargetCursor
 * @parent <EnemySide>
 * @type file
 * @dir img/pictures
 * @desc Specify the cursor image when targeting an enemy.
 * The image should be placed in the picture folder.
 *
 * @param EnemyCursorPosition
 * @parent <EnemySide>
 * @type select
 * @option Foot @value foot
 * @option Center @value center
 * @option Head @value head
 * @default center
 * @desc The reference point for the enemy cursor image.
 * The name window will also be based on this.
 *
 * @param EnemyCursorAdjustX
 * @parent <EnemySide>
 * @type number
 * @min -999 @max 999
 * @default 60
 * @desc Adjusts the X coordinate of the cursor image when targeting an enemy.
 *
 * @param EnemyCursorAdjustY
 * @parent <EnemySide>
 * @type number
 * @min -999 @max 999
 * @default 0
 * @desc Adjusts the Y coordinate of the cursor image when targeting an enemy.
 *
 * @param ShowTargetEnemyName
 * @parent <EnemySide>
 * @type boolean
 * @default true
 * @desc Displays the name of the currently selected enemy.
 *
 * @param EnemyNameWindowHeight
 * @parent <EnemySide>
 * @type number
 * @default 72
 * @desc The height of the enemy name window.
 * Default value is 72.
 *
 * @param EnemyNameWindowOpacity
 * @parent <EnemySide>
 * @type number
 * @default 255
 * @desc The opacity of the enemy name window (0~255).
 *
 * @param EnemyNameWindowAdjustX
 * @parent <EnemySide>
 * @type number
 * @min -999 @max 999
 * @default 0
 * @desc Adjusts the X coordinate of the enemy name window.
 *
 * @param EnemyNameWindowAdjustY
 * @parent <EnemySide>
 * @type number
 * @min -999 @max 999
 * @default 60
 * @desc Adjusts the Y coordinate of the enemy name window.
 *
 * @param EnemyNameFontSize
 * @parent <EnemySide>
 * @type number
 * @desc The font size of the enemy name.
 * If blank, the system setting is used.
 *
 * @param EnemyNameTextAdjustY
 * @parent <EnemySide>
 * @type number
 * @min -999 @max 999
 * @default 0
 * @desc Adjusts the Y coordinate of the text displayed in the enemy name window.
 *
 * @param <ActorSide>
 *
 * @param SwitchActorUpDown
 * @parent <ActorSide>
 * @type boolean
 * @default true
 * @desc Enables to switch the selection of actors up&down.
 *
 * @param SwitchActorLeftRight
 * @parent <ActorSide>
 * @type boolean
 * @default true
 * @desc Enables to switch the selection of actors left&right.
 * This is the standard behavior of RPG Maker MZ.
 *
 * @param ActorTargetCursor
 * @parent <ActorSide>
 * @type file
 * @dir img/pictures
 * @desc Specify the cursor image when targeting an actor.
 * If blank, it will flip the enemy's one left or right.
 *
 * @param ActorCursorPosition
 * @parent <ActorSide>
 * @type select
 * @option Foot @value foot
 * @option Center @value center
 * @option Head @value head
 * @desc The reference point for the actor cursor image.
 * If blank, use the same value as the enemy.
 *
 * @param ActorCursorAdjustX
 * @parent <ActorSide>
 * @type number
 * @min -999 @max 999
 * @desc Adjusts the X coordinate of the cursor when targeting an actor.
 * If blank, use the enemy's value flipped left or right.
 *
 * @param ActorCursorAdjustY
 * @parent <ActorSide>
 * @type number
 * @min -999 @max 999
 * @desc Adjusts the Y coordinate of the cursor when targeting an actor.
 * If blank, use the same value as the enemy.
 *
 * @param ShowTargetActorName
 * @parent <ActorSide>
 * @type boolean
 * @desc Displays the name of the currently selected actor.
 * If blank, use the same value as the enemy.
 *
 * @param ActorNameWindowHeight
 * @parent <ActorSide>
 * @type number
 * @desc The height of the actor name window.
 * If blank, use the same value as the enemy.
 *
 * @param ActorNameWindowOpacity
 * @parent <ActorSide>
 * @type number
 * @desc The opacity of the actor name window (0~255).
 * If blank, use the same value as the enemy.
 *
 * @param ActorNameWindowAdjustX
 * @parent <ActorSide>
 * @type number
 * @desc Adjusts the X coordinate of the enemy name window.
 * If blank, use the enemy's value flipped left or right.
 *
 * @param ActorNameWindowAdjustY
 * @parent <ActorSide>
 * @type number
 * @min -999 @max 999
 * @desc Adjusts the Y coordinate of the enemy name window.
 * If blank, use the same value as the enemy.
 *
 * @param ActorNameFontSize
 * @parent <ActorSide>
 * @type number
 * @desc The font size of the actor name.
 * If blank, use the same value as the enemy.
 *
 * @param ActorNameTextAdjustY
 * @parent <ActorSide>
 * @type number
 * @min -999 @max 999
 * @desc Adjusts the Y coordinate of the text in the enemy name window. If blank, use the same value as the enemy.
 *
 * @param <Common>
 *
 * @param LeftLimitX
 * @parent <Common>
 * @type string
 * @default 0
 * @desc The X coordinate that is the left limit of the cursor display. Default: left edge of the screen (0).
 *
 * @param RightLimitX
 * @parent <Common>
 * @type string
 * @default Graphics.width
 * @desc The X coordinate that is the right limit of the cursor display. Default: right end of the screen (Graphics.width).
 *
 * @param UpLimitY
 * @parent <Common>
 * @type string
 * @default 0
 * @desc The Y coordinate that is the upper limit of the cursor display. Default: upper edge of the screen (0).
 *
 * @param DownLimitY
 * @parent <Common>
 * @type string
 * @default Graphics.height
 * @desc The Y coordinate that is the lower limit of the cursor display. Default: Bottom edge of the screen (Graphics.height).
 */

/*:ja
 * @target MV MZ
 * @plugindesc v1.00 戦闘時、選択中の対象にカーソルを表示
 * @author 砂川赳 (http://newrpg.seesaa.net/)
 * @url http://newrpg.seesaa.net/article/482370647.html
 *
 * @help 戦闘時、選択中の対象にカーソルを表示します。
 * それに伴い、様々な調整が可能です。
 *
 * 初期値ではサイドビューを想定していますが、
 * 設定を調整すれば、フロントビューでも使えます。
 *
 * 方向キーを押した時に対象の位置関係を参照して、
 * カーソルの遷移先を判定するような高度な機能はありません。
 * キーを押すと単純に前後のＩＤの対象に移るだけです。
 *
 * ■使用法
 * 導入するだけで挙動が変わります。
 * カーソル画像は初期状態では設定されていませんので、
 * 対象の名前表示だけが行われます。
 *
 * 紹介ページにカーソルのサンプルを用意しています。
 * ご参考にしていただければ。
 *
 * 後はプラグインパラメータを好きなように調整してください。
 *
 * なお、アクター側のパラメータの大半は空白でも構いません。
 * その場合は、自動的に敵側のパラメータが参照されます。
 * ※カーソル画像およびＸ座標の調整分は左右反転されます。
 *
 * ■利用規約
 * 特に制約はありません。
 * 改変、再配布自由、商用可、権利表示も任意です。
 * 作者は責任を負いませんが、不具合については可能な範囲で対応します。
 *
 * @param <EnemySide>
 * @text ＜敵側＞
 *
 * @param HideEnemyNameList
 * @parent <EnemySide>
 * @text 敵名リストを非表示
 * @type boolean
 * @default true
 * @desc デフォルトの敵名リストを非表示にします。
 *
 * @param SwitchEnemyUpDown
 * @parent <EnemySide>
 * @text 敵の上下切替
 * @type boolean
 * @default true
 * @desc 敵の選択を上下で切替できるようにします。
 * 『敵名リストを非表示』がオンの場合のみ有効です。
 *
 * @param SwitchEnemyLeftRight
 * @parent <EnemySide>
 * @text 敵の左右切替
 * @type select
 * @option 無効 @value
 * @option 左で次へ @value left
 * @option 右で次へ @value right
 * @default left
 * @desc 敵の選択を左右で切替できるようにします。
 * 『敵名リストを非表示』がオンの場合のみ有効です。
 *
 * @param EnemyTargetCursor
 * @parent <EnemySide>
 * @text 敵カーソルの画像
 * @type file
 * @dir img/pictures
 * @desc 敵を対象にした際のカーソル画像を指定します。
 * 画像はピクチャーフォルダに配置してください。
 *
 * @param EnemyCursorPosition
 * @parent <EnemySide>
 * @text 敵のカーソル基準点
 * @type select
 * @option 足元 @value foot
 * @option 中央 @value center
 * @option 頭上 @value head
 * @default center
 * @desc 敵を対象にした際のカーソル画像の上下の基準点です。
 * 敵名ウィンドウもこれを基準にします。
 *
 * @param EnemyCursorAdjustX
 * @parent <EnemySide>
 * @text 敵のカーソル調整Ｘ
 * @type number
 * @min -999 @max 999
 * @default 60
 * @desc 敵を対象にした際のカーソル画像のＸ座標を調整します。
 *
 * @param EnemyCursorAdjustY
 * @parent <EnemySide>
 * @text 敵のカーソル調整Ｙ
 * @type number
 * @min -999 @max 999
 * @default 0
 * @desc 敵を対象にした際のカーソル画像のＹ座標を調整します。
 *
 * @param ShowTargetEnemyName
 * @parent <EnemySide>
 * @text 選択中の敵名を表示
 * @type boolean
 * @default true
 * @desc 選択中の敵の名前を表示します。
 *
 * @param EnemyNameWindowHeight
 * @parent <EnemySide>
 * @text 敵名ウィンドウの縦幅
 * @type number
 * @default 72
 * @desc 敵名ウィンドウの縦幅です。
 * 初期値は72です。
 *
 * @param EnemyNameWindowOpacity
 * @parent <EnemySide>
 * @text 敵名ウィンドウの不透明度
 * @type number
 * @default 255
 * @desc 敵名ウィンドウの不透明度（0~255）です。
 *
 * @param EnemyNameWindowAdjustX
 * @parent <EnemySide>
 * @text 敵名ウィンドウの調整Ｘ
 * @type number
 * @min -999 @max 999
 * @default 0
 * @desc 敵名ウィンドウのＸ座標を調整します。
 *
 * @param EnemyNameWindowAdjustY
 * @parent <EnemySide>
 * @text 敵名ウィンドウの調整Ｙ
 * @type number
 * @min -999 @max 999
 * @default 60
 * @desc 敵名ウィンドウのＹ座標を調整します。
 *
 * @param EnemyNameFontSize
 * @parent <EnemySide>
 * @text 敵名フォントサイズ
 * @type number
 * @desc 敵名のフォントサイズです。
 * 空欄ならシステムの設定値を使用します。
 *
 * @param EnemyNameTextAdjustY
 * @parent <EnemySide>
 * @text 敵名テキストの調整Ｙ
 * @type number
 * @min -999 @max 999
 * @default 0
 * @desc 敵名ウィンドウに表示されるテキストのＹ座標を調整します。
 *
 * @param <ActorSide>
 * @text ＜アクター側＞
 *
 * @param SwitchActorUpDown
 * @parent <ActorSide>
 * @text アクターの上下切替
 * @type boolean
 * @default true
 * @desc アクターの選択を上下で切替できるようにします。
 *
 * @param SwitchActorLeftRight
 * @parent <ActorSide>
 * @text アクターの左右切替
 * @type boolean
 * @default true
 * @desc アクターの選択を左右で切替できるようにします。
 * こちらはＭＺの標準動作です。
 *
 * @param ActorTargetCursor
 * @parent <ActorSide>
 * @text アクターカーソルの画像
 * @type file
 * @dir img/pictures
 * @desc アクターを対象にした際のカーソル画像を指定します。
 * 空欄なら敵のものを左右反転します。
 *
 * @param ActorCursorPosition
 * @parent <ActorSide>
 * @text アクターカーソル基準点
 * @type select
 * @option 足元 @value foot
 * @option 中央 @value center
 * @option 頭上 @value head
 * @desc アクターを対象にした際のカーソル画像の上下の基準点です。
 * 空欄なら敵と同じ値を使用します。
 *
 * @param ActorCursorAdjustX
 * @parent <ActorSide>
 * @text アクターカーソル調整Ｘ
 * @type number
 * @min -999 @max 999
 * @desc アクターを対象にした際のカーソル画像のＸ座標を調整します。
 * 空欄なら敵の値を左右反転して使用します。
 *
 * @param ActorCursorAdjustY
 * @parent <ActorSide>
 * @text アクターカーソル調整Ｙ
 * @type number
 * @min -999 @max 999
 * @desc アクターを対象にした際のカーソル画像のＹ座標を調整します。
 * 空欄なら敵と同じ値を使用します。
 *
 * @param ShowTargetActorName
 * @parent <ActorSide>
 * @text 選択中のアクター名を表示
 * @type boolean
 * @desc 選択中のアクターの名前を表示します。
 * 空欄なら敵と同じ値を使用します。
 *
 * @param ActorNameWindowHeight
 * @parent <ActorSide>
 * @text アクター名ｳｨﾝﾄﾞｳの縦幅
 * @type number
 * @desc アクター名ウィンドウの縦幅です。
 * 空欄なら敵と同じ値を使用します。
 *
 * @param ActorNameWindowOpacity
 * @parent <ActorSide>
 * @text アクター名ｳｨﾝﾄﾞｳ不透明度
 * @type number
 * @desc アクター名ウィンドウの不透明度（0~255）です。
 * 空欄なら敵と同じ値を使用します。
 *
 * @param ActorNameWindowAdjustX
 * @parent <ActorSide>
 * @text アクター名ｳｨﾝﾄﾞｳの調整Ｘ
 * @type number
 * @desc アクター名ウィンドウのＸ座標を調整します。
 * 空欄なら敵の値を左右反転して使用します。
 *
 * @param ActorNameWindowAdjustY
 * @parent <ActorSide>
 * @text アクター名ｳｨﾝﾄﾞｳの調整Ｙ
 * @type number
 * @min -999 @max 999
 * @desc アクター名ウィンドウのＹ座標を調整します。
 * 空欄なら敵と同じ値を使用します。
 *
 * @param ActorNameFontSize
 * @parent <ActorSide>
 * @text アクター名フォントサイズ
 * @type number
 * @desc アクター名のフォントサイズです。
 * 空欄なら敵と同じ値を使用します。
 *
 * @param ActorNameTextAdjustY
 * @parent <ActorSide>
 * @text アクター名ﾃｷｽﾄの調整Ｙ
 * @type number
 * @min -999 @max 999
 * @desc アクター名ウィンドウに表示されるテキストのＹ座標を調整します。
 * 空欄なら敵と同じ値を使用します。
 *
 * @param <Common>
 * @text ＜共通設定＞
 *
 * @param LeftLimitX
 * @parent <Common>
 * @text 左の限界Ｘ座標
 * @type string
 * @default 0
 * @desc カーソル表示の左限界となるＸ座標です。
 * 初期値は画面左端（0）
 *
 * @param RightLimitX
 * @parent <Common>
 * @text 右の限界Ｘ座標
 * @type string
 * @default Graphics.width
 * @desc カーソル表示の右限界となるＸ座標です。
 * 初期値は画面右端（Graphics.width）
 *
 * @param UpLimitY
 * @parent <Common>
 * @text 上の限界Ｙ座標
 * @type string
 * @default 0
 * @desc カーソル表示の上限界となるＹ座標です。
 * 初期値は画面上端（0）
 *
 * @param DownLimitY
 * @parent <Common>
 * @text 下の限界Ｙ座標
 * @type string
 * @default Graphics.height
 * @desc カーソル表示の下限界となるＹ座標です。
 * 初期値は画面下端（Graphics.height）
 */

(function () {
  "use strict";

  /**
   * バージョン互換対応
   */
  if (!Array.prototype.find) {
    Array.prototype.find = function (predicate) {
      if (this === null) {
        throw new TypeError("Array.prototype.find called on null or undefined");
      }
      if (typeof predicate !== "function") {
        throw new TypeError("predicate must be a function");
      }
      var list = Object(this);
      var length = list.length >>> 0;
      var thisArg = arguments[1];
      var value;

      for (var i = 0; i < length; i++) {
        value = list[i];
        if (predicate.call(thisArg, value, i, list)) {
          return value;
        }
      }
      return undefined;
    };
  }

  function toBoolean(val, def) {
    // 空白なら初期値を返す
    if (val === "" || val === undefined) {
      return def;

      // 既にboolean型なら、そのまま返す
    } else if (typeof val === "boolean") {
      return val;
    }
    // 文字列ならboolean型に変換して返す
    return val.toLowerCase() == "true";
  }
  function toNumber(str, def) {
    if (str == undefined || str == "") {
      return def;
    }
    return isNaN(str) ? def : +(str || def);
  }
  function setDefault(str, def) {
    return str ? str : def;
  }
  function isBlank(str) {
    return str === "" || str === undefined || str === null;
  }

  const parameters = PluginManager.parameters("NRP_BattleTargetCursor");

  const pHideEnemyNameList = toBoolean(parameters["HideEnemyNameList"], true);
  const pSwitchEnemyUpDown = toBoolean(parameters["SwitchEnemyUpDown"], true);
  const pSwitchEnemyLeftRight = setDefault(parameters["SwitchEnemyLeftRight"]);
  const pEnemyTargetCursor = parameters["EnemyTargetCursor"];
  const pEnemyCursorPosition = parameters["EnemyCursorPosition"];
  const pEnemyCursorAdjustX = toNumber(parameters["EnemyCursorAdjustX"], 0);
  const pEnemyCursorAdjustY = toNumber(parameters["EnemyCursorAdjustY"], 0);

  const pShowTargetEnemyName = toBoolean(
    parameters["ShowTargetEnemyName"],
    true
  );
  const pEnemyNameWindowHeight = toNumber(
    parameters["EnemyNameWindowHeight"],
    72
  );
  const pEnemyNameWindowOpacity = toNumber(
    parameters["EnemyNameWindowOpacity"],
    255
  );
  const pEnemyNameWindowAdjustX = toNumber(
    parameters["EnemyNameWindowAdjustX"],
    0
  );
  const pEnemyNameWindowAdjustY = toNumber(
    parameters["EnemyNameWindowAdjustY"],
    0
  );
  const pEnemyNameFontSize = toNumber(parameters["EnemyNameFontSize"]);
  const pEnemyNameTextAdjustY = toNumber(parameters["EnemyNameTextAdjustY"], 0);

  const pSwitchActorUpDown = toBoolean(parameters["SwitchActorUpDown"], true);
  const pSwitchActorLeftRight = toBoolean(
    parameters["SwitchActorLeftRight"],
    true
  );

  // 以下Enemyの値をデフォルト値とする。
  // ただし、画像およびＸ座標系は加工するため別途処理。
  const pActorTargetCursor = parameters["ActorTargetCursor"];
  const pActorCursorPosition = setDefault(
    parameters["ActorCursorPosition"],
    pEnemyCursorPosition
  );
  const pActorCursorAdjustX = parameters["ActorCursorAdjustX"];
  const pActorCursorAdjustY = toNumber(
    parameters["ActorCursorAdjustY"],
    pEnemyCursorAdjustY
  );
  const pShowTargetActorName = toBoolean(
    parameters["ShowTargetActorName"],
    pShowTargetEnemyName
  );
  const pActorNameWindowHeight = toNumber(
    parameters["ActorNameWindowHeight"],
    pEnemyNameWindowHeight
  );
  const pActorNameWindowOpacity = toNumber(
    parameters["ActorNameWindowOpacity"],
    pEnemyNameWindowOpacity
  );
  const pActorNameWindowAdjustX = parameters["ActorNameWindowAdjustX"];
  const pActorNameWindowAdjustY = toNumber(
    parameters["ActorNameWindowAdjustY"],
    pEnemyNameWindowAdjustY
  );
  const pActorNameFontSize = toNumber(
    parameters["ActorNameFontSize"],
    pEnemyNameFontSize
  );
  const pActorNameTextAdjustY = toNumber(
    parameters["ActorNameTextAdjustY"],
    pEnemyNameTextAdjustY
  );

  const pLeftLimitX = setDefault(parameters["LeftLimitX"], "0");
  const pRightLimitX = setDefault(parameters["RightLimitX"], "Graphics.width");
  const pUpLimitY = setDefault(parameters["UpLimitY"], "0");
  const pDownLimitY = setDefault(parameters["DownLimitY"], "Graphics.height");

  // 敵名リストを非表示
  if (pHideEnemyNameList) {
    /**
     * 【上書】最大列数を１に変更
     * これにより、上下での切替を実現する。
     */
    Window_BattleEnemy.prototype.maxCols = function () {
      return 1;
    };

    /**
     * ●NRP_PageWindowとの競合対策
     */
    Window_BattleEnemy.prototype.isUsePage = function () {
      return false;
    };

    /**
     * ●敵名ウィンドウ表示
     */
    const _Window_BattleEnemy_show = Window_BattleEnemy.prototype.show;
    Window_BattleEnemy.prototype.show = function () {
      _Window_BattleEnemy_show.apply(this, arguments);

      // 非表示
      // ※機能はそのまま使うのでhideにはしない。
      // ※ＭＶで微妙に何かが見えるので位置を-100に設定しておく。
      this.move(-100, -100, 0, 0);
    };

    /**
     * ●敵選択開始
     */
    const _Scene_Battle_startEnemySelection =
      Scene_Battle.prototype.startEnemySelection;
    Scene_Battle.prototype.startEnemySelection = function () {
      _Scene_Battle_startEnemySelection.apply(this, arguments);

      // ステータスウィンドウを非表示しない。
      this._statusWindow.show();
    };
  }

  /**
   * 戦闘画面用にWindowを追加する。
   */
  const _Scene_Battle_prototype_createAllWindows =
    Scene_Battle.prototype.createAllWindows;
  Scene_Battle.prototype.createAllWindows = function () {
    _Scene_Battle_prototype_createAllWindows.apply(this, arguments);

    //--------------------------------------
    // 敵ウィンドウ生成
    //--------------------------------------
    if (pShowTargetEnemyName) {
      // この時点ではサイズ不明なので、とりあえずGraphicsの全領域を確保
      // ※確保しておかないと文字が表示できない。
      this._enemyNameWindow = new Window_BattleEnemyName(
        new Rectangle(0, 0, Graphics.boxWidth, Graphics.boxHeight)
      );
      // this.addWindow(this._enemyNameWindow);
      this.addChild(this._enemyNameWindow);

      // 追加ウィンドウへアクセスできるように設定
      this._enemyWindow._enemyNameWindow = this._enemyNameWindow;
    }

    //--------------------------------------
    // アクターウィンドウ生成
    //--------------------------------------
    if (pShowTargetActorName) {
      this._actorNameWindow = new Window_BattleActorName(
        new Rectangle(0, 0, Graphics.boxWidth, Graphics.boxHeight)
      );
      this.addChild(this._actorNameWindow);

      // 追加ウィンドウへアクセスできるように設定
      this._actorWindow._actorNameWindow = this._actorNameWindow;
    }
  };

  /**
   * ●敵選択
   */
  const _Window_BattleEnemy_select = Window_BattleEnemy.prototype.select;
  Window_BattleEnemy.prototype.select = function (index) {
    _Window_BattleEnemy_select.apply(this, arguments);

    const enemy = this.enemy();
    if (enemy && this._enemyNameWindow) {
      this._enemyNameWindow.setEnemy(enemy);
      this._enemyNameWindow.select(index);
    }
  };

  /**
   * ●敵選択ウィンドウ非表示
   */
  const _Window_BattleEnemy_hide = Window_BattleEnemy.prototype.hide;
  Window_BattleEnemy.prototype.hide = function () {
    _Window_BattleEnemy_hide.apply(this, arguments);

    if (this._enemyNameWindow) {
      this._enemyNameWindow.hide();
    }
  };

  /**
   * ●アクター選択
   */
  const _Window_BattleActor_select = Window_BattleActor.prototype.select;
  Window_BattleActor.prototype.select = function (index) {
    _Window_BattleActor_select.apply(this, arguments);

    const actor = this.actor(index);
    if (actor && this._actorNameWindow) {
      this._actorNameWindow.setActor(actor);
      this._actorNameWindow.select(index);
    }
  };

  /**
   * ●アクター選択ウィンドウ非表示
   */
  const _Window_BattleActor_hide = Window_BattleActor.prototype.hide;
  Window_BattleActor.prototype.hide = function () {
    _Window_BattleActor_hide.apply(this, arguments);

    if (this._actorNameWindow) {
      this._actorNameWindow.hide();
    }
  };

  //-----------------------------------------------------------------------------
  // 敵名表示用の追加ウィンドウ
  //-----------------------------------------------------------------------------

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

  Window_BattleEnemyName.prototype = Object.create(Window_Selectable.prototype);
  Window_BattleEnemyName.prototype.constructor = Window_BattleEnemyName;

  // MV
  if (Utils.RPGMAKER_NAME == "MV") {
    Window_BattleEnemyName.prototype.initialize = function (x, y) {
      this._enemy = undefined;
      const width = Graphics.boxWidth;
      const height = pEnemyNameWindowHeight;

      Window_Selectable.prototype.initialize.call(this, x, y, width, height);

      // // 暗くするかどうか。
      // this.setBackgroundType(1);

      this.refresh();
      this.hide();

      // ウィンドウの不透明度
      this.opacity = pEnemyNameWindowOpacity;
    };

    // MZ
  } else {
    Window_BattleEnemyName.prototype.initialize = function (rect) {
      this._enemy = undefined;
      Window_Selectable.prototype.initialize.call(this, rect);

      // 暗くするかどうか。
      // ※うまくいかないので注釈化
      // this.setBackgroundType(1);

      this.refresh();
      this.hide();

      // ウィンドウの不透明度
      this.opacity = pEnemyNameWindowOpacity;

      // 余白（機能未実装）
      // this.padding = 0;
    };
  }

  Window_BattleEnemyName.prototype.maxCols = function () {
    return 1;
  };

  Window_BattleEnemyName.prototype.maxItems = function () {
    return 1;
  };

  Window_BattleEnemyName.prototype.setEnemy = function (enemy) {
    return (this._enemy = enemy);
  };

  Window_BattleEnemyName.prototype.select = function (index) {
    const enemy = this._enemy;
    if (enemy) {
      // 指定があればフォントサイズを設定
      if (pEnemyNameFontSize) {
        this.contents.fontSize = pEnemyNameFontSize;
      }

      const sprite = getSprite(enemy);

      // ウィンドウ幅を求める。
      const textWidth = this.textWidth(
        this.convertEscapeCharacters(enemy.name())
      );
      // ウィンドウの余白と文字の余白を加算
      let textPaddingWidth =
        textWidth + this.padding * 2 + itemPadding(this) * 2;
      // 根拠不明だが調整
      // ※加算しないと文字が小さい……。
      textPaddingWidth += 16;

      // ウィンドウの高さ
      const windowHeight = pEnemyNameWindowHeight;

      // Ｘ座標
      let x = sprite.x - textPaddingWidth / 2;
      x += eval(pEnemyNameWindowAdjustX);

      // Ｙ座標
      let y = sprite.y;
      // 頭上
      if (pEnemyCursorPosition == "head") {
        y -= sprite.height;
        // 中央
      } else if (pEnemyCursorPosition == "center") {
        y -= sprite.height / 2 + windowHeight / 2;
        // 足元
      } else {
        y -= windowHeight;
      }

      // 調整Ｙ座標
      y += eval(pEnemyNameWindowAdjustY);

      // 上下左右の限界を考慮
      x = limitedX(x);
      y = limitedY(y);

      // ウィンドウ位置を設定（Ｘ座標、Ｙ座標、幅、高さ）
      this.move(x, y, textPaddingWidth, windowHeight);
      this.show();

      // 敵のカーソル画像描画
      const spriteset = getSpriteset();
      if (spriteset) {
        dispCursorSprite(spriteset._enemyCursorSprite, spriteset._enemySprites);
      }
    }
  };

  /**
   * ●項目の描画
   */
  Window_BattleEnemyName.prototype.drawItem = function (index) {
    if (this._enemy) {
      this.resetTextColor();

      // 表示名＆位置サイズの指定
      const name = this._enemy.name();

      let rect;
      // MV
      if (Utils.RPGMAKER_NAME == "MV") {
        rect = this.itemRectForText(index);
        // MZ
      } else {
        rect = this.itemLineRect(index);
      }

      this.drawText(
        name,
        rect.x,
        rect.y + pEnemyNameTextAdjustY,
        rect.width,
        "center"
      );
    }
  };

  Window_BattleEnemyName.prototype.show = function () {
    this.refresh();

    // MZにしかない関数なので考慮
    if ($gameTemp.clearTouchState) {
      $gameTemp.clearTouchState();
    }

    Window_Selectable.prototype.show.call(this);
  };

  Window_BattleEnemyName.prototype.hide = function () {
    Window_Selectable.prototype.hide.call(this);

    // 敵のカーソル画像描画（消去）
    const spriteset = getSpriteset();
    if (spriteset) {
      dispCursorSprite(spriteset._enemyCursorSprite, spriteset._enemySprites);
    }
  };

  Window_BattleEnemyName.prototype.refresh = function () {
    Window_Selectable.prototype.refresh.call(this);
  };

  /**
   * ●項目の背景
   */
  Window_BattleEnemyName.prototype.drawItemBackground = function (index) {
    // 非表示
    // const rect = this.itemRect(index);
    // this.drawBackgroundRect(rect);
  };

  //-----------------------------------------------------------------------------
  // アクター名表示用の追加ウィンドウ
  //-----------------------------------------------------------------------------

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

  Window_BattleActorName.prototype = Object.create(Window_Selectable.prototype);
  Window_BattleActorName.prototype.constructor = Window_BattleActorName;

  // MV
  if (Utils.RPGMAKER_NAME == "MV") {
    Window_BattleActorName.prototype.initialize = function (x, y) {
      this._actor = undefined;
      const width = Graphics.boxWidth;
      const height = pActorNameWindowHeight;

      Window_Selectable.prototype.initialize.call(this, x, y, width, height);

      this.refresh();
      this.hide();

      // ウィンドウの不透明度
      this.opacity = pActorNameWindowOpacity;
    };

    // MZ
  } else {
    Window_BattleActorName.prototype.initialize = function (rect) {
      this._actor = undefined;
      Window_Selectable.prototype.initialize.call(this, rect);

      this.refresh();
      this.hide();

      // ウィンドウの不透明度
      this.opacity = pActorNameWindowOpacity;
    };
  }

  Window_BattleActorName.prototype.maxCols = function () {
    return 1;
  };

  Window_BattleActorName.prototype.maxItems = function () {
    return 1;
  };

  Window_BattleActorName.prototype.setActor = function (actor) {
    return (this._actor = actor);
  };

  /**
   * ●選択時（カーソル移動時）
   */
  Window_BattleActorName.prototype.select = function (index) {
    const actor = this._actor;
    if (actor) {
      // 指定があればフォントサイズを設定
      if (pActorNameFontSize) {
        this.contents.fontSize = pActorNameFontSize;
      }

      const sprite = getSprite(actor);

      // ウィンドウ幅を求める。
      const textWidth = this.textWidth(
        this.convertEscapeCharacters(actor.name())
      );
      // ウィンドウの余白と文字の余白を加算
      let textPaddingWidth =
        textWidth + this.padding * 2 + itemPadding(this) * 2;
      // 根拠不明だが調整
      // ※加算しないと文字が小さい……。
      textPaddingWidth += 16;

      const windowHeight = pActorNameWindowHeight;

      // Ｘ座標
      let x = sprite.x - textPaddingWidth / 2;

      // Ｘ座標の調整
      let actorNameWindowAdjustX;
      // 値が存在しない場合は敵の値を参照し、左右反転
      if (isBlank(pActorNameWindowAdjustX)) {
        actorNameWindowAdjustX = eval(pEnemyNameWindowAdjustX) * -1;
      } else {
        actorNameWindowAdjustX = eval(pActorNameWindowAdjustX);
      }
      x += actorNameWindowAdjustX;

      // Ｙ座標
      let y = sprite.y;
      // 頭上
      if (pActorCursorPosition == "head") {
        y -= sprite.height;
        // 中央
      } else if (pActorCursorPosition == "center") {
        y -= sprite.height / 2 + windowHeight / 2;
        // 足元
      } else {
        y -= windowHeight;
      }

      // 調整Ｙ座標
      y += eval(pActorNameWindowAdjustY);

      // 上下左右の限界を考慮
      x = limitedX(x);
      y = limitedY(y);

      this.move(x, y, textPaddingWidth, windowHeight);
      this.show();

      // アクターのカーソル画像描画
      const spriteset = getSpriteset();
      if (spriteset) {
        dispCursorSprite(spriteset._actorCursorSprite, spriteset._actorSprites);
      }
    }
  };

  /**
   * ●項目の描画
   */
  Window_BattleActorName.prototype.drawItem = function (index) {
    if (this._actor) {
      this.resetTextColor();

      // 表示名＆位置サイズの指定
      const name = this._actor.name();

      let rect;
      // MV
      if (Utils.RPGMAKER_NAME == "MV") {
        rect = this.itemRectForText(index);
        // MZ
      } else {
        rect = this.itemLineRect(index);
      }

      this.drawText(
        name,
        rect.x,
        rect.y + pActorNameTextAdjustY,
        rect.width,
        "center"
      );
    }
  };

  Window_BattleActorName.prototype.show = function () {
    this.refresh();

    // MZにしかない関数なので考慮
    if ($gameTemp.clearTouchState) {
      $gameTemp.clearTouchState();
    }

    Window_Selectable.prototype.show.call(this);
  };

  Window_BattleActorName.prototype.hide = function () {
    Window_Selectable.prototype.hide.call(this);

    // アクターのカーソル画像描画（消去）
    const spriteset = getSpriteset();
    if (spriteset) {
      dispCursorSprite(spriteset._actorCursorSprite, spriteset._actorSprites);
    }
  };

  Window_BattleActorName.prototype.refresh = function () {
    Window_Selectable.prototype.refresh.call(this);
  };

  /**
   * ●項目の背景
   */
  Window_BattleActorName.prototype.drawItemBackground = function (index) {
    // 非表示
    // const rect = this.itemRect(index);
    // this.drawBackgroundRect(rect);
  };

  //-----------------------------------------------------------------------------
  // 敵の選択
  //-----------------------------------------------------------------------------

  // 敵名リストが非表示の場合のみ有効
  if (pHideEnemyNameList) {
    // 上下カーソルでの切替を無効に
    if (!pSwitchEnemyUpDown) {
      Window_BattleEnemy.prototype.cursorDown = function (wrap) {
        // 何もしない
      };

      Window_BattleEnemy.prototype.cursorUp = function (wrap) {
        // 何もしない
      };
    }

    // 左右カーソルでの切替を有効に
    if (pSwitchEnemyLeftRight) {
      Window_BattleEnemy.prototype.cursorRight = function (wrap) {
        // 右送りの場合
        if (pSwitchEnemyLeftRight == "right") {
          // 下と同じ動作
          Window_Selectable.prototype.cursorDown.apply(this, arguments);
          // 左送りの場合
        } else if (pSwitchEnemyLeftRight == "left") {
          // 上と同じ動作
          Window_Selectable.prototype.cursorUp.apply(this, arguments);
        }
      };

      Window_BattleEnemy.prototype.cursorLeft = function (wrap) {
        // 右送りの場合
        if (pSwitchEnemyLeftRight == "right") {
          // 上と同じ動作
          Window_Selectable.prototype.cursorUp.apply(this, arguments);
          // 左送りの場合
        } else if (pSwitchEnemyLeftRight == "left") {
          // 下と同じ動作
          Window_Selectable.prototype.cursorDown.apply(this, arguments);
        }
      };
    }
  }

  //-----------------------------------------------------------------------------
  // アクターの選択
  //-----------------------------------------------------------------------------

  // MV
  if (Utils.RPGMAKER_NAME == "MV") {
    // 上下カーソルでの切替を無効に
    if (!pSwitchActorUpDown) {
      Window_BattleActor.prototype.cursorDown = function (wrap) {
        // 何もしない
      };

      Window_BattleActor.prototype.cursorUp = function (wrap) {
        // 何もしない
      };
    }

    // 左右カーソルでの切替を有効に
    if (pSwitchActorLeftRight) {
      Window_BattleActor.prototype.cursorRight = function (wrap) {
        // 下と同じ動作
        Window_Selectable.prototype.cursorDown.apply(this, arguments);
      };

      Window_BattleActor.prototype.cursorLeft = function (wrap) {
        // 上と同じ動作
        Window_Selectable.prototype.cursorUp.apply(this, arguments);
      };
    }

    // MZ
  } else {
    // 上下カーソルでの切替を有効に
    if (pSwitchActorUpDown) {
      Window_BattleActor.prototype.cursorDown = function (wrap) {
        // 右と同じ動作
        Window_Selectable.prototype.cursorRight.apply(this, arguments);
      };

      Window_BattleActor.prototype.cursorUp = function (wrap) {
        // 左と同じ動作
        Window_Selectable.prototype.cursorLeft.apply(this, arguments);
      };
    }

    // 左右カーソルでの切替を無効に
    if (!pSwitchActorLeftRight) {
      Window_BattleActor.prototype.cursorRight = function (wrap) {
        // 何もしない
      };

      Window_BattleActor.prototype.cursorLeft = function (wrap) {
        // 何もしない
      };
    }
  }

  //-----------------------------------------------------------------------------
  // 対象カーソル
  //-----------------------------------------------------------------------------

  /**
   * ●戦闘時、下層の作成
   */
  const _Spriteset_Battle_createLowerLayer =
    Spriteset_Battle.prototype.createLowerLayer;
  Spriteset_Battle.prototype.createLowerLayer = function () {
    _Spriteset_Battle_createLowerLayer.apply(this, arguments);

    // 敵カーソル画像の作成
    if (pEnemyTargetCursor) {
      this._enemyCursorSprite = new Sprite();
      this._enemyCursorSprite.bitmap =
        ImageManager.loadPicture(pEnemyTargetCursor);
      this._baseSprite.addChild(this._enemyCursorSprite);
      this._enemyCursorSprite.hide();

      // アクターカーソル画像が空白の場合、敵カーソル画像を適用
      if (!pActorTargetCursor) {
        this._actorCursorSprite = new Sprite();
        this._actorCursorSprite.bitmap =
          ImageManager.loadPicture(pEnemyTargetCursor);
        // 画像を左右反転（scaleX = -1）する。
        this._actorCursorSprite.scale.x = -1;
        this._baseSprite.addChild(this._actorCursorSprite);
        this._actorCursorSprite.hide();
        return;
      }
    }

    // アクターカーソル画像の作成
    if (pActorTargetCursor) {
      this._actorCursorSprite = new Sprite();
      this._actorCursorSprite.bitmap =
        ImageManager.loadPicture(pActorTargetCursor);
      this._baseSprite.addChild(this._actorCursorSprite);
      this._actorCursorSprite.hide();
    }
  };

  /**
   * ●カーソル画像の表示制御
   */
  function dispCursorSprite(cursorSprite, battlerSprites) {
    // 画像がなければ処理終了
    if (!cursorSprite) {
      return;
    }

    // 選択中のバトラースプライトを取得
    const battlerSprite = battlerSprites.find(function (sprite) {
      return sprite._battler && sprite._battler.isSelected();
    });

    // 対象が存在する場合は表示
    if (battlerSprite) {
      // Ｘ座標
      let x = battlerSprite.x;
      // Ｙ座標
      let y = battlerSprite.y;

      // アクターの場合
      if (battlerSprite._actor) {
        let actorCursorAdjustX;

        // 値が存在しない場合は敵の値を参照し、左右反転
        if (isBlank(pActorCursorAdjustX)) {
          actorCursorAdjustX = eval(pEnemyCursorAdjustX) * -1;
        } else {
          actorCursorAdjustX = eval(pActorCursorAdjustX);
        }

        // 調整Ｘ座標
        x += actorCursorAdjustX;

        // 頭上
        if (pActorCursorPosition == "head") {
          y -= battlerSprite.height - cursorSprite.height / 2;
          // 中央
        } else if (pActorCursorPosition == "center") {
          y -= battlerSprite.height / 2;
          // 足元
        } else {
          y -= cursorSprite.height / 2;
        }
        // 調整Ｙ座標
        y += eval(pActorCursorAdjustY);

        // 敵の場合
      } else {
        // 調整Ｘ座標
        x += eval(pEnemyCursorAdjustX);

        // 頭上
        if (pEnemyCursorPosition == "head") {
          y -= battlerSprite.height - cursorSprite.height / 2;
          // 中央
        } else if (pEnemyCursorPosition == "center") {
          y -= battlerSprite.height / 2;
          // 足元
        } else {
          y -= cursorSprite.height / 2;
        }
        // 調整Ｙ座標
        y += eval(pEnemyCursorAdjustY);
      }

      // 上下左右の限界を考慮
      x = limitedX(x);
      y = limitedY(y);

      cursorSprite.x = x;
      cursorSprite.y = y;
      cursorSprite.anchor.x = 0.5;
      cursorSprite.anchor.y = 0.5;
      cursorSprite.show();

      // 対象が存在しない場合は非表示
    } else {
      cursorSprite.hide();
    }
  }

  //-----------------------------------------------------------------------------
  // 共通関数
  //-----------------------------------------------------------------------------

  /**
   * ●左右の限界を考慮したＸ座標
   */
  function limitedX(x) {
    const leftLimitX = eval(pLeftLimitX);
    const rightLimitX = eval(pRightLimitX);

    if (!isBlank(leftLimitX) && x < leftLimitX) {
      x = leftLimitX;
    } else if (!isBlank(rightLimitX) && x > rightLimitX) {
      x = rightLimitX;
    }

    return x;
  }

  /**
   * ●上下の限界を考慮したＹ座標
   */
  function limitedY(y) {
    const upLimitY = eval(pUpLimitY);
    const downLimitY = eval(pDownLimitY);

    if (!isBlank(upLimitY) && y < upLimitY) {
      y = upLimitY;
    } else if (!isBlank(downLimitY) && y > downLimitY) {
      y = downLimitY;
    }

    return y;
  }

  /**
   * ●現在の画面のSpritesetを取得する。
   */
  function getSpriteset() {
    return BattleManager._spriteset;
  }

  /**
   * 指定したバトラーのスプライトを取得する。
   */
  function getSprite(battler) {
    if (!battler) {
      return undefined;
    }

    var sprite;
    const spriteset = getSpriteset();
    const actorSprites = spriteset._actorSprites;
    const enemySprites = spriteset._enemySprites;

    if (battler.isActor()) {
      for (var i = 0; i < actorSprites.length; i++) {
        var s = actorSprites[i];
        if (s._battler == battler) {
          sprite = s;
          break;
        }
      }

      // Sprite_Actorのサイズ設定
      setActorSpriteSize(sprite);
    } else {
      for (var i = 0; i < enemySprites.length; i++) {
        var s = enemySprites[i];
        if (s._battler == battler) {
          sprite = s;
          break;
        }
      }
    }

    return sprite;
  }

  /**
   * 指定したアクタースプライトのサイズを設定する。
   */
  function setActorSpriteSize(sprite) {
    if (sprite && sprite._battler.isActor()) {
      // Sprite_Actorのサイズが取れないのでeffectTargetのものをセットする。
      // やや強引かも……。
      // MZの場合は不要なのでMVのみ。
      if (Utils.RPGMAKER_NAME == "MV") {
        sprite.width = sprite._effectTarget.width;
        sprite.height = sprite._effectTarget.height;
      }
    }
  }

  /**
   * ●余白を取得
   */
  function itemPadding(win) {
    if (Utils.RPGMAKER_NAME == "MZ") {
      return win.itemPadding();
    } else {
      return win.textPadding();
    }
  }
})();


code:
//=============================================================================
// NRP_CharacterDirectionPriority.js
//=============================================================================
/*:
 * @target MV MZ
 * @plugindesc v1.00 Accept direction changes, even if the direction is fixed.
 * @author Takeshi Sunagawa (http://newrpg.seesaa.net/)
 * @url http://newrpg.seesaa.net/article/481360429.html
 *
 * @help Make it possible to accept direction change commands
 * for characters in fixed direction.
 *
 * In RPG Maker MV and MZ, instructions to change the direction of a character
 * with a fixed direction from the movement route settings will be ignored.
 * However, this specification is not very reasonable.
 * If you don't intend to change the direction, you don't tell it to do so.
 *
 * There is also a way to undo the direction fixation each time,
 * but it is not reasonable,
 * as it is time-consuming and error-prone.
 *
 * Therefore, I will change the specification
 * to accept direction changes only at the time of instruction,
 * just like RPG Maker 2000.
 *
 * The relevant process is as follows.
 * - Change the direction of the move route setting
 * - Change the direction when autonomous movement is custom.
 * - Change the direction when setting the position of an event.
 *
 * ■Usage
 * You only need to apply the plugin.
 * There are no parameters.
 *
 * [Terms]
 * There are no restrictions.
 * Modification, redistribution freedom, commercial availability,
 * and rights indication are also optional.
 * The author is not responsible,
 * but will deal with defects to the extent possible.
 */

/*:ja
 * @target MV MZ
 * @plugindesc v1.00 キャラクターの向き固定時も向き変更を有効に
 * @author 砂川赳（http://newrpg.seesaa.net/）
 * @url http://newrpg.seesaa.net/article/481360429.html
 *
 * @help 向き固定状態のキャラクターに対しても、
 * 向きの変更命令を受けつけるようにします。
 *
 * ツクールＭＶ～ＭＺでは、向き固定状態のキャラクターに対して、
 * 移動ルートの設定から向き変更を指示しても、無視される仕様です。
 * しかしながら、この仕様はあまり合理的ではありません。
 * そもそも、向きを変更する意図がないなら指示などしないからです。
 *
 * いちいち、向き固定を解除する方法もありますが、
 * 手間がかかる上にミスもしやすく、やはり合理的ではありません。
 *
 * というわけで、ツクール2000などと同じように、
 * 命令時のみ、向き変更を受けつけるように仕様を変更します。
 *
 * 該当の処理は以下の通りです。
 * ・移動ルートの設定の向き変更
 * ・自律移動がカスタムの際の向き変更
 * ・イベントの位置設定時の向き変更
 *
 * ■使用方法
 * プラグインを適用するだけでＯＫです。
 * パラメータなどはありません。
 *
 * ■利用規約
 * 特に制約はありません。
 * 改変、再配布自由、商用可、権利表示も任意です。
 * 作者は責任を負いませんが、不具合については可能な範囲で対応します。
 */
(function () {
  "use strict";

  const PLUGIN_NAME = "NRP_CharacterDirectionPriority";
  const parameters = PluginManager.parameters(PLUGIN_NAME);

  /**
   * ●向き設定
   */
  const _Game_CharacterBase_setDirection =
    Game_CharacterBase.prototype.setDirection;
  Game_CharacterBase.prototype.setDirection = function (d) {
    // 向き変更強制フラグがオンなら変更
    if (this.isForceDirection() && d) {
      this._direction = d;
    }

    _Game_CharacterBase_setDirection.apply(this, arguments);
  };

  /**
   * 【独自】向き変更強制フラグを設定
   */
  Game_CharacterBase.prototype.setForceDirection = function (flg) {
    this._forceDirection = flg;
  };

  /**
   * 【独自】向き変更強制フラグを取得
   */
  Game_CharacterBase.prototype.isForceDirection = function () {
    return !!this._forceDirection;
  };

  /**
   * ●移動ルートの設定
   */
  const _Game_Character_processMoveCommand =
    Game_Character.prototype.processMoveCommand;
  Game_Character.prototype.processMoveCommand = function (command) {
    const gc = Game_Character;

    // 向き変更に該当する処理の場合、強制フラグを立てる。
    switch (command.code) {
      case gc.ROUTE_TURN_DOWN:
        this.setForceDirection(true);
        break;
      case gc.ROUTE_TURN_LEFT:
        this.setForceDirection(true);
        break;
      case gc.ROUTE_TURN_RIGHT:
        this.setForceDirection(true);
        break;
      case gc.ROUTE_TURN_UP:
        this.setForceDirection(true);
        break;
      case gc.ROUTE_TURN_90D_R:
        this.setForceDirection(true);
        break;
      case gc.ROUTE_TURN_90D_L:
        this.setForceDirection(true);
        break;
      case gc.ROUTE_TURN_180D:
        this.setForceDirection(true);
        break;
      case gc.ROUTE_TURN_90D_R_L:
        this.setForceDirection(true);
        break;
      case gc.ROUTE_TURN_RANDOM:
        this.setForceDirection(true);
        break;
      case gc.ROUTE_TURN_TOWARD:
        this.setForceDirection(true);
        break;
      case gc.ROUTE_TURN_AWAY:
        this.setForceDirection(true);
        break;
    }

    _Game_Character_processMoveCommand.apply(this, arguments);

    // 強制フラグを解除
    this.setForceDirection(false);
  };

  /**
   * ●イベントの位置設定
   */
  const _Game_Interpreter_command203 = Game_Interpreter.prototype.command203;
  Game_Interpreter.prototype.command203 = function (params) {
    const character = this.character(params[0]);
    if (character) {
      if (params[4] > 0) {
        character.setForceDirection(true);
      }
    }

    const ret = _Game_Interpreter_command203.apply(this, arguments);

    // 戻す
    if (character) {
      if (params[4] > 0) {
        character.setForceDirection(false);
      }
    }

    return ret;
  };
})();

repo_name: dazed/translations

license: unknown


code:
//=============================================================================
// NRP_EnemyRoutineKai.js
//=============================================================================
/*:
 * @target MV MZ
 * @plugindesc v1.07 Improve the enemy's action routine.
 * @author Takeshi Sunagawa (http://newrpg.seesaa.net/)
 * @url http://newrpg.seesaa.net/article/473218336.html
 *
 * @help Improve the enemy's action routine as follows.
 *
 * 1. Re-select actions not only at the start of the turn
 *    but also just before the action.
 * 2. Select the target with the least amount of HP & MP recovery.
 * 3. Do not use the MP drain skill on an opponent with 0 MP.
 * 4. Do not overlay already active states.
 * 5. Do not overlay already limited buffs or debuffs.
 * 6. Do not heal, cure, or revive if there is no valid target.
 *
 * [Individual Settings]
 * You can also make individual settings
 * by entering the following in the note of the enemy.
 *
 * - Re-select just before action?  : <RoutineReset:[true or false]>
 * - Pointless action or not?       : <RoutineTest:[true or false]>
 * - Control recovery actions?      : <RoutineRecover:[true or false]>
 * - Condition for HP recovery      : <RoutineIfHp:[condition]>.
 * - Condition for MP recovery      : <RoutineIfMp:[condition]>.
 * - Control MP drain               ：<RoutineMpDrain:[true or false]>
 * - Control revival action?        : <RoutineForDead:[true or false]>
 * - Control the effect for actors? : <RoutineActorEffect:[true or false]>
 * - Control the effect for enemys  : <RoutineEnemyEffect:[true or false]>
 * - Watch for state resistance?    : <RoutineWatchResist:[true or false]>
 *
 * [Notes]
 * - Only recovery type skills can be targeted for recovery control.
 * - Effects such as state are only determined by "none" type skills.
 * - Skills that belong to the HP damage/drain type
 *   are not controlled regardless of their effect.
 * - If a speed compensation skill is selected at the start of a turn,
 *   the action will not be reselected.
 * - Also, when you re-select an action,
 *   you will not select a speed compensation skill.
 * - In the case of "Force Action", if the target is "Random",
 *   the control will also be performed.
 *
 * [Terms]
 * There are no restrictions.
 * Modification, redistribution freedom, commercial availability,
 * and rights indication are also optional.
 * The author is not responsible,
 * but will deal with defects to the extent possible.
 *
 * @param <Base Setting>
 *
 * @param resetAction
 * @parent <Base Setting>
 * @type boolean
 * @default false
 * @desc If ON, it will make another action decision just before the action.
 * The action is determined one at a time, even when there are multiple actions.
 *
 * @param testApply
 * @parent <Base Setting>
 * @type boolean
 * @default true
 * @desc Execute an action judgment.
 * If ON, you will no longer use ineffective skills.
 *
 * @param <Heal>
 *
 * @param controlRecover
 * @parent <Heal>
 * @type boolean
 * @default true
 * @desc If it is on, it controls the recovery skill.
 *
 * @param ifHpRecover
 * @parent <Heal>
 * @type text
 * @default b.hpRate() < 1.0
 * @desc Set the condition for HP recovery.
 * When someone's HP drops below 100% with "b.hpRate() < 1.0".
 *
 * @param ifMpRecover
 * @parent <Heal>
 * @type text
 * @default b.mpRate() < 1.0
 * @desc Set the condition for MP recovery.
 * When someone's MP drops below 100% with "b.mpRate() < 1.0".
 *
 * @param controlForDead
 * @parent <Heal>
 * @type boolean
 * @default true
 * @desc If it's on, it gives you control over skills that target the dead.
 *
 * @param <MP Drain>
 *
 * @param controlMpDrain
 * @parent <MP Drain>
 * @type boolean
 * @default true
 * @desc If ON, you control related to MP drain/damage skills.
 *
 * @param <Effect>
 *
 * @param controlEnemyEffect
 * @parent <Effect>
 * @type boolean
 * @default true
 * @desc If ON, you control the effect skill for the enemy.
 * (e.g. support states)
 *
 * @param controlActorEffect
 * @parent <Effect>
 * @type boolean
 * @default true
 * @desc If ON, you control the effect skill for the actor.
 * (e.g. abnormal states)
 *
 * @param watchResist
 * @parent <Effect>
 * @type boolean
 * @default false
 * @desc If ON, the target's resistance is checked
 * and the invalid state is not used.
 */

/*:ja
 * @target MV MZ
 * @plugindesc v1.07 敵行動ルーチンを改善します。
 * @author 砂川赳（http://newrpg.seesaa.net/）
 * @url http://newrpg.seesaa.net/article/473218336.html
 *
 * @help 敵の行動ルーチンを以下の通り改善します。
 *
 * ・ターン開始時だけでなく行動直前にも行動再選択を行う。
 * ・ＨＰ＆ＭＰ回復時に最も減っている対象を選択する。
 * ・ＭＰが０の相手にＭＰ吸収スキルを使わない。
 * ・既にかかっているステートを重ねがけしない。
 * ・既に限界となっている能力変化を重ねがけしない。
 * ・有効な対象が存在しない場合、回復や治療、蘇生を行わない。
 *
 * ■個別設定
 * 敵キャラのメモ欄に以下を記入すれば個別設定も可能です。
 *
 * ・行動直前の再設定を行うか？     ：<RoutineReset:[true or false]>
 * ・無意味な行動をしないか？       ：<RoutineTest:[true or false]>
 * ・回復系行動を制御するか？       ：<RoutineRecover:[true or false]>
 * ・HP回復を行う条件              ：<RoutineIfHp:[condition]>
 * ・MP回復を行う条件              ：<RoutineIfMp:[condition]>
 * ・MP吸収を制御するか？          ：<RoutineMpDrain:[true or false]>
 * ・蘇生系行動を制御するか？       ：<RoutineForDead:[true or false]>
 * ・対アクター使用効果を制御するか？：<RoutineActorEffect:[true or false]>
 * ・対エネミー使用効果を制御するか？：<RoutineEnemyEffect:[true or false]>
 * ・ステート耐性を見るか？         ：<RoutineWatchResist:[true or false]>
 *
 * ■注意点
 * ・回復時の対象制御はタイプが回復系のスキルのみ行います。
 * ・ステートなど使用効果の有効判定は、タイプが『なし』のスキルのみ行います。
 * ・タイプがＨＰダメージ・吸収に該当するスキルは使用効果に依らず制御しません。
 * ・ターン制かつターン開始時に速度補正技が選ばれた場合、行動再選択を行いません。
 * ・また、行動再選択時に速度補正技を選ぶこともありません。
 * ・『戦闘行動の強制』を使用した場合も、対象がランダムだと制御が行われます。
 *
 * ■利用規約
 * 特に制約はありません。
 * 改変、再配布自由、商用可、権利表示も任意です。
 * 作者は責任を負いませんが、不具合については可能な範囲で対応します。
 *
 * @param <Base Setting>
 * @text ＜基本設定＞
 * @desc 見出しです。
 *
 * @param resetAction
 * @text 行動直前の再設定
 * @parent <Base Setting>
 * @type boolean
 * @default false
 * @desc ONならば、行動直前にも再度の行動決定を行います。
 * 複数回行動時も一回毎に行動決定を行います。
 *
 * @param testApply
 * @text 行動判定の実行
 * @parent <Base Setting>
 * @type boolean
 * @default true
 * @desc ONならば効果の得られないスキルは使用しなくなります。
 *
 * @param <Heal>
 * @text ＜回復関連＞
 * @desc 見出しです。
 *
 * @param controlRecover
 * @text 回復系行動の制御
 * @parent <Heal>
 * @type boolean
 * @default true
 * @desc ONならば回復系スキルの制御を行います。
 *
 * @param ifHpRecover
 * @text HP回復行動の条件
 * @parent <Heal>
 * @type text
 * @default b.hpRate() < 1.0
 * @desc 指定した条件でHP回復を行います。
 * 『b.hpRate() < 1.0』で誰かのHPが100%未満になった時です。
 *
 * @param ifMpRecover
 * @text MP回復行動の条件
 * @parent <Heal>
 * @type text
 * @default b.mpRate() < 1.0
 * @desc MP回復系行動を行う条件を設定します。
 * 『b.mpRate() < 1.0』で誰かのMPが100%未満になった時です。
 *
 * @param controlForDead
 * @text 蘇生系行動の制御
 * @parent <Heal>
 * @type boolean
 * @default true
 * @desc ONならば戦闘不能者を対象にしたスキルの制御を行います。
 *
 * @param <MP Drain>
 * @text ＜MP吸収関連＞
 * @desc 見出しです。
 *
 * @param controlMpDrain
 * @text MP吸収の制御
 * @parent <MP Drain>
 * @type boolean
 * @default true
 * @desc ONならばMP吸収／ダメージスキルに関する制御を行います。
 *
 * @param <Effect>
 * @text ＜使用効果関連＞
 * @desc 見出しです。
 *
 * @param controlEnemyEffect
 * @text 対エネミーの使用効果制御
 * @parent <Effect>
 * @type boolean
 * @default true
 * @desc ONならば仲間（エネミー）サイドに対する使用効果系スキルの制御を行います。（支援系ステートなど）
 *
 * @param controlActorEffect
 * @text 対アクターの使用効果制御
 * @parent <Effect>
 * @type boolean
 * @default true
 * @desc ONならば相手（アクター）サイドに対する使用効果系スキルの制御を行います。（異常系ステートなど）
 *
 * @param watchResist
 * @text ステート耐性を見るか
 * @parent <Effect>
 * @type boolean
 * @default false
 * @desc ONならば対象の耐性を確認し、無効なステートは使用しません。
 */
(function () {
  "use strict";

  function setDefault(str, def) {
    return str ? str : def;
  }
  function toBoolean(val, def) {
    // 空白なら初期値を返す
    if (val === "" || val === undefined) {
      return def;
    }
    // 文字列ならboolean型に変換して返す
    return val.toLowerCase() == "true";
  }

  var parameters = PluginManager.parameters("NRP_EnemyRoutineKai");

  var pResetAction = toBoolean(parameters["resetAction"], true);
  var pTestApply = toBoolean(parameters["testApply"], true);

  var pControlRecover = toBoolean(parameters["controlRecover"], true);
  var pIfHpRecover = setDefault(parameters["ifHpRecover"], "b.hpRate() < 1.0");
  var pIfMpRecover = setDefault(parameters["ifMpRecover"], "b.mpRate() < 1.0");
  var pControlMpDrain = toBoolean(parameters["controlMpDrain"], true);
  var pControlForDead = toBoolean(parameters["controlForDead"], true);

  var pControlActorEffect = toBoolean(parameters["controlActorEffect"], true);
  var pControlEnemyEffect = toBoolean(parameters["controlEnemyEffect"], true);
  var pWatchResist = toBoolean(parameters["watchResist"], true);

  /**
   * ●行動再決定を行うか？
   */
  function isResetAction(subject) {
    // メモ欄のmeta情報を確認
    var flg = eval(subject.enemy().meta.RoutineReset);
    // 設定がある場合
    if (flg == true || flg == false) {
      return flg;
    }
    // 未定義の場合
    return pResetAction;
  }

  /**
   * ●行動判定の実行を行うか？
   */
  function isTestApply(subject) {
    var flg = eval(subject.enemy().meta.RoutineTest);
    if (flg == true || flg == false) {
      return flg;
    }
    return pTestApply;
  }

  /**
   * ●回復系行動の制御を行うか？
   */
  function isControlRecover(subject) {
    var flg = eval(subject.enemy().meta.RoutineRecover);
    if (flg == true || flg == false) {
      return flg;
    }
    return pControlRecover;
  }

  /**
   * ●HP回復を行うかを判定する。
   * @param a 行動主体
   * @param b 対象
   */
  function isRoutineHpRecover(a, b) {
    var flg = eval(a.enemy().meta.RoutineIfHp);
    if (flg == true || flg == false) {
      return flg;
    }
    return eval(pIfHpRecover);
  }

  /**
   * ●MP回復を行うかを判定する。
   * @param a 行動主体
   * @param b 対象
   */
  function isRoutineMpRecover(a, b) {
    var flg = eval(a.enemy().meta.RoutineIfMp);
    if (flg == true || flg == false) {
      return flg;
    }
    return eval(pIfMpRecover);
  }

  /**
   * ●MP吸収制御を行うかを判定する。
   */
  function isControlMpDrain(subject) {
    var flg = eval(subject.enemy().meta.RoutineMpDrain);
    if (flg == true || flg == false) {
      return flg;
    }
    return pControlMpDrain;
  }

  /**
   * ●蘇生系行動の制御を行うか？
   */
  function isControlForDead(subject) {
    var flg = eval(subject.enemy().meta.RoutineForDead);
    if (flg == true || flg == false) {
      return flg;
    }
    return pControlForDead;
  }

  /**
   * ●対エネミーの使用効果制御を行うか？
   */
  function isControlEnemyEffect(subject) {
    var flg = eval(subject.enemy().meta.RoutineEnemyEffect);
    if (flg == true || flg == false) {
      return flg;
    }
    return pControlEnemyEffect;
  }

  /**
   * ●対アクターの使用効果制御を行うか？
   */
  function isControlActorEffect(subject) {
    var flg = eval(subject.enemy().meta.RoutineActorEffect);
    if (flg == true || flg == false) {
      return flg;
    }
    return pControlActorEffect;
  }

  /**
   * ●無効なステートを使用しないか？
   */
  function isRoutineWatchResist(subject) {
    var flg = eval(subject.enemy().meta.RoutineWatchResist);
    if (flg == true || flg == false) {
      return flg;
    }
    return pWatchResist;
  }

  /**
   * ●速度補正技の除外処理を行うか？
   */
  function isSpeedException(action) {
    // CTB, CTTB,なら速度補正の意味が異なるので除外しない
    // _cttbCountの有無で判定
    if (
      BattleManager._cttbCount ||
      BattleManager._isCttb ||
      BattleManager._isCtb
    ) {
      return false;

      // MZのTPBも同じく除外
    } else if (Utils.RPGMAKER_NAME == "MZ" && BattleManager.isTpb()) {
      return false;
    }

    // 以下はターン制
    // 速度補正技なので除外する
    if (action.item().speed != 0) {
      return true;
    }
    return false;
  }

  /**
   * ●ターン開始入力
   */
  var _BattleManager_startInput = BattleManager.startInput;
  BattleManager.startInput = function () {
    // 行動確定前にフラグ初期化
    clearFirstActionDone();

    _BattleManager_startInput.apply(this, arguments);
  };

  /**
   * ●ターン開始
   */
  var _BattleManager_startTurn = BattleManager.startTurn;
  BattleManager.startTurn = function () {
    _BattleManager_startTurn.apply(this, arguments);

    // TPBではstartInputを通らないためこちらでクリア
    if (Utils.RPGMAKER_NAME == "MZ" && this.isTpb()) {
      // 行動確定前にフラグ初期化
      clearFirstActionDone();
    }
  };

  /**
   * ●行動確定前にフラグ初期化
   */
  function clearFirstActionDone() {
    for (let enemy of $gameTroop.members()) {
      enemy._firstActionDone = undefined;
    }
  }

  // 行動再設定を判定するフラグ
  var resetActionFlg = false;

  /**
   * ●戦闘行動の処理
   */
  var _BattleManager_processTurn = BattleManager.processTurn;
  BattleManager.processTurn = function () {
    var subject = this._subject;

    // 敵の場合、行動直前の再設定を実行
    if (subject.isEnemy() && isResetAction(subject)) {
      var action = subject.currentAction();
      // 行動前のタイミングでアクション再設定
      if (action) {
        // ただし、速度補正技は除く
        if (!action.item() || !isSpeedException(action)) {
          resetActionFlg = true;
          subject.makeActions();
          resetActionFlg = false;

          // 既に行動再設定を一度実行した。
          subject._firstActionDone = true;
        }
      }
    }

    // 元処理実行
    _BattleManager_processTurn.apply(this);
  };

  /**
   * ●アクション生成
   * ※Game_Enemy.prototype.makeActionTimesをオーバライド
   */
  const _Game_Enemy_makeActions = Game_Enemy.prototype.makeActions;
  Game_Enemy.prototype.makeActions = function () {
    // クリアする前に行動回数を保持しておく。
    // 下のmakeActionTimesに使うだけの使い捨て
    this._keepActionTimes = this._actions.length;

    _Game_Enemy_makeActions.apply(this, arguments);
  };

  /**
   * ●行動回数計算
   * ※Game_Battler.prototype.makeActionTimesをオーバライド
   */
  const _Game_Enemy_makeActionTimes = Game_Enemy.prototype.makeActionTimes;
  Game_Enemy.prototype.makeActionTimes = function () {
    // 既に一度行動済の場合は、行動回数を加算しない。
    if (this._firstActionDone) {
      // 現在の残り行動回数をそのまま取得
      return this._keepActionTimes;
    }
    return _Game_Enemy_makeActionTimes.apply(this, arguments);
  };

  /**
   * ●敵のターン参照
   */
  var _Game_Troop_turnCount = Game_Troop.prototype.turnCount;
  Game_Troop.prototype.turnCount = function () {
    var turnCount = _Game_Troop_turnCount.apply(this);

    // 行動再設定時はターン-1
    // そうしないとターン最初の行動決定と条件が一致しない
    if (resetActionFlg) {
      turnCount -= 1;
    }
    return turnCount;
  };

  /**
   * ●仲間への対象選択
   */
  var _Game_Action_targetsForFriends = Game_Action.prototype.targetsForFriends;
  Game_Action.prototype.targetsForFriends = function () {
    var subject = this.subject();

    /*
     * 以下の条件を全て満たす場合が対象
     * ・行動主体が敵
     * ・死亡者向けではない
     * ・対象が使用者ではない
     * ・単体が対象
     * ・対象がランダム（敵の単体対象は原則ランダム扱い）
     * ・ダメージ・吸収ではない
     */
    if (
      subject.isEnemy() &&
      !this.isForDeadFriend() &&
      !this.isForUser() &&
      this.isForOne() &&
      this._targetIndex < 0 &&
      !this.isDamage() &&
      !this.isDrain()
    ) {
      var targets = [];
      var target;
      var unit = this.friendsUnit();

      // 回復制御を行う場合
      if (isControlRecover(subject)) {
        // HP回復の場合
        if (this.isHpRecover()) {
          // HPが最も減っている対象を選択
          target = unit.aliveMembers().reduce(function (a, b) {
            return a.hpRate() < b.hpRate() ? a : b;
          });
          targets.push(target);
          return targets;

          // MP回復の場合
        } else if (this.isMpRecover()) {
          // MPが最も減っている対象を選択
          target = unit.aliveMembers().reduce(function (a, b) {
            return a.mpRate() < b.mpRate() ? a : b;
          });
          targets.push(target);
          return targets;
        }
      }

      // ダメージタイプがなしで、かつ使用効果がある場合
      if (this.checkDamageType([0]) && this.item().effects.length > 0) {
        // 効果のある対象だけに候補を絞る
        const filterTargets = unit.aliveMembers().filter(function (m) {
          return this.testApplyEnemy(m);
        }, this);

        // 有効な対象が存在する場合
        if (filterTargets.length > 0) {
          // 絞った候補からさらにランダムで選択する。
          targets.push(randomTargetFilter(filterTargets));
          return targets;
        }
      }
    }

    // 条件を満たさない場合は元処理実行
    return _Game_Action_targetsForFriends.apply(this);
  };

  /**
   * ●相手サイドへの対象選択
   */
  var _Game_Action_targetsForOpponents =
    Game_Action.prototype.targetsForOpponents;
  Game_Action.prototype.targetsForOpponents = function () {
    var subject = this.subject();

    /*
     * 以下の条件を全て満たす場合が対象
     * ・行動主体が敵
     * ・単体が対象
     * ・対象がランダム（敵の単体対象は原則ランダム扱い）
     */
    if (subject.isEnemy() && this.isForOne() && this._targetIndex < 0) {
      // 制御対象かどうかの判定
      let controlFlg = false;

      /*
       * ・ダメージタイプがなし
       * ・使用効果が存在する
       */
      if (this.checkDamageType([0]) && this.item().effects.length > 0) {
        controlFlg = true;

        /*
         * ・ダメージタイプがＭＰダメージ／ＭＰ吸収
         */
      } else if (this.isMpEffect() && (this.isDamage() || this.isDrain())) {
        controlFlg = true;
      }

      // 制御対象として処理する。
      if (controlFlg) {
        const targets = [];
        const unit = this.opponentsUnit();

        // 効果のある対象だけに候補を絞る
        const filterTargets = unit.aliveMembers().filter(function (m) {
          return this.testApplyEnemy(m);
        }, this);

        // 有効な対象が存在する場合
        if (filterTargets.length > 0) {
          // 絞った候補からさらにランダムで選択する。
          targets.push(randomTargetFilter(filterTargets));
          return targets;
        }
      }
    }

    // 条件を満たさない場合は元処理実行
    return _Game_Action_targetsForOpponents.apply(this);
  };

  /**
   * 【独自定義】指定した対象集団の狙われ率合計を求める。
   */
  function tgrSumFilter(targets) {
    return targets.reduce(function (r, member) {
      return r + member.tgr;
    }, 0);
  }

  /**
   * 【独自定義】指定した対象集団から狙われ率に従ってランダムで選択する。
   */
  function randomTargetFilter(targets) {
    var tgrRand = Math.random() * tgrSumFilter(targets);
    var target = null;
    targets.forEach(function (member) {
      tgrRand -= member.tgr;
      if (tgrRand <= 0 && !target) {
        target = member;
      }
    });
    return target;
  }

  /**
   * ●敵の行動有効判定
   * ここで無効にした行動は取らない。
   */
  var Game_Enemy_isActionValid = Game_Enemy.prototype.isActionValid;
  Game_Enemy.prototype.isActionValid = function (action) {
    // 元処理実行（通常の使用条件判定）
    var ret = Game_Enemy_isActionValid.apply(this, arguments);
    // 使用不可なら、そこで終了
    if (ret == false) {
      return ret;
    }

    // 行動判定の実行を行う場合のみ
    if (isTestApply(this)) {
      // 引数のactionはJSONのパラメータそのままなので、
      // Game_Actionに変換する。
      var gameAction = new Game_Action(this);
      gameAction.setSkill(action.skillId);

      // 既にターン実行中ならば、速度補正技は使用候補から外す。
      if (isResetAction(this) && BattleManager.isInTurn()) {
        if (gameAction.item() && isSpeedException(gameAction)) {
          return false;
        }
      }

      // 使用者が対象
      if (gameAction.isForUser()) {
        // 効果が無効ならば、使用候補から外す。
        if (!gameAction.testApplyEnemy(this)) {
          return false;
        }

        // それ以外
      } else {
        // 対象がアクター向けかつ全員無効ならば、使用候補から外す。
        if (
          gameAction.isForOpponent() &&
          $gameParty.members().every(function (m) {
            return !gameAction.testApplyEnemy(m);
          })
        ) {
          return false;

          // 対象がエネミー向けかつ全員無効ならば、使用候補から外す。
        } else if (
          gameAction.isForFriend() &&
          $gameTroop.members().every(function (m) {
            return !gameAction.testApplyEnemy(m);
          })
        ) {
          return false;
        }
      }
    }

    // 使用可能と認定
    return true;
  };

  /**
   * 【独自】敵行動の有効判定
   * ここでtrueを返した場合に使用可能な行動となる。
   */
  Game_Action.prototype.testApplyEnemy = function (target) {
    const subject = this.subject();

    // 対象が戦闘不能の仲間向け
    if (this.isForDeadFriend()) {
      // 蘇生制御を行わない場合は常に有効
      if (!isControlForDead(subject)) {
        return true;
      }
      // 対象が戦闘不能なら有効
      if (target.isDead()) {
        return true;
      }
      // それ以外は無効
      return false;

      // それ以外で対象が戦闘不能（逃走も含む）なら無効
    } else if (!target.isAlive()) {
      return false;
    }

    // ダメージまたは吸収
    if (this.isDamage() || this.isDrain()) {
      // ＭＰダメージ／吸収制御を行う場合、
      // ＭＰ効果の場合、対象のＭＰが０なら無効
      if (isControlMpDrain(subject) && this.isMpEffect() && target.mp == 0) {
        return false;
      }

      // それ以外は有効
      return true;

      // 回復系の場合
    } else if (this.isRecover()) {
      // 回復制御を行わない場合は常に有効
      if (!isControlRecover(subject)) {
        return true;
      }

      // 効果がHP回復、かつ対象のHPが減っている。
      if (this.isHpRecover() && isRoutineHpRecover(subject, target)) {
        return true;
        // 効果がMP回復、かつ対象のMPが減っている。
      } else if (this.isMpRecover() && isRoutineMpRecover(subject, target)) {
        return true;
      }

      // ダメージタイプがなし
    } else if (this.checkDamageType([0])) {
      // 対象がアクター、かつ使用効果制御を行わない場合は有効
      if (target.isActor() && !isControlActorEffect(subject)) {
        return true;
        // 対象がエネミー、かつ使用効果制御を行わない場合は有効
      } else if (target.isEnemy() && !isControlEnemyEffect(subject)) {
        return true;
      }

      // 使用効果が空（様子を見るなどの何もしない行動）なら有効
      if (this.item().effects.length == 0) {
        return true;
        // 何らかの使用効果が得られるなら有効
      } else if (this.hasItemAnyValidEffects(target)) {
        return true;
      }
    }

    // それ以外は無効と判定
    return false;
  };

  /**
   * ●使用効果を得られるか？
   */
  var _Game_Action_testItemEffect = Game_Action.prototype.testItemEffect;
  Game_Action.prototype.testItemEffect = function (target, effect) {
    // 敵限定
    if (this.subject().isEnemy()) {
      // ステート追加の場合のみ、無効かどうかを判定
      switch (effect.code) {
        case Game_Action.EFFECT_ADD_STATE:
          // 対象の耐性を参照する。
          if (isRoutineWatchResist(this.subject())) {
            // 対象がステート無効である。
            if (target.isStateResist(effect.dataId)) {
              return false;
              // 行動が必中ではない。かつ、ステート成功率が０
            } else if (
              !this.isCertainHit() &&
              target.stateRate(effect.dataId) == 0
            ) {
              return false;
            }
          }
      }
    }

    // 元処理実行
    return _Game_Action_testItemEffect.apply(this, arguments);
  };

  /**
   * ●エネミーに対する戦闘行動の強制
   */
  const Game_Enemy_forceAction = Game_Enemy.prototype.forceAction;
  Game_Enemy.prototype.forceAction = function (skillId, targetIndex) {
    // 対象が-1（ランダム）の場合、decideRandomTarget()を経由せず、
    // そのまま返すように修正。
    // ここで確定してしまうと対象の補正を行えなくなる。
    if (targetIndex === -1) {
      this.clearActions();
      var action = new Game_Action(this, true);
      action.setSkill(skillId);
      action.setTarget(targetIndex);
      this._actions.push(action);
      return;
    }

    // 元処理実行
    Game_Enemy_forceAction.apply(this, arguments);
  };
})();


code:
//=============================================================================
// NRP_LoopCursor.js
//=============================================================================

/*:
 * @target MZ
 * @plugindesc v1.011 Change the cursor to loop up, down, left and right.
 * @author Takeshi Sunagawa (http://newrpg.seesaa.net/)
 * @url http://newrpg.seesaa.net/article/475238742.html
 *
 * @help Change the behavior so that the cursor loops up,
 * down, left, right and right in each window.
 * This is a soberly useful plugin.
 *
 * For more information, please see below.
 * http://newrpg.seesaa.net/article/475238742.html
 *
 * <Terms>
 * There are no restrictions.
 * Modification, redistribution freedom, commercial availability,
 * and rights indication are also optional.
 * The author is not responsible,
 * but we will respond to defects as far as possible.
 *
 * @param loopLR
 * @type boolean
 * @default true
 * @desc The left and right movement is also looped.
 *
 * @param noStopCursol
 * @type boolean
 * @default false
 * @desc The cursor is not paused at the edge of the window.
 *
 * @param selectOverLastSpace
 * @type boolean
 * @default false
 * @desc Allows the cursor to be placed in the space behind the final element.
 * If it is "false", it stops at the last element.
 */

/*:ja
 * @target MZ
 * @plugindesc v1.011 カーソルが上下左右にループするよう挙動を変更します。
 * @author 砂川赳 (http://newrpg.seesaa.net/)
 * @url http://newrpg.seesaa.net/article/475238742.html
 *
 * @help 各種ウィンドウにて、カーソルが上下左右にループするよう挙動を変更します。
 * 地味に便利なプラグインです。
 *
 * 詳細は以下をご覧ください。
 * http://newrpg.seesaa.net/article/475238742.html
 *
 * ■利用規約
 * 特に制約はありません。
 * 改変、再配布自由、商用可、権利表示も任意です。
 * 作者は責任を負いませんが、不具合については可能な範囲で対応します。
 *
 * @param loopLR
 * @text 左右もループする
 * @type boolean
 * @default true
 * @desc 左右の移動もループさせます。
 *
 * @param noStopCursol
 * @text カーソルを一旦停止しない
 * @type boolean
 * @default false
 * @desc ウィンドウ端でカーソルが一旦停止しないようにします。
 *
 * @param selectOverLastSpace
 * @text 末尾の空欄を有効に
 * @type boolean
 * @default false
 * @desc 最終要素よりも後ろの空欄にカーソルを合わせられるようにします。
 * falseの場合は最終要素で止まります。
 */

(function () {
  "use strict";

  function toBoolean(str) {
    if (str == true) {
      return true;
    }
    return str == "true" ? true : false;
  }

  var parameters = PluginManager.parameters("NRP_LoopCursor");
  var pLoopLR = toBoolean(parameters["loopLR"], true);
  var pNoStopCursol = toBoolean(parameters["noStopCursol"], false);
  var pSelectOverLastSpace = toBoolean(
    parameters["selectOverLastSpace"],
    false
  );

  /**
   * 【独自】選択領域の合計
   */
  Window_Selectable.prototype.maxCells = function () {
    return this.maxCols() * this.maxRows();
  };

  /**
   * 【上書】カーソル下移動
   */
  Window_Selectable.prototype.cursorDown = function (wrap) {
    wrap = isWrap(wrap);

    var index = this.index();
    var maxCols = this.maxCols();
    var maxCells = this.maxCells();

    if (index < maxCells - maxCols || wrap) {
      // MZならsmoothSelect、MVはselect
      if (Utils.RPGMAKER_NAME == "MZ") {
        this.smoothSelect((index + maxCols) % maxCells);
      } else {
        this.select((index + maxCols) % maxCells);
      }
    }
  };

  /**
   * 【上書】カーソル上移動
   */
  Window_Selectable.prototype.cursorUp = function (wrap) {
    wrap = isWrap(wrap);

    var index = this.index();
    var maxCols = this.maxCols();
    var maxCells = this.maxCells();

    if (index >= maxCols || wrap) {
      // MZならsmoothSelect、MVはselect
      if (Utils.RPGMAKER_NAME == "MZ") {
        this.smoothSelect((index - maxCols + maxCells) % maxCells);
      } else {
        this.select((index - maxCols + maxCells) % maxCells);
      }
    }
  };

  if (pLoopLR) {
    /**
     * 【上書】カーソル右移動
     */
    Window_Selectable.prototype.cursorRight = function (wrap) {
      wrap = isWrap(wrap);

      var index = this.index();
      var maxCols = this.maxCols();
      var maxCells = this.maxCells();

      if (maxCols >= 2 && (index < maxCells || (wrap && this.isHorizontal()))) {
        if (index % maxCols < maxCols - 1) {
          index++;
        } else if (wrap) {
          index -= maxCols - 1;
        }
        this.select(index % maxCells);
      }
    };

    /**
     * 【上書】カーソル左移動
     */
    Window_Selectable.prototype.cursorLeft = function (wrap) {
      wrap = isWrap(wrap);

      var index = this.index();
      var maxCols = this.maxCols();
      var maxCells = this.maxCells();

      if (maxCols >= 2 || (wrap && this.isHorizontal())) {
        if (index % maxCols > 0) {
          index--;
        } else if (wrap) {
          index += maxCols - 1;
        }
        this.select(index % maxCells);
      }
    };
  }

  /**
   * ●カーソル移動時
   */
  var _Window_Selectable_select = Window_Selectable.prototype.select;
  Window_Selectable.prototype.select = function (index) {
    if (!pSelectOverLastSpace) {
      // 最終要素を超えた場合は、最終要素へカーソルを移動
      if (index + 1 > this.maxItems()) {
        index = this.maxItems() - 1;
        // ただしマイナスにはならない
        index = Math.max(index, 0);
      }
    }

    _Window_Selectable_select.call(this, index);
  };

  /**
   * ●選択中の要素の有効判定
   */
  var _Window_Selectable_isCurrentItemEnabled =
    Window_Selectable.prototype.isCurrentItemEnabled;
  Window_Selectable.prototype.isCurrentItemEnabled = function () {
    // 末尾の空白選択可かつ要素が存在する場合
    // 要素が0の場合は、数値入力などが想定されるため処理しない
    if (pSelectOverLastSpace && this.maxItems() > 0) {
      //  要素数を超えている場合は空欄なので不可
      if (this.index() + 1 > this.maxItems()) {
        return false;
      }
    }

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

  /**
   * ●【MZ用】不具合対応
   */
  var _Window_Command_maxItems = Window_Command.prototype.maxItems;
  Window_Command.prototype.maxItems = function () {
    // listが取得できなければ0を返す。
    if (!this._list) {
      return 0;
    }

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

  /**
   * ●端で止まるかどうか？
   */
  function isWrap(wrap) {
    if (pNoStopCursol) {
      return true;
    }
    return wrap;
  }
})();


code:
//=============================================================================
// NRP_VehiclePassage.js
//=============================================================================
/*:
 * @target MV MZ
 * @plugindesc v1.02 Extends the passage & get on/off decision for vehicles.
 * @author Takeshi Sunagawa (http://newrpg.seesaa.net/)
 * @url http://newrpg.seesaa.net/article/482385836.html
 *
 * @help Extends the passage & get on/off decision for vehicles.
 *
 * You can also fine-tune the settings by using TerrainTags,
 * RegionIDs, TileIDs and AutoTileTypes as conditions.
 *
 * If you add a "SettingId" to the settings you have created,
 * you can call up the settings for each tileset.
 *
 * Depending on the plugin parameters,
 * getting on and off can be prohibited in principle.
 * This is useful if you want to be able to get on and off only in certain terrains.
 * For example, a ship that can only arrive and depart at a pier,
 * or a black bird that can only land in a forest, can be realized.
 *
 * In addition, the altitude of the airship can be set.
 * This can be used to create low-flying vehicles
 * that cannot pass through mountainous areas.
 *
 * [Usage]
 * Set the passage & get on/off decision
 * in each SettingList of plugin parameters.
 * It is possible to switch between enabling it for all tilesets
 * and calling it for each tileset by using the parameters in the list.
 *
 * [Tilesets Notes]
 * If you want to set it for each tile set,
 * specify the following in the Note field.
 * <VehiclePassage:?>
 *
 * ? is the SettingId registered in the SettingList.
 * It is also possible to specify multiple settings by separating them with commas.
 * <VehiclePassage:A,B,C>
 *
 * [Terms]
 * There are no restrictions.
 * Modification, redistribution freedom, commercial availability,
 * and rights indication are also optional.
 * The author is not responsible,
 * but will deal with defects to the extent possible.
 *
 * @param BoatSettingList
 * @type struct<Setting>[]
 * @desc This is a list of Boat's passage & get on/off settings.
 *
 * @param ShipSettingList
 * @type struct<Setting>[]
 * @desc This is a list of Ship's passage & get on/off settings.
 *
 * @param AirshipSettingList
 * @type struct<Setting>[]
 * @desc This is a list of Airship's passage & get on/off settings.
 *
 * @param BoatNoGetOff
 * @type boolean
 * @default false
 * @desc In principle, getting on and off the Boat is prohibited.
 * Please define exceptions in the SettingList.
 *
 * @param ShipNoGetOff
 * @type boolean
 * @default false
 * @desc In principle, getting on and off the Ship is prohibited.
 * Please define exceptions in the SettingList.
 *
 * @param AirshipNoGetOff
 * @type boolean
 * @default false
 * In principle, getting on and off the Airship is prohibited.
 * Please define exceptions in the SettingList.
 *
 * @param AirshipAltitude
 * @type number
 * @min 0 @max 999
 * @desc The altitude of the airship.
 * Default is 48.
 */
/*~struct~Setting:
 * @param SettingId
 * @type string
 * @desc Identifier used to call from the note field of a tileset. This is not required if "ValidAllTilesets" is on.
 *
 * @param Memo
 * @type string
 * @desc This is a memo for identification.
 * Please give it an easy-to-understand name.
 *
 * @param ValidAllTilesets
 * @type boolean
 * @default false
 * @desc Enables the setting for all tilesets. If turned off, the setting can be applied to individual tilesets.
 *
 * @param <Condition>
 *
 * @param TerrainTag
 * @parent <Condition>
 * @type string
 * @desc Specify the Terrain Tag (1~7) to be targeted.
 * Multiple tags can be specified. (Example: 1,3~5)
 *
 * @param RegionId
 * @parent <Condition>
 * @type string
 * @desc Specify the Region Id (1~255) to be targeted.
 * Multiple ids can be specified. (Example: 1,3~5)
 *
 * @param AutotileType
 * @parent <Condition>
 * @type string
 * @desc Specify the Autotile Type to be targeted.
 * Multiple types can be specified. (Example: 1,3~5)
 *
 * @param TileId
 * @parent <Condition>
 * @type string
 * @desc Specify the Tile Id to be targeted.
 * Multiple ids can be specified. (Example: 1,3~5)
 *
 * @param <PassageSetting>
 *
 * @param CanPass
 * @parent <PassageSetting>
 * @type boolean
 * @desc Is it passable?
 *
 * @param CanGetOn
 * @parent <PassageSetting>
 * @type boolean
 * @desc Is it possible to get in and out?
 */

/*:ja
 * @target MV MZ
 * @plugindesc v1.02 乗物の通行＆乗降判定を拡張します。
 * @author 砂川赳（http://newrpg.seesaa.net/）
 * @url http://newrpg.seesaa.net/article/482385836.html
 *
 * @help 乗物の通行＆乗降判定を拡張します。
 *
 * 地形タグやリージョンＩＤ、タイルＩＤやオートタイルタイプを
 * 条件にして細かく設定できます。
 * 作成した設定に『設定ＩＤ』を付加すれば、
 * タイルセット毎に設定を呼び出すことも可能です。
 *
 * プラグインパラメータによって、乗降を原則禁止にすることもできます。
 * 桟橋でのみ発着できる船や、森のみ着陸できる黒い馬鳥など、
 * 特定の地形でのみ乗降できるようにしたい場合は便利です。
 *
 * ついでに飛行船の高度も設定可能です。
 * 山岳地帯を通れない低空飛行の乗物などが実現できます。
 *
 * ■使用方法
 * プラグインパラメータの各設定リストに、通行＆乗降判定を設定してください。
 * 全タイルセットで有効とするか、タイルセット毎に呼び出すかを、
 * リスト内のパラメータによって切り替え可能です。
 *
 * ■タイルセットのメモ欄
 * タイルセット毎に設定する場合はメモ欄に以下を指定してください。
 * <VehiclePassage:?>
 *
 * ?の部分が設定リストに登録した『設定ＩＤ』となります。
 * また、カンマ区切りによって複数指定も可能です。
 * <VehiclePassage:A,B,C>
 *
 * ■利用規約
 * 特に制約はありません。
 * 改変、再配布自由、商用可、権利表示も任意です。
 * 作者は責任を負いませんが、不具合については可能な範囲で対応します。
 *
 * @param BoatSettingList
 * @text 小型船の設定リスト
 * @type struct<Setting>[]
 * @desc 小型船の通行＆乗降設定の一覧です。
 *
 * @param ShipSettingList
 * @text 大型船の設定リスト
 * @type struct<Setting>[]
 * @desc 大型船の通行＆乗降設定の一覧です。
 *
 * @param AirshipSettingList
 * @text 飛行船の設定リスト
 * @type struct<Setting>[]
 * @desc 飛行船の通行＆乗降設定の一覧です。
 *
 * @param BoatNoGetOff
 * @text 小型船の原則乗降禁止
 * @type boolean
 * @default false
 * @desc 小型船の乗降を原則禁止します。
 * 設定リストに定義した地形のみ乗降できます。
 *
 * @param ShipNoGetOff
 * @text 大型船の原則乗降禁止
 * @type boolean
 * @default false
 * @desc 大型船の乗降を原則禁止します。
 * 設定リストに定義した地形のみ乗降できます。
 *
 * @param AirshipNoGetOff
 * @text 飛行船の原則乗降禁止
 * @type boolean
 * @default false
 * @desc 飛行船の乗降を原則禁止します。
 * 設定リストに定義した地形のみ乗降できます。
 *
 * @param AirshipAltitude
 * @text 飛行船の高度
 * @type number
 * @min 0 @max 999
 * @desc 飛行船の高度です。
 * デフォルトは48となります。
 *
 * @param AirshipSameAsCharacters
 * @text 飛行船を通常キャラと同じに
 * @type boolean
 * @default false
 * @desc 飛行船のプライオリティタイプを『通常キャラと同じ』に変更します。
 */
/*~struct~Setting:ja
 * @param SettingId
 * @text 設定ＩＤ
 * @type string
 * @desc タイルセットのメモ欄からの呼び出しに使う識別子です。
 * 『全タイルセットで有効』がオンの場合は不要です。
 *
 * @param Memo
 * @text メモ
 * @type string
 * @desc 判別用のメモです。
 * 分かりやすい名前を付けてください。
 *
 * @param ValidAllTilesets
 * @text 全タイルセットで有効
 * @type boolean
 * @default false
 * @desc 設定を全てのタイルセットで有効にします。
 * オフにした場合は、個別のタイルセットに設定できます。
 *
 * @param <Condition>
 * @text ＜条件＞
 *
 * @param TerrainTag
 * @text 地形タグ
 * @parent <Condition>
 * @type string
 * @desc 対象とする地形タグ（1~7）を指定します。
 * 複数指定も可能です。（例：1,3~5）
 *
 * @param RegionId
 * @text リージョンＩＤ
 * @parent <Condition>
 * @type string
 * @desc 対象とするリージョン（1~255）を指定します。
 * 複数指定も可能です。（例：1,3~5）
 *
 * @param AutotileType
 * @text オートタイルタイプ
 * @parent <Condition>
 * @type string
 * @desc 対象とするオートタイルタイプを指定します。
 * 複数指定も可能です。（例：1,3~5）
 *
 * @param TileId
 * @text タイルＩＤ
 * @parent <Condition>
 * @type string
 * @desc 対象とするタイルＩＤを指定します。
 * 複数指定も可能です。（例：1,3~5）
 *
 * @param <PassageSetting>
 * @text ＜通行設定＞
 *
 * @param CanPass
 * @text 通行可能
 * @parent <PassageSetting>
 * @type boolean
 * @desc 通行可能かどうか？
 *
 * @param CanGetOn
 * @text 乗降可能
 * @parent <PassageSetting>
 * @type boolean
 * @desc 乗降可能かどうか？
 */
(function () {
  "use strict";

  function toBoolean(val, def) {
    // 空白なら初期値を返す
    if (val === "" || val === undefined) {
      return def;

      // 既にboolean型なら、そのまま返す
    } else if (typeof val === "boolean") {
      return val;
    }
    // 文字列ならboolean型に変換して返す
    return val.toLowerCase() == "true";
  }
  function toNumber(str, def) {
    if (str == undefined || str == "") {
      return def;
    }
    return isNaN(str) ? def : +(str || def);
  }

  /**
   * ●構造体（二重配列）をJSで扱えるように変換
   */
  function parseStruct2(arg) {
    var ret = [];

    if (arg) {
      JSON.parse(arg).forEach(function (str) {
        ret.push(JSON.parse(str));
      });
    }

    return ret;
  }

  /**
   * ●引数を元に配列を取得する。
   */
  function makeArray(values) {
    const results = [];
    if (!values) {
      return undefined;
    }

    // カンマ区切りでループ
    for (let value of values.split(",")) {
      // 空白除去
      value = value.trim();
      // 1~5というように範囲指定の場合
      // ※~が存在する。
      if (value.indexOf("~") >= 0) {
        const range = value.split("~");
        const rangeStart = eval(range[0]);
        const rangeEnd = eval(range[1]);

        // 指定範囲で実行
        // 開始のほうが終了より大きい場合は反対に実行
        if (rangeEnd < rangeStart) {
          for (let i = rangeStart; i >= rangeEnd; i--) {
            results.push(eval(i));
          }
        } else {
          for (let i = rangeStart; i <= rangeEnd; i++) {
            results.push(eval(i));
          }
        }

        // 通常時
      } else {
        // 数値変換するためeval
        results.push(eval(value));
      }
    }
    return results;
  }

  const PLUGIN_NAME = "NRP_VehiclePassage";
  const parameters = PluginManager.parameters(PLUGIN_NAME);
  const pBoatSettingList = parseStruct2(parameters["BoatSettingList"]);
  const pShipSettingList = parseStruct2(parameters["ShipSettingList"]);
  const pAirshipSettingList = parseStruct2(parameters["AirshipSettingList"]);
  const pBoatNoGetOff = toBoolean(parameters["BoatNoGetOff"], false);
  const pShipNoGetOff = toBoolean(parameters["ShipNoGetOff"], false);
  const pAirshipNoGetOff = toBoolean(parameters["AirshipNoGetOff"], false);
  const pAirshipAltitude = toNumber(parameters["AirshipAltitude"]);
  const pAirshipSameAsCharacters = toBoolean(
    parameters["AirshipSameAsCharacters"],
    false
  );

  /**
   * ●通行リストを使用できる形式に設定
   */
  function setSettingList(settingList, type) {
    for (const setting of settingList) {
      setting.settingId = setting.SettingId;
      setting.vehicleType = type;
      setting.validAllTilesets = toBoolean(setting.ValidAllTilesets, false);
      setting.terrainTags = makeArray(setting.TerrainTag);
      setting.regionIds = makeArray(setting.RegionId);
      setting.autotileTypes = makeArray(setting.AutotileType);
      setting.tileIds = makeArray(setting.TileId);
      setting.canPass = toBoolean(setting.CanPass);
      setting.canGetOn = toBoolean(setting.CanGetOn);
    }
  }

  setSettingList(pBoatSettingList, "boat");
  setSettingList(pShipSettingList, "ship");
  setSettingList(pAirshipSettingList, "airship");

  // リストを全連結
  const pSettingList = pBoatSettingList
    .concat(pShipSettingList)
    .concat(pAirshipSettingList);

  //----------------------------------------
  // 通行判定
  //----------------------------------------

  /**
   * ●小型船の通行判定
   */
  const _Game_Map_isBoatPassable = Game_Map.prototype.isBoatPassable;
  Game_Map.prototype.isBoatPassable = function (x, y) {
    // 条件設定が一つでも存在する場合
    if (existSetting()) {
      const setting = getMatchSetting(x, y);
      // 条件設定が取得できた場合、通行判定を適用
      if (setting && setting.canPass !== undefined) {
        return setting.canPass;
      }
    }

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

  /**
   * ●大型船の通行判定
   */
  const _Game_Map_isShipPassable = Game_Map.prototype.isShipPassable;
  Game_Map.prototype.isShipPassable = function (x, y) {
    // 条件設定が一つでも存在する場合
    if (existSetting()) {
      const setting = getMatchSetting(x, y);
      // 条件設定が取得できた場合、通行判定を適用
      if (setting && setting.canPass !== undefined) {
        return setting.canPass;
      }
    }

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

  /**
   * ●通行判定
   * ※Game_CharacterBase.prototype.canPassのオーバーライド
   * ※飛行船はすり抜け属性をデフォルトで持っているため、
   * 　Game_Vehicle.prototype.isMapPassableを通過しない。
   * 　そのためここで実装する。
   */
  const _Game_Player_canPass = Game_Player.prototype.canPass;
  Game_Player.prototype.canPass = function (x, y, d) {
    // プレイヤーが飛行船に乗っている場合
    if (this.isInAirship()) {
      const x2 = $gameMap.roundXWithDirection(x, d);
      const y2 = $gameMap.roundYWithDirection(y, d);
      return isAirShipPassable(x2, y2);
    }

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

  /**
   * ●飛行船の通行判定
   */
  function isAirShipPassable(x, y) {
    // 条件設定が一つでも存在する場合
    if (existSetting()) {
      const setting = getMatchSetting(x, y);
      // 条件設定が取得できた場合、通行判定を適用
      if (setting && setting.canPass !== undefined) {
        return setting.canPass;
      }
    }

    return true;
  }

  //----------------------------------------
  // 乗降判定
  //----------------------------------------

  const _Game_Vehicle_isLandOk = Game_Vehicle.prototype.isLandOk;
  Game_Vehicle.prototype.isLandOk = function (x, y, d) {
    // 通過判定、イベントとの衝突判定などをここで参照
    const ret = _Game_Vehicle_isLandOk.apply(this, arguments);

    // 飛行船ではない
    if (ret && !this.isAirship()) {
      // 条件設定が一つでも存在する場合
      if (existSetting()) {
        const x2 = $gameMap.roundXWithDirection(x, d);
        const y2 = $gameMap.roundYWithDirection(y, d);

        const setting = getMatchSetting(x2, y2);
        // 条件設定が取得できた場合、乗降判定を適用
        if (setting && setting.canGetOn !== undefined) {
          return setting.canGetOn;
        }
      }

      // 原則禁止の場合はfalse固定
      if (this.isBoat() && pBoatNoGetOff) {
        return false;
      } else if (this.isShip() && pShipNoGetOff) {
        return false;
      }
    }

    return ret;
  };

  /**
   * ●飛行船の乗降判定
   */
  const _Game_Map_isAirshipLandOk = Game_Map.prototype.isAirshipLandOk;
  Game_Map.prototype.isAirshipLandOk = function (x, y) {
    // 条件設定が一つでも存在する場合
    if (existSetting()) {
      const setting = getMatchSetting(x, y);
      // 条件設定が取得できた場合、乗降判定を適用
      if (setting && setting.canGetOn !== undefined) {
        return setting.canGetOn;
      }
    }

    // 原則禁止の場合はfalse固定
    if (pAirshipNoGetOff) {
      return false;
    }

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

  //----------------------------------------
  // マップ切替時
  //----------------------------------------

  /**
   * ●マップ情報の設定
   */
  const _Game_Map_setup = Game_Map.prototype.setup;
  Game_Map.prototype.setup = function (mapId) {
    _Game_Map_setup.apply(this, arguments);

    setTilesetInfo();
  };

  /**
   * ●タイルセットの変更
   */
  const _Game_Map_changeTileset = Game_Map.prototype.changeTileset;
  Game_Map.prototype.changeTileset = function (tilesetId) {
    _Game_Map_changeTileset.apply(this, arguments);

    setTilesetInfo();
  };

  /**
   * ●タイルセットに情報を設定
   * ※常駐処理は避けて、極力処理はこちらでやっておく。
   */
  function setTilesetInfo() {
    const tileset = $gameMap.tileset();
    if (!tileset) {
      return;
    }

    // 設定リスト
    const vehiclePassageSettingList = [];

    // 条件に一致する設定を抽出する。
    for (const setting of pSettingList) {
      // どのタイルセットでも常に有効の場合
      if (setting.validAllTilesets) {
        // 追加して次へ
        vehiclePassageSettingList.push(setting);
        continue;
      }

      // 有効な設定ＩＤかどうかを確認
      if (isValidSetting(setting.settingId, tileset)) {
        // 追加して次へ
        vehiclePassageSettingList.push(setting);
      }
    }

    // タイルセットに設定
    tileset.vehiclePassageSettingList = vehiclePassageSettingList;
  }

  /**
   * ●通行設定が指定のタイルセットで有効か？
   */
  function isValidSetting(settingId, tileset) {
    if (settingId === undefined) {
      return false;
    }

    const setting = tileset.meta.VehiclePassage;
    if (setting) {
      // カンマ区切りで分解
      const settingArray = setting.split(",");
      // 含まれていればtrue
      return settingArray.includes(settingId);
    }
    return false;
  }

  //----------------------------------------
  // 飛行船の最大高度
  //----------------------------------------

  if (pAirshipAltitude !== undefined) {
    /**
     * 【上書】飛行船の最大高度
     */
    Game_Vehicle.prototype.maxAltitude = function () {
      return pAirshipAltitude;
    };

    /**
     * ●飛行船の更新
     */
    const _Game_Vehicle_updateAirship = Game_Vehicle.prototype.updateAirship;
    Game_Vehicle.prototype.updateAirship = function () {
      _Game_Vehicle_updateAirship.apply(this, arguments);

      // 最大高度が0以下の場合、プライオリティタイプを下げないように調整
      if (this.maxAltitude() <= 0) {
        if (this._driving) {
          this.setPriorityType(2);
          this.setStepAnime(true);
        } else {
          this.setPriorityType(0);
          this.setStepAnime(false);
        }
      }
    };
  }

  //----------------------------------------
  // 飛行船のプライオリティタイプ
  //----------------------------------------

  if (pAirshipSameAsCharacters) {
    const _Game_Vehicle_refresh = Game_Vehicle.prototype.refresh;
    Game_Vehicle.prototype.refresh = function () {
      _Game_Vehicle_refresh.apply(this, arguments);

      if (this.isAirship() && !this._driving) {
        // 飛行中でない場合は通常キャラと同じに
        this.setPriorityType(1);
      }
    };

    const _Game_Vehicle_updateAirship = Game_Vehicle.prototype.updateAirship;
    Game_Vehicle.prototype.updateAirship = function () {
      _Game_Vehicle_updateAirship.apply(this, arguments);

      if (this.isLowest()) {
        // 飛行中でない場合は通常キャラと同じに
        this.setPriorityType(1);
      }
    };
  }

  //----------------------------------------
  // ゲームロード時
  //----------------------------------------

  // ロード時判定フラグ
  let mIsGameLoad = false;

  /**
   * ●ロード時のデータ展開
   */
  const _DataManager_extractSaveContents = DataManager.extractSaveContents;
  DataManager.extractSaveContents = function (contents) {
    _DataManager_extractSaveContents.apply(this, arguments);
    // ロード時判定フラグ
    mIsGameLoad = true;
  };

  /**
   * ●マップデータのロード完了時に通行情報を反映
   */
  const _DataManager_onLoad = DataManager.onLoad;
  DataManager.onLoad = function (object) {
    _DataManager_onLoad.apply(this, arguments);

    // ゲームロード時
    if (object === $dataMap && $gameMap && mIsGameLoad) {
      // 通行情報を反映
      setTilesetInfo();
      // 用済みなのでフラグ初期化
      mIsGameLoad = false;
    }
  };

  //----------------------------------------
  // その他共通関数
  //----------------------------------------

  /**
   * ●現在位置に一致する通行設定を取得する。
   */
  function getMatchSetting(x, y) {
    const tileset = $gameMap.tileset();

    // 設定がなければundefined
    if (!tileset.vehiclePassageSettingList) {
      return undefined;
    }

    // なるべく負荷をかけないよう値を一度だけ取得する。
    let terrainTag;
    let regionId;
    let autotileType;
    let tileId;

    // 設定を１つずつ確認
    for (const setting of tileset.vehiclePassageSettingList) {
      let noMatch = false;

      // 該当の乗物以外は無視
      if ($gamePlayer.vehicle()._type != setting.vehicleType) {
        continue;
      }

      // 地形タグの一致を確認
      if (setting.terrainTags && setting.terrainTags.length > 0) {
        // 未取得なら地形タグを取得
        if (terrainTag === undefined) {
          terrainTag = $gameMap.terrainTag(x, y);
        }

        noMatch = setting.terrainTags.every(function (tag) {
          return terrainTag != tag;
        });
        if (noMatch) {
          continue;
        }
      }

      // リージョンの一致を確認
      if (setting.regionIds && setting.regionIds.length > 0) {
        // 未取得ならリージョンＩＤを取得
        if (regionId === undefined) {
          regionId = $gameMap.regionId(x, y);
        }

        noMatch = setting.regionIds.every(function (id) {
          return regionId != id;
        });
        if (noMatch) {
          continue;
        }
      }

      // オートタイルタイプの一致を確認
      if (setting.autotileTypes && setting.autotileTypes.length > 0) {
        // 未取得ならオートタイルタイプを取得
        if (autotileType === undefined) {
          const layerNo = getStepLayer(x, y);
          autotileType = $gameMap.autotileType(x, y, layerNo);
        }

        noMatch = setting.autotileTypes.every(function (type) {
          return autotileType != type;
        });
        if (noMatch) {
          continue;
        }
      }

      // タイルＩＤの一致を確認
      if (setting.tileIds && setting.tileIds.length > 0) {
        // 未取得ならタイルＩＤを取得
        if (tileId === undefined) {
          const layerNo = getStepLayer(x, y);
          tileId = $gameMap.tileId(x, y, layerNo);
        }

        noMatch = setting.tileIds.every(function (id) {
          return tileId != id;
        });
        if (noMatch) {
          continue;
        }
      }

      // 条件を満たす設定があった。
      return setting;
    }

    // なければundefined
    return undefined;
  }

  /**
   * ●☆を除いた有効な最大レイヤー番号
   * ※要するに足を踏みしめるレイヤー
   */
  function getStepLayer(x, y) {
    // 各タイルが保有するフラグ情報
    const flags = $gameMap.tilesetFlags();
    // 上のレイヤーから順番にループ
    const tiles = $gameMap.layeredTiles(x, y);
    for (let i = 0; i < 4; i++) {
      const tileId = tiles[i];
      const layerNo = 3 - i;
      const flag = flags[tileId];

      // 通行判定☆（0x10）を除く最初のタイルが対象
      if ((flag & 0x10) === 0) {
        return layerNo;
      }
    }
    // 無効の場合は-1
    return -1;
  }

  /**
   * ●条件設定が存在するかどうか？
   */
  function existSetting() {
    return pSettingList.length > 0;
  }
})();