// DarkPlasma_MenuLayout 1.3.4
// Copyright (c) 2020 DarkPlasma

/**
 * 2020/10/26 1.3.4 キャラクター選択時にLRキーを無効にする
 * 2020/10/14 1.3.3 パーティ4人以下の場合にキャラ画像が正常に表示されない不具合を修正
 *                  左右キーによるスクロールの挙動が一部不自然だったのを修正
 * 2020/10/13 1.3.2 一瞬末尾のキャラクターの立ち絵が表示される不具合を修正
 * 2020/10/12 1.3.1 アクター選択で右キーを押したときのスクロールの挙動が一部不自然だったのを修正
 *                  アクターリストのスクロール時にステータスが追従しない不具合を修正
 *                  ステートアイコンが正しく表示されない不具合を修正
 * 2020/09/19 1.3.0 現在位置の表示位置を横方向に調整する設定追加
 *                  5人以上のパーティの時に下矢印が表示される不具合を修正
 * 2020/09/08 1.2.1 rollup構成へ移行
 * 2020/08/30 1.2.0 画面外のキャラを非表示にする設定項目追加
 * 2020/08/26 1.1.1 プレイ時間の幅を調整
 *            1.1.0 プレイ時間表示位置の設定項目追加
 *            1.0.1 アクター選択をキャンセルするとカーソル位置が見えなくなる不具合を修正
 *                  アクター選択時の上下キー禁止
 */

/*:ja
 * @plugindesc メニューレイアウトを整える
 * @author DarkPlasma
 * @license No License
 *
 * @target MZ
 *
 * @param menuCommands
 * @text メニューコマンド
 * @type struct<MenuCommand>[]
 * @default ["{\"name\":\"アイテム\",\"icon\":\"img/menu/com_item\",\"symbol\":\"item\",\"action\":\"0\",\"scene\":\"\"}","{\"name\":\"スキル\",\"icon\":\"img/menu/com_skill\",\"symbol\":\"skill\",\"action\":\"1\",\"scene\":\"\"}","{\"name\":\"装備\",\"icon\":\"img/menu/com_equip\",\"symbol\":\"equip\",\"action\":\"2\",\"scene\":\"\"}","{\"name\":\"ステータス\",\"icon\":\"img/menu/com_status\",\"symbol\":\"status\",\"action\":\"3\",\"scene\":\"\"}","{\"name\":\"並び替え\",\"icon\":\"img/menu/com_formation\",\"symbol\":\"formation\",\"action\":\"4\",\"scene\":\"\"}","{\"name\":\"オプション\",\"icon\":\"img/menu/com_options\",\"symbol\":\"options\",\"action\":\"5\",\"scene\":\"\"}","{\"name\":\"賞金首\",\"icon\":\"img/menu/com_bountyList\",\"symbol\":\"bountyList\",\"action\":\"8\",\"scene\":\"Scene_BountyList\"}","{\"name\":\"魔物図鑑\",\"icon\":\"img/menu/com_enemyBook\",\"symbol\":\"enemyBook\",\"action\":\"8\",\"scene\":\"Scene_EnemyBook\"}","{\"name\":\"セーブ\",\"icon\":\"img/menu/com_save\",\"symbol\":\"save\",\"action\":\"6\",\"scene\":\"\"}","{\"name\":\"ゲーム終了\",\"icon\":\"img/menu/com_quit\",\"symbol\":\"gameEnd\",\"action\":\"7\",\"scene\":\"\"}"]
 *
 * @param commandIconWidth
 * @text コマンドアイコン幅
 * @type number
 * @default 48
 *
 * @param commandIconHeight
 * @text コマンドアイコン高さ
 * @type number
 * @default 48
 *
 * @param frameLayoutImage
 * @text レイアウト画像
 * @type file
 *
 * @param frameStatusImage
 * @text ステータスフレーム画像
 * @type file
 *
 * @param backgroundImage
 * @text 背景画像
 * @type file
 *
 * @param drawLevel
 * @text レベル描画設定
 * @type struct<DrawNumber>
 * @default {"maxDigits":"3", "numberFile":"img/menu/LVNumber", "baseX":"32", "y":"380"}
 *
 * @param drawHp
 * @text HP描画設定
 * @type struct<DrawNumber>
 * @default {"maxDigits":"4", "numberFile":"img/menu/HPNumber", "baseX":"48", "y":"415"}
 *
 * @param drawMaxHp
 * @text 最大HP描画設定
 * @type struct<DrawNumberMax>
 * @default {"baseX":"80", "y":"447"}
 *
 * @param drawHpMeter
 * @text HPメーター描画設定
 * @type struct<DrawMeter>
 * @default {"meterFile":"img/menu/HPMeter", "baseX":"44", "y":"430"}
 *
 * @param drawMp
 * @text MP描画設定
 * @type struct<DrawNumber>
 * @default {"maxDigits":"3", "numberFile":"img/menu/MPNumber", "baseX":"48", "y":"465"}
 *
 * @param drawMaxMp
 * @text 最大MP描画設定
 * @type struct<DrawNumberMax>
 * @default {"baseX":"80", "y":"500"}
 *
 * @param drawMpMeter
 * @text MPメーター描画設定
 * @type struct<DrawMeter>
 * @default {"meterFile":"img/menu/MPMeter", "baseX":"44", "y":"480"}
 *
 * @param drawIcon
 * @text ステート/バフアイコン描画設定
 * @type struct<DrawIcon>
 * @default {"baseX":"41", "y":"367", "iconChangeFrame":"60"}
 *
 * @param playtimePosition
 * @text プレイ時間位置
 * @type struct<WindowRect>
 * @default {"x":"625", "y":"42", "width":"150", "height":"64"}
 *
 * @param hideOuterCharacter
 * @text 画面外のキャラ非表示
 * @type boolean
 * @default true
 *
 * @param locationOffsetX
 * @desc 現在位置表示位置を横方向にずらします。増やすと右へ移動します
 * @text 現在位置オフセットX
 * @type number
 * @default 0
 *
 * @noteParam stand_picture
 * @noteDir img/pictures/
 * @noteType file
 * @noteData actor
 *
 * @help
 * メニューレイアウトを整えます。
 *
 * 必要な画像がないと動作しません。
 * 以下、必要な画像リスト
 * - 背景画像
 * - フレームレイアウト画像
 * - ステータスフレーム画像
 * - HPメーター画像
 * - MPメーター画像
 * - LV, HP, MP数値描画用画像
 */
