Module: Lich::DragonRealms::DRCT
- Defined in:
- documented/dragonrealms/commons/common-travel.rb
Constant Summary collapse
- DIRECTION_REVERSE =
Direction reversal mapping for path reversal Note: With frozen_string_literal: true, string literals are already frozen Direction reversal mapping for path reversal.
This constant provides a mapping of directions to their opposites.
{ 'northeast' => 'southwest', 'southwest' => 'northeast', 'northwest' => 'southeast', 'southeast' => 'northwest', 'north' => 'south', 'south' => 'north', 'east' => 'west', 'west' => 'east', 'up' => 'down', 'down' => 'up' }.freeze
- SELL_SUCCESS_PATTERNS =
Patterns indicating successful sale at a merchant Patterns indicating successful sale at a merchant.
This constant contains regular expressions that match successful sale messages.
[ /hands? you \d+ (?:kronars|lirums|dokoras)/i ].freeze
- SELL_FAILURE_PATTERNS =
Patterns indicating failed sale attempt Patterns indicating failed sale attempt.
This constant contains regular expressions that match failure messages when attempting to sell an item.
[ /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/ ].freeze
- BUY_PRICE_PATTERNS =
[ /prepared to offer it to you for (?<amount>.*) (?:kronar|lirum|dokora)s?/, /Let me but ask the humble sum of (?<amount>.*) coins/, /it would be just (?<amount>\d*) (?:kronar|lirum|dokora)s?/, /for a (?:mere )?(?<amount>\d*) (?:kronar|lirum|dokora)s?/, /I can let that go for\.\.\.(?<amount>.*) (?:kronar|lirum|dokora)s?/, /cost you (?:just )?(?<amount>.*) (?:kronar|lirum|dokora)s?/, /it may be yours for just (?<amount>.*) (?:kronar|lirum|dokora)s?/, /I'll give that to you for (?<amount>.*) (?:kronar|lirum|dokora)s?/, /I'll let you have it for (?<amount>.*) (?:kronar|lirum|dokora)s?/i, /I ask that you give (?<amount>.*) copper (?:kronar|lirum|dokora)s?/, /it'll be (?<amount>.*) (?:kronar|lirum|dokora)s?/, /the price of (?<amount>.*) coins? is all I ask/, /tis only (?<amount>.*) (?:kronar|lirum|dokora)s?/, /That will be (?<amount>.*) copper (?:kronar|lirum|dokora)s? please/, /That'll be (?<amount>.*) copper (?:kronar|lirum|dokora)s?/, /to you for (?<amount>.*) (?:kronar|lirum|dokora)s?/, /I ask (?<amount>.*) copper (?:kronar|lirum|dokora)s or if you would prefer/, /Cost you just (?<amount>.*) (?:kronar|lirum|dokora)s?, okie dokie\?/i, /It will cost just (?<amount>.*) (?:kronar|lirum|dokora)s?/i, /I would suggest (?<amount>.*) (?:kronar|lirum|dokora)s?/i, /to you for (?<amount>.*) (?:kronar|lirum|dokora)s?/i, /asking (?<amount>.*) (?:kronar|lirum|dokora)s?/i ].freeze
- BUY_NON_PRICE_PATTERNS =
Patterns indicating buy action without price info Patterns indicating buy action without price info.
This constant contains phrases that indicate a purchase action without specifying a price.
[ 'You decide to purchase', 'Buy what' ].freeze
- ASK_SUCCESS_PATTERNS =
Patterns indicating successful ASK request Patterns indicating successful ASK request.
This constant contains regular expressions that match successful ask messages.
[ /hands you/ ].freeze
- ASK_FAILURE_PATTERNS =
Patterns indicating failed ASK request Patterns indicating failed ASK request.
This constant contains regular expressions that match failure messages when attempting to ask for an item.
[ /does not seem to know anything about that/, /All I know about/, /To whom are you speaking/, /Usage: ASK/ ].freeze
- MAX_WALK_TO_RETRIES =
Maximum number of times walk_to will retry navigation before giving up. Prevents unbounded recursion (and eventual SystemStackError) when the game server connection is lost or go2 repeatedly fails.
3
Class Method Summary collapse
-
.ask_for_item?(room, name, item) ⇒ Boolean
Asks a character for an item in the specified room.
-
.buy_item(room, item) ⇒ void
Buys an item in the specified room.
-
.dispose(item, trash_room = nil, worn_trashcan = nil, worn_trashcan_verb = nil) ⇒ void
Disposes of an item in the specified trash room.
-
.find_empty_room(search_rooms, idle_room, predicate = nil, min_mana = 0, strict_mana = false, max_search_attempts = Float::INFINITY, prioritize_buddies = false) ⇒ Boolean
Finds an empty room from a list of search rooms.
-
.find_sorted_empty_room(search_rooms, idle_room, predicate = nil) ⇒ Boolean
Finds an empty room from a sorted list of search rooms.
-
.get_hometown_target_id(hometown, target) ⇒ Integer?
Retrieves the target ID for a given target in a hometown.
-
.order_item(room, item_number) ⇒ void
Orders an item in the specified room.
-
.refill_lockpick_container(lockpick_type, hometown, container, count) ⇒ void
Refills a lockpick container with a specified number of lockpicks.
-
.retreat(ignored_npcs = []) ⇒ void
Retreats from the current room, ignoring specified NPCs.
-
.reverse_path(path) ⇒ Array<String>?
Reverses a path of directions.
-
.sell_item(room, item) ⇒ Boolean
Sells an item in the specified room.
-
.sort_destinations(target_list) ⇒ Array<Integer>
Sorts a list of destinations based on distance from the current room.
-
.tag_to_id(target) ⇒ Integer?
Converts a tag to a room ID.
-
.time_to_room(origin, destination) ⇒ Integer
Calculates the time to travel from one room to another.
-
.walk_to(target_room, restart_on_fail = true, retry_depth: 0) ⇒ Boolean
Walks to the specified target room.
Class Method Details
.ask_for_item?(room, name, item) ⇒ Boolean
Asks a character for an item in the specified room.
155 156 157 158 159 160 161 162 163 164 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 155 def ask_for_item?(room, name, item) walk_to(room) case DRC.bput("ask #{name} for #{item}", *ASK_SUCCESS_PATTERNS, *ASK_FAILURE_PATTERNS) when *ASK_SUCCESS_PATTERNS true else false end end |
.buy_item(room, item) ⇒ void
This method returns an undefined value.
Buys an item in the specified room.
137 138 139 140 141 142 143 144 145 146 147 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 137 def buy_item(room, item) walk_to(room) all_patterns = BUY_PRICE_PATTERNS + BUY_NON_PRICE_PATTERNS result = DRC.bput("buy #{item}", *all_patterns) match_data = BUY_PRICE_PATTERNS.lazy.filter_map { |p| p.match(result) }.first amount = match_data[:amount] if match_data 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.
Disposes of an item in the specified trash room.
186 187 188 189 190 191 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 186 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, prioritize_buddies = false) ⇒ Boolean
Finds an empty room from a list of search rooms.
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 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 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 387 def find_empty_room(search_rooms, idle_room, predicate = nil, min_mana = 0, strict_mana = false, max_search_attempts = Float::INFINITY, prioritize_buddies = false) search_attempt = 0 check_mana = min_mana > 0 rooms_searched = 0 loop do search_attempt += 1 Lich::Messaging.msg('plain', "DRCT: 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 prioritize_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? Lich::Messaging.msg('plain', 'DRCT: 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, 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 Lich::Messaging.msg('plain', 'DRCT: Empty rooms found, but not with the right mana. Going to use those anyway.') next end check_mana = min_mana > 0 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) Lich::Messaging.msg('plain', "DRCT: Failed to find an empty room, pausing #{wait_time} seconds.") pause wait_time else Lich::Messaging.msg('plain', 'DRCT: 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
Finds an empty room from a sorted list of search rooms.
455 456 457 458 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 455 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?
Retrieves the target ID for a given target in a hometown.
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 490 def get_hometown_target_id(hometown, target) hometown_data = get_data('town')[hometown] target_id = hometown_data[target] && hometown_data[target]['id'] unless target_id Lich::Messaging.msg('plain', "DRCT: 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'] unless target_id Lich::Messaging.msg('plain', "DRCT: get_hometown_target_id failed second attempt for #{target} in #{hometown}. Likely target doesn't exist.") if $common_travel_debug target_id = nil else Lich::Messaging.msg('plain', "DRCT: get_hometown_target_id succeeded second attempt for #{target} in #{hometown}.") if $common_travel_debug end end Lich::Messaging.msg('plain', "DRCT: target_id = #{target_id}") if $common_travel_debug target_id end |
.order_item(room, item_number) ⇒ void
This method returns an undefined value.
Orders an item in the specified room.
171 172 173 174 175 176 177 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 171 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.
Refills a lockpick container with a specified number of lockpicks.
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 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 200 def refill_lockpick_container(lockpick_type, hometown, container, count) return if count < 1 room = get_data('town')[hometown]['locksmithing']['id'] if room.nil? Lich::Messaging.msg('bold', 'DRCT: No locksmith location found for current hometown. Skipping refilling.') return end walk_to(room) if Room.current.id != room Lich::Messaging.msg('bold', 'DRCT: Could not reach locksmith location. Skipping refilling.') return end count.times do buy_item(room, "#{lockpick_type} lockpick") unless DRCI.put_away_item_unsafe?('my lockpick', "my #{container}", 'on') Lich::Messaging.msg('bold', "DRCT: Failed to put lockpick on #{container}. Check 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.
Retreats from the current room, ignoring specified NPCs.
371 372 373 374 375 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 371 def retreat(ignored_npcs = []) return if (DRRoom.npcs - ignored_npcs).empty? DRC.retreat(ignored_npcs) end |
.reverse_path(path) ⇒ Array<String>?
Reverses a path of directions.
474 475 476 477 478 479 480 481 482 483 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 474 def reverse_path(path) path.reverse.map do |dir| reversed = DIRECTION_REVERSE[dir] unless reversed Lich::Messaging.msg('bold', "DRCT: No reverse direction found for '#{dir}'. Use full direction names (e.g., 'northeast' not 'ne'). Path must be an array.") return nil end reversed end end |
.sell_item(room, item) ⇒ Boolean
Sells an item in the specified room.
119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 119 def sell_item(room, item) return false unless DRCI.in_hands?(item) walk_to(room) case DRC.bput("sell my #{item}", *SELL_SUCCESS_PATTERNS, *SELL_FAILURE_PATTERNS) when *SELL_SUCCESS_PATTERNS true when *SELL_FAILURE_PATTERNS false end end |
.sort_destinations(target_list) ⇒ Array<Integer>
Sorts a list of destinations based on distance from the current room.
442 443 444 445 446 447 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 442 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?
Converts a tag to a room ID.
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 339 def tag_to_id(target) start_room = Room.current.id target_list = Map.list.find_all { |room| room..include?(target) }.collect { |room| room.id } if target_list.empty? Lich::Messaging.msg('bold', "DRCT: No go2 targets matching '#{target}' found.") return nil end if target_list.include?(start_room) Lich::Messaging.msg('plain', "DRCT: 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? Lich::Messaging.msg('bold', "DRCT: Couldn't find a path from here to any room with a '#{target}' tag.") return nil end target_id = target_list.sort { |a, b| shortest_distances[a] <=> shortest_distances[b] }.first unless target_id && (destination = Map[target_id]) Lich::Messaging.msg('bold', "DRCT: Something went wrong! Debug failed with target_id=#{target_id}, destination=#{destination}, tag='#{target}'.") return nil end target_id end |
.time_to_room(origin, destination) ⇒ Integer
Calculates the time to travel from one room to another.
465 466 467 468 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 465 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, retry_depth: 0) ⇒ Boolean
Walks to the specified target room.
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 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 |
# File 'documented/dragonrealms/commons/common-travel.rb', line 240 def walk_to(target_room, restart_on_fail = true, retry_depth: 0) 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? Lich::Messaging.msg('plain', "DRCT: 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 Lich::Messaging.msg('bold', 'DRCT: Failed to find a matching room from unknown location.') 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 if retry_depth >= MAX_WALK_TO_RETRIES Lich::Messaging.msg('bold', "DRCT: Failed to navigate from unknown room after #{MAX_WALK_TO_RETRIES} retries, giving up.") return false end return walk_to(room_num, true, retry_depth: retry_depth + 1) end script_handle = start_script('go2', [room_num.to_s], force: true) timer = Time.now prev_room = XMLData.room_description + XMLData.room_title 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') begin while Script.running.include?(script_handle) 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') restart_on_fail = false break end timer = Time.now script_handle = start_script('go2', [room_num.to_s]) end 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 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 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 prev_room = XMLData.room_description + XMLData.room_title pause 0.5 end ensure Flags.delete('travel-closed-shop') Flags.delete('travel-engaged') end if room_num != Room.current.id && restart_on_fail if retry_depth >= MAX_WALK_TO_RETRIES Lich::Messaging.msg('bold', "DRCT: Failed to navigate to room #{room_num} after #{MAX_WALK_TO_RETRIES} retries, giving up.") return false end Lich::Messaging.msg('bold', "DRCT: Failed to navigate to room #{room_num}, attempting again (#{retry_depth + 1}/#{MAX_WALK_TO_RETRIES}).") return walk_to(room_num, true, retry_depth: retry_depth + 1) end room_num == Room.current.id end |