[SCRIPT] AUTO-POTION (VX ACE)

Posts

Pages: first 12 next last
Hi! I seek for a script that could automatically restore MP during the battle but in price of a special item. When these special items end, MP doesn`t restore.
Thanks in advance!
SunflowerGames
The most beautiful user on RMN!
13323

You mean like an auto-potion ability similiar to FF IX?
author=kory_toombs
You mean like an auto-potion ability similiar to FF IX?


I guess? (Haven`t played the game yet)
The idea is that a character has some amount of so-called grains, each of them contains limited portion of magical energy. So, when player uses skills during battle and spends all of his mp, it means that magical energy inside of a grain has ended and so he can take another grain, which makes mp bar full again. Thus, when grains end, player cannot restore mp bar anymore.
I have no idea what you're talking about. Can you give a step-by-step of what the player should do and how the game reacts? At a glance I'd assume there's an item that when the player uses it restores their MP so they can cast spells until they run out of MP where they use said MP restoring item but I don't think that's what you have in mind at all.


e: Or when they use a MP restoring item they can't use another for the remainder of that battle?
author=GreatRedSpirit
I have no idea what you're talking about. Can you give a step-by-step of what the player should do and how the game reacts?

Sure thing.
Player casts spells during the battle. When his MP reaches 0, the game automatically restores it, but in price of a special item, "magic grain" in our case. MP can be automatically restored as many times, as many "grains" does player have, but when there are no grains left, player cannot cast spells and has to finish battle by using attacks. An effect of automatic MP restore starts to work again only after player collects new "grains". There are no other items, that can restore MP, only those "grains".
Hope, it`s a little clearer now.
Ahh, that makes sense. I'll take a look later this weekend and see if I can whip something up.
A little change of a question: I`ve recently found an Auto-life script, an effect of which is fairly close to the one I want to achieve, except it recovers HP not MP. Is there a way to change this script so that it would recover MP instead of HP?
Fuck, I forgot all about this. I'll take a look at this Monday night. I wouldn't worry about the autolife script, I took a peek at the battle script code and adding an extra invoke item at certain conditions (is a player and has zero(?) mp and has >0 grains) shouldn't be too hard.
author=GreatRedSpirit
Fuck, I forgot all about this. I'll take a look at this Monday night. I wouldn't worry about the autolife script, I took a peek at the battle script code and adding an extra invoke item at certain conditions (is a player and has zero(?) mp and has >0 grains) shouldn't be too hard.


