code:
//=============================================================================
// WeaponSkill.js
//=============================================================================

/*:
 * @plugindesc Change skill id of attack for each weapon.
 * @author Sasuke KANNAZUKI
 *
 * @help This plugin does not provide plugin commands.
 *
 * When <skill_id:3> is written in a weapon's note field,
 * skill id # 3 is used for the weapon's attack.
 * If nothing is written, default id(=1) is used.
 *
 * Check Points:
 * - When multiple weapons are equipped, the skill id of the weapon
 *  held in the dominant hand (previously defined) is used.
 * - It is most favorable for "skill type" to be "none"(=0),
 *  otherwise you cannot attack when your skill is blocked.
 *
 * Usage examples of this plugin:
 * - to create all-range weapons
 * - to create dual-attack or triple-attack weapons
 * - If healing skill is set when actor attacks, you can choose a friend to heal.
 * - It is possible to make a weapon that functions similar to a guard command.
 */

/*:ja
 * @plugindesc 武器ごとに通常攻撃のスキルIDを変更します。
 * @author 神無月サスケ
 *
 * @help このプラグインにはプラグインコマンドはありません。
 *
 *  武器の「メモ」欄に、<skill_id:3> と書いた場合、
 * 通常攻撃の際、3番のスキルが発動します。
 * ※特に記述がなければ、通常通り1番のスキルが採用されます。
 *
 * チェックポイント:
 * - 二刀流の場合、利き腕(先に定義された方)に持っているスキルIDが採用されます。
 * - スキルタイプは「なし」にするのが望ましいです。
 * さもなくば、技などを封じられたとき、攻撃が出来なくなります。
 *
 * 想定される用途:
 * - 全体攻撃可能な武器
 * - 2回攻撃、3回攻撃する武器
 * - 回復魔法をスキルに指定した場合、
 * 「攻撃」を選んだ際、味方の選択が出来、その仲間を回復します
 * - 防御コマンドなどと同等になる武器も実現可能です。
 */

(function () {
  //
  // set skill id for attack.
  //
  Game_Actor.prototype.attackSkillId = function () {
    var normalId = Game_BattlerBase.prototype.attackSkillId.call(this);
    if (this.hasNoWeapons()) {
      return normalId;
    }
    var weapon = this.weapons()[0]; // at plural weapon, one's first skill.
    var id = weapon.meta.skill_id;
    return id ? Number(id) : normalId;
  };

  //
  // for command at battle
  //
  var _Scene_Battle_commandAttack = Scene_Battle.prototype.commandAttack;
  Scene_Battle.prototype.commandAttack = function () {
    BattleManager.inputtingAction().setAttack();
    // normal attack weapon (or other single attack weapon)
    var action = BattleManager.inputtingAction();
    if (action.needsSelection() && action.isForOpponent()) {
      _Scene_Battle_commandAttack.call(this);
      return;
    }
    // special skill weapon
    this.onSelectAction();
  };
})();

repo_name: dazed/translations

license: unknown


code:
//=============================================================================
// WindowBackImage.js
// ----------------------------------------------------------------------------
// (C)2017 Triacontane
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php
// ----------------------------------------------------------------------------
// Version
// 1.5.1 2021/01/30 バトラーステータスウィンドウが選択肢に抜けていたので追加
// 1.5.0 2021/01/24 ウィンドウごとに個別のウィンドウスキンを指定できる機能を追加
// 1.4.0 2020/12/28 JK_MailSystem.jsの各ウィンドウをオプション項目に追加
// 1.3.1 2020/08/22 カスタムメニュープラグインで作成したウィンドウ背景を変えられる機能を追加
// 1.3.0 2019/01/13 ウィンドウ背景の画像を複数表示できる機能を追加
//                  ウィンドウ背景を指定した場合も元のウィンドウフレームを表示したままにできる機能を追加
// 1.2.0 2018/11/29 ウィンドウ背景を有効にするかどうかを動的に制御するスイッチを追加
// 1.1.0 2017/11/19 拡大率を設定できる機能を追加
// 1.0.0 2017/11/18 初版
// ----------------------------------------------------------------------------
// [Blog]   : https://triacontane.blogspot.jp/
// [Twitter]: https://twitter.com/triacontane/
// [GitHub] : https://github.com/triacontane/
//=============================================================================

/*:
 * @plugindesc ウィンドウ背景画像指定プラグイン
 * @author トリアコンタン
 *
 * @param windowImageInfo
 * @text ウィンドウ画像情報
 * @desc 背景画像を差し替えるウィンドウの情報です。
 * @default
 * @type struct<WindowImages>[]
 *
 * @help WindowBackImage.js
 *
 * ウィンドウの背景を任意の画像に置き換えます。
 * 画像は複数指定可能で、それぞれに出現条件スイッチを指定できます。
 * 画像が表示された場合、元のウィンドウフレームを非表示するかどうかを
 * 選択できます。
 *
 * 背景画像はウィンドウのサイズにかかわらず、中央を原点に表示されます。
 * 拡大率と座標を補正することは可能ですがサイズが可変、不定のウィンドウに
 * 対して背景画像を指定することは推奨しません。
 *
 * プラグインによって追加されたウィンドウにも指定可能ですが
 * 正常に動作するとは限りません。
 *
 * SceneCustomMenu.jsで追加したウィンドウの背景を変えたい場合は
 * WindowClassの指定を同プラグインの『ウィンドウ識別子』を指定してください。
 *
 * このプラグインにはプラグインコマンドはありません。
 *
 * 利用規約：
 *  作者に無断で改変、再配布が可能で、利用形態（商用、18禁利用等）
 *  についても制限はありません。
 *  このプラグインはもうあなたのものです。
 */

