Module: Lich::DragonRealms::DRCA

Defined in:
documented/dragonrealms/commons/common-arcana.rb

Constant Summary collapse

CYCLIC_RELEASE_SUCCESS_PATTERNS =

Patterns for successful cyclic spell release messages.

Examples:

Matches

/^The world seems to accelerate around you as the spirit of the cheetah escapes you/
/^You feel distinctly frail and vulnerable as the spirit of the bear leaves you/
/^The forces of nature that you roused are no longer with you/

See Also:

[
  # Ranger spells
  /^The world seems to accelerate around you as the spirit of the cheetah escapes you/, # Cheetah Swiftness
  /^You feel distinctly frail and vulnerable as the spirit of the bear leaves you/, # Bear Strength
  /^The forces of nature that you roused are no longer with you/, # Awaken Forest
  # Empath spells
  /^Aesandry Darlaeth loses cohesion, returning your reaction time to normal/, # Aesandry Darlaeth
  /^You sense your hold on your Guardian Spirit weaken, then evaporate entirely/, # Guardian Spirit
  /^The signs of empathic atrocity escape to the deepest pits of your personality, your touch no longer deadly/, # Icutu Zaharenela
  /^The tingling across your body diminishes as you feel the motes of energy fade away/, # Regenerate
  # Bard spells
  /^You sing, purposely warbling some of the held notes for effec/, # Abandoned Heart (ABAN)
  /^The final tones of your enchante end with an abrupt flourish that leaves stark silence in its wake/, # Aether Wolves (AEWO)
  /^With a rising crescendo in your voice, you reprise the strong lines of the chorus of Albreda's Balm before bringing it to an abrupt conclusion/, # Albreda's Balm (ALB)
  /^The final, quiet notes of Blessing of the Fae stir the air gently, and die away/, # Blessing of the Fae (BOTF)
  /^The warm air swirling around you stills and begins to cool/, # Caress of the Sun (CARE)
  /^A few fleeting, soporific notes tarry in the air before your lullaby slowly dies down like the night receding at Anduwen/, # Damaris' Lullaby (DALU)
  /^You no longer feel the clarity of vision you had, as shadows creep across the area/, # Eye of Kertigen (EYE)
  /^You let your voice fade even as the pace of Faenella's Grace slows, winding down to a quiet conclusion/, # Faenella's Grace (FAE)
  /^The aethereal static subsides, returning your spellcasting abilities to normal/, # Glythtide's Joy (GJ)
  /^As your rendition of Hodierna's Lilt winds down to a close, you let each note linger on the air a moment, drawing out the final moment with a reluctance to let the soothing melody fade/, # Hodierna's Lilt (HODI)
  /^You build the final notes of Phoenix's Pyre with an upward scale that rises into a steep crescendo, and end with an abrupt silence/, # Phoenix's Pyre (PYRE)
  /^The dome of light extinguishes as the final notes of music die away/, # Sanctuary
  # Warrior Mage spells
  /^The dark mantle of aether surrounding you fades away/, # Aether Cloak (AC)
  /^You release your connection to the Elemental Plane of Electricity, allowing the static electricity to dissipate/, # Electrostatic Eddy (EE)
  /^Your link to the Fire Rain matrix has been severed/, # Fire Rain (FR)
  /^The chilling vapor surrounding you dissipates slowly/, # Rimefang (spell) (RIM)
  /^The frost-covered blade circling around you shatters into a fine icy mist/, # Rimefang (spell) (RIM)
  /^The jagged stone spears surrounding you at .* range tremble slightly, then crumble into a grey dust that is quickly reclaimed by the earth/, # Ring of Spears (ROS)
  # Cleric spells
  /^The deadening murk around you subsides/, # Hydra Hex (HYH)
  /^The dark patch of grime around you subsides/, # Hydra Hex (HYH)
  /^You sense the dark presence depart/, # Soul Attrition (SA)
  # Resurrection (REZZ) does not have messaging that makes it usable here.
  /^The heightened sense of spiritual awareness leaves you/, # Revelation (REV)
  /^The swirling fog dissipates from around you/, # Ghost Shroud (GHS)
  # Paladin spells
  /^The holy golden radiance of your soul subsides, retreating into your body/, # Holy Warrior (HOW)
  /^Truffenyi's Rally ends, leaving behind a momentary sensation of something stuck in your throat/, # Truffenyi's Rally (TR)
  # Moon Mage spells
  /^The web of shadows twitches one last time and then goes inert/, # Shadow Web (SHW)
  /^You release your mental hold on the lunar energy that sustains your moongate/, # Moongate (MG)
  /^The refractive field surrounding you fades away/, # Steps of Vuan (SOV)
  /^A .* sphere suddenly flares with a cold light and vaporizes/, # Starlight Sphere (SLS)
  # Trader spells
  /^Your calligraphy of light assailing/, # Arbiter's Stylus (ARS)
  /^The .* moonsmoke blows away from your face/, # Mask of the Moons (MOM)
  # Necromancer spells
  /^The Rite of Contrition matrix loses cohesion, leaving your aura naked/, # Rite of Contrition (ROC)
  /^The Rite of Forbearance matrix loses cohesion, leaving you to wallow in temptation/, # Rite of Forbearance (ROF)
  /^The Rite of Grace matrix loses cohesion, leaving your body exposed/, # Rite of Grace (ROG)
  /^The greenish hues about you vanish as the Universal Solvent matrix loses its cohesion/, # Universal Solvent (USOL)
  /^You sense your Call from Within spell weaken and disperse/ # Call from Within (CFW)
].freeze
INFUSE_OM_SUCCESS_PATTERNS =

Patterns for successful infusion of Osrel Meraud messages.

Examples:

Matches

'having reached its full capacity'
'A sense of fullness'
'Something in the area is interfering with your attempt to harness'

See Also:

[
  'having reached its full capacity',
  'A sense of fullness',
  'Something in the area is interfering with your attempt to harness'
].freeze
INFUSE_OM_FAILURE_PATTERNS =

Patterns for failed infusion of Osrel Meraud messages.

Examples:

Matches

'as if it hungers for more'
'Your infusion fails completely'
"You don't have enough harnessed mana to infuse that much"
'You have no harnessed'

See Also:

[
  'as if it hungers for more',
  'Your infusion fails completely',
  "You don't have enough harnessed mana to infuse that much",
  'You have no harnessed'
].freeze
INFUSE_OM_MAX_RETRIES =
20
PREPARE_MAX_RETRIES =
3
CAST_MAX_RETRIES =
3
BARB_BUFF_MAX_RETRIES =
3
STOW_FOCUS_MAX_RETRIES =
3
WIELD_FOCUS_SUCCESS_PATTERNS =
[
  /^You draw out/,
  /^You grab/,
  /^You slip/,
  /^You deftly remove/
].freeze
WIELD_FOCUS_FAILURE_PATTERNS =
[
  /^What were you/,
  /^Wield what/,
  /^You need a free hand/
].freeze
SHEATHE_FOCUS_SUCCESS_PATTERNS =
[
  /^You sheathe/,
  /^Sheathing/,
  /^You secure/,
  /^You slip/,
  /^You hang/,
  /^You strap/,
  /^You easily strap/
].freeze
SHEATHE_FOCUS_FAILURE_PATTERNS =
[
  /^Sheathe your .* where/,
  /^There's no room/
].freeze
STARLIGHT_MESSAGES =
[
  'The smallest hint of starlight flickers within your aura',
  'A bare flicker of starlight plays within your aura',
  'A faint amount of starlight illuminates your aura',
  'Your aura pulses slowly with starlight',
  'A steady pulse of starlight runs through your aura',
  'Starlight dances vividly across the confines of your aura',
  'Strong pulses of starlight flare within your aura',
  'Your aura seethes with brilliant starlight',
  'Your aura is blinding',
  'The power contained in your aura'
].freeze
CHARGE_LEVELS =
[
  /^You sense nothing out of the ordinary.  Only magic could detect the useless trace of .* still in your system.$/,
  /^A small charge lingers within your body, just above the threshold of perception.$/,
  /^A small charge lingers within your body.$/,
  /^A charge dances through your body.$/,
  /^A charge dances just below the threshold of discomfort.$/,
  /^A charge circulates through your body, causing a low hum to vibrate through your bones.$/,
  /^Elemental essence floats freely within your body, leaving little untouched.$/,
  /^Elemental essence has infused every inch of your body.  While you could contain more, you'd do so at the risk of your health.$/,
  /^Extraplanar power crackles within your body, leaving you feeling mildly feverish.$/,
  /^Extraplanar power crackles within your body, leaving you feeling acutely ill.$/,
  /^Your body sings and crackles with a barely contained charge, destroying what little cenesthesia you had left.$/,
  /^You have reached the limits of your body's capacity to store a charge.  The laws of the Elemental Plane of .* scream demands upon your physiology, threatening your life.$/
].freeze
PERC_MANA_START_PATTERN =
/streams of .* mana/
PERC_MANA_END_PATTERN =
/Roundtime/
SYMBIOSIS_PATTERN =
/combine the weaves of the (?<type>\w+) symbiosis/
DISCERN_SORCERY_PATTERN =
/requires at minimum (?<min>\d+) mana streams/
DISCERN_FULL_PATTERN =
/minimum (?<min>\d+) mana streams and you think you can reinforce it with (?<more>\d+) more/i
USELESS_RUNESTONE_PATTERNS =
[
  /^You get a useless/
].freeze
GET_RUNESTONE_SUCCESS_PATTERNS =
[
  /^You get/,
  /^You pick up/
].freeze
GET_RUNESTONE_FAILURE_PATTERNS =
[
  /^What were you referring to/,
  /^I could not find/
].freeze

