Submit Your Article


 
RPG Maker

Welcome Guest ( Log In | Register )


  Games Resources RPG Maker VX RPG Maker XP Scripts Tutorials Downloads

 
Closed TopicStart new topic
> NPC Actors, How to make some of your heroes controlled by CPU
Jens of Zanicuud
post Dec 8 2011, 04:56 AM
Post #1


Dark Jentleman
Group Icon

Group: Local Mod
Posts: 904
Type: Scripter
RM Skill: Skilled
Rev Points: 120




RPG NPC Actors

Author: Jens of Zanicuud
Version: 1.0
Released on: 9 Dec 2011
Terms of use: Free use, just credit; for commercial use, you have to ask for permission.
Customization: Free, just link the mod version in this topic...
FAQ or help:Feel free to ask for everything.

Hello everyone again.

This time I'll post the Game_NPC script system...

It handles with heroes controlled by computer. You can set their actions ratings in the Skill panel...
I think it could be useful for host characters or Summons.

To use it, just create a new script and paste the code below.

N.B. You'll need to download these files and put it in the Icons folder to let this script work.
Attached File  Icons.zip ( 1.46K ) Number of downloads: 44


CODE
#==============================================================================
# ** Game_NPC Version 1.0 - Jens of Zanicuud
#  Free use, but credit is needed. You can find me on www.rpgrevolution.com
#  Just advise me if you want to employ it anywhere
#------------------------------------------------------------------------------
#  This class handles with actors controlled by CPU...
#  In Skill Window you can set the rating of their actions.
#  
#==============================================================================

NPC = [5,6]  #Set here the IDs of all NPC actors

#==============================================================================
# ** Game_Party
#------------------------------------------------------------------------------
#  This class handles the party. It includes information on amount of gold
#  and items. Refer to "$game_party" for the instance of this class.
#==============================================================================

class Game_Party
  #--------------------------------------------------------------------------
  # * good_health? (i.e. all party with heal greater than 70%)
  #--------------------------------------------------------------------------
  def good_health?
    flag = true
    for actor in self.actors
      if actor.hp*100/actor.maxhp < 70.0
        flag = false
      end
    end
    return flag
  end
  #--------------------------------------------------------------------------
  # * weakest actor (i.e. actors with lesser heal)
  #--------------------------------------------------------------------------
  def weakest_actors_roulette
    weakest = [self.actors[0]]
    for actor in self.actors
      if actor.hp*100/actor.maxhp < weakest[0].hp*100/actor.maxhp
        weakest = [actor]
      elsif actor.hp*100/actor.maxhp == weakest[0].hp*100/actor.maxhp
        weakest.push(actor)
      end
    end
    return weakest[rand(weakest.size)]
  end
  
  
  
  end
#==============================================================================
# ** Game_Game_BattleAction
#------------------------------------------------------------------------------
#  Added ratings in actions
#==============================================================================

class Game_BattleAction
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :speed                    # speed
  attr_accessor :kind                     # kind (basic / skill / item)
  attr_accessor :basic                    # basic (attack / guard / escape)
  attr_accessor :skill_id                 # skill ID
  attr_accessor :item_id                  # item ID
  attr_accessor :target_index             # target index
  attr_accessor :forcing                  # forced flag
  attr_accessor :rating                   # rating
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    clear
  end
  #--------------------------------------------------------------------------
  # * Clear
  #--------------------------------------------------------------------------
  def clear
    @speed = 0
    @kind = 0
    @basic = 3
    @skill_id = 0
    @item_id = 0
    @target_index = -1
    @forcing = false
    @rating = 0
  end
  #--------------------------------------------------------------------------
  # * Skill
  #--------------------------------------------------------------------------
  def skill
    return $data_skills[self.skill_id]
  end
  #--------------------------------------------------------------------------
  # * Random Target (for NPC)
  #--------------------------------------------------------------------------
  def decide_random_target_for_NPC
    # Diverge with effect scope
    
    #NEW HP check
    if self.kind == 1 and self.skill.power < 0
      if for_one_friend?
        battler = $game_party.weakest_actors_roulette
      end
    end
    
    if battler == nil
    if for_one_friend_hp0?
      battler = $game_party.random_target_actor_hp0
    elsif for_one_friend?
      battler = $game_party.random_target_actor
    else
      battler = $game_troop.random_target_enemy
    end
    end
    # If a target exists, get an index, and if a target doesn't exist,
    # clear the action
    if battler != nil
      @target_index = battler.index
    else
      clear
    end
  end
end

#==============================================================================
# ** Game_NPC
#------------------------------------------------------------------------------
#  This class handles the actor. It's used within the Game_Actors class
#  ($game_actors) and refers to the Game_Party class ($game_party).
#==============================================================================