Okay, then I`ll be waiting!
Here's my quick and dirty proof of concept demo for an automatically used item.

Here's the script as is:
class Scene_Battle < Scene_Base

  alias :use_item_autoitem :use_item unless $@
  def use_item(*args)
    # First call the original method
    use_item_autoitem(*args)
    # Now see if now's the time to use an auto item
    if @subject.is_a? Game_Actor 
      # The subject is an actor so see if the party has any items that can be
      # autoused here
      auto_item = $game_party.items.
        select{|x| x.auto_item_condition(@subject)}.
        sort_by{ |a| a.auto_item_priority }.
        first;
      # If no auto item was found then there's no work to do
      return if auto_item.nil?
      
      # Otherwise we found an item and it's time to use it!
      
      # Prepare to use the item! This will display it to the user and consume the item
      @log_window.display_use_item(@subject, auto_item)
      @subject.use_item(auto_item)
      refresh_status
      # Create an action to represent this auto item execution
      action = Game_Action.new(@subject, true)
      action.set_item(auto_item.id)
      # And generate the targets for it
      targets = action.make_targets.compact
      show_animation(targets, auto_item.animation_id)
      # Now use the item!
      invoke_item(@subject, auto_item)
    end
  end
  
end



class RPG::Item < RPG::UsableItem
  
  def auto_item_condition(actor)
    return (@id == 1 and actor.mp == 0)
  end
  
  def auto_item_priority
    return @id == 1 ? 1 : 0
  end
end


For now after an actor acts the game will see if there's any items in their inventory that they can currently automatically used. Currently the condition is hard coded here:
def auto_item_condition(actor)
    return (@id == 1 and actor.mp == 0)
  end

Only the item with ID#1 is an auto item and only if the actor who'll use it has zero MP. In the demo project the item with ID#1 is the Grain which recovers 10MP. When the player uses an attack that sets their MP to zero and if they have any grains (you can get them by talking to the girl) the character will use a grain and automatically consume the item, recovering MP.

So far it only applied to when a character takes a turn, there's no auto item when an actor is targetted by an enemy for example. I'm going to expand on this script a bit to cover that and make it data driven the conditions for an item to be used automatically are set by the item and not hard coded by the script.

A bit less proof of concepty, I threw this together too. Now it won't process when you're dead and handle when you're hit too!

class Scene_Battle < Scene_Base

  alias :use_item_autoitem :use_item unless $@
  def use_item(*args)
    # First call the original method
    use_item_autoitem(*args)
    # Now see if now's the time to use an auto item
    use_autoitem(@subject) if @subject.is_a? Game_Actor 
  end
  
  alias apply_item_effects_autoitem apply_item_effects unless $@
  def apply_item_effects(target, item)
    # Execute the original script
    apply_item_effects_autoitem(target, item)
    # Now see if we should use an autoitem
    use_autoitem(target) if target.is_a? Game_Actor and not @subject.is_a? Game_Actor
  end
  
  def use_autoitem(actor)
    # Sanity check, we only deal with actors
    return unless actor.is_a? Game_Actor and actor.alive?
    
    # Look for items in the player's inventory that the current actor can
    # use as an autoitem
    auto_item = $game_party.items.
      select{|x| x.auto_item_condition(actor, $game_party, $game_troop)}.
      sort_by{ |a| a.auto_item_priority }.
      first;
    # If no auto item was found then there's no work to do
    return if auto_item.nil?
    
    # Otherwise we found an item and it's time to use it!
    
    # Prepare to use the item! This will display it to the user and consume the item
    @log_window.display_use_item(actor, auto_item)
    actor.use_item(auto_item)
    refresh_status
    # Create an action to represent this auto item execution
    action = Game_Action.new(actor, true)
    action.set_item(auto_item.id)
    # And generate the targets for it
    targets = action.make_targets.compact
    show_animation(targets, auto_item.animation_id)
    # Now use the item!
    invoke_item(actor, auto_item)
    
  end
  
end



class RPG::Item < RPG::UsableItem
  
  def auto_item_condition(user, party, enemies)
    return (@id == 1 and user.mp == 0)
  end
  
  def auto_item_priority
    return @id == 1 ? 1 : 0
  end
end


Similar instructions as before. I'm going to hack more at it thursday and do some more extensive testing to make sure it works right because right now a multiple attack action will trigger an auto item for each item which feels silly.
author=GreatRedSpirit

I`ve tested the script inside of my game and it works perfectly, thank you so much! Then I`ll wait for the final version, and also, how would you like to be credited? If you don`t have any preferences, I`ll just use your nickname.
Glad to hear it works for you! Just a kudos to GreatRedSpirit in your game or gameprofile is all I need.

For the script here's where I so far plan to take it:

- Get the conditions for auto items out of the script and into the items.

- Get auto items that trigger after an enemy targets an actor to process _after_ a skill completes, otherwise it disrupts the flow of a turn.

- Split the conditions so certain items are only used at certain times. Right now its tailored for NaoKernel's grains where a grain is used at any time when MP hits zero. However if somebody wants an auto potion that only triggers after an enemy action the script wont' do it since an actor will also use a potion after their turn if they meet the conditions for it. For now at least have:
+ Use item after turn
+ Use item after targeted by enemy (with apathy towards result)

- Make sure it works! Test it with a variety of combinations, like party-wide items, different conditions, and the party in different states. The script itself simply triggers using an item if the condition is met so the item used can be quite a few things including an attack item.
Alright, I made one that actually works, isn't hard coded, and tested with more than one item and character in the party! (spoilers there were bugs when anything changed)

class Scene_Battle < Scene_Base

  #alias :use_item_autoitem :use_item unless $@
  def use_item(*args)
    # First call the original method
    item = @subject.current_action.item
    @log_window.display_use_item(@subject, item)
    @subject.use_item(item)
    refresh_status
    targets = @subject.current_action.make_targets.compact
    show_animation(targets, item.animation_id)
    targets.each {|target| item.repeats.times { invoke_item(target, item) } }
    
    # If the subject is an actor then they can use an autoitem after their turn
    if @subject.is_a? Game_Actor
      use_autoitem(@subject, :after_action)
    # Otherwise if its an enemy then actors can act if they were a target
    elsif @subject.is_a? Game_Enemy
      # Use the determined actors list to see who gets a turn at auto item!
      targets.each do |target|
        use_autoitem(target, :after_target, @subject)
      end
    end
  end


  def use_autoitem(actor, phase = :after_target, source = nil)
    # Sanity check, we only deal with actors
    return unless actor.is_a? Game_Actor and actor.alive?
    
    # Look for items in the player's inventory that the current actor can
    # use as an autoitem
    auto_item = get_autoitem(actor, phase)
    # If no auto item was found then there's no work to do
    return if auto_item.nil?
    
    # Otherwise we found an item and it's time to use it!
    
    # Prepare to use the item! This will display it to the user and consume the item
    @log_window.display_use_item(actor, auto_item)
    actor.use_item(auto_item)
    refresh_status
    
    # And generate the targets for it
    targets = []
    
    # If the item is a single-target ally item then the user will use it on themselves
    if auto_item.for_friend? and not auto_item.for_all?
      targets = [actor]
    # If the target is for a single enemy use the source target (if they aren't nil)
    elsif auto_item.for_opponent? and not auto_item.for_all? and not source.nil?
      targets = [source]
    # Otherwise just use a standard action to generate our targets
    else
      # Create an action to represent this auto item execution
      action = Game_Action.new(actor, true)
      action.set_item(auto_item.id)
      targets = action.make_targets.compact
    end
    
    # Show the animation for the item being used
    show_animation(targets, auto_item.animation_id)
    # Now use the item!
    targets.each {|target| auto_item.repeats.times { invoke_item(target, auto_item) } }
    
  end
  
  def get_autoitem(actor, phase)
    case phase
      when :after_action
        return  $game_party.items.
          select{|x| x.auto_item_condition_after_action(actor, $game_party, $game_troop)}.
          sort_by{ |a| a.auto_item_priority_after_action(actor, $game_party, $game_troop) }.
          last;
      when :after_target
        return $game_party.items.
          select{|x| x.auto_item_condition_after_target(actor, $game_party, $game_troop)}.
          sort_by{ |a| a.auto_item_priority_after_target(actor, $game_party, $game_troop) }.
          last;
    end
    
  end
  
end



class RPG::Item < RPG::UsableItem
  
  AutoItemConditionAfterActionRegEx =
    /<AutoItemConditionAfterAction>(.*)<\/AutoItemConditionAfterAction>/mix
   
  def auto_item_condition_after_action(user, party, enemies)
    # First get the eval to execute for the item
    if @aicaa.nil?
      if @note =~ AutoItemConditionAfterActionRegEx
        @aicaa = $1
      else
        @aicaa = ""
      end
    end
    # If the condition eval is empty then this is always false
    return false if @aicaa == ""
    # Otherwise try to execute the condition
    begin
      return eval(@aicaa)
    rescue
      print "Error occured evaluating #{@aicaa} in auto_item_condition_after_action, error: #{$!}\n"
      return false
    end
  end
  
  AutoItemPriorityAfterActionRegEx =
    /<AutoItemPriorityAfterAction>(.*)<\/AutoItemPriorityAfterAction>/mix
  
  def auto_item_priority_after_action(user, party, enemies)
    # First get the eval to execute for the item
    if @aipaa.nil?
      if @note =~ AutoItemPriorityAfterActionRegEx
        @aipaa = $1
      else
        @aipaa = ""
      end
    end
    # If the condition eval is empty then this is always false
    return 0 if @aipaa == ""
    # Otherwise try to execute the condition
    begin
      return eval(@aipaa)
    rescue
      print "Error occured evaluating #{@aipaa} in auto_item_priority_after_action, error: #{$!}\n"
      return false
    end
  end
  
  AutoItemConditionAfterTargetRegEx =
    /<AutoItemConditionAfterTarget>(.*)<\/AutoItemConditionAfterTarget>/mix
    
  def auto_item_condition_after_target(user, party, enemies)
    # First get the eval to execute for the item
    if @aicat.nil?
      if @note =~ AutoItemConditionAfterTargetRegEx
        @aicat = $1
      else
        @aicat = ""
      end
    end
    # If the condition eval is empty then this is always false
    return false if @aicat == ""
    # Otherwise try to execute the condition
    begin
      return eval(@aicat)
    rescue
      print "Error occured evaluating #{@aicat} in auto_item_condition_after_target, error: #{$!}\n"
      return false
    end
  end
  
  AutoItemPriorityAfterTargetRegEx =
    /<AutoItemPriorityAfterTarget>(.*)<\/AutoItemPriorityAfterTarget>/mix
  
  def auto_item_priority_after_target(user, party, enemies)
    # First get the eval to execute for the item
    if @aipat.nil?
      if @note =~ AutoItemPriorityAfterTargetRegEx
        @aipat = $1
      else
        @aipat = ""
      end
    end
    # If the condition eval is empty then this is always false
    return 0 if @aipat == ""
    # Otherwise try to execute the condition
    begin
      return eval(@aipat)
    rescue
      print "Error occured evaluating #{@aipat} in auto_item_priority_after_target, error: #{$!}\n"
      return -1
    end
  end
end


Demo project here. Some changes!

- I added a second actor to the party. When a character uses a single target healing item they will always use it on themselves right now. Before it would randomly pick one.

- If an offensive item is used as an auto item and it only targets one enemy it'll auto target to whoever targeted the actor who's using an auto item. Unless they used it after their action (aka nobody targeted them) where it'll pick one at random.

- You can set the conditions and priority of auto items in an item's note box. Look at Grain, Hi-Potion, and Fire Bag for examples. Two are the condition for after taking an action, one is the condition after being targeted by an enemy. It accepts ruby script as conditions which you can see with the Grain item:

Grain
<AutoItemConditionAfterAction>
user.mp == 0 && user.id == 1
</AutoItemConditionAfterAction>
<AutoItemPriorityAfterAction>
2
</AutoItemPriorityAfterAction>

<AutoItemConditionAfterTarget>
auto_item_condition_after_action(user, party, enemies)
</AutoItemConditionAfterTarget>
<AutoItemPriorityAfterTarget>
2
</AutoItemPriorityAfterTarget>


In <AutoItemConditionAfterAction> there's ruby code saying that the grain can only be used when the user's MP is zero and, for demo purposes, only when Eric has zero MP. The <AutoItemConditionAfterAction> tag is the condition to check after an actor takes a turn, not when they are targeted by an enemy.

<AutoItemPriorityAfterAction> is the priority, or order, to use auto items in. It goes high to low and can take decimal numbers. This has some power too, more on that later.


<AutoItemConditionAfterTarget> is the condition for after being targeted by an enemy. This code here will just use what is in the <AutoItemConditionAfterAction> tag so changing the condition for grains can be done in one place instead of two.

<AutoItemPriorityAfterTarget> is the priority for using this auto item after being targeted.


The hi-potion is a bit different:
<AutoItemConditionAfterTarget>
user.hp < user.mhp
</AutoItemConditionAfterTarget>
<AutoItemPriorityAfterTarget>
1
</AutoItemPriorityAfterTarget>


It's only condition is after being targeted. Since it doesn't have a <AutoItemConditionAfterAction> tag it'll only be used after being targeted by an enemy. user.hp < user.mhp means the actor using the item's HP must be below MaxHP in order to use the item. It has a boring priority.


Fire Bag is again a bit different:
<AutoItemConditionAfterTarget>
true
</AutoItemConditionAfterTarget>
<AutoItemPriorityAfterTarget>
user.id == 2 ? 3 : 0
</AutoItemPriorityAfterTarget>


<AutoItemConditionAfterTarget> has 'true' in it which means its condition is always met. If you have any Fire Bags in the player's inventory an actor will use one after being targeted.

<AutoItemPriorityAfterTarget> is a bit tricky. Instead of having a simply defined priority it can check who is using the auto item. In this case if the actor's ID is 2 (aka Natalie) they'll use the Fire Bag on a higher priority than anyone else (like Eric).


It's more complex than what it was before since it has some juice so if you need any help setting it up let me know. Or if you find any bugs. Or think of anything else you want it to do, let me know.


death to noteboxes
author=GreatRedSpirit

Finally`ve managed test it! The third version of script works perfectly, but there is a little problem with MOG`s Blitz Commands script: it just doesn`t work. I pasted the second version of your script, and Blitz Commands executed just fine, so I guess the problem lies in the third version of the script.
I know the reason: I had to override the use_item method instead of alias it because the make_targets method wasn't determinate. Post a link to the script and I'll find a fix.


