iPhone App Directory
RPG Maker VX
 
Gallery Members Search Calendar Help


Welcome Guest ( Log In | Register )

Collapse

>Announcement

Keep an eye out for the upcoming 3rd Quarter Contest!
 
Reply to this topicStart new topic
> Simple Enemy and Random Target AI Boost (v 1.1), Targeting those that wont be affected? No more!
Mithran
post Apr 5 2009, 03:00 AM
Post #1


Scripter
Group Icon


Type: Coder
Alignment: True Neutral




Simple Enemy and Random Target AI Boost v 1.1
by Mithran


Introduction
The conditions for setting up enemy actions are rather basic, and to make matters worse, even if you get them to use the correct skill at the right time, its not likely that they will target the right person. I've been waiting to do an advanced enemy AI script for a while to rectify this, but haven't really had the time. After seeing a request on the boards, I broke down and threw this together and decided to expand upon it a bit. What it does is simple.. prevents enemies from selecting a skill that will have no effect on ANY avilable target (if cure is an available action, they will only use it if someone is infliced with a state they can cure), and if an action is selected that can be targeted, choose the target between only those that CAN be affected.

Features
- if an enemy detects that a skill will have no effect (it will not do damage, cure states, or inflict states on any of its potential targets), it will not use the action that turn
- if an enemy or autobattle actor uses a skill that normally needs selection, the available targets will be chosen at random from a pool of those that can actually be affected by the skill
- includes note tags to disable either of the above features on any given skill

How to Use
Install on its own script page in the materials section of the script editor above main and below Enemy AI State, if used.

Script
Spoiler:
CODE
# Simple Enemy and Random Target AI boost
# v 1.1
# by Mithran at RMVX.net
# Please do not redistribute without asking!
# Makes ai based random targeting take preference to targets that can be affected
# over those that cannot (both actors and enemies)
# This affects only 'random' skills that would normally 'need selection'
# (eg., enemy actions, autobattle actions)
# and does NOT affect full random attacks (eg., target 3 random enemies)
# Targeting odds are still respected, a target is simply removed from the list if
# the battler detects that their attack will not have any effect.
# Also makes enemies not use skills from their action lists at all if it will
# not have any effect on any of their potential targets

# Note tags:
# <forced use>
# Adding this tag to the note field of any skill will make the enemy not test
# whether they should or should not use the skill before attempting to use it
# (exactly like normal).  Forced use skills will still use the smart methods for
# targeting, but will be used whether or not the game detects that they will have
# any effect.
# Use this for evented skills.

# <true random>
# Adding to the note tag of any skill will revert its random targeting to normal,
# it will not distinguish if the skill will have any effect before choosing a target.
# Just in case anyone wants this.

# Install: Place above main and below my Enemy AI State script, if used.

class Game_Unit
  def random_smart_skill_target(user, skill)
    roulette = []
    for member in valid_skill_targets(user, skill)
      member.odds.times do
        roulette.push(member)
      end
    end
    return roulette.size > 0 ? roulette[rand(roulette.size)] : nil
  end
  
  def valid_skill_targets(user, skill)
    result = []
    for member in members
      result.push(member) if member.battle_skill_test(user, skill) if skill.for_dead_friend? == member.dead?
    end
    return result
  end
  
end

class Game_BattleAction
  alias decide_random_target_smartskill decide_random_target
  def decide_random_target
    if skill? && !skill.ai_true_random
      target = nil
      if skill.for_user?
        target = battler
      elsif skill.for_friend?
        target = friends_unit.random_smart_skill_target(battler, skill)
      else
        target = opponents_unit.random_smart_skill_target(battler, skill)
      end
      if target != nil
        @target_index = target.index
        return
      end
      # if target is nil, choose a random target using the old method
    end
    decide_random_target_smartskill
  end
end

class Game_Battler
  def battle_skill_test(user, skill)
    return false if skill.for_user? && user != self
    tester = Marshal.load(Marshal.dump(self))
    # deep clones the battler to tester
    tester.clear_action_results
    tester.make_obj_damage_value(user, skill)
    # makes a damage value for testing purposes
    if tester.hp_damage > 0
      return true if tester.hp > 0
    elsif tester.hp_damage < 0
      return true if tester.hp < tester.maxhp
    end
    if tester.mp_damage > 0
      return true if tester.mp > 0
    elsif tester.mp_damage < 0
      return true if tester.mp < tester.maxmp
    end
    # if any damage can be dealt or cured, passes the test
    @current_asp_obj = skill
    for i in skill.plus_state_set
      return true if !state?(i) && state_probability(i) > 0 && !state_ignore?(i)
      # if any state can be applied (will not target immune or people that have it already)
    end
    for i in skill.minus_state_set
      return true if tester.state?(i)
      # if any state can be cured (target is inflicted with the state)
    end
    @current_asp_obj = nil