class Game_NPC < Game_Actor
  #--------------------------------------------------------------------------
  # * Setup
  #     actor_id : actor ID
  #--------------------------------------------------------------------------
  def setup(actor_id)
  super
  @rates = {}
  for i in -3...$data_skills.size
  @rates[i] = 4
  end
  end
  #--------------------------------------------------------------------------
  # * Actions
  #--------------------------------------------------------------------------
  def actions
    act = []
    for id in self.skills
      skill = $data_skills[id]
      ab = Game_BattleAction.new
      ab.kind = 1
      ab.skill_id = skill.id
      ab.rating = @rates[skill.id]
      act.push(ab)
    end
      ab = Game_BattleAction.new
      ab.kind = 0
      ab.basic = 0
      ab.rating = @rates[0]
      act.push(ab)
      ab = Game_BattleAction.new
      ab.kind = 0
      ab.basic = 1
      ab.rating = @rates[-1]
      act.push(ab)
    return act
  end
  #--------------------------------------------------------------------------
  # * Rates
  #--------------------------------------------------------------------------
  def rates(index)
    return @rates[index]
  end
  #--------------------------------------------------------------------------
  # * Change Rates
  #--------------------------------------------------------------------------
  def change_rate(skill_id,rate)
    @rates[skill_id] = rate
  end
  #--------------------------------------------------------------------------
  # * Make Action
  #--------------------------------------------------------------------------
  def make_action
    # Clear current action
    self.current_action.clear
    # If unable to move
    unless self.movable?
      # End Method
      return
    end
    # Extract current effective actions
    available_actions = []
    rating_max = 0
    for action in self.actions
      # Confirm sp conditions
      if action.kind == 1 and $data_skills[action.skill_id].sp_cost>self.sp
        next
      end
      # Confirm rating conditions
      if action.rating == 0
        next
      end
  #NEW# Stop healing skills to be used if there's no target
      test_skill = $data_skills[action.skill_id]
      if action.kind == 1 and test_skill.power < 0 and
        (test_skill.scope == 3 or test_skill.scope == 4)
        if $game_party.good_health?
        next
        end
      end
      # Add this action to applicable conditions
      available_actions.push(action)
      if action.rating > rating_max
        rating_max = action.rating
      end
    end
    # Calculate total with max rating value at 3 (exclude 0 or less)
    ratings_total = 0
    for action in available_actions
      if action.rating > rating_max - 3
        ratings_total += action.rating - (rating_max - 3)
      end
    end
    # If ratings total isn't 0
    if ratings_total > 0
      # Create random numbers
      value = rand(ratings_total)
      # Set things that correspond to created random numbers as current action
      for action in available_actions
        if action.rating > rating_max - 3
          if value < action.rating - (rating_max - 3)
            self.current_action.kind = action.kind
            self.current_action.basic = action.basic
            self.current_action.skill_id = action.skill_id
            self.current_action.decide_random_target_for_NPC
            return
          else
            value -= action.rating - (rating_max - 3)
          end
        end
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Inputable?
  #--------------------------------------------------------------------------
  def inputable?
    return false
  end
end

#==============================================================================
# ** Game_Actors
#------------------------------------------------------------------------------
#  This class handles the actor array. Refer to "$game_actors" for each
#  instance of this class.
#==============================================================================

class Game_Actors
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    @data = []
  end
  #--------------------------------------------------------------------------
  # * Get Actor
  #     actor_id : actor ID
  #--------------------------------------------------------------------------
  def [](actor_id)
    if actor_id > 999 or $data_actors[actor_id] == nil
      return nil
    end
    if @data[actor_id] == nil
      if NPC.include?(actor_id)
      @data[actor_id] = Game_NPC.new(actor_id)
      else
      @data[actor_id] = Game_Actor.new(actor_id)
      end
    end
    return @data[actor_id]
  end
  #--------------------------------------------------------------------------
  # * Change to NPC
  #     actor_id : actor ID
  #--------------------------------------------------------------------------
  def change_to_NPC(id)
    @data[id] = Game_NPC.new(id)
  end
end

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

class Scene_Battle

  #--------------------------------------------------------------------------
  # * Start Main Phase
  #--------------------------------------------------------------------------
  def start_phase4
    # Shift to phase 4
    @phase = 4
    # Turn count
    $game_temp.battle_turn += 1
    # Search all battle event pages
    for index in 0...$data_troops[@troop_id].pages.size
      # Get event page
      page = $data_troops[@troop_id].pages[index]
      # If this page span is [turn]
      if page.span == 1
        # Clear action completed flags
        $game_temp.battle_event_flags[index] = false
      end
    end
    # Set actor as unselectable
    @actor_index = -1
    @active_battler = nil
    # Enable party command window
    @party_command_window.active = false
    @party_command_window.visible = false
    # Disable actor command window
    @actor_command_window.active = false
    @actor_command_window.visible = false
    # Set main phase flag
    $game_temp.battle_main_phase = true
    # Make enemy action
    for enemy in $game_troop.enemies
      enemy.make_action
    end
    for actor in $game_party.actors
      if actor.is_a?(Game_NPC)
      actor.make_action
      end
    end
    # Make action orders
    make_action_orders
    # Shift to step 1
    @phase4_step = 1
  end
  end


#==============================================================================
# ** Window_Skill
#------------------------------------------------------------------------------
#  This window displays usable skills on the skill and battle screens.
#==============================================================================

class Window_Skill < Window_Selectable
  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  def refresh
    if self.contents != nil
      self.contents.dispose
      self.contents = nil
    end
    @data = []
    @data.push("Attacco") if @actor.is_a?(Game_NPC)
    @data.push("Guardia") if @actor.is_a?(Game_NPC)
    for i in 0...@actor.skills.size
      skill = $data_skills[@actor.skills[i]]
      if skill != nil
        @data.push(skill)
      end
    end
    # If item count is not 0, make a bit map and draw all items
    @item_max = @data.size
    if @item_max > 0
      self.contents = Bitmap.new(width - 32, row_max * 32)
      for i in 0...@item_max
        draw_item(i)
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Draw Item
  #     index : item number
  #--------------------------------------------------------------------------
  def draw_item(index)
    skill = @data[index]
    if skill.is_a?(String)
      case skill
      when "Attacco"
        id = 0
      when "Guardia"
        id = -1
      end
    self.contents.font.color = normal_color
    x = 4 + index % 2 * (288 + 32)
    y = index / 2 * 32
    rect = Rect.new(x, y, self.width / @column_max - 32, 32)
    self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
    case @actor.rates(id)
    when 5
    bitmap = RPG::Cache.icon("High")
    when 4
    bitmap = RPG::Cache.icon("Medium")
    when 3
    bitmap = RPG::Cache.icon("Low")
    when 0
    bitmap = RPG::Cache.icon("Never")
    end
    opacity = 255
    self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24), opacity)
    self.contents.draw_text(x + 28, y, 204, 32, skill, 0)
    else
    if @actor.skill_can_use?(skill.id) or @actor.is_a?(Game_NPC)
      self.contents.font.color = normal_color
    else
      self.contents.font.color = disabled_color
    end
    x = 4 + index % 2 * (288 + 32)
    y = index / 2 * 32
    rect = Rect.new(x, y, self.width / @column_max - 32, 32)
    self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
    if @actor.is_a?(Game_NPC)
    case @actor.rates(skill.id)
    when 5
    bitmap = RPG::Cache.icon("High")
    when 4
    bitmap = RPG::Cache.icon("Medium")
    when 3
    bitmap = RPG::Cache.icon("Low")
    when 0
    bitmap = RPG::Cache.icon("Never")
    end
    else
    bitmap = RPG::Cache.icon(skill.icon_name)
    end
    opacity = self.contents.font.color == normal_color ? 255 : 128
    self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24), opacity)
    self.contents.draw_text(x + 28, y, 204, 32, skill.name, 0)
    self.contents.draw_text(x + 232, y, 48, 32, skill.sp_cost.to_s, 2)
  end
