[XP] Threat System v1.2

Started by Fantasist, July 13, 2009, 12:40:40 pm

Previous topic - Next topic

Fantasist

July 13, 2009, 12:40:40 pm Last Edit: October 23, 2011, 04:43:00 am by Fantasist
Threat System
Authors: Fantasist
Version: 1.2
Type: Enemy behavior enhancement
Key Term: Battle Add-on



Introduction

During battle, enemies will choose their targets based on their "threat" rather than randomly. The threat for an actor changes depending on what they do. For example, attacking raises threat and defending decreases threat.


Features


  • Players maintain "threat" and the enemies attack the most dangerous actors first
  • Offers a new aspect of control in battles
  • Configurable change in threat for attack, defend, skill and items
  • Configurable "Threat Chance" decides how often enemies choose actors based on their threat (leaving some randomness for those who prefer it)
  • An ignore list for enemies who use random actor selection
  • Temporarily disable the threat system by using a switch
  • A "threat-by-damage" mode where threat is calculated based on damage inflicted
  • Two ways of keeping track of threat: status window mod for DBS and threat window for other battle system mods



Screenshots

N/A


Demo

http://www.sendspace.com/file/rxrtfp


Script

Place this script anywhere above "Main" and below "Scene_Debug".

Spoiler: ShowHide

#==============================================================================
# ** Threat System
#------------------------------------------------------------------------------
# by Fantasist
# Version: 1.2
# Date: 23-Oct-2011
#------------------------------------------------------------------------------
# Version History:
#
#   1.0 - First version (13-July-2009)
#   1.1 - Fixed Force Action bug and added enemy-specific threat ignore (21-Oct-2011)
#   1.2 - Added disable by switch, threat by damage, fixed a display bug
#------------------------------------------------------------------------------
# Description:
#
#     During battle, enemies will choose their targets based on their "threat"
#   rather than randomly. The threat for an actor changes depending on what they
#   do. For example, attacking raises threat and defending decreases threat.
#------------------------------------------------------------------------------
# Compatibility:
#
#   Might be incompatible with other battle systems or battle addons.
#------------------------------------------------------------------------------
# Instructions:
#
#    Place this script anywhere above "Main" and below "Scene_Debug".
#------------------------------------------------------------------------------
# Configuration:
#
#     Scroll down a bit and you'll see the configuration.
#
#    ATTACK_THREAT: Threat to increase when actor attacks
#    DEFEND_THREAT: Threat to decrease when actor defends (read THREAT_BY_DAMAGE)
#    THREAT_CHANCE: The chance of enemies attacking based on threat
#    THREAT_SWITCH: Turn this ON to temporarily disable the threat system
# THREAT_BY_DAMAGE: When "true", an actor's threat increases by the damage
#                   caused to enemies. Enabling this ignores ATTACK_THREAT.
#                   When defending, actor's threat is divided by DEFEND_THREAT.
#   THREAT_DISPLAY: Display players' threats besides their name
#    THREAT_WINDOW: Whether to enable or disable threat window
#                   To enable it, set it to "true". If you want to set it's
#                   position and width, you can also set it to an array with
#                   it's X position, Y position and width (eg: [0, 64, 160]).
#     ENEMY_IGNORE: List of enemy IDs which ignore the threat system
#
#   Skill Threat Configuration:
#
#     Look for "SKILL THREAT CONFIG BEGIN" and follow the example.
#     In the given example:
#
#               when 57 then [10, -2]
#
#     the skill 57 (Cross Cut) increases user's threat by 10 and decreases the
#     rest of the party's threat by 2.
#
#   Item Threat Configuration:
#
#     Works exactly the same as skill threat configuration.
#------------------------------------------------------------------------------
# Credits:
#
#   Fantasist, for making this script
#   KCMike20, for requesting this script
#
# Thanks:
#
#   Blizzard, for helping me
#   winkio, for helping me
#   Jackolas, for pointing out a bug
#   yuhikaru, for fixing force action bug
#   Fenriswolf, for requesting enemy ignore list
#   Kagutsuchi, for requesting threat-by-damage feature
#------------------------------------------------------------------------------
# Notes:
#
#   If you have any problems, suggestions or comments, you can find me at:
#
#     forum.chaos-project.com
#
#   Enjoy ^_^
#==============================================================================

