Garryl

Villager
Member
Joined
Feb 19, 2015
Messages
25
Reaction score
4
First Language
English
Primarily Uses
This is a script I'm working on to provide individual threat-based targeting, similar to that found in World of Warcraft or Dragon Age, where each battler maintains its own threat table independent of all other battlers. So, for example, one battler might see the mage that's been nuking it as the most threatening and focus on him, while another that hasn't been attacked as much might focus on the healer instead.

Basic functionality is present and has been tested to a limited extent. Basically, battlers will maintain a threat table and choose skill targets using threat. Damage and healing (presently only hp/mp damage/healing/drain calculated from the formula of a skill/item, not from recover hp/mp/tp effects) will add threat towards the battler that caused the damage/healing from the recipient of the damage or the enemies of the battler being healed.

Tested:

- Targeting based on threat target.
- Selection of threat target based on threat table, including not switching over until the threashold requirement is met.
- Threat from damage taken (non-global).
- Threat from healing done (global).
- Threat decay at end of turn.
- Threat decay on battler death.
- Threat multipliers on skills and items.
- Fixation from states preventing the automatic change of threat targets.
I haven't had much chance to work on this in the past few weeks, so while I've identified how to do a lot of the things I need to implement, I haven't actually programmed them yet. I have a lot of projects coming up, so I probably won't be able to spend much time on this for another month.

