Class: Lich::Common::Map

Inherits:
Object
  • Object
show all
Includes:
Enumerable, MapBase
Defined in:
documented/common/map/map_dr.rb,
documented/common/map/map_gs.rb

Overview

Represents a map in the Lich project.

This class handles the management of rooms, their properties, and interactions within the game world.

See Also:

Direct Known Subclasses

Room

Constant Summary collapse

@@loaded =
false
@@load_mutex =
Mutex.new
@@current_room_mutex =
Mutex.new
@@fuzzy_room_mutex =
Mutex.new
@@uids =
{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id, title, description, paths, uid = [], location = nil, climate = nil, terrain = nil, wayto = {}, timeto = {}, image = nil, image_coords = nil, tags = [], check_location = nil, unique_loot = nil) ⇒ Map

Initializes a new Map instance.

Parameters:

  • id (Integer)

    unique identifier for the map

  • title (Array<String>)

    titles of the map

  • description (Array<String>)

    descriptions of the map

  • paths (Array<String>)

    paths available in the map

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

    unique identifiers for the map

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

    location of the map

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

    climate of the map

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

    terrain type of the map

  • wayto (Hash) (defaults to: {})

    paths leading to other rooms

  • timeto (Hash) (defaults to: {})

    time taken to travel along paths

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

    image associated with the map

  • image_coords (Array<Integer>, nil) (defaults to: nil)

    coordinates for the image

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

    tags associated with the map

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

    location check flag

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

    unique loot associated with the map



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
# File 'documented/common/map/map_dr.rb', line 57

def initialize(id, title, description, paths, uid = [], location = nil,
               climate = nil, terrain = nil, wayto = {}, timeto = {},
               image = nil, image_coords = nil, tags = [], check_location = nil,
               unique_loot = nil, _room_objects = nil,
               genie_id = nil, genie_zone = nil, genie_pos = nil)
  @id = id
  @title = title
  @description = description
  @paths = paths
  @uid = uid
  @location = location
  @climate = climate
  @terrain = terrain
  @wayto = wayto
  @timeto = timeto
  @image = image
  @image_coords = image_coords
  @tags = tags
  @check_location = check_location
  @unique_loot = unique_loot
  @genie_id = genie_id
  @genie_zone = genie_zone
  @genie_pos = genie_pos
  @@list[@id] = self
end

Instance Attribute Details

#check_locationObject

Returns the value of attribute check_location.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def check_location
  @check_location
end

#climateObject

Returns the value of attribute climate.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def climate
  @climate
end

#descriptionObject

Returns the value of attribute description.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def description
  @description
end

#genie_idObject

Returns the value of attribute genie_id.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def genie_id
  @genie_id
end

#genie_posObject

Returns the value of attribute genie_pos.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def genie_pos
  @genie_pos
end

#genie_zoneObject

Returns the value of attribute genie_zone.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def genie_zone
  @genie_zone
end

#idObject (readonly)

Returns the value of attribute id.



31
32
33
# File 'documented/common/map/map_dr.rb', line 31

def id
  @id
end

#imageObject

Returns the value of attribute image.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def image
  @image
end

#image_coordsObject

Returns the value of attribute image_coords.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def image_coords
  @image_coords
end

#locationObject

Returns the value of attribute location.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def location
  @location
end

#pathsObject

Returns the value of attribute paths.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def paths
  @paths
end

#room_objectsObject

Returns the value of attribute room_objects.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def room_objects
  @room_objects
end

#tagsObject

Returns the value of attribute tags.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def tags
  @tags
end

#terrainObject

Returns the value of attribute terrain.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def terrain
  @terrain
end

#timetoObject

Returns the value of attribute timeto.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def timeto
  @timeto
end

#titleObject

Returns the value of attribute title.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def title
  @title
end

#uidObject

Returns the value of attribute uid.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def uid
  @uid
end

#unique_lootObject