#==============================================================================
# ** ThreatConfig module
#------------------------------------------------------------------------------
#  Module for settings and configuration of the Threat system.
#==============================================================================

module ThreatConfig
  #--------------------------------------------------------------------------
  # * Config
  #--------------------------------------------------------------------------
  ATTACK_THREAT = 10 # Threat to increase when actor attacks
  DEFEND_THREAT = 10 # Threat to decrease when actor defends
  THREAT_CHANCE = 100 # The chance of enemies attacking based on threat
  THREAT_SWITCH = 25 # ID of switch which disables threat system when ON
  THREAT_BY_DAMAGE = true # Threat is increased based on damage caused
  THREAT_DISPLAY = true # Display player's threat besides their name
  THREAT_WINDOW = [480, 64, 160] # Whether to enable or disable threat window
  ENEMY_IGNORE = [] # List of enemy IDs which ignore the threat system
  #--------------------------------------------------------------------------
  # * Configure skill threats
  #--------------------------------------------------------------------------
  def self.get_skill_threat(skill_id)
    threat = case skill_id
    #========================================================================
    # SKILL THREAT CONFIG BEGIN
    #========================================================================
    when 57 then [5, -1] # Cross Cut
    when 61 then [5, -1] # Leg Sweep
    when 7 then [5, 0]   # Fire
    # when skill_ID then [ user_threat, party_threat ]
    #========================================================================
    # SKILL THREAT CONFIG END
    #========================================================================
    else false
    end
    return threat
  end
  #--------------------------------------------------------------------------
  # * Configure item threats
  #--------------------------------------------------------------------------
  def self.get_item_threat(item_id)
    threat = case item_id
    #========================================================================
    # ITEM THREAT CONFIG BEGIN
    #========================================================================
    when 1 then [2, -5] # Potion
    # when item_ID then [ user_threat, party_threat ]
    #========================================================================
    # ITEM THREAT CONFIG END
    #========================================================================
    else false
    end
    return threat
  end
  #--------------------------------------------------------------------------
  # * Configure enemy ignore IDs
  #--------------------------------------------------------------------------
 
end

#==============================================================================
# ** Game_Actor
#------------------------------------------------------------------------------
#  Added the threat attribute.
#==============================================================================

class Game_Actor
  #--------------------------------------------------------------------------
  # * Initialize threat attribute
  #--------------------------------------------------------------------------
  alias game_actor_threat_setup setup
  def setup(actor_id)
    @threat = 0
    game_actor_threat_setup(actor_id)
  end
  #--------------------------------------------------------------------------
  # * Get the threat attribute
  #--------------------------------------------------------------------------
  attr_reader :threat
  #--------------------------------------------------------------------------
  # * Set the threat attribute
  #--------------------------------------------------------------------------
  def threat=(val)
    val = 0 if val < 0
    @threat = val
  end
end

#==============================================================================
# ** Game_Party
#------------------------------------------------------------------------------
#  Modified random actor selection to select by threat.
#==============================================================================

class Game_Party
  #--------------------------------------------------------------------------
  # * Choose actor by threat
  #--------------------------------------------------------------------------
  alias choose_actor_threat_orig random_target_actor
  def random_target_actor(hp0 = false)
    if $game_switches[ThreatConfig::THREAT_SWITCH]
      return choose_actor_threat_orig(hp0)
    end
    if $scene.active_battler.is_a?(Game_Enemy) &&
      ThreatConfig::ENEMY_IGNORE.include?($scene.active_battler.id)
      return choose_actor_threat_orig(hp0)
    end
    if rand(100) >= ThreatConfig::THREAT_CHANCE
      return choose_actor_threat_orig(hp0)
    else
      return threat_target_actor(hp0)
    end
  end
  #--------------------------------------------------------------------------
  # * Calculate threat and choose actor
  #--------------------------------------------------------------------------
  def threat_target_actor(hp0=false)
    # Collect valid actors
    targets = []
    for actor in @actors
      next unless (!hp0 && actor.exist?) || (hp0 && actor.hp0?)
      targets.push(actor)
    end
    # Get actors with maximum threat
    targets.sort! {|a, b| b.threat - a.threat}
    targets = targets.find_all {|a| a.threat == targets[0].threat}
    # Choose random
    target = targets[rand(targets.size)]
    return target
  end