end
  #--------------------------------------------------------------------------
  # * Help Text Update
  #--------------------------------------------------------------------------
  def update_help
    if self.skill == "Attack"
    description = "Regular attack with equipped weapon."
  elsif self.skill == "Guard"
    description = "Halves damage received."
    else
    description = self.skill.description if self.skill != nil
    end
    @help_window.set_text(description == nil ? "" : description )
  end
end

#==============================================================================
# ** Scene_Skill
#------------------------------------------------------------------------------
#  This class performs skill screen processing.
#==============================================================================

class Scene_Skill
  #--------------------------------------------------------------------------
  # * Object Initialization
  #     actor_index : actor index
  #--------------------------------------------------------------------------
  def initialize(actor_index = 0, equip_index = 0)
    @actor_index = actor_index
  end
  #--------------------------------------------------------------------------
  # * Main Processing
  #--------------------------------------------------------------------------
  def main
    # Get actor
    @actor = $game_party.actors[@actor_index]
    # Make help window, status window, and skill window
    @help_window = Window_Help.new
    @status_window = Window_SkillStatus.new(@actor)
    @skill_window = Window_Skill.new(@actor)
    # Associate help window
    @skill_window.help_window = @help_window
    # Make target window (set to invisible / inactive)
    @target_window = Window_Target.new
    @target_window.visible = false
    @target_window.active = false
    # Execute transition
    Graphics.transition
    # Main loop
    loop do
      # Update game screen
      Graphics.update
      # Update input information
      Input.update
      # Frame update
      update
      # Abort loop if screen is changed
      if $scene != self
        break
      end
    end
    # Prepare for transition
    Graphics.freeze
    # Dispose of windows
    @help_window.dispose
    @status_window.dispose
    @skill_window.dispose
    @target_window.dispose
  end
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    # Update windows
    @help_window.update
    @status_window.update
    @skill_window.update
    @target_window.update
    # If skill window is active: call update_skill
    if @skill_window.active
      update_skill
      return
    end
    # If skill target is active: call update_target
    if @target_window.active
      update_target
      return
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update (if skill window is active)
  #--------------------------------------------------------------------------
  def update_skill
    # If B button was pressed
    if Input.trigger?(Input::B)
      # Play cancel SE
      $game_system.se_play($data_system.cancel_se)
      # Switch to menu screen
      $scene = Scene_Menu.new(1)
      return
    end
    # If C button was pressed
    if @actor.is_a?(Game_NPC)
      if Input.trigger?(Input::C)
      $game_system.se_play($data_system.decision_se)
      @skill = @skill_window.skill if @skill_window.index > 0
      update_command
      return
      end
      else
    if Input.trigger?(Input::C)
      # Get currently selected data on the skill window
      @skill = @skill_window.skill
      # If unable to use
      if @skill == nil or not @actor.skill_can_use?(@skill.id)
        # Play buzzer SE
        $game_system.se_play($data_system.buzzer_se)
        return
      end
      # Play decision SE
      $game_system.se_play($data_system.decision_se)
      # If effect scope is ally
      if @skill.scope >= 3
        # Activate target window
        @skill_window.active = false
        @target_window.x = (@skill_window.index + 1) % 2 * 304
        @target_window.visible = true
        @target_window.active = true
        # Set cursor position to effect scope (single / all)
        if @skill.scope == 4 || @skill.scope == 6
          @target_window.index = -1
        elsif @skill.scope == 7
          @target_window.index = @actor_index - 10
        else
          @target_window.index = 0
        end
      # If effect scope is other than ally
      else
        # If common event ID is valid
        if @skill.common_event_id > 0
          # Common event call reservation
          $game_temp.common_event_id = @skill.common_event_id
          # Play use skill SE
          $game_system.se_play(@skill.menu_se)
          # Use up SP
          @actor.sp -= @skill.sp_cost
          # Remake each window content
          @status_window.refresh
          @skill_window.refresh
          @target_window.refresh
          # Switch to map screen
          $scene = Scene_Map.new
          return
        end
      end
      return
    end
    end
    # If R button was pressed
    if Input.trigger?(Input::R)
      # Play cursor SE
      $game_system.se_play($data_system.cursor_se)
      # To next actor
      @actor_index += 1
      @actor_index %= $game_party.actors.size
      # Switch to different skill screen
      $scene = Scene_Skill.new(@actor_index)
      return
    end
    # If L button was pressed
    if Input.trigger?(Input::L)
      # Play cursor SE
      $game_system.se_play($data_system.cursor_se)
      # To previous actor
      @actor_index += $game_party.actors.size - 1
      @actor_index %= $game_party.actors.size
      # Switch to different skill screen
      $scene = Scene_Skill.new(@actor_index)
      return
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update (when target window is active)
  #--------------------------------------------------------------------------
  def update_target
    # If B button was pressed
    if Input.trigger?(Input::B)
      # Play cancel SE
      $game_system.se_play($data_system.cancel_se)
      # Erase target window
      @skill_window.active = true
      @target_window.visible = false
      @target_window.active = false
      return
    end
    # If C button was pressed
    if Input.trigger?(Input::C)
      # If unable to use because SP ran out
      unless @actor.skill_can_use?(@skill.id)
        # Play buzzer SE
        $game_system.se_play($data_system.buzzer_se)
        return
      end
      # If target is all
      if @target_window.index == -1
        # Apply skill use effects to entire party
        used = false
        for i in $game_party.actors
          used |= i.skill_effect(@actor, @skill)
        end
      end
      # If target is user
      if @target_window.index <= -2
        # Apply skill use effects to target actor
        target = $game_party.actors[@target_window.index + 10]
        used = target.skill_effect(@actor, @skill)
      end
      # If single target
      if @target_window.index >= 0
        # Apply skill use effects to target actor
        target = $game_party.actors[@target_window.index]
        used = target.skill_effect(@actor, @skill)
      end
      # If skill was used
      if used
        # Play skill use SE
        $game_system.se_play(@skill.menu_se)
        # Use up SP
        @actor.sp -= @skill.sp_cost
        # Remake each window content
        @status_window.refresh
        @skill_window.refresh
        @target_window.refresh
        # If entire party is dead
        if $game_party.all_dead?
          # Switch to game over screen
          $scene = Scene_Gameover.new
          return
        end
        # If command event ID is valid
        if @skill.common_event_id > 0
          # Command event call reservation
          $game_temp.common_event_id = @skill.common_event_id
          # Switch to map screen
          $scene = Scene_Map.new
          return
        end
      end
      # If skill wasn't used
      unless used
        # Play buzzer SE
        $game_system.se_play($data_system.buzzer_se)
      end
      return
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update (when arrow window is active)
  #--------------------------------------------------------------------------
  def update_command
      if @skill_window.index == 0
      rate = 0
      elsif @skill_window.index == 1
      rate = -1
      else
      rate = @skill.id
      end
      case @actor.rates(rate)
      when 0
      rate = 3
      when 3
      rate = 4
      when 4
      rate = 5
      when 5
      rate = 0
      end
      if @skill_window.index == 0
      @actor.change_rate(0,rate)
      elsif @skill_window.index == 1
      @actor.change_rate(-1,rate)
      else
      @actor.change_rate(@skill.id,rate)
      end
      @skill_window.refresh
      return
    end