Class Method Summary collapse

Class Method Details

.activate_barb_buff?(name, meditation_pause_timer = 20, sit_to_meditate = false, retries: BARB_BUFF_MAX_RETRIES) ⇒ Boolean

Activates a Barbarian buff ability.

Parameters:

  • name (String)

    the name of the buff to activate

  • meditation_pause_timer (Integer) (defaults to: 20)

    the time to pause for meditation

  • sit_to_meditate (Boolean) (defaults to: false)

    whether to sit while meditating

  • retries (Integer) (defaults to: BARB_BUFF_MAX_RETRIES)

    the number of retries allowed for activation

Returns:

  • (Boolean)

    true if the buff was activated, false otherwise



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
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
# File 'documented/dragonrealms/commons/common-arcana.rb', line 292

def activate_barb_buff?(name, meditation_pause_timer = 20, sit_to_meditate = false, retries: BARB_BUFF_MAX_RETRIES)
  # Note, you must know Power meditation or Powermonger mastery
  # for your active abilities to be detected by DRSpells.
  return true if DRSpells.active_spells[name]

  if retries <= 0
    Lich::Messaging.msg("bold", "DRCA: activate_barb_buff? exhausted #{BARB_BUFF_MAX_RETRIES} retries for '#{name}' — giving up")
    return false
  end

  activated = false
  ability_data = get_data('spells').barb_abilities[name]
  if ability_data['type'].eql?('meditation') && sit_to_meditate
    DRC.retreat
    DRC.bput('sit', 'You sit', 'You are already', 'You rise', 'While swimming?')
  end
  case DRC.bput(ability_data['start_command'], ability_data['activated_message'], 'You have not been trained', 'But you are already', 'Your inner fire lacks', 'find yourself lacking the inner fire', 'You should stand', 'You must be sitting', 'You must be unengaged', 'While swimming?')
  when 'You must be unengaged'
    DRC.retreat
    activated = activate_barb_buff?(name, meditation_pause_timer, sit_to_meditate, retries: retries - 1)
  when 'You must be sitting'
    DRC.retreat
    case DRC.bput('sit', 'You sit', 'You are already', 'You rise', 'While swimming?')
    when 'While swimming?'
      Lich::Messaging.msg("bold", "DRCA: cannot sit to activate '#{name}' — water is too deep")
      activated = false
    else
      activated = activate_barb_buff?(name, meditation_pause_timer, sit_to_meditate, retries: retries - 1)
    end
  when 'You should stand'
    DRC.fix_standing
    activated = activate_barb_buff?(name, meditation_pause_timer, sit_to_meditate, retries: retries - 1)
  when /#{ability_data['activated_message']}/
    # Pause at least for the preferred amount of time
    # to let the meditation take effect else it may fail.
    if ability_data['type'].eql?('meditation') && meditation_pause_timer
      pause meditation_pause_timer
    end
    # Wait for any remaining RT before proceeding
    waitrt?
    activated = true
  else
    activated = false
  end
  DRC.fix_standing
  activated
end

.activate_khri?(settings_kneel, ability) ⇒ Boolean

Activates a specific Khri ability if it is not already active.

Parameters:

  • settings_kneel (Boolean, Array<String>)

    whether to kneel for Khri activation

  • ability (String)

    the name of the ability to activate

Returns:

  • (Boolean)

    true if the ability was activated, false otherwise



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'documented/dragonrealms/commons/common-arcana.rb', line 243

def activate_khri?(settings_kneel, ability)
  abilities = ability.split.map(&:capitalize)

  # Standardize for with/without 'Khri' on the front
  abilities = abilities.drop(1) if abilities.first.casecmp('khri') == 0

  # Handling for 'Delay'
  should_delay = abilities.first.casecmp('delay') == 0
  abilities = abilities.drop(1) if should_delay

  # Check each khri in the list against Active Spells, Drop any that are active
  needed_abilities = abilities.select { |ability_to_check| DRSpells.active_spells["Khri #{ability_to_check}"].nil? }
  return true if needed_abilities.empty?

  kneel = needed_abilities.any? { |ability_to_check| kneel_for_khri?(settings_kneel, ability_to_check) }
  DRC.retreat if kneel
  DRC.bput('kneel', 'You kneel', 'You are already', 'You rise', "While swimming?  Don't be silly") if kneel && !kneeling?

  result = DRC.bput("Khri #{should_delay ? 'Delay ' : ''}#{needed_abilities.join(' ')}", get_data('spells').khri_preps)
  waitrt?
  DRC.fix_standing

  return ['Your mind and body are willing', 'Your body is willing', 'You have not recovered'].none?(result)
end

.backfired?Boolean

Checks if the last spell cast backfired.

Returns:

  • (Boolean)

    true if the last spell backfired, false otherwise



480
481
482
# File 'documented/dragonrealms/commons/common-arcana.rb', line 480

def backfired?
  @backfired_status || false
end

.calculate_mana(min, more, discern_data, cyclic_or_ritual, settings) ⇒ void

This method returns an undefined value.

Calculates the mana requirements based on the provided parameters.

Parameters:

  • min (Integer)

    the minimum mana required

  • more (Integer)

    additional mana required

  • discern_data (Hash)

    the discernment data to update

  • cyclic_or_ritual (Boolean)

    whether the spell is cyclic or a ritual

  • settings (OpenStruct)

    the settings for mana calculation



1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1046

def calculate_mana(min, more, discern_data, cyclic_or_ritual, settings)
  total = min + more
  total = (total * settings.prep_scaling_factor).floor
  discern_data['mana'] = [(total / 5.0).ceil, min].max
  remaining = total - discern_data['mana']
  normalize_cambrinth_items(settings)
  # Ignore cambrinth if charges to use is nil or 0
  settings.cambrinth_num_charges ||= 0
  settings.cambrinth_items = [] if settings.cambrinth_num_charges == 0
  total_cambrinth_cap = settings.cambrinth_items.map { |x| x['cap'] }.inject(&:+) || 0
  charges_count_floor = remaining >= settings.cambrinth_num_charges ? settings.cambrinth_num_charges : 1
  settings.cambrinth_items.each do |item|
    item['charges'] = ((item['cap'].to_f / total_cambrinth_cap) * charges_count_floor).ceil
  end
  total_cambrinth_charges = settings.cambrinth_items.map { |x| x['charges'] }.inject(&:+) || 0
  if remaining > total_cambrinth_cap
    discern_data['mana'] = discern_data['mana'] + (remaining - total_cambrinth_cap)
    remaining = total - discern_data['mana']
  end
  if cyclic_or_ritual || total_cambrinth_charges == 0
    discern_data['cambrinth'] = nil
    discern_data['mana'] = discern_data['mana'] + remaining
  elsif remaining > 0
    total_cambrinth_mana = [remaining, total_cambrinth_cap].min
    settings.cambrinth_items.each_with_index do |item, index|
      discern_data['cambrinth'] ||= []
      charge_amount = (total_cambrinth_mana / total_cambrinth_charges) * item['charges']
      discern_data['cambrinth'][index] = []
      charge_amount.times do |i|
        discern_data['cambrinth'][index][i % item['charges']] += 1
      end
    end
  else
    discern_data['cambrinth'] = nil
  end
