Module: Lich::Stash

Defined in:
documented/stash.rb

Overview

Provides methods for managing items in the game, including stashing and retrieving items from containers.

See Also:

  • #find_container
  • #add_to_bag

Class Method Summary collapse

Class Method Details

.add_to_bag(bag, item) ⇒ Boolean

Adds an item to a specified bag, handling special cases like vapor messages.

Parameters:

  • bag (String)

    the name of the bag to add the item to

  • item (GameObj)

    the item to add

Returns:

  • (Boolean)

    true if the item was successfully added, false otherwise



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'documented/stash.rb', line 67

def self.add_to_bag(bag, item)
  bag = container(bag)
  try_or_fail(command: "_drag ##{item.id} ##{bag.id}") do |result|
    # Check for vapor message first (bandolier)
    if result =~ /As you drop .+ it dissolves into vapor\./
      @bandolier_weapon[item.name] = "unknown"
      return true
    end

    20.times {
      return true if @bandolier_weapon[item.name]
      return true if ![GameObj.right_hand, GameObj.left_hand].map(&:id).compact.include?(item.id) && @weapon_displayer.include?(bag.id)
      return true if (![GameObj.right_hand, GameObj.left_hand].map(&:id).compact.include?(item.id) && bag.contents.to_a.map(&:id).include?(item.id))
      return true if item.name =~ /^ethereal \w+$/ && ![GameObj.right_hand, GameObj.left_hand].map(&:id).compact.include?(item.id)
      sleep 0.1
    }
    return false
  end
end

.container(param) ⇒ GameObj

Retrieves a container and ensures it is accessible.

Parameters:

  • param (String)

    the name of the container

Returns:

  • (GameObj)

    the container object



39
40
41
42
43
44
45
46
47
# File 'documented/stash.rb', line 39

def self.container(param)
  container_to_check = find_container(param)
  unless @weapon_displayer.include?(container_to_check.id)
    result = Lich::Util.issue_command("look in ##{container_to_check.id}", /In the .*$|That is closed\.|^You glance at/, silent: true, quiet: true) if container_to_check.contents.nil?
    fput "open ##{container_to_check.id}" if result.include?('That is closed.')
    @weapon_displayer.push(container_to_check.id) if GameObj.containers.find { |item| item[0] == container_to_check.id }.nil?
  end
  return container_to_check
end

.equip_hands(left: false, right: false, both: false) ⇒ void

This method returns an undefined value.

Equips items from the stash back into the hands based on user settings.

Parameters:

  • left (Boolean) (defaults to: false)

    whether to equip the left hand

  • right (Boolean) (defaults to: false)

    whether to equip the right hand

  • both (Boolean) (defaults to: false)

    whether to equip both hands



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
# File 'documented/stash.rb', line 309

def self.equip_hands(left: false, right: false, both: false)
  if both
    for action in $fill_hands_actions.pop
      action.call
    end
  elsif left
    for action in $fill_left_hand_actions.pop
      action.call
    end
  elsif right
    for action in $fill_right_hand_actions.pop
      action.call
    end
  else
    if $fill_right_hand_actions.length > 0
      for action in $fill_right_hand_actions.pop
        action.call
      end
    elsif $fill_left_hand_actions.length > 0
      for action in $fill_left_hand_actions.pop
        action.call
      end
    end
  end
end

.find_bandolier_bag(item) ⇒ String?

Finds the bag associated with a specific item in the bandolier.

Parameters:

  • item (GameObj)

    the item to find the bag for

Returns:

  • (String, nil)

    the ID of the found bag or nil if not found



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'documented/stash.rb', line 107

