Module: Lich::DragonRealms::DRParser
- Defined in:
- documented/dragonrealms/drinfomon/drparser.rb
Overview
The DRParser module contains methods and patterns for parsing DragonRealms game data.
Defined Under Namespace
Modules: Pattern
Class Method Summary collapse
-
.check_events(server_string) ⇒ String
Checks the server string for events and updates flags accordingly.
-
.check_exp_mods(server_string) ⇒ String
Parses the output from the ‘exp mods` command and updates skill modifiers.
-
.check_known_barbarian_abilities(server_string) ⇒ String
Parses the output from the ‘ability` command for Barbarians and populates known abilities.
-
.check_known_spells(server_string) ⇒ String
Parses the output from the ‘spells` command for magic users and populates known spells.
-
.check_known_thief_khri(server_string) ⇒ String
Parses the output from the ‘ability` command for Thieves and populates known khri.
-
.parse(line) ⇒ void
Parses a line of server output and updates game state accordingly.
-
.populate_inventory_get(server_string) ⇒ String
Populates the inventory based on the server string received.
Class Method Details
.check_events(server_string) ⇒ String
Checks the server string for events and updates flags accordingly.
81 82 83 84 85 86 87 88 89 90 91 |
# File 'documented/dragonrealms/drinfomon/drparser.rb', line 81 def self.check_events(server_string) Flags.matchers.each do |key, regexes| regexes.each do |regex| if (matches = server_string.match(regex)) Flags.flags[key] = matches break end end end server_string end |
.check_exp_mods(server_string) ⇒ String
Parses the output from the ‘exp mods` command and updates skill modifiers.
154 155 156 157 158 159 160 161 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 |
# File 'documented/dragonrealms/drinfomon/drparser.rb', line 154 def self.check_exp_mods(server_string) # This method parses the output from `exp mods` command # and updates the DRSkill.exp_modifiers hash with the skill and value. # This is primarily used by the `skill-recorder` script. # # Example output without any modifiers: # The following skills are currently under the influence of a modifier: # <output class="mono"/> # None # <output class=""/> # # Example output with modifiers: # The following skills are currently under the influence of a modifier: # <output class="mono"/> # +75 Athletics # -10 Evasion # <output class=""/> # # Zero or more skills may be listed between the <output> tags # but exactly one skill and its skill modifier are listed per line. # The number is signed to indicate a buff (+) or debuff (-). # case server_string when %r{^<output class=""/>} if @parsing_exp_mods_output @parsing_exp_mods_output = false end else if @parsing_exp_mods_output # https://rubular.com/r/hg7SFvVNUtdLdh # Sample line # <preset id="speech">+79 Attunement</preset> match = /^(?:<preset id="speech">)?(?<sign>[+-])(?<value>\d+)\s+(?<skill>[\w\s]+)(?:<\/preset>)?$/.match(server_string.strip) if match skill = match[:skill].strip sign = match[:sign] value = match[:value].to_i value = (value * -1) if sign == '-' DRSkill.update_mods(skill, value) end end end server_string end |
.check_known_barbarian_abilities(server_string) ⇒ String
Parses the output from the ‘ability` command for Barbarians and populates known abilities.
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 339 340 341 342 343 344 345 |
# File 'documented/dragonrealms/drinfomon/drparser.rb', line 305 def self.(server_string) # This method parses the output from `ability` command for Barbarians # and populates the known spells/feats based on the known abilities/masteries. # # The XML stream for DragonRealms is whack at best. # # Examples of the data we're looking for: # You know the Berserks:<pushBold/> Avalanche, Drought. # <popBold/>You know the Forms:<pushBold/> Monkey. # <popBold/>You know the Roars:<pushBold/> Anger the Earth. # <popBold/>You know the Meditations:<pushBold/> Flame, Power, Contemplation. # <popBold/>You know the Masteries:<pushBold/> Juggernaut, Duelist. # <popBold/> # You recall that you have 0 training sessions remaining with the Guild. case server_string when /^(<(push|pop)Bold\/>)?You know the (Berserks|Forms|Roars|Meditations):(<(push|pop)Bold\/>)?/ if DRSpells. server_string .sub(/^(<(push|pop)Bold\/>)?You know the (Berserks|Forms|Roars|Meditations):(<(push|pop)Bold\/>)?/, '') .sub('.', '') .split(',') .map(&:strip) .reject { |ability| ability.nil? || ability.empty? } .each { |ability| DRSpells.known_spells[ability] = true } end when /^(<(push|pop)Bold\/>)?You know the (Masteries):(<(push|pop)Bold\/>)?/ # Barbarian masteries are the equivalent of magical feats. if DRSpells. server_string .sub(/^(<(push|pop)Bold\/>)?You know the (Masteries):(<(push|pop)Bold\/>)?/, '') .sub('.', '') .split(',') .map(&:strip) .reject { |mastery| mastery.nil? || mastery.empty? } .each { |mastery| DRSpells.known_feats[mastery] = true } end when /^You recall that you have (\d+) training sessions? remaining with the Guild/ DRSpells. = false end server_string end |
.check_known_spells(server_string) ⇒ String
Parses the output from the ‘spells` command for magic users and populates known spells.
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 |
# File 'documented/dragonrealms/drinfomon/drparser.rb', line 202 def self.check_known_spells(server_string) # This method parses the output from `spells` command for magic users # and populates the known spells/feats based on the output. # # As of June 2022, There are two different output formats: column-formatted and non-column. # https://elanthipedia.play.net/Post:Tuesday_Tidings_-_120_-_Spells_-_06/07/2022_-_18:34 # # The XML stream for DragonRealms is whack at best. # # Examples of non-column output: # You recall the spells you have learned from your training. # In the chapter entitled "Analogous Patterns", you have notes on the Manifest Force [maf] and Gauge Flow [gaf] spells. # You have temporarily memorized the Tailwind [tw] spell. # You recall proficiency with the magic feats of Sorcerous Patterns, Alternate Preparation and Augmentation Mastery. # You are NOT currently set to recognize known spells when prepared by someone else in the area. (Use SPELL RECOGNIZE ON to change this.) # You are currently set to display full cast messaging. (Use SPELL BRIEFMSG ON to change this.) # You are currently attempting to hide your spell preparing. (Use PREPARE /HIDE to change this.) # You can use SPELL STANCE [HELP] to view or modify your spellcasting preferences. # # Examples of column-formatted output: # You recall the spells you have learned from your training. # <output class="mono"/> # <pushBold/> # Analogous Patterns: # <popBold/> maf Manifest Force Slot(s): 1 Min Prep: 1 Max Prep: 100 # gaf Gauge Flow Slot(s): 2 Min Prep: 5 Max Prep: 100 # <pushBold/> # Synthetic Creation: # <popBold/> acs Acid Splash Slot(s): 1 Min Prep: 1 Max Prep: 50 # vs Viscous Solution Slot(s): 2 Min Prep: 10 Max Prep: 66 # <output class=""/> # <output class="mono"/> # <pushBold/> # Temporarily Memorized: # <popBold/> tw Tailwind Slot(s): 1 Min Prep: 5 Max Prep: 100 # <output class=""/> # You recall proficiency with the magic feats of Sorcerous Patterns, Alternate Preparation and Augmentation Mastery. # You are NOT currently set to recognize known spells when prepared by someone else in the area. (Use SPELL RECOGNIZE ON to change this.) # You are currently set to display full cast messaging. (Use SPELL BRIEFMSG ON to change this.) # You are currently attempting to hide your spell preparing. (Use PREPARE /HIDE to change this.) # You can use SPELL STANCE [HELP] to view or modify your spellcasting preferences. # # One or more spells may be listed between a <popBold/> <pushBold/> pair, # but only one spell and its information are ever listed per line. case server_string when /^<output class="mono"\/>/ # Matched an xml tag while parsing spells, must be column-formatted output if DRSpells.grabbing_known_spells DRSpells.spellbook_format = 'column-formatted' end when /^[\w\s]+:/ # Matched the spellbook name in column-formatted output, ignore when /Slot\(s\): \d+ \s+ Min Prep: \d+ \s+ Max Prep: \d+/ # Matched the spell info in column-formatted output, parse if DRSpells.grabbing_known_spells && DRSpells.spellbook_format == 'column-formatted' spell = server_string .sub('<popBold/>', '') # remove xml tag at start of some lines .slice(10, 32) # grab the spell name, after the alias and before Slots .strip if !spell.empty? DRSpells.known_spells[spell] = true end end # Preserve the pop bold command we removed from start of spell line # otherwise lots of game text suddenly are highlighted yellow when /^In the chapter entitled|^You have temporarily memorized|^From your apprenticeship you remember practicing/ if DRSpells.grabbing_known_spells server_string .sub(/^In the chapter entitled "[\w\s\'-]+", you have notes on the /, '') .sub(/^You have temporarily memorized the /, '') .sub(/^From your apprenticeship you remember practicing with the /, '') .sub(/ spells?\./, '') .sub(/,? and /, ',') .split(',') .map { |mapped_spell| mapped_spell.include?('[') ? mapped_spell.slice(0, mapped_spell.index('[')) : mapped_spell } .map(&:strip) .reject { |rejected_spell| rejected_spell.nil? || rejected_spell.empty? } .each { |each_spell| DRSpells.known_spells[each_spell] = true } end when /^You recall proficiency with the magic feats of/ if DRSpells.grabbing_known_spells # The feats are listed without the Oxford comma separating the last item. # This makes splitting the string by comma difficult because the next to last and last # items would be captured together. The workaround is we'll replace ' and ' with a comma # and hope no feats ever have the word 'and' in them... server_string .sub(/^You recall proficiency with the magic feats of/, '') .sub(/,? and /, ',') .sub('.', '') .split(',') .map(&:strip) .reject { |feat| feat.nil? || feat.empty? } .each { |feat| DRSpells.known_feats[feat] = true } end when /^You can use SPELL STANCE|^You have (no|yet to receive any) training in the magical arts|You have no desire to soil yourself with magical trickery|^You really shouldn't be loitering here|\(Use SPELL|\(Use PREPARE/ DRSpells.grabbing_known_spells = false end server_string end |
.check_known_thief_khri(server_string) ⇒ String
Parses the output from the ‘ability` command for Thieves and populates known khri.
350 351 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 |
# File 'documented/dragonrealms/drinfomon/drparser.rb', line 350 def self.check_known_thief_khri(server_string) # This method parses the output from `ability` command for Thieves # and populates the known spells/feats based on the known khri. # # The XML stream for DragonRealms is whack at best. # # Examples of the data we're looking for: # From the Subtlety tree, you know the following khri: Darken (Aug), Dampen (Util/Ward), Strike (Aug), Silence (Util), Shadowstep (Util), Harrier (Aug) # From the Finesse tree, you know the following khri: Hasten (Util), Safe (Aug), Avoidance (Aug), Plunder (Aug), Flight (Aug/Ward), Elusion (Aug), Slight (Util) # From the Potence tree, you know the following khri: Focus (Aug), Prowess (Debil), Sight (Aug), Calm (Util), Steady (Aug), Eliminate (Debil), Serenity (Ward), Sagacity (Ward), Terrify (Debil) # You have 7 available slots. case server_string when /^From the (Subtlety|Finesse|Potence) tree, you know the following khri:/ if DRSpells.grabbing_known_khri server_string .sub(/^From the (Subtlety|Finesse|Potence) tree, you know the following khri:/, '') .sub('.', '') .gsub(/\(.+?\)/, '') .split(',') .map(&:strip) .reject { |ability| ability.nil? || ability.empty? } .each { |ability| DRSpells.known_spells[ability] = true } end when /^You have (\d+) available slots?/ DRSpells.grabbing_known_khri = false end server_string end |
.parse(line) ⇒ void
This method returns an undefined value.
Parses a line of server output and updates game state accordingly.
382 383 384 385 386 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 437 438 439 440 441 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 469 470 471 472 473 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 |
# File 'documented/dragonrealms/drinfomon/drparser.rb', line 382 def self.parse(line) check_events(line) begin case line when Pattern::InventoryGetStart GameObj.clear_inv @parsing_inventory_get = true when Pattern::GenderAgeCircle DRStats.gender = Regexp.last_match[:gender] DRStats.age = Regexp.last_match[:age].to_i DRStats.circle = Regexp.last_match[:circle].to_i when Pattern::NameRaceGuild DRStats.race = Regexp.last_match[:race] DRStats.guild = Regexp.last_match[:guild] when Pattern::EncumbranceValue DRStats.encumbrance = Regexp.last_match[:encumbrance] when Pattern::LuckValue DRStats.luck = Regexp.last_match[:luck].to_i when Pattern::StatValue line.scan(Pattern::StatValue) do |stat, value| DRStats.send("#{stat.downcase}=", value.to_i) end when Pattern::TDPValue DRStats.tdps = Regexp.last_match(1).to_i # CharSettings['Stats'] = DRStats.serialize when Pattern::BalanceValue DRStats.balance = DR_BALANCE_VALUES.index(Regexp.last_match[:balance]) when Pattern::RoomPlayersEmpty DRRoom.pcs = [] when Pattern::RoomPlayers DRRoom.pcs = find_pcs(Regexp.last_match(1).dup) DRRoom.pcs_prone = find_pcs_prone(Regexp.last_match(1).dup) DRRoom.pcs_sitting = find_pcs_sitting(Regexp.last_match(1).dup) when Pattern::RoomObjs DRRoom.npcs = find_npcs(Regexp.last_match(1).dup) UserVars.npcs = DRRoom.npcs DRRoom.dead_npcs = find_dead_npcs(Regexp.last_match(1).dup) DRRoom.room_objs = find_objects(Regexp.last_match(1).dup) when Pattern::RoomObjsEmpty DRRoom.npcs = [] DRRoom.dead_npcs = [] DRRoom.room_objs = [] when Pattern::GroupMembersEmpty DRRoom.group_members = [] when Pattern::GroupMembers DRRoom.group_members << Regexp.last_match(1) when Pattern::BriefExpOn, Pattern::BriefExpOff skill = Regexp.last_match[:skill] rank = Regexp.last_match[:rank].to_i rate = Regexp.last_match[:rate].to_i > 0 ? Regexp.last_match[:rate] : DR_LEARNING_RATES.index(Regexp.last_match[:rate]) percent = Regexp.last_match[:percent] DRSkill.update(skill, rank, rate, percent) when Pattern::ExpClearMindstate skill = Regexp.last_match[:skill] DRSkill.clear_mind(skill) when Pattern::ExpColumns line.scan(Pattern::ExpColumns) do |skill_value, rank_value, percent_value, rate_as_word| rate_as_number = DR_LEARNING_RATES.index(rate_as_word) # convert word to number DRSkill.update(skill_value, rank_value, rate_as_number, percent_value) end when Pattern::ExpModsStart @parsing_exp_mods_output = true DRSkill.exp_modifiers.clear when Pattern::SpellBookFormat # Parse `toggle spellbook` command DRSpells.spellbook_format = Regexp.last_match[:format] when Pattern::KnownSpellsStart DRSpells.grabbing_known_spells = true DRSpells.known_spells.clear() DRSpells.known_feats.clear() DRSpells.spellbook_format = 'non-column' # assume original format when Pattern::BarbarianAbilitiesStart DRSpells. = true DRSpells.known_spells.clear() DRSpells.known_feats.clear() when Pattern::ThiefKhriStart DRSpells.grabbing_known_khri = true DRSpells.known_spells.clear() DRSpells.known_feats.clear() when Pattern::PlayedAccount if Account.name.nil? Account.name = Regexp.last_match[:account].upcase end when Pattern::PlayedSubscription matches = Regexp.last_match if Account.subscription.nil? Account.subscription = matches[:subscription].gsub('Basic', 'Normal').gsub('F2P', 'Free').gsub('Platinum', 'Premium').upcase end UserVars.account_type = matches[:subscription].gsub('Basic', 'Normal').gsub('F2P', 'Free').upcase if Account.subscription == 'PREMIUM' || XMLData.game == 'DRX' || XMLData.game == 'DRF' UserVars.premium = true else UserVars.premium = false end when Pattern::LastLogoff matches = Regexp.last_match month = Date::ABBR_MONTHNAMES.find_index(matches[:month]) weekdays = [nil, 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] dst_check = matches[:day].to_i - weekdays.find_index(matches[:weekday]) if month.between?(4, 10) || (month == 3 && dst_check >= 7) || (month == 11 && dst_check < 0) tz = '-0400' else tz = '-0500' end $last_logoff = Time.new(matches[:year].to_i, month, matches[:day].to_i, matches[:hour].to_i, matches[:minute].to_i, matches[:second].to_i, tz).getlocal when Pattern::RoomIDOff put("flag showroomid on") respond("Lich requires ShowRoomID to be ON for mapping to work, please do not turn this off.") respond("If you wish to hide the Real ID#, you can toggle it off by doing ;display flaguid") when Pattern::Rested_EXP matches = Regexp.last_match DRSkill.update_rested_exp(matches[:stored].strip, matches[:usable].strip, matches[:refresh].strip) when Pattern::Rested_EXP_F2P # f2p characters without brain boost don't get rested exp DRSkill.update_rested_exp('none', 'none', 'none') when Pattern::TDPValue_XPWindow matches = Regexp.last_match DRStats.tdps = matches[:tdp].to_i when Pattern::FavorValue_XPWindow matches = Regexp.last_match DRStats.favors = matches[:favor].to_i else :noop end populate_inventory_get(line) if @parsing_inventory_get check_exp_mods(line) if @parsing_exp_mods_output (line) if DRSpells. check_known_thief_khri(line) if DRSpells.grabbing_known_khri check_known_spells(line) if DRSpells.grabbing_known_spells rescue StandardError respond "--- Lich: error: DRParser.parse: #{$!}" respond "--- Lich: error: line: #{line}" Lich.log "error: DRParser.parse: #{$!}\n\t#{$!.backtrace.join("\n\t")}" Lich.log "error: line: #{line}\n\t" end end |
.populate_inventory_get(server_string) ⇒ String
Populates the inventory based on the server string received.
96 97 98 99 100 101 102 103 104 105 106 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 |
# File 'documented/dragonrealms/drinfomon/drparser.rb', line 96 def self.populate_inventory_get(server_string) case server_string when %r{^<output class=""/>} if @parsing_inventory_get @parsing_inventory_get = false end else # This block parses a single line from the output of the `inv search <string>` verb, # which lists items on your character. Each line is an XML-like string. # Example: <d cmd='get #12345'>a small pouch</d> if @parsing_inventory_get && server_string.strip.start_with?('<d cmd=') # The server string is an XML fragment, so we wrap it in a root element to make it parsable. document = REXML::Document.new("<root>#{server_string.strip}</root>") d_element = document.root.elements["d"] return unless d_element # Extract the item name from the text inside the <d> tag. # Normalize it by lowercasing and removing leading articles ('a', 'an', 'some'). item_name = d_element.text.sub(/^(?:a|an|some)\s/, '').strip # Extract the command and the unique item ID from the 'cmd' attribute. cmd = d_element.attributes["cmd"].downcase.strip id_match = /get (?<itemID>#\d+)(?: in (?<container1>#\d+|[^']+))?(?: in (?<container2>#\d+|[^']+))?/.match(cmd) # <!-- Regex to capture item and container IDs: cmd='get (?<itemID>#\d+)(?: in (?<container1>#\d+|[^']+))?(?: in (?<container2>#\d+|[^']+))?' --> # <d cmd='get #8286821 in #8286816 in #8286762'>A papyrus parchment</d> is in a black winter cloak crafted from thick cashmere, which is in a scuffed traveler's pack. # <d cmd='get #8735861 in #8735860 in watery portal'>Some arzumodine cloth</d> is in a lumpy canvas sack, which is in an effervescent eddy of honey-hued light captured by a sungold frame. # <d cmd='get #8761784'>A seagull feather quill with dyed snowy white barbs</d> is lying at your feet. # <d cmd='get #8761784'>A seagull feather quill with dyed snowy white barbs</d> is in your right hand. if id_match id = id_match[:itemID].strip.delete('#').to_s noun = nil # This isn't exposed in the DR XML stream name = item_name container1 = id_match[:container1].strip.delete('#') if id_match[:container1] container2 = id_match[:container2].strip.delete('#') if id_match[:container2] container = container1 || nil before = cmd after = nil # Store the parsed item information. # DRItems.update_item(item, id, cmd, full_description) Lich.log("DRParser: Adding inventory item - ID: #{id}, Noun: #{noun}, Name: #{name}, Container: #{container}, Before: #{before}, After: #{after}") GameObj.new_inv(id, noun, name, container, before, after) if container2 before = cmd.sub(/get \#\d+ in/, "get") name = nil # We don't know the name of the item in container2 GameObj.new_inv(container1, noun, name, container2, before, after) end end end end server_string end |