end

.cast?(cast_command = 'cast', symbiosis = false, before = [], after = [], retries: CAST_MAX_RETRIES) ⇒ Boolean

Casts a spell with the specified command and conditions.

Parameters:

  • cast_command (String) (defaults to: 'cast')

    the command to use for casting

  • symbiosis (Boolean) (defaults to: false)

    whether the casting is for symbiosis

  • before (Array<Hash>) (defaults to: [])

    actions to perform before casting

  • after (Array<Hash>) (defaults to: [])

    actions to perform after casting

  • retries (Integer) (defaults to: CAST_MAX_RETRIES)

    the number of retries allowed for casting

Returns:

  • (Boolean)

    true if the spell was successfully cast, false otherwise



492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
# File 'documented/dragonrealms/commons/common-arcana.rb', line 492

def cast?(cast_command = 'cast', symbiosis = false, before = [], after = [], retries: CAST_MAX_RETRIES)
  before.each { |action| DRC.bput(action['message'], action['matches']) }

  Flags.add('unknown-command', "Please rephrase that command")
  Flags.add('barrage-fail', "That was an invalid attack choice.", "Wouldn't it be better if you used a melee weapon?", "You'll need to be using a weapon to BARRAGE your target", "You must have a fully developed target matrix to make a barrage attack", "You are unable to muster the energy to do that", "You do not know how to manipulate that pathway.", "You cannot BARRAGE with that spell.")
  Flags.add('spell-fail', 'Currently lacking the skill to complete the pattern', "You don't have a spell prepared!", /^Your spell .*backfires/, 'Something is interfering with the spell', 'There is nothing else to face', 'You strain, but are too mentally fatigued', 'The spell pattern resists the influx of unfocused mana', 'Your target pattern dissipates because')
  Flags.add('cyclic-too-recent', 'The mental strain of initiating a cyclic spell so recently prevents you from formulating the spell pattern')
  Flags.add('spell-full-prep', /^This pattern may only be cast with full preparation/)
  Flags.add('spell-backfired', /^Your spell .*backfires/)

  case DRC.bput(cast_command || 'cast', get_data('spells').cast_messages)
  when /^Your target pattern dissipates/, /^You can't cast that at yourself/, /^You need to specify a body part to consume/, /^There is nothing else to face/
    DRC.bput('release spell', 'You let your concentration lapse', "You aren't preparing a spell")
    DRC.bput('release mana', 'You release all', "You aren't harnessing any mana")
  when /You gesture/
    pause 0.25
  end
  waitrt?

  # Warrior Mage failed to use (or doesn't know) barrage ability. Do regular cast instead.
  if cast_command =~ /\b(barrage)\b/i && (Flags['unknown-command'] || Flags['barrage-fail'])
    return cast?('cast', symbiosis, [], after, retries: retries - 1) if retries > 0

    Lich::Messaging.msg("bold", "DRCA: cast? barrage fallback exhausted retries — giving up")
    return false
  end

  if Flags['cyclic-too-recent'] || Flags['spell-full-prep']
    if retries <= 0
      Lich::Messaging.msg("bold", "DRCA: cast? exhausted #{CAST_MAX_RETRIES} retries waiting for cyclic/full-prep — giving up")
      return false
    end
    pause 1
    Flags.delete('spell-full-prep')
    return cast?(cast_command, symbiosis, [], after, retries: retries - 1)
  end

  after.each { |action| DRC.bput(action['message'], action['matches']) }

  if symbiosis && Flags['spell-fail']
    DRC.bput('release mana', 'You release all', "You aren't harnessing any mana")
    DRC.bput('release symbiosis', 'You release', 'But you haven\'t prepared')
  elsif Flags['spell-fail']
    DRC.bput('release mana', 'You release all', "You aren't harnessing any mana")
  end

  @backfired_status = Flags['spell-backfired']

  !Flags['spell-fail']
end

.cast_spell(data, settings, force_cambrinth = false, cast_lifecycle_lambda = nil) ⇒ Boolean

Casts a spell based on the provided data and settings.

Parameters:

  • data (Hash)

    the spell data to cast

  • settings (OpenStruct)

    the settings for casting

  • force_cambrinth (Boolean) (defaults to: false)

    whether to force cambrinth usage

  • cast_lifecycle_lambda (Proc, nil) (defaults to: nil)

    a lambda for lifecycle events during casting

Returns:

  • (Boolean)

    true if the spell was successfully cast, false otherwise



920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
# File 'documented/dragonrealms/commons/common-arcana.rb', line 920

def cast_spell(data, settings, force_cambrinth = false, cast_lifecycle_lambda = nil)
  return unless data
  return unless settings

  data = DRCMM.update_astral_data(data, settings)
  return unless data # DRCMM.update_astral_data returns nil on failure

  if (data['abbrev'] =~ /locat/i) && !DRSpells.active_spells['Clear Vision']
    cast_spell({ 'abbrev' => 'cv', 'mana' => 1, 'prep_time' => 5 }, settings)
  end

  if data['ritual']
    ritual(data, settings)
    return
  end

  if data['runestone_name']
    return unless prepare_to_cast_runestone?(data, settings)
  end

  cast_lifecycle_lambda&.call('pre-prep', data, settings)

  command = 'prep'
  command = data['prep'] if data['prep']
  command = data['prep_type'] if data['prep_type']

  if command == 'segue'
    return if segue?(data['abbrev'], data['mana'])

    command = 'prep'
  end

  release_cyclics if data['cyclic']
  DRC.bput('release spell', 'You let your concentration lapse', "You aren't preparing a spell") unless checkprep == 'None'
  DRC.bput('release mana', 'You release all', "You aren't harnessing any mana")

  return unless prepare?(data['abbrev'], data['mana'], data['symbiosis'], command, data['tattoo_tm'], data['runestone_name'], data['runestone_tm'], settings['custom_spell_prep'])

  DRCI.put_away_item?(data['runestone_name'], settings.runestone_storage) if DRCI.in_hands?(data['runestone_name'])
  prepare_time = Time.now

  normalize_cambrinth_items(settings)
  if check_to_harness(settings.use_harness_when_arcana_locked) && !force_cambrinth
    harness_mana(data['cambrinth'].flatten)
  else
    charge_cambrinth_items(data, settings)
  end

  cast_lifecycle_lambda&.call('post-prep', data, settings)

  if data['prep_time']
    pause until Time.now - prepare_time >= data['prep_time']
  else
    waitcastrt?
  end

  cast_lifecycle_lambda&.call('pre-cast', data, settings)
  spell_cast = cast?(data['cast'], data['symbiosis'], data['before'], data['after'])
  cast_lifecycle_lambda&.call('post-cast', data, settings)

  spell_cast
end

.cast_spell?(data, settings, force_cambrinth = false, cast_lifecycle_lambda = nil) ⇒ Boolean

Attempts to cast a spell and returns the result.

Parameters:

  • data (Hash)

    the spell data to cast

  • settings (OpenStruct)

    the settings for casting

  • force_cambrinth (Boolean) (defaults to: false)

    whether to force cambrinth usage

  • cast_lifecycle_lambda (Proc, nil) (defaults to: nil)

    a lambda for lifecycle events during casting

Returns:

  • (Boolean)

    true if the spell was successfully cast, false otherwise



909
910
911
# File 'documented/dragonrealms/commons/common-arcana.rb', line 909

def cast_spell?(data, settings, force_cambrinth = false, cast_lifecycle_lambda = nil)
  !!cast_spell(data, settings, force_cambrinth, cast_lifecycle_lambda)
end

.cast_spells(spells, settings, force_cambrinth = false, cast_lifecycle_lambda = nil) ⇒ void

This method returns an undefined value.

Casts a series of spells based on the provided settings.

Parameters:

  • spells (Hash)

    a hash of spells to cast

  • settings (OpenStruct)

    the settings for casting spells

  • force_cambrinth (Boolean) (defaults to: false)

    whether to force cambrinth usage

  • cast_lifecycle_lambda (Proc, nil) (defaults to: nil)

    a lambda for lifecycle events during casting



889
890
891
892
893
894
895
896
897
898
899
900
# File 'documented/dragonrealms/commons/common-arcana.rb', line 889

