Module: Lich::DragonRealms::DRCT

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

Class Method Summary collapse

Class Method Details

.ask_for_item?(room, name, item) ⇒ Boolean

Visit a merchant and ask for an item. Usually this is for bundling ropes or gem pouches. Visit a merchant and ask for an item. Usually this is for bundling ropes or gem pouches.

Examples:

Asking for an item

result = ask_for_item?("market", "merchant", "gem pouch")

Parameters:

  • room (String)

    The room where the merchant is located.

  • name (String)

    The name of the merchant.

  • item (String)

    The item to ask for.

Returns:

  • (Boolean)

    Returns true if the item is received, false otherwise.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'documented/dragonrealms/commons/common-travel.rb', line 102

def ask_for_item?(room, name, item)
  walk_to(room)

  success_patterns = [
    /hands you/
  ]

  failure_patterns = [
    /does not seem to know anything about that/,
    /All I know about/,
    /To whom are you speaking/,
    /Usage: ASK/
  ]

  case DRC.bput("ask #{name} for #{item}", *success_patterns, *failure_patterns)
  when /hands you/
    true
  else
    false
  end
end

.buy_item(room, item) ⇒ void

This method returns an undefined value.

Visit a merchant and buy an item by offering their asking price. Usually for restocking ammunition or other misc items. Visit a merchant and buy an item by offering their asking price. Usually for restocking ammunition or other misc items.

Examples:

Buying an item

buy_item("market", "arrows")

Parameters:

  • room (String)

    The room where the merchant is located.

  • item (String)

    The item to buy.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'documented/dragonrealms/commons/common-travel.rb', line 54

def buy_item(room, item)
  walk_to(room)

  # Amount to pay should be first capturing group.
  patterns = [/prepared to offer it to you for (.*) (?:kronar|lirum|dokora)s?/,
              /Let me but ask the humble sum of (.*) coins/,
              /it would be just (\d*) (?:kronar|lirum|dokora)s?/,
              /for a (?:mere )?(\d*) (?:kronar|lirum|dokora)s?/,
              /I can let that go for...(.*) (?:kronar|lirum|dokora)s?/,
              /cost you (?:just )?(.*) (?:kronar|lirum|dokora)s?/,
              /it may be yours for just (.*) (?:kronar|lirum|dokora)s?/,
              /I'll give that to you for (.*) (?:kronar|lirum|dokora)s?/,
              /I'll let you have it for (.*) (?:kronar|lirum|dokora)s?/i,
              /I ask that you give (.*) copper (?:kronar|lirum|dokora)s?/,
              /it'll be (.*) (?:kronar|lirum|dokora)s?/,
              /the price of (.*) coins? is all I ask/,
              /tis only (.*) (?:kronar|lirum|dokora)s?/,
              /That will be (.*) copper (?:kronar|lirum|dokora)s? please/,
              /That'll be (.*) copper (?:kronar|lirum|dokora)s?/,
              /to you for (.*) (?:kronar|lirum|dokora)s?/,
              /I ask (.*) copper (?:kronar|lirum|dokora)s or if you would prefer/,
              /Cost you just (.*) (?:kronar|lirum|dokora)s?, okie dokie\?/i,
              /It will cost just (.*) (?:kronar|lirum|dokora)s?/i,
              /I would suggest (.*) (?:kronar|lirum|dokora)s?/i,
              /to you for (.*) (?:kronar|lirum|dokora)s?/i,
              /asking (.*) (?:kronar|lirum|dokora)s?/i,
              'You decide to purchase',
              'Buy what']

  match = DRC.bput("buy #{item}", *patterns)
  if match
    patterns.each { |p| break if p.match(match) }
  end
  amount = Regexp.last_match(1)

  fput("offer #{amount}") if amount
end

.dispose(item, trash_room = nil, worn_trashcan = nil, worn_trashcan_verb = nil) ⇒ void

This method returns an undefined value.

Dispose of an item in a trash can or designated area.

Examples:

Disposing of an item

dispose("old shoes", "trash room")

Parameters:

  • item (String)

    The item to dispose of.

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

    The room where the trash can is located.

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

    The trash can being worn.

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

    The verb to use for disposing.



147
148
149
150
151
152
# File 'documented/dragonrealms/commons/common-travel.rb', line 147