end


That's all folks! I hope it can be useful.
See you next script...

Jens

This post has been edited by Jens of Zanicuud: Dec 10 2011, 04:51 AM


__________________________
"Thorns are the rose's sweetest essence..."
-Jens of Zanicuud


Games I'm working on:
>

official website: TryAdIne eFfeCt

>

Games I worked on (mainly as a scripter):
>
(Warning: it's a 3rr3's project and it's in Italian!)


Awards

Go to the top of the page
 
+Quote Post
   
Night_Runner
post Dec 9 2011, 07:32 PM
Post #2


Level 50
Group Icon

Group: +Gold Member
Posts: 1,523
Type: Scripter
RM Skill: Undisclosed




You define the NPC array twice, once on line 11 and once on line 180.
I also had a problem where the NPC's aren't very smart, my Cleric decided to try and heal party members that had completely full HP.

Otherwise, I really like the premise of the script, and I'm sure it'll come in useful for some people smile.gif


__________________________
K.I.S.S.
Want help with your scripting problems? Upload a demo! Or at the very least; provide links to the scripts in question.

Most important guide ever: Newbie's Guide to Switches
Go to the top of the page
 
+Quote Post
   
Jens of Zanicuud
post Dec 10 2011, 01:44 AM
Post #3


Dark Jentleman
Group Icon

Group: Local Mod
Posts: 904
Type: Scripter
RM Skill: Skilled
Rev Points: 120




QUOTE (Night_Runner @ Dec 10 2011, 04:32 AM) *
You define the NPC array twice, once on line 11 and once on line 180.
I also had a problem where the NPC's aren't very smart, my Cleric decided to try and heal party members that had completely full HP.

Otherwise, I really like the premise of the script, and I'm sure it'll come in useful for some people smile.gif


Yeah, I actually recicled enemies' AI, but can be SERIOUSLY improved...
Well, actually I made enemies smarter, but as regards NPC...
The problem is that I haven't tested it with healing skills, just offensive.

I'll fix the double declaration bug quickly, then...
When the New AI system will be complete, I will just post it in this section or edit the previous script.

Thanks for support and help.

Jens

EDIT: Ok, Night Runner, I've updated the script. Now your Mystic won't cure if there's no need.
I've just modified the script in the first topic of the post.

In fact, there's still a problem as regards healing altered status, but I hope to solve it quinckly, just some other manipulation...
I've created this script for a project in which magic didn't exist, so I forgot to fix that healing issue.

Major updates will come soon (i.e. item usage and stuff like this...)
Please, give feedback!

Jens

This post has been edited by Jens of Zanicuud: Dec 10 2011, 04:55 AM


__________________________
"Thorns are the rose's sweetest essence..."
-Jens of Zanicuud


Games I'm working on:
>

official website: TryAdIne eFfeCt

>

Games I worked on (mainly as a scripter):
>
(Warning: it's a 3rr3's project and it's in Italian!)


Awards

Go to the top of the page
 
+Quote Post
   
Jens of Zanicuud
post Dec 10 2011, 07:20 AM
Post #4


Dark Jentleman
Group Icon

Group: Local Mod
Posts: 904
Type: Scripter
RM Skill: Skilled
Rev Points: 120




RPG NPC Actors v 1.2

Author: Jens of Zanicuud
Version: 1.2
Released on: 9 Dec 2011
Terms of use: Free use, just credit; for commercial use, you have to ask for permission.
Customization: Free, just link the mod version in this topic...
FAQ or help:Feel free to ask for everything.

Ok, guys, some bug fixed and some features added...
Let me explain...

-NPC will heal only if necessary;
-NPC will heal status only if necessary;
-NPC will cast status like Sharpen, Barrier and so on only if needed;
-RATINGS relative to frequency can be customized to privilege High Rating or equilibrating the whole of them.


N.B. Icons posted before are mandatory to use this script...

New code:

CODE
#==============================================================================
# ** Game_NPC Version 1.2 - Jens of Zanicuud
#  Free use, but credit is needed. You can find me on www.rpgrevolution.com
#  Just advise me if you want to employ it anywhere
#------------------------------------------------------------------------------
#  This class handles with actors controlled by CPU...
#  In Skill Window you can set the rating of their actions.
#  
#==============================================================================

NPC   = [5,6]     #Set here the IDs of all NPC actors
RATES = [0,3,5,7] #Set here the ratings for Never/Low/Medium/High priority
HEAL_RATE = 70.0 #Set here the heal rate, i.e. the hp percentage below which NPC will cure (a float is better than a int...)


#==============================================================================
# ** Game_Party
#------------------------------------------------------------------------------
#  This class handles the party. It includes information on amount of gold
#  and items. Refer to "$game_party" for the instance of this class.
#==============================================================================

class Game_Party
  #--------------------------------------------------------------------------
  # * good_health? (i.e. all party with heal greater than 70%)
  #--------------------------------------------------------------------------
  def good_health?
    flag = true
    for actor in self.actors
      if actor.hp*100/actor.maxhp < HEAL_RATE
        flag = false
      end
    end
    return flag
  end
  #--------------------------------------------------------------------------
  # * weakest actor (i.e. actors with lesser heal)
  #--------------------------------------------------------------------------
  def weakest_actors_roulette
    weakest = [self.actors[0]]
    for actor in self.actors
      if actor.hp*100/actor.maxhp < weakest[0].hp*100/actor.maxhp
        weakest = [actor]
      elsif actor.hp*100/actor.maxhp == weakest[0].hp*100/actor.maxhp
        weakest.push(actor)
      end
    end
    return weakest[rand(weakest.size)]
  end
  #--------------------------------------------------------------------------
  # * random actor with a specified status
  #--------------------------------------------------------------------------
  def rand_act_status(id)
    state = []
    for actor in self.actors
      if actor.state?(id)
        state.push(actor)
      end
    end
    return state[rand(state.size)]
  end
  #--------------------------------------------------------------------------
  # * random actor with a specified status in a skill selection
  #--------------------------------------------------------------------------
  def rand_act_status_max(skill)
    state = []
    for id in skill.minus_state_set
    for actor in self.actors
      if actor.state?(id)
        state.push(actor)
      end
    end
    end
    return state[rand(state.size)]
  end
  #--------------------------------------------------------------------------
  # * random actor without a specified status
  #--------------------------------------------------------------------------
  def rand_act_non_status(id)
    state = []
    for actor in self.actors
      if !actor.state?(id)
        state.push(actor)
      end
    end
    return state[rand(state.size)]
  end
  #--------------------------------------------------------------------------
  # * random actor without a specified status in a skill selection
  #--------------------------------------------------------------------------
  def rand_act_non_status_max(skill)
    state = []
    for id in skill.plus_state_set
    for actor in self.actors
      if !actor.state?(id)
        state.push(actor)
      end
    end
    end
    return state[rand(state.size)]
  end
  end
#==============================================================================
# ** Game_Game_BattleAction
#------------------------------------------------------------------------------
#  Added ratings in actions
#==============================================================================

class Game_BattleAction
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :speed                    # speed
  attr_accessor :kind                     # kind (basic / skill / item)
  attr_accessor :basic                    # basic (attack / guard / escape)
  attr_accessor :skill_id                 # skill ID
  attr_accessor :item_id                  # item ID
  attr_accessor :target_index             # target index
  attr_accessor :forcing                  # forced flag
  attr_accessor :rating                   # rating
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    clear
  end
  #--------------------------------------------------------------------------
  # * Clear
  #--------------------------------------------------------------------------
  def clear
    @speed = 0
    @kind = 0
    @basic = 3
    @skill_id = 0
    @item_id = 0
    @target_index = -1
    @forcing = false
    @rating = 0
  end
  #--------------------------------------------------------------------------
  # * Skill
  #--------------------------------------------------------------------------
  def skill
    return $data_skills[self.skill_id]
  end
  #--------------------------------------------------------------------------
  # * Random Target (for NPC)
  #--------------------------------------------------------------------------
  def decide_random_target_for_NPC
    # Diverge with effect scope
    
    #NEW HP check
    if self.kind == 1 and self.skill.power < 0
      if for_one_friend?
        battler = $game_party.weakest_actors_roulette
      end
    end
    
    #NEW state minus check
    if battler == nil
    if self.kind == 1 and self.skill.minus_state_set.size != 0
      if for_one_friend?
        battler = $game_party.rand_act_status_max(skill)
      end
     end  
    end
    
    #NEW state plus check
    if battler == nil
    if self.kind == 1 and self.skill.plus_state_set.size != 0
      if for_one_friend?
        battler = $game_party.rand_act_non_status_max(skill)
      end
     end  
    end
    
    if battler == nil
    if for_one_friend_hp0?
      battler = $game_party.random_target_actor_hp0
    elsif for_one_friend?
      battler = $game_party.random_target_actor
    else
      battler = $game_troop.random_target_enemy
    end
    end
    # If a target exists, get an index, and if a target doesn't exist,
    # clear the action
    if battler != nil
      @target_index = battler.index
    else
      clear
    end
  end
end

#==============================================================================
# ** Game_NPC
#------------------------------------------------------------------------------
#  This class handles the actor. It's used within the Game_Actors class
#  ($game_actors) and refers to the Game_Party class ($game_party).
#==============================================================================


class Game_NPC < Game_Actor
  #--------------------------------------------------------------------------
  # * Setup
  #     actor_id : actor ID
  #--------------------------------------------------------------------------
  def setup(actor_id)
  super
  @rates = {}
  for i in -3...$data_skills.size
  @rates[i] = RATES[2] #Set default value to Medium
  end
  end
  #--------------------------------------------------------------------------
  # * Actions
  #--------------------------------------------------------------------------
  def actions
    act = []
    for id in self.skills
      skill = $data_skills[id]
      ab = Game_BattleAction.new
      ab.kind = 1
      ab.skill_id = skill.id
      ab.rating = @rates[skill.id]
      act.push(ab)
    end
      ab = Game_BattleAction.new
      ab.kind = 0
      ab.basic = 0
      ab.rating = @rates[0]
      act.push(ab)
      ab = Game_BattleAction.new
      ab.kind = 0
      ab.basic = 1
      ab.rating = @rates[-1]
      act.push(ab)
    return act
  end
  #--------------------------------------------------------------------------
  # * Rates
  #--------------------------------------------------------------------------
  def rates(index)
    return @rates[index]
  end
  #--------------------------------------------------------------------------
  # * Change Rates
  #--------------------------------------------------------------------------
  def change_rate(skill_id,rate)
    @rates[skill_id] = rate
  end
  #--------------------------------------------------------------------------
  # * Make Action
  #--------------------------------------------------------------------------
  def make_action
    # Clear current action
    self.current_action.clear
    # If unable to move
    unless self.movable?
      # End Method
      return
    end
    # Extract current effective actions
    available_actions = []
    rating_max = 0
    for action in self.actions
      # Confirm sp conditions
      if action.kind == 1 and $data_skills[action.skill_id].sp_cost>self.sp
        next
      end
      # Confirm rating conditions
      if action.rating == 0
        next
      end
  #NEW# Stop healing skills to be used if there's no target
      test_skill = $data_skills[action.skill_id]
      if action.kind == 1 and test_skill.power < 0 and
        (test_skill.scope == 3 or test_skill.scope == 4)
        if $game_party.good_health?
        next
        end
      end
  #NEW# Stop healing status if there's no target
      test_skill = $data_skills[action.skill_id]
      if action.kind == 1 and test_skill.minus_state_set.size != 0 and
        (test_skill.scope == 3 or test_skill.scope == 4)
        flag = false
        for id in test_skill.minus_state_set
          if $game_party.rand_act_status(id) != nil
            flag = true
          end
        end
        
        if !flag
          next
        end
        
      end
      
   #NEW# Stop giving status if there's no target
      test_skill = $data_skills[action.skill_id]
      if action.kind == 1 and test_skill.plus_state_set.size != 0 and
        (test_skill.scope == 3 or test_skill.scope == 4)
        flag = false
        for id in test_skill.plus_state_set
          if $game_party.rand_act_non_status(id) != nil
            flag = true
          end
        end
        
        if !flag
          next
        end
        
      end
      # Add this action to applicable conditions
      available_actions.push(action)
      if action.rating > rating_max
        rating_max = action.rating
      end
    end
    # Calculate total with max rating value at 4 (exclude 0 or less)
    ratings_total = 0
    for action in available_actions
      if action.rating > rating_max - 4
        ratings_total += action.rating - (rating_max - 4)
      end
    end
    # If ratings total isn't 0
    if ratings_total > 0
      # Create random numbers
      value = rand(ratings_total)
      # Set things that correspond to created random numbers as current action
      for action in available_actions
        if action.rating > rating_max - 4
          if value < action.rating - (rating_max - 4)
            self.current_action.kind = action.kind
            self.current_action.basic = action.basic
            self.current_action.skill_id = action.skill_id
            self.current_action.decide_random_target_for_NPC
            return
          else
            value -= action.rating - (rating_max - 4)
          end
        end
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Inputable?
  #--------------------------------------------------------------------------
  def inputable?
    return false
  end
end

#==============================================================================
# ** Game_Actors
#------------------------------------------------------------------------------
#  This class handles the actor array. Refer to "$game_actors" for each
#  instance of this class.
#==============================================================================

class Game_Actors
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    @data = []
  end
  #--------------------------------------------------------------------------
  # * Get Actor
  #     actor_id : actor ID
  #--------------------------------------------------------------------------
  def [](actor_id)
    if actor_id > 999 or $data_actors[actor_id] == nil
      return nil
    end
    if @data[actor_id] == nil
      if NPC.include?(actor_id)
      @data[actor_id] = Game_NPC.new(actor_id)
      else
      @data[actor_id] = Game_Actor.new(actor_id)
      end
    end
    return @data[actor_id]
  end
  #--------------------------------------------------------------------------
  # * Change to NPC
  #     actor_id : actor ID
  #--------------------------------------------------------------------------
  def change_to_NPC(id)
    @data[id] = Game_NPC.new(id)
  end
end

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

class Scene_Battle

  #--------------------------------------------------------------------------
  # * Start Main Phase
  #--------------------------------------------------------------------------
  def start_phase4
    # Shift to phase 4
    @phase = 4
    # Turn count
    $game_temp.battle_turn += 1
    # Search all battle event pages
    for index in 0...$data_troops[@troop_id].pages.size
      # Get event page
      page = $data_troops[@troop_id].pages[index]
      # If this page span is [turn]
      if page.span == 1
        # Clear action completed flags
        $game_temp.battle_event_flags[index] = false
      end
    end
    # Set actor as unselectable
    @actor_index = -1
    @active_battler = nil
    # Enable party command window
    @party_command_window.active = false
    @party_command_window.visible = false
    # Disable actor command window
    @actor_command_window.active = false
    @actor_command_window.visible = false
    # Set main phase flag
    $game_temp.battle_main_phase = true
    # Make enemy action
    for enemy in $game_troop.enemies
      enemy.make_action
    end
    for actor in $game_party.actors
      if actor.is_a?(Game_NPC)
      actor.make_action
      end
    end
    # Make action orders
    make_action_orders
    # Shift to step 1
    @phase4_step = 1
  end
  end


#==============================================================================
# ** Window_Skill
#------------------------------------------------------------------------------
#  This window displays usable skills on the skill and battle screens.
#==============================================================================

class Window_Skill < Window_Selectable
  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  def refresh
    if self.contents != nil
      self.contents.dispose
      self.contents = nil
    end
    @data = []
    @data.push("Attack") if @actor.is_a?(Game_NPC)
    @data.push("Guard") if @actor.is_a?(Game_NPC)
    for i in 0...@actor.skills.size
      skill = $data_skills[@actor.skills[i]]
      if skill != nil
        @data.push(skill)
      end
    end
    # If item count is not 0, make a bit map and draw all items
    @item_max = @data.size
    if @item_max > 0
      self.contents = Bitmap.new(width - 32, row_max * 32)
      for i in 0...@item_max
        draw_item(i)
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Draw Item
  #     index : item number
  #--------------------------------------------------------------------------
  def draw_item(index)
    skill = @data[index]
    if skill.is_a?(String)
      case skill
      when "Attack"
        id = 0
      when "Guard"
        id = -1
      end
    self.contents.font.color = normal_color
    x = 4 + index % 2 * (288 + 32)
    y = index / 2 * 32
    rect = Rect.new(x, y, self.width / @column_max - 32, 32)
    self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
    case @actor.rates(id)
    when RATES[3] #High
    bitmap = RPG::Cache.icon("High")
    when RATES[2] #Medium
    bitmap = RPG::Cache.icon("Medium")
    when RATES[1] #Low
    bitmap = RPG::Cache.icon("Low")
    when RATES[0] #Never
    bitmap = RPG::Cache.icon("Never")
    end
    opacity = 255
    self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24), opacity)
    self.contents.draw_text(x + 28, y, 204, 32, skill, 0)
    else
    if @actor.skill_can_use?(skill.id) or @actor.is_a?(Game_NPC)
      self.contents.font.color = normal_color
    else
      self.contents.font.color = disabled_color
    end
    x = 4 + index % 2 * (288 + 32)
    y = index / 2 * 32
    rect = Rect.new(x, y, self.width / @column_max - 32, 32)
    self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
    if @actor.is_a?(Game_NPC)
    case @actor.rates(skill.id)
    when RATES[3] #High
    bitmap = RPG::Cache.icon("High")
    when RATES[2] #Medium
    bitmap = RPG::Cache.icon("Medium")
    when RATES[1] #Low
    bitmap = RPG::Cache.icon("Low")
    when RATES[0] #Nevet
    bitmap = RPG::Cache.icon("Never")
    end
    else
    bitmap = RPG::Cache.icon(skill.icon_name)
    end
    opacity = self.contents.font.color == normal_color ? 255 : 128
    self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24), opacity)
    self.contents.draw_text(x + 28, y, 204, 32, skill.name, 0)
    self.contents.draw_text(x + 232, y, 48, 32, skill.sp_cost.to_s, 2)
  end
