#==============================================================================
# ** TDS Custom Action AI (Skill Use AI)
#    Ver: 1.4 BETA
#------------------------------------------------------------------------------
#  * Description:
#  This script allows you to set requirements for enemies to use skills. Such
#  as its target not having a certain state or its HP  being above a certain
#  value
#------------------------------------------------------------------------------
#  * Features: 
#  Adds a simple AI to enemies to decide which skill to use.
#------------------------------------------------------------------------------
#  * Instructions:
#  To add AI to a skill, put this in the enemy's notebox:
#
#    <Skill_Use_AI: ID>
#     AI_TAGS
#    </skill_use_AI>
#
#    ID = Is the ID of the skill to apply the AI to.
#    AI_TAGS = These are the tags that determine how the AI for the skill will
#              work. A list is below.
#
#  Examples:
#
#  Here are some examples of AI based on certain scenarios. (The default RMVXACE
#  is used for them)
#
#  Scenario:
#    The enemy has the skill "Heal II" (ID 27) and only wants to use it on 
#    targets that do not have full HP. It also wants to select whoever has
#    the least HP to heal them.
#
#    Here is how it would translate into tags.
#
#    <Skill_Use_AI: 27>
#      Remove_If: (t.hp_rate * 100) == 100
#      Sort_by: t.hp_rate
#      Select_First
#    </Skill_Use_AI>
#
#
#  Scenario:
#    The enemy has the skill "Poison" (ID 27) and only wants to use it on 
#    targets that are not already afflicted with the state Poison (ID 2). It
#    also wants to select whoever has the highest HP.
#   
#    Here is how it would translate into tags.
#
#    <Skill_Use_AI: 35>
#      Remove_If: t.state?(2)
#      Sort_by: t.hp
#      Select_First
#    </Skill_Use_AI>
#
#------------------------------------------------------------------------------
#  * AI Commands List:
#------------------------------------------------------------------------------
#  Condition Shorcuts:
#   Some tags use conditions as part of their process and these are some
#   shorcuts to relevant objects such as targets, user, switches and variables.
#------------------------------------------------------------------------------
#     t = Target within a block of code of targets array.
#     u = Action AI user. (Battler that is processing skill AI)
#     s = Switches. (s[1] would be the same as Switch ID 1 or $game_switches[1])
#     v = Variables. (v[1] would be the same as Variable ID 1 or $game_variables[1])
#
#     Examples:
#       t.hp (This would get you the HP of a target)
#       s[1] == true (This would be true if Switch ID 1 is ON)
#       v[10] < 100 (This would be true if the value of Variable ID 1 is less than 100)
#
#------------------------------------------------------------------------------
#  Requirements:
#    Requirement tags are required parameters from the user in order to use the
#    skill.
#------------------------------------------------------------------------------
#    Required_Turn: Turn_A, Turn_B
#    Req_HP/MP: Min, Max
#      ^ HP/MP Should be replaced with either.
#      ^ Min and Max are the ranges the HP or MP must be between. (As Float values)
#      ^ Example: Req_HP: 0.5, 1.0
#
#    Required_State: State_ID
#    Required_Party_Level: Level
#    Required_Switch: Switch_ID, State
#      ^ State is the state of the switch to check (ON or OFF)
#
#    Require: Condition
#      ^ Require that Condition code is true.
#
#    TRequire(_ALL):
#      ^ Targets must meet required conditions.
#      ^ If _All is part of the tag then all targets must meet the condition.
#      ^ Example: TRequire: t.hp > 100 or TRequire_All: t.hp < 100
#------------------------------------------------------------------------------
#  Remove:
#    Remove tags remove targets if the tag requirements are met.
#------------------------------------------------------------------------------
#    TRemove_If: Condition
#      ^ Remove target if conditions code is true.
#      ^ Example: t.hp > 100
#
#------------------------------------------------------------------------------
#   Select:
#     Select tags allow you select a specific target if necessary.
#------------------------------------------------------------------------------
#    Sort_By: Condition
#      ^ Sorts targets by condition code. (Must be a comparable value)
#      ^ This tag is meant to be used with others like "Select_First/Last"
#
#    Select_(First/Last)(: Min)
#     ^ First/Last Should be replaced with either for selection.
#     ^ Min is the minimun amount of random targets to select. (Optional)
#     ^ Example: Select_First or Select_First: 2
#
#    Select_Default
#      ^ Selects amounts of targets based on the scope of the skill.
#
#    Select_If: Condition
#      ^ Select Targets if condition code is true.
#      ^ Example: t.id == 20
#
#    Select_Random(: Min)
#      ^ Min is the minimun amount of random targets to select. (Optional)
#      ^ If no value is used then the default 1 will be used.
#      ^ Example: Select_Random or Select_Random: 3
#
#    Select_Targeting_User
#      ^ Selects targets whose actions are targeting the AI user.
#
#------------------------------------------------------------------------------
#  * Notes:
#  None.
#------------------------------------------------------------------------------
# WARNING:
#
# Do not release, distribute or change my work without my expressed written 
# consent, doing so violates the terms of use of this work.
#
# If you really want to share my work please just post a link to the original
# site.
#
# * Not Knowing English or understanding these terms will not excuse you in any
#   way from the consequenses.
#==============================================================================
# * Import to Global Hash *
#==============================================================================
($imported ||= {})[:TDS_Custom_Action_AI] = true