/*~struct~WindowImages:
 *
 * @param WindowClass
 * @desc 専用の画像に差し替える対象のウィンドウです。一覧にない場合は直接入力してください。
 * @type select
 * @default
 * @option [ゲーム全般]ヘルプウィンドウ
 * @value Window_Help
 * @option [ゲーム全般]お金ウィンドウ
 * @value Window_Gold
 * @option [メインメニュー]メインコマンドウィンドウ
 * @value Window_MenuCommand
 * @option [メインメニュー]アクターステータスウィンドウ
 * @value Window_MenuStatus
 * @option [アイテム画面]アイテムカテゴリウィンドウ
 * @value Window_ItemCategory
 * @option [アイテム画面]アイテムリストウィンドウ
 * @value Window_ItemList
 * @option [アイテム画面]アクター選択ウィンドウ
 * @value Window_MenuActor
 * @option [スキル画面]スキルタイプウィンドウ
 * @value Window_SkillType
 * @option [スキル画面]ステータスウィンドウ
 * @value Window_SkillStatus
 * @option [スキル画面]スキルリストウィンドウ
 * @value Window_SkillList
 * @option [装備画面]ステータスウィンドウ
 * @value Window_EquipStatus
 * @option [装備画面]装備コマンドウィンドウ
 * @value Window_EquipCommand
 * @option [装備画面]装備スロットウィンドウ
 * @value Window_EquipSlot
 * @option [装備画面]装備リストウィンドウ
 * @value Window_EquipItem
 * @option [ステータス画面]ステータスウィンドウ
 * @value Window_Status
 * @option [オプション画面]オプションウィンドウ
 * @value Window_Options
 * @option [セーブ、ロード画面]ファイルリストウィンドウ
 * @value Window_SavefileList
 * @option [ショップ画面]ショップコマンドウィンドウ
 * @value Window_ShopCommand
 * @option [ショップ画面]購入アイテムウィンドウ
 * @value Window_ShopBuy
 * @option [ショップ画面]売却アイテムウィンドウ
 * @value Window_ShopSell
 * @option [ショップ画面]数値入力ウィンドウ
 * @value Window_ShopNumber
 * @option [ショップ画面]ステータスウィンドウ
 * @value Window_ShopStatus
 * @option [名前入力画面]名前ウィンドウ
 * @value Window_NameEdit
 * @option [名前入力画面]名前入力ウィンドウ
 * @value Window_NameInput
 * @option [マップ画面]選択肢ウィンドウ
 * @value Window_ChoiceList
 * @option [マップ画面]数値入力ウィンドウ
 * @value Window_NumberInput
 * @option [マップ画面]アイテム選択ウィンドウ
 * @value Window_EventItem
 * @option [マップ画面]メッセージウィンドウ
 * @value Window_Message
 * @option [マップ画面]スクロールメッセージウィンドウ
 * @value Window_ScrollText
 * @option [マップ画面]マップ名ウィンドウ
 * @value Window_MapName
 * @option [戦闘画面]バトルログウィンドウ
 * @value Window_BattleLog
 * @option [戦闘画面]パーティコマンドウィンドウ
 * @value Window_PartyCommand
 * @option [戦闘画面]アクターコマンドウィンドウ
 * @value Window_ActorCommand
 * @option [戦闘画面]バトラーステータスウィンドウ
 * @value Window_BattleStatus
 * @option [戦闘画面]アクター一覧ウィンドウ
 * @value Window_BattleActor
 * @option [戦闘画面]敵キャラ一覧ウィンドウ
 * @value Window_BattleEnemy
 * @option [戦闘画面]スキル一覧ウィンドウ
 * @value Window_BattleSkill
 * @option [戦闘画面]アイテム一覧ウィンドウ
 * @value Window_BattleItem
 * @option [タイトル画面]タイトルウィンドウ
 * @value Window_TitleCommand
 * @option [ゲーム終了画面]終了確認ウィンドウ
 * @value Window_GameEnd
 * @option [デバッグ画面]変数選択ウィンドウ
 * @value Window_DebugRange
 * @option [デバッグ画面]変数設定ウィンドウ
 * @value Window_DebugEdit
 * @option [行動目標ウィンドウプラグイン]行動目標ウィンドウ
 * @value Window_Destination
 * @option [行動目標ウィンドウプラグイン]メニュー行動目標ウィンドウ
 * @value Window_DestinationMenu
 * @option [ゲーム内時間の導入プラグイン]時間ウィンドウ
 * @value Window_Chronus
 * @option [公式ガチャプラグイン]ガチャ表示ウィンドウ
 * @value Window_Gacha
 * @option [公式ガチャプラグイン]コマンドウィンドウ
 * @value Window_GachaCommand
 * @option [公式ガチャプラグイン]入手確認ウィンドウ
 * @value Window_GachaGetCommand
 * @option [公式ガチャプラグイン]入手情報ウィンドウ
 * @value Window_GachaGet
 * @option [公式ガチャプラグイン]コストウィンドウ
 * @value Window_Cost
 * @option [ノベルゲーム総合プラグイン]ノベル選択肢ウィンドウ
 * @value Window_NovelChoiceList
 * @option [ノベルゲーム総合プラグイン]ノベルメッセージウィンドウ
 * @value Window_NovelMessage
 * @option [ノベルゲーム総合プラグイン]ノベルタイトルコマンドウィンドウ
 * @value Window_NovelTitleCommand
 * @option [ノベルゲーム総合プラグイン]ノベル数値入力ウィンドウ
 * @value Window_NovelNumberInput
 * @option [ノベルゲーム総合プラグイン]ポーズメニューウィンドウ
 * @value Window_PauseMenu
 * @option [クロスセーブプラグイン]パスワード入力ウィンドウ
 * @value Window_PasswordInput
 * @option [クロスセーブプラグイン]パスワードウィンドウ
 * @value Window_PasswordEdit
 * @option [用語辞典プラグイン]用語カテゴリウィンドウ
 * @value Window_GlossaryCategory
 * @option [用語辞典プラグイン]用語リストウィンドウ
 * @value Window_GlossaryList
 * @option [用語辞典プラグイン]使用確認ウィンドウ
 * @value Window_GlossaryConfirm
 * @option [用語辞典プラグイン]収集率ウィンドウ
 * @value Window_GlossaryComplete
 * @option [用語辞典プラグイン]用語ウィンドウ
 * @value Window_Glossary
 * @option [サウンドテストプラグイン]オーディオカテゴリウィンドウ
 * @value Window_AudioCategory
 * @option [サウンドテストプラグイン]オーディオリストウィンドウ
 * @value Window_AudioList
 * @option [サウンドテストプラグイン]オーディオ設定ウィンドウ
 * @value Window_AudioSetting
 * @option [数値入力画面プラグイン]数値入力ウィンドウ
 * @value Window_NumberInput
 * @option [数値入力画面プラグイン]数値ウィンドウ
 * @value Window_NumberEdit
 * @option [JK_MailSystem]メールリストウィンドウ
 * @value Window_MailList
 * @option [JK_MailSystem]未読メールウィンドウ
 * @value Window_Unread
 * @option [JK_MailSystem]メール本文ウィンドウ
 * @value Window_Mail
 * @option [JK_MailSystem]添付ファイルウィンドウ
 * @value Window_MailAttachment
 * @option [JK_MailSystem]メールコマンドウィンドウ
 * @value Window_MailBoxCommand
 *
 * @param ImageFile
 * @desc 差し替える画像のファイル名です。(img/pictureの中から選択します)　空を指定すると枠だけが非表示になります。
 * @default
 * @require 1
 * @dir img/pictures/
 * @type file
 *
 * @param WindowSkin
 * @desc 専用のウィンドウスキン画像です。
 * @default
 * @require 1
 * @dir img/system/
 * @type file
 *
 * @param OffsetX
 * @desc 表示X座標の補正値です。
 * @default 0
 * @type number
 * @min -2000
 * @max 2000
 *
 * @param OffsetY
 * @desc 表示Y座標の補正値です。
 * @default 0
 * @type number
 * @min -2000
 * @max 2000
 *
 * @param ScaleX
 * @desc X方向の拡大率(%指定)です。
 * @default 100
 * @type number
 * @min -2000
 * @max 2000
 *
 * @param ScaleY
 * @desc Y方向の拡大率(%指定)です。
 * @default 100
 * @type number
 * @min -2000
 * @max 2000
 *
 * @param WindowShow
 * @desc 画像が表示されているときでもウィンドウの元背景を表示したままにします。
 * @default false
 * @type boolean
 *
 * @param SwitchId
 * @desc 指定したスイッチがONのときのみウィンドウを差し替えます。
 * @default 0
 * @type switch
 */