end

#==============================================================================
# ** Game_Battler
#------------------------------------------------------------------------------
#  Added attack and skill threat handling.
#==============================================================================

class Game_Battler
  #--------------------------------------------------------------------------
  # * Attack Threat
  #--------------------------------------------------------------------------
  alias attack_effect_threat attack_effect
  def attack_effect(attacker)
    result = attack_effect_threat(attacker)
    if attacker.is_a?(Game_Actor) && self.damage.is_a?(Numeric) && self.damage > 0
      if ThreatConfig::THREAT_BY_DAMAGE
        attacker.threat += self.damage
      else
        attacker.threat += ThreatConfig::ATTACK_THREAT
      end
    end
    return result
  end
  #--------------------------------------------------------------------------
  # * Skill Threat
  #--------------------------------------------------------------------------
  alias skill_effect_threat skill_effect
  def skill_effect(user, skill)
    result = skill_effect_threat(user, skill)
    threat = user.is_a?(Game_Actor) && ThreatConfig.get_skill_threat(skill.id)
    if threat && self.damage.is_a?(Numeric) && self.damage > 0
      user_threat, party_threat = threat[0], threat[1]
      for actor in $game_party.actors
        threat_plus = actor.id == user.id ? user_threat : party_threat
        actor.threat += threat_plus
      end
      if ThreatConfig::THREAT_BY_DAMAGE
        user.threat -= user_threat
        user.threat += self.damage
      end
    end
    return result
  end
end

#==============================================================================
# ** Scene_Battle
#------------------------------------------------------------------------------
#  Added defend and item threat handling and realtime target selection.
#==============================================================================

class Scene_Battle
  attr_reader :active_battler
  #--------------------------------------------------------------------------
  # * Defend Threat
  #--------------------------------------------------------------------------
  alias basic_action_threat_result make_basic_action_result
  def make_basic_action_result
    if @active_battler.current_action.basic == 1 && @active_battler.is_a?(Game_Actor)
      if ThreatConfig::THREAT_BY_DAMAGE
        @active_battler.threat /= ThreatConfig::DEFEND_THREAT
      else
        @active_battler.threat -= ThreatConfig::DEFEND_THREAT
      end
    end
    basic_action_threat_result
  end
  #--------------------------------------------------------------------------
  # * Item Threat
  #--------------------------------------------------------------------------
  alias item_action_threat_result make_item_action_result
  def make_item_action_result
    item_action_threat_result
    threat = @active_battler.is_a?(Game_Actor) && ThreatConfig.get_item_threat(@item.id)
    if threat
      user_threat, party_threat = threat[0], threat[1]
      for actor in $game_party.actors
        threat_plus = actor.id == @active_battler.id ? user_threat : party_threat
        actor.threat += threat_plus
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Choose target actor in realtime
  #--------------------------------------------------------------------------
  alias update_phase4_step2_choose_actor_realtime update_phase4_step2
  def update_phase4_step2
    # Fore action fix by yuhikaru
    # If there is no force action this turn
    if $game_temp.forcing_battler == nil
      @active_battler.make_action if @active_battler.is_a?(Game_Enemy)
    end
    update_phase4_step2_choose_actor_realtime
  end
  #--------------------------------------------------------------------------
  # * Clear threats before battle
  #--------------------------------------------------------------------------
  alias clear_threats_battle_main main
  def main
    $game_party.actors.each {|actor| actor.threat = 0}
    clear_threats_battle_main
  end
end

if ThreatConfig::THREAT_DISPLAY
#==============================================================================
# ** Window_BattleStatus
#------------------------------------------------------------------------------
#  Modded to show threat beside actor's name.
#==============================================================================

class Window_BattleStatus
  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  alias threat_display_refresh refresh
  def refresh
    threat_display_refresh
    for i in 0...$game_party.actors.size
      actor = $game_party.actors[i]
      next if actor.threat == 0
      actor_x = i * 160 + 4
      actor_x += self.contents.text_size(actor.name).width + 4
      self.contents.draw_text(actor_x, 0, 160, 32, "(#{actor.threat})")
    end
  end
end
end

if ThreatConfig::THREAT_WINDOW
#==============================================================================
# ** Window_Threat
#------------------------------------------------------------------------------
#  This window displays the threats of actors in the party.
#==============================================================================