#~     tester.apply_state_changes(skill)
#~     # just in case, use the normal apply state method and see if anything got missed
#~     return true unless tester.added_states.empty?
#~     return true unless tester.removed_states.empty?
# removed, causes conflicts with ATB
    return false
  end
  
  def any_usable_targets?(action)
    if action.kind == 1
      skill = $data_skills[action.skill_id]
      return true if skill.ai_forced_use
      test = false
      # test starts out invalid
      if skill.for_friend? or skill.for_dead_friend?
        for member in $game_troop.members
          test = true if member.battle_skill_test(self, skill)
          # test is valid if any of the potential targets
        end
      else
        for member in $game_party.members
          test = true if member.battle_skill_test(self, skill)
          # test is valid if any of the potential targets
        end
      end
      return test
    end
    return true
  end
  
end

class Game_Enemy
  
  alias conditions_met_smartskill conditions_met?
  def conditions_met?(action)
    result = conditions_met_smartskill(action)
    # get normal conditions met
    return result if result == false
    # no further tests are needed if the normal conditions are not met
    return any_usable_targets?(action)
    # otherwise, return the normal action conditions
  end

end

class Game_Actor

  if method_defined?(:eai_conditions_met?)
  alias eai_conditions_met_smartskill eai_conditions_met?
  def eai_conditions_met?(action)
    result = eai_conditions_met_smartskill(action)
    # get normal conditions met
    return result if result == false
    # no further tests are needed if the normal conditions are not met
    return any_usable_targets?(action)
    # does the skill have anyone that it can affect?
  end
  end # if method_defined

end

class RPG::Skill
  def ai_forced_use
    return true if self.note =~ /<forced use>/i
    return false
  end
  
  def ai_true_random
    return true if self.note =~ /<true random>/i
    return false
  end
end


Tips
- Enemy actions are created at the beginning of the combat round. If you are having problems with more than one enemy curing the same target, try dropping the rating of the 'cure' action so they have other actions available to pick from.
- Action ratings and targeting odds are still respected. Actions with higher ratings will still be picked over those with lower ratings, but invalid actions will be disabled.
- Actions whose external conditions are not met (example, if their HP is not in the correct range) will still not execute even if they are considered valid.
- If every action on the list is a skill and the script detects all of them to be invalid, the enemy will do nothing for the round. This will rarely happen unless every actor has super high defense and state immunity, but you should have at least one 'forced use' action or normal attack on the list (preferably with a rating of 1)

Credit and Thanks
- Mithran

Author's Notes
Feedback is welcome. Please do not redistribute without asking.


--------------------
Go to the top of the page
 
+Quote Post
   
Halik
post Apr 5 2009, 10:32 AM
Post #2



Group Icon


Type: Designer
Alignment: Neutral Good




Lovely, just lovely!
No more stupid allies and enemies!

Really good script!
Keep up.


--------------------

Spoiler:

I support:




Go to the top of the page
 
+Quote Post
   
The Dark Flame K...
post May 21 2009, 09:45 PM
Post #3



Group Icon


Type: Writer




This will actually help me out a lot! I've had to go through a lot of event command set up for the enemy AI. May I use this script if you will allow me to?
Go to the top of the page
 
+Quote Post
   
Mithran
post May 22 2009, 05:25 AM
Post #4


Scripter
Group Icon


Type: Coder
Alignment: True Neutral




QUOTE (The Dark Flame Knight @ May 21 2009, 02:45 PM) *
This will actually help me out a lot! I've had to go through a lot of event command set up for the enemy AI. May I use this script if you will allow me to?


Go right ahead. It is why I posted it, after all wink.gif My scripts are free for use in projects, I only ask that if someone wants a copy, refer them to my original thread instead of posting it on another site. Credit as you see fit.


--------------------
Go to the top of the page
 
+Quote Post
   
aba3k
post Jun 20 2009, 06:07 PM
Post #5



Group Icon


Type: Undisclosed
Alignment: Chaotic Good