(function () {
  "use strict";

  var getClassName = function (object) {
    return object.constructor
      .toString()
      .replace(/function\s+(.*)\s*\([\s\S]*/m, "$1");
  };

  //=============================================================================
  // ローカル関数
  //  プラグインパラメータやプラグインコマンドパラメータの整形やチェックをします
  //=============================================================================
  /**
   * Create plugin parameter. param[paramName] ex. param.commandPrefix
   * @param pluginName plugin name(EncounterSwitchConditions)
   * @returns {Object} Created parameter
   */
  var createPluginParameter = function (pluginName) {
    var paramReplacer = function (key, value) {
      if (value === "null") {
        return value;
      }
      if (value[0] === '"' && value[value.length - 1] === '"') {
        return value;
      }
      try {
        return JSON.parse(value);
      } catch (e) {
        return value;
      }
    };
    var parameter = JSON.parse(
      JSON.stringify(PluginManager.parameters(pluginName), paramReplacer)
    );
    PluginManager.setParameters(pluginName, parameter);
    return parameter;
  };

  var param = createPluginParameter("WindowBackImage");
  if (!param.windowImageInfo) {
    param.windowImageInfo = [];
  }

  //=============================================================================
  // Window
  //  専用の背景画像を設定します。
  //=============================================================================
  var _Window__createAllParts = Window.prototype._createAllParts;
  Window.prototype._createAllParts = function () {
    _Window__createAllParts.apply(this, arguments);
    this._backImageDataList = this.initBackImageData();
    if (this._backImageDataList.length >= 0) {
      this._createBackImage();
    }
  };

  Window.prototype._setBackImageProperty = function (backImageData) {
    this._backImageDx = parseInt(backImageData["OffsetX"]) || 0;
    this._backImageDy = parseInt(backImageData["OffsetY"]) || 0;
    this._windowBackImageSprite.scale.x =
      (parseInt(backImageData["ScaleX"]) || 100) / 100;
    this._windowBackImageSprite.scale.y =
      (parseInt(backImageData["ScaleY"]) || 100) / 100;
  };

  /**
   * 背景画像を作成します。
   * @private
   */
  Window.prototype._createBackImage = function () {
    this._windowBackSprite.visible = false;
    this._windowFrameSprite.visible = false;
    this._windowBackImageSprites = [];
    this._backImageDataList.forEach(function (backImageData) {
      var bitmap = ImageManager.loadPicture(backImageData["ImageFile"]);
      var sprite = new Sprite_WindowBackImage(bitmap);
      sprite.scale.x = (backImageData["ScaleX"] || 100) / 100;
      sprite.scale.y = (backImageData["ScaleY"] || 100) / 100;
      this._windowBackImageSprites.push(sprite);
      this._windowSpriteContainer.addChild(sprite);
    }, this);
  };

  Window.prototype.initBackImageData = function () {
    var className = getClassName(this);
    // for SceneCustomMenu.js
    if (this._data && this._data.Id) {
      className = this._data.Id;
    }
    return param.windowImageInfo.filter(function (data) {
      return data["WindowClass"] === className;
    }, this);
  };

  Window.prototype.getBackImageDataItem = function (index, propName) {
    return this._backImageDataList[index][propName];
  };

  var _Window__refreshAllParts = Window.prototype._refreshAllParts;
  Window.prototype._refreshAllParts = function () {
    if (this._windowBackImageSprites) {
      this._refreshBackImage();
    }
    _Window__refreshAllParts.apply(this, arguments);
  };

  /**
   * 背景画像をリフレッシュします。
   * @private
   */
  Window.prototype._refreshBackImage = function () {
    this._windowBackImageSprites.forEach(function (sprite, index) {
      sprite.x = this.width / 2 + this.getBackImageDataItem(index, "OffsetX");
      sprite.y = this.height / 2 + this.getBackImageDataItem(index, "OffsetY");
    }, this);
  };

  var _Window_update = Window.prototype.update;
  Window.prototype.update = function () {
    _Window_update.apply(this, arguments);
    if (!this._windowBackImageSprites) {
      return;
    }
    var defaultVisible = true;
    this._windowBackImageSprites.forEach(function (sprite, index) {
      var switchId = this.getBackImageDataItem(index, "SwitchId");
      sprite.visible = !switchId || $gameSwitches.value(switchId);
      if (sprite.visible && !this.getBackImageDataItem(index, "WindowShow")) {
        defaultVisible = false;
      }
    }, this);
    this._windowBackSprite.visible = defaultVisible;
    this._windowFrameSprite.visible = defaultVisible;
  };

  var _Window_Base_loadWindowskin = Window_Base.prototype.loadWindowskin;
  Window_Base.prototype.loadWindowskin = function () {
    _Window_Base_loadWindowskin.apply(this, arguments);
    var list = this._backImageDataList;
    if (list && list.length > 0 && list[0].WindowSkin) {
      this.windowskin = ImageManager.loadSystem(list[0].WindowSkin);
    }
  };

  var _Scene_Boot_isReady = Scene_Boot.prototype.isReady;
  Scene_Boot.prototype.isReady = function () {
    var ready = _Scene_Boot_isReady.apply(this, arguments);
    if (!ready) {
      return false;
    }
    if (!this._skins) {
      this.loadAllWindowSkin();
    }
    return this._skins.every(function (bitmap) {
      return bitmap.isReady();
    });
  };

  Scene_Boot.prototype.loadAllWindowSkin = function () {
    this._skins = param.windowImageInfo
      .filter(function (win) {
        return !!win.WindowSkin;
      })
      .map(function (win) {
        return ImageManager.loadSystem(win.WindowSkin);
      });
  };

  //=============================================================================
  // Sprite_WindowBackImage
  //  ウィンドウ背景画像のスプライトです。
  //=============================================================================
  function Sprite_WindowBackImage() {
    this.initialize.apply(this, arguments);
  }

  Sprite_WindowBackImage.prototype = Object.create(Sprite.prototype);
  Sprite_WindowBackImage.prototype.constructor = Sprite_WindowBackImage;

  Sprite_WindowBackImage.prototype.initialize = function (bitmap) {
    Sprite.prototype.initialize.call(this);
    this.bitmap = bitmap;
    this.anchor.x = 0.5;
    this.anchor.y = 0.5;
  };
})();


code:
//=============================================================================
// Yanfly Engine Plugins - Battle Artificial Intelligence Core
// YEP_BattleAICore.js
//=============================================================================

var Imported = Imported || {};
Imported.YEP_BattleAICore = true;

var Yanfly = Yanfly || {};
Yanfly.CoreAI = Yanfly.CoreAI || {};
Yanfly.CoreAI.version = 1.13;

//=============================================================================
/*:
 * @plugindesc v1.13 This plugin allows you to structure battle A.I.
 * patterns with more control.
 * @author Yanfly Engine Plugins
 *
 * @param Dynamic Actions
 * @type boolean
 * @on YES
 * @off NO
 * @desc If enabled, enemy actions are decided on the spot instead
 * of at the start of turn.   NO - false     YES - true
 * @default true
 *
 * @param Dynamic Turn Count
 * @type boolean
 * @on YES
 * @off NO
 * @desc Decide if the turn count dynamic is counted for earlier or later.
 * true - Current Turn + 1   false - Current Turn
 * @default false
 *
 * @param Element Testing
 * @type boolean
 * @on YES
 * @off NO
 * @desc If enabled, enemies will test actors on their elements by
 * setting them to match first.   NO - false     YES - true
 * @default true
 *
 * @param Default AI Level
 * @type number
 * @min 0
 * @max 100
 * @desc This is the default AI level of all enemies.
 * Level 0: Very Random     Level 100: Very Strict
 * @default 80
 *
 * @help
 * ============================================================================
 * Introduction
 * ============================================================================
 *
 * RPG Maker MV's default enemy AI is a bit lackluster even if you managed to
 * have it based completely on the rates and switches. There is no way to
 * control the way the enemy chooses targets by default, nor are the conditions
 * imposed by the default editor enough to satisfy the majority of checks. This
 * plugin enables you to set a priority list of conditions, actions, and the
 * targets selected for the enemy to go through before making a decision on how
 * to participate in battle.
 *
 * These conditions contain all of the default editor's conditions plus more,
 * such as determining the parameter values of a target, whether or not a state
 * exists on a target, the target's elemental weakness (or resistances), and
 * more before deciding an action. Furthermore, you can set an AI level for the
 * enemies to make them more consistent in the way they go about fighting your
 * players or more random in the way the enemies treat the priority list, too.
 *
 * ============================================================================
 * Parameters
 * ============================================================================
 *
 * Dynamic Actions
 * By default, the enemy's actions are determined at the start of the turn.
 * While this works in its own right, enabling Dynamic Actions allow enemies
 * to make a decision when the enemy's turn comes up instead. This prompts
 * enemies to be more flexible and to appear more intelligent in battle, thus,
 * giving your players a bit more of a challenge.
 *
 * Element Testing
 * If this is disabled, enemies will automatically know the elemental weakness,
 * resistance, etc. about all actors. If enabled, enemies will need to test out
 * the skills on various actors first before making a decision. Until the enemy
 * learns about the actor's elemental rates, the enemy is always willing to try
 * using the skill on the target actor. However, if the skill itself does not
 * possess an element, then no information will be registered. All elemental
 * data is reset at the start of each battle for all enemy parties.
 *
 * Default AI Level
 * Not all enemies are intelligent. In fact, some of them are dumb or random.
 * Setting the AI Level of a foe at a low number means the foe is more random
 * while a higher AI Level foe is more consistent. How the AI Level works is,
 * a random number will be checked from 0 to 99. If that enemy's AI Level is
 * higher than that number, that action is checked to see if the condition is
 * fulfilled or not. If the AI Level is lower than that number, the condition
 * is automatically deemed false and continues on to the next action. The check
 * is ran each time a new action is checked upon. This random factor is only
 * applied to <AI Priority> lists and do not apply to default actions.
 *
 * ============================================================================
 * Enemy AI Level
 * ============================================================================
 *
 * Enemy AI levels do not determine how difficult they are. Instead, they
 * determine how strictly they will follow the <AI Priority> lists. An AI Level
 * of 80 means it has an 80% chance of following the prioritized action on the
 * AI Priority list before moving onto the next one where there will be another
 * 80% chance and so on. If the AI level is lower, the chance is lower, making
 * the AI to be more random.
 *
 * Enemy Notetag:
 *   <AI Level: x>
 *   Sets the enemy's AI level to x. The lower x, the more random the enemy.
 *   The higher for x, the more strict the enemy is about following the AI
 *   Priority list found in its notebox, too.
 *
 * ============================================================================
 * Enemy AI Priority
 * ============================================================================
 *
 * If an enemy has an AI Priority list, the enemy will go down that list from
 * top to bottom (giving the actions at the top more priority than the ones at
 * the bottom) looking for any actions whose conditions are fulfilled. If that
 * condition is fulfilled, then that action will be the action the enemy will
 * partake in.
 *
 * To set up a Priority List for the enemy, you must place inside the enemy's
 * notebox notetags that match the following format:
 *
 *   <AI Priority>                      <AI Priority>
 *    condition: SKILL x, target   or    condition: skill name, target
 *    condition: SKILL x, target         condition: skill name, target
 *   </AI Priority>                     </AI Priority>
 *
 * Any number of conditions and skills can be placed in between the two
 * <AI Priority> tags. You can choose to use skill ID's or the skill names.
 * However, if you use the skill names, keep in mind that it is not case
 * sensitive and if any skills in your database have matching names, the skill
 * with the larger skill ID will be the action used.
 *
 * ============================================================================
 * Conditions
 * ============================================================================
 *
 * The following is a list of ways you can format your conditions for the enemy
 * to choose the right skill. In addition to deciding whether or not the skill
 * will be used, the condition also selects the enemy target. The following
 * list will tell you how the conditions are met and what targets will be
 * selected for battle.
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * ALWAYS
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This condition will always be fulfilled. The valid target group is all
 * targets within scope.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Always: Skill 10, Lowest HP%
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * ELEMENT X case
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This allows you to match the element rate of element X (use either a number
 * or the name of the element in place of 'X') to see whether or not the
 * conditions for the action are fulfilled. Replace 'case' with 'Neutral' for
 * normal element rate (under 110% and above 90%), 'Weakness' for anything
 * above 100% element rate, 'Resistant' for below 100% element rate, 'Null' for
 * 0% element rate, and 'Absorb' for below 0% element rate. Valid targets will
 * be those with the matching element rates.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Element Fire Weakness: Fireball, Lowest HP%
 *            Element Water Resistant: Water Cancel, Highest MAT
 *            Element 4 Null: Earthquake, Lowest MDF
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * EVAL eval
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This allows you to use any kind of code to check and fulfill a condition.
 * This condition uses all alive members of the skill's scope as valid targets.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Eval user.name() === 'Bat A': Skill 10, Highest HP%
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * group ALIVE MEMBERS eval
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Replace 'group' with either 'party' for the player's party or 'troop' for
 * the enemy party. This runs the number of party alive members or troop alive
 * members in a check to see if the conditions can be fulfilled.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Party Alive Members > 2: Skill 10, Lowest HP%
 *            Troop Alive Members <= 4: Skill 11, Highest HP%
 *            Troop Alive Members === $gameVariables.value(3): Skill 12, Random
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * group DEAD MEMBERS eval
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Replace 'group' with either 'party' for the player's party or 'troop' for
 * the enemy party. This runs the number of party dead members or troop dead
 * members in a check to see if the conditions can be fulfilled.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Party Dead Members > 2: Undead, Highest ATK
 *            Troop Dead Members <= 4: Life, Highest ATK
 *            Troop Dead Members === $gameVariables.value(3): Skill 12, Random
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * stat PARAM eval
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Replace 'stat' with either 'atk', 'def', 'mat', 'mdf', 'agi', 'luk',
 * 'maxhp', 'maxmp', 'hp', 'mp', 'hp%', 'mp%', or 'level' to run it in a
 * condition check again to see if the action gets passed. The group that it
 * checks will be based on the skill's scope. If the skill targets foes, then
 * all foes will take a check to see if they fulfill the conditions. Likewise
 * for party members if the skill is for allies. The valid targets will be
 * those who pass the condition check.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   HP% param <= 50%: Heal, Lowest HP%
 *            MP param > 90: Mana Drain, Highest MP
 *            ATK param > user.atk: Power Break, Highest ATK
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * type PARTY LEVEL eval
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Replace 'type' with either 'highest', 'lowest', or 'average' to get the
 * respective party level for the skill's scope. This will reference the entire
 * party's level. If this condition is fulfilled, all targets would become
 * valid targets.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Highest Party Level > 10: Skill 10, Lowest MP%
 *            Lowest Party Level < 12: Skill 11, Lowest HP%
 *            Average Party Level > 15: Skill 12, Highest HP%
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * RANDOM x%
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This will make the condition based on a random x percent chance. This
 * condition allows all possible targets to be valid for targeting.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Random 50%: Skill 10, Lowest HP%
 *            Random 75%: Skill 11, Highest HP%
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * STATE === state x
 * STATE === state name
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This will detect if the target scope has state x (or state name if you use
 * that instead). If the target does, that target is added into the pool of
 * valid targets. Any targets not affected by the state will be ignored.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   State === State 5: DeBlind, Highest ATK
 *            State === Knockout: Life, Random
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * STATE !== state x
 * STATE !== state name
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This will detect if the target scope does not have state x (or state name if
 * you use that instead). If the target doesn't, that target is added into the
 * pool of valid targets. Any targets affected by the state will be ignored.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   State !== State 12: Haste, Random
 *            State !== Courage: Cowardice, Highest ATK
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * SWITCH X case
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Replace 'x' with the ID of the switch you wish to check. Replace 'case' with
 * either 'on' or 'off' (you may also use 'true' or 'false'). If the switch
 * matches the case, the condition is fulfilled and all skill targets become
 * valid targets.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Switch 5 On: Skill 10, Lowest HP%
 *            Switch 6 Off: Skill 11, Highest HP%
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * TURN eval
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This will make the condition based on the turn count to be fulfilled by an
 * eval statement. This condition allows all possible targets to be valid for
 * targeting.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Turn > 3: Skill 10, Lowest hp%
 *            Turn === 4: Skill 11, Highest hp%
 *            Turn <= $gameVariables.value(2): Skill 12, Random
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * USER stat PARAM eval
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Replace 'stat' with either 'atk', 'def', 'mat', 'mdf', 'agi', 'luk',
 * 'maxhp', 'maxmp', 'hp', 'mp', 'hp%', 'mp%', or 'level' to run it in a
 * condition check again to see if the action gets passed. If the user's param
 * matches the conditions, the check is fulfilled.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   User HP% param <= 50%: Heal, Lowest HP%
 *            User MP param > 90: Mana Drain, Highest MP
 *            User ATK param > user.atk: Power Break, Highest ATK
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * VARIABLE X eval
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This will call forth the value of variable 'x' to partake in an eval
 * comparison to see if the condition is fulfilled. If it is, all skill targets
 * become valid targets.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Variable 3 > 10: Skill 10, Lowest HP%
 *            Variable 5 <= 100: Skill 11, Highest HP%
 *            Variable 2 === user.atk: Skill 12
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * ============================================================================
 * Multiple Conditions
 * ============================================================================
 *
 * As of the version 1.11 update, the Battle A.I. Core is now able to support
 * multiple conditions. Setting up multiple conditions is relatively simple to
 * do and still follows the 'condition: SKILL x, target' format.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 * To add multiple conditions, simply insert a +++ between each condition like
 * the following examples:
 *
 *     Switch 1 on +++ Switch 2 on: Fire, Lowest HP%
 *     Turn 3 > 1 +++ Variable 5 <= 100 +++ Switch 3 on: Ice, Lowest HP%
 *     Random 50% +++ Highest Party Level > 50: Thunder, Highest HP%
 *
 * In the above examples, all the conditions must be met in order for the
 * selected skills to be considered for use.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 * For conditions that have strict targeting groups, the targeting group will
 * end up becoming the combination of all of the strict targeting groups. For
 * example:
 *
 *     STATE === Blind +++ STATE === Fear: Dark, Lowest HP%
 *
 * In this example, the enemy will only use the 'Dark' skill on a target that
 * is both affected by 'Blind' and 'Fear'. If there are multiple targets, then
 * the target with the lowest HP% will become the target the enemy will cast
 * the 'Dark' on.
 *
 *     STATE !== Blind +++ ATK param >= 150: Darkness, Highest ATK
 *
 * In the above example, the enemy will use the 'Darkness' skill against any
 * target that isn't blinded and has an ATK parameter of at least 150. If there
 * are multiple targets, then the enemy will first cast 'Darkness' on the
 * target with the highest ATK before casting it on a target with a lower ATK.
 *
 * ============================================================================
 * Targeting
 * ============================================================================
 *
 * Targeting is optional but can be done via a small change to the condition.
 * All you have to do is add a ',' after the skill to indicate which target in
 * the valid target group you would like to target. For example:
 *
 *             Random 50%: Fire, Highest HP%
 *
 * The condition to be met is the 50% random chance, but if it is fulfilled,
 * the target selected will be the member on the targeting scope's team with
 * the highest HP percentage. When that happens, the 'Fire' skill will be used
 * upon that target.
 *
 * If no target is specified, a random target will be selected amongst the
 * group of valid targets. Otherwise, refer to the following list:
 *
 * ----------------------------------------------------------------------------
 *      <<nothing>>       Selects a random member of the valid target group.
 *      First             Selects first member of the valid target group.
 *      User              Selects the user itself.
 *      Highest MaxHP     Selects highest MaxHP valid target.
 *      Highest HP        Selects highest HP valid target.
 *      Highest HP%       Selects highest HP% valid target. *Note1
 *      Highest MaxMP     Selects highest MaxMP valid target.
 *      Highest MP        Selects highest MP valid target.
 *      Highest MP%       Selects highest MP% valid target. *Note1
 *      Highest MaxTP     Selects highest MaxTP valid target.
 *      Highest TP        Selects highest TP valid target.
 *      Highest TP%       Selects highest TP% valid target. *Note1
 *      Highest ATK       Selects highest ATK valid target.
 *      Highest DEF       Selects highest DEF valid target.
 *      Highest MAT       Selects highest MAT valid target.
 *      Highest MDF       Selects highest MDF valid target.
 *      Highest AGI       Selects highest AGI valid target.
 *      Highest LUK       Selects highest LUK valid target.
 *      Highest Level     Selects highest Level valid target. *Note2
 *      Lowest MaxHP      Selects lowest MaxHP valid target.
 *      Lowest HP         Selects lowest HP valid target.
 *      Lowest HP%        Selects lowest HP% valid target. *Note1
 *      Lowest MaxMP      Selects lowest MaxMP valid target.
 *      Lowest MP         Selects lowest MP valid target.
 *      Lowest MP%        Selects lowest MP% valid target. *Note1
 *      Lowest MaxTP      Selects lowest MaxMP valid target.
 *      Lowest TP         Selects lowest MP valid target.
 *      Lowest TP%        Selects lowest MP% valid target. *Note1
 *      Lowest ATK        Selects lowest ATK valid target.
 *      Lowest DEF        Selects lowest DEF valid target.
 *      Lowest MAT        Selects lowest MAT valid target.
 *      Lowest MDF        Selects lowest MDF valid target.
 *      Lowest AGI        Selects lowest AGI valid target.
 *      Lowest LUK        Selects lowest LUK valid target.
 *      Lowest Level      Selects lowest Level valid target. *Note2
 *
 * Note1: This is calculated by dividing the current HP with the MaxHP or the
 * current MP with the MaxMP.
 *
 * Note2: If this is used on an enemy without a proper enemy level plugin
 * installed, this will return the player party's highest level.
 *
 * ----------------------------------------------------------------------------
 *
 * ============================================================================
 * Special Notes
 * ============================================================================
 *
 * If you are using YEP_Taunt.js, enemies will automatically not factor in
 * taunts by default. For that matter, targets can be protected by taunts
 * effects, which can effectively shut down an enemy's AI performance. However,
 * if you want some enemies to consider the taunt effects of an opponent,
 * place this notetag inside of the enemy's notebox:
 *
 *   <AI Consider Taunt>
 *
 * This will make it that when an enemy makes a decision, it will make a right
 * decision while thinking of the taunted enemies, too. You can use this for
 * smarter enemies while keep this notetag disabled for less intelligent foes.
 *
 * ============================================================================
 * Changelog
 * ============================================================================
 *
 * Version 1.13:
 * - Updated for RPG Maker MV version 1.5.0.
 *
 * Version 1.12:
 * - Added 'Dynamic Turn Count' plugin parameter for those who wish to push the
 * turn count further by 1 turn in order to adjust for Dynamic Actions. Code
 * provided by Talonos.
 *
 * Version 1.11:
 * - Adding the ability to support multiple conditions. Please Read the
 * 'Multiple Conditions' section in the help file for more details.
 *
 * Version 1.10:
 * - Lunatic Mode fail safes added.
 *
 * Version 1.09:
 * - Added 'user' to the list of valid skill targets.
 * - Added 'USER stat PARAM eval' to valid conditions.
 *
 * Version 1.08:
 * - Neutral elemental resistance is now considered to be above 90% and under
 * 110% for a better range of activation.
 * - Optimization update.
 *
 * Version 1.07:
 * - Fixed a compatibility bug that caused certain conditions to bypass taunts.
 *
 * Version 1.06:
 * - Fixed a bug that caused 'Highest TP' and 'Lowest TP' target searches to
 * crash the game.
 *
 * Version 1.05:
 * - Updated for RPG Maker MV version 1.1.0.
 *
 * Version 1.04a:
 * - Fixed a bug that would cause a crash with the None scope for skills.
 * - Switched over a function to operate in another for better optimization.
 *
 * Version 1.03:
 * - Fixed a bug that returned the wrong MP% rate.
 *
 * Version 1.02:
 * - Fixed a bug that targeted the highest parameter enemy instead of lowest.
 *
 * Version 1.01:
 * - Added 'MaxTP' and 'TP' to targets.
 * - Compatibility update with Battle Engine Core v1.19+. Turn settings are now
 * based 'AI Self Turns' if the enabled.
 *
 * Version 1.00:
 * - Finished Plugin!
 */
//=============================================================================

//=============================================================================
// Parameter Variables
//=============================================================================

Yanfly.Parameters = PluginManager.parameters("YEP_BattleAICore");
Yanfly.Param = Yanfly.Param || {};

Yanfly.Param.CoreAIDynamic = String(Yanfly.Parameters["Dynamic Actions"]);
Yanfly.Param.CoreAIDynamic = eval(Yanfly.Param.CoreAIDynamic);
Yanfly.Param.CoreAIDynTurnCnt = String(Yanfly.Parameters["Dynamic Turn Count"]);
Yanfly.Param.CoreAIDynTurnCnt = eval(Yanfly.Param.CoreAIDynTurnCnt);
Yanfly.Param.CoreAIElementTest = String(Yanfly.Parameters["Element Testing"]);
Yanfly.Param.CoreAIElementTest = eval(Yanfly.Param.CoreAIElementTest);
Yanfly.Param.CoreAIDefaultLevel = Number(Yanfly.Parameters["Default AI Level"]);

//=============================================================================
// DataManager
//=============================================================================

Yanfly.CoreAI.DataManager_isDatabaseLoaded = DataManager.isDatabaseLoaded;
DataManager.isDatabaseLoaded = function () {
  if (!Yanfly.CoreAI.DataManager_isDatabaseLoaded.call(this)) return false;
  if (!Yanfly._loaded_YEP_BattleAICore) {
    this.processCoreAINotetags1($dataEnemies);
    this.processCoreAINotetags2($dataSkills);
    this.processCoreAINotetags3($dataStates);
    this.processCoreAINotetags4($dataSystem);
    Yanfly._loaded_YEP_BattleAICore = true;
  }
  return true;
};

DataManager.processCoreAINotetags1 = function (group) {
  var note1 = /<(?:AI PRIORITY)>/i;
  var note2 = /<\/(?:AI PRIORITY)>/i;
  var note3 = /<(?:AI CONSIDER TAUNT|ai considers taunts)>/i;
  var note4 = /<(?:AI LEVEL):[ ](\d+)>/i;
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    var notedata = obj.note.split(/[\r\n]+/);

    obj.aiPattern = [];
    var aiPatternFlag = false;
    obj.aiConsiderTaunt = false;
    obj.aiLevel = Yanfly.Param.CoreAIDefaultLevel * 0.01;

    for (var i = 0; i < notedata.length; i++) {
      var line = notedata[i];
      if (line.match(note1)) {
        aiPatternFlag = true;
      } else if (line.match(note2)) {
        aiPatternFlag = false;
      } else if (aiPatternFlag) {
        obj.aiPattern.push(line);
      } else if (line.match(note3)) {
        obj.aiConsiderTaunt = true;
      } else if (line.match(note4)) {
        obj.aiLevel = parseFloat(RegExp.$1 * 0.01);
      }
    }
  }
};