class Window_Threat < Window_Base
  H = 18
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    a = ThreatConfig::THREAT_WINDOW
    if a.is_a?(Array)
      x, y, w = a[0], a[1], a[2]
    else
      x, y, w = 0, 64, 160
    end
    super(x, y, w, 32 + H + $game_party.actors.size * H)
    @threats = []
    $game_party.actors.each {|a| @threats.push(a.threat)}
    self.contents = Bitmap.new(w-32, self.height-32)
    self.contents.font.size = H
    self.contents.font.bold = H <= 22
    self.opacity = 160
    refresh
  end
  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  def refresh
    self.contents.clear
    self.contents.draw_text(0, 0, self.width-32, H, 'Threats', 1)
    $game_party.actors.each_with_index {|a, i| y_off = H
    self.contents.draw_text(0, y_off + i*H, self.width-32, H, a.name)
    self.contents.draw_text(0, y_off + i*H, self.width-32, H, @threats[i].to_s, 2)}
  end
  #--------------------------------------------------------------------------
  # * Update
  #--------------------------------------------------------------------------
  def update
    flag = false
    $game_party.actors.each_with_index {|a, i|
    @threats[i] = a.threat if a.threat != @threats[i]
    flag = true}
    refresh if flag
  end
end

#==============================================================================
# ** Scene_Battle
#------------------------------------------------------------------------------
#  Modded to handle threat window.
#==============================================================================

class Scene_Battle
  #--------------------------------------------------------------------------
  # * Main Processing
  #--------------------------------------------------------------------------
  alias threat_win_init main
  def main
    @threat_win = Window_Threat.new
    threat_win_init
    @threat_win.dispose
  end
  #--------------------------------------------------------------------------
  # * Update
  #--------------------------------------------------------------------------
  alias threat_win_upd update
  def update
    @threat_win.update
    threat_win_upd
  end
end
end



Instructions

Look for the configuration section in the script and configure the following.


  • ATTACK_THREAT: Threat to increase when actor attacks
  • DEFEND_THREAT: Threat to decrease when actor defends
  • THREAT_CHANCE: The chance of enemies attacking based on threat
  • THREAT_DISPLAY: Display players' threats besides their name
  • THREAT_WINDOW: Whether to enable or disable threat window. To enable it, set it to "true". If you want to set it's position and width, you can also set it to an array with it's X position, Y position and width (eg: [0, 64, 160]).
  • ENEMY_IGNORE: List of enemy IDs which ignore the threat system


Skill Threat Configuration:

  Look for "SKILL THREAT CONFIG BEGIN" and follow the example. In the given example:
when 57 then [10, -2]


the skill 57 (Cross Cut) increases user's threat by 10 and decreases the rest of the party's threat by 2.

Item Threat Configuration:

  Works exactly the same as skill threat configuration.


Compatibility

Might be incompatible with other battle systems or battle addons.


Credits and Thanks

Credit Fantasist (me) for making this.


  • Fantasist, for making this script
  • KCMike20, for requesting this script
  • Blizzard, for helping me
  • winkio, for helping me
  • Jackolas, for pointing out a bug
  • yukiharu for fixing force action bug
  • Fenriswolf for requesting enemy ignore list
  • Kagutsuchi, for requesting threat-by-damage feature



Author's Notes

If you have any problems, suggestions or comments, you can find me at:

  forum.chaos-project.com