end
  #--------------------------------------------------------------------------
  # * Help Text Update
  #--------------------------------------------------------------------------
  def update_help
    if self.skill == "Attack"
    description = "Regular attack with equipped weapon."
  elsif self.skill == "Guard"
    description = "Halves damage received."
    else
    description = self.skill.description if self.skill != nil
    end
    @help_window.set_text(description == nil ? "" : description )
  end
end

#==============================================================================
# ** Scene_Skill
#------------------------------------------------------------------------------
#  This class performs skill screen processing.
#==============================================================================

class Scene_Skill
  #--------------------------------------------------------------------------
  # * Object Initialization
  #     actor_index : actor index
  #--------------------------------------------------------------------------
  def initialize(actor_index = 0, equip_index = 0)
    @actor_index = actor_index
  end
  #--------------------------------------------------------------------------
  # * Main Processing
  #--------------------------------------------------------------------------
  def main
    # Get actor
    @actor = $game_party.actors[@actor_index]
    # Make help window, status window, and skill window
    @help_window = Window_Help.new
    @status_window = Window_SkillStatus.new(@actor)
    @skill_window = Window_Skill.new(@actor)
    # Associate help window
    @skill_window.help_window = @help_window
    # Make target window (set to invisible / inactive)
    @target_window = Window_Target.new
    @target_window.visible = false
    @target_window.active = false
    # Execute transition
    Graphics.transition
    # Main loop
    loop do
      # Update game screen
      Graphics.update
      # Update input information
      Input.update
      # Frame update
      update
      # Abort loop if screen is changed
      if $scene != self
        break
      end
    end
    # Prepare for transition
    Graphics.freeze
    # Dispose of windows
    @help_window.dispose
    @status_window.dispose
    @skill_window.dispose
    @target_window.dispose
  end
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    # Update windows
    @help_window.update
    @status_window.update
    @skill_window.update
    @target_window.update
    # If skill window is active: call update_skill
    if @skill_window.active
      update_skill
      return
    end
    # If skill target is active: call update_target
    if @target_window.active
      update_target
      return
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update (if skill window is active)
  #--------------------------------------------------------------------------
  def update_skill
    # If B button was pressed
    if Input.trigger?(Input::B)
      # Play cancel SE
      $game_system.se_play($data_system.cancel_se)
      # Switch to menu screen
      $scene = Scene_Menu.new(1)
      return
    end
    # If C button was pressed
    if @actor.is_a?(Game_NPC)
      if Input.trigger?(Input::C)
      $game_system.se_play($data_system.decision_se)
      @skill = @skill_window.skill if @skill_window.index > 0
      update_command
      return
      end
      else
    if Input.trigger?(Input::C)
      # Get currently selected data on the skill window
      @skill = @skill_window.skill
      # If unable to use
      if @skill == nil or not @actor.skill_can_use?(@skill.id)
        # Play buzzer SE
        $game_system.se_play($data_system.buzzer_se)
        return
      end
      # Play decision SE
      $game_system.se_play($data_system.decision_se)
      # If effect scope is ally
      if @skill.scope >= 3
        # Activate target window
        @skill_window.active = false
        @target_window.x = (@skill_window.index + 1) % 2 * 304
        @target_window.visible = true
        @target_window.active = true
        # Set cursor position to effect scope (single / all)
        if @skill.scope == 4 || @skill.scope == 6
          @target_window.index = -1
        elsif @skill.scope == 7
          @target_window.index = @actor_index - 10
        else
          @target_window.index = 0
        end
      # If effect scope is other than ally
      else
        # If common event ID is valid
        if @skill.common_event_id > 0
          # Common event call reservation
          $game_temp.common_event_id = @skill.common_event_id
          # Play use skill SE
          $game_system.se_play(@skill.menu_se)
          # Use up SP
          @actor.sp -= @skill.sp_cost
          # Remake each window content
          @status_window.refresh
          @skill_window.refresh
          @target_window.refresh
          # Switch to map screen
          $scene = Scene_Map.new
          return
        end
      end
      return
    end
    end
    # If R button was pressed
    if Input.trigger?(Input::R)
      # Play cursor SE
      $game_system.se_play($data_system.cursor_se)
      # To next actor
      @actor_index += 1
      @actor_index %= $game_party.actors.size
      # Switch to different skill screen
      $scene = Scene_Skill.new(@actor_index)
      return
    end
    # If L button was pressed
    if Input.trigger?(Input::L)
      # Play cursor SE
      $game_system.se_play($data_system.cursor_se)
      # To previous actor
      @actor_index += $game_party.actors.size - 1
      @actor_index %= $game_party.actors.size
      # Switch to different skill screen
      $scene = Scene_Skill.new(@actor_index)
      return
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update (when target window is active)
  #--------------------------------------------------------------------------
  def update_target
    # If B button was pressed
    if Input.trigger?(Input::B)
      # Play cancel SE
      $game_system.se_play($data_system.cancel_se)
      # Erase target window
      @skill_window.active = true
      @target_window.visible = false
      @target_window.active = false
      return
    end
    # If C button was pressed
    if Input.trigger?(Input::C)
      # If unable to use because SP ran out
      unless @actor.skill_can_use?(@skill.id)
        # Play buzzer SE
        $game_system.se_play($data_system.buzzer_se)
        return
      end
      # If target is all
      if @target_window.index == -1
        # Apply skill use effects to entire party
        used = false
        for i in $game_party.actors
          used |= i.skill_effect(@actor, @skill)
        end
      end
      # If target is user
      if @target_window.index <= -2
        # Apply skill use effects to target actor
        target = $game_party.actors[@target_window.index + 10]
        used = target.skill_effect(@actor, @skill)
      end
      # If single target
      if @target_window.index >= 0
        # Apply skill use effects to target actor
        target = $game_party.actors[@target_window.index]
        used = target.skill_effect(@actor, @skill)
      end
      # If skill was used
      if used
        # Play skill use SE
        $game_system.se_play(@skill.menu_se)
        # Use up SP
        @actor.sp -= @skill.sp_cost
        # Remake each window content
        @status_window.refresh
        @skill_window.refresh
        @target_window.refresh
        # If entire party is dead
        if $game_party.all_dead?
          # Switch to game over screen
          $scene = Scene_Gameover.new
          return
        end
        # If command event ID is valid
        if @skill.common_event_id > 0
          # Command event call reservation
          $game_temp.common_event_id = @skill.common_event_id
          # Switch to map screen
          $scene = Scene_Map.new
          return
        end
      end
      # If skill wasn't used
      unless used
        # Play buzzer SE
        $game_system.se_play($data_system.buzzer_se)
      end
      return
    end
  end
  #--------------------------------------------------------------------------
  # * Frame Update (when arrow window is active)
  #--------------------------------------------------------------------------
  def update_command
      if @skill_window.index == 0
      rate = 0
      elsif @skill_window.index == 1
      rate = -1
      else
      rate = @skill.id
      end
    
      #case @actor.rates(rate)
      #when RATES[0]
      #rate = RATES[1]
      #when RATES[1]
      #rate = RATES[2]
      #when RATES[2]
      #rate = RATES[3]
      #when RATES[3]
      #rate = RATES[0]
      #end
      
      for i in 0...RATES.size
        if @actor.rates(rate) == RATES[i]
          rate = RATES[(i+1)%RATES.size]
          break
        end
      end
      
      if @skill_window.index == 0
      @actor.change_rate(0,rate)
      elsif @skill_window.index == 1
      @actor.change_rate(-1,rate)
      else
      @actor.change_rate(@skill.id,rate)
      end
      @skill_window.refresh
      return
    end