e: In fact, never mind. I know how to improve my script so I can alias the use_item method again to ensure script compatibility.

e2: Fixed. c/p this script over my old one:
class Scene_Battle < Scene_Base

  alias :use_item_autoitem :use_item unless $@
  def use_item(*args)
    # First call the original method
    use_item_autoitem
    # And get the targets last hit by the above method
    targets = @subject.current_action.last_targets.compact
    
    # If the subject is an actor then they can use an autoitem after their turn
    if @subject.is_a? Game_Actor
      use_autoitem(@subject, :after_action)
    # Otherwise if its an enemy then actors can act if they were a target
    elsif @subject.is_a? Game_Enemy and not targets.nil?
      # Use the determined actors list to see who gets a turn at auto item!
      targets.each do |target|
        use_autoitem(target, :after_target, @subject)
      end
    end
  end


  def use_autoitem(actor, phase = :after_target, source = nil)
    # Sanity check, we only deal with actors
    return unless actor.is_a? Game_Actor and actor.alive?
    
    # Look for items in the player's inventory that the current actor can
    # use as an autoitem
    auto_item = get_autoitem(actor, phase)
    # If no auto item was found then there's no work to do
    return if auto_item.nil?
    
    # Otherwise we found an item and it's time to use it!
    
    # Prepare to use the item! This will display it to the user and consume the item
    @log_window.display_use_item(actor, auto_item)
    actor.use_item(auto_item)
    refresh_status
    
    # And generate the targets for it
    targets = []
    
    # If the item is a single-target ally item then the user will use it on themselves
    if auto_item.for_friend? and not auto_item.for_all?
      targets = [actor]
    # If the target is for a single enemy use the source target (if they aren't nil)
    elsif auto_item.for_opponent? and not auto_item.for_all? and not source.nil?
      targets = [source]
    # Otherwise just use a standard action to generate our targets
    else
      # Create an action to represent this auto item execution
      action = Game_Action.new(actor, true)
      action.set_item(auto_item.id)
      targets = action.make_targets.compact
    end
    
    # Show the animation for the item being used
    show_animation(targets, auto_item.animation_id)
    # Now use the item!
    targets.each {|target| auto_item.repeats.times { invoke_item(target, auto_item) } }
    
  end
  
  def get_autoitem(actor, phase)
    case phase
      when :after_action
        return  $game_party.items.
          select{|x| x.auto_item_condition_after_action(actor, $game_party, $game_troop)}.
          sort_by{ |a| a.auto_item_priority_after_action(actor, $game_party, $game_troop) }.
          last;
      when :after_target
        return $game_party.items.
          select{|x| x.auto_item_condition_after_target(actor, $game_party, $game_troop)}.
          sort_by{ |a| a.auto_item_priority_after_target(actor, $game_party, $game_troop) }.
          last;
    end
    
  end
  