Returns the value of attribute unique_loot.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def unique_loot
  @unique_loot
end

#waytoObject

Returns the value of attribute wayto.



32
33
34
# File 'documented/common/map/map_dr.rb', line 32

def wayto
  @wayto
end

Class Method Details

.[](val) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'documented/common/map/map_dr.rb', line 161

def self.[](val)
  self.load unless @@loaded
  if val.is_a?(Integer) || val =~ /^[0-9]+$/
    @@list[val.to_i]
  elsif val =~ /^u(-?\d+)$/i
    uid_request = ::Regexp.last_match(1).dup.to_i
    @@list[(ids_from_uid(uid_request)[0]).to_i]
  else
    chkre = /#{val.strip.sub(/\.$/, '').gsub(/\.(?:\.\.)?/, '|')}/i
    chk = /#{Regexp.escape(val.strip)}/i
    @@list.find { |room| room&.title&.find { |title| title =~ chk } } ||
      @@list.find { |room| room&.description&.find { |desc| desc =~ chk } } ||
      @@list.find { |room| room&.description&.find { |desc| desc =~ chkre } }
  end
end

.by_genie_ref(zone_id, node_id) ⇒ Map?

Finds a map by its genie reference.

Parameters:

  • zone_id (String)

    the zone identifier

  • node_id (String)

    the node identifier

Returns:

  • (Map, nil)

    the map if found, nil otherwise



148
149
150
151
# File 'documented/common/map/map_dr.rb', line 148

def self.by_genie_ref(zone_id, node_id)
  self.load unless @@loaded
  @@list.find { |r| r&.genie_zone == zone_id.to_s && r&.genie_id == node_id.to_s }
end

.clearBoolean

Clears all map data and resets the state.

Returns:

  • (Boolean)

    true if cleared successfully



428
429
430
431
432
433
434
435
436
# File 'documented/common/map/map_dr.rb', line 428

def self.clear
  @@load_mutex.synchronize do
    @@list.clear
    @@tags.clear
    @@loaded = false
    GC.start
  end
  true
end

.clear_tags_cachevoid

This method returns an undefined value.

Clears the cached tags for the maps.



127
128
129
# File 'documented/common/map/map_dr.rb', line 127

def clear_tags_cache
  @@tags.clear
end

.currentMap?

Retrieves the current room based on the game state.

Returns:

  • (Map, nil)

    the current room or nil if not found



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'documented/common/map/map_dr.rb', line 194

def self.current
  self.load unless @@loaded
  if Script.current
    return @@list[@@current_room_id] if XMLData.room_count == @@current_room_count && !@@current_room_id.nil?
  elsif XMLData.room_count == @@fuzzy_room_count && !@@current_room_id.nil?
    return @@list[@@current_room_id]
  end
  ids = XMLData.room_id.zero? ? [] : ids_from_uid(XMLData.room_id)
  return set_current(ids[0]) if ids.size == 1

  if ids.size > 1 && !@@current_room_id.nil? && (id = match_multi_ids(ids))
    return set_current(id)
  end
  match_no_uid
end

.current_or_newMap?

Retrieves the current room or creates a new one if it doesn't exist.

Returns:

  • (Map, nil)

    the current or newly created room



336
337
338
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
366
# File 'documented/common/map/map_dr.rb', line 336

def self.current_or_new
  return nil unless Script.current

  @@current_room_count = -1
  @@fuzzy_room_count = -1

  self.load unless @@loaded

  id = current&.id

  echo("Map: current room id is #{id.inspect}")
  unless id.nil?
    room = self[id]
    unless XMLData.room_id.zero? || room.uid.include?(XMLData.room_id)
      room.uid << XMLData.room_id
      uids_add(XMLData.room_id, room.id)
      echo "Map: Adding new uid for #{room.id}: #{XMLData.room_id}"
    end
    return set_current(room.id)
  end

  id = get_free_id
  title = [XMLData.room_title]
  description = [XMLData.room_description.strip]
  paths = [XMLData.room_exits_string.strip]
  uid = XMLData.room_id.zero? ? [] : [XMLData.room_id]
  room = new(id, title, description, paths, uid)
  uids_add(XMLData.room_id, room.id) unless XMLData.room_id.zero?
  echo "mapped new room, set current room to #{room.id}"
  set_current(id)