def cast_spells(spells, settings, force_cambrinth = false, cast_lifecycle_lambda = nil)
  infuse_om(!settings.osrel_no_harness, settings.osrel_amount)
  spells.each do |name, data|
    next if DRSpells.active_spells[name] && (data['recast'].nil? || DRSpells.active_spells[name].to_i > data['recast'])

    while DRStats.mana < settings.waggle_spells_mana_threshold || DRStats.concentration < settings.waggle_spells_concentration_threshold
      Lich::Messaging.msg("plain", "DRCA: waiting on mana over #{settings.waggle_spells_mana_threshold} or concentration over #{settings.waggle_spells_concentration_threshold}...")
      pause 15
    end
    cast_spell(data, settings, force_cambrinth, cast_lifecycle_lambda)
  end
end

.charge?(cambrinth, mana) ⇒ Boolean

Charges a cambrinth item with the specified amount of mana.

Parameters:

  • cambrinth (String)

    the name of the cambrinth item

  • mana (Integer)

    the amount of mana to charge

Returns:

  • (Boolean)

    true if the charging was successful, false otherwise



728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
# File 'documented/dragonrealms/commons/common-arcana.rb', line 728

def charge?(cambrinth, mana)
  charged = false
  result = DRC.bput("charge my #{cambrinth} #{mana}", get_data('spells').charge_messages, 'I could not find')
  pause
  waitrt?
  case result
  when /You are in no condition to do that/
    charged = harness?(mana)
  when /You'll have to hold it/
    # You're not wearing nor holding your cambrinth item, go find it again.
    # Likely it's configured in your yaml that you wear it but it's stowed for some reason.
    # Try to find the cambrinth and get it to your hands.
    Lich::Messaging.msg("bold", "DRCA: where did your cambrinth go?")
    retry_find_cambrinth = true
  when /you find it too clumsy/
    Lich::Messaging.msg("bold", "DRCA: your arcana skill is too low to charge your cambrinth while worn")
    retry_find_cambrinth = true
  else
    charged = result =~ /absorbs? all of the energy/
  end
  if retry_find_cambrinth
    # If the cambrinth is in your hands and you can't charge it, nothing else to do.
    unless DRCI.in_hands?(cambrinth)
      # Otherwise, try to find the cambrinth and get it to your hands.
      find_cambrinth(cambrinth, false, 999)
      # If you were able to get the cambrinth into a hand then retry charging it.
      # You might not have been able to if your hands were full.
      if DRCI.in_hands?(cambrinth)
        charged = charge?(cambrinth, mana)
        stow_cambrinth(cambrinth, false, 999)
      end
    end
  end
  charged
end

.charge_and_invoke(cambrinth, dedicated_camb_use, charges, invoke_exact_amount = nil) ⇒ void

This method returns an undefined value.

Charges and invokes a cambrinth item.

Parameters:

  • cambrinth (String)

    the name of the cambrinth item

  • dedicated_camb_use (Boolean)

    whether to use dedicated cambrinth

  • charges (Array<Integer>)

    the charges to use

  • invoke_exact_amount (Integer, nil) (defaults to: nil)

    the exact amount to invoke



682
683
684
685
686
687
688
689
690
691
692
# File 'documented/dragonrealms/commons/common-arcana.rb', line 682

def charge_and_invoke(cambrinth, dedicated_camb_use, charges, invoke_exact_amount = nil)
  return unless charges&.any?

  charges.each do |mana|
    break unless charge?(cambrinth, mana)
  end

  invoke_amount = invoke_exact_amount ? charges.inject(0, :+) : nil

  invoke(cambrinth, dedicated_camb_use, invoke_amount)
end

.charge_avtalia(cambrinth, charge_amount) ⇒ void

This method returns an undefined value.

Charges the Avtalia focus with the specified amount.

Parameters:

  • cambrinth (String)

    the name of the cambrinth item

  • charge_amount (Integer)

    the amount to charge



1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1223

def charge_avtalia(cambrinth, charge_amount)
  return unless cambrinth
  return unless Script.running?('avtalia')

  if !charge?(cambrinth, charge_amount)
    UserVars.avtalia[cambrinth]['mana'] = UserVars.avtalia[cambrinth]['cap']
  else
    # Experiments show very roughly 10% falloff regardless of cap every 10 minutes
    # Assume 10% every 5 minutes.  No falloff in starlight, but that's not tracked ATM.
    time_diff = Time.now - UserVars.avtalia[cambrinth]['time_seen']
    time_mod = (time_diff / 300.0).floor
    time_adjust = 1 - [time_mod * 0.10, 1.0].min
    assumed_reserve = (UserVars.avtalia[cambrinth]['mana'] * time_adjust).floor + charge_amount
    UserVars.avtalia[cambrinth]['mana'] = [assumed_reserve, UserVars.avtalia[cambrinth]['cap']].min
  end
  UserVars.avtalia[cambrinth]['time_seen'] = Time.now
end

.charge_cambrinth_items(data, settings) ⇒ void

This method returns an undefined value.

Charges the cambrinth items based on the provided data and settings.

Parameters:

  • data (Hash)

    the spell data for charging

  • settings (OpenStruct)

    the settings for charging



1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1299

def charge_cambrinth_items(data, settings)
  settings.cambrinth_items.each_with_index do |item, index|
    case data['cambrinth'].first
    when Array
      find_charge_invoke_stow(item['name'], item['stored'], item['cap'], settings.dedicated_camb_use, data['cambrinth'][index], settings.cambrinth_invoke_exact_amount)
    when Integer
      find_charge_invoke_stow(item['name'], item['stored'], item['cap'], settings.dedicated_camb_use, data['cambrinth'], settings.cambrinth_invoke_exact_amount)
    end
  end
end

.check_discern(data, settings, spell_is_sorcery = false, more_override = nil) ⇒ Hash

Checks the discernment requirements for a spell.

Parameters:

  • data (Hash)

    the spell data to check

  • settings (OpenStruct)

    the settings for discernment

  • spell_is_sorcery (Boolean) (defaults to: false)

    whether the spell is sorcery

  • more_override (Integer, nil) (defaults to: nil)

    an override for additional mana requirements

Returns:

  • (Hash)

    the updated spell data with discernment information



1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1003

def check_discern(data, settings, spell_is_sorcery = false, more_override = nil)
  UserVars.discerns = {} unless UserVars.discerns
  discern_data = UserVars.discerns[data['abbrev']] || {}
  if data['symbiosis'] || spell_is_sorcery
    if discern_data.empty? || discern_data['min'].nil? || more_override
      DRC.retreat
      discern_result = DRC.bput("discern #{data['abbrev']}", 'requires at minimum \d+ mana streams')
      match = discern_result.match(DISCERN_SORCERY_PATTERN)
      if match
        discern_data['mana'] = match[:min].to_i
        discern_data['cambrinth'] = nil
        discern_data['min'] = match[:min].to_i
        discern_data['more'] = (more_override || 0)
      end
    end
    calculate_mana(discern_data['min'], discern_data['more'], discern_data, false, settings)
  elsif discern_data.empty? || discern_data['time_stamp'].nil? || Time.now - discern_data['time_stamp'] > settings.check_discern_timer_in_hours * 60 * 60 || !discern_data['more'].nil?
    discern_data['time_stamp'] = Time.now
    DRC.retreat
    case discern = DRC.bput("discern #{data['abbrev']}", 'The spell requires at minimum \d+ mana streams and you think you can reinforce it with \d+ more', 'You don\'t think you are able to cast this spell', 'You have no idea how to cast that spell', 'You don\'t seem to be able to move to do that')
    when /you don't think you are able/i, 'You have no idea how to cast that spell', 'You don\'t seem to be able to move to do that'
      discern_data['mana'] = 1
      discern_data['cambrinth'] = nil
    else
      match = discern.match(DISCERN_FULL_PATTERN)
      calculate_mana(match[:min].to_i, match[:more].to_i, discern_data, data['cyclic'] || data['ritual'], settings) if match
    end
  end
  waitrt?
  UserVars.discerns[data['abbrev']] = discern_data
  data['mana'] = discern_data['mana']
  data['cambrinth'] = discern_data['cambrinth']
  data
end

.check_elemental_chargeInteger

Checks the elemental charge level for the character.

Returns:

  • (Integer)

    the charge level or 0 if not applicable



1257
1258
1259
1260
1261
1262
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1257

def check_elemental_charge
  return 0 unless DRStats.warrior_mage?

  result = DRC.bput("pathway sense", *CHARGE_LEVELS)
  CHARGE_LEVELS.find_index { |pattern| pattern =~ result }
end