end

class Game_Action

  # We need to access the last targets of this action without invoking
  # make_targets again since it isn't determinate. This will cache the last
  # targets and make it accessible
  attr_reader :last_targets

  alias :make_targets_autoitem :make_targets unless $@
  def make_targets
    @last_targets = make_targets_autoitem
    return @last_targets
  end
  
end



class RPG::Item < RPG::UsableItem
  
  AutoItemConditionAfterActionRegEx =
    /<AutoItemConditionAfterAction>(.*)<\/AutoItemConditionAfterAction>/mix
   
  def auto_item_condition_after_action(user, party, enemies)
    # First get the eval to execute for the item
    if @aicaa.nil?
      if @note =~ AutoItemConditionAfterActionRegEx
        @aicaa = $1
      else
        @aicaa = ""
      end
    end
    # If the condition eval is empty then this is always false
    return false if @aicaa == ""
    # Otherwise try to execute the condition
    begin
      return eval(@aicaa)
    rescue
      print "Error occured evaluating #{@aicaa} in auto_item_condition_after_action, error: #{$!}\n"
      return false
    end
  end
  
  AutoItemPriorityAfterActionRegEx =
    /<AutoItemPriorityAfterAction>(.*)<\/AutoItemPriorityAfterAction>/mix
  
  def auto_item_priority_after_action(user, party, enemies)
    # First get the eval to execute for the item
    if @aipaa.nil?
      if @note =~ AutoItemPriorityAfterActionRegEx
        @aipaa = $1
      else
        @aipaa = ""
      end
    end
    # If the condition eval is empty then this is always false
    return 0 if @aipaa == ""
    # Otherwise try to execute the condition
    begin
      return eval(@aipaa)
    rescue
      print "Error occured evaluating #{@aipaa} in auto_item_priority_after_action, error: #{$!}\n"
      return false
    end
  end
  
  AutoItemConditionAfterTargetRegEx =
    /<AutoItemConditionAfterTarget>(.*)<\/AutoItemConditionAfterTarget>/mix
    
  def auto_item_condition_after_target(user, party, enemies)
    # First get the eval to execute for the item
    if @aicat.nil?
      if @note =~ AutoItemConditionAfterTargetRegEx
        @aicat = $1
      else
        @aicat = ""
      end
    end
    # If the condition eval is empty then this is always false
    return false if @aicat == ""
    # Otherwise try to execute the condition
    begin
      return eval(@aicat)
    rescue
      print "Error occured evaluating #{@aicat} in auto_item_condition_after_target, error: #{$!}\n"
      return false
    end
  end
  
  AutoItemPriorityAfterTargetRegEx =
    /<AutoItemPriorityAfterTarget>(.*)<\/AutoItemPriorityAfterTarget>/mix
  
  def auto_item_priority_after_target(user, party, enemies)
    # First get the eval to execute for the item
    if @aipat.nil?
      if @note =~ AutoItemPriorityAfterTargetRegEx
        @aipat = $1
      else
        @aipat = ""
      end
    end
    # If the condition eval is empty then this is always false
    return 0 if @aipat == ""
    # Otherwise try to execute the condition
    begin
      return eval(@aipat)
    rescue
      print "Error occured evaluating #{@aipat} in auto_item_priority_after_target, error: #{$!}\n"
      return -1
    end
  end
end
author=GreatRedSpirit

Yyyyyep, now everything works fine! Thanks for the script again, I will write you if I find any other bugs.
Thanks. Please just thank me in your credits is all I ask for.

I'll probably update it again with documentation and submit it to the site. If I find any issues I'll send ya a PM.
author=GreatRedSpirit
Found another issue, and again with MOG`s Blitz Commands Script http://www.atelier-rgss.com/RGSS/Battle/ACE_BAT21.html
When pressing a wrong button or being out of time, a mistake saying "Script "Auto Item" line 8: NoMethodError" appears.
Sorry, I missed your post!

Sounds like the blitz script doesn't always generate a target which my script assumes does. If you go into it and find the bit near the top
targets = @subject.current_action.last_targets.compact


And change it to
targets = @subject.current_action.last_targets.compact unless @subject.current_action.last_targets.nil?

Hopefully that'll do the trick. This will have it not assume there are always valid targets from the last action.

(I even had a bit of code that did the check for no valid actions but didn't think think it all the way through which caused this bug)
Pages: first 12 next last