DataManager.processCoreAINotetags2 = function (group) {
  if (Yanfly.SkillIdRef) return;
  Yanfly.SkillIdRef = {};
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    if (obj.name.length <= 0) continue;
    Yanfly.SkillIdRef[obj.name.toUpperCase()] = n;
  }
};

DataManager.processCoreAINotetags3 = function (group) {
  if (Yanfly.StateIdRef) return;
  Yanfly.StateIdRef = {};
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    if (obj.name.length <= 0) continue;
    Yanfly.StateIdRef[obj.name.toUpperCase()] = n;
  }
};

DataManager.processCoreAINotetags4 = function (group) {
  if (Yanfly.ElementIdRef) return;
  Yanfly.ElementIdRef = {};
  for (var i = 0; i < group.elements.length; ++i) {
    var obj = group.elements[i].toUpperCase();
    if (typeof obj === "string" && obj !== "") Yanfly.ElementIdRef[obj] = i;
  }
};

//=============================================================================
// BattleManager
//=============================================================================

if (Yanfly.Param.CoreAIDynamic) {
  Yanfly.CoreAI.BattleManager_getNextSubject = BattleManager.getNextSubject;
  BattleManager.getNextSubject = function () {
    //this.updateAIPatterns();
    var battler = Yanfly.CoreAI.BattleManager_getNextSubject.call(this);
    if (battler && battler.isEnemy()) battler.setAIPattern();
    return battler;
  };
}