def dispose(item, trash_room = nil, worn_trashcan = nil, worn_trashcan_verb = nil)
  return unless item

  DRCT.walk_to(trash_room) unless trash_room.nil?
  DRCI.dispose_trash(item, worn_trashcan, worn_trashcan_verb)
end

.find_empty_room(search_rooms, idle_room, predicate = nil, min_mana = 0, strict_mana = false, max_search_attempts = Float::INFINITY, priotize_buddies = false) ⇒ Boolean

Find an empty room based on specified criteria.

Examples:

Finding an empty room

found = find_empty_room([1, 2, 3], "idle room")

Parameters:

  • search_rooms (Array)

    An array of room IDs to search.

  • idle_room (String)

    A room to go to if no empty room is found.

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

    A condition to check for suitability.

  • min_mana (Integer) (defaults to: 0)

    Minimum mana required to consider a room.

  • strict_mana (Boolean) (defaults to: false)

    Whether to enforce the minimum mana strictly.

  • max_search_attempts (Integer) (defaults to: Float::INFINITY)

    Maximum attempts to find an empty room.

  • priotize_buddies (Boolean) (defaults to: false)

    Whether to prioritize rooms with friends.

Returns:

  • (Boolean)

    Returns true if an empty room is found, false otherwise.



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
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'documented/dragonrealms/commons/common-travel.rb', line 353

def find_empty_room(search_rooms, idle_room, predicate = nil, min_mana = 0, strict_mana = false, max_search_attempts = Float::INFINITY, priotize_buddies = false)
  search_attempt = 0
  check_mana = min_mana > 0
  rooms_searched = 0
  loop do
    search_attempt += 1
    echo("*** Search attempt #{search_attempt} of #{max_search_attempts} to find a suitable room. ***")
    found_empty = false
    search_rooms.each do |room_id|
      walk_to(room_id)
      pause 0.1 until room_id == Room.current.id

      rooms_searched += 1

      if priotize_buddies && (rooms_searched <= search_rooms.size)
        suitable_room = ((DRRoom.pcs & UserVars.friends).any? && (DRRoom.pcs & UserVars.hunting_nemesis).none?)
        if (rooms_searched == search_rooms.size && (DRRoom.pcs & UserVars.friends).empty? && (DRRoom.pcs & UserVars.hunting_nemesis).empty?)
          echo("*** Reached last room in list, and found no buddies. Retrying for empty room. ***")
          return find_empty_room(search_rooms, idle_room, predicate, min_mana, strict_mana, max_search_attempts, priotize_buddies = false)
        end
      else
        suitable_room = predicate ? predicate.call(search_attempt) : (DRRoom.pcs - DRRoom.group_members).empty?
      end
      if suitable_room && check_mana && !(DRStats.moon_mage? || DRStats.trader?)
        found_empty = true
        suitable_room = (DRCA.perc_mana >= min_mana)
      end
      return true if suitable_room
    end

    if found_empty && check_mana && !strict_mana
      check_mana = false
      echo '*** Empty rooms found, but not with the right mana. Going to use those anyway! ***'
      next
    end

    (check_mana = min_mana > 0) && !check_mana

    if idle_room && search_attempt < max_search_attempts
      idle_room = idle_room.sample if idle_room.is_a?(Array)
      walk_to(idle_room)
      wait_time = rand(20..40)
      echo "*** Failed to find an empty room, pausing #{wait_time} seconds ***"
      pause wait_time
    else
      echo '*** Failed to find an empty room, stopping the search ***'
      return false
    end
  end
end

.find_sorted_empty_room(search_rooms, idle_room, predicate = nil) ⇒ Boolean

Find an empty room from a sorted list of rooms.

Examples:

Finding a sorted empty room

found = find_sorted_empty_room([1, 2, 3], "idle room")

Parameters:

  • search_rooms (Array)

    An array of room IDs to search.

  • idle_room (String)

    A room to go to if no empty room is found.

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

    A condition to check for suitability.

Returns:

  • (Boolean)

    Returns true if an empty room is found, false otherwise.



423
424
425
426
# File 'documented/dragonrealms/commons/common-travel.rb', line 423

def find_sorted_empty_room(search_rooms, idle_room, predicate = nil)
  sorted_rooms = sort_destinations(search_rooms)
  find_empty_room(sorted_rooms, idle_room, predicate)
end

.get_hometown_target_id(hometown, target) ⇒ Integer?

Get the target ID for a specific target in a hometown.

Examples:

Getting a target ID