#==============================================================================
# ** Scene_Battle
#------------------------------------------------------------------------------
#  This class performs battle screen processing.
#==============================================================================

class Scene_Battle < Scene_Base
  #--------------------------------------------------------------------------
  # * Alias Listing
  #--------------------------------------------------------------------------
  alias tds_custom_action_ai_scene_battle_execute_action       execute_action    
  #--------------------------------------------------------------------------
  # * Execute Battle Actions
  #--------------------------------------------------------------------------
  def execute_action
    # Evaluate Enemy Battle AI
    evaluate_custom_action_ai(@subject) if @subject.enemy?
    # Run Original Method
    tds_custom_action_ai_scene_battle_execute_action
  end
  #--------------------------------------------------------------------------
  # * Evaluate Custom Action Battle AI
  #--------------------------------------------------------------------------
  def evaluate_custom_action_ai(battler)
    # Return if Battler is nil or Subject is not an enemy
    return if battler.nil?
    # Create AI Targets
    @ai_targets = []
    # Selected Skill
    skill = nil
    # Get All Battler Custom Skill AI
    ai_actions = battler.all_custom_skill_ai    
    # Go Through AI Actions
    ai_actions.each {|a| 
      # Get Skill Object
      skill = $data_skills[a.at(0)]    
      # Next if Skill is not usable
      next if !battler.usable?(skill)
      # Process Battle Action AI
      break if process_battle_action_ai(battler, skill, a.at(1))       
    }    
    # Set Current Action to Selected Skill
    battler.current_action.set_skill(skill.id) if !skill.nil?     
    # Set Current Action Custom Action AI Targets
    battler.current_action.custom_action_ai_targets = @ai_targets    
  end
  #--------------------------------------------------------------------------
  # * Conver Match String value to proper Numerical
  #--------------------------------------------------------------------------
  def convert_match_value(string)
    # Return if string is not a number
    return nil if !(string =~ /^\d+/)
    # Convert String to float if it contains a period
    return string.to_f if string =~ /\./
    # Convert String to integer
    return string.to_i
  end
  #--------------------------------------------------------------------------
  # * Process Battle Action AI
  #--------------------------------------------------------------------------
  def process_battle_action_ai(battler, skill, ai_list)    
    # Clear AI Targets
    @ai_targets.clear
    # Return if AI List is empty
    return true if ai_list.empty?    
    # Get Targets Avaiable for skill
    @ai_targets = skill.for_opponent? ? battler.opponents_unit.members.dup : battler.friends_unit.members.dup
    # Delete Dead Members if Skill is not for dead friends
    @ai_targets.delete_if {|t| t.dead? and !skill.for_dead_friend?}
    # Go Through AI List
    ai_list.each {|ai|
      # Process Required, Remove and Select Action AI Tags
      process_battle_required_action_ai(battler, skill, ai) if !@ai_targets.empty?
      process_battle_remove_action_ai(battler, skill, ai)   if !@ai_targets.empty?
      process_battle_select_action_ai(battler, skill, ai)   if !@ai_targets.empty?
      # Break if Targets is empty
      break if @ai_targets.empty?      
    }    
    # Return if AI Targets is empty
    return false if @ai_targets.empty?
    # Return true
    return true
  end
  #--------------------------------------------------------------------------
  # * [Required] Process Battle Action AI
  #--------------------------------------------------------------------------
  def process_battle_required_action_ai(battler, skill, ai)   
    # Set Object Shorcuts
    v = $game_variables ; s = $game_switches ; u = battler
    # AI Case
    case ai
    # Require User conditions are met. (Condition)
    when /\bRequire\b: (?'condition'.+)/i
      # Get Match Information
      m = Regexp.last_match
      # Clear AI Targets (Breaking Loop)
      @ai_targets.clear if !eval(m[:condition])   
    # Require Target conditions are met for one or all targets. (Condition)
    when /\bTRequire[_]?(?'con'ALL)?: (?'condition'.+)/i
      # Get Match Information
      m = Regexp.last_match
      # Clear AI Targets (Breaking Loop)
      @ai_targets.clear if m[:con] =~ /ALL/i and !@ai_targets.all? {|t| eval(m[:condition])}      
      @ai_targets.clear if m[:con].nil? and !@ai_targets.any? {|t| eval(m[:condition])}
    # Required Turn to use skill (Turn A, Turn B)
    when /Required_Turn: (?'turn_a'[\d]+), (?'turn_b'[\d]+)/i
      # Get Match Information
      m = Regexp.last_match
      # Get Turns and A & B Parameters
      n = $game_troop.turn_count ; a = m[:turn_a].to_i ; b = m[:turn_b].to_i
      # Clear AI Targets (Breaking Loop)
      @ai_targets.clear if (b == 0 and n != a) or (b > 0 and (n < 1 or n < a or n % b != a % b))
    # Required HP/MP to use skill (Min, Max) rates
    when /Required_(?'type'HP|MP): (?'min'[\.\d]+), (?'max'[\.\d]+)/i
      # Get Match Information
      m = Regexp.last_match
      # Get Converted String Values
      min = convert_match_value(m[:min]) ; max = convert_match_value(m[:max])
      # Clear All AI Targets (Breaking the loop)
      @ai_targets.clear if m[:type] =~ /HP/i and !battler.hp_rate.between?(min, max) 
      @ai_targets.clear if m[:type] =~ /MP/i and !battler.mp_rate.between?(min, max)        
    # Required State to have to use the skill (State ID)
    when /Required_State: (?'id'[\d]+)/i
      # Get Match Information
      m = Regexp.last_match
      # Clear All AI Targets (Breaking the loop)
      @ai_targets.clear if !battler.state?(m[:id].to_i)        
    # Minimun Party Level Required to use the skill (Level)
    when /Required_Party_Level: (?'level'[\d]+)/i
      # Get Match Information
      m = Regexp.last_match
      # Clear All AI Targets (Breaking the loop)
      @ai_targets.clear if $game_party.highest_level < m[:level].to_i
    # Required Active Switch to use the skill (Switch ID, State)
    when /Required_Switch: (?'id'[\d]+), (?'state'[ON|OFF]+)/i
      # Get Match Information
      m = Regexp.last_match
      # Clear All AI Targets (Breaking the loop)
      @ai_targets.clear if m[:state] =~ /ON/i  and $game_switches[m[:id].to_i] == false
      @ai_targets.clear if m[:state] =~ /OFF/i and $game_switches[m[:id].to_i] == true
    # Require Variable conditions are met. (Condition)
    when /Required_Variable: (?'condition'.+)/i
      # Get Match Information
      m = Regexp.last_match
      # Clear AI Targets (Breaking Loop)
      @ai_targets.clear if !eval(m[:condition])      
    # Require Code conditions are met. (Condition)        
    when /Required_Code: (?'condition'.+)/
      # Get Match Information
      m = Regexp.last_match      
      # Clear AI Targets (Breaking Loop)
      @ai_targets.clear if !eval(m[:condition])      
    end      
  end
  #--------------------------------------------------------------------------
  # * [Remove] Process Battle Action AI
  #--------------------------------------------------------------------------
  def process_battle_remove_action_ai(battler, skill, ai)  
    # Set Object Shorcuts
    v = $game_variables ; s = $game_switches ; u = battler    
    # AI Case
    case ai
    # Remove Dead Targets
    when /Remove_Dead/i ; @ai_targets.delete_if {|t| t.dead?}
    # Remove Alive Targets
    when /Remove_Alive/i ; @ai_targets.delete_if {|t| t.alive?}        
    # Remove Target if conditions are met. (Condition)
    when /Remove_If: (?'condition'.+)/i
      # Get Match Information
      m = Regexp.last_match
      # Process Target Removal Condition
      @ai_targets.delete_if {|t| eval(m[:condition])}
    end
  end  
  #--------------------------------------------------------------------------
  # * [Select] Process Battle Action AI
  #--------------------------------------------------------------------------
  def process_battle_select_action_ai(battler, skill, ai)
    # Set Object Shorcuts
    v = $game_variables ; s = $game_switches ; u = battler    
    # AI Case
    case ai
    # Sort Targets Array by condition
    when /Sort_by: (?'condition'.+)/i
      # Get Match Information
      m = Regexp.last_match
      # Sort Targets Array
      @ai_targets.sort_by! {|t| eval(m[:condition])}
    # Select Targets by Default Targetting Scope (Skill Range)
    when /Select_Default/i
      # Select User As Target if skill is for user
      @ai_targets = [battler] if skill.for_user?
      # If Skill is for one target (Select one randomly)
      @ai_targets = [@ai_targets.sample] if skill.for_one?
      # Select Random Targets
      @ai_targets = [@ai_targets.sample(skill.number_of_targets)].flatten if skill.for_random?
    # Select First or Last Targets in the Targets Array (Min Amount to Select)
    when /Select_(?'type'First|Last)[:]?(?'min'\s\d+)?/i
      # Get Match Information
      m = Regexp.last_match
      # Get First Targets from Target Array
      @ai_targets = @ai_targets.take([m[:min].to_i, 1].max) if m[:type] =~ /First/i
      # Get Last Targets from Target Array
      @ai_targets = @ai_targets.reverse.take([m[:min].to_i, 1].max) if m[:type] =~ /Last/i
    # Select Target if conditions are met. (Condition)
    when /Select_If: (?'condition'.+)/i
      # Get Match Information
      m = Regexp.last_match
      # Process Target Selection Condition
      @ai_targets.select! {|t| eval(m[:condition])}
    # Select Random Target (Min Random value)
    when /Select_Random[:]?(?'min'\s\d+)?/i
      # Get Match Information
      m = Regexp.last_match
      # Get Random AI Targets
      @ai_targets = [@ai_targets.sample([m[:min].to_i, 1].max)].flatten
    # Select Targets that are targeting the user with their action
    when /Select_Targeting_User/i
      # Get Match Information
      m = Regexp.last_match
      # Get Random AI Targets      
      @ai_targets.select! {|t| t.targeting_battler?(battler)}    
    end
  end