BattleManager.updateAIPatterns = function () {
  $gameTroop.updateAIPatterns();
};

Yanfly.CoreAI.BattleManager_isInputting = BattleManager.isInputting;
BattleManager.isInputting = function () {
  if ($gameTemp._tauntMode) return false;
  return Yanfly.CoreAI.BattleManager_isInputting.call(this);
};

//=============================================================================
// Game_Battler
//=============================================================================

Object.defineProperty(Game_Battler.prototype, "level", {
  get: function () {
    return this.getLevel();
  },
  configurable: true,
});

if (!Game_Battler.prototype.getLevel) {
  Game_Battler.prototype.getLevel = function () {
    return $gameTroop.highestLevel();
  };
}

Game_Battler.prototype.setAIPattern = function () {
  Game_Battler.prototype.makeActions.call(this);
};

Game_Battler.prototype.aiConsiderTaunt = function () {
  return false;
};

Game_Battler.prototype.hasSkill = function (skillId) {
  return this.skills().contains($dataSkills[skillId]);
};

Game_Battler.prototype.hasState = function (stateId) {
  return this.states().contains($dataStates[stateId]);
};

Game_Battler.prototype.notState = function (stateId) {
  return !this.isStateAffected(stateId);
};

Game_Battler.prototype.aiLevel = function () {
  return Yanfly.Param.CoreAIDefaultLevel * 0.01;
};

//=============================================================================
// Game_Enemy
//=============================================================================

Game_Enemy.prototype.skills = function () {
  var skills = [];
  for (var i = 0; i < this.enemy().actions.length; ++i) {
    var skill = $dataSkills[this.enemy().actions[i].skillId];
    if (skill) skills.push(skill);
  }
  skills = AIManager.getPatternSkills(skills, this.enemy().aiPattern);
  return skills;
};

Yanfly.CoreAI.Game_Enemy_makeActions = Game_Enemy.prototype.makeActions;
Game_Enemy.prototype.makeActions = function () {
  if (this.enemy().aiPattern.length > 0) {
    this.setAIPattern();
    this.setActionState("waiting");
  } else {
    Yanfly.CoreAI.Game_Enemy_makeActions.call(this);
  }
};

Game_Enemy.prototype.aiConsiderTaunt = function () {
  if (!Imported.YEP_Taunt) return false;
  return this.enemy().aiConsiderTaunt;
};

Game_Enemy.prototype.setAIPattern = function () {
  Game_Battler.prototype.setAIPattern.call(this);
  if (this.numActions() <= 0) return;
  AIManager.setBattler(this);
  for (var i = 0; i < this.enemy().aiPattern.length; ++i) {
    if (Math.random() > this.aiLevel()) continue;
    var line = this.enemy().aiPattern[i];
    if (AIManager.isDecidedActionAI(line)) return;
  }
  Yanfly.CoreAI.Game_Enemy_makeActions.call(this);
};

Game_Enemy.prototype.aiLevel = function () {
  return this.enemy().aiLevel;
};

//=============================================================================
// Game_Unit
//=============================================================================

Game_Unit.prototype.highestLevel = function () {
  return $gameParty.highestLevel();
};

Game_Unit.prototype.lowestLevel = function () {
  return $gameParty.lowestLevel();
};

Game_Unit.prototype.averageLevel = function () {
  return $gameParty.averageLevel();
};

Yanfly.CoreAI.Game_Unit_onBattleStart = Game_Unit.prototype.onBattleStart;
Game_Unit.prototype.onBattleStart = function () {
  Yanfly.CoreAI.Game_Unit_onBattleStart.call(this);
};

Game_Unit.prototype.aiElementRateKnown = function (target, elementId) {
  return true;
};