Enjoy ^_^
Do you like ambient/electronic music? Then you should promote a talented artist! Help out here. (I'm serious. Just listen to his work at least!)


The best of freeware reviews: Gizmo's Freeware Reviews




Aqua


Seox

You beat me to this by weeks. Holy god.. Even has the same NAME. You just saved me a LAWT of time. THANK YOU! *powers up*


I'm gonna add edits for my game, where enemies with greater INT have a greater chance of attacking POTENTIAL threats, IE mages, and not as much of attacking the PERCEIVED threats.


Thanks again!
... (<<<<<<<<<<<<<<< TEH DOTS OF DOOM. Hey, kinda catchy. :naughty:)

Jackolas

still think its 1 of the most requested scripts out there :P

great work as always :P

Blizzard

Fantasist makes my fantasies come true. <3 *levels up*
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Seox

Quote from: Blizzard on July 13, 2009, 03:18:18 pm
Fantasist makes my fantasies come true. <3 *levels up*


I dunno if that was epic or phail.  :P

XD, no, really, it's a great script. Just tested it with mah battle system, and it works *perfectly*. Great job!
... (<<<<<<<<<<<<<<< TEH DOTS OF DOOM. Hey, kinda catchy. :naughty:)

C.C. rOyAl

does this work with Blizzards ABS?
Spoiler: ShowHide

Aqua


winkio

I'll eventually convert it if someone else doesn't beat me to it.

C.C. rOyAl

k cool cuz this sounds real cool 
Spoiler: ShowHide

Blizzard

Should be fairly easy. It rewrites only a few methods in Scene_Battle.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Subsonic_Noise

Wouldn't we also need this for Remexos? An MMO without such a system would be a bit hard to play.
Very nice script btw, it's one of these scripts many people could need, but nobody had the idea yet^^

Kagutsuchi

Does this script give you threat depending on how much damage you do?

C.C. rOyAl

thats basicly right. just read the request on Script Request. it goes into pretty good detail
Spoiler: ShowHide

Seox

July 14, 2009, 12:12:18 am #14 Last Edit: July 14, 2009, 01:04:05 am by Seox
Quote from: Kagutsuchi on July 13, 2009, 08:28:01 pm
Does this script give you threat depending on how much damage you do?


No. Even misses generate threat. Try the demo, you'll see very quickly how it works. However, with basic scripting knowledge, an edit is possible. I might not be able to SOON, but within the next couple of days, I can edit it for you.
... (<<<<<<<<<<<<<<< TEH DOTS OF DOOM. Hey, kinda catchy. :naughty:)

C.C. rOyAl

Spoiler: ShowHide

Seox

Why? You didn't do anything wrong ^_^. You actually tried to help. No need to feel bad.
... (<<<<<<<<<<<<<<< TEH DOTS OF DOOM. Hey, kinda catchy. :naughty:)

Kagutsuchi

Quote from: Seox on July 14, 2009, 12:12:18 am
Quote from: Kagutsuchi on July 13, 2009, 08:28:01 pm
Does this script give you threat depending on how much damage you do?


No. Even misses generate threat. Try the demo, you'll see very quickly how it works. However, with basic scripting knowledge, an edit is possible. I might not be able to SOON, but within the next couple of days, I can edit it for you.

Good ^^ I could see that some people might want to use this version of a threat system, but to me it just doesn't make sense to have a threat system where how much damage you deal doesn't deside your threat.
< wow and aoc geek =D

Fantasist

Thank you everyone :)

Quote from: Seox on July 13, 2009, 02:37:42 pm
You beat me to this by weeks. Holy god.. Even has the same NAME. You just saved me a LAWT of time. THANK YOU! *powers up*

lol! I was supposed to complete this a year ago!

Quote from: winkio on July 13, 2009, 04:56:32 pm
I'll eventually convert it if someone else doesn't beat me to it.

Sure, I'd appreciate that. I don't know BABS enough to do it XD

Quote from: Kagutsuchi on July 14, 2009, 06:43:09 am
Good ^^ I could see that some people might want to use this version of a threat system, but to me it just doesn't make sense to have a threat system where how much damage you deal doesn't deside your threat.
< wow and aoc geek =D

A threat system can ideally do a lot of stuff, a LOT. Many of them, though, are the user's preference. I admit I could've done a better job with it's capabilities, but I've gotten sick of RGSS. You see, I'm at a point of life where everything I do should pay off to my education or my career, or I feel like I'm wasting time, or worse; that I'm letting my peers go way ahead in the game. I don't want to touch RMXP just for scripting anymore. I will only use it when I have the time and plan for my game. I left a lot of requests hanging and I felt bad, so I'm sort of completing them.
btw, if any of that rant sounded like I was offended or something, it's not that, I appreciate your remarks :)
Do you like ambient/electronic music? Then you should promote a talented artist! Help out here. (I'm serious. Just listen to his work at least!)


The best of freeware reviews: Gizmo's Freeware Reviews




Subsonic_Noise

Quote from: Fantasist on July 14, 2009, 08:32:17 am
You see, I'm at a point of life where everything I do should pay off to my education or my career, or I feel like I'm wasting time, or worse;

No! Don't think like that! Education and career isn't everything!