.check_to_harness(should_harness) ⇒ Boolean

Checks if the character should harness mana based on the provided conditions.

Parameters:

  • should_harness (Boolean)

    whether to check for harnessing

Returns:

  • (Boolean)

    true if harnessing is appropriate, false otherwise



1087
1088
1089
1090
1091
1092
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1087

def check_to_harness(should_harness)
  return false unless should_harness
  return false if DRSkill.getxp('Attunement') > DRSkill.getxp('Arcana')

  true
end

.choose_avtalia(charge_needed, mana_percentage) ⇒ Array<String>?

Chooses an Avtalia focus based on the specified criteria.

Parameters:

  • charge_needed (Integer)

    the amount of charge needed

  • mana_percentage (Integer)

    the minimum mana percentage required

Returns:

  • (Array<String>, nil)

    the selected Avtalia focus or nil if none found



1246
1247
1248
1249
1250
1251
1252
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1246

def choose_avtalia(charge_needed, mana_percentage)
  UserVars.avtalia.select { |_camb, data| data['time_seen'] && data['cap'] && data['mana'] }
                  .select { |_camb, data| Time.now - data['time_seen'] < 600.0 }
                  .select { |_camb, data| (data['mana'].to_f / data['cap'].to_f) * 100 >= mana_percentage }
                  .select { |_camb, data| data['mana'] > charge_needed / 10 }
                  .max_by { |_camb, data| data['mana'] }
end

.crafting_cast_spell(data, settings) ⇒ void

This method returns an undefined value.

Casts a spell as part of a crafting routine.

Parameters:

  • data (Hash)

    the spell data to cast

  • settings (OpenStruct)

    the settings for casting



1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1099

def crafting_cast_spell(data, settings)
  return unless data
  return unless settings

  normalize_cambrinth_items(settings)
  if check_to_harness(settings.use_harness_when_arcana_locked)
    harness_mana(data['cambrinth'].flatten)
  else
    charge_cambrinth_items(data, settings)
  end

  cast?(data['cast'], data['symbiosis'], data['before'], data['after'])
end

.crafting_magic_routine(settings) ⇒ void

This method returns an undefined value.

Executes the crafting magic routine based on the provided settings.

Parameters:

  • settings (OpenStruct)

    the settings for crafting



1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1140

def crafting_magic_routine(settings)
  training_spells = settings.crafting_training_spells

  return if training_spells.empty?
  return if DRStats.mana <= settings.waggle_spells_mana_threshold

  if checkcastrt > 0
    return
  elsif !XMLData.prepared_spell.eql?('None') && checkcastrt == 0
    spell = XMLData.prepared_spell
    data = training_spells.find { |_skill, info| info['name'] == spell }.last
    crafting_cast_spell(data, settings)
  end

  return if checkcastrt > 0

  needs_training = %w[Warding Utility Augmentation]
  needs_training.append("Sorcery") if (settings.crafting_training_spells_enable_sorcery && !Script.running?('forge')) ||
                                      (settings.crafting_training_spells_enable_sorcery && settings.crafting_training_spells_enable_sorcery_forging)
  needs_training = needs_training.select { |skill| training_spells[skill] }
                                 .select { |skill| DRSkill.getxp(skill) < 31 }
                                 .sort_by { |skill| [DRSkill.getxp(skill), DRSkill.getrank(skill)] }
                                 .first

  return unless needs_training

  crafting_prepare_spell(training_spells[needs_training], settings)
end

.crafting_prepare_spell(data, settings) ⇒ void

This method returns an undefined value.

Prepares a spell as part of a crafting routine.

Parameters:

  • data (Hash)

    the spell data to prepare

  • settings (OpenStruct)

    the settings for preparing



1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1118

def crafting_prepare_spell(data, settings)
  return unless data
  return unless settings

  # Skip preparing lunar spell if no moons available
  return unless DRCMM.set_moon_data(data)

  release_cyclics if data['cyclic']
  DRC.bput('release spell', 'You let your concentration lapse', "You aren't preparing a spell") unless checkprep == 'None'
  DRC.bput('release mana', 'You release all', "You aren't harnessing any mana")

  command = 'prep'
  command = data['prep'] if data['prep']
  command = data['prep_type'] if data['prep_type']

  prepare?(data['abbrev'], data['mana'], data['symbiosis'], command, data['tattoo_tm'], data['runestone_name'], data['runestone_tm'], settings['custom_spell_prep'])
end

.do_buffs(settings, set_name) ⇒ void

This method returns an undefined value.

Activates buffs based on the provided settings.

Parameters:

  • settings (OpenStruct)

    the settings for activating buffs

  • set_name (String)

    the name of the buff set to activate



1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1174

def do_buffs(settings, set_name)
  return unless settings.waggle_sets[set_name]

  spells = settings.waggle_sets[set_name]

  if DRStats.barbarian?
    start_barb_abilities(spells, settings)
  elsif DRStats.thief?
    start_khris(spells, settings)
  else
    spells
      .select! { |_name, data| data['night'] ? UserVars.sun['night'] : true }
      .select! { |_name, data| data['day'] ? UserVars.sun['day'] : true }

    spells.values
          .select { |spell| spell['use_auto_mana'] }
          .each { |spell| check_discern(spell, settings) }

    cast_spells(spells, settings, settings.waggle_force_cambrinth)
  end
end

.find_cambrinth(cambrinth, stored_cambrinth, cambrinth_cap) ⇒ void

This method returns an undefined value.

Finds a cambrinth item based on the provided conditions.

Parameters:

  • cambrinth (String)

    the name of the cambrinth item

  • stored_cambrinth (Boolean)

    whether the cambrinth is stored

  • cambrinth_cap (Integer)

    the maximum capacity of the cambrinth



620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
# File 'documented/dragonrealms/commons/common-arcana.rb', line 620

def find_cambrinth(cambrinth, stored_cambrinth, cambrinth_cap)
  if stored_cambrinth
    # Your config says you keep your cambrinth stowed.
    # If item not in your hands, maybe you're wearing it by accident?
    DRCI.get_item_if_not_held?(cambrinth) || DRCI.remove_item?(cambrinth)
  elsif !skilled_to_charge_while_worn?(cambrinth_cap)
    # Your config says you wear your cambrinth.
    # But you're not skilled to charge it while worn.
    # If item not in your hands, maybe you're wearing it or stowed it by accident?
    DRCI.in_hands?(cambrinth) || DRCI.remove_item?(cambrinth) || DRCI.get_item?(cambrinth)
  else
    # Your config says you wear your cambrinth
    # and you're skilled to charge it while worn.
    # Let's hope you're wearing or holding it :)
    # To verify that would require more commands
    # and more time, and be more spammy.
    # For now, no validation or recovery for this scenario.
    true
  end
end

.find_charge_invoke_stow(cambrinth, stored_cambrinth, cambrinth_cap, dedicated_camb_use, charges, invoke_exact_amount = nil) ⇒ void

This method returns an undefined value.

Finds, charges, and invokes a cambrinth item, then stows it.

Parameters:

  • cambrinth (String)

    the name of the cambrinth item

  • stored_cambrinth (Boolean)

    whether the cambrinth is stored

  • cambrinth_cap (Integer)

    the maximum capacity of the cambrinth

  • dedicated_camb_use (Boolean)

    whether to use dedicated cambrinth

  • charges (Array<Integer>)

    the charges to use

  • invoke_exact_amount (Integer, nil) (defaults to: nil)

    the exact amount to invoke



552
553
554
555
556
557
558
# File 'documented/dragonrealms/commons/common-arcana.rb', line 552

def find_charge_invoke_stow(cambrinth, stored_cambrinth, cambrinth_cap, dedicated_camb_use, charges, invoke_exact_amount = nil)
  return unless charges

  find_cambrinth(cambrinth, stored_cambrinth, cambrinth_cap)
  charge_and_invoke(cambrinth, dedicated_camb_use, charges, invoke_exact_amount)
  stow_cambrinth(cambrinth, stored_cambrinth, cambrinth_cap)
end

.find_focus(focus, worn, tied, sheathed) ⇒ Boolean

Finds and retrieves a focus item based on the provided conditions.

Parameters:

  • focus (String)

    the name of the focus item

  • worn (Boolean)

    whether the focus is worn

  • tied (Boolean)

    whether the focus is tied

  • sheathed (Boolean)

    whether the focus is sheathed

Returns:

  • (Boolean)

    true if the focus was successfully found, false otherwise