target_id = get_hometown_target_id("town", "blacksmith")

Parameters:

  • hometown (String)

    The name of the hometown.

  • target (String)

    The target to find.

Returns:

  • (Integer, nil)

    The ID of the target, or nil if not found.



476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
# File 'documented/dragonrealms/commons/common-travel.rb', line 476

def get_hometown_target_id(hometown, target)
  hometown_data = get_data('town')[hometown]
  target_id = hometown_data[target] && hometown_data[target]['id']
  # try twice with pause if failed the first time, because sometimes data doesn't get loaded correctly
  if !target_id
    echo("*** get_hometown_target_id failed first attempt for #{target} in #{hometown}. Trying again:") if $common_travel_debug
    pause 2
    hometown_data = get_data('town')[hometown]
    target_id = hometown_data[target] && hometown_data[target]['id']
    if !target_id
      echo("*** get_hometown_target_id failed second attempt for #{target} in #{hometown}.  Likely target doesn't exist") if $common_travel_debug
      target_id = nil
    else
      echo("*** get_hometown_target_id succeeded second attempt for #{target} in #{hometown}.") if $common_travel_debug
    end
  end
  echo("*** target_id = #{target_id}") if $common_travel_debug
  target_id
end

.order_item(room, item_number) ⇒ void

This method returns an undefined value.

Visit a merchant and order from a menu. Visit a merchant and order from a menu.

Examples:

Ordering an item

order_item("tavern", 1)

Parameters:

  • room (String)

    The room where the merchant is located.

  • item_number (Integer)

    The number of the item to order.



131
132
133
134
135
136
137
# File 'documented/dragonrealms/commons/common-travel.rb', line 131

def order_item(room, item_number)
  walk_to(room)

  return if DRC.bput("order #{item_number}", 'Just order it again', 'you don\'t have enough coins') == 'you don\'t have enough coins'

  DRC.bput("order #{item_number}", 'takes some coins from you')
end

.refill_lockpick_container(lockpick_type, hometown, container, count) ⇒ void

This method returns an undefined value.

Refill a lockpick container with a specified type of lockpick.

Examples:

Refilling a lockpick container

refill_lockpick_container("basic", "town", "lockpick case", 5)

Parameters:

  • lockpick_type (String)

    The type of lockpick to refill.

  • hometown (String)

    The hometown where the locksmith is located.

  • container (String)

    The container to refill.

  • count (Integer)

    The number of lockpicks to buy.



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
# File 'documented/dragonrealms/commons/common-travel.rb', line 162

def refill_lockpick_container(lockpick_type, hometown, container, count)
  return if count < 1

  room = get_data('town')[hometown]['locksmithing']['id']

  if room.nil?
    echo '***No locksmith location found for current Hometown! Skipping refilling!***'
    return
  end

  walk_to(room)
  if Room.current.id != room
    echo '***Could not reach locksmith location! Skipping refilling!***'
    return
  end

  count.times do
    buy_item(room, "#{lockpick_type} lockpick")
    case DRC.bput("put my lockpick on my #{container}", 'You put', 'different kinds of lockpicks on the same lockpick')
    when 'different kinds of lockpicks on the same lockpick'
      DRC.message('There is something wrong with your lockpick settings. Mixing types in a container is not allowed.')
      break
    end
  end

  # Be polite to Thieves, who need the room to be empty
  DRC.fix_standing
  move('out') if XMLData.room_exits.include?('out')
end

.retreat(ignored_npcs = []) ⇒ void

This method returns an undefined value.

Retreat from a room, avoiding specified NPCs.

Examples:

Retreating from a room

retreat(["goblin"])

Parameters:

  • ignored_npcs (Array) (defaults to: [])

    An array of NPCs to ignore while retreating.



336
337
338
339
340
# File 'documented/dragonrealms/commons/common-travel.rb', line 336

def retreat(ignored_npcs = [])
  return if (DRRoom.npcs - ignored_npcs).empty?

  DRC.retreat(ignored_npcs)
end

.reverse_path(path) ⇒ Array

Reverse a path of directions.

Examples:

Reversing a path

reversed = reverse_path(["north", "east"])

Parameters:

  • path (Array)

    An array of directions to reverse.

Returns:

  • (Array)

    An array of reversed directions.

Raises:

  • (StandardError)

    Raises an error if a direction cannot be reversed.



445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
# File 'documented/dragonrealms/commons/common-travel.rb', line 445