Very nice, very useful.
One thing: if i'm immune to certain element (let's say fire), the enemy never will attack me using fire, they know before attack that i'm immune. If i put <forced use> in the skill, then they will use the skill normally, but then they'll act like idiot, and will use the skill several times, even after making nothing in me (or healing me). The best way would be if they attempted to use the ability once at each battler, and they start to act 'intelligent'. Or maybe a chance to detect if the skill is useful or not, better than make the same stupid action (heal the player) over and over.


--------------------
Go for the eyes Boo, go for the eyes!
Go to the top of the page
 
+Quote Post
   
Mithran
post Jun 20 2009, 06:54 PM
Post #6


Scripter
Group Icon


Type: Coder
Alignment: True Neutral




QUOTE (aba3k @ Jun 20 2009, 11:07 AM) *
Very nice, very useful.
One thing: if i'm immune to certain element (let's say fire), the enemy never will attack me using fire, they know before attack that i'm immune. If i put <forced use> in the skill, then they will use the skill normally, but then they'll act like idiot, and will use the skill several times, even after making nothing in me (or healing me).


Forced Use is intended to be for the enemies' last-ditch effort attack, the one that they will use if they run out of options on all other attacks. Otherwise, if they detect that all their attacks are worthless, they will sit there and do nothing. Its other intended use is for evented skills, which, since they do nothing by themselves, the script will not allow the enemy to use them.

QUOTE
The best way would be if they attempted to use the ability once at each battler, and they start to act 'intelligent'. Or maybe a chance to detect if the skill is useful or not, better than make the same stupid action (heal the player) over and over.


While this is a good idea, I probably won't add indiviudal tests to this simple script. For one thing, conditions are constantly changing, so skills that are valid one turn may not be the next. Not saying that it is impossible, just that the tracking system for this kind of thing, if done right, would be far more advanced than this script.

Currently, actions dont get blocked if they do 'something', and that includes healing an enemy. If you want to change this, copy this here:
CODE
&& user.actor? == tester.actor?


Paste it at the end of the line that says
CODE
elsif tester.hp_damage < 0


So it looks like
CODE
elsif tester.hp_damage < 0 && user.actor? == tester.actor?


Do the same thing for this line:
CODE
elsif tester.mp_damage < 0


So it is:
CODE
elsif tester.mp_damage < 0 && user.actor? == tester.actor?


Skills will then fail the damage test if they are healing an opponent, so enemies will exclude them from their action lists if they dont do anything else. I agree that is probably doesnt make much sense like this, with the inclusion of the forced action tag, so I'll consider making actions like this disabed by default with a note tag to reenable them. Hurting an ally will remain a valid action. Currently, there is also no way to distinguish inflicting bad states from dispelling good ones (the engine itself does not make this distinction, so unless I gave the option for the player to specify bad from good, it would be my assumption). If the skill will apply any kind of state change, it remains a valid action.


--------------------
Go to the top of the page
 
+Quote Post
   
aba3k
post Jun 21 2009, 04:29 PM
Post #7



Group Icon


Type: Undisclosed
Alignment: Chaotic Good




Thanks for the reply, and i agree with you. Making the engine inteligent must be very difficult, it's just that i think smart enemies are essential.


--------------------
Go for the eyes Boo, go for the eyes!
Go to the top of the page
 
+Quote Post
   
Cass
post Jul 1 2009, 08:17 AM
Post #8


*Insert something slightly wise and satirical*
Group Icon


Type: Writer
Alignment: True Neutral




Very useful script! However....Does it work for the Side View battle system, too?
Also, another question..
When you say that they don't use skills that are pretty much useless, would that mean if they have a healing skill, which would heal themselves. Does that mean, if they are full in health, they won't use that skill?


--------------------


*Mwah*
Go to the top of the page
 
+Quote Post
   
Mithran
post Jul 1 2009, 09:19 AM
Post #9


Scripter
Group Icon


Type: Coder
Alignment: True Neutral




QUOTE (Cass @ Jul 1 2009, 01:17 AM) *
Very useful script! However....Does it work for the Side View battle system, too?
Also, another question..
When you say that they don't use skills that are pretty much useless, would that mean if they have a healing skill, which would heal themselves. Does that mean, if they are full in health, they won't use that skill?


Yes, it works with the SBS and should work with most custom battle systems.

There are a few tests that happen to determine if a skill should not be used. Under most cases, they wont be used all of the following are true -
1. It wont deal any damage or heal any potential target.
2. It wont apply or remove states to any potential target. (states that are automatically resisted or cures for states that arent applied do not count)
3. It does not have <forced use> in the note tag.