Game_Unit.prototype.aiRegisterElementRate = function (target, elementId) {};

//=============================================================================
// Game_Party
//=============================================================================

Game_Party.prototype.lowestLevel = function () {
  return Math.min.apply(
    null,
    this.members().map(function (actor) {
      return actor.level;
    })
  );
};

Game_Party.prototype.averageLevel = function () {
  var level = 0;
  for (var i = 0; i < this.members().length; ++i) {
    var member = this.members()[i];
    if (member) level += member.level;
  }
  level /= this.members().length;
  return level;
};

//=============================================================================
// Game_Troop
//=============================================================================

Game_Troop.prototype.updateAIPatterns = function () {
  for (var i = 0; i < this.aliveMembers().length; ++i) {
    var member = this.aliveMembers()[i];
    if (member) member.setAIPattern();
  }
};

Yanfly.CoreAI.Game_Troop_setup = Game_Troop.prototype.setup;
Game_Troop.prototype.setup = function (troopId) {
  Yanfly.CoreAI.Game_Troop_setup.call(this, troopId);
  this._aiKnownElementRates = {};
};

Game_Troop.prototype.aiElementRateKnown = function (target, elementId) {
  if (target.isEnemy()) return true;
  if (!Yanfly.Param.CoreAIElementTest) return true;
  var index = target.index();
  if (this._aiKnownElementRates[index] === undefined) {
    this._aiKnownElementRates[index] = [];
  }
  return this._aiKnownElementRates[index].contains(elementId);
};

Game_Troop.prototype.aiRegisterElementRate = function (target, elementId) {
  if (!Yanfly.Param.CoreAIElementTest) return;
  var index = target.index();
  if (this._aiKnownElementRates[index] === undefined) {
    this._aiKnownElementRates[index] = [];
  }
  if (!this._aiKnownElementRates[index].contains(elementId)) {
    this._aiKnownElementRates[index].push(elementId);
  }
};

//=============================================================================
// Game_Action
//=============================================================================

Yanfly.CoreAI.Game_Action_apply = Game_Action.prototype.apply;
Game_Action.prototype.apply = function (target) {
  Yanfly.CoreAI.Game_Action_apply.call(this, target);
  this.aiRegisterElementRate(target);
};

Game_Action.prototype.aiRegisterElementRate = function (target) {
  if (this.item().damage.elementId < 0) return;
  var elementId = this.item().damage.elementId;
  if (this.subject().isActor()) {
    $gameParty.aiRegisterElementRate(target, elementId);
  } else {
    $gameTroop.aiRegisterElementRate(target, elementId);
  }
};

//=============================================================================
// AIManager
//=============================================================================

function AIManager() {
  throw new Error("This is a static class");
}

AIManager.setBattler = function (battler) {
  this._battler = battler;
  this._action = battler.action(0);
};

AIManager.battler = function () {
  return this._battler;
};

AIManager.action = function () {
  return this._action;
};

AIManager.isDecidedActionAI = function (line) {
  if (line.match(/[ ]*(.*):[ ](?:SKILL)[ ](\d+),[ ](.*)/i)) {
    this._origCondition = String(RegExp.$1);
    var condition = String(RegExp.$1);
    this._aiSkillId = parseInt(RegExp.$2);
    this._aiTarget = String(RegExp.$3);
  } else if (line.match(/[ ]*(.*):[ ](?:SKILL)[ ](\d+)/i)) {
    this._origCondition = String(RegExp.$1);
    var condition = String(RegExp.$1);
    this._aiSkillId = parseInt(RegExp.$2);
    this._aiTarget = "RANDOM";
  } else if (line.match(/[ ]*(.*):[ ](.*),[ ](.*)/i)) {
    this._origCondition = String(RegExp.$1);
    var condition = String(RegExp.$1);
    this._aiSkillId = Yanfly.SkillIdRef[String(RegExp.$2).toUpperCase()];
    this._aiTarget = String(RegExp.$3);
  } else if (line.match(/[ ]*(.*):[ ](.*)/i)) {
    this._origCondition = String(RegExp.$1);
    var condition = String(RegExp.$1);
    this._aiSkillId = Yanfly.SkillIdRef[String(RegExp.$2).toUpperCase()];
    this._aiTarget = "RANDOM";
  } else {
    return false;
  }
  if (!this.initialCheck(this._aiSkillId)) return false;
  if (!this.meetCustomAIConditions(this._aiSkillId)) return false;
  this.action().setSkill(this._aiSkillId);
  if (!this.passAllAIConditions(condition)) return false;
  return true;
};

AIManager.getPatternSkills = function (array, patterns) {
  for (var i = 0; i < patterns.length; ++i) {
    var line = patterns[i];
    if (line.match(/[ ]*(.*):[ ](?:SKILL)[ ](\d+),[ ](.*)/i)) {
      var skillId = parseInt(RegExp.$2);
    } else if (line.match(/[ ]*(.*):[ ](?:SKILL)[ ](\d+)/i)) {
      var skillId = parseInt(RegExp.$2);
    } else if (line.match(/[ ]*(.*):[ ](.*),[ ](.*)/i)) {
      var skillId = Yanfly.SkillIdRef[String(RegExp.$2).toUpperCase()];
    } else if (line.match(/[ ]*(.*):[ ](.*)/i)) {
      var skillId = Yanfly.SkillIdRef[String(RegExp.$2).toUpperCase()];
    } else {
      continue;
    }
    if ($dataSkills[skillId]) array.push($dataSkills[skillId]);
  }
  return array;
};

AIManager.initialCheck = function (skillId) {
  if (!$dataSkills[skillId]) return false;
  if (!this.hasSkill(skillId)) return false;
  return this.battler().meetsSkillConditions($dataSkills[skillId]);
};

AIManager.hasSkill = function (skillId) {
  return this.battler().hasSkill(skillId);
};

AIManager.meetCustomAIConditions = function (skillId) {
  return true;
};

AIManager.getActionGroup = function () {
  var action = this.action();
  if (Imported.YEP_X_SelectionControl) action.setSelectionFilter(true);
  if (!action) return [];
  if (action.isForUser()) {
    var group = [this.battler()];
  } else if (action.isForDeadFriend()) {
    var group = action.friendsUnit().deadMembers();
  } else if (action.isForFriend()) {
    var group = action.friendsUnit().aliveMembers();
  } else if (action.isForOpponent()) {
    if (this.battler().aiConsiderTaunt()) {
      $gameTemp._tauntMode = true;
      $gameTemp._tauntAction = action;
      var group = action.opponentsUnit().tauntMembers();
      $gameTemp._tauntMode = false;
      $gameTemp._tauntAction = undefined;
    } else {
      var group = action.opponentsUnit().aliveMembers();
    }
  } else {
    var group = [];
  }
  if (this._setActionGroup !== undefined) {
    group = Yanfly.Util.getCommonElements(this._setActionGroup, group);
  }
  this._setActionGroup = group;
  return this._setActionGroup;
};

AIManager.setActionGroup = function (group) {
  this._setActionGroup = group;
};

AIManager.setProperTarget = function (group) {
  this.setActionGroup(group);
  var action = this.action();
  var randomTarget = group[Math.floor(Math.random() * group.length)];
  if (!randomTarget) return action.setTarget(0);
  if (group.length <= 0) return action.setTarget(randomTarget.index());
  var line = this._aiTarget.toUpperCase();
  if (line.match(/FIRST/i)) {
    action.setTarget(0);
  } else if (line.match(/USER/i)) {
    var index = group.indexOf();
    action.setTarget(action.subject().index());
  } else if (line.match(/HIGHEST[ ](.*)/i)) {
    var param = this.getParamId(String(RegExp.$1));
    if (param < 0) return action.setTarget(randomTarget.index());
    if (param === 8) return this.setHighestHpFlatTarget(group);
    if (param === 9) return this.setHighestMpFlatTarget(group);
    if (param === 10) return this.setHighestHpRateTarget(group);
    if (param === 11) return this.setHighestMpRateTarget(group);
    if (param === 12) return this.setHighestLevelTarget(group);
    if (param === 13) return this.setHighestMaxTpTarget(group);
    if (param === 14) return this.setHighestTpTarget(group);
    if (param > 15) return action.setTarget(randomTarget.index());
    this.setHighestParamTarget(group, param);
  } else if (line.match(/LOWEST[ ](.*)/i)) {
    var param = this.getParamId(String(RegExp.$1));
    if (param < 0) return action.setTarget(randomTarget.index());
    if (param === 8) return this.setLowestHpFlatTarget(group);
    if (param === 9) return this.setLowestMpFlatTarget(group);
    if (param === 10) return this.setLowestHpRateTarget(group);
    if (param === 11) return this.setLowestMpRateTarget(group);
    if (param === 12) return this.setLowestLevelTarget(group);
    if (param === 13) return this.setLowestMaxTpTarget(group);
    if (param === 14) return this.setLowestTpTarget(group);
    if (param > 15) return action.setTarget(randomTarget.index());
    this.setLowestParamTarget(group, param);
  } else {
    this.setRandomTarget(group);
  }
};

