Module: Lich::Gemstone::Shield

Defined in:
documented/gemstone/psms/shield.rb

Overview

Manages shield techniques and their interactions in the game.

This module provides methods to access and utilize various shield techniques.

Constant Summary collapse

@@shield_techniques =
{
  "adamantine_bulwark"    => {
    :short_name => "bulwark",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Adamantine Bulwark does not need to be activated\.  If you are wielding the appropriate type of shield, it will always be active\./,
    :usage      => nil
  },
  "block_specialization"  => {
    :short_name => "blockspec",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /The Block Specialization combat maneuver is always active once you have learned it\./,
    :usage      => nil
  },
  "block_the_elements"    => {
    :short_name => "blockelements",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Block the Elements does not need to be activated\.  If you are wielding the appropriate type of shield, it will always be active\./,
    :usage      => nil
  },
  "deflect_magic"         => {
    :short_name => "deflectmagic",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Deflect Magic does not need to be activated once you have learned it\.  It will automatically apply to all relevant attacks, provided that you are wielding a shield and possess 3 ranks of the relevant Shield Focus specialization\./,
    :usage      => nil
  },
  "deflect_missiles"      => {
    :short_name => "deflectmissiles",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Deflect Missiles does not need to be activated once you have learned it\.  It will automatically apply to all relevant attacks, provided that you are wielding a shield and possess 3 ranks of the relevant Shield Focus specialization\./,
    :usage      => nil
  },
  "deflect_the_elements"  => {
    :short_name => "deflectelements",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Deflect the Elements does not need to be activated\.  If you are wielding the appropriate type of shield, it will always be active\./,
    :usage      => nil
  },
  "disarming_presence"    => {
    :short_name => "dpresence",
    :type       => :martial_stance,
    :cost       => { stamina: 20 },
    :regex      => Regexp.union(/You assume the Disarming Presence Stance, adjusting your footing and grip to allow for the proper pivot and thrust technique to disarm attacking foes\./,
                                /You re\-settle into the Disarming Presence Stance, re-ensuring your footing and grip are properly positioned\./),
    :usage      => "dpresence"
  },
  "guard_mastery"         => {
    :short_name => "gmastery",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Guard Mastery does not need to be activated\.  If you are wielding the appropriate type of shield, it will always be active\./,
    :usage      => nil
  },
  "large_shield_focus"    => {
    :short_name => "lfocus",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Large Shield Focus does not need to be activated\.  If you are wielding the appropriate type of shield, it will always be active\./,
    :usage      => nil
  },
  "medium_shield_focus"   => {
    :short_name => "mfocus",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Medium Shield Focus does not need to be activated\.  If you are wielding the appropriate type of shield, it will always be active\./,
    :usage      => nil
  },
  "phalanx"               => {
    :short_name => "phalanx",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Phalanx does not need to be activated\.  If you are wielding the appropriate type of shield, it will always be active\./,
    :usage      => nil
  },
  "prop_up"               => {
    :short_name => "prop",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Prop Up does not need to be activated once you have learned it\.  It will automatically apply to all relevant attacks, provided that you are wielding a shield and possess 3 ranks of the relevant Shield Focus specialization\./,
    :usage      => nil
  },
  "protective_wall"       => {
    :short_name => "pwall",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Protective Wall does not need to be activated\.  If you are wielding the appropriate type of shield, it will always be active\./,
    :usage      => nil
  },
  "shield_bash"           => {
    :short_name => "bash",
    :type       => :setup,
    :cost       => { stamina: 9 },
    :regex      => /You lunge forward at .+ with your .+ and attempt a shield bash\!/,
    :usage      => "bash"
  },
  "shield_charge"         => {
    :short_name => "charge",
    :type       => :setup,
    :cost       => { stamina: 14 },
    :regex      => /You charge forward at .+ with your .+ and attempt a shield charge\!/,
    :usage      => "charge"
  },
  "shield_forward"        => {
    :short_name => "forward",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Shield Forward does not need to be activated once you have learned it\.  It will automatically activate upon the use of a shield attack\./,
    :usage      => "forward"
  },
  "shield_mind"           => {
    :short_name => "mind",
    :type       => :buff,
    :cost       => { stamina: 10 },
    :regex      => /You must be wielding an ensorcelled or anti-magical shield to be able to properly shield your mind and soul\./,
    :usage      => "mind"
  },
  "shield_pin"            => {
    :short_name => "pin",
    :type       => :attack,
    :cost       => { stamina: 15 },
    :regex      => /You attempt to expose a vulnerability with a diversionary shield bash on .+\!/,
    :usage      => "pin"
  },
  "shield_push"           => {
    :short_name => "push",
    :type       => :setup,
    :cost       => { stamina: 7 },
    :regex      => /You raise your .+ before you and attempt to push .+ away\!/,
    :usage      => "push"
  },
  "shield_riposte"        => {
    :short_name => "riposte",
    :type       => :martial_stance,
    :cost       => { stamina: 20 },
    :regex      => Regexp.union(/You assume the Shield Riposte Stance, preparing yourself to lash out at a moment's notice\./,
                                /You re\-settle into the Shield Riposte Stance, preparing yourself to lash out at a moment's notice\./),
    :usage      => "riposte"
  },
  "shield_spike_mastery"  => {
    :short_name => "spikemastery",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Shield Spike Mastery does not need to be activated\.  If you are wielding the appropriate type of shield, it will always be active\./,
    :usage      => nil
  },
  "shield_strike"         => {
    :short_name => "strike",
    :type       => :attack,
    :cost       => { stamina: 15 },
    :regex      => /You launch a quick bash with your .+ at .+\!/,
    :usage      => "strike"
  },
  "shield_strike_mastery" => {
    :short_name => "strikemastery",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Shield Strike Mastery does not need to be activated once you have learned it\.  It will automatically apply to all relevant focused multi\-attacks, provided that you maintain the prerequisite ranks of Shield Bash\./,
    :usage      => nil
  },
  "shield_swiftness"      => {
    :short_name => "swiftness",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Shield Swiftness does not need to be activated once you have learned it\.  It will automatically apply to all relevant attacks, provided that you are wielding a small or medium shield and have at least 3 ranks of the relevant Shield Focus specialization\./,
    :usage      => nil
  },
  "shield_throw"          => {
    :short_name => "throw",
    :type       => :area_of_effect,
    :cost       => { stamina: 20 },
    :regex      => /You snap your arm forward, hurling your .+ at .+ with all your might\!/,
    :usage      => "throw"
  },
  "shield_trample"        => {
    :short_name => "trample",
    :type       => :area_of_effect,
    :cost       => { stamina: 14 },
    :regex      => /You raise your .+ before you and charge headlong towards .+\!/,
    :usage      => "trample"
  },
  "shielded_brawler"      => {
    :short_name => "brawler",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Shielded Brawler does not need to be activated once you have learned it\.  It will automatically apply to all relevant attacks, provided that you are wielding a shield and possess 3 ranks of the relevant Shield Focus specialization\./,
    :usage      => nil
  },
  "small_shield_focus"    => {
    :short_name => "sfocus",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Small Shield Focus does not need to be activated\.  If you are wielding the appropriate type of shield, it will always be active\./,
    :usage      => nil
  },
  "spell_block"           => {
    :short_name => "spellblock",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Spell Block does not need to be activated once you have learned it\.  It will automatically apply to all relevant attacks, provided that you are wielding a shield and possess 3 ranks of the relevant Shield Focus specialization\./,
    :usage      => nil
  },
  "steady_shield"         => {
    :short_name => "steady",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Steady Shield does not need to be activated once you have learned it\.  It will automatically apply to all relevant attacks against you, provided that you maintain the prerequisite ranks of Stun Maneuvers\./,
    :usage      => nil
  },
  "steely_resolve"        => {
    :short_name => "resolve",
    :type       => :buff,
    :cost       => { stamina: 30 },
    :regex      => Regexp.union(/You focus your mind in a steely resolve to block all attacks against you\./,
                                /You are still mentally fatigued from your last invocation of your Steely Resolve\./),
    :usage      => "resolve"
  },
  "tortoise_stance"       => {
    :short_name => "tortoise",
    :type       => :martial_stance,
    :cost       => { stamina: 20 },
    :regex      => Regexp.union(/You assume the Stance of the Tortoise, holding back some of your offensive power in order to maximize your defense\./,
                                /You re\-settle into the Stance of the Tortoise, holding back your offensive power in order to maximize your defense\./),
    :usage      => "tortoise"
  },
  "tower_shield_focus"    => {
    :short_name => "tfocus",
    :type       => :passive,
    :cost       => { stamina: 0 },
    :regex      => /Tower Shield Focus does not need to be activated\.  If you are wielding the appropriate type of shield, it will always be active\./i,
    :usage      => nil
  }
}

Class Method Summary collapse

Class Method Details

.[](name) ⇒ Hash?

Retrieves a shield technique by its name.

Parameters:

  • name (String) —

    the name of the shield technique to retrieve.

Returns:

  • (Hash, nil) —

    the shield technique details or nil if not found.



266
267
268
# File 'documented/gemstone/psms/shield.rb', line 266

def self.[](name)
  return PSMS.assess(name, 'Shield')
end

.affordable?(name, forcert_count: 0) ⇒ Boolean

Determines if a shield technique can be afforded based on the current conditions.

Parameters:

  • name (String) —

    the name of the shield technique to check.

  • forcert_count (Integer) (defaults to: 0) —

    the number of forcerts available (default is 0).

Returns:

  • (Boolean) —

    true if the technique is affordable, false otherwise.



283
284
285
286
# File 'documented/gemstone/psms/shield.rb', line 283

def self.affordable?(name, forcert_count: 0)
  return true if @@shield_techniques.fetch(PSMS.find_name(name, "Shield")[:long_name])[:type] == :area_of_effect && Effects::Buffs.active?("Glorious Momentum")
  return PSMS.assess(name, 'Shield', true, forcert_count: forcert_count)
end

.available?(name, min_rank: 1, forcert_count: 0) ⇒ Boolean

Checks if a shield technique is available for use based on known status, affordability, and availability.

Parameters:

  • name (String) —

    the name of the shield technique.

  • min_rank (Integer) (defaults to: 1) —

    the minimum rank required to use the technique (default is 1).

  • forcert_count (Integer) (defaults to: 0) —

    the number of forcerts available (default is 0).

Returns:

  • (Boolean) —

    true if the technique is available, false otherwise.



293
294
295
296
297
# File 'documented/gemstone/psms/shield.rb', line 293

def self.available?(name, min_rank: 1, forcert_count: 0)
  Shield.known?(name, min_rank: min_rank) &&
    Shield.affordable?(name, forcert_count: forcert_count) &&
    PSMS.available?(name)
end

.buff_active?(name) ⇒ Boolean?

Checks if a buff associated with a shield technique is currently active.

Parameters:

  • name (String) —

    the name of the shield technique.

Returns:

  • (Boolean, nil) —

    true if the buff is active, false if not, or nil if the technique does not have a buff.



302
303
304
305
# File 'documented/gemstone/psms/shield.rb', line 302

def self.buff_active?(name)
  return unless @@shield_techniques.fetch(PSMS.find_name(name, "Shield")[:long_name]).key?(:buff)
  Effects::Buffs.active?(@@shield_techniques.fetch(PSMS.find_name(name, "Shield")[:long_name])[:buff])
end

.known?(name, min_rank: 1) ⇒ Boolean

Checks if a shield technique is known at or above a specified rank.

Parameters:

  • name (String) —

    the name of the shield technique.

  • min_rank (Integer) (defaults to: 1) —

    the minimum rank to check against (default is 1).

Returns:

  • (Boolean) —

    true if the technique is known at the specified rank, false otherwise.



274
275
276
277
# File 'documented/gemstone/psms/shield.rb', line 274

def self.known?(name, min_rank: 1)
  min_rank = 1 unless min_rank >= 1 # in case a 0 or below is passed
  Shield[name] >= min_rank
end

.regexp(name) ⇒ Regexp

Retrieves the regular expression associated with a shield technique.

Parameters:

  • name (String) —

    the name of the shield technique.

Returns:

  • (Regexp) —

    the regex pattern for the technique.



361
362
363
# File 'documented/gemstone/psms/shield.rb', line 361

def self.regexp(name)
  @@shield_techniques.fetch(PSMS.find_name(name, "Shield")[:long_name])[:regex]
end

.shield_lookups ⇒ Array<Hash>

Returns a list of available shield techniques with their long and short names and costs.

Returns:

  • (Array<Hash>) —

    an array of hashes containing long_name, short_name, and cost for each shield technique.



253
254
255
256
257
258
259
260
261
# File 'documented/gemstone/psms/shield.rb', line 253

def self.shield_lookups
  @@shield_techniques.map do |long_name, psm|
    {
      long_name: long_name,
      short_name: psm[:short_name],
      cost: psm[:cost]
    }
  end
end

.use(name, target = "", results_of_interest: nil, forcert_count: 0) ⇒ String?

Uses a shield technique against a target, processing the results of the action.

Parameters:

  • name (String) —

    the name of the shield technique to use.

  • target (String) (defaults to: "") —

    the target of the technique (default is an empty string).

  • results_of_interest (Regexp, nil) (defaults to: nil) —

    additional regex patterns to match results (default is nil).

  • forcert_count (Integer) (defaults to: 0) —

    the number of forcerts available (default is 0).

Returns:

  • (String, nil) —

    the result of the technique use or nil if it cannot be used.



313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'documented/gemstone/psms/shield.rb', line 313

def self.use(name, target = "", results_of_interest: nil, forcert_count: 0)
  return unless Shield.available?(name, forcert_count: forcert_count)

  name_normalized = PSMS.name_normal(name)
  technique = @@shield_techniques.fetch(PSMS.find_name(name_normalized, "Shield")[:long_name])
  usage = technique[:usage]
  return if usage.nil?

  in_cooldown_regex = /^#{name} is still in cooldown\./i

  results_regex = Regexp.union(
    PSMS::FAILURES_REGEXES,
    /^#{name} what\?$/i,
    in_cooldown_regex,
    technique[:regex],
    /^Roundtime: [0-9]+ sec\.$/,
  )

  results_regex = Regexp.union(results_regex, results_of_interest) if results_of_interest.is_a?(Regexp)

  usage_cmd = "shield #{usage}"
  if target.is_a?(GameObj)
    usage_cmd += " ##{target.id}"
  elsif target.is_a?(Integer)
    usage_cmd += " ##{target}"
  elsif target != ""
    usage_cmd += " #{target}"
  end

  if forcert_count > 0
    usage_cmd += " forcert"
  else # if we're using forcert, we don't want to wait for rt, but we need to otherwise
    waitrt?
    waitcastrt?
  end

  usage_result = dothistimeout usage_cmd, 5, results_regex
  if usage_result == "You don't seem to be able to move to do that."
    100.times { break if clear.any? { |line| line =~ /^You regain control of your senses!$/ }; sleep 0.1 }
    usage_result = dothistimeout usage_cmd, 5, results_regex
  end

  usage_result
end