All this is in addition to the 'condition' given to the action. So, if a healer has a healing skill that can heal themselves and they are at full health, they will still use the skill if it meets any of the above tests (such as an ally, but not themselves, being hurt, and the skill being ally targetable, or it also being attached to a state).

In addition, when choosing 'random' targets (ai random selection, skills that would be selectable if an actor used them but an enemy or autobattle actor is using them), it excludes a potential target if -
1. It potentially wont deal or heal any damage.
2. It cannot apply or remove any states to the target.
3. The skill does not have <true random> in the note tag.

Basically, the same tests. Allied autobattle 'random' was already somewhat smart in this regard, but the way selection occurs is a bit different (potential actions are identfied, actions are given with random targets, the AI assigns a point value to the actions depending on how much they heal or damage, and a final random determines which is used). Even so, you may see a silght improvement here because the improved 'random' is still applied.


--------------------
Go to the top of the page
 
+Quote Post
   
Reedo
post Jan 3 2010, 05:10 PM
Post #10



Group Icon


Type: Coder




Hi Mithran,

Could you please take a look at http://www.rpgmakervx.net/index.php?s=&...st&p=223048

I'm still going to adjust my script, but I think you might want to adjust yours too... Typically when designing an application, you avoid the serialization deep-clone technique in methods which have the potential to execute repeatedly within a logic segment. In this case, a user could have a battle that takes an hour to complete (sounds crazy, I know, and you probably wouldn't do it on purpose, but it could happen). For the number of times that the method could be called, you could really pound on that hard drive with a lot of excess I/O. Usually something you try to avoid doing to your user's hardware. smile.gif (especially with the popularity of flash-based drives now; though the flash technology is getting better, maximum writes per sector do still exist (even if the numbers are getting huge).

Its a great technique when you know it will only be used occasionaly, but it is also one of those techniques that you have to watch out for over use. When it would mean using recursive functions to manually copy tons and tons of layers of members, the serialization technique is very handy; but for just a handful, its probably better to write a little extra code to make a new unique obect, or to cache the values you need to change, then reset them when finished with your test.

Just a suggestion for your script smile.gif; like I said, I'm going to try to modify mine to work whether yours changes or not. wink.gif


--------------------
"Yes, I saw that. You were doing well, until everyone died."
"When you do things right, people won't be sure you've done anything at all."
The Satellite That Collided With God – Futurama

Spoiler:


Go to the top of the page
 
+Quote Post
   
Mithran
post Jan 3 2010, 06:49 PM
Post #11


Scripter
Group Icon


Type: Coder
Alignment: True Neutral




I'm not using any IO objects here, I'm simply serializing into a string object (held in memory), then deserializing back into an object (memory, again). Unless I am missing something, this should have no impact on the hard drive (or no more than would be necessary for windows virtual memory were a similar amount of data to be added to memory).


--------------------
Go to the top of the page
 
+Quote Post
   
Reedo
post Jan 3 2010, 08:46 PM
Post #12



Group Icon


Type: Coder




QUOTE (Mithran @ Jan 3 2010, 12:49 PM) *
I'm not using any IO objects here, I'm simply serializing into a string object (held in memory), then deserializing back into an object (memory, again). Unless I am missing something, this should have no impact on the hard drive (or no more than would be necessary for windows virtual memory were a similar amount of data to be added to memory).


No, you are right - I guess I missed that the dump target was memory registers - sorry about that.

I'm too used to the process being I/O related, and I jumped to a conclusion. Again, my appologies. unsure.gif

-EDIT-

Oh, I see what it is; I forgot that dump.(object, stream) is an overload of the dump(object) method. I was thinking that dump(object) was the only implementation, and that the I/O was implicit.

LOL I'm still having trouble remembering all these new details. Once three-months gets to six-months, I should be doing much better! wink.gif hahaha I already find I'm looking stuff up less and less, so I guess that means I'm making progress. tongue.gif

This post has been edited by Reedo: Jan 3 2010, 08:53 PM


--------------------
"Yes, I saw that. You were doing well, until everyone died."
"When you do things right, people won't be sure you've done anything at all."
The Satellite That Collided With God – Futurama

Spoiler:


Go to the top of the page
 
+Quote Post
   
praygon
post Jan 15 2010, 04:33 AM
Post #13


Lord of the dance
Group Icon


Type: Writer
Alignment: Chaotic Neutral




mithran you are a god! you have no idea how much this helps me bravo old chap bravo! laugh.gif