AIManager.getParamId = function (string) {
  string = string.toUpperCase();
  switch (string) {
    case "MAXHP":
    case "MAX HP":
      return 0;
      break;
    case "MAXMP":
    case "MAX MP":
    case "MAXSP":
    case "MAX SP":
      return 1;
      break;
    case "ATK":
    case "STR":
      return 2;
      break;
    case "DEF":
      return 3;
      break;
    case "MAT":
    case "INT":
    case "SPI":
      return 4;
      break;
    case "MDF":
    case "RES":
      return 5;
      break;
    case "AGI":
    case "SPD":
      return 6;
      break;
    case "LUK":
      return 7;
      break;
    case "HP":
      return 8;
      break;
    case "MP":
    case "SP":
      return 9;
      break;
    case "HP%":
      return 10;
      break;
    case "MP%":
    case "SP%":
      return 11;
      break;
    case "LEVEL":
    case "LV":
    case "LVL":
      return 12;
      break;
    case "MAXTP":
      return 13;
      break;
    case "TP":
      return 14;
      break;
  }
  return -1;
};

AIManager.setHighestHpRateTarget = function (group) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.hp / target.mhp > maintarget.hp / maintarget.mhp) {
      maintarget = target;
    }
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setHighestHpFlatTarget = function (group) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.hp > maintarget.hp) maintarget = target;
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setLowestHpRateTarget = function (group) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.hp / target.mhp < maintarget.hp / maintarget.mhp) {
      maintarget = target;
    }
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setLowestHpFlatTarget = function (group) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.hp < maintarget.hp) maintarget = target;
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setHighestMpRateTarget = function (group) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.mp / target.mmp > maintarget.mp / maintarget.mmp) {
      maintarget = target;
    }
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setHighestMpFlatTarget = function (group) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.mp > maintarget.mp) maintarget = target;
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setLowestMpRateTarget = function (group) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.mp / target.mmp < maintarget.mp / maintarget.mmp) {
      maintarget = target;
    }
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setLowestMpFlatTarget = function (group) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.mp < maintarget.mp) maintarget = target;
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setHighestParamTarget = function (group, id) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.param(id) > maintarget.param(id)) maintarget = target;
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setLowestParamTarget = function (group, id) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.param(id) < maintarget.param(id)) maintarget = target;
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setHighestLevelTarget = function (group, id) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.level > maintarget.level) maintarget = target;
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setLowestLevelTarget = function (group, id) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.level < maintarget.level) maintarget = target;
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setHighestMaxTpTarget = function (group, id) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.level > maintarget.maxTp()) maintarget = target;
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setLowestMaxTpTarget = function (group, id) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.level < maintarget.maxTp()) maintarget = target;
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setHighestTpTarget = function (group, id) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.level > maintarget.tp) maintarget = target;
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setLowestTpTarget = function (group, id) {
  var maintarget = group[Math.floor(Math.random() * group.length)];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (target.level < maintarget.tp) maintarget = target;
  }
  this.action().setTarget(maintarget.index());
};

AIManager.setRandomTarget = function (group) {
  var target = group[Math.floor(Math.random() * group.length)];
  this.action().setTarget(target.index());
};

AIManager.convertIntegerPercent = function (n) {
  n *= 0.01;
  return String(n);
};

AIManager.elementRateMatch = function (target, elementId, type) {
  var rate = target.elementRate(elementId).toFixed(2);
  if (this.battler().isActor()) {
    if (!$gameParty.aiElementRateKnown(target, elementId)) return true;
  } else {
    if (!$gameTroop.aiElementRateKnown(target, elementId)) return true;
  }
  if (["NEUTRAL", "NORMAL"].contains(type)) {
    return rate >= 0.9 && rate <= 1.1;
  } else if (["WEAK", "WEAKNESS", "VULNERABLE"].contains(type)) {
    return rate > 1.0;
  } else if (["RESIST", "RESISTANT", "STRONG"].contains(type)) {
    return rate < 1.0;
  } else if (["NULL", "CANCEL", "NO EFFECT"].contains(type)) {
    return rate === 0.0;
  } else if (["ABSORB", "HEAL"].contains(type)) {
    return rate < 0.0;
  }
  return false;
};

AIManager.passAllAIConditions = function (line) {
  this._setActionGroup = undefined;
  var conditions = line.split("+++");
  if (conditions.length <= 0) return false;
  while (conditions.length > 0) {
    var condition = conditions.shift().trim();
    if (!this.passAIConditions(condition)) return false;
  }
  return true;
};

AIManager.passAIConditions = function (line) {
  // ALWAYS
  if (line.match(/ALWAYS/i)) {
    return this.conditionAlways();
  }
  // ELEMENT
  if (line.match(/ELEMENT[ ](.*)/i)) {
    return this.conditionElement();
  }
  // EVAL
  if (line.match(/EVAL[ ](.*)/i)) {
    var condition = String(RegExp.$1);
    return this.conditionEval(condition);
  }
  // GROUP ALIVE MEMBERS EVAL
  if (line.match(/(.*)[ ]ALIVE[ ]MEMBERS[ ](.*)/i)) {
    var members = String(RegExp.$1);
    var condition = String(RegExp.$2);
    return this.conditionGroupAlive(members, condition);
  }
  // GROUP DEAD MEMBERS EVAL
  if (line.match(/(.*)[ ]DEAD[ ]MEMBERS[ ](.*)/i)) {
    var members = String(RegExp.$1);
    var condition = String(RegExp.$2);
    return this.conditionGroupDead(members, condition);
  }
  // USER PARAM EVAL
  if (line.match(/USER[ ](.*)[ ]PARAM[ ](.*)/i)) {
    var paramId = this.getParamId(String(RegExp.$1));
    var condition = String(RegExp.$2);
    return this.conditionUserParamEval(paramId, condition);
  }
  // PARAM EVAL
  if (line.match(/(.*)[ ]PARAM[ ](.*)/i)) {
    var paramId = this.getParamId(String(RegExp.$1));
    var condition = String(RegExp.$2);
    return this.conditionParamEval(paramId, condition);
  }
  // PARTY LEVEL
  if (line.match(/(.*)[ ]PARTY[ ]LEVEL[ ](.*)/i)) {
    var type = String(RegExp.$1);
    var condition = String(RegExp.$2);
    return this.conditionPartyLevel(type, condition);
  }
  // RANDOM x%
  if (line.match(/RANDOM[ ](\d+)([%％])/i)) {
    return this.conditionRandom(parseFloat(RegExp.$1 * 0.01));
  }
  // STATE === X
  if (line.match(/STATE[ ]===[ ](.*)/i)) {
    return this.conditionStateHas(String(RegExp.$1));
  }
  // STATE !== X
  if (line.match(/STATE[ ]!==[ ](.*)/i)) {
    return this.conditionStateNot(String(RegExp.$1));
  }
  // SWITCH X case
  if (line.match(/SWITCH[ ](\d+)[ ](.*)/i)) {
    var switchId = parseInt(RegExp.$1);
    var value = String(RegExp.$2);
    return this.conditionSwitch(switchId, value);
  }
  // TURN EVAL
  if (line.match(/TURN[ ](.*)/i)) {
    return this.conditionTurnCount(String(RegExp.$1));
  }
  // VARIABLE X eval
  if (line.match(/VARIABLE[ ](\d+)[ ](.*)/i)) {
    var variableId = parseInt(RegExp.$1);
    var condition = String(RegExp.$2);
    return this.conditionVariable(variableId, condition);
  }
  return false;
};

AIManager.conditionAlways = function () {
  var group = this.getActionGroup();
  this.setProperTarget(group);
  return true;
};

AIManager.conditionElement = function () {
  var line = this._origCondition;
  if (line.match(/ELEMENT[ ](\d+)[ ](.*)/i)) {
    var elementId = parseInt(RegExp.$1);
    var type = String(RegExp.$2).toUpperCase();
  } else if (line.match(/ELEMENT[ ](.*)[ ](.*)/i)) {
    var elementId = Yanfly.ElementIdRef[String(RegExp.$1).toUpperCase()];
    var type = String(RegExp.$2).toUpperCase();
  } else {
    return false;
  }
  var group = this.getActionGroup();
  var validTargets = [];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (!target) continue;
    if (this.elementRateMatch(target, elementId, type)) {
      validTargets.push(target);
    }
  }
  if (validTargets.length <= 0) return false;
  this.setProperTarget(validTargets);
  return true;
};

AIManager.conditionEval = function (condition) {
  var action = this.action();
  var item = action.item();
  var user = this.battler();
  var s = $gameSwitches._data;
  var v = $gameVariables._data;
  try {
    if (!eval(condition)) return false;
  } catch (e) {
    Yanfly.Util.displayError(e, condition, "A.I. EVAL ERROR");
    return false;
  }
  var group = this.getActionGroup();
  this.setProperTarget(group);
  return true;
};

AIManager.conditionGroupAlive = function (members, condition) {
  var action = this.action();
  var item = action.item();
  var user = this.battler();
  var s = $gameSwitches._data;
  var v = $gameVariables._data;
  members = members.toUpperCase();
  if (["TROOP", "TROOPS", "ENEMY", "ENEMIES"].contains(members)) {
    members = $gameTroop.aliveMembers();
  } else if (["PARTY", "PLAYER"].contains(members)) {
    members = $gameParty.aliveMembers();
  } else {
    return false;
  }
  if (members.length <= 0) return false;
  condition = "members.length " + condition;
  try {
    if (!eval(condition)) return false;
  } catch (e) {
    Yanfly.Util.displayError(e, condition, "A.I. GROUP ALIVE ERROR");
    return false;
  }
  var group = this.getActionGroup();
  this.setProperTarget(group);
  return true;
};