Code:
#TODO#- threat_rate(): Function call to get a battler's threat relative to current threat target#- Auto-Battler AI#- Interface#- Non-formula Threat#- Skill Threat Tags  (fixed/formula threat)#- State Threat Tags=begin-------------------------------------------------------------------------------Individual Threat SystemVersion 0.3Created: Feb. 22, 2015Last update: Feb. 23, 2015Author: Garryl-------------------------------------------------------------------------------Description:This script provides a basic threat system. Each battler tracks how threateningit perceives every other battler in combat. Individual targeting then directsattacks against the current threat target. Customizable along multiple axes,including sticky targeting, threat decay, the relative threat values of damageand healing, and more!At present, threat is only generated for dealing damage or healing (includingMP damage/restore) as part of the formula's resolution. Additional effects(including HP/MP Restore effects) produce no threat.-------------------------------------------------------------------------------Installation:Copy into a new script slot in the Materials section.-------------------------------------------------------------------------------Usage Instructions:Modify the settings to your liking. All settings are found below, in theGarryl::Threat::Settings module. Full descriptions for each setting areincluded in that section. Do not modify anything else in the script.This script supports note tags. See below for more information.You can call the various methods implemented in this script as part of skills,items, and events.-------------------------------------------------------------------------------Note Tags:The following note tags are supported. Additional functionality can begained by putting the indicated text in the "Note" field of states or skill.Fixation: While a battler has any fixation state, it will remain fixated onits current target regardless of threat. No matter how much threat anotheropponent may have, the fixated battler will not change its threat target.- Valid Locations: State- Valid Strings (case insensitive): <fixate>Skill/Item Threat Multiplier: A skill or item can have threat threat that itsusage causes be multiplied by some amount. Accepts both integers andfloating point values. Any amount of white space is allowed around the number.- Valid Locations: Skill, Item- Valid Strings (case insensitive): <threat multiplier: # >    Ex: <threat multiplier:2>    Ex: <threat multiplier:     -0.8   >-------------------------------------------------------------------------------Script Calls:A lot of the methods implemented here can be useful. Here are some of them.Following the convention used in skill formulas, the user will be referencedby "a", and the target will be referenced by "b".- a.reset_threat_table: Resets the battler's threat table, setting all threat    values to the initial threat value.- a.add_threat(b, threat): Adds a fixed amount of threat from a towards b and    recalculates a's threat target.- a.decay_threat(b, decay_factor): Reduces a's threat towards b by a factor and    recalculates a's threat target.- a.add_threat_to_all_opponents(threat): Adds a fixed amount of threat from a    towards all of a's opponents and recalculates their threat targets.- a.decay_threat_of_all_opponents(decay_factor): Reduces a's threat towards all    opponents by a factor and recalculates their threat targets.- a.change_threat_target(: Forces a to immediately select b as its threat    target. This applies regardless of threat and fixation. Note that a will    likely recalculate targets before its next action and thus potentially    switch targets unless it is fixated.Examples:- Taunt: You can create a taunt skill by adding a fixate state to the target    and forcibly changing its threat target in the formula:    b.change_threat_target(a);    Note, however, that enemy action targets are picked at the beginning of the    turn in the default battle system, so this would only affect the targeting    of the following turn.-------------------------------------------------------------------------------Extended Description:Goals: Each enemy should have a threat table with all opposing units and theirrespective threat values. Attacks against a single non-random enemy are alwaysdirected at the attacker's target. Different battlers may see differentopponents as greater or lesser threats and target their attacks accordingly.Threat: Threat is generated by dealing damage or healing. Threat caused isproportional to damage or healing done, which can be applied in differentproportions to all opponents and/or to just the target (or opponents that aretargeting the recipient of healing). Any threat a battler generates ismultiplied by that battler's TGR (TarGet Rate/Threat Generation Rate).  Threat can be set to decay each turn, reducing it by a percentage for allbattlers. This way, large threat gains early on do not guarantee targetinglater in the fight unless they are maintained with additional threatgeneration.  Threat also instantly decays towards a battler that dies or escapes. Whilethey are nominally out of the fight, they do have the potential to returnlater on (revival or reappearance), and any threat they still have towardsor from opponents will be retained for such an occurence.  Finally, and probably least importantly, each threat table entry is seededwith a small amount of threat at the beginning of battle. This is to helpprevent target switching from being too volatile in the early stages ofbattle (see Threat Target, below).Threat Table: Each battler maintains both its threat target and a threat table.The threat table tracks the threat level of each opponent the battler is awareof (battlers that join mid-battle are not added until they perform an actionthat can generate threat, for example).Threat Target: In addition to a simple table of threat values, each battlermaintains a threat target, the opposing battler that has caught its attention.Threat targets are slightly sticky. While the highest threat battler isselected as a new target if the previous one becomes invalid, switching fromone valid target to another is slightly more complex. A battler will onlychange threat targets if the new one exceeds its current target's threat bya certain percentage. In this way, a battler will not ping pong between twoopponents with very close threat values, and will instead to stick with justone of them for a time.  Note that while actors are not restricted by threat, they do maintain threattables and threat targets for use under situations where the player's targetinstructions don't work. Specifically, when the action's target dies before itcan be resolved, and if the actor is berserk (restriction: "Attack an Enemy").These can be switched from threat back to default with the settings.  A fixated battler will only change its threat target if the previous targetbecomes invalid. It will otherwise ignore threat values. Fixation is caused bystates with the <fixate> note tag.-------------------------------------------------------------------------------Settings:Several settings in this script can be changed to customize it to your liking.These settings are found below, in the section marked "Settings".Targeting: These options influence how targets are selected.- TARGET_SWITCH_THRESHOLD_PERCENT: Controls the percentage greater threat that    a battler must have over the current target to trigger a switch.- FORCE_ACTION_LAST_THREAT_TARGET: Customizes how the "last target" option of    "Force Action" event commands work. If true, a battler will try to use    its threat target instead of the target of its last attack.- USE_THREAT_WHEN_BERSERK: Customizes how the "Attack an Enemy" state    restriction works. If true, a battler will try to use its threat target    instead of a random target.- USE_THREAT_WHEN_TARGET_DEAD: Customizes how targeting works when the    desired enemy dies before the action can be resolved. If true, the    battler will try to attack its threat target instead of the first    opponent still alive.- USE_TGR_FOR_RANDOM_TARGETING: If true, uses TGR    (TarGet Rate/Threat Generation Rate) to modify the respective weights when    picking a random target, as per the default RTP scripts. If false, all    battlers are equally likely targets in random selection.Threat Decay: These settings control how much threat is lost automaticallyunder certain situations.- END_TURN_THREAT_DECAY_PERCENT: All threat values decrease by this percentage    each turn.- DEATH_THREAT_DECAY_PERCENT: All opponents decrease battler's threat by this    percentage when this battler dies.- ESCAPE_THREAT_DECAY_PERCENT: All opponents decrease battler's threat by this    percentage when this battler escapes.Threat Generation: These settings control how much threat is caused by skillsand items.- INITIAL_THREAT: Threat value given for everyone to everyone at the start of    battle (and upon becoming aware of a new battler who joins mid-battle).    Affected by opponent's TGR.- THREAT_PER_DAMAGE: Points of threat to the subject per point of damage dealt.- THREAT_PER_DAMAGE_GLOBAL: Points of threat to all opposing battlers per point    of damage dealt to any opposing battler.- THREAT_PER_HEAL: Points of threat to any opposing battlers currently    targeting the subject per point of healing caused.- THREAT_PER_HEAL_GLOBAL: Points of threat to all opposing battlers per point    of healing caused to any battler not on their side.- THREAT_MP_MULTIPLIER: Each point of MP damage/restore is equal to this many    points of hp damage/heal for threat generation.- THREAT_ABSORB_MULTIPLIER: Each point of healing from an absorb/drain skill    is equal to this many points of direct healing for threat generation.   -------------------------------------------------------------------------------Future Features:There are a number of features I would like to add later, now that I haveimplemented these bare bones. They are generally of a larger scope, however.- Auto-Battler AI: Use threat tables for auto-battling actors. The default    scripts, when attacking an opponent, select target for which the skill is    likely to deal the most damage as a percentage of remaining hp.- Interface: I would eventually like to add interface elements to show    changing threat. For example, when an actor becomes the target of an    opponent, it might show an exclamation balloon over the opponent's sprite.    I don't think I want to go as far as a full threat meter window like World    of Warcraft, although that would be useful for debugging.- Non-formula Threat: I have yet to implement any sort of threat generation    from skill or item usage outside of the formula's damage/healing and the    action results it produces. I would like to add support for threat from    buffs/debuffs, states, and at the very least, from Recover HP/MP effects.- Skill Threat Tags: Add note tags to skills, letting them add an extra    multiplier and/or a fixed bonus amount to the threat. Bonus    points if I can do it as an evaluated function. The multiplier is done,    but I'd like to add a formula-based flat amount, too.- State Threat Tags: Add note tags to states, letting them add a fixed amount    of threat to the subject when they are applied. Bonus points if    I can do it as an evaluated function.-------------------------------------------------------------------------------Ongoing Issues:Game_Party may need to be modified to account for changing out party membersmid-battle. The current logic doesn't fail or anything, and generally worksas expected, but there are a few nuances I'd like to adjust, maybe.-------------------------------------------------------------------------------Change Log:v0.3- Monday, Feb. 23- Added support for fixation via states. While a battler has any state that    causes fixation, it will stay on its current target regardless of threat.    It will still change targets if its previous target becomes invalid.- Added support for threat multiplier note tags for skills and items.v0.2- Monday, Feb. 23- Targeting complete. Enemies target their actions using their threat targets.v0.1- Sunday, Feb. 22- Threat tables and threat generation from direct damage/healing complete.-------------------------------------------------------------------------------References:- [URL="http://www.rpgmakervxace.net/topic/14439-basic-guide-to-working-with-note-tags/-"]http://www.rpgmakervxace.net/topic/14439-basic-guide-to-working-with-note-tags/-[/URL] Game_Battler- Game_Action.set_enemy_action- Game_Action.set_confusion_target- Game_Action.targets_for_opponents- Game_Unit.smooth_target- Game_Unit.random_target- RPG::State- RPG::BaseItem-------------------------------------------------------------------------------Compatibility:The following default script functions are overwritten:- Game_Battler.force_action- Game_Action.set_enemy_action- Game_Action.set_confusion_target- Game_Action.targets_for_opponents- Game_Unit.smooth_targetThe following default script functions are aliased:- Game_Battler.initialize- Game_Battler.clear_states- Game_Battler.remove_state- Game_Battler.die- Game_Battler.escape- Game_Battler.on_battle_start- Game_Battler.on_turn_end- Game_Battler.item_apply- Game_Unit.smooth_targetThe following functions are added to default script classes:- Game_Battler.reset_threat_table- Game_Battler.init_threat_table_entry- Game_Battler.add_threat- Game_Battler.decay_threat- Game_Battler.add_threat_to_all_opponents- Game_Battler.decay_threat_of_all_opponents- Game_Battler.turn_end_threat_decay- Game_Battler.generate_threat_from_item- Game_Battler.generate_damage_threat- Game_Battler.generate_healing_threat- Game_Battler.generate_absorb_threat- Game_Battler.update_threat_target- Game_Battler.has_any_threat_target?- Game_Battler.has_valid_threat_target?- Game_Battler.change_threat_target- Game_Battler.fixated?- Game_Battler.is_valid_threat_target?- Game_Battler.get_threat_target_battler_index- Game_Battler.get_opponent_battler_index- Game_Battler.select_highest_threat_among_battlers- Game_Action.use_threat_target?- RPG::State.fixation- RPG::State.load_notetag_fixate- RPG::UsableItem.threat_multiplier- RPG::UsableItem.load_notetag_threat_multiplier-------------------------------------------------------------------------------=end# *****************************************************************************# * Import marker key                                                         *# *****************************************************************************$imported ||= {}$imported["GarrylThreatSystem"] = truemodule Garryl  module Threat    module Settings      # ***********************************************************************      # * Settings                                                            *      # ***********************************************************************      # * You can modify the variables here to fine tune the threat system    *      # * for your game.                                                      *      # ***********************************************************************           #------------------------------------------------------------------------      # * Targeting      # * These settings deal how battlers choose their targets.      #------------------------------------------------------------------------           TARGET_SWITCH_THRESHOLD_PERCENT = 30  # Percentage by which a new target                                            # must exceed the old to cause a                                            # target switch.           FORCE_ACTION_LAST_THREAT_TARGET = true  # If true, the last target option                                            # for Force Action instead selects                                            # the threat target.           USE_THREAT_WHEN_BERSERK = true        # If true, any berserk battler                                            # (restriction: target enemy)                                            # will use its threat table instead                                            # of attacking randomly.           USE_THREAT_WHEN_TARGET_DEAD = true    # If true, any action targeting a                                            # dead opponent will use the next                                            # highest threat target from the                                            # attacker's threat table instead                                            # of attacking the first one still                                            # alive in the unit.           USE_TGR_FOR_RANDOM_TARGETING = true   # If true, any random targeting                                            # will weight each battler                                            # according to its TGR.                                            # If false, each battler in the                                            # unit is equally likely to be                                            # targeted, and TGR is only used                                            # as a multiplier to threat                                            # generation.           #------------------------------------------------------------------------      # * Threat decay      # * These settings deal with automatic threat reduction.      #------------------------------------------------------------------------           END_OF_TURN_THREAT_DECAY_PERCENT = 10 # Percentage of threat to remove                                            # for all battlers at end of turn.                                            # Includes dead and escaped                                            # battlers.                                                 DEATH_THREAT_DECAY_PERCENT = 80       # Percentage of threat towards a                                            # dying battler to remove. The                                            # remainder will be retained in                                            # case the battler revives.                                                 ESCAPE_THREAT_DECAY_PERCENT = 80      # Percentage of threat towards an                                            # escaping battler to remove. The                                            # remainder will be retained if                                            # the battler returns.                                                 #------------------------------------------------------------------------      # * Threat generation      # * These settings deal with threat generation from damage and healing.      #------------------------------------------------------------------------           INITIAL_THREAT = 100                  # Initial threat value given for                                            # all battlers at the start, just                                            # to initialize things. Multiplied                                            # by TGR.           THREAT_PER_DAMAGE = 1.0               # Threat generated from target per                                            # point of damage dealt.           THREAT_PER_DAMAGE_GLOBAL = 0.0        # Threat generated from all                                            # opponents per point of damage                                            # dealt to any of them.           THREAT_PER_HEAL = 0.0                 # Threat generated from opponents                                            # currently targeting the battler                                            # healed per point of healing.           THREAT_PER_HEAL_GLOBAL = 0.5          # Threat generated from all                                            # opponents per point of healing                                            # done to any of their enemies.           THREAT_MP_MULTPLIER = 10.0            # Damaging/restoring MP generates                                            # threat as per dealing HP damage                                            # or healing multiplied by this                                            # amount.           THREAT_ABSORB_MULTIPLIER = 1.0        # Absorption healing multiplied                                            # by this amount when determining                                            # threat generated from healing.                                            # Multiplicative with                                            # THREAT_PER_HEAL and                                            # THREAT_PER_HEAL_GLOBAL.           # ***********************************************************************      # * End of Settings                                                     *      # ***********************************************************************    end       module Buff      #TODO: NYI      #Threat data for buffs/debuffs to core stats      INDEX_BASE_THREAT = 0;                      # Buff: Flat threat to opponents of target currently targeting it                                                  # Debuff: Flat threat to target           INDEX_THREAT_PER_STAT_CHANGE = 1;           # Buff: Flat threat to opponents of target currently targeting it                                                  # per point of stat that the buff added                                                  # Debuff: Flat threat to target                                                  # per point of stat that the debuff removed      INDEX_BASE_THREAT_GLOBAL = 2;               # Buff: Flat threat to all opponents of target                                                  # Debuff: Flat threat to all allies of the target      INDEX_THREAT_PER_STAT_CHANGE_GLOBAL = 3;    # Buff: Flat threat to all opponents of target                                                  # per point of stat that the buff added                                                  # Debuff: Flat threat to all allies of the target                                                  # per point of stat that the debuff removed                BUFF_THREAT = [        #base   per stat+   global  global/stat+        [0.0,   0.0,        0.0,    0.5],         #max hp        [0.0,   0.0,        0.0,    5.0],         #max mp        [0.0,   0.0,        0.0,    1.0],         #atk        [0.0,   0.0,        0.0,    1.0],         #def        [0.0,   0.0,        0.0,    1.0],         #mat        [0.0,   0.0,        0.0,    1.0],         #mdf        [0.0,   0.0,        0.0,    1.0],         #agi        [0.0,   0.0,        0.0,    1.0],         #luk      ]           DEBUFF_THREAT = [        #base   per stat-   global  global/stat-        [0.0,   1.0,        0.0,    0.0],         #max hp        [0.0,   10.0,       0.0,    0.0],         #max mp        [0.0,   2.0,        0.0,    0.0],         #atk        [0.0,   2.0,        0.0,    0.0],         #def        [0.0,   2.0,        0.0,    0.0],         #mat        [0.0,   2.0,        0.0,    0.0],         #mdf        [0.0,   2.0,        0.0,    0.0],         #agi        [0.0,   2.0,        0.0,    0.0],         #luk      ]    end    module Regex      #skills/items      #THREAT_PERCENT = /<Threat Percent:\s*([\-\+]?[0-9]*(\.[0-9]+)?)(?=%)?\s*>/i      THREAT_MULTIPLIER = /<Threat Multiplier:\s*([\-\+]?[0-9]*(\.[0-9]+)?)\s*>/i           #states      STATE_FIXATE = /<fixate>/i    end       END_OF_TURN_THREAT_DECAY_FACTOR = [[Settings::END_OF_TURN_THREAT_DECAY_PERCENT, 0].max, 100].min * 0.01    DEATH_THREAT_DECAY_FACTOR = [[Settings::DEATH_THREAT_DECAY_PERCENT, 0].max, 100].min * 0.01    ESCAPE_THREAT_DECAY_FACTOR = [[Settings::ESCAPE_THREAT_DECAY_PERCENT, 0].max, 100].min * 0.01    TARGET_SWITCH_THRESHOLD = 1 + [Settings::TARGET_SWITCH_THRESHOLD_PERCENT, 0].max * 0.01     endend# ***************************************************************************# * Game_Battler class                                                      *# ***************************************************************************# * Contains the vast majority of the threat code.                          *# * AI enemies. So they must both be  *# * altered as a result. This is not to be confused with the random         *# * target selection in Game_Unit, which is still to be called upon for     *# * skills with randomized targets, nor the targeting of forced actions,    *# * which are selected by the call, not by threat.                          *# ***************************************************************************# TODO: And I'll need to modify the auto-battle functions so they use threat. That's# in both Game_Actor and Game_Action. Game_Action uses it to evaluate which# skill and target to select for auto-battling battlers. Game_Actor is more about# selecting the action in general.#   Game_Action.evaluate#   Game_Action.evaluate_item#   Game_Action.item_target_candidates#   Game_Action.evaluate_item_with_target#   Game_Actor.make_action_list#   Game_Actor.make_auto_battle_actions#   Game_Actor.make_confusion_actions#   Game_Actor.make_actions# ***************************************************************************class Game_Battler < Game_BattlerBase   # *************************************************************************  # * Aliases                                                               *  # *************************************************************************  alias garryl_threat_alias_game_battler_initialize       initialize  alias garryl_threat_alias_game_battler_clear_states     clear_states  alias garryl_threat_alias_game_battler_remove_state     remove_state  alias garryl_threat_alias_game_battler_die              die  alias garryl_threat_alias_game_battler_escape           escape  alias garryl_threat_alias_game_battler_on_battle_start  on_battle_start  alias garryl_threat_alias_game_battler_on_turn_end      on_turn_end   alias garryl_threat_alias_game_battler_item_apply       item_apply  # *************************************************************************  # * Public Variables                                                      *  # *************************************************************************  attr_reader :threat_target  attr_reader :threat_table  attr_reader :dying  # Workaround for threat system so it can tell the                      # battler is not a valid target while in the process of                      # performing death cleanup. Needed because the death                      # state doesn't get applied until after die returns.                      # TODO: Consider if alive? should check for dying, too?   # *************************************************************************  # * Overwritten Functions                                                 *  # *************************************************************************  #--------------------------------------------------------------------------  # * Force Action  # * Overwritten function.  # * If target_index == -2 (select last target), uses threat_target instead  #   if that options has been enabled in the settings.  #--------------------------------------------------------------------------  def force_action(skill_id, target_index)    clear_actions    action = Game_Action.new(self, true)    action.set_skill(skill_id)    if target_index == -2      action.target_index = last_target_index unless Garryl::Threat::Settings::FORCE_ACTION_LAST_THREAT_TARGET      action.target_index = get_opponent_battler_index(@threat_target) if Garryl::Threat::Settings::FORCE_ACTION_LAST_THREAT_TARGET    elsif target_index == -1      action.decide_random_target    else      action.target_index = target_index    end    @actions.push(action)  end   # *************************************************************************  # * Aliased Functions                                                     *  # *************************************************************************  #--------------------------------------------------------------------------  # * Object Initialization  # * Aliased function.  #--------------------------------------------------------------------------  def initialize    garryl_threat_alias_game_battler_initialize    @threat_target = nil    @threat_table = {} #hash.new()  end   #--------------------------------------------------------------------------  # * Clear State Information  # * Aliased function.  # * Updates threat target if this removes fixation.  #--------------------------------------------------------------------------  def clear_states    #TODO: clear_states is called during the initialization process, before the states array has been set up    #fixatedBefore = fixated?    garryl_threat_alias_game_battler_clear_states    #update_threat_target if fixatedBefore && !fixated?  end   #--------------------------------------------------------------------------  # * Remove State  # * Aliased function.  # * Updates threat target if this removes fixation.  #--------------------------------------------------------------------------  def remove_state(state_id)    fixatedBefore = fixated?    garryl_threat_alias_game_battler_remove_state(state_id)    update_threat_target if fixatedBefore && !fixated?  end   #--------------------------------------------------------------------------  # * Knock Out  # * Aliased function.  #--------------------------------------------------------------------------  def die    @dying = true;    garryl_threat_alias_game_battler_die    decay_threat_of_all_opponents(Garryl::Threat::DEATH_THREAT_DECAY_FACTOR)    @dying = false;  end   #--------------------------------------------------------------------------  # * Escape  # * Aliased function.  #--------------------------------------------------------------------------  def escape    garryl_threat_alias_game_battler_escape    decay_threat_of_all_opponents(Garryl::Threat::ESCAPE_THREAT_DECAY_FACTOR)  end   #--------------------------------------------------------------------------  # * Processing at Start of Battle  # * Aliased function.  #--------------------------------------------------------------------------  def on_battle_start    garryl_threat_alias_game_battler_on_battle_start    reset_threat_table  end   #--------------------------------------------------------------------------  # * Processing at End of Turn  # * Aliased function.  # * Note that threat decay occurs after states have been removed, so target  #     may change if fixate states are lost.  #--------------------------------------------------------------------------  def on_turn_end    garryl_threat_alias_game_battler_on_turn_end    turn_end_threat_decay  end  #--------------------------------------------------------------------------  # * Apply Effect of Skill/Item  # * Aliased function.  # * Note: Called by each subject of the skill/item.  #--------------------------------------------------------------------------  def item_apply(user, item)    garryl_threat_alias_game_battler_item_apply(user, item)    user.generate_threat_from_item(self, item, @result)  end   # *************************************************************************  # * Threat Initialization                                                 *  # *************************************************************************  #--------------------------------------------------------------------------  # * Initializes threat table  # * Used for start of battle and for total threat clear  #--------------------------------------------------------------------------  def reset_threat_table    @threat_table = {} #hash.new()       #Only initialize opposite numbers    opponents_unit.members.each { |battler| init_threat_table_entry(@threat_table, battler) if is_valid_threat_target?(battler)}    update_threat_target  end   #--------------------------------------------------------------------------  # * Initializes an entry in a threat table  #--------------------------------------------------------------------------  def init_threat_table_entry(table, battler)    table[battler] = (battler.tgr * Garryl::Threat::Settings::INITIAL_THREAT).to_i       #Test    #puts "Initializing #{battler.name} for #{self.name} with #{table[battler]} threat (tgr = #{battler.tgr})"  end   # *************************************************************************  # * Threat Modification                                                   *  # *************************************************************************  #--------------------------------------------------------------------------  # * Adds a flat amount of threat towards a battler  # * Dynamically initializes the threat table entry if not present  #   (ex: join mid-battle)  #--------------------------------------------------------------------------  def add_threat(battler, threat, skip_update = false)    init_threat_table_entry(@threat_table, battler) if @threat_table[battler] == nil    @threat_table[battler] += threat.to_i    update_threat_target unless skip_update  end   #--------------------------------------------------------------------------  # * Removes a fraction of threat towards a battler  # * Ignores if the threat table entry if not present  #   (ex: join mid-battle)  #--------------------------------------------------------------------------  def decay_threat(battler, decay_factor, skip_update = false)    @threat_table[battler] = (@threat_table[battler] * (1.0 - decay_factor)).to_int unless @threat_table[battler] == nil    update_threat_target unless skip_update  end   #--------------------------------------------------------------------------  # * Adds a flat amount of threat from all opponents towards this battler  #--------------------------------------------------------------------------  def add_threat_to_all_opponents(threat, skip_update = false)    opponents_unit.members.each { |battler| battler.add_threat(self, threat, skip_update)}  end   #--------------------------------------------------------------------------  # * Removes a fraction of threat from all opponents towards this battler  #--------------------------------------------------------------------------  def decay_threat_of_all_opponents(decay_factor, skip_update = false)    opponents_unit.members.each { |battler| battler.decay_threat(self, decay_factor, skip_update)}  end   #--------------------------------------------------------------------------  # * Removes a fraction of threat from this battler towards all opponents  #     at end of turn  #--------------------------------------------------------------------------  def turn_end_threat_decay(decay_factor = Garryl::Threat::END_OF_TURN_THREAT_DECAY_FACTOR, skip_update = false)    @threat_table.each_key { |battler| decay_threat(battler, decay_factor, true) }    update_threat_target unless skip_update  end   # *************************************************************************  # * Skill/Item Usage Threat                                               *  # *************************************************************************  #--------------------------------------------------------------------------  # * Generates threat as the result of an item's resolution (skill/item)  #--------------------------------------------------------------------------  def generate_threat_from_item(subject, item, result)    targeting_enemy = opponents_unit.members.include?(subject)       if result.hit?      unless item.damage.none?        generate_damage_threat(subject, item, result) if targeting_enemy        generate_healing_threat(subject, item, result) unless targeting_enemy        generate_absorb_threat(subject, item, result)      end      #item.effects.each {|effect| item_effect_apply(user, item, effect) }      #item_user_effect(user, item)      #TODO: Threat from non-damage effects.           #After all threat is applied, then have everyone update their targets.      opponents_unit.members.each { |battler| battler.update_threat_target }    end     end   #--------------------------------------------------------------------------  # * Generates threat from dealing damage  #--------------------------------------------------------------------------  def generate_damage_threat(subject, item, result)    threat_multiplier = tgr * item.threat_multiplier       damage_threat = 0    damage_threat += result.hp_damage if result.hp_damage > 0    damage_threat += result.mp_damage * Garryl::Threat::Settings::THREAT_MP_MULTPLIER if result.mp_damage > 0    damage_threat *= threat_multiplier    subject.add_threat(self, damage_threat * Garryl::Threat::Settings::THREAT_PER_DAMAGE, true)    add_threat_to_all_opponents(damage_threat * Garryl::Threat::Settings::THREAT_PER_DAMAGE_GLOBAL, true)  end   #--------------------------------------------------------------------------  # * Generates threat from healing  #--------------------------------------------------------------------------  def generate_healing_threat(subject, item, result)    threat_multiplier = tgr * item.threat_multiplier       targeting_enemies = opponents_unit.members.select {|battler| battler.threat_target == subject}       healing_threat = 0    #healing represented as negative damage    healing_threat -= result.hp_damage if result.hp_damage < 0    healing_threat -= result.mp_damage * Garryl::Threat::Settings::THREAT_MP_MULTPLIER if result.mp_damage < 0    healing_threat *= threat_multiplier    targeting_enemies.each {|battler| battler.add_threat(self, healing_threat * Garryl::Threat::Settings::THREAT_PER_HEAL, true)}    add_threat_to_all_opponents(healing_threat * Garryl::Threat::Settings::THREAT_PER_HEAL_GLOBAL, true)  end   #--------------------------------------------------------------------------  # * Generates threat from absorption damage  #--------------------------------------------------------------------------  def generate_absorb_threat(subject, item, result)    threat_multiplier = tgr * item.threat_multiplier       targeting_enemies = opponents_unit.members.select {|battler| battler.threat_target == self}       absorb_threat = 0    absorb_threat += result.hp_drain if result.hp_drain > 0    absorb_threat += result.mp_drain * Garryl::Threat::Settings::THREAT_MP_MULTPLIER if result.mp_drain > 0    absorb_threat *= Garryl::Threat::Settings::THREAT_ABSORB_MULTIPLIER    absorb_threat *= threat_multiplier    targeting_enemies.each {|battler| battler.add_threat(self, absorb_threat * Garryl::Threat::Settings::THREAT_PER_HEAL, true)}    add_threat_to_all_opponents(absorb_threat * Garryl::Threat::Settings::THREAT_PER_HEAL_GLOBAL, true)  end   # *************************************************************************  # * Threat Target Selection                                               *  # *************************************************************************  #--------------------------------------------------------------------------  # * Updates current threat target  # * Should be called after every change to the threat table is finished.  # * Should also be called whenever fixate is removed.  #--------------------------------------------------------------------------  def update_threat_target       #Get all valid targets (alive battlers in opponent unit)    validTargets = opponents_unit.members.select { |battler| is_valid_threat_target?(battler) }    validThreats = {}    @threat_table.select{ |battler, threat| validTargets.include?(battler) }.each{|kvp| validThreats[kvp[0]] = kvp[1]}       #Test    #puts "#{name}'s valid targets:"    #validTargets.each {|battler| puts "  #{battler.name}" }    #puts "#{name}'s valid target threats:"    #validThreats.each {|battler, threat| puts "  #{battler.name} => #{threat}" }       #If current target is invalid...    if (!has_valid_threat_target?)      #...select highest threat non-dead target           #If no valid targets, set to no target      if (validTargets.empty? || validThreats.empty?)        change_threat_target(nil)        #Test        #puts "#{name} has no valid targets"             #If a valid target...      else        #... select the one with the highest threat        highestValidThreat = validThreats.values.max        highestThreatBattler = validThreats.index(highestValidThreat)        change_threat_target(highestThreatBattler)               #Test        #puts "#{name} selected #{@threat_target.name} with #{highestValidThreat} threat"      end         #If current target is valid, unless fixated...    elsif (!fixated?)      #...check if should change target      currentTargetThreat = @threat_table[@threat_target]      highestValidThreat = validThreats.values.max           #Test      #if (highestValidThreat > (currentTargetThreat * Garryl::Threat::TARGET_SWITCH_THRESHOLD))      #  puts "#{name} changed to #{validThreats.index(highestValidThreat).name} with #{highestValidThreat} threat"      #else      #  puts "#{name} stayed with #{@threat_target.name} with #{@threat_table[@threat_target]} threat vs. #{highestValidThreat} threat"      #end           change_threat_target(validThreats.index(highestValidThreat)) if (highestValidThreat > (currentTargetThreat * Garryl::Threat::TARGET_SWITCH_THRESHOLD))    else      #Test      #puts "#{name} remains fixated on #{@threat_target.name}"    end     end   #--------------------------------------------------------------------------  # * Checks if a target is assigned to a target  # * False if the target index is out of range  #--------------------------------------------------------------------------  def has_any_threat_target?    return false if (@threat_target == nil)    return (get_opponent_battler_index(@threat_target) != nil)  end   #--------------------------------------------------------------------------  # * Checks if a target is assigned to a valid target  # * False if the target index is invalid or dead  #--------------------------------------------------------------------------  def has_valid_threat_target?    return (has_any_threat_target? && is_valid_threat_target?(@threat_target))  end   #--------------------------------------------------------------------------  # * Changes current threat target  #--------------------------------------------------------------------------  def change_threat_target(new_target)    #Test    #if (new_target == nil && @threat_target == nil)    #  puts "#{name} went from no target to no target"    #elsif (new_target != nil && @threat_target == nil)    #  puts "#{name} acquired #{new_target.name} as a threat target"    #elsif (new_target == nil && @threat_target != nil)    #  puts "#{name} dropped #{@threat_target.name} as a threat target"    #else    #  puts "#{name} changed target from #{@threat_target.name} to #{new_target.name}"    #end       @threat_target = new_target  end   #--------------------------------------------------------------------------  # * Checks if this battler is fixated on its current threat target  #--------------------------------------------------------------------------  def fixated?    #Test    #puts "Checking fixation for #{name}"    #puts "States.size = #{states.size}, fixation states.size = #{states.select {|state| state.fixation }.size}"       return !states.select {|state| state.fixation }.empty?    #return false  end   #--------------------------------------------------------------------------  # * Checks if the passed battler is a valid threat target for this battler  #--------------------------------------------------------------------------  def is_valid_threat_target?(battler)    #Test    #valid = (battler != nil && opponents_unit.members.include?(battler) && battler.alive? && !battler.dying)    #if (valid)    #  puts "#{name} sees #{battler.name} as a valid target"    #else    #  puts "#{name} sees #{battler.name} as an invalid target"    #end       return (battler != nil && opponents_unit.members.include?(battler) && battler.alive? && !battler.dying)  end   # *************************************************************************  # * Utility functions                                                     *  # *************************************************************************  #--------------------------------------------------------------------------  # * Gets the index of the current threat target  # * Returns -1 if no target  #--------------------------------------------------------------------------  def get_threat_target_battler_index    return get_opponent_battler_index(@threat_target)  end   #--------------------------------------------------------------------------  # * Gets the index of the indicated opposing battler  # * Returns -1 if no target  #--------------------------------------------------------------------------  def get_opponent_battler_index(battler)    #return opponents_unit.members.index(battler) unless battler == nil    #return nil    return battler.index unless battler == nil    return -1  end   #--------------------------------------------------------------------------  # * Finds the highest threat battler among those passed.  # * Returns the highest threat battler, or nil if none are on the threat  #   list.  #--------------------------------------------------------------------------  def select_highest_threat_among_battlers(battlers_array)    selectedThreats = {}    #battlers_array.each {|battler| selectedThreats[battler] = @threat_table[battler] if @threat_table.has_key?(battler)}    @threat_table.select{ |battler, threat| battlers_array.include?(battler) }.each{|key_value_pair| selectedThreats[key_value_pair[0]] = key_value_pair[1]}       # If anybody alive on the list is on this battler's threat table, use the    # one with highest threat. Otherwise, nil.    return (selectedThreats.empty? ? nil : selectedThreats.index(selectedThreats.values.max))  end   #--------------------------------------------------------------------------  # * Performs a World of Warcraft style taunt.  # * User's threat becomes equal to the threat of the current threat target  #   (unless already higher), and user becomes target's threat target.  #--------------------------------------------------------------------------#  def taunt(target)#    if (target.is_valid_threat_target?(self))#      targetTargetThreat = (target.has_valid_threat_target? != nil && target.threat_table[target.threat_target] != nil ? target.threat_table[target.threat_target] : 0)#      userThreat = (target.threat_table[self] != nil ? target.threat_table[self] : 0)#      target.add_threat(self, [targetTargetThreat - userThreat, 0].max)#      target.change_threat_target(self)#    end#  end end# ***************************************************************************# * Game_Action class                                                       *# ***************************************************************************# * Note: targets_for_opponents not necessary to change. For non-random     *# * targets, it just smoothes out the targeting, in case the selected index *# * is dead, and for random targeting it does random like we want.          *# ***************************************************************************class Game_Action   # *************************************************************************  # * Overwritten Functions                                                 *  # *************************************************************************  #--------------------------------------------------------------------------  # * Set Battle Action of Enemy Character  #     action : RPG::Enemy::Action  # * Overwritten function.  # * Uses this to set up the skill's target when an enemy uses a skill that  #   can use the threat target. The default target index (-1) normally just  #   doesn't get changed from instantiation, and represents a random target  #   when it comes to evaluating the actual action resolution.  #   See Game_Action.make_targets for when it resolves the desired target  #   index against confusion/restriction and which battlers are actually  #   still alive to be attacked.  #--------------------------------------------------------------------------  def set_enemy_action(action)    if action      set_skill(action.skill_id)      @target_index = subject.get_threat_target_battler_index if use_threat_target?           #Test      #puts "Setting enemy action..."      #puts "  User: #{subject.name}"      #puts "  Threat Target: #{subject.threat_target.name}" unless subject.threat_target == nil      #puts "  Threat Target: No target" if subject.threat_target == nil      #puts "  Item: #{item.name}"      #puts "  Scope: #{item.scope}"      #puts "  Target Index: #{@target_index}"    else      clear    end  end   #--------------------------------------------------------------------------  # * Target When Confused  # * Overwritten function.  # * Uses threat target if berserk and that option is set.  #--------------------------------------------------------------------------  def confusion_target    case subject.confusion_level    when 1  #restriction: target enemy (berserk)      #opponents_unit.random_target      return opponents_unit.random_target unless Garryl::Threat::Settings::USE_THREAT_WHEN_BERSERK      return subject.get_opponent_battler_index(subject.threat_target) if Garryl::Threat::Settings::USE_THREAT_WHEN_BERSERK    when 2      if rand(2) == 0        opponents_unit.random_target      else        friends_unit.random_target      end    else      friends_unit.random_target    end  end   #--------------------------------------------------------------------------  # * Targets for Opponents  # * Overwritten function.  # * Uses smooth targeting with threat target.  #--------------------------------------------------------------------------  def targets_for_opponents    if item.for_random?      Array.new(item.number_of_targets) { opponents_unit.random_target }    elsif item.for_one?      num = 1 + (attack? ? subject.atk_times_add.to_i : 0)      if @target_index < 0        [opponents_unit.random_target] * num      elsif use_threat_target?        [opponents_unit.smooth_target(@target_index, subject)] * num      else        [opponents_unit.smooth_target(@target_index)] * num      end    else      opponents_unit.alive_members    end  end   # *************************************************************************  # * Target Selection                                                      *  # *************************************************************************  #--------------------------------------------------------------------------  # * Returns true if this item/skill's targeting is ruled by threat  #--------------------------------------------------------------------------  def use_threat_target?    return item.for_opponent? && item.for_one? && !item.for_random?  end end# ***************************************************************************# * Game_Party class                                                        *# ***************************************************************************class Game_Unit   # *************************************************************************  # * Aliases                                                               *  # *************************************************************************  alias garryl_threat_alias_game_unit_random_target       random_target   # *************************************************************************  # * Overwritten Functions                                                 *  # *************************************************************************  #--------------------------------------------------------------------------  # * Smooth Selection of Target  # * Overwritten function.  # * Like smooth_target, it produces the member at the target index, or  #   replaces it with another if that member is dead. The difference is that  #   this one selects whichever member has the highest threat from the  #   attacker instead of the first alive one only.  #--------------------------------------------------------------------------  def smooth_target(index, attacker = nil)    member = members[index]    return member if (member && member.alive?)    return alive_members[0] if attacker == nil || !Garryl::Threat::Settings::USE_THREAT_WHEN_TARGET_DEAD       highestThreatBattler = attacker.select_highest_threat_among_battlers(alive_members)    #aliveThreats = {}    #alive_members.each {|member| aliveThreats[member] = attacker.threat_table[member] if attacker.threat_table.has_key?(member)}       # If anybody alive in this unit is on the attacker's threat table, use the    # one with highest threat. Otherwise, use the first alive target.    return (highestThreatBattler == nil ? alive_members[0] : highestThreatBattler)    #return (aliveThreats.empty? ? random_target : aliveThreats.index(aliveThreats.values.max))  end   # *************************************************************************  # * Aliased Functions                                                     *  # *************************************************************************  #--------------------------------------------------------------------------  # * Random Selection of Target  # * Aliased function.  # * Added support for the option to ignore TGR for random targeting.  #--------------------------------------------------------------------------  def random_target    return garryl_threat_alias_game_unit_random_target if Garryl::Threat::Settings::USE_TGR_FOR_RANDOM_TARGETING    return alive_members.empty? ? nil : alive_members[rand(alive_members.size)]  end end# ***************************************************************************# * Game_Party class                                                        *# ***************************************************************************# ***************************************************************************# TODO: Alias Game_Party.add_actor and Game_Patrty.remove_actor.# Need to add handling for party members being added mid-fight.# Add: If the party member is not already part of the party, and the party member# ... actually, may not be necessary. The data structures get initialized just fine,#   and it's easier to dynamically check for missing threat entries and add them then.#   However, there are enough little flaws (triggering death decay, not updating for new TGRs, etc.) that it's worth considering a little effort.# ***************************************************************************# ***************************************************************************# * RPG::State class                                                        *# ***************************************************************************# Loading done in the style of [URL="http://www.rpgmakervxace.net/topic/14439-basic-guide-to-working-with-note-tags/#"]http://www.rpgmakervxace.net/topic/14439-basic-guide-to-working-with-note-tags/#[/URL] Technically, this can mess up if it's somehow asked for fixation before   *# the data manager has finished loading the state from the database.        *# However, that can't really happen unless someone were actively trying to  *# mess things up by inserting a check inside the part where the loading     *# takes place. So no worries.                                               *# ***************************************************************************class RPG::State < RPG::BaseItem   # *************************************************************************  # * Fixation                                                              *  # *************************************************************************  #--------------------------------------------------------------------------  # * Checks if this state causes fixation.  # * Lazy instantiation. Doesn't check the note tag and set the variable  #   until it's first requested to be used.  #--------------------------------------------------------------------------  def fixation    #Lazy instantiation    return @fixation unless @fixation == nil    load_notetag_fixate    return @fixation  end   # *************************************************************************  # * Notetag Loading                                                       *  # *************************************************************************  #--------------------------------------------------------------------------  # * Uses the note tag to set the fixation variable.  #--------------------------------------------------------------------------  def load_notetag_fixate    #Test    #puts "Loading fixation notetag for state #{@id}: #{@name}"    #puts "Note: #{@note}"    #puts "Fixation state? #{self.note =~ Garryl::Threat::Regex::STATE_FIXATE ? true : false}"       @fixation = self.note =~ Garryl::Threat::Regex::STATE_FIXATE ? true : false  end end# ***************************************************************************# * RPG::UsableItem class                                                   *# ***************************************************************************# For both skills and items.                                                *# Loading done in the style of [URL="http://www.rpgmakervxace.net/topic/14439-basic-guide-to-working-with-note-tags/#"]http://www.rpgmakervxace.net/topic/14439-basic-guide-to-working-with-note-tags/#[/URL] Technically, this can mess up if it's somehow asked for threat data before*# the data manager has finished loading the item from the database.         *# However, that can't really happen unless someone were actively trying to  *# mess things up by inserting a check inside the part where the loading     *# takes place. So no worries.                                               *# ***************************************************************************class RPG::UsableItem < RPG::BaseItem   # *************************************************************************  # * Threat Effects                                                        *  # *************************************************************************  #--------------------------------------------------------------------------  # * Gets this state's threat multiplier.  # * Lazy instantiation. Doesn't check the note tag and set the variable  #   until it's first requested to be used.  #--------------------------------------------------------------------------  def threat_multiplier    #Lazy instantiation    return @threat_multiplier unless @threat_multiplier == nil    load_notetag_threat_multiplier    return @threat_multiplier  end   # *************************************************************************  # * Notetag Loading                                                       *  # *************************************************************************  #--------------------------------------------------------------------------  # * Uses the note tag to set the threat_multiplier variable.  #--------------------------------------------------------------------------  def load_notetag_threat_multiplier    #Test    #puts "Loading threat_multiplier notetag for state #{@id}: #{@name}"    #puts "Note: #{@note}"    #puts "regex result:  #{self.note =~ Garryl::Threat::Regex::THREAT_MULTIPLIER}"    #puts "threat_multiplier: #{self.note =~ Garryl::Threat::Regex::THREAT_MULTIPLIER ? $1.to_f : 1.0}"       @threat_multiplier = self.note =~ Garryl::Threat::Regex::THREAT_MULTIPLIER ? $1.to_f : 1.0  end end
 

Latest Threads

Latest Profile Posts

Zaj
Hey morning all! I got another quick question..I just switched to RMMZ from VX Ace..so I am wondering why default scripts(is that how u call it) of VXAce is available, but not in MZ's case? How do I figure out ahh I mean could you show me
Sometimes im the biggest fool alive... Ah well, ce la vie
The worst feeling in the world is going for a walk on a nice summer day, seeing an ice cream truck, and realizing you don't have any money with you :(
Mike running through an area that's influenced by his thoughts, thus his drawings are infused into the land.
We're playing Omori by OMOCAT starting at 2pm est :D

Forum statistics

Threads
111,343
Messages
1,060,320
Members
144,668
Latest member
emilycastilloknowles
Top