#============================================================================== # ** 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