end


#==============================================================================
# ** Game_Battler
#------------------------------------------------------------------------------
#  A battler class with methods for sprites and actions added. This class 
# is used as a super class of the Game_Actor class and Game_Enemy class.
#==============================================================================

class Game_Battler < Game_BattlerBase
  #--------------------------------------------------------------------------
  # * Get Battler Note Text
  #--------------------------------------------------------------------------
  def note ; return actor.note if actor? ; return enemy.note if enemy? ; '' end    
  #--------------------------------------------------------------------------
  # * Get Battler ID
  #--------------------------------------------------------------------------
  def id ; actor.id if actor? ; enemy.enemy_id if enemy? end    
  #--------------------------------------------------------------------------
  # * Get Battler Battle Actions
  #--------------------------------------------------------------------------
  def battle_actions
    return enemy.actions if enemy?
    []
  end  
  #--------------------------------------------------------------------------
  # * Get Custom Skill AI Array
  #     skill_id : ID of skill to check
  #--------------------------------------------------------------------------
  def custom_skill_ai(skill_id)
    # Match text to Get Custom AI for Skill Use
    note[/\<Skill_Use_AI: [#{skill_id}]+\>(.+?)<\/[^>]+>/im]
    # Return Match Stripped and Separated into individual lines
    return $1.nil? ? [] : $1.strip.split(/\r\n/)
  end  
  #--------------------------------------------------------------------------
  # * Get All Custom Skill AI Array
  #--------------------------------------------------------------------------
  def all_custom_skill_ai
    # Create Actions Array
    actions = []
    # Scan Notes for Skill Use AI Actions
    note.scan(/\<Skill_Use_AI: (\d+)\>(.+?)<\/[^>]+>/im) {|id, tags| 
      # Strip and Remove Empty lines from tags
      tags = tags.strip.split(/\r\n/).delete_if {|s| s.empty?}
      # Add Skill ID and Tags to actions array
      actions << [id.to_i, tags]
    }    
    # Return Actions Array
    return actions
  end 
  #--------------------------------------------------------------------------
  # * Determine if Targeting Battler
  #--------------------------------------------------------------------------
  def targeting_battler?(battler)
    # Return false if Current Action is nil
    return false if current_action.nil?
    # Check if Targeting battler
    return current_action.make_targets.compact.include?(battler)
  end  
end


#==============================================================================
# ** Game_Action
#------------------------------------------------------------------------------
#  This class handles battle actions. This class is used within the
# Game_Battler class.
#==============================================================================

class Game_Action
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :custom_action_ai_targets          # Custom AI Targets Array  
  #--------------------------------------------------------------------------
  # * Alias Listings
  #--------------------------------------------------------------------------
  alias tds_custom_action_ai_game_action_clear                   clear
  alias tds_custom_action_ai_game_action_make_targets            make_targets
  #--------------------------------------------------------------------------
  # * Clear
  #--------------------------------------------------------------------------
  def clear(*args, &block)
    # Run Original Method
    tds_custom_action_ai_game_action_clear(*args, &block)
    # Clear Custom Action AI Targets Array
    @custom_action_ai_targets = []
  end
  #--------------------------------------------------------------------------
  # * Create Target Array
  #--------------------------------------------------------------------------
  def make_targets(*args, &block)
    # Delete Custom Targets if dead and item is not for dead friend
    @custom_action_ai_targets.delete_if {|t| t.dead? and !item.for_dead_friend?}
    # Return Custom Action AI Targets If Custom Action AI Targets Array is not empty
    return @custom_action_ai_targets if !@custom_action_ai_targets.empty?
    # Run Original Method
    tds_custom_action_ai_game_action_make_targets(*args, &block)
  end
end