--------------------
"Stranger: asl
You: I WASSSS ONCCCEEE A MAAAAAAAAAAAAAAAAN
Stranger: now you are a girl?
You: NOO
You: I AM A COBRRAAA
Stranger: so wat then
You: COBRAAAAAAAAAAAAAAAAAAAAA"

Go to the top of the page
 
+Quote Post
   
John Dane
post May 26 2010, 09:37 AM
Post #14



Group Icon


Type: Undisclosed




Hey Mithran, I have here what could possibly serve as a nice upgrade to your script.

First, enemies are not supposed to use status-inflicting skills on actors who are immune, but when testing, an enemy repeatedly tried to poison one of my actors who was wearing armor that gave him poison immunity. This is because there is no check for equipment. So, we should alter this line:

CODE
return true if !state?(i) && state_probability(i) > 0 && !state_ignore?(i)


to

CODE
return true if !state?(i) && state_probability(i) > 0 && !state_ignore?(i) && !state_resist?(i)


Second, in the default system, autobattlers will never use status-altering skills unless you attach some kind of "value" to the skill, and by value I mean it has to either heal or deal damage. In my game, I'd like to see my battlers be a bit more intelligent. That is, I'd like them buffing party members, debuffing enemies, curing diseases, casting damage spells, and attacking with a bit more "science".

I want healing and "state removal" spells to have more priority over others...so if an actor gets poisoned, that state will be the priority of anyone with the skill that cures poison. Or if an actor needs healing, that actor will get priority from a healer, and so on and so forth. Essentially this means I had to rewrite the Game_BattlerAction method "evaluate_skill_with_target".

In my new autobattler "value system", there are only four ranks of "value", (1-4, with 4 being the highest). The ranks are:

4 -- Healing (both HP and MP)
3 --- Cures and state removals (Cure Poison, Remove "ATK Up", etc.)
2 ---- Buffs and state additions (ATK up spells, poisons, blinds, etc.)
1 ----- Damage-dealing skills/spells (both HP and MP)


Taking notes from your script, and using it in conjunction, here's what I've done to so far:

Spoiler:
CODE
  
  #--------------------------------------------------------------------------
  # * OVERWRITE Skill Evaluation (target designation)
  #     target : Target battler
  #--------------------------------------------------------------------------
  def evaluate_skill_with_target(target)
    target.clear_action_results
    target.make_obj_damage_value(battler, skill)
    rank = 0
  # Below we determine if the skill will heal or deal any damage.
    if target.hp_damage < 0 && target.hp <= (0.6 * target.maxhp)
      rank = 4
    end
    if target.mp_damage < 0 && target.mp <= (0.25 * target.maxmp)
      rank = 4
    end
    if target.hp_damage > 0 && target.hp > 0
      rank = 1
    end
    if target.mp_damage > 0 && target.mp > 0
      rank = 1
    end
  # Next we determine if the skill will remove or add any states.
    @current_asp_obj = skill
    for i in skill.minus_state_set
      if rank == 4 && !target.state?(i)
        rank = 0
      end
      if rank == 1 && target.state?(i)
        rank = 3
      else
        rank = 0
      end
      if target.state?(i)
        rank = 3
      end
    end
    for i in skill.plus_state_set
      if rank == 4 && target.state?(i)
        rank = 0
      end
      if rank == 1 && !target.state?(i) && target.state_probability(i) > 0 && !target.state_ignore?(i) && !target.state_resist?(i)
        rank = 2
      else
        rank = 0
      end
      if !target.state?(i) && target.state_probability(i) > 0 && !target.state_ignore?(i) && !target.state_resist?(i)
        rank = 2
      end
    end
    @current_asp_obj = nil
    return rank
  end


It's probably worth it to note that I have fixed line 302 in Game_BattlerAction, which now reads:

CODE
@value += rand(nil)


And I've commented out lines 346-348, which for some reason gave preference to target-all skills.

The following can be inferred:

1) Healing will always be a priority over Curing, Curing (state subtracting) over Buff/Debuff (state adding), and damage-dealing skills/spells will be cast when all other conditions are absent. This follows my new 4-tier ranking system above.

2) If a skill satisfies more than one value rank, the lowest value rank is used. That means a skill that heals and buffs at the same time is given rank 2, essentially making it a buff. I did this only because I don't want my autobattlers spamming buffs when all they aim to do is heal.
---2a)EXCEPTION: If a skill satisfies both rank 1 and ranks 3 or 2, it will be treated as rank 3 or 2 (in that order). This ensures rank 1 skills will always be cast when rank 3 or 2 skills have no effect. In other words, it prevents spamming. Let's say you have a skill that only casts poison, and a skill that casts poison and deals damage. The latter skill will be treated as a rank 2 skill, which means if it was successful in inflicting the poison state the first time, it will not be cast again until the poison wears off. Which leads to:

3) Ranks 4, 3, and 2 skills will never be spammed by autobattlers. This is perhaps the most important aspect of my new system. Now actors will behave intelligently.

So far everything is working perfectly, at least for me, and I tested various different skill combinations. As it stands, autobattlers are buffing party members, inflicting states on enemies, casting cures, damage spells, and heals effectively. Everything is prioritized as I mentioned above.

All in all, my autobattlers are behaving smarter, but there are still some holes to be filled in. For example, perhaps I can do some database note-tag customization that allows users to manually decide the priority of skills they choose. Also, I'd like to have some customization before the script for some variables, such as when a party member will heal HP/MP (by default I have them set to 60% and 25%, respectively).

Any tips? I'm thinking about submitting this as a "script snippet" if you will, but I'd rather it be included with your script since it compliments it very well. No credit is needed from me, since I'm just polishing your work.

The final script would look something like this:

Spoiler:
CODE
# Simple Enemy, Autobattle, and Random Target AI boost
# v 2.0
# by Mithran at RMVX.net, with help from his sidekick John Dane
# Please do not redistribute without asking!
# Makes ai based random targeting take preference to targets that can be affected
# over those that cannot (both actors and enemies)
# This affects only 'random' skills that would normally 'need selection'
# (eg., enemy actions, autobattle actions)
# and does NOT affect full random attacks (eg., target 3 random enemies)
# Targeting odds are still respected, a target is simply removed from the list if
# the battler detects that their attack will not have any effect.
# Also makes enemies not use skills from their action lists at all if it will
# not have any effect on any of their potential targets

# Note tags:
# <forced use>
# Adding this tag to the note field of any skill will make the enemy not test
# whether they should or should not use the skill before attempting to use it
# (exactly like normal).  Forced use skills will still use the smart methods for
# targeting, but will be used whether or not the game detects that they will have
# any effect.
# Use this for evented skills.

# <true random>
# Adding to the note tag of any skill will revert its random targeting to normal,
# it will not distinguish if the skill will have any effect before choosing a target.
# Just in case anyone wants this.

# Install: Place above main and below my Enemy AI State script, if used.

class Game_Unit
  def random_smart_skill_target(user, skill)
    roulette = []
    for member in valid_skill_targets(user, skill)
      member.odds.times do
        roulette.push(member)
      end
    end
    return roulette.size > 0 ? roulette[rand(roulette.size)] : nil
  end
  
  def valid_skill_targets(user, skill)
    result = []
    for member in members
      result.push(member) if member.battle_skill_test(user, skill) if skill.for_dead_friend? == member.dead?
    end
    return result
  end
  
end

class Game_BattleAction
  alias decide_random_target_smartskill decide_random_target
  def decide_random_target
    if skill? && !skill.ai_true_random
      target = nil
      if skill.for_user?
        target = battler
      elsif skill.for_friend?
        target = friends_unit.random_smart_skill_target(battler, skill)
      else
        target = opponents_unit.random_smart_skill_target(battler, skill)
      end
      if target != nil
        @target_index = target.index
        return
      end
      # if target is nil, choose a random target using the old method
    end
    decide_random_target_smartskill
  end
  
  #--------------------------------------------------------------------------
  # * OVERWRITE (fix) Action Value Evaluation (for automatic battle)
  #    @value and @target_index are automatically set.
  #--------------------------------------------------------------------------
def evaluate
    if attack?
      evaluate_attack
    elsif skill?
      evaluate_skill
    else
      @value = 0
    end
   if value > 0
      @value += rand(nil)
    end
  end
  
  #--------------------------------------------------------------------------
  # * OVERWRITE ("fix") Skill Evaluation
  #--------------------------------------------------------------------------
  def evaluate_skill
    @value = 0
    unless battler.skill_can_use?(skill)
      return
    end
    if skill.for_opponent?
      targets = opponents_unit.existing_members
    elsif skill.for_user?
      targets = [battler]
    elsif skill.for_dead_friend?
      targets = friends_unit.dead_members
    else
      targets = friends_unit.existing_members
    end
    for target in targets
      value = evaluate_skill_with_target(target)