end


Still, it isn't a major improvement, but is better than before...

Thanks a lot Night Runner for your input!

See you next script...

Jens

EDIT:
Now we have customizable heal rate, as required...
Script was updated succesfully:)

Jens

This post has been edited by Jens of Zanicuud: Dec 11 2011, 03:13 AM


__________________________
"Thorns are the rose's sweetest essence..."
-Jens of Zanicuud


Games I'm working on:
>

official website: TryAdIne eFfeCt

>

Games I worked on (mainly as a scripter):
>
(Warning: it's a 3rr3's project and it's in Italian!)


Awards

Go to the top of the page
 
+Quote Post
   
carnie_natas
post Dec 10 2011, 07:28 PM
Post #5


~Noctem~Shinai~
Group Icon

Group: Revolutionary
Posts: 261
Type: Developer
RM Skill: Advanced




Can you not customize them to heal on a certain condition specificly like 37% or some odd number of hp?

This post has been edited by carnie_natas: Dec 10 2011, 07:38 PM


__________________________
Light one up!
You can run.....But you'll only die tired!
Go to the top of the page
 
+Quote Post
   
Jens of Zanicuud
post Dec 11 2011, 03:10 AM
Post #6


Dark Jentleman
Group Icon

Group: Local Mod
Posts: 904
Type: Scripter
RM Skill: Skilled
Rev Points: 120




QUOTE (carnie_natas @ Dec 11 2011, 04:28 AM) *
Can you not customize them to heal on a certain condition specificly like 37% or some odd number of hp?


Sure, just modify a number...

Well, actually the method that handles with this is

class Game_Party
def good_health?
flag = true
for actor in self.actors
if actor.hp*100/actor.maxhp < 70.0
flag = false
end
end
return flag
end

If you switch that 70.0 with the percentage you need, you'll have the effect you asked for:)

Maybe this worths a correction between the script, like adding a constant...

I'll do this soon...

Jens


__________________________
"Thorns are the rose's sweetest essence..."
-Jens of Zanicuud


Games I'm working on:
>

official website: TryAdIne eFfeCt

>

Games I worked on (mainly as a scripter):
>
(Warning: it's a 3rr3's project and it's in Italian!)


Awards

Go to the top of the page
 
+Quote Post
   

Closed TopicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 

Lo-Fi Version Time is now: 23rd May 2013 - 04:30 AM
RPG RPG Revolution is an Privacy Policy and Legal
eXTReMe Tracker