def reverse_path(path)
  dir_to_prev_dir = {
    'northeast' => 'southwest',
    'southwest' => 'northeast',
    'northwest' => 'southeast',
    'southeast' => 'northwest',
    'north'     => 'south',
    'south'     => 'north',
    'east'      => 'west',
    'west'      => 'east',
    'up'        => 'down',
    'down'      => 'up'
  }
  reverse_path = []
  path = path.reverse
  for i in 0..path.length - 1
    if dir_to_prev_dir[path[i]].nil?
      DRC.message("Error: No reverse direction found for #{path[i]}.  Please use the full direction, northeast instead of ne, check your spelling, and make sure the path parameter is an array.")
      exit
    end
    reverse_path.push(dir_to_prev_dir[path[i]])
  end
  return reverse_path
end

.sell_item(room, item) ⇒ Boolean

Visit a merchant and sell an item. Usually to sell junk at pawn shops. Visit a merchant and sell an item. Usually to sell junk at pawn shops.

Examples:

Selling an item

result = sell_item("pawn shop", "old sword")

Parameters:

  • room (String)

    The room where the merchant is located.

  • item (String)

    The item to sell.

Returns:

  • (Boolean)

    Returns true if the item was sold, false otherwise.

Raises:

  • (StandardError)

    Raises an error if the item is not in hands.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'documented/dragonrealms/commons/common-travel.rb', line 21

def sell_item(room, item)
  return false unless DRCI.in_hands?(item)

  walk_to(room)

  success_patterns = [
    /hands? you \d+ (kronars|lirums|dokoras)/i
  ]

  failure_patterns = [
    /I need to examine the merchandise first/,
    /That's not worth anything/,
    /I only deal in pelts/,
    /There's folk around here that'd slit a throat for this/
  ]

  case DRC.bput("sell my #{item}", *success_patterns, *failure_patterns)
  when *success_patterns
    true
  when *failure_patterns
    false
  end
end

.sort_destinations(target_list) ⇒ Array

Sort a list of destinations based on distance from the current room.

Examples:

Sorting destinations

sorted_rooms = sort_destinations([3, 1, 2])

Parameters:

  • target_list (Array)

    An array of room IDs to sort.

Returns:

  • (Array)

    A sorted array of room IDs.



409
410
411
412
413
414
# File 'documented/dragonrealms/commons/common-travel.rb', line 409

def sort_destinations(target_list)
  target_list = target_list.collect(&:to_i)
  _previous, shortest_distances = Map.dijkstra(Room.current.id)
  target_list.delete_if { |room_num| shortest_distances[room_num].nil? && room_num != Room.current.id }
  target_list.sort { |a, b| shortest_distances[a] <=> shortest_distances[b] }
end

.tag_to_id(target) ⇒ Integer

Convert a tag to a room ID.

Examples:

Getting a room ID from a tag

room_id = tag_to_id("tavern")

Parameters:

  • target (String)

    The tag to convert.

Returns:

  • (Integer)

    The ID of the room associated with the tag.

Raises:

  • (StandardError)

    Raises an error if no matching room is found.



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
# File 'documented/dragonrealms/commons/common-travel.rb', line 303

def tag_to_id(target)
  start_room = Room.current.id
  target_list = Map.list.find_all { |room| room.tags.include?(target) }.collect { |room| room.id }

  if target_list.empty?
    DRC.message("No go2 targets matching #{target} found!")
    exit
  end

  if target_list.include?(start_room)
    echo "You're already here..."
    return start_room
  end
  _previous, shortest_distances = Room.current.dijkstra(target_list)
  target_list.delete_if { |room_id| shortest_distances[room_id].nil? }
  if target_list.empty?
    DRC.message("Couldn't find a path from here to any room with a #{target} tag")
    exit
  end

  target_id = target_list.sort { |a, b| shortest_distances[a] <=> shortest_distances[b] }.first
  unless target_id and (destination = Map[target_id])
    DRC.message("Something went wrong!  Debug failed with #{target_id}, #{destination}, and #{target}")
    exit
  end
  target_id
end

.time_to_room(origin, destination) ⇒ Integer

Calculate the time to travel from one room to another.

Examples:

Calculating time to a room

time = time_to_room(1, 3)

Parameters:

  • origin (Integer)

    The ID of the starting room.

  • destination (Integer)

    The ID of the destination room.