##   These lines gave preference to "target-alls".
#~       if skill.for_all?
#~         @value += value        
#~       else
      if value > @value
        @value = value
        @target_index = target.index
      end
      end
    end

  #--------------------------------------------------------------------------
  # * OVERWRITE Skill Evaluation (target designation)
  #     target : Target battler
  #--------------------------------------------------------------------------
  def evaluate_skill_with_target(target)
    target.clear_action_results
    target.make_obj_damage_value(battler, skill)
    rank = 0
  # Below we determine if the skill will heal or deal any damage.
    if target.hp_damage < 0 && target.hp <= (0.6 * target.maxhp)
      rank = 4
    end
    if target.mp_damage < 0 && target.mp <= (0.25 * target.maxmp)
      rank = 4
    end
    if target.hp_damage > 0 && target.hp > 0
      rank = 1
    end
    if target.mp_damage > 0 && target.mp > 0
      rank = 1
    end
  # Next we determine if the skill will remove or add any states.
    @current_asp_obj = skill
    for i in skill.minus_state_set
      if rank == 4 && !target.state?(i)
        rank = 0
      end
      if rank == 1 && target.state?(i)
        rank = 3
      else
        rank = 0
      end
      if target.state?(i)
        rank = 3
      end
    end
    for i in skill.plus_state_set
      if rank == 4 && target.state?(i)
        rank = 0
      end
      if rank == 1 && !target.state?(i) && target.state_probability(i) > 0 && !target.state_ignore?(i) && !target.state_resist?(i)
        rank = 2
      else
        rank = 0
      end
      if !target.state?(i) && target.state_probability(i) > 0 && !target.state_ignore?(i) && !target.state_resist?(i)
        rank = 2
      end
    end
    @current_asp_obj = nil
    return rank
  end
end


class Game_Battler
  def battle_skill_test(user, skill)
    return false if skill.for_user? && user != self
    tester = Marshal.load(Marshal.dump(self))
    # deep clones the battler to tester
    tester.clear_action_results
    tester.make_obj_damage_value(user, skill)
    # makes a damage value for testing purposes
    if tester.hp_damage > 0
      return true if tester.hp > 0
    elsif tester.hp_damage < 0
      return true if tester.hp < tester.maxhp
    end
    if tester.mp_damage > 0
      return true if tester.mp > 0
    elsif tester.mp_damage < 0
      return true if tester.mp < tester.maxmp
    end
    # if any damage can be dealt or cured, passes the test
    @current_asp_obj = skill
    for i in skill.plus_state_set
      return true if !state?(i) && state_probability(i) > 0 && !state_ignore?(i) && !state_resist?(i)
      # if any state can be applied (will not target immune or people that have it already)
    end
    for i in skill.minus_state_set
      return true if tester.state?(i)
      # if any state can be cured (target is inflicted with the state)
    end
    @current_asp_obj = nil
    return false
  end
  
  def any_usable_targets?(action)
    if action.kind == 1
      skill = $data_skills[action.skill_id]
      return true if skill.ai_forced_use
      test = false
      # test starts out invalid
      if skill.for_friend? or skill.for_dead_friend?
        for member in $game_troop.members
          test = true if member.battle_skill_test(self, skill)
          # test is valid if any of the potential targets
        end
      else
        for member in $game_party.members
          test = true if member.battle_skill_test(self, skill)
          # test is valid if any of the potential targets
        end
      end
      return test
    end
    return true
  end
  
end

class Game_Enemy
  
  alias conditions_met_smartskill conditions_met?
  def conditions_met?(action)
    result = conditions_met_smartskill(action)
    # get normal conditions met
    return result if result == false
    # no further tests are needed if the normal conditions are not met
    return any_usable_targets?(action)
    # otherwise, return the normal action conditions
  end

end

class Game_Actor

  if method_defined?(:eai_conditions_met?)
  alias eai_conditions_met_smartskill eai_conditions_met?
  def eai_conditions_met?(action)
    result = eai_conditions_met_smartskill(action)
    # get normal conditions met
    return result if result == false
    # no further tests are needed if the normal conditions are not met
    return any_usable_targets?(action)
    # does the skill have anyone that it can affect?
  end
  end # if method_defined

end

class RPG::Skill
  def ai_forced_use
    return true if self.note =~ /<forced use>/i
    return false
  end
  
  def ai_true_random
    return true if self.note =~ /<true random>/i
    return false
  end
end


I'm sure you can help clean this up a bit. I probably have a lot of unnecessary code in there, but I just wanted to make sure I covered all the bases.

Thanks!