567
568
569
570
571
572
573
574
575
576
577
578
579
580
# File 'documented/dragonrealms/commons/common-arcana.rb', line 567

def find_focus(focus, worn, tied, sheathed)
  return unless focus

  if worn
    DRCI.remove_item?(focus)
  elsif tied
    DRCI.untie_item?(focus, tied)
  elsif sheathed
    result = DRC.bput("wield my #{focus}", WIELD_FOCUS_SUCCESS_PATTERNS, WIELD_FOCUS_FAILURE_PATTERNS)
    WIELD_FOCUS_FAILURE_PATTERNS.none? { |pattern| pattern =~ result }
  else
    DRCI.get_item?(focus)
  end
end

.get_runestone?(runestone, settings) ⇒ Boolean

Retrieves a runestone from storage.

Parameters:

  • runestone (String)

    the name of the runestone to retrieve

  • settings (OpenStruct)

    the settings for retrieving runestones

Returns:

  • (Boolean)

    true if the runestone was successfully retrieved, false otherwise



459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
# File 'documented/dragonrealms/commons/common-arcana.rb', line 459

def get_runestone?(runestone, settings)
  return true if DRCI.in_hands?(runestone)

  result = DRC.bput(
    "get my #{runestone} from my #{settings.runestone_storage}",
    USELESS_RUNESTONE_PATTERNS, GET_RUNESTONE_SUCCESS_PATTERNS, GET_RUNESTONE_FAILURE_PATTERNS
  )
  if USELESS_RUNESTONE_PATTERNS.any? { |pat| pat.match?(result) }
    DRCI.dispose_trash(runestone)
    Lich::Messaging.msg("bold", "DRCA: got a useless #{runestone} — disposing and giving up")
    return false
  elsif GET_RUNESTONE_FAILURE_PATTERNS.any? { |pat| pat.match?(result) }
    Lich::Messaging.msg("bold", "DRCA: could not find #{runestone} in #{settings.runestone_storage}")
    return false
  end
  true
end

.harness?(mana) ⇒ Boolean

Checks if the specified amount of mana can be harnessed.

Parameters:

  • mana (Integer)

    the amount of mana to harness

Returns:

  • (Boolean)

    true if harnessing is successful, false otherwise



213
214
215
216
217
218
# File 'documented/dragonrealms/commons/common-arcana.rb', line 213

def harness?(mana)
  result = DRC.bput("harness #{mana}", 'You tap into', 'Strain though you may')
  pause 0.5
  waitrt?
  return result =~ /You tap into/
end

.harness_mana(amounts) ⇒ Object



220
221
222
223
224
# File 'documented/dragonrealms/commons/common-arcana.rb', line 220

def harness_mana(amounts)
  amounts.each do |mana|
    break unless harness?(mana)
  end
end

.infuse_om(harness, amount) ⇒ void

Note:

This method will retry infusion up to a maximum number of times defined by INFUSE_OM_MAX_RETRIES.

This method returns an undefined value.

Infuses the specified amount of mana into Osrel Meraud.

Parameters:

  • harness (Boolean)

    whether to harness mana before infusion

  • amount (Integer)

    the amount of mana to infuse



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'documented/dragonrealms/commons/common-arcana.rb', line 188

def infuse_om(harness, amount)
  return unless DRSpells.active_spells['Osrel Meraud'] && DRSpells.active_spells['Osrel Meraud'] < 90
  return unless amount

  retries = 0
  loop do
    if retries >= INFUSE_OM_MAX_RETRIES
      Lich::Messaging.msg("bold", "DRCA: infuse_om exhausted #{INFUSE_OM_MAX_RETRIES} retries — giving up")
      break
    end
    retries += 1

    pause 5 while DRStats.mana <= 40
    harness_mana([amount]) if harness
    break if INFUSE_OM_SUCCESS_PATTERNS.include?(DRC.bput("infuse om #{amount}", INFUSE_OM_SUCCESS_PATTERNS, INFUSE_OM_FAILURE_PATTERNS))

    pause 0.5
    waitrt?
  end
end

.invoke(cambrinth, dedicated_camb_use, invoke_amount) ⇒ void

This method returns an undefined value.

Invokes a cambrinth item with the specified amount.

Parameters:

  • cambrinth (String)

    the name of the cambrinth item

  • dedicated_camb_use (Boolean)

    whether to use dedicated cambrinth

  • invoke_amount (Integer, nil)

    the amount to invoke



700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
# File 'documented/dragonrealms/commons/common-arcana.rb', line 700

def invoke(cambrinth, dedicated_camb_use, invoke_amount)
  return unless cambrinth

  result = DRC.bput("invoke my #{cambrinth} #{invoke_amount} #{dedicated_camb_use}".strip, get_data('spells').invoke_messages, 'Invoke what?')
  pause
  waitrt?
  case result
  when /you find it too clumsy/
    Lich::Messaging.msg("bold", "DRCA: your arcana skill is too low to invoke your cambrinth while worn")
    # If the cambrinth is in your hands and you can't invoke it, nothing else to do.
    unless DRCI.in_hands?(cambrinth)
      # Otherwise, try to find the cambrinth and get it to your hands.
      find_cambrinth(cambrinth, false, 999)
      # If you were able to get the cambrinth into a hand then retry invoking it.
      # You might not have been able to if your hands were full.
      if DRCI.in_hands?(cambrinth)
        invoke(cambrinth, dedicated_camb_use, invoke_amount)
        stow_cambrinth(cambrinth, false, 999)
      end
    end
  end
end

.invoke_avtalia(cambrinth, dedicated_camb_use, invoke_amount) ⇒ void

This method returns an undefined value.

Invokes the Avtalia focus with the specified parameters.

Parameters:

  • cambrinth (String)

    the name of the cambrinth item

  • dedicated_camb_use (Boolean)

    whether to use dedicated cambrinth

  • invoke_amount (Integer)

    the amount to invoke



1210
1211
1212
1213
1214
1215
1216
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1210

def invoke_avtalia(cambrinth, dedicated_camb_use, invoke_amount)
  return unless cambrinth
  return unless Script.running?('avtalia')

  invoke(cambrinth, dedicated_camb_use, invoke_amount)
  UserVars.avtalia[cambrinth]['mana'] -= [DRStats.mana, invoke_amount].min
end

.kneel_for_khri?(kneel, ability) ⇒ Boolean

Returns:

  • (Boolean)


268
269
270
271
272
273
274
# File 'documented/dragonrealms/commons/common-arcana.rb', line 268

def kneel_for_khri?(kneel, ability)
  if kneel.is_a? Array
    kneel.map(&:downcase).include? ability.downcase.sub('khri ', '')
  else
    kneel
  end
end

.normalize_cambrinth_items(settings) ⇒ void

This method returns an undefined value.

Normalizes the cambrinth items in the settings.

Parameters:

  • settings (OpenStruct)

    the settings for cambrinth items



1284
1285
1286
1287
1288
1289
1290
1291
1292
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1284

def normalize_cambrinth_items(settings)
  return if settings.cambrinth_items[0]['name']

  settings.cambrinth_items = [{
    'name'   => settings.cambrinth,
    'cap'    => settings.cambrinth_cap,
    'stored' => settings.stored_cambrinth
  }]
end

.parse_mana_message(mana_msg) ⇒ Integer

Parses a mana message to determine the mana level.

Parameters:

  • mana_msg (String)

    the mana message to parse

Returns:

  • (Integer)

    the mana level based on the message



811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
# File 'documented/dragonrealms/commons/common-arcana.rb', line 811

def parse_mana_message(mana_msg)
  manalevels = if mana_msg.include? 'weak'
                 $MANA_MAP['weak']
               elsif mana_msg.include? 'developing'
                 $MANA_MAP['developing']
               elsif mana_msg.include? 'improving'
                 $MANA_MAP['improving']
               else
                 $MANA_MAP['good']
               end

  adj = mana_msg.split(' ')[-1]

  manalevels.index(adj).to_i + 1
end

.parse_regaliaArray<String>

Parses the player's worn combat regalia.

Returns:

  • (Array<String>)

    a list of regalia items that match specific criteria



781
782
783
784
785
786
787
788
789
# File 'documented/dragonrealms/commons/common-arcana.rb', line 781