AIManager.conditionGroupDead = function (members, condition) {
  var action = this.action();
  var item = action.item();
  var user = this.battler();
  var s = $gameSwitches._data;
  var v = $gameVariables._data;
  members = members.toUpperCase();
  if (["TROOP", "TROOPS", "ENEMY", "ENEMIES"].contains(members)) {
    members = $gameTroop.deadMembers();
  } else if (["PARTY", "PLAYER"].contains(members)) {
    members = $gameParty.deadMembers();
  } else {
    return false;
  }
  if (members.length <= 0) return false;
  condition = "members.length " + condition;
  try {
    if (!eval(condition)) return false;
  } catch (e) {
    Yanfly.Util.displayError(e, condition, "A.I. GROUP DEAD ERROR");
    return false;
  }
  var group = this.getActionGroup();
  this.setProperTarget(group);
  return true;
};

AIManager.conditionPartyLevel = function (type, condition) {
  if (type.match(/HIGHEST/i)) {
    condition = ".highestLevel() " + condition;
  } else if (type.match(/LOWEST/i)) {
    condition = ".lowestLevel() " + condition;
  } else if (type.match(/AVERAGE/i)) {
    condition = ".averageLevel() " + condition;
  } else {
    return false;
  }
  var action = this.action();
  var item = action.item();
  var user = this.battler();
  var s = $gameSwitches._data;
  var v = $gameVariables._data;
  if (action.isForFriend()) {
    condition = "action.friendsUnit()" + condition;
  } else if (action.isForOpponent()) {
    condition = "action.opponentsUnit()" + condition;
  }
  try {
    if (!eval(condition)) return false;
  } catch (e) {
    Yanfly.Util.displayError(e, condition, "A.I. PARTY LEVEL ERROR");
    return false;
  }
  var group = this.getActionGroup();
  this.setProperTarget(group);
  return true;
};

AIManager.conditionUserParamEval = function (paramId, condition) {
  var action = this.action();
  var item = action.item();
  var user = this.battler();
  var s = $gameSwitches._data;
  var v = $gameVariables._data;
  condition = condition.replace(
    /(\d+)([%％])/g,
    function () {
      return this.convertIntegerPercent(parseInt(arguments[1]));
    }.bind(this)
  );
  if (paramId < 0) return false;
  if (paramId >= 0 && paramId <= 7) {
    condition = "user.param(paramId) " + condition;
  } else if (paramId === 8) {
    condition = "user.hp " + condition;
  } else if (paramId === 9) {
    condition = "user.mp " + condition;
  } else if (paramId === 10) {
    condition = "user.hp / user.mhp " + condition;
  } else if (paramId === 11) {
    condition = "user.mp / user.mmp " + condition;
  } else if (paramId === 12) {
    condition = "user.level " + condition;
  }
  var group = this.getActionGroup();
  var validTargets = [];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (!target) continue;
    try {
      if (eval(condition)) validTargets.push(target);
    } catch (e) {
      Yanfly.Util.displayError(e, condition, "A.I. USER PARAM ERROR");
    }
  }
  if (validTargets.length <= 0) return false;
  this.setProperTarget(validTargets);
  return true;
};

AIManager.conditionParamEval = function (paramId, condition) {
  var action = this.action();
  var item = action.item();
  var user = this.battler();
  var s = $gameSwitches._data;
  var v = $gameVariables._data;
  condition = condition.replace(
    /(\d+)([%％])/g,
    function () {
      return this.convertIntegerPercent(parseInt(arguments[1]));
    }.bind(this)
  );
  if (paramId < 0) return false;
  if (paramId >= 0 && paramId <= 7) {
    condition = "target.param(paramId) " + condition;
  } else if (paramId === 8) {
    condition = "target.hp " + condition;
  } else if (paramId === 9) {
    condition = "target.mp " + condition;
  } else if (paramId === 10) {
    condition = "target.hp / target.mhp " + condition;
  } else if (paramId === 11) {
    condition = "target.mp / target.mmp " + condition;
  } else if (paramId === 12) {
    condition = "target.level " + condition;
  }
  var group = this.getActionGroup();
  var validTargets = [];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (!target) continue;
    try {
      if (eval(condition)) validTargets.push(target);
    } catch (e) {
      Yanfly.Util.displayError(e, condition, "A.I. PARAM ERROR");
    }
  }
  if (validTargets.length <= 0) return false;
  this.setProperTarget(validTargets);
  return true;
};

AIManager.conditionRandom = function (rate) {
  if (Math.random() >= rate) return false;
  var group = this.getActionGroup();
  this.setProperTarget(group);
  return true;
};

AIManager.conditionStateHas = function (condition) {
  if (condition.match(/STATE[ ](\d+)/i)) {
    var stateId = parseInt(RegExp.$1);
  } else {
    var stateId = Yanfly.StateIdRef[condition.toUpperCase()];
    if (!stateId) return false;
  }
  if (!$dataStates[stateId]) return false;
  var group = this.getActionGroup();
  var validTargets = [];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (!target) continue;
    if (target.hasState(stateId)) validTargets.push(target);
  }
  if (validTargets.length <= 0) return false;
  this.setProperTarget(validTargets);
  return true;
};

AIManager.conditionStateNot = function (condition) {
  if (condition.match(/STATE[ ](\d+)/i)) {
    var stateId = parseInt(RegExp.$1);
  } else {
    var stateId = Yanfly.StateIdRef[condition.toUpperCase()];
    if (!stateId) return false;
  }
  if (!$dataStates[stateId]) return false;
  var group = this.getActionGroup();
  var validTargets = [];
  for (var i = 0; i < group.length; ++i) {
    var target = group[i];
    if (!target) continue;
    if (target.notState(stateId)) validTargets.push(target);
  }
  if (validTargets.length <= 0) return false;
  this.setProperTarget(validTargets);
  return true;
};

AIManager.conditionSwitch = function (switchId, value) {
  if (["ON", "TRUE", "YES"].contains(value.toUpperCase())) {
    value = true;
  } else if (["OFF", "FALSE", "NO"].contains(value.toUpperCase())) {
    value = false;
  } else {
    return false;
  }
  if ($gameSwitches.value(switchId) !== value) return false;
  var group = this.getActionGroup();
  this.setProperTarget(group);
  return true;
};

if (!Yanfly.Param.CoreAIDynTurnCnt) {
  AIManager.conditionTurnCount = function (condition) {
    var action = this.action();
    var item = action.item();
    var user = this.battler();
    var s = $gameSwitches._data;
    var v = $gameVariables._data;
    if (Imported.YEP_BattleEngineCore) {
      condition = "user.turnCount() " + condition;
    } else {
      condition = "$gameTroop.turnCount() " + condition;
    }
    try {
      if (!eval(condition)) return false;
    } catch (e) {
      Yanfly.Util.displayError(e, condition, "A.I. TURN COUNT ERROR");
      return false;
    }
    var group = this.getActionGroup();
    this.setProperTarget(group);
    return true;
  };
} else {
  // Alternative provided by Talonos

  AIManager.conditionTurnCount = function (condition) {
    var action = this.action();
    var item = action.item();
    var user = this.battler();
    var s = $gameSwitches._data;
    var v = $gameVariables._data;
    if (Imported.YEP_BattleEngineCore) {
      if (BattleManager._phase === "input" && BattleManager.isTurnBased()) {
        condition = "(user.turnCount() + 1) " + condition;
      } else {
        condition = "user.turnCount() " + condition;
      }
    } else {
      if (BattleManager._phase === "input") {
        condition = "($gameTroop.turnCount() + 1) " + condition;
      } else {
        condition = "$gameTroop.turnCount() " + condition;
      }
    }
    try {
      if (!eval(condition)) return false;
    } catch (e) {
      Yanfly.Util.displayError(e, condition, "A.I. TURN COUNT ERROR");
      return false;
    }
    var group = this.getActionGroup();
    this.setProperTarget(group);
    return true;
  };
} // Yanfly.Param.CoreAIDynamic

AIManager.conditionVariable = function (variableId, condition) {
  var action = this.action();
  var item = action.item();
  var user = this.battler();
  var s = $gameSwitches._data;
  var v = $gameVariables._data;
  condition = "$gameVariables.value(" + variableId + ") " + condition;
  try {
    if (!eval(condition)) return false;
  } catch (e) {
    Yanfly.Util.displayError(e, condition, "A.I. VARIABLE ERROR");
    return false;
  }
  var group = this.getActionGroup();
  this.setProperTarget(group);
  return true;
};

//=============================================================================
// Utilities
//=============================================================================

Yanfly.Util = Yanfly.Util || {};

Yanfly.Util.displayError = function (e, code, message) {
  console.log(message);
  console.log(code || "NON-EXISTENT");
  console.error(e);
  if (Utils.isNwjs() && Utils.isOptionValid("test")) {
    if (!require("nw.gui").Window.get().isDevToolsOpen()) {
      require("nw.gui").Window.get().showDevTools();
    }
  }
};

Yanfly.Util.getCommonElements = function (array1, array2) {
  var elements = [];
  var length = array1.length;
  for (var i = 0; i < length; ++i) {
    var element = array1[i];
    if (array2.contains(element)) elements.push(element);
  }
  return elements;
};

//=============================================================================
// End of File
//=============================================================================