end

.current_room_idObject



85
86
87
# File 'documented/common/map/map_gs.rb', line 85

def current_room_id
  @@current_room_id
end

.current_room_id=(id) ⇒ Object



89
90
91
# File 'documented/common/map/map_gs.rb', line 89

def current_room_id=(id)
  @@current_room_id = id
end

.fuzzy_room_idObject



145
146
147
# File 'documented/common/map/map_gs.rb', line 145

def self.fuzzy_room_id
  @@fuzzy_room_id
end

.get_free_idInteger

Gets a new unique ID for a room.

Returns:

  • (Integer)

    a unique ID for a new room



156
157
158
159
# File 'documented/common/map/map_dr.rb', line 156

def self.get_free_id
  self.load unless @@loaded
  @@list.compact.max_by(&:id).id + 1
end

.get_locationString?

Retrieves the current location based on the game state.

Returns:

  • (String, nil)

    the current location or nil if not determined



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'documented/common/map/map_gs.rb', line 174

def self.get_location
  unless XMLData.room_count == @@current_location_count
    if (script = Script.current)
      save_want_downstream = script.want_downstream
      script.want_downstream = true
      waitrt?
      location_result = dothistimeout(
        'location', 15,
        /^You carefully survey your surroundings and guess that your current location is .*? or somewhere close to it\.$|^You can't do that while submerged under water\.$|^You can't do that\.$|^It would be rude not to give your full attention to the performance\.$|^You can't do that while hanging around up here!$|^You are too distracted by the difficulty of staying alive in these treacherous waters to do that\.$|^You carefully survey your surroundings but are unable to guess your current location\.$|^Not in pitch darkness you don't\.$|^That is too difficult to consider here\.$/
      )
      script.want_downstream = save_want_downstream
      @@current_location_count = XMLData.room_count
      if location_result =~ /^You can't do that while submerged under water\.$|^You can't do that\.$|^It would be rude not to give your full attention to the performance\.$|^You can't do that while hanging around up here!$|^You are too distracted by the difficulty of staying alive in these treacherous waters to do that\.$|^You carefully survey your surroundings but are unable to guess your current location\.$|^Not in pitch darkness you don't\.$|^That is too difficult to consider here\.$/
        @@current_location = false
      else
        @@current_location = /^You carefully survey your surroundings and guess that your current location is (.*?) or somewhere close to it\.$/.match(location_result).captures.first
      end
    else
      return nil
    end
  end
  @@current_location
end

.ids_from_uid(n) ⇒ Array<Integer>

Retrieves IDs associated with a given unique ID.

Parameters:

  • n (String)

    the unique ID to look up

Returns:

  • (Array<Integer>)

    list of IDs associated with the unique ID



421
422
423
# File 'documented/common/map/map_dr.rb', line 421

def self.ids_from_uid(n)
  @@uids[n].nil? || n.zero? ? [] : @@uids[n]
end

.imagesArray<String>

Retrieves a list of unique images from the map.

Returns:

  • (Array<String>)

    list of unique images



530
531
532
533
534
# File 'documented/common/map/map_gs.rb', line 530

def self.images
  self.load unless @@loaded
  @@images = @@list.each_with_object({}) { |r, h| h[r.image] = nil unless h.key?(r.image) }.keys if @@images.empty?
  @@images.dup
end

.listArray<Map>

Retrieves the list of maps.

Returns:

  • (Array<Map>)

    array of loaded maps