def parse_regalia
  return unless DRStats.trader?

  snapshot = Lich::Util.issue_command("inv combat", /All of your worn combat|You aren't wearing anything like that/, /Use INVENTORY HELP for more options/, usexml: false, include_end: false)
                       .map(&:strip)
  regalia_items = snapshot - ["All of your worn combat equipment:", "You aren't wearing anything like that."]
  regalia_items.select { |item| item.include?('rough-cut crystal') || item.include?('faceted crystal') || item.include?('resplendent crystal') }
               .map { |item| DRC.get_noun(item) }
end

.perc_auraHash

Retrieves the current aura status for the character.

Returns:

  • (Hash)

    a hash containing aura level, capped status, and growth status



865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
# File 'documented/dragonrealms/commons/common-arcana.rb', line 865

def perc_aura
  return unless DRStats.trader?

  Flags.add('aura-level', Regexp.union(STARLIGHT_MESSAGES))
  Flags.add('aura-capped?', 'Your aura contains as much starlight as you can safely handle')
  Flags.add('aura-growing?', 'Local conditions permit optimal growth of your aura', 'Local conditions are hindering the growth of your aura')
  aura = {}
  DRC.bput('perceive aura', 'Roundtime')
  aura['level'] = Flags['aura-level'] ? STARLIGHT_MESSAGES.index(Flags['aura-level'][0]) : 0
  aura['capped'] = Flags['aura-capped?'] ? true : false
  aura['growing'] = Flags['aura-growing?'] ? true : false
  Flags.delete('aura-level')
  Flags.delete('aura-capped?')
  Flags.delete('aura-growing?')
  aura
end

.perc_manaHash?

Retrieves the current mana percentage for the character.

Returns:

  • (Hash, nil)

    a hash containing mana levels or nil if not applicable



830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
# File 'documented/dragonrealms/commons/common-arcana.rb', line 830

def perc_mana
  return nil if DRStats.barbarian? || DRStats.thief? || DRStats.trader? || DRStats.commoner?

  if DRStats.moon_mage?
    lines = Lich::Util.issue_command(
      'perc mana',
      PERC_MANA_START_PATTERN,
      PERC_MANA_END_PATTERN,
      usexml: false,
      include_end: false,
      quiet: true,
      timeout: 5
    )
    return nil if lines.nil?

    mana_msgs = lines.map(&:strip).select { |line| line.include?('streams') }[0..3]
    return nil if mana_msgs.length < 4

    mana_msgs.collect! { |mana_msg| mana_msg.split(' streams')[0] }

    {
      'enlightened_geometry'   => parse_mana_message(mana_msgs[0]),
      'moonlight_manipulation' => parse_mana_message(mana_msgs[1]),
      'perception'             => parse_mana_message(mana_msgs[2]),
      'psychic_projection'     => parse_mana_message(mana_msgs[3])
    }
  else
    mana_msg = DRC.bput('perc', '^You reach out with your .* and (see|hear) \w+')
    parse_mana_message(mana_msg)
  end
end

.perc_symbiotic_researchString?

Performs a perception check for symbiotic research.

Returns:

  • (String, nil)

    the type of symbiosis or nil if not applicable



1267
1268
1269
1270
1271
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1267

def perc_symbiotic_research
  result = DRC.bput('perceive', SYMBIOSIS_PATTERN, /Roundtime/)
  match = result.match(SYMBIOSIS_PATTERN)
  match ? match[:type] : nil
end

.prepare?(abbrev, mana, symbiosis = false, command = 'prepare', tattoo_tm = false, runestone_name = nil, runestone_tm = false, custom_prep = nil, retries: PREPARE_MAX_RETRIES) ⇒ String?

Prepares a spell for casting.

Parameters:

  • abbrev (String)

    the abbreviation of the spell to prepare

  • mana (Integer)

    the amount of mana to use for preparation

  • symbiosis (Boolean) (defaults to: false)

    whether the preparation is for symbiosis

  • command (String) (defaults to: 'prepare')

    the command to use for preparation

  • tattoo_tm (Boolean) (defaults to: false)

    whether to use a tattoo for preparation

  • runestone_name (String, nil) (defaults to: nil)

    the name of the runestone to use

  • runestone_tm (Boolean) (defaults to: false)

    whether to use a runestone for preparation

  • custom_prep (String, nil) (defaults to: nil)

    a custom preparation message

  • retries (Integer) (defaults to: PREPARE_MAX_RETRIES)

    the number of retries allowed for preparation

Returns:

  • (String, nil)

    the result of the preparation or nil if failed



352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'documented/dragonrealms/commons/common-arcana.rb', line 352

def prepare?(abbrev, mana, symbiosis = false, command = 'prepare', tattoo_tm = false, runestone_name = nil, runestone_tm = false, custom_prep = nil, retries: PREPARE_MAX_RETRIES)
  return false unless abbrev
  spell_prep_messages = !custom_prep ? get_data('spells').prep_messages : (get_data('spells').prep_messages + [custom_prep])

  DRC.bput('prepare symbiosis', 'You recall the exact details of the', 'But you\'ve already prepared', 'Please don\'t do that here') if symbiosis
  if runestone_name.nil?
    match = DRC.bput("#{command} #{abbrev} #{mana}", spell_prep_messages)
  else
    match = DRC.bput("#{command} my #{runestone_name}", get_data('spells').invoke_messages)
  end
  case match
  when 'Your desire to prepare this offensive spell suddenly slips away'
    if retries <= 0
      Lich::Messaging.msg("bold", "DRCA: prepare? exhausted #{PREPARE_MAX_RETRIES} retries for '#{abbrev}' — giving up")
      return false
    end
    pause 1
    return prepare?(abbrev, mana, symbiosis, command, tattoo_tm, runestone_name, runestone_tm, custom_prep, retries: retries - 1)
  when 'Something in the area interferes with your spell preparations', 'You shouldn\'t disrupt the area right now', 'You have no idea how to cast that spell', 'You have yet to receive any training in the magical arts', 'Please don\'t do that here', 'You cannot use the tattoo while maintaining the effort to stay hidden'
    DRC.bput('release symbiosis', 'You release the', 'But you haven\'t') if symbiosis
    return false
  when 'Well, that was fun'
    DRCI.dispose_trash(runestone_name)
    return false
  when 'You\'ll have to hold it'
    return false
  end

  DRC.bput("target", spell_prep_messages) if tattoo_tm || runestone_tm

  match
end

.prepare_to_cast_runestone?(spell, settings) ⇒ Boolean

Prepares to cast a spell using a runestone.

Parameters:

  • spell (Hash)

    the spell data including runestone information

  • settings (OpenStruct)

    the settings for casting

Returns:

  • (Boolean)

    true if preparation was successful, false otherwise



444
445
446
447
448
449
450
451
452
# File 'documented/dragonrealms/commons/common-arcana.rb', line 444

def prepare_to_cast_runestone?(spell, settings)
  if DRCI.inside?("#{spell['runestone_name']}", settings.runestone_storage)
    return false unless get_runestone?(spell['runestone_name'], settings)
  else
    Lich::Messaging.msg("bold", "DRCA: out of #{spell['runestone_name']}!")
    return false
  end
  true
end

.release_cyclics(cyclic_no_release = []) ⇒ void

This method returns an undefined value.

Releases all active cyclic spells that are currently active.

Parameters:

  • cyclic_no_release (Array<String>) (defaults to: [])

    a list of cyclic spells to not release



768
769
770
771
772
773
774
775
776
# File 'documented/dragonrealms/commons/common-arcana.rb', line 768

def release_cyclics(cyclic_no_release = [])
  get_data('spells')
    .spell_data
    .select { |_name, properties| properties['cyclic'] }
    .select { |name, _properties| DRSpells.active_spells.keys.include?(name) }
    .reject { |name| cyclic_no_release.include?(name) }
    .map { |_name, properties| properties['abbrev'] }
    .each { |abbrev| DRC.bput("release #{abbrev}", CYCLIC_RELEASE_SUCCESS_PATTERNS, 'Release what?') }
end

.release_magical_researchvoid

This method returns an undefined value.

Releases magical research symbiosis.



1276
1277
1278
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1276

def release_magical_research
  2.times { DRC.bput("release symbiosis", "Are you sure", "You intentionally wipe", "But you haven't") }
end

.ritual(data, settings) ⇒ void

This method returns an undefined value.

Performs a ritual based on the provided data and settings.

Parameters:

  • data (Hash)

    the data for the ritual

  • settings (OpenStruct)

    the settings for the ritual



410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
# File 'documented/dragonrealms/commons/common-arcana.rb', line 410

def ritual(data, settings)
  DRC.retreat(settings.ignored_npcs) unless data['skip_retreat']
  DRC.release_invisibility
  DRC.set_stance('shield') unless data['skip_retreat']

  command = 'prepare'
  command = data['prep'] if data['prep']
  command = data['prep_type'] if data['prep_type']

  return unless prepare?(data['abbrev'], data['mana'], data['symbiosis'], command, data['tattoo_tm'], data['runestone_name'], data['runestone_tm'], settings['custom_spell_prep'])

  prepare_time = Time.now
  find_focus(data['focus'], data['worn_focus'], data['tied_focus'], data['sheathed_focus'])

  invoke(data['focus'], nil, nil)
  stow_focus(data['focus'], data['worn_focus'], data['tied_focus'], data['sheathed_focus'])
  DRC.retreat(settings.ignored_npcs) unless data['skip_retreat']

  if data['prep_time']
    pause until Time.now - prepare_time >= data['prep_time']
  else
    waitcastrt?
  end

  return unless cast?(data['cast'], data['symbiosis'], data['before'], data['after'])

  DRC.retreat(settings.ignored_npcs) unless data['skip_retreat']
end

.segue?(abbrev, mana) ⇒ Boolean

Checks if a segue can be performed from the current spell.

Parameters:

  • abbrev (String)

    the abbreviation of the spell to segue from

  • mana (Integer)

    the amount of mana to use for the segue

Returns:

  • (Boolean)

    true if the segue is possible, false otherwise



988
989
990
991
992
993
994
# File 'documented/dragonrealms/commons/common-arcana.rb', line 988

def segue?(abbrev, mana)
  case DRC.bput("segue #{abbrev} #{mana}", get_data('spells').segue_messages)
  when 'You must be performing a cyclic spell to segue from', 'It is too soon to segue', 'You are lacking the bardic flair'
    return false
  end
  true
end

.shatter_regalia?(worn_regalia = nil) ⇒ Boolean

Shatters the player's worn regalia items.

Parameters:

  • worn_regalia (Array<String>, nil) (defaults to: nil)

    the worn regalia items to shatter

Returns:

  • (Boolean)

    true if regalia was shattered, false otherwise



795
796
797
798
799
800
801
802
803
804
805
# File 'documented/dragonrealms/commons/common-arcana.rb', line 795

def shatter_regalia?(worn_regalia = nil)
  return false unless DRStats.trader?

  worn_regalia ||= parse_regalia
  return false if worn_regalia.empty?

  worn_regalia.each do |item|
    DRC.bput("remove my #{item}", 'into motes of silvery', 'Remove what?', "You .*#{item}")
  end
  true
end

.skilled_to_charge_while_worn?(cambrinth_cap) ⇒ Boolean

Checks if the character is skilled enough to charge a cambrinth while worn.

Parameters:

  • cambrinth_cap (Integer)

    the maximum capacity of the cambrinth

Returns:

  • (Boolean)

    true if skilled enough, false otherwise



671
672
673
# File 'documented/dragonrealms/commons/common-arcana.rb', line 671

def skilled_to_charge_while_worn?(cambrinth_cap)
  DRSkill.getrank('Arcana').to_i >= ((cambrinth_cap.to_i * 2) + 100)
end

.spell_prepared?Boolean

Checks if a spell has been prepared and is ready to cast.

Returns:

  • (Boolean)

    true if the spell is prepared, false otherwise



395
396
397
# File 'documented/dragonrealms/commons/common-arcana.rb', line 395

def spell_prepared?
  spell_preparing? && checkcastrt <= 0
end

.spell_preparingObject



399
400
401
402
403
# File 'documented/dragonrealms/commons/common-arcana.rb', line 399

def spell_preparing
  name = XMLData.prepared_spell
  name = nil if name.empty? || name.eql?('None')
  name
end

.spell_preparing?Boolean

Checks if a spell is currently being prepared.

Returns:

  • (Boolean)

    true if a spell is being prepared, false otherwise



388
389
390
# File 'documented/dragonrealms/commons/common-arcana.rb', line 388

def spell_preparing?
  !spell_preparing.nil?
end

.start_barb_abilities(abilities, settings) ⇒ void

This method returns an undefined value.

Starts a set of Barbarian abilities.

Parameters:

  • abilities (Array<String>)

    the list of Barbarian abilities to activate

  • settings (OpenStruct)

    the settings for activating Barbarian abilities



281
282
283
# File 'documented/dragonrealms/commons/common-arcana.rb', line 281

def start_barb_abilities(abilities, settings)
  abilities.each { |name| activate_barb_buff?(name, settings.meditation_pause_timer, settings.sit_to_meditate) }
end

.start_khris(khris, settings) ⇒ void

This method returns an undefined value.

Activates a set of Khri abilities.

Parameters:

  • khris (Array<String>)

    the list of Khri abilities to activate

  • settings (OpenStruct)

    the settings for activating Khri



231
232
233
234
235
236
# File 'documented/dragonrealms/commons/common-arcana.rb', line 231

def start_khris(khris, settings)
  khris
    .each do |khri_set|
      activate_khri?(settings.kneel_khri, khri_set)
    end
end

.stow_cambrinth(cambrinth, stored_cambrinth, _cambrinth_cap) ⇒ void

This method returns an undefined value.

Stows a cambrinth item based on the provided conditions.

Parameters:

  • cambrinth (String)

    the name of the cambrinth item

  • stored_cambrinth (Boolean)

    whether the cambrinth is stored

  • _cambrinth_cap (Integer)

    the maximum capacity of the cambrinth



647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
# File 'documented/dragonrealms/commons/common-arcana.rb', line 647

def stow_cambrinth(cambrinth, stored_cambrinth, _cambrinth_cap)
  if stored_cambrinth
    # Your config says you keep your cambrinth stowed.
    # If item not in your hands and not stowed, maybe you're wearing it by accident?
    DRCI.get_item_if_not_held?(cambrinth) || DRCI.remove_item?(cambrinth)
    DRCI.stow_item?(cambrinth)
  elsif DRCI.in_hands?(cambrinth)
    # Your config says you wear your cambrinth.
    # For some reason it's currently in your hands.
    # If can't wear item for some reason then stow it.
    DRCI.wear_item?(cambrinth) || DRCI.stow_item?(cambrinth)
  else
    # Your config says you wear your cambrinth
    # and you're not currently holding it so
    # we'll assume you're wearing it.
    # No further action needed.
    true
  end
end

.stow_focus(focus, worn, tied, sheathed, retries: STOW_FOCUS_MAX_RETRIES) ⇒ void

This method returns an undefined value.

Stows a focus item based on the provided conditions.

Parameters:

  • focus (String)

    the name of the focus item

  • worn (Boolean)

    whether the focus is worn

  • tied (Boolean)

    whether the focus is tied

  • sheathed (Boolean)

    whether the focus is sheathed

  • retries (Integer) (defaults to: STOW_FOCUS_MAX_RETRIES)

    the number of retries allowed for stowing



590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
# File 'documented/dragonrealms/commons/common-arcana.rb', line 590

def stow_focus(focus, worn, tied, sheathed, retries: STOW_FOCUS_MAX_RETRIES)
  return unless focus

  if worn
    DRCI.wear_item?(focus)
  elsif tied
    result = DRCI.tie_item?(focus, tied)
    unless result
      if retries <= 0
        Lich::Messaging.msg("bold", "DRCA: stow_focus exhausted #{STOW_FOCUS_MAX_RETRIES} retries tying #{focus} — giving up")
        return false
      end
      DRC.retreat
      return stow_focus(focus, worn, tied, sheathed, retries: retries - 1)
    end
    result
  elsif sheathed
    result = DRC.bput("sheathe my #{focus}", SHEATHE_FOCUS_SUCCESS_PATTERNS, SHEATHE_FOCUS_FAILURE_PATTERNS)
    SHEATHE_FOCUS_FAILURE_PATTERNS.none? { |pattern| pattern =~ result }
  else
    DRCI.stow_item?(focus)
  end
end

.update_avtaliavoid

This method returns an undefined value.

Updates the status of the Avtalia focus.



1199
1200
1201
1202
# File 'documented/dragonrealms/commons/common-arcana.rb', line 1199

def update_avtalia
  DRC.bput("focus cambrinth", /^The .+ pulses? .+ (\d+)/, 'dim, almost magically null', '^You let your magical senses wander')
  waitrt?
end