This post has been edited by John Dane: May 27 2010, 05:00 PM
Go to the top of the page
 
+Quote Post
   
Mithran
post Jun 1 2010, 08:58 AM
Post #15


Scripter
Group Icon


Type: Coder
Alignment: True Neutral




Hey, thanks for the notes. I totally missed this reply when I checked the thread last time, so I am just seeing it now. The status inflicting tests do need a better test, thanks for bringing that to my attention.

As for the rest of it, this simple AI boost was originally intended only to boost the AI for which skills are available to an enemy and, given a skill that targets 'randomly' which targets will end up being chosen. When I first made this, I did intend to make a version for actor AI, but never got around to it. I do like your ideas for boosting the actor AI, though. You could tack this on to mine, but I think you have the makings of a good script here. I say run with it, and PM me or make a post if you have any questions (such as compatability with my Advanced State Probability, I see you have some lines in there specifically for that). Either way, I think I should add the target weight bugfixes to this script (using this method, slightly different) because I haven't seen them anywhere else, and it would fit within the 'simple' nature of this script. Good job finding those target weight bugs by the way, I didn't even notice them myself until they were brought to my attention in that thread.


--------------------
Go to the top of the page
 
+Quote Post
   
John Dane
post Jun 9 2010, 07:11 PM
Post #16



Group Icon


Type: Undisclosed




QUOTE (Mithran @ Jun 1 2010, 04:58 AM) *
I say run with it.



Thanks for the encouragement and support, Mithran. I made another tweak to my autobattler script, which now treats skills that absorb HP/MP as healing skills (i.e., rank 4). So now your Dark Knight will only absorb HP when he needs to, and focus on other skills until the time is right.

Spoiler:
CODE
  
=begin
Smart Autobattlers
By: John Dane
=end
  #--------------------------------------------------------------------------
  # * OVERWRITE Skill Evaluation (target designation)
  #     target : Target battler
  #--------------------------------------------------------------------------
  def evaluate_skill_with_target(target)
    target.clear_action_results
    target.make_obj_damage_value(battler, skill)
    target.make_obj_absorb_effect(battler, skill)
    rank = 0
  # Below we determine if the skill will heal
    if (target.hp_damage < 0 && target.hp <= (0.6 * target.maxhp)) ||
       (target.mp_damage < 0 && target.mp <= (0.25 * target.maxmp))
      rank = 4
    end
  # Now we ask if the skill does any damage (but doesn't absorb)
    if ((target.hp_damage > 0 && target.hp > 0) ||
       (target.mp_damage > 0 && target.mp > 0)) && !skill.absorb_damage
      rank = 1
    end
  # Next we ask if the skill absorbs
    if skill.absorb_damage && ((battler.hp <= (0.6 * battler.maxhp)) ||
       (battler.mp <= (0.25 * battler.maxmp)))
      rank = 4
    end
  # Next we determine if the skill will remove or add any states.
    @current_asp_obj = skill
    for i in skill.minus_state_set
      if rank == 4 && !target.state?(i)
        rank = 0
      end
      if rank == 1 && target.state?(i)
        rank = 3
      else
        rank = 0
      end
      if target.state?(i)
        rank = 3
      end
    end
    for i in skill.plus_state_set
      if rank == 4 && target.state?(i)
        rank = 0
      end
      if rank == 1 && !target.state?(i) &&
        target.state_probability(i) > 0 &&
        !target.state_ignore?(i) &&
        !target.state_resist?(i)
        rank = 2
      else
        rank = 0
      end
      if !target.state?(i) &&
        target.state_probability(i) > 0 &&
        !target.state_ignore?(i) &&
        !target.state_resist?(i)
        rank = 2
      end
    end
    @current_asp_obj = nil
    return rank
  end


I've been playing with this script for a while now, and I still haven't found any real bugs with it, so I think I'll take your advice and submit it. Maybe others will find it useful...I think it works extremely well with Yanfly's YERD Skill Slot script. In combination with this, you can practically "program" your autobattlers.

I'm also using your Advanced State Probability and it works great with it. It's a little scary since autobattlers are a bit too smart. They won't even bother using skills if they have no effect. But hey, that's how it should be, right? =) I might even consider allowing the option for them to use certain items, given circumstances.

Peace

This post has been edited by John Dane: Jun 10 2010, 12:35 PM
Go to the top of the page
 
+Quote Post
   

Reply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 

Lo-Fi Version Time is now: 2nd October 2014 - 11:35 AM

RPGMakerVX.net is an Privacy Policy, Legal.