108
109
110
111
# File 'documented/common/map/map_dr.rb', line 108

def list
  self.load unless @@loaded
  @@list
end

.list=(value) ⇒ Object



113
114
115
# File 'documented/common/map/map_dr.rb', line 113

def list=(value)
  @@list = value
end

.load(filename = nil) ⇒ Boolean

Loads map data from files.

Parameters:

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

    optional filename to load

Returns:

  • (Boolean)

    true if loaded successfully



442
443
444
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/common/map/map_dr.rb', line 442

def self.load(filename = nil)
  file_list = if filename.nil?
                Dir.entries(File.join(DATA_DIR, XMLData.game))
                   .find_all { |fn| fn =~ /^map-[0-9]+\.(?:dat|xml|json)$/i }
                   .collect { |fn| File.join(DATA_DIR, XMLData.game, fn) }
                   .sort
                   .reverse
              else
                [filename]
              end

  if file_list.empty?
    respond '--- Lich: error: no map database found'
    return false
  end

  while (filename = file_list.shift)
    if filename =~ /\.json$/i
      return true if load_json(filename)
    elsif filename =~ /\.xml$/
      return true if load_xml(filename)
    elsif load_dat(filename)
      return true
    end
  end
  false
end

.load_json(filename = nil) ⇒ Boolean

Loads map data from a JSON file.

Parameters:

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

    optional filename to load

Returns:

  • (Boolean)

    true if loaded successfully



474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
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
# File 'documented/common/map/map_dr.rb', line 474

def self.load_json(filename = nil)
  @@load_mutex.synchronize do
    return true if @@loaded

    file_list = if filename
                  [filename]
                else
                  Dir.entries(File.join(DATA_DIR, XMLData.game))
                     .find_all { |fn| fn =~ /^map-[0-9]+\.json$/i }
                     .collect { |fn| File.join(DATA_DIR, XMLData.game, fn) }
                     .sort
                     .reverse
                end

    if file_list.empty?
      respond '--- Lich: error: no map database found'
      return false
    end

    while (filename = file_list.shift)
      next unless File.exist?(filename)

      File.open(filename) do |f|
        JSON.parse(f.read).each do |room|
          room['wayto'].keys.each do |k|
            if room['wayto'][k][0..2] == ';e '
              room['wayto'][k] = StringProc.new(room['wayto'][k][3..])
            end
          end
          room['timeto'].keys.each do |k|
            if room['timeto'][k].is_a?(String) && room['timeto'][k][0..2] == ';e '
              room['timeto'][k] = StringProc.new(room['timeto'][k][3..])
            end
          end
          room['tags'] ||= []
          room['uid'] ||= []
          new(
            room['id'], room['title'], room['description'], room['paths'],
            room['uid'], room['location'], room['climate'], room['terrain'],
            room['wayto'], room['timeto'], room['image'], room['image_coords'],
            room['tags'], room['check_location'], room['unique_loot'],
            nil, # _room_objects
            room['genie_id'], room['genie_zone'], room['genie_pos']
          )
        end
      end
      @@tags.clear
      respond "--- Map loaded #{filename}"
      @@loaded = true
      load_uids
      return true
    end
  end
end

.load_uidsvoid

This method returns an undefined value.

Loads unique IDs from the map data.



394
395
396
397
398
399
400
401
402
403
404
405
406
# File 'documented/common/map/map_dr.rb', line 394

def self.load_uids
  self.load unless @@loaded
  @@uids.clear
  @@list.each do |r|
    r.uid.each do |u|
      if @@uids[u].nil?
        @@uids[u] = [r.id]
      elsif !@@uids[u].include?(r.id)
        @@uids[u] << r.id
      end
    end
  end
end

.load_xml(filename = File.join(DATA_DIR, XMLData.game, 'map.xml')) ⇒ Boolean

Deprecated.

Use Map.load_json instead.

Loads map data from an XML file.