def self.find_bandolier_bag(item)
  # Return cached value if valid and item exists in inventory
  cached_id = @bandolier_weapon[item.name]
  return cached_id if cached_id && cached_id != "unknown" &&
                      GameObj.inv.any? { |inv_item| inv_item.id == cached_id }

  # Regex patterns for parsing
  look_in_regex = Regexp.union(
    /^I could not find what you were referring to./,
    /^Surrounded by some swirling mist is /,
    /^In the /,
    /contains (?:DOSE|TINCTURE)s of the following /,
    /There is nothing in there\./,
    /<exposeContainer/,
    /<dialogData/,
    /<container/,
    /you glance/,
    /That is closed\./
  )

  item_regex = %r{<a exist="(?<id>[^"]+)" noun="(?<noun>[^"]+)">(?<name>[^<]+)</a>}

  # Collect all containers from inventory
  waitrt?
  results = Lich::Util.issue_command("inventory containers", /^You are holding /, timeout: 3, silent: true, quiet: true)
  containers = results.flat_map { |line|
    line.scan(item_regex).map { |id, _noun, _name| GameObj[id] }
  }.compact

  # Find container with the item using mist indicator
  item_noun_regex = /\b#{Regexp.escape(item.noun)}\b/
  found_container = containers.find do |container|
    waitrt?
    results = Lich::Util.issue_command(
      "look in ##{container.id}",
      look_in_regex,
      timeout: 2,
      silent: true,
      quiet: true
    )

    results.any? { |line|
      line.include?("Surrounded by some swirling mist is") && line.match?(item_noun_regex)
    }
  end

  @bandolier_weapon[item.name] = found_container&.id || "unknown"
end

.find_container(param, loud_fail: true) ⇒ GameObj

Finds a container by its name.

Parameters:

  • param (String, GameObj)

    the name of the container or a GameObj instance

  • loud_fail (Boolean) (defaults to: true)

    whether to raise an error if the container is not found (default: true)

Returns:

  • (GameObj)

    the found container

Raises:

  • (RuntimeError)

    if the container is not found and loud_fail is true



23
24
25
26
27
28
29
30
31
32
33
# File 'documented/stash.rb', line 23

def self.find_container(param, loud_fail: true)
  param = param.name if param.is_a?(GameObj) # (Lich::Gemstone::GameObj)
  found_container = GameObj.inv.find do |container|
    container.name =~ %r[#{param.strip}]i || container.name =~ %r[#{param.sub(' ', ' .*')}]i
  end
  if found_container.nil? && loud_fail
    fail "could not find Container[name: #{param}]"
  else
    return found_container
  end
end

.stash_hands(right: false, left: false, both: false) ⇒ void

This method returns an undefined value.

Stashes items from the hands into appropriate containers based on user settings.

Parameters:

  • right (Boolean) (defaults to: false)

    whether to stash the right hand

  • left (Boolean) (defaults to: false)

    whether to stash the left hand

  • both (Boolean) (defaults to: false)

    whether to stash both hands



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'documented/stash.rb', line 162

def self.stash_hands(right: false, left: false, both: false)
  $fill_hands_actions ||= Array.new
  $fill_left_hand_actions ||= Array.new
  $fill_right_hand_actions ||= Array.new

  actions = Array.new
  right_hand = GameObj.right_hand
  left_hand = GameObj.left_hand

  # extending to use sheath / 2sheath wherever possible
  unless ReadyList.valid?
    ReadyList.check(silent: true, quiet: true)
  end
  # extending to use default stow container wherever possible
  unless StowList.valid?
    StowList.check(silent: true, quiet: true)
  end
  if ReadyList.sheath
    unless ReadyList.secondary_sheath
      sheath = second_sheath = ReadyList.sheath
    else
      sheath = ReadyList.sheath if ReadyList.sheath
      second_sheath = ReadyList.secondary_sheath if ReadyList.secondary_sheath
    end
  elsif ReadyList.secondary_sheath
    sheath = second_sheath = ReadyList.secondary_sheath
  else
    sheath = second_sheath = nil
  end
  # weaponsack for both hands
  if UserVars.weapon.is_a?(String) && UserVars.weaponsack.is_a?(String) && !UserVars.weapon.empty? && !UserVars.weaponsack.empty? && (right_hand.name =~ /#{Regexp.escape(UserVars.weapon.strip)}/i || right_hand.name =~ /#{Regexp.escape(UserVars.weapon).sub(' ', ' .*')}/i)
    weaponsack = nil unless (weaponsack = find_container(UserVars.weaponsack, loud_fail: false)).is_a?(GameObj) # (Lich::Gemstone::GameObj)
  end
  # lootsack for both hands
  if !UserVars.lootsack.is_a?(String) || UserVars.lootsack.empty?
    lootsack = nil
  else
    lootsack = nil unless (lootsack = find_container(UserVars.lootsack, loud_fail: false)).is_a?(GameObj) # (Lich::Gemstone::GameObj)
  end
  # finding another container if needed
  other_containers_var = nil
  other_containers = proc {
    results = Lich::Util.issue_command('inventory containers', /^(?:You are (?:carrying nothing|holding no containers) at this time|You are wearing)/, silent: true, quiet: true)
    other_containers_ids = results.to_s.scan(/exist=\\"(.*?)\\"/).flatten - [lootsack.id]
    other_containers_var = GameObj.inv.find_all { |obj| other_containers_ids.include?(obj.id) }
    other_containers_var
  }

  if (left || both) && left_hand.id
    waitrt?
    if (left_hand.noun =~ /shield|buckler|targe|heater|parma|aegis|scutum|greatshield|mantlet|pavis|arbalest|bow|crossbow|yumi|arbalest/) && @worn_items[left_hand.name] != false && Lich::Stash::wear_to_inv(left_hand)
      actions.unshift proc {
        fput "remove ##{left_hand.id}"
        20.times { break if GameObj.left_hand.id == left_hand.id || GameObj.right_hand.id == left_hand.id; sleep 0.1 }

        if GameObj.right_hand.id == left_hand.id
          dothistimeout 'swap', 3, /^You don't have anything to swap!|^You swap/
        end
      }
    else
      actions.unshift proc {
        if left_hand.name =~ /^ethereal \w+$/
          fput "rub #{left_hand.noun} tattoo"
          20.times { break if (GameObj.left_hand.name == left_hand.name) || (GameObj.right_hand.name == left_hand.name); sleep 0.1 }
        elsif @bandolier_weapon[left_hand.name]
          fput "rub ##{find_bandolier_bag(left_hand)}"
          20.times { break if (GameObj.left_hand.name == left_hand.name) || (GameObj.right_hand.name == left_hand.name); sleep 0.1 }
        else
          fput "get ##{left_hand.id}"
          20.times { break if (GameObj.left_hand.id == left_hand.id) || (GameObj.right_hand.id == left_hand.id); sleep 0.1 }
        end

        if GameObj.right_hand.id == left_hand.id || (GameObj.right_hand.name == left_hand.name && left_hand.name =~ /^ethereal \w+$/)
          dothistimeout 'swap', 3, /^You don't have anything to swap!|^You swap/
        end
      }
      if (ready_item = ReadyList.ready_list.find { |_k, v| v.id.eql?(GameObj.left_hand.id) }) && ReadyList.store_list[ready_item[0]]
        result = Lich::Stash.add_to_bag(sheath, GameObj.left_hand) if ReadyList.store_list[ready_item[0]].eql?("put in sheath")
        result = Lich::Stash.add_to_bag(second_sheath, GameObj.left_hand) if ReadyList.store_list[ready_item[0]].eql?("put in secondary sheath")
        result = Lich::Stash.add_to_bag(StowList.default, GameObj.left_hand) if ["worn if possible, stowed otherwise", "stowed"].include?(ReadyList.store_list[ready_item[0]])
      elsif !second_sheath.nil? && GameObj.left_hand.type =~ /weapon/
        result = Lich::Stash.add_to_bag(second_sheath, GameObj.left_hand)
      elsif weaponsack && GameObj.left_hand.type =~ /weapon/
        result = Lich::Stash::add_to_bag(weaponsack, GameObj.left_hand)
      elsif lootsack
        result = Lich::Stash::add_to_bag(lootsack, GameObj.left_hand)
      else
        result = nil
      end
      if result.nil? || !result
        for container in other_containers.call
          result = Lich::Stash::add_to_bag(container, GameObj.left_hand)
          break if result
        end
      end
    end
  end
  if (right || both) && right_hand.id
    waitrt?
    actions.unshift proc {
      if right_hand.name =~ /^ethereal \w+$/
        fput "rub #{right_hand.noun} tattoo"
        20.times { break if GameObj.left_hand.name == right_hand.name || GameObj.right_hand.name == right_hand.name; sleep 0.1 }
      elsif @bandolier_weapon[right_hand.name]
        fput "rub ##{find_bandolier_bag(right_hand)}"
        20.times { break if GameObj.left_hand.name == right_hand.name || GameObj.right_hand.name == right_hand.name; sleep 0.1 }
      else
        fput "get ##{right_hand.id}"
        20.times { break if GameObj.left_hand.id == right_hand.id || GameObj.right_hand.id == right_hand.id; sleep 0.1 }
      end

      if GameObj.left_hand.id == right_hand.id || (GameObj.left_hand.name == right_hand.name && right_hand.name =~ /^ethereal \w+$/)
        dothistimeout 'swap', 3, /^You don't have anything to swap!|^You swap/
      end
    }
    if (ready_item = ReadyList.ready_list.find { |_k, v| v.id.eql?(GameObj.right_hand.id) }) && ReadyList.store_list[ready_item[0]]
      result = Lich::Stash.add_to_bag(sheath, GameObj.right_hand) if ReadyList.store_list[ready_item[0]].eql?("put in sheath")
      result = Lich::Stash.add_to_bag(second_sheath, GameObj.right_hand) if ReadyList.store_list[ready_item[0]].eql?("put in secondary sheath")
      result = Lich::Stash.add_to_bag(StowList.default, GameObj.right_hand) if ["worn if possible, stowed otherwise", "stowed"].include?(ReadyList.store_list[ready_item[0]])
    elsif !sheath.nil? && GameObj.right_hand.type =~ /weapon/
      result = Lich::Stash.add_to_bag(sheath, GameObj.right_hand)
    elsif weaponsack && GameObj.right_hand.type =~ /weapon/
      result = Lich::Stash::add_to_bag(weaponsack, GameObj.right_hand)
    elsif lootsack
      result = Lich::Stash::add_to_bag(lootsack, GameObj.right_hand)
    else
      result = nil
    end
    sleep 0.1
    if result.nil? || !result
      for container in other_containers.call
        result = Lich::Stash::add_to_bag(container, GameObj.right_hand)
        break if result
      end
    end
  end
  $fill_hands_actions.push(actions) if both
  $fill_left_hand_actions.push(actions) if left
  $fill_right_hand_actions.push(actions) if right
end

.try_or_fail(seconds: 2, command: nil) ⇒ void

This method returns an undefined value.

Attempts to execute a command and waits for a condition to be met.

Parameters:

  • seconds (Integer) (defaults to: 2)

    the maximum time to wait for the command to succeed

  • command (String) (defaults to: nil)

    the command to execute

Raises:

  • (RuntimeError)

    if the command does not succeed within the specified time



55
56
57
58
59
60
# File 'documented/stash.rb', line 55

def self.try_or_fail(seconds: 2, command: nil)
  result = fput(command)
  expiry = Time.now + seconds
  wait_until do yield(result) || Time.now > expiry end
  fail "Error[command: #{command}, seconds: #{seconds}]" if Time.now > expiry
end

.wear_to_inv(item) ⇒ Boolean

Attempts to wear an item and checks if it is successfully worn.

Parameters:

  • item (GameObj)

    the item to wear

Returns:

  • (Boolean)

    true if the item was successfully worn, false otherwise



91
92
93
94
95
96
97
98
99
100
101
# File 'documented/stash.rb', line 91

def self.wear_to_inv(item)
  try_or_fail(command: "wear ##{item.id}") do |result|
    20.times {
      return true if (![GameObj.right_hand, GameObj.left_hand].map(&:id).compact.include?(item.id) && GameObj.inv.to_a.map(&:id).include?(item.id))
      return true if item.name =~ /^ethereal \w+$/ && ![GameObj.right_hand, GameObj.left_hand].map(&:id).compact.include?(item.id)
      sleep 0.1
    } unless result =~ /You can only wear two items in that location\./

    return @worn_items[item.name] = false
  end
end