Returns:

  • (Integer)

    The time taken to travel to the destination room.



434
435
436
437
# File 'documented/dragonrealms/commons/common-travel.rb', line 434

def time_to_room(origin, destination)
  _previous, shortest_paths = Map.dijkstra(origin, destination)
  shortest_paths[destination]
end

.walk_to(target_room, restart_on_fail = true) ⇒ Boolean

Walk to a specified room.

Examples:

Walking to a room

success = walk_to("market")

Parameters:

  • target_room (String, Integer)

    The room to walk to, can be a name or ID.

  • restart_on_fail (Boolean) (defaults to: true)

    Whether to retry if the first attempt fails.

Returns:

  • (Boolean)

    Returns true if successfully reached the room, false otherwise.



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
# File 'documented/dragonrealms/commons/common-travel.rb', line 198

def walk_to(target_room, restart_on_fail = true)
  target_room = tag_to_id(target_room) if target_room.is_a?(String) && target_room.count("a-zA-Z") > 0

  return false if target_room.nil?

  room_num = target_room.to_i
  return true if room_num == Room.current.id

  DRC.fix_standing

  if Room.current.id.nil?
    echo "In an unknown room, manually attempting to navigate to #{room_num}"
    rooms = Map.list.select { |room| room.description.include?(XMLData.room_description.strip) && room.title.include?(XMLData.room_title) }
    if rooms.empty? || rooms.length > 1
      echo 'failed to find a matching room'
      return false
    end
    room = rooms.first
    return true if room_num == room.id

    if room.wayto[room_num.to_s]
      move room.wayto[room_num.to_s]
      return room_num == room.id
    end
    path = Map.findpath(room, Map[room_num])
    way = room.wayto[path.first.to_s]
    if way.is_a?(StringProc)
      way.call
    else
      move way
    end
    return walk_to(room_num)
  end

  script_handle = start_script('go2', [room_num.to_s], force: true)

  timer = Time.now
  prev_room = XMLData.room_description + XMLData.room_title

  # Moved flag declaration from the start of the method to here
  # so that they only exist when needed and because the above code
  # has lots of 'return' statements that'd have to be refactored to
  # properly delete the flags at each method exit point.
  # I didn't want to tackle that endeavor in this change.
  Flags.add('travel-closed-shop', 'The door is locked up tightly for the night', 'You smash your nose', '^A servant (blocks|stops)')
  Flags.add('travel-engaged', 'You are engaged')

  while Script.running.include?(script_handle)
    # Shop closed, stop script and open door then restart
    if Flags['travel-closed-shop']
      Flags.reset('travel-closed-shop')
      kill_script(script_handle)
      if /You open/ !~ DRC.bput('open door', 'It is locked', 'You .+', 'What were')
        # You cannot get to where you want to go,
        # and no amount of retries will get you through the locked door.
        restart_on_fail = false
        break
      end
      timer = Time.now
      script_handle = start_script('go2', [room_num.to_s])
    end
    # You're engaged, stop script and retreat then restart
    if Flags['travel-engaged']
      Flags.reset('travel-engaged')
      kill_script(script_handle)
      DRC.retreat
      timer = Time.now
      script_handle = start_script('go2', [room_num.to_s])
    end
    # Something interferred with movement, stop script then restart
    if (Time.now - timer) > 90
      kill_script(script_handle)
      pause 0.5 while Script.running.include?(script_handle)
      break unless restart_on_fail

      timer = Time.now
      script_handle = start_script('go2', [room_num.to_s])
    end
    # If an escort script is running or we're making progress then update our timer so we don't timeout (see above)
    if Script.running?('escort') || Script.running?('bescort') || (XMLData.room_description + XMLData.room_title) != prev_room || XMLData.room_description =~ /The terrain constantly changes as you travel along on your journey/
      timer = Time.now
    end
    # Where did you come from, where did you go? Where did you come from, Cotten-Eye Joe?
    prev_room = XMLData.room_description + XMLData.room_title
    pause 0.5
  end

  # Delete flags, no longer needed at this point
  Flags.delete('travel-closed-shop')
  Flags.delete('travel-engaged')

  # Consider just returning this boolean and letting callers decide what to do on a failed move.
  if room_num != Room.current.id && restart_on_fail
    echo "Failed to navigate to room #{room_num}, attempting again"
    walk_to(room_num)
  end
  room_num == Room.current.id
end