/*~struct~MenuCommand:
 * @param name
 * @text コマンド名
 * @type string
 *
 * @param icon
 * @text アイコン
 * @type file
 *
 * @param symbol
 * @text シンボル
 * @type string
 *
 * @param action
 * @text 選択時の挙動
 * @type select
 * @option アイテムシーン
 * @value 0
 * @option スキルアクター選択
 * @value 1
 * @option 装備アクター選択
 * @value 2
 * @option ステータスアクター選択
 * @value 3
 * @option 並び替え
 * @value 4
 * @option オプション
 * @value 5
 * @option セーブ
 * @value 6
 * @option ゲーム終了
 * @value 7
 * @option その他のシーン
 * @value 8
 *
 * @param scene
 * @desc 選択時の挙動でその他のシーンを選んだ場合に、遷移するシーンのクラス名（例: Scene_EnemyBook）
 * @text その他シーン
 * @type string
 */
/*~struct~DrawNumber:
 * @param maxDigits
 * @desc 数値の最大桁数を設定します。上限が99なら2、上限が100なら3
 * @text 最大桁数
 * @type number
 * @min 1
 *
 * @param numberFile
 * @desc 数値描画に使用する画像を設定します。
 * @text 数値画像
 * @type file
 *
 * @param baseX
 * @desc 基準となるX座標を設定します。
 * @text 基準X座標
 * @type number
 *
 * @param y
 * @desc Y座標を設定します。
 * @text Y座標
 * @type number
 */
/*~struct~DrawNumberMax:
 * @param baseX
 * @text 基準X座標
 * @type number
 *
 * @param y
 * @text Y座標
 * @type number
 */
/*~struct~DrawMeter:
 * @param meterFile
 * @text メーター画像
 * @type file
 *
 * @param baseX
 * @text 基準X座標
 * @type number
 * @default 37
 *
 * @param y
 * @text Y座標
 * @type number
 */
/*~struct~DrawIcon:
 * @param baseX
 * @text 基準X座標
 * @type number
 * @default 41
 *
 * @param y
 * @text Y座標
 * @type number
 * @default 367
 *
 * @param changeFrame
 * @desc 複数のアイコンを表示する際の切り替え間隔をフレーム数で指定します
 * @text アイコン切り替え間隔
 * @type number
 * @default 60
 * @min 1
 */
/*~struct~WindowRect:
 * @param x
 * @text X座標
 * @type number
 *
 * @param y
 * @text Y座標
 * @type number
 *
 * @param width
 * @text 幅
 * @type number
 *
 * @param height
 * @text 高さ
 * @type number
 */
(() => {
  'use strict';

  const pluginName = document.currentScript.src.replace(/^.*\/(.*).js$/, function () {
    return arguments[1];
  });

  const pluginParameters = PluginManager.parameters(pluginName);

  const settings = {
    menuCommands: JSON.parse(
      pluginParameters.menuCommands ||
        '[{"name":"アイテム","icon":"img/menu/com_item","symbol":"item","action":"0","scene":""},{"name":"スキル","icon":"img/menu/com_skill","symbol":"skill","action":"1","scene":""},{"name":"装備","icon":"img/menu/com_equip","symbol":"equip","action":"2","scene":""},{"name":"ステータス","icon":"img/menu/com_status","symbol":"status","action":"3","scene":""},{"name":"並び替え","icon":"img/menu/com_formation","symbol":"formation","action":"4","scene":""},{"name":"オプション","icon":"img/menu/com_options","symbol":"options","action":"5","scene":""},{"name":"賞金首","icon":"img/menu/com_bountyList","symbol":"bountyList","action":"8","scene":"Scene_BountyList"},{"name":"魔物図鑑","icon":"img/menu/com_enemyBook","symbol":"enemyBook","action":"8","scene":"Scene_EnemyBook"},{"name":"セーブ","icon":"img/menu/com_save","symbol":"save","action":"6","scene":""},{"name":"ゲーム終了","icon":"img/menu/com_quit","symbol":"gameEnd","action":"7","scene":""}]'
    ).map((e) => {
      return ((parameter) => {
        const parsed = JSON.parse(parameter);
        return {
          name: String(parsed.name || ''),
          icon: String(parsed.icon || ''),
          symbol: String(parsed.symbol || ''),
          action: Number(parsed.action || 0),
          scene: String(parsed.scene || ''),
        };
      })(e || '{}');
    }),
    commandIconWidth: Number(pluginParameters.commandIconWidth || 48),
    commandIconHeight: Number(pluginParameters.commandIconHeight || 48),
    frameLayoutImage: String(pluginParameters.frameLayoutImage || ''),
    frameStatusImage: String(pluginParameters.frameStatusImage || ''),
    backgroundImage: String(pluginParameters.backgroundImage || ''),
    drawLevel: ((parameter) => {
      const parsed = JSON.parse(parameter);
      return {
        maxDigits: Number(parsed.maxDigits || 0),
        numberFile: String(parsed.numberFile || ''),
        baseX: Number(parsed.baseX || 0),
        y: Number(parsed.y || 0),
      };
    })(pluginParameters.drawLevel || '{"maxDigits":"3", "numberFile":"img/menu/LVNumber", "baseX":"32", "y":"380"}'),
    drawHp: ((parameter) => {
      const parsed = JSON.parse(parameter);
      return {
        maxDigits: Number(parsed.maxDigits || 0),
        numberFile: String(parsed.numberFile || ''),
        baseX: Number(parsed.baseX || 0),
        y: Number(parsed.y || 0),
      };
    })(pluginParameters.drawHp || '{"maxDigits":"4", "numberFile":"img/menu/HPNumber", "baseX":"48", "y":"415"}'),
    drawMaxHp: ((parameter) => {
      const parsed = JSON.parse(parameter);
      return {
        baseX: Number(parsed.baseX || 0),
        y: Number(parsed.y || 0),
      };
    })(pluginParameters.drawMaxHp || '{"baseX":"80", "y":"447"}'),
    drawHpMeter: ((parameter) => {
      const parsed = JSON.parse(parameter);
      return {
        meterFile: String(parsed.meterFile || ''),
        baseX: Number(parsed.baseX || 37),
        y: Number(parsed.y || 0),
      };
    })(pluginParameters.drawHpMeter || '{"meterFile":"img/menu/HPMeter", "baseX":"44", "y":"430"}'),
    drawMp: ((parameter) => {
      const parsed = JSON.parse(parameter);
      return {
        maxDigits: Number(parsed.maxDigits || 0),
        numberFile: String(parsed.numberFile || ''),
        baseX: Number(parsed.baseX || 0),
        y: Number(parsed.y || 0),
      };
    })(pluginParameters.drawMp || '{"maxDigits":"3", "numberFile":"img/menu/MPNumber", "baseX":"48", "y":"465"}'),
    drawMaxMp: ((parameter) => {
      const parsed = JSON.parse(parameter);
      return {
        baseX: Number(parsed.baseX || 0),
        y: Number(parsed.y || 0),
      };
    })(pluginParameters.drawMaxMp || '{"baseX":"80", "y":"500"}'),
    drawMpMeter: ((parameter) => {
      const parsed = JSON.parse(parameter);
      return {
        meterFile: String(parsed.meterFile || ''),
        baseX: Number(parsed.baseX || 37),
        y: Number(parsed.y || 0),
      };
    })(pluginParameters.drawMpMeter || '{"meterFile":"img/menu/MPMeter", "baseX":"44", "y":"480"}'),
    drawIcon: ((parameter) => {
      const parsed = JSON.parse(parameter);
      return {
        baseX: Number(parsed.baseX || 41),
        y: Number(parsed.y || 367),
        changeFrame: Number(parsed.changeFrame || 60),
      };
    })(pluginParameters.drawIcon || '{"baseX":"41", "y":"367", "iconChangeFrame":"60"}'),
    playtimePosition: ((parameter) => {
      const parsed = JSON.parse(parameter);
      return {
        x: Number(parsed.x || 0),
        y: Number(parsed.y || 0),
        width: Number(parsed.width || 0),
        height: Number(parsed.height || 0),
      };
    })(pluginParameters.playtimePosition || '{"x":"625", "y":"42", "width":"150", "height":"64"}'),
    hideOuterCharacter: String(pluginParameters.hideOuterCharacter || true) === 'true',
    locationOffsetX: Number(pluginParameters.locationOffsetX || 0),
  };

  const ACTION_TYPE = {
    ITEM: 0,
    SKILL: 1,
    EQUIP: 2,
    STATUS: 3,
    FORMATION: 4,
    OPTION: 5,
    SAVE: 6,
    GAMEEND: 7,
    OTHER: 8,
  };

  /**
   * アクター数値描画スプライト
   */
  class Sprite_ActorNumbers extends Sprite {
    constructor(index) {
      super();
      this._levelSprite = new Sprite_ActorLevel(index);
      this._hpSprite = new Sprite_ActorHp(index);
      this._mhpSprite = new Sprite_ActorMaxHp(index);
      this._hpMeterSprite = new Sprite_ActorHpMeter(index);
      this._mpSprite = new Sprite_ActorMp(index);
      this._mmpSprite = new Sprite_ActorMaxMp(index);
      this._mpMeterSprite = new Sprite_ActorMpMeter(index);
      this._iconSprite = new Sprite_ActorIcon(index);
      this.addChild(this._levelSprite);
      this.addChild(this._hpSprite);
      this.addChild(this._mhpSprite);
      this.addChild(this._hpMeterSprite);
      this.addChild(this._mpSprite);
      this.addChild(this._mmpSprite);
      this.addChild(this._mpMeterSprite);
      this.addChild(this._iconSprite);
    }

    allSprites() {
      return [
        this._levelSprite,
        this._hpSprite,
        this._mhpSprite,
        this._hpMeterSprite,
        this._mpSprite,
        this._mmpSprite,
        this._mpMeterSprite,
        this._iconSprite,
      ];
    }

    change() {
      this._levelSprite.change();
      this._hpSprite.change();
      this._mhpSprite.change();
      this._mpSprite.change();
      this._mmpSprite.change();
    }

    /**
     * パーティ内での位置をセットする
     * @param {number} index インデックス
     */
    setPositionIndex(index) {
      this.allSprites().forEach((sprite) => sprite.setPositionIndex(index));
    }
  }

  class Sprite_ActorNumber extends Sprite {
    constructor(index) {
      super();
      this.initialize.apply(this, arguments);
    }

    initialize(index) {
      super.initialize();
      this.setFrame(0, 0, Graphics.width, Graphics.height);
      this._positionIndex = index;
      this._positionInScreenIndex = index;
      this._sprites = Array.from(Array(this.maxDigits()).keys()).map((_) => {
        const sprite = new Sprite();
        sprite.bitmap = this.numberImageBitmap();
        sprite.x = this.baseX();
        sprite.y = this.y();
        sprite.anchor.x = 0.5;
        sprite.anchor.y = 0.5;
        sprite.visible = false;
        return sprite;
      });
      this._sprites.forEach((sprite) => this.addChild(sprite));
      this._changed = true;
    }

    /**
     * パーティの何番目のアクターの数値であるか
     * @return {number}
     */
    get positionIndex() {
      return this._positionIndex;
    }

    /**
     * 画面内の何番目のアクターの数値であるか
     * @return {number}
     */
    get positionInScreenIndex() {
      return this._positionInScreenIndex;
    }

    get sprites() {
      return this._sprites;
    }

    /**
     * @return {number} 基準X座標
     */
    baseX() {
      return 0;
    }

    /**
     * @return {number} Y座標
     */
    y() {
      return 0;
    }

    /**
     * @return {number} 最大桁数
     */
    maxDigits() {
      return 1;
    }

    /**
     * @return {Bitmap} 数値画像ビットマップ
     */
    numberImageBitmap() {
      return ImageManager._emptyBitmap;
    }

    /**
     * @return {number} 数字画像1桁の幅
     */
    numberImageWidth() {
      return Math.round(this.sprites[0].bitmap.width / 10);
    }

    /**
     * @return {number} 数字画像の高さ
     */
    numberImageHeight() {
      return this.sprites[0].bitmap.height;
    }

    /**
     * @return {number} 描画する数値
     */
    number() {
      return 0;
    }

    /**
     * @return {string[]} 描画する数値を1桁ずつ文字列に分解した配列
     */
    numberStringArray() {
      return Math.abs(this.number()).toString().split('');
    }

    /**
     * 並び替えで変化した時に呼ぶ
     */
    change() {
      this._changed = true;
    }

    /**
     * パーティ内の位置をセットする
     * @param {number} index インデックス
     */
    setPositionIndex(index) {
      this._positionIndex = index;
      this.refresh();
    }

    update() {
      if (this._changed) {
        this.refresh();
        this._changed = false;
      }
    }

    refresh() {
      /**
       * まず全spriteを非表示にする
       */
      this._sprites.forEach((sprite) => (sprite.visible = false));
      const numberOfDigits = this.numberStringArray().length;
      /**
       * 1桁ずつSpriteを用意する
       */
      this.numberStringArray().forEach((numberString, index) => {
        const number = Number(numberString);
        this.sprites[index].setFrame(
          number * this.numberImageWidth(),
          0,
          this.numberImageWidth(),
          this.numberImageHeight()
        );
        this.sprites[index].x = this.baseX() - this.numberImageWidth() * (numberOfDigits - index);
        this.sprites[index].visible = true;
      });
    }
  }

  /**
   * アクターレベル表示用
   */
  class Sprite_ActorLevel extends Sprite_ActorNumber {
    /**
     * @return {number} X座標基準
     */
    baseX() {
      const xSpace = Math.floor((Graphics.boxWidth - 32) / 4);
      return settings.drawLevel.baseX + xSpace / 2 + xSpace * this.positionInScreenIndex;
    }

    /**
     * @return {number} Y座標
     */
    y() {
      return settings.drawLevel.y;
    }

    /**
     * @return {number} 最大桁数
     */
    maxDigits() {
      return settings.drawLevel.maxDigits;
    }

    /**
     * @return {Bitmap} 数値画像ビットマップ
     */
    numberImageBitmap() {
      return ImageManager.loadBitmap('', settings.drawLevel.numberFile);
    }

    /**
     * @return {number} 数値
     */
    number() {
      return this.level();
    }

    /**
     * @return {number} アクターのレベル
     */
    level() {
      return $gameParty.members()[this.positionIndex].level;
    }
  }

  /**
   * アクターHP表示用
   */
  class Sprite_ActorHp extends Sprite_ActorNumber {
    /**
     * @return {number} X座標基準
     */
    baseX() {
      const xSpace = Math.floor((Graphics.boxWidth - 32) / 4);
      return settings.drawHp.baseX + xSpace / 2 + xSpace * this._positionInScreenIndex;
    }

    y() {
      return settings.drawHp.y;
    }

    /**
     * @return {number}
     */
    maxDigits() {
      return settings.drawHp.maxDigits;
    }

    /**
     * @return {Bitmap} 数値画像ビットマップ
     */
    numberImageBitmap() {
      return ImageManager.loadBitmap('', settings.drawHp.numberFile);
    }

    /**
     * @return {number}
     */
    number() {
      return this.hp();
    }

    /**
     * @return {number} アクターの現在HP
     */
    hp() {
      return $gameParty.members()[this._positionIndex].hp;
    }
  }

  class Sprite_ActorMaxHp extends Sprite_ActorNumber {
    /**
     * @return {number} X座標基準
     */
    baseX() {
      const xSpace = Math.floor((Graphics.boxWidth - 32) / 4);
      return settings.drawMaxHp.baseX + xSpace / 2 + xSpace * this.positionInScreenIndex;
    }

    /**
     * @return {number} Y座標
     */
    y() {
      return settings.drawMaxHp.y;
    }

    /**
     * @return {number} 最大桁数
     */
    maxDigits() {
      return settings.drawHp.maxDigits;
    }

    /**
     * @return {Bitmap} 数値画像ビットマップ
     */
    numberImageBitmap() {
      return ImageManager.loadBitmap('', settings.drawHp.numberFile);
    }

    /**
     * @return {number} 数値
     */
    number() {
      return this.mhp();
    }

    /**
     * @return {number} アクターの最大HP
     */
    mhp() {
      return $gameParty.members()[this._positionIndex].mhp;
    }
  }

  class Sprite_ActorMeter extends Sprite {
    constructor(index) {
      super();
      this.initialize.apply(this, arguments);
    }

    initialize(index) {
      super.initialize();
      this.setFrame(0, 0, Graphics.width, Graphics.height);
      this._positionIndex = index;
      this._positionInScreenIndex = index;
      this._sprite = new Sprite();
      this._sprite.bitmap = this.meterImageBitmap();
      this._sprite.x = this.baseX();
      this._sprite.y = this.y();
      this.addChild(this._sprite);
      // widthが取得できるまで時間がかかるため決め打ち
      this._meterScroll = Math.randomInt(248);
    }

    get positionIndex() {
      return this._positionIndex;
    }

    get positionInScreenIndex() {
      return this._positionInScreenIndex;
    }

    /**
     * パーティ内での位置をセットする
     * @param {number} index インデックス
     */
    setPositionIndex(index) {
      this._positionIndex = index;
    }

    /**
     * @return {Bitmap} メーター画像ビットマップ
     */
    meterImageBitmap() {
      return ImageManager._emptyBitmap;
    }

    /**
     * @return {number} X座標基準
     */
    baseX() {
      return 0;
    }

    y() {
      return 0;
    }

    /**
     * @return {number} メーター画像の幅
     */
    meterImageWidth() {
      return this._sprite.bitmap.width / 3;
    }

    /**
     * @return {number} メーター画像の高さ
     */
    meterImageHeight() {
      return this._sprite.bitmap.height;
    }

    get meterScroll() {
      return this._meterScroll;
    }

    scrollMeter() {
      this._meterScroll += 4;
      if (this._meterScroll > this.meterImageWidth() * 2) {
        this._meterScroll = 0;
      }
    }

    /**
     * @return {number} 割合
     */
    rate() {
      return 1;
    }

    update() {
      this.refresh();
    }

    refresh() {
      this._sprite.setFrame(this.meterScroll, 0, this.rate() * this.meterImageWidth(), this.meterImageHeight());
      this.scrollMeter();
    }
  }

  class Sprite_ActorHpMeter extends Sprite_ActorMeter {
    /**
     * @return {number} X座標基準
     */
    baseX() {
      const xSpace = Math.floor((Graphics.boxWidth - 32) / 4);
      return settings.drawHpMeter.baseX + xSpace * this.positionInScreenIndex;
    }

    /**
     * @return {number}
     */
    y() {
      return settings.drawHpMeter.y;
    }

    /**
     * @return {Bitmap}
     */
    meterImageBitmap() {
      return ImageManager.loadBitmap('', settings.drawHpMeter.meterFile);
    }

    /**
     * @return {number} アクターの現在HP
     */
    hp() {
      return $gameParty.members()[this._positionIndex].hp;
    }

    /**
     * @return {number} アクターの最大HP
     */
    mhp() {
      return $gameParty.members()[this._positionIndex].mhp;
    }

    /**
     * @return {number} HP割合
     */
    rate() {
      return this.hp() / this.mhp();
    }
  }

  class Sprite_ActorMp extends Sprite_ActorNumber {
    /**
     * @return {number} X座標基準
     */
    baseX() {
      const xSpace = Math.floor((Graphics.boxWidth - 32) / 4);
      return settings.drawMp.baseX + xSpace / 2 + xSpace * this._positionInScreenIndex;
    }

    y() {
      return settings.drawMp.y;
    }

    /**
     * @return {number}
     */
    maxDigits() {
      return settings.drawMp.maxDigits;
    }

    /**
     * @return {Bitmap} 数値画像ビットマップ
     */
    numberImageBitmap() {
      return ImageManager.loadBitmap('', settings.drawMp.numberFile);
    }

    /**
     * @return {number}
     */
    number() {
      return this.mp();
    }

    /**
     * @return {number} アクターの現在HP
     */
    mp() {
      return $gameParty.members()[this._positionIndex].mp;
    }
  }

  class Sprite_ActorMaxMp extends Sprite_ActorNumber {
    /**
     * @return {number} X座標基準
     */
    baseX() {
      const xSpace = Math.floor((Graphics.boxWidth - 32) / 4);
      return settings.drawMaxMp.baseX + xSpace / 2 + xSpace * this.positionInScreenIndex;
    }

    /**
     * @return {number} Y座標
     */
    y() {
      return settings.drawMaxMp.y;
    }

    /**
     * @return {number} 最大桁数
     */
    maxDigits() {
      return settings.drawMp.maxDigits;
    }

    /**
     * @return {Bitmap} 数値画像ビットマップ
     */
    numberImageBitmap() {
      return ImageManager.loadBitmap('', settings.drawMp.numberFile);
    }

    /**
     * @return {number} 数値
     */
    number() {
      return this.mmp();
    }

    /**
     * @return {number} アクターの最大MP
     */
    mmp() {
      return $gameParty.members()[this._positionIndex].mmp;
    }
  }

  class Sprite_ActorMpMeter extends Sprite_ActorMeter {
    /**
     * @return {number} X座標基準
     */
    baseX() {
      const xSpace = Math.floor((Graphics.boxWidth - 32) / 4);
      return settings.drawMpMeter.baseX + xSpace * this.positionInScreenIndex;
    }

    /**
     * @return {number}
     */
    y() {
      return settings.drawMpMeter.y;
    }

    /**
     * @return {Bitmap}
     */
    meterImageBitmap() {
      return ImageManager.loadBitmap('', settings.drawMpMeter.meterFile);
    }

    /**
     * @return {number} アクターの現在HP
     */
    mp() {
      return $gameParty.members()[this._positionIndex].mp;
    }

    /**
     * @return {number} アクターの最大HP
     */
    mmp() {
      return $gameParty.members()[this._positionIndex].mmp;
    }

    /**
     * @return {number} HP割合
     */
    rate() {
      return this.mp() / this.mmp();
    }
  }

  class Sprite_ActorIcon extends Sprite {
    constructor(index) {
      super();
      this.initialize.apply(this, arguments);
    }

    initialize(index) {
      super.initialize();
      this._positionIndex = index;
      this._positionInScreenIndex = index;
      this.bitmap = ImageManager.loadSystem('IconSet');
      this.x = this.baseX();
      this.y = this.positionY();
      this.visible = false;
      this._targetIndex = 0;
    }

    /**
     * パーティ内での位置をセットする
     * @param {number} index インデックス
     */
    setPositionIndex(index) {
      this._positionIndex = index;
    }

    /**
     * @return {number} X座標基準
     */
    baseX() {
      const xSpace = Math.floor((Graphics.boxWidth - 32) / 4);
      return settings.drawIcon.baseX + xSpace / 2 + xSpace * this._positionInScreenIndex;
    }

    /**
     * @return {number}
     */
    positionY() {
      return settings.drawIcon.y;
    }

    /**
     * @return {number[]} アイコンインデックス一覧
     */
    allIcons() {
      return $gameParty.members()[this._positionIndex].allIcons();
    }

    /**
     * @return {number} 描画対象アイコンインデックス
     */
    targetIconIndex() {
      return this.allIcons()[Math.floor(this._targetIndex / this.iconChangeFrame())];
    }

    /**
     * @return {number} アイコン切り替え間隔
     */
    iconChangeFrame() {
      return settings.drawIcon.changeFrame;
    }

    increaseTargetIndex() {
      this._targetIndex++;
      if (this.allIcons().length * this.iconChangeFrame() <= this._targetIndex) {
        this._targetIndex = 0;
      }
    }

    update() {
      this.refresh();
    }

    refresh() {
      const icons = this.allIcons();
      if (icons.length === 0) {
        this.visible = false;
        return;
      }
      this.visible = true;
      this.setFrame(
        (this.targetIconIndex() % 16) * ImageManager.iconWidth,
        Math.floor(this.targetIconIndex() / 16) * ImageManager.iconHeight,
        ImageManager.iconWidth,
        ImageManager.iconHeight
      );
      this.increaseTargetIndex();
    }
  }

  /**
   * アクターの立ち絵
   */
  class Sprite_ActorImage extends Sprite {
    /**
     * @param {number} index アクターインデックス
     */
    constructor(index) {
      super();
      this.initialize.apply(this, arguments);
    }

    initialize(index) {
      super.initialize();
      if (index === undefined) return;
      this._actorIndex = index;
      this.setPositionIndex(index);
      const filename = $gameParty.members()[index].actor().meta.stand_picture;
      this.bitmap = filename ? ImageManager.loadPicture(filename.trim()) : null;
      this.anchor.x = 0.5;
      this.y = Graphics.boxHeight;
      this.anchor.y = 1.0;
    }

    get actorIndex() {
      return this._actorIndex;
    }

    setPositionIndex(index) {
      this._positionIndex = index;
      if (settings.hideOuterCharacter) {
        this.visible = index >= 0 && index < $gameParty.maxBattleMembers();
      }
      this.x = this.calcXPosition();
    }

    /**
     * @return {number}
     */
    calcXPosition() {
      const xSpace = Math.floor((Graphics.boxWidth - 32) / 4);
      return 16 + xSpace / 2 + xSpace * this._positionIndex;
    }

    /**
     * @return {boolean} 選択中かどうか
     */
    isSelected() {
      return this._selected;
    }

    select() {
      this._selected = true;
    }

    deselect() {
      this._selected = false;
    }

    blendColorNumber() {
      return (
        (this._blendColorNumber % this.blendInterval() > this.blendInterval() / 2
          ? this.blendInterval() - (this._blendColorNumber % this.blendInterval())
          : this._blendColorNumber % this.blendInterval()) + this.blendOffset()
      );
    }

    blendOffset() {
      return 190;
    }

    blendInterval() {
      return 60;
    }

    blendColor() {
      const blendColorNumber = this.blendColorNumber();
      return this.isSelected() ? [blendColorNumber, blendColorNumber, blendColorNumber, 128] : [0, 0, 0, 0];
    }

    /**
     * 選択中の点滅
     */
    updateBlendColor() {
      if (!this._blendColorNumber) {
        this._blendColorNumber = 0;
      }
      this._blendColorNumber++;
    }

    updateBitmap() {
      const filename = $gameParty.members()[this.actorIndex].actor().meta.stand_picture;
      this.bitmap = filename ? ImageManager.loadPicture(filename.trim()) : null;
    }

    update() {
      this.updateBlendColor();
      this.setBlendColor(this.blendColor());
    }
  }

  class Sprite_MenuCommandIcon extends Sprite_Clickable {
    constructor() {
      super();
      this.initialize.apply(this, arguments);
    }

    initialize(iconFileName) {
      super.initialize();
      this.loadIconImage(iconFileName);
      this.anchor.x = 0.5;
      this.anchor.y = 0.5;
    }

    loadIconImage(filename) {
      this.bitmap = ImageManager.loadBitmap('', filename);
    }
  }

  class Window_MenuCommandName extends Window_Base {
    constructor(rect) {
      super(rect);
      this.initialize.apply(this, arguments);
    }

    initialize(rect, text) {
      super.initialize(rect);
      this.opacity = 0;
      this.contents.fontSize = this.fontSize();
      this.setText(text);
    }

    fontSize() {
      return 22;
    }

    text() {
      return this._text;
    }

    drawCommandName() {
      this.drawText(this.text(), 20, 0, 100, 150, 'center');
    }

    setText(text) {
      if (text !== this._text) {
        this._text = text;
        this.contents.clear();
        this.drawCommandName();
      }
    }
  }

  class Window_MenuPlayTime extends Window_Base {
    constructor(rect) {
      super(rect);
      this.initialize.apply(this, arguments);
    }

    initialize(rect) {
      super.initialize(rect);
      this.opacity = 0;
    }

    update() {
      this.refresh();
    }

    refresh() {
      this.contents.clear();
      this.drawText($gameSystem.playtimeText(), 0, 0, this.textWidth('0000:00:00'), 'center');
    }
  }

  class Window_MenuLocation extends Window_Base {
    constructor(rect, locationName) {
      super(rect);
      this.initialize.apply(this, arguments);
    }

    initialize(rect, locationName) {
      super.initialize(rect);
      this._locationName = locationName;
      this.opacity = 0;
    }

    update() {
      this.refresh();
    }

    refresh() {
      this.contents.clear();
      if (this._locationName) {
        this.drawText(this._locationName, 0, 10, this.width, 'center');
      }
    }
  }

  const _Scene_MenuBase_createBackground = Scene_MenuBase.prototype.createBackground;
  Scene_MenuBase.prototype.createBackground = function () {
    _Scene_MenuBase_createBackground.call(this);
    if (settings.backgroundImage) {
      this._backgroundSprite = new Sprite();
      this._backgroundSprite.bitmap = ImageManager.loadBitmap('', settings.backgroundImage);
      this.addChild(this._backgroundSprite);
    }
  };

  const _Scene_Menu_initialize = Scene_Menu.prototype.initialize;
  Scene_Menu.prototype.initialize = function () {
    _Scene_Menu_initialize.call(this);
    this._actionTypeFunction = [
      this.commandItem.bind(this),
      this.commandPersonal.bind(this),
      this.commandPersonal.bind(this),
      this.commandPersonal.bind(this),
      this.commandFormation.bind(this),
      this.commandOptions.bind(this),
      this.commandSave.bind(this),
      this.commandGameEnd.bind(this),
    ];
    this._commandIconScaleOffset = 0;
  };

  const _Scene_Menu_create = Scene_Menu.prototype.create;
  Scene_Menu.prototype.create = function () {
    _Scene_Menu_create.call(this);
    this.createCommandNameWindow();
    this.createPlaytimeWindow();
    this.createLocationWindow();
  };

  Scene_Menu.prototype.createStatusWindow = function () {
    const rect = this.statusWindowRect();
    this._statusWindow = new Window_MenuStatusEx(rect);
    this._statusWindow.opacity = 0;
    this.addWindow(this._statusWindow);
  };

  const _Scene_Menu_createGoldWindow = Scene_Menu.prototype.createGoldWindow;
  Scene_Menu.prototype.createGoldWindow = function () {
    _Scene_Menu_createGoldWindow.call(this);
    this._goldWindow.opacity = 0;
  };

  const _Scene_Menu_createCommandWindow = Scene_Menu.prototype.createCommandWindow;
  Scene_Menu.prototype.createCommandWindow = function () {
    _Scene_Menu_createCommandWindow.call(this);
    settings.menuCommands
      .filter((command) => command.action < ACTION_TYPE.OTHER)
      .forEach((command) => this._commandWindow.setHandler(command.symbol, this._actionTypeFunction[command.action]));
    settings.menuCommands
      .filter((command) => command.action === ACTION_TYPE.OTHER)
      .forEach((command) => this._commandWindow.setHandler(command.symbol, this.commandOther.bind(this)));
    this._commandWindow.opacity = 0;
  };

  const _Scene_Menu_onPersonalOk = Scene_Menu.prototype.onPersonalOk;
  Scene_Menu.prototype.onPersonalOk = function () {
    const skillCommand = settings.menuCommands.find((command) => command.action === ACTION_TYPE.SKILL);
    const equipCommand = settings.menuCommands.find((command) => command.action === ACTION_TYPE.EQUIP);
    const statusCommand = settings.menuCommands.find((command) => command.action === ACTION_TYPE.STATUS);

    const currentSymbol = this._commandWindow.currentSymbol();

    if (skillCommand && skillCommand.symbol === currentSymbol) {
      SceneManager.push(Scene_Skill);
    } else if (equipCommand && equipCommand.symbol === currentSymbol) {
      SceneManager.push(Scene_Equip);
    } else if (statusCommand && statusCommand.symbol === currentSymbol) {
      SceneManager.push(Scene_Status);
    } else {
      _Scene_Menu_onPersonalOk.call(this);
    }
  };

  const _Scene_Menu_onFormationOk = Scene_Menu.prototype.onFormationOk;
  Scene_Menu.prototype.onFormationOk = function () {
    const index = this._statusWindow.index();
    const pendingIndex = this._statusWindow.pendingIndex();
    _Scene_Menu_onFormationOk.call(this);
    if (pendingIndex !== -1) {
      this._actorSprites[index].updateBitmap();
      if (index < $gameParty.maxBattleMembers()) {
        this._actorNumberSpritesList[index].change();
      }
      this._actorSprites[pendingIndex].updateBitmap();
      if (pendingIndex < $gameParty.maxBattleMembers()) {
        this._actorNumberSpritesList[pendingIndex].change();
      }
      this._statusWindow.refresh();
    }
  };

  Scene_Menu.prototype.commandOther = function () {
    const symbol = this._commandWindow.currentSymbol();
    const className = settings.menuCommands.find((command) => command.symbol === symbol).scene;
    if (className && window[className]) {
      SceneManager.push(window[className]);
    }
  };

  Scene_Menu.prototype.createCommandNameWindow = function () {
    this._commandNameWindow = new Window_MenuCommandName(this.commandNameWindowRect());
    this.addWindow(this._commandNameWindow);
  };

  Scene_Menu.prototype.createPlaytimeWindow = function () {
    this._playtimeWindow = new Window_MenuPlayTime(this.playtimeWindowRect());
    this.addWindow(this._playtimeWindow);
  };

  Scene_Menu.prototype.createLocationWindow = function () {
    this._locationWindow = new Window_MenuLocation(
      this.locationWindowRect(),
      $gameMap.displayName() ? $gameMap.displayName() : $dataMapInfos[$gameMap.mapId()].name
    );
    this.addWindow(this._locationWindow);
  };

  Scene_Menu.prototype.commandNameWindowRect = function () {
    const x = 0;
    const y = 50 + settings.commandIconHeight / 2;
    const width = 150;
    const height = 64;
    return new Rectangle(x, y, width, height);
  };

  Scene_Menu.prototype.playtimeWindowRect = function () {
    return settings.playtimePosition;
  };

  Scene_Menu.prototype.locationWindowRect = function () {
    const ww = Graphics.boxWidth / 2;
    const wh = this.calcWindowHeight(1, true);
    const wx = this._goldWindow.width + settings.locationOffsetX;
    const wy = this.mainAreaBottom() - wh;
    return new Rectangle(wx, wy, ww, wh);
  };

  Scene_Menu.prototype.mainCommandWidth = function () {
    return Graphics.boxWidth - 240;
  };

  Scene_Menu.prototype.commandWindowHeight = function () {
    return settings.commandIconHeight * 3;
  };

  Scene_Menu.prototype.commandWindowX = function () {
    return 120;
  };

  Scene_Menu.prototype.buttonAreaBottom = function () {
    return 0;
  };

  Scene_Menu.prototype.buttonAreaHeight = function () {
    return 0;
  };

  Scene_Menu.prototype.goldWindowHeight = function () {
    return this.calcWindowHeight(1, true);
  };

  Scene_Menu.prototype.statusWindowRect = function () {
    const h1 = this.commandWindowHeight();
    const h2 = this.goldWindowHeight();
    const ww = Graphics.boxWidth;
    const wh = this.mainAreaHeight() - h1 - h2;
    const wx = 0;
    const wy = this.mainAreaTop() + this.commandWindowHeight();
    return new Rectangle(wx, wy, ww, wh);
  };

  Scene_Menu.prototype.commandWindowRect = function () {
    const ww = this.mainCommandWidth();
    const wh = this.commandWindowHeight();
    const wx = this.commandWindowX();
    const wy = this.mainAreaTop();
    return new Rectangle(wx, wy, ww, wh);
  };

  const _Scene_Menu_createBackground = Scene_Menu.prototype.createBackground;
  Scene_Menu.prototype.createBackground = function () {
    _Scene_Menu_createBackground.call(this);
    this.createActorImage();
    this._layoutSprite = new Sprite();
    if (settings.frameLayoutImage) {
      this._layoutSprite.bitmap = ImageManager.loadBitmap('', settings.frameLayoutImage);
      this.addChild(this._layoutSprite);
    }
    this._layoutStatusSprites = $gameParty.battleMembers().map((_, index) => {
      const sprite = new Sprite();
      sprite.bitmap = ImageManager.loadBitmap('', settings.frameStatusImage);
      const xSpace = Math.floor((Graphics.boxWidth - 32) / 4);
      sprite.x = 16 + xSpace / 2 + xSpace * index;
      sprite.anchor.x = 0.5;
      sprite.y = Graphics.boxHeight - 280;
      return sprite;
    });
    this._layoutStatusSprites.forEach((sprite) => this.addChild(sprite));
    this._actorNumberSpritesList = $gameParty.battleMembers().map((_, index) => {
      return new Sprite_ActorNumbers(index);
    });
    this._actorNumberSpritesList.forEach((sprites) => this.addChild(sprites));
  };

  Scene_Menu.prototype.createActorImage = function () {
    this._actorSprites = $gameParty.members().map((_, index) => {
      return new Sprite_ActorImage(index);
    });
    this._actorSprites.filter((sprite) => sprite).forEach((sprite) => this.addChild(sprite));
  };

  const _Scene_Menu_createButtons = Scene_Menu.prototype.createButtons;
  Scene_Menu.prototype.createButtons = function () {
    _Scene_Menu_createButtons.call(this);
    this.createCommandIcons();
  };

  Scene_Menu.prototype.createCommandIcons = function () {
    this._commandIconSprites = settings.menuCommands.map((command) => {
      return new Sprite_MenuCommandIcon(command.icon);
    });
    this._commandIconSprites.forEach((sprite, index) => {
      sprite.x = this.commandIconX(index);
      sprite.y = this.commandIconY(index);
      this.addChild(sprite);
    });
  };

  /**
   * @return {number}
   */
  Scene_Menu.prototype.commandIconBaseX = function () {
    return 180;
  };

  /**
   * @return {number}
   */
  Scene_Menu.prototype.commandIconBaseY = function () {
    return 50;
  };

  /**
   * @param {number} index インデックス
   * @return {number}
   */
  Scene_Menu.prototype.commandIconX = function (index) {
    return this.commandIconBaseX() + index * settings.commandIconWidth;
  };

  /**
   * @param {number} index インデックス
   * @return {number}
   */
  Scene_Menu.prototype.commandIconY = function (index) {
    return this.commandIconBaseY() + (index % 2) * settings.commandIconHeight;
  };

  /**
   * @param {number} index インデックス
   * @return {number}
   */
  Scene_Menu.prototype.commandIconScale = function (index) {
    return this._commandWindow.index() === index ? this.currentCommandIconScale() : 1.0;
  };

  Scene_Menu.prototype.currentCommandIconScale = function () {
    this._commandIconScaleOffset++;
    const offset =
      this._commandIconScaleOffset % 60 > 30
        ? 60 - (this._commandIconScaleOffset % 60)
        : this._commandIconScaleOffset % 60;
    return 1.0 + offset / 100;
  };

  Scene_Menu.prototype.goldWindowRect = function () {
    const ww = 300;
    const wh = this.calcWindowHeight(1, true);
    const wx = 0;
    const wy = this.mainAreaBottom() - wh;
    return new Rectangle(wx, wy, ww, wh);
  };

  const _Scene_Menu_update = Scene_Menu.prototype.update;
  Scene_Menu.prototype.update = function () {
    _Scene_Menu_update.call(this);
    this.updateCommandIcons();
    this.updateCommandName();
    this.updateActorSpritePosition();
    this.updateActorBlend();
  };

  Scene_Menu.prototype.updateCommandName = function () {
    const commandName = this._commandWindow.commandName(this._commandWindow.index());
    this._commandNameWindow.setText(commandName);
  };

  Scene_Menu.prototype.updateCommandIcons = function () {
    this._commandIconSprites.forEach((sprite, index) => {
      const scale = this.commandIconScale(index);
      sprite.scale.x = scale;
      sprite.scale.y = scale;
      sprite.opacity = index === this._commandWindow.index() ? 255 : 180;
    });
  };

  Scene_Menu.prototype.updateActorSpritePosition = function () {
    if (this._lastTopIndex === this._statusWindow.topIndex()) {
      return;
    }
    this._lastTopIndex = this._statusWindow.topIndex();
    /**
     * 全アクターの立ち絵画像の位置設定
     */
    this._actorSprites.forEach((sprite, index) => {
      const positionIndex =
        index < this._statusWindow.topIndex()
          ? index - this._statusWindow.topIndex() + this._statusWindow.maxItems()
          : index - this._statusWindow.topIndex();
      sprite.setPositionIndex(positionIndex);
    });
    /**
     * 表示アクターのステータス画像の位置設定
     */
    this._actorNumberSpritesList.forEach((sprite, index) => {
      const positionIndex =
        this._statusWindow.topIndex() + index >= $gameParty.size()
          ? index + this._statusWindow.topIndex() - $gameParty.size()
          : index + this._statusWindow.topIndex();
      sprite.setPositionIndex(positionIndex);
    });
  };

  /**
   * 選択中アクターの点滅
   */
  Scene_Menu.prototype.updateActorBlend = function () {
    const selectedSprites = this._actorSprites.filter((sprite) => sprite.isSelected());
    if (this._statusWindow.isOpenAndActive()) {
      selectedSprites
        .filter((sprite) => {
          return (
            this._statusWindow.index() !== sprite.actorIndex && this._statusWindow.pendingIndex() !== sprite.actorIndex
          );
        })
        .forEach((sprite) => sprite.deselect());
      if (!this._actorSprites[this._statusWindow.index()].isSelected()) {
        this._actorSprites[this._statusWindow.index()].select();
      }
    } else {
      selectedSprites.forEach((sprite) => sprite.deselect());
    }
  };

  const _Window_Gold_itemLineRect = Window_Gold.prototype.itemLineRect;
  Window_Gold.prototype.itemLineRect = function (index) {
    const rect = _Window_Gold_itemLineRect.call(this, index);
    rect.y += 8; // FIXME
    return rect;
  };

  class Window_MenuStatusEx extends Window_MenuStatus {
    initialize(rect) {
      super.initialize(rect);
      this._topIndex = 0;
      this._lastIndex = 0;
    }

    /**
     * @return {number}
     */
    topIndex() {
      return this._topIndex || 0;
    }

    deselect() {
      this._lastIndex = this.index();
      super.deselect();
    }

    selectLast() {
      this.smoothSelect(this._lastIndex);
      if (this.topIndex() > this.index() || this.topIndex() + 4 <= this.index()) {
        this._topIndex = this.index() - 3;
        if (this.topIndex() < 0) {
          this._topIndex += this.maxItems();
        }
      }
    }

    /**
     * 直感に反する挙動なので何もしない
     */
    cursorDown() {}
    cursorUp() {}

    cursorLeft(wrap) {
      super.cursorLeft(wrap);
      if ($gameParty.size() > 4 && (this.topIndex() - 1 + $gameParty.size()) % $gameParty.size() === this.index()) {
        this._topIndex = this.index();
        this.refresh();
      }
    }

    cursorRight(wrap) {
      const lastIndex = this.index();
      super.cursorRight(wrap);
      if ($gameParty.size() > 4 && (this.topIndex() + 3) % $gameParty.size() === lastIndex) {
        this._topIndex++;
        if (this._topIndex >= this.maxItems()) {
          this._topIndex = 0;
        }
        this.refresh();
      }
    }

    cursorPagedown() {}
    cursorPageup() {}

    maxCols() {
      return 4;
    }

    numVisibleRows() {
      return 1;
    }

    maxVisibleItems() {
      return $gameParty.size() < 4 ? $gameParty.size() : 4;
    }

    /**
     * 自前でSprite側で処理するため、削除
     */
    drawPendingItemBackground() {}
    drawItemBackground() {}
    drawItemImage() {}
    smoothScrollDown() {}
    smoothScrollUp() {}
    smoothScrollTo() {}

    drawActorName(actor, x, y, width) {
      this.contents.fontSize = 20;
      this.changeTextColor(ColorManager.hpColor(actor));
      this.drawText(actor.name(), x, y, width, 'center');
      this.resetFontSettings();
    }

    drawItemStatus(index) {
      const targetIndex = index >= this.maxItems() ? index - this.maxItems() : index;
      const positionIndex =
        index < this.topIndex() ? index - this.topIndex() + this.maxItems() : index - this.topIndex();
      const actor = this.actor(targetIndex);
      if (actor) {
        const rect = this.itemRectWithPadding(positionIndex);
        const x = rect.x;
        const width = rect.width;
        this.drawActorName(actor, x, 175, width);
      }
    }

    drawAllItems() {
      const topIndex = this.topIndex();
      for (let i = 0; i < this.maxVisibleItems(); i++) {
        const index = topIndex + i < this.maxItems() ? topIndex + i : topIndex + i - this.maxItems();
        this.drawItemBackground(index);
        this.drawItem(index);
      }
    }

    refreshCursor() {
      this.setCursorRect(0, 0, 0, 0);
    }

    updateArrows() {
      this.downArrowVisible = false;
      this.upArrowVisible = false;
    }
  }

  Window_MenuCommand.prototype.maxCols = function () {
    return settings.menuCommands.length;
  };

  Window_MenuCommand.prototype.isHorizontal = function () {
    return true;
  };

  Window_MenuCommand.prototype.makeCommandList = function () {
    settings.menuCommands.forEach((command) => {
      this.addCommand(command.name, command.symbol, true);
    });
  };

  Window_MenuCommand.prototype.drawItemBackground = function () {};

  /**
   * 独自に描画するため削除
   */
  Window_MenuCommand.prototype.drawItem = function (index) {};

  /**
   * @return {number}
   */
  Window_MenuCommand.prototype.commandIconBaseX = function () {
    return 180;
  };

  /**
   * @return {number}
   */
  Window_MenuCommand.prototype.commandIconBaseY = function () {
    return 50;
  };

  Window_MenuCommand.prototype.refreshCursor = function () {
    this.setCursorRect(0, 0, 0, 0);
  };

  Window_MenuCommand.prototype.itemRect = function (index) {
    const x = index * settings.commandIconWidth;
    const y = (index % 2) * settings.commandIconHeight;
    const width = settings.commandIconWidth;
    const height = settings.commandIconHeight;
    return new Rectangle(x, y, width, height);
  };

  Window_MenuCommand.prototype.hitTest = function (x, y) {
    if (this.innerRect.contains(x, y)) {
      const cx = this.origin.x + x - this.padding;
      const cy = this.origin.y + y - this.padding;
      const topIndex = this.topIndex();
      for (let i = 0; i < this.maxVisibleItems(); i++) {
        const index = topIndex + i;
        if (index < this.maxItems()) {
          const rect = this.itemRect(index);
          if (rect.contains(cx, cy)) {
            return index;
          }
        }
      }
    }
    return -1;
  };
})();