Parameters:

  • filename (String) (defaults to: File.join(DATA_DIR, XMLData.game, 'map.xml'))

    the filename to load

Returns:

  • (Boolean)

    true if loaded successfully



533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
# File 'documented/common/map/map_dr.rb', line 533

def self.load_xml(filename = File.join(DATA_DIR, XMLData.game, 'map.xml'))
  respond '--- WARNING: Map.load_xml is deprecated. Use Map.load_json instead.'
  @@load_mutex.synchronize do
    return true if @@loaded

    unless File.exist?(filename)
      raise Exception.exception('MapDatabaseError'), "Fatal error: file `#{filename}' does not exist!"
    end

    missing_end = false
    current_tag = nil
    current_attributes = nil
    room = nil
    buffer = String.new
    unescape = { 'lt' => '<', 'gt' => '>', 'quot' => '"', 'apos' => "'", 'amp' => '&' }

    tag_start = proc do |element, attributes|
      current_tag = element
      current_attributes = attributes
      if element == 'room'
        room = {}
        room['id'] = attributes['id'].to_i
        room['location'] = attributes['location']
        room['climate'] = attributes['climate']
        room['terrain'] = attributes['terrain']
        room['wayto'] = {}
        room['timeto'] = {}
        room['title'] = []
        room['description'] = []
        room['paths'] = []
        room['tags'] = []
        room['unique_loot'] = []
        room['uid'] = []
        room['room_objects'] = []
      elsif element =~ /^(?:image|tsoran)$/ && attributes['name'] && attributes['x'] && attributes['y'] && attributes['size']
        room['image'] = attributes['name']
        room['image_coords'] = [
          (attributes['x'].to_i - (attributes['size'] / 2.0).round),
          (attributes['y'].to_i - (attributes['size'] / 2.0).round),
          (attributes['x'].to_i + (attributes['size'] / 2.0).round),
          (attributes['y'].to_i + (attributes['size'] / 2.0).round)
        ]
      elsif element == 'image' && attributes['name'] && attributes['coords'] && attributes['coords'] =~ /[0-9]+,[0-9]+,[0-9]+,[0-9]+/
        room['image'] = attributes['name']
        room['image_coords'] = attributes['coords'].split(',').collect(&:to_i)
      elsif element == 'map'
        missing_end = true
      end
    end

    text = proc do |text_string|
      if current_tag == 'tag'
        room['tags'].push(text_string)
      elsif current_tag =~ /^(?:title|description|paths|unique_loot|tag|room_objects)$/
        room[current_tag].push(text_string)
      elsif current_tag =~ /^(?:uid)$/
        room[current_tag].push(text_string.to_i)
      elsif current_tag == 'exit' && current_attributes['target']
        room['wayto'][current_attributes['target']] = if current_attributes['type'].downcase == 'string'
                                                        text_string
                                                      else
                                                        StringProc.new(text_string)
                                                      end
        room['timeto'][current_attributes['target']] = if current_attributes['cost'] =~ /^[0-9.]+$/
                                                         current_attributes['cost'].to_f
                                                       elsif current_attributes['cost'].length.positive?
                                                         StringProc.new(current_attributes['cost'])
                                                       else
                                                         0.2
                                                       end
      end
    end

    tag_end = proc do |element|
      if element == 'room'
        room['unique_loot'] = nil if room['unique_loot'].empty?
        room['room_objects'] = nil if room['room_objects'].empty?
        new(
          room['id'], room['title'], room['description'], room['paths'],
          room['uid'], room['location'], room['climate'], room['terrain'],
          room['wayto'], room['timeto'], room['image'], room['image_coords'],
          room['tags'], room['check_location'], room['unique_loot'], room['room_objects']
        )
      elsif element == 'map'
        missing_end = false
      end
      current_tag = nil
    end

    begin
      File.open(filename) do |file|
        while (line = file.gets)
          buffer.concat(line)
          while (str = buffer.slice!(/^<([^>]+)><\/\1>|^[^<]+(?=<)|^<[^<]+>/))
            if str[0, 1] == '<'
              if str[1, 1] == '/'
                element = %r{^</([^\s>/]+)}.match(str).captures.first
                tag_end.call(element)
              elsif str =~ %r{^<([^>]+)></\1>}
                element = ::Regexp.last_match(1)
                tag_start.call(element)
                text.call('')
                tag_end.call(element)
              else
                element = %r{^<([^\s>/]+)}.match(str).captures.first
                attributes = {}
                str.scan(/([A-z][A-z0-9_-]*)=(["'])(.*?)\2/).each do |attr|
                  attributes[attr[0]] = attr[2].gsub(/&(#{unescape.keys.join('|')});/) { unescape[::Regexp.last_match(1)] }
                end
                tag_start.call(element, attributes)
                tag_end.call(element) if str[-2, 1] == '/'
              end
            else
              text.call(str.gsub(/&(#{unescape.keys.join('|')});/) { unescape[::Regexp.last_match(1)] })
            end
          end
        end
      end

      if missing_end
        respond "--- Lich: error: failed to load #{filename}: unexpected end of file"
        return false
      end

      @@tags.clear
      load_uids
      @@loaded = true
      true
    rescue StandardError => e
      respond "--- Lich: error: failed to load #{filename}: #{e}"
      false
    end
  end
end

.loadedObject



93
94
95
# File 'documented/common/map/map_gs.rb', line 93

def loaded
  @@loaded
end

.loaded?Boolean

Checks if the map has been loaded.

Returns:

  • (Boolean)

    true if the map is loaded, false otherwise



101
102
103
# File 'documented/common/map/map_dr.rb', line 101

def loaded?
  @@loaded
end

.locationsArray<String>

Retrieves a list of unique locations from the map.

Returns:

  • (Array<String>)

    list of unique locations



522
523
524
525
526
# File 'documented/common/map/map_gs.rb', line 522

def self.locations
  self.load unless @@loaded
  @@locations = @@list.each_with_object({}) { |r, h| h[r.location] = nil unless h.key?(r.location) }.keys if @@locations.empty?
  @@locations.dup
end

.mark_loadedvoid

This method returns an undefined value.

Marks the map as loaded.



134
135
136
# File 'documented/common/map/map_dr.rb', line 134

def mark_loaded
  @@loaded = true
end

.match_current(script) ⇒ Integer?

Matches the current room based on the game state.

Parameters:

  • script (Script)

    the current script context

Returns:

  • (Integer, nil)

    the matched room ID or nil if no match found



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
# File 'documented/common/map/map_gs.rb', line 264

def self.match_current(_script)
  @@current_room_mutex.synchronize do
    need_set_desc_off = false
    begin
      loop do
        @@current_room_count = XMLData.room_count
        foggy_exits = XMLData.room_exits_string =~ /^Obvious (?:exits|paths): obscured by a thick fog$/
        room = @@list.find do |r|
          r.title.include?(XMLData.room_title) &&
            r.description.include?(XMLData.room_description.strip) &&
            (foggy_exits || r.paths.include?(XMLData.room_exits_string.strip))
        end

        if room
          redo unless @@current_room_count == XMLData.room_count
          if room.uid.any?
            return room.uid.include?(XMLData.room_id) ? room.id : nil
          else
            return room.id
          end
        else
          redo unless @@current_room_count == XMLData.room_count
          desc_regex = /#{Regexp.escape(XMLData.room_description.strip.sub(/\.+$/, '')).gsub(/\\\.(?:\\\.\\\.)?/, '|')}/
          room = @@list.find do |r|
            r.title.include?(XMLData.room_title) &&
              (foggy_exits || r.paths.include?(XMLData.room_exits_string.strip)) &&
              (XMLData.room_window_disabled || r.description.any? { |desc| desc =~ desc_regex })
          end

          if room
            redo unless @@current_room_count == XMLData.room_count
            if room.uid.any?
              return room.uid.include?(XMLData.room_id) ? room.id : nil
            else
              return room.id
            end
          else
            redo unless @@current_room_count == XMLData.room_count
            return nil
          end
        end
      end
    ensure
      put 'set description off' if need_set_desc_off
    end
  end
end

.match_fuzzyMap?

Matches the current map using fuzzy logic.

Returns:

  • (Map, nil)

    the matched map if found, nil otherwise



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
# File 'documented/common/map/map_dr.rb', line 284

def self.match_fuzzy
  @@fuzzy_room_mutex.synchronize do
    @@fuzzy_room_count = XMLData.room_count
    loop do
      foggy_exits = XMLData.room_exits_string =~ /^Obvious (?:exits|paths): obscured by a thick fog$/
      room = @@list.find do |r|
        r.title.include?(XMLData.room_title) &&
          r.description.include?(XMLData.room_description.strip) &&
          (foggy_exits || r.paths.include?(XMLData.room_exits_string.strip))
      end

      if room
        redo unless @@fuzzy_room_count == XMLData.room_count

        if room.uid.any?
          return room.uid.include?(XMLData.room_id) ? room.id : nil
        elsif room.tags.any? { |tag| tag =~ /^(set desc on; )?peer [a-z]+ =~ \/.+\/$/ }
          return nil
        else
          return room.id
        end
      else
        redo unless @@fuzzy_room_count == XMLData.room_count
        desc_regex = /#{Regexp.escape(XMLData.room_description.strip.sub(/\.+$/, '')).gsub(/\\\.(?:\\\.\\\.)?/, '|')}/
        room = @@list.find do |r|
          r.title.include?(XMLData.room_title) &&
            (foggy_exits || r.paths.include?(XMLData.room_exits_string.strip)) &&
            (XMLData.room_window_disabled || r.description.any? { |desc| desc =~ desc_regex })
        end

        if room
          redo unless @@fuzzy_room_count == XMLData.room_count

          if room.uid.any?
            return room.uid.include?(XMLData.room_id) ? room.id : nil
          elsif room.tags.any? { |tag| tag =~ /^(set desc on; )?peer [a-z]+ =~ \/.+\/$/ }
            return nil
          else
            return room.id
          end
        else
          redo unless @@fuzzy_room_count == XMLData.room_count
          return nil
        end
      end
    end
  end
end

.match_multi_ids(ids) ⇒ Integer?

Matches multiple IDs to find a valid room.

Parameters:

  • ids (Array<Integer>)

    list of IDs to match

Returns:

  • (Integer, nil)

    the matched room ID or nil if no match found



384
385
386
387
388
389
# File 'documented/common/map/map_dr.rb', line 384

def self.match_multi_ids(ids)
  matches = ids.find_all { |s| @@list[@@current_room_id].wayto.keys.include?(s.to_s) }
  return matches[0] if matches.size == 1

  nil
end

.match_no_uidMap?

Matches the current map when no UID is available.

Returns:

  • (Map, nil)

    the matched map if found, nil otherwise



213
214
215
216
217
218
219
# File 'documented/common/map/map_dr.rb', line 213

def self.match_no_uid
  if (script = Script.current)
    set_current(match_current(script))
  else
    set_fuzzy(match_fuzzy)
  end
end

.previousMap?

Retrieves the previous map.

Returns:

  • (Map, nil)

    the previous map if available, nil otherwise



180
181
182
# File 'documented/common/map/map_dr.rb', line 180

def self.previous
  @@list[@@previous_room_id]
end

.previous_room_idObject



101
102
103
# File 'documented/common/map/map_gs.rb', line 101

def previous_room_id
  @@previous_room_id
end

.previous_room_id=(id) ⇒ Object



105
106
107
# File 'documented/common/map/map_gs.rb', line 105

def previous_room_id=(id)
  @@previous_room_id = id
end

.previous_uidString

Retrieves the previous UID for the map.

Returns:

  • (String)

    the previous UID



187
188
189
# File 'documented/common/map/map_dr.rb', line 187

def self.previous_uid
  XMLData.previous_nav_rm
end

.save_xml(_filename = nil) ⇒ void

This method returns an undefined value.

Saves the map data to an XML file.

Parameters:

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

    optional filename to save to



672
673
674
# File 'documented/common/map/map_dr.rb', line 672

def self.save_xml(_filename = nil)
  respond '--- WARNING: Map.save_xml is deprecated. Use Map.save_json instead.'
end

.set_current(id) ⇒ Map?

Sets the current map by ID.

Parameters:

  • id (Integer)

    the ID of the map to set

Returns:

  • (Map, nil)

    the set map if successful, nil otherwise



372
373
374
375
376
377
378
# File 'documented/common/map/map_dr.rb', line 372

def self.set_current(id)
  @@previous_room_id = @@current_room_id if id != @@current_room_id
  @@current_room_id = id
  return nil if id.nil?

  @@list[id]
end

.set_fuzzy(id) ⇒ Map?

Sets the current map using fuzzy matching.

Parameters:

  • id (Integer)

    the ID of the map to set

Returns:

  • (Map, nil)

    the set map if successful, nil otherwise



225
226
227
228
229
230
231
# File 'documented/common/map/map_dr.rb', line 225

def self.set_fuzzy(id)
  @@previous_room_id = @@current_room_id if !id.nil? && id != @@current_room_id
  @@current_room_id = id
  return nil if id.nil?

  @@list[id]
end

.synchronize_load(&block) ⇒ Object



138
139
140
# File 'documented/common/map/map_dr.rb', line 138

def synchronize_load(&block)
  @@load_mutex.synchronize(&block)
end

.tagsArray<String>

Retrieves a list of unique tags from the map.

Returns:

  • (Array<String>)

    list of unique tags



411
412
413
414
415
# File 'documented/common/map/map_dr.rb', line 411

def self.tags
  self.load unless @@loaded
  @@tags = @@list.compact.each_with_object({}) { |r, h| r.tags.each { |t| h[t] = nil unless h.key?(t) } }.keys if @@tags.empty?
  @@tags.dup
end

.uidsHash

Retrieves the unique identifiers for the maps.

Returns:

  • (Hash)

    hash mapping unique identifiers to map IDs



120
121
122
# File 'documented/common/map/map_dr.rb', line 120

def uids
  @@uids
end

.uids_clearvoid

This method returns an undefined value.

Clears all unique IDs from the map.



546
547
548
# File 'documented/common/map/map_gs.rb', line 546

def self.uids_clear
  @@uids.clear
end

Instance Method Details

#fuzzy_room_idString?

Retrieves the current fuzzy room ID.

Returns:

  • (String, nil)

    the current fuzzy room ID or nil if not set



137
138
139
# File 'documented/common/map/map_gs.rb', line 137

def fuzzy_room_id
  @@current_room_id
end

#json_extra_fieldsHash

Returns additional fields for JSON representation.

Returns:

  • (Hash)

    hash containing genie_id, genie_zone, and genie_pos



93
94
95
# File 'documented/common/map/map_dr.rb', line 93

def json_extra_fields
  { genie_id: @genie_id, genie_zone: @genie_zone, genie_pos: @genie_pos }
end

#to_sString

Returns a string representation of the map.

Returns:

  • (String)

    formatted string containing map details



86
87
88
# File 'documented/common/map/map_dr.rb', line 86

def to_s
  "##{@id} (#{@uid[-1]}):\n#{@title[-1]}\n#{@description[-1]}\n#{@paths[-1]}"
end