/lich.rbw
Ruby | 11183 lines | 10826 code | 253 blank | 104 comment | 1089 complexity | 4c9d08048fd30f391f1b3725a3951214 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- #!/usr/bin/env ruby
- #####
- # Copyright (C) 2005-2006 Murray Miron
- # All rights reserved.
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following conditions
- # are met:
- #
- # Redistributions of source code must retain the above copyright
- # notice, this list of conditions and the following disclaimer.
- #
- # Redistributions in binary form must reproduce the above copyright
- # notice, this list of conditions and the following disclaimer in the
- # documentation and/or other materials provided with the distribution.
- #
- # Neither the name of the organization nor the names of its contributors
- # may be used to endorse or promote products derived from this software
- # without specific prior written permission.
- #
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- #####
- =begin
- Lich is currently maintained by Tillmen (tillmen@lichproject.org)
- =end
- # I hate Windows...
- begin
- $stderr.write(' ')
- rescue
- require 'stringio'
- $stderr = StringIO.new('')
- $stdout = StringIO.new('')
- STDERR = $stderr rescue()
- STDOUT = $stderr rescue()
- end
- $version = '4.4.9'
- if ARGV.any? { |arg| (arg == '-h') or (arg == '--help') }
- puts 'Usage: lich [OPTION]'
- puts ''
- puts 'Options are:'
- puts ' -h, --help Display this list.'
- puts ' -V, --version Display the program version number and credits.'
- puts ''
- puts ' -d, --directory Set the main Lich program directory.'
- puts ' --script-dir Set the directoy where Lich looks for scripts.'
- puts ' --data-dir Set the directory where Lich will store script data.'
- puts ' --temp-dir Set the directory where Lich will store temporary files.'
- puts ''
- puts ' -w, --wizard Run in Wizard mode (default)'
- puts ' -s, --stormfront Run in StormFront mode.'
- puts ' --avalon Run in Avalon mode.'
- puts ''
- puts ' --gemstone Connect to the Gemstone IV Prime server (default).'
- puts ' --dragonrealms Connect to the DragonRealms server.'
- puts ' --platinum Connect to the Gemstone IV/DragonRealms Platinum server.'
- puts ' -g, --game Set the IP address and port of the game. See example below.'
- puts ''
- puts ' --install Edits the Windows/WINE registry so that Lich is started when logging in using the website or SGE.'
- puts ' --uninstall Removes Lich from the registry.'
- puts ''
- puts 'The majority of Lich\'s built-in functionality was designed and implemented with Simutronics MUDs in mind (primarily Gemstone IV): as such, many options/features provided by Lich may not be applicable when it is used with a non-Simutronics MUD. In nearly every aspect of the program, users who are not playing a Simutronics game should be aware that if the description of a feature/option does not sound applicable and/or compatible with the current game, it should be assumed that the feature/option is not. This particularly applies to in-script methods (commands) that depend heavily on the data received from the game conforming to specific patterns (for instance, it\'s extremely unlikely Lich will know how much "health" your character has left in a non-Simutronics game, and so the "health" script command will most likely return a value of 0).'
- puts ''
- puts 'The level of increase in efficiency when Lich is run in "bare-bones mode" (i.e. started with the --bare argument) depends on the data stream received from a given game, but on average results in a moderate improvement and it\'s recommended that Lich be run this way for any game that does not send "status information" in a format consistent with Simutronics\' GSL or XML encoding schemas.'
- puts ''
- puts ''
- puts 'Examples:'
- puts ' lich -w -d /usr/bin/lich/ (run Lich in Wizard mode using the dir \'/usr/bin/lich/\' as the program\'s home)'
- puts ' lich -g gs3.simutronics.net:4000 (run Lich using the IP address \'gs3.simutronics.net\' and the port number \'4000\')'
- puts ' lich --script-dir /mydir/scripts (run Lich with its script directory set to \'/mydir/scripts\')'
- puts ' lich --bare -g skotos.net:5555 (run in bare-bones mode with the IP address and port of the game set to \'skotos.net:5555\')'
- puts ''
- exit
- end
- if ARGV.any? { |arg| (arg == '-v') or (arg == '--version') }
- # puts "The Lich, version #{$version}"
- puts ' (an implementation of the Ruby interpreter by Yukihiro Matsumoto designed to be a \'script engine\' for text-based MUDs)'
- puts ''
- puts '- The Lich program and all material collectively referred to as "The Lich project" is copyright (C) 2005-2006 Murray Miron.'
- puts '- The Gemstone IV and DragonRealms games are copyright (C) Simutronics Corporation.'
- puts '- The Wizard front-end and the StormFront front-end are also copyrighted by the Simutronics Corporation.'
- puts '- Ruby is (C) Yukihiro \'Matz\' Matsumoto.'
- puts '- Inno Setup Compiler 5 is (C) 1997-2005 Jordan Russell (used for the Windows installation package).'
- puts ''
- puts 'Thanks to all those who\'ve reported bugs and helped me track down problems on both Windows and Linux.'
- exit
- end
- ARGV.delete_if { |arg| arg =~ /launcher\.exe/i }
- if arg = ARGV.find { |a| (a == '-d') or (a == '--directory') }
- i = ARGV.index(arg)
- ARGV.delete_at(i)
- $lich_dir = ARGV[i]
- ARGV.delete_at(i)
- unless $lich_dir and File.exists?($lich_dir)
- $stdout.puts "warning: given Lich directory does not exist: #{$lich_dir}"
- $lich_dir = nil
- end
- end
- unless $lich_dir
- Dir.chdir(File.dirname($PROGRAM_NAME))
- $lich_dir = Dir.pwd
- end
- $lich_dir = $lich_dir.tr('\\', '/')
- $lich_dir += '/' unless $lich_dir[-1..-1] == '/'
- Dir.chdir($lich_dir)
- if arg = ARGV.find { |a| a == '--script-dir' }
- i = ARGV.index(arg)
- ARGV.delete_at(i)
- $script_dir = ARGV[i]
- ARGV.delete_at(i)
- if $script_dir and File.exists?($script_dir)
- $script_dir = $script_dir.tr('\\', '/')
- $script_dir += '/' unless $script_dir[-1..-1] == '/'
- else
- $stdout.puts "warning: given script directory does not exist: #{$script_dir}"
- $script_dir = nil
- end
- end
- unless $script_dir
- $script_dir = "#{$lich_dir}scripts/"
- unless File.exists?($script_dir)
- $stdout.puts "info: creating directory: #{$script_dir}"
- Dir.mkdir($script_dir)
- end
- end
- if arg = ARGV.find { |a| a == '--data-dir' }
- i = ARGV.index(arg)
- ARGV.delete_at(i)
- $data_dir = ARGV[i]
- ARGV.delete_at(i)
- if $data_dir and File.exists?($data_dir)
- $data_dir = $data_dir.tr('\\', '/')
- $data_dir += '/' unless $data_dir[-1..-1] == '/'
- else
- $stdout.puts "warning: given data directory does not exist: #{$data_dir}"
- $data_dir = nil
- end
- end
- unless $data_dir
- $data_dir = "#{$lich_dir}data/"
- unless File.exists?($data_dir)
- $stdout.puts "info: creating directory: #{$data_dir}"
- Dir.mkdir($data_dir)
- end
- end
- if arg = ARGV.find { |a| a == '--temp-dir' }
- i = ARGV.index(arg)
- ARGV.delete_at(i)
- $temp_dir = ARGV[i]
- ARGV.delete_at(i)
- if $temp_dir and File.exists?($temp_dir)
- $temp_dir = $temp_dir.tr('\\', '/')
- $temp_dir += '/' unless $temp_dir[-1..-1] == '/'
- else
- $stdout.puts "warning: given temp directory does not exist: #{$temp_dir}"
- $temp_dir = nil
- end
- end
- unless $temp_dir
- $temp_dir = "#{$lich_dir}temp/"
- unless File.exists?($temp_dir)
- $stdout.puts "info: creating directory: #{$temp_dir}"
- Dir.mkdir($temp_dir)
- end
- end
- if arg = ARGV.find { |a| a == '--hosts-dir' }
- i = ARGV.index(arg)
- ARGV.delete_at(i)
- hosts_dir = ARGV[i]
- ARGV.delete_at(i)
- if hosts_dir and File.exists?(hosts_dir)
- hosts_dir = hosts_dir.tr('\\', '/')
- hosts_dir += '/' unless hosts_dir[-1..-1] == '/'
- else
- $stdout.puts "warning: given hosts directory does not exist: #{hosts_dir}"
- hosts_dir = nil
- end
- else
- hosts_dir = nil
- end
- num = Time.now.to_i
- debug_filename = "#{$temp_dir}debug-#{num}.txt"
- debug_filename = "#{$temp_dir}debug-#{num+=1}.txt" while File.exists?(debug_filename)
- $stderr = File.open(debug_filename, 'w')
- $stderr.sync = true
- $stderr.puts "info: #{Time.now}"
- $stderr.puts "info: Lich #{$version}"
- $stderr.puts "info: Ruby #{RUBY_VERSION}"
- $stderr.puts "info: #{RUBY_PLATFORM}"
- $stderr.puts "info: $lich_dir: #{$lich_dir}"
- $stderr.puts "info: $script_dir: #{$script_dir}"
- $stderr.puts "info: $data_dir: #{$data_dir}"
- $stderr.puts "info: $temp_dir: #{$temp_dir}"
- #
- # delete cache and debug files that are more than 24 hours old
- #
- Dir.entries($lich_dir).delete_if { |fn| (fn == '.') or (fn == '..') }.each { |filename|
- if filename =~ /^cache-([0-9]+).txt$/
- if $1.to_i + 86400 < Time.now.to_i
- File.delete($lich_dir + filename) rescue()
- end
- end
- }
- Dir.entries($temp_dir).delete_if { |fn| (fn == '.') or (fn == '..') }.each { |filename|
- if filename =~ /^(?:cache|debug)-([0-9]+).txt$/
- if $1.to_i + 86400 < Time.now.to_i
- File.delete($temp_dir + filename) rescue()
- end
- end
- }
- =begin
- # fixme: Somehow this makes gtk2 fail to load?
- begin
- require 'rubygems'
- rescue LoadError
- $stdout.puts "warning: failed to load rubygems: #{$!}"
- $stderr.puts "warning: failed to load rubygems: #{$!}"
- rescue
- $stdout.puts "warning: failed to load rubygems: #{$!}"
- $stderr.puts "warning: failed to load rubygems: #{$!}"
- end
- =end
- require 'time'
- require 'socket'
- include Socket::Constants
- require 'rexml/document'
- require 'rexml/streamlistener'
- include REXML
- require 'stringio'
- require 'dl'
- require 'zlib'
- require 'drb'
- require 'resolv'
- require 'digest/md5'
- # stupid workaround for Windows to avoid 10 second or so freeze when lnet starts up
- # no idea why this works
- if (RUBY_PLATFORM =~ /mingw|win/i) and (RUBY_PLATFORM !~ /darwin/i)
- begin
- require 'openssl'
- OpenSSL::PKey::RSA.new(512)
- rescue LoadError
- $stderr.puts "warning: failed to load openssl: #{$!}"
- rescue
- $stderr.puts "warning: failed to load openssl: #{$!}"
- end
- end
- begin
- require 'gtk2'
- module Gtk
- @@pending_blocks = Array.new
- def Gtk.queue &block
- @@pending_blocks.push(block)
- GLib::Timeout.add(1) { Gtk.do_queue; false }
- end
- def Gtk.do_queue
- if block = @@pending_blocks.shift
- begin
- block.call
- rescue
- respond "error in Gtk.queue: #{$!}"
- $stderr.puts "error in Gtk.queue: #{$!}"
- $stderr.puts $!.backtrace
- rescue SyntaxError
- respond "error in Gtk.queue: #{$!}"
- $stderr.puts "error in Gtk.queue: #{$!}"
- $stderr.puts $!.backtrace
- rescue SystemExit
- nil
- rescue SecurityError
- respond "error in Gtk.queue: #{$!}"
- $stderr.puts "error in Gtk.queue: #{$!}"
- $stderr.puts $!.backtrace
- rescue ThreadError
- respond "error in Gtk.queue: #{$!}"
- $stderr.puts "error in Gtk.queue: #{$!}"
- $stderr.puts $!.backtrace
- rescue SystemStackError
- respond "error in Gtk.queue: #{$!}"
- $stderr.puts "error in Gtk.queue: #{$!}"
- $stderr.puts $!.backtrace
- rescue Exception
- respond "error in Gtk.queue: #{$!}"
- $stderr.puts "error in Gtk.queue: #{$!}"
- $stderr.puts $!.backtrace
- rescue ScriptError
- respond "error in Gtk.queue: #{$!}"
- $stderr.puts "error in Gtk.queue: #{$!}"
- $stderr.puts $!.backtrace
- rescue LoadError
- respond "error in Gtk.queue: #{$!}"
- $stderr.puts "error in Gtk.queue: #{$!}"
- $stderr.puts $!.backtrace
- rescue NoMemoryError
- respond "error in Gtk.queue: #{$!}"
- $stderr.puts "error in Gtk.queue: #{$!}"
- $stderr.puts $!.backtrace
- rescue
- respond "error in Gtk.queue: #{$!}"
- $stderr.puts "error in Gtk.queue: #{$!}"
- $stderr.puts $!.backtrace
- end
- end
- end
- end
- $warned_depreciated_gtk_do = false
- class BeBackwardCompatible1
- def do(what)
- unless $warned_depreciated_gtk_do
- unless script_name = Script.self.name
- script_name = 'unknown script'
- end
- respond "--- warning: #{script_name} is using depreciated method $gtk.do"
- $warned_depreciated_gtk_do = true
- end
- Gtk.queue { eval(what) }
- end
- end
- $gtk = BeBackwardCompatible1.new
- $warned_depreciated_lich_do = false
- class BeBackwardCompatible2
- def do(what)
- unless $warned_depreciated_lich_do
- unless script_name = Script.self.name
- script_name = 'unknown script'
- end
- respond "--- warning: #{script_name} is using depreciated method $lich.do"
- $warned_depreciated_lich_do = true
- end
- eval(what)
- end
- end
- $lich = BeBackwardCompatible2.new
- HAVE_GTK = true
- $stderr.puts "info: HAVE_GTK: true"
- rescue LoadError
- HAVE_GTK = false
- $stdout.puts "warning: failed to load GTK bindings: #{$!}"
- $stderr.puts "warning: failed to load GTK bindings: #{$!}"
- rescue
- HAVE_GTK = false
- $stdout.puts "warning: failed to load GTK bindings: #{$!}"
- $stderr.puts "warning: failed to load GTK bindings: #{$!}"
- end
- # fixme: warlock
- # fixme: terminal mode
- # fixme: maybe add script dir to load path
- # at_exit { Process.waitall }
- $room_count = 0
- #
- # Allow untrusted scripts to do a few things
- #
- UNTRUSTED_SETTINGS_LOAD = proc { Settings.load }
- UNTRUSTED_SETTINGS_SAVE = proc { Settings.save }
- UNTRUSTED_SETTINGS_SAVE_ALL = proc { Settings.save_all }
- UNTRUSTED_GAMESETTINGS_LOAD = proc { GameSettings.load }
- UNTRUSTED_GAMESETTINGS_SAVE = proc { GameSettings.save }
- UNTRUSTED_GAMESETTINGS_SAVE_ALL = proc { GameSettings.save_all }
- UNTRUSTED_CHARSETTINGS_LOAD = proc { CharSettings.load }
- UNTRUSTED_CHARSETTINGS_SAVE = proc { CharSettings.save }
- UNTRUSTED_CHARSETTINGS_SAVE_ALL = proc { CharSettings.save_all }
- UNTRUSTED_MAP_LOAD = proc { Map.load }
- UNTRUSTED_MAP_LOAD_DAT = proc { Map.load_dat }
- UNTRUSTED_MAP_LOAD_XML = proc { Map.load_xml }
- UNTRUSTED_MAP_SAVE = proc { Map.save }
- UNTRUSTED_MAP_SAVE_XML = proc { Map.save_xml }
- UNTRUSTED_SPELL_LOAD = proc { Spell.load }
- UNTRUSTED_START_SCRIPT = proc { |script_name,cli_vars,force| start_script(script_name,cli_vars,force) }
- UNTRUSTED_START_EXEC_SCRIPT = proc { |cmd_data,flags| flags = Hash.new unless flags.class == Hash; flags[:trusted] = false; start_exec_script(cmd_data, flags) }
- UNTRUSTED_SCRIPT_EXISTS = proc { |scriptname| Script.exists?(scriptname) }
- UNTRUSTED_UNTAINT = proc { |str| str.untaint }
- UNTRUSTED_USERVARIABLES_METHOD_MISSING = proc { |arg1, arg2| UserVariables.method_missing(arg1, arg2) }
- UNTRUSTED_USERVARIABLES_DELETE = proc { |var_name, type| UserVariables.delete(var_name, type) }
- UNTRUSTED_USERVARIABLES_ADD = proc { |var_name, value, type| UserVariables.add(var_name, value, type) }
- UNTRUSTED_USERVARIABLES_CHANGE = proc { |var_name, value, type| UserVariables.change(var_name, value, type) }
- UNTRUSTED_USERVARIABLES_SAVE = proc { UserVariables.save }
- UNTRUSTED_SPELL_RANKS_LOAD = proc { SpellRanks.load }
- UNTRUSTED_SPELL_RANKS_SAVE = proc { SpellRanks.save }
- UNTRUSTED_SCRIPT_LOG = proc { |data| Script.log(data) }
- UNTRUSTED_GAMEOBJ_LOAD_DATA = proc { GameObj.load_data }
- JUMP = Exception.exception('JUMP')
- JUMP_ERROR = Exception.exception('JUMP_ERROR')
- DIRMAP = {
- 'out' => 'K',
- 'ne' => 'B',
- 'se' => 'D',
- 'sw' => 'F',
- 'nw' => 'H',
- 'up' => 'I',
- 'down' => 'J',
- 'n' => 'A',
- 'e' => 'C',
- 's' => 'E',
- 'w' => 'G',
- }
- SHORTDIR = {
- 'out' => 'out',
- 'northeast' => 'ne',
- 'southeast' => 'se',
- 'southwest' => 'sw',
- 'northwest' => 'nw',
- 'up' => 'up',
- 'down' => 'down',
- 'north' => 'n',
- 'east' => 'e',
- 'south' => 's',
- 'west' => 'w',
- }
- LONGDIR = {
- 'out' => 'out',
- 'ne' => 'northeast',
- 'se' => 'southeast',
- 'sw' => 'southwest',
- 'nw' => 'northwest',
- 'up' => 'up',
- 'down' => 'down',
- 'n' => 'north',
- 'e' => 'east',
- 's' => 'south',
- 'w' => 'west',
- }
- MINDMAP = {
- 'clear as a bell' => 'A',
- 'fresh and clear' => 'B',
- 'clear' => 'C',
- 'muddled' => 'D',
- 'becoming numbed' => 'E',
- 'numbed' => 'F',
- 'must rest' => 'G',
- 'saturated' => 'H',
- }
- ICONMAP = {
- 'IconKNEELING' => 'GH',
- 'IconPRONE' => 'G',
- 'IconSITTING' => 'H',
- 'IconSTANDING' => 'T',
- 'IconSTUNNED' => 'I',
- 'IconHIDDEN' => 'N',
- 'IconINVISIBLE' => 'D',
- 'IconDEAD' => 'B',
- 'IconWEBBED' => 'C',
- 'IconJOINED' => 'P',
- 'IconBLEEDING' => 'O',
- }
- class NilClass
- def dup
- nil
- end
- def method_missing(*args)
- nil
- end
- def split(*val)
- Array.new
- end
- def to_s
- ""
- end
- def strip
- ""
- end
- def +(val)
- val
- end
- def closed?
- true
- end
- end
- class Array
- def method_missing(*usersave)
- self
- end
- end
- class LimitedArray < Array
- attr_accessor :max_size
- def initialize(size=0, obj=nil)
- @max_size = 200
- super
- end
- def push(line)
- self.shift while self.length >= @max_size
- super
- end
- def shove(line)
- push(line)
- end
- def history
- Array.new
- end
- end
- # fixme: causes slowdown on Windows (maybe)
- class CachedArray < Array
- attr_accessor :min_size, :max_size
- def initialize(size=0, obj=nil)
- @min_size = 200
- @max_size = 250
- num = Time.now.to_i-1
- @filename = "#{$temp_dir}cache-#{num}.txt"
- @filename = "#{$temp_dir}cache-#{num+=1}.txt" while File.exists?(@filename)
- @file = File.open(@filename, 'w')
- super
- end
- def push(line)
- if self.length >= @max_size
- @file.puts(self.shift) while (self.length >= @min_size)
- @file.flush
- end
- super
- end
- def history
- @file.flush
- @file.close
- @file = File.open(@filename, 'r')
- h = @file.readlines
- @file.close
- @file = File.open(@filename, 'a')
- h
- end
- end
- module StringFormatting
- def as_time
- sprintf("%d:%02d:%02d", (self / 60).truncate, self.truncate % 60, ((self % 1) * 60).truncate)
- end
- end
- class Numeric
- include StringFormatting
- end
- class TrueClass
- def method_missing(*usersave)
- true
- end
- def to_ary
- nil
- end
- end
- class FalseClass
- def method_missing(*usersave)
- nil
- end
- end
- class String
- # causes an error in File.exists? for Ruby 1.9.2
- # def method_missing(*usersave)
- # ""
- # end
- def silent
- false
- end
- def to_s
- self.dup
- end
- def split_as_list
- string = self
- string.sub!(/^You (?:also see|notice) |^In the .+ you see /, ',')
- string.sub('.','').sub(/ and (an?|some|the)/, ', \1').split(',').reject { |str| str.strip.empty? }.collect { |str| str.lstrip }
- end
- end
- class XMLParser
- attr_reader :mana, :max_mana, :health, :max_health, :spirit, :max_spirit, :last_spirit, :stamina, :max_stamina, :stance_text, :stance_value, :mind_text, :mind_value, :prepared_spell, :encumbrance_text, :encumbrance_full_text, :encumbrance_value, :indicator, :injuries, :injury_mode, :room_count, :room_title, :room_description, :room_exits, :room_exits_string, :familiar_room_title, :familiar_room_description, :familiar_room_exits, :bounty_task, :injury_mode, :server_time, :server_time_offset, :roundtime_end, :cast_roundtime_end, :last_pulse, :level, :next_level_value, :next_level_text, :society_task, :stow_container_id, :name, :game, :in_stream, :player_id, :active_spells
- attr_accessor :send_fake_tags
- @@warned_depreciated_spellfront = false
- include StreamListener
- def initialize
- @buffer = String.new
- @unescape = { 'lt' => '<', 'gt' => '>', 'quot' => '"', 'apos' => "'", 'amp' => '&' }
- @bold = false
- @active_tags = Array.new
- @active_ids = Array.new
- @last_tag = String.new
- @last_id = String.new
- @current_stream = String.new
- @current_style = String.new
- @stow_container_id = nil
- @obj_location = nil
- @obj_exist = nil
- @obj_noun = nil
- @obj_before_name = nil
- @obj_name = nil
- @obj_after_name = nil
- @pc = nil
- @last_obj = nil
- @in_stream = false
- @player_status = nil
- @fam_mode = String.new
- @room_window_disabled = false
- @wound_gsl = String.new
- @scar_gsl = String.new
- @send_fake_tags = false
- #@prompt = String.new
- @nerve_tracker_num = 0
- @nerve_tracker_active = 'no'
- @server_time = Time.now.to_i
- @server_time_offset = 0
- @roundtime_end = 0
- @cast_roundtime_end = 0
- @last_pulse = Time.now.to_i
- @level = 0
- @next_level_value = 0
- @next_level_text = String.new
- @room_count = 0
- @room_title = String.new
- @room_description = String.new
- @room_exits = Array.new
- @room_exits_string = String.new
- @room_changing = true
- @familiar_room_title = String.new
- @familiar_room_description = String.new
- @familiar_room_exits = Array.new
- @bounty_task = String.new
- @society_task = String.new
- @name = String.new
- @game = String.new
- @player_id = String.new
- @mana = 0
- @max_mana = 0
- @health = 0
- @max_health = 0
- @spirit = 0
- @max_spirit = 0
- @last_spirit = nil
- @stamina = 0
- @max_stamina = 0
- @stance_text = String.new
- @stance_value = 0
- @mind_text = String.new
- @mind_value = 0
- @prepared_spell = 'None'
- @encumbrance_text = String.new
- @encumbrance_full_text = String.new
- @encumbrance_value = 0
- @indicator = Hash.new
- @injuries = {'back' => {'scar' => 0, 'wound' => 0}, 'leftHand' => {'scar' => 0, 'wound' => 0}, 'rightHand' => {'scar' => 0, 'wound' => 0}, 'head' => {'scar' => 0, 'wound' => 0}, 'rightArm' => {'scar' => 0, 'wound' => 0}, 'abdomen' => {'scar' => 0, 'wound' => 0}, 'leftEye' => {'scar' => 0, 'wound' => 0}, 'leftArm' => {'scar' => 0, 'wound' => 0}, 'chest' => {'scar' => 0, 'wound' => 0}, 'leftFoot' => {'scar' => 0, 'wound' => 0}, 'rightFoot' => {'scar' => 0, 'wound' => 0}, 'rightLeg' => {'scar' => 0, 'wound' => 0}, 'neck' => {'scar' => 0, 'wound' => 0}, 'leftLeg' => {'scar' => 0, 'wound' => 0}, 'nsys' => {'scar' => 0, 'wound' => 0}, 'rightEye' => {'scar' => 0, 'wound' => 0}}
- @injury_mode = 0
- @active_spells = Hash.new
- end
- def reset
- @active_tags = Array.new
- @active_ids = Array.new
- @current_stream = String.new
- @current_style = String.new
- end
- def make_wound_gsl
- @wound_gsl = sprintf("0b0%02b%02b%02b%02b%02b%02b%02b%02b%02b%02b%02b%02b%02b%02b",@injuries['nsys']['wound'],@injuries['leftEye']['wound'],@injuries['rightEye']['wound'],@injuries['back']['wound'],@injuries['abdomen']['wound'],@injuries['chest']['wound'],@injuries['leftHand']['wound'],@injuries['rightHand']['wound'],@injuries['leftLeg']['wound'],@injuries['rightLeg']['wound'],@injuries['leftArm']['wound'],@injuries['rightArm']['wound'],@injuries['neck']['wound'],@injuries['head']['wound'])
- end
- def make_scar_gsl
- @scar_gsl = sprintf("0b0%02b%02b%02b%02b%02b%02b%02b%02b%02b%02b%02b%02b%02b%02b",@injuries['nsys']['scar'],@injuries['leftEye']['scar'],@injuries['rightEye']['scar'],@injuries['back']['scar'],@injuries['abdomen']['scar'],@injuries['chest']['scar'],@injuries['leftHand']['scar'],@injuries['rightHand']['scar'],@injuries['leftLeg']['scar'],@injuries['rightLeg']['scar'],@injuries['leftArm']['scar'],@injuries['rightArm']['scar'],@injuries['neck']['scar'],@injuries['head']['scar'])
- end
- def parse(line)
- @buffer.concat(line)
- loop {
- if str = @buffer.slice!(/^[^<]+/)
- text(str.gsub(/&(lt|gt|quot|apos|amp)/) { @unescape[$1] })
- elsif str = @buffer.slice!(/^<\/[^<]+>/)
- element = /^<\/([^\s>\/]+)/.match(str).captures.first
- tag_end(element)
- elsif str = @buffer.slice!(/^<[^<]+>/)
- element = /^<([^\s>\/]+)/.match(str).captures.first
- attributes = Hash.new
- str.scan(/([A-z][A-z0-9_\-]*)=(["'])(.*?)\2/).each { |attr| attributes[attr[0]] = attr[2] }
- tag_start(element, attributes)
- tag_end(element) if str =~ /\/>$/
- else
- break
- end
- }
- end
- def tag_start(name, attributes)
- begin
- @active_tags.push(name)
- @active_ids.push(attributes['id'].to_s)
- if name == 'dialogData' and attributes['id'] == 'ActiveSpells' and attributes['clear'] == 't'
- @active_spells.clear
- elsif name == 'resource' or name == 'nav'
- nil
- elsif name == 'pushStream'
- @in_stream = true
- @current_stream = attributes['id'].to_s
- GameObj.clear_inv if attributes['id'].to_s == 'inv'
- elsif name == 'popStream'
- @in_stream = false
- if attributes['id'] == 'bounty'
- @bounty_task.strip!
- end
- @current_stream = String.new
- elsif name == 'pushBold'
- @bold = true
- elsif name == 'popBold'
- @bold = false
- elsif (name == 'streamWindow')
- if attributes['id'] == 'room'
- @room_changing = true
- elsif (attributes['id'] == 'main') and attributes['subtitle']
- @room_title = '[' + attributes['subtitle'][3..-1] + ']'
- end
- elsif name == 'style'
- @current_style = attributes['id']
- if @room_changing and attributes['id'] == 'roomName'
- @room_count += 1
- $room_count += 1
- @room_changing = false
- end
- elsif name == 'prompt'
- @server_time = attributes['time'].to_i
- @server_time_offset = (Time.now.to_i - @server_time)
- $_CLIENT_.puts "\034GSq#{sprintf('%010d', @server_time)}\r\n" if @send_fake_tags
- elsif (name == 'compDef') or (name == 'component')
- if attributes['id'] == 'room objs'
- GameObj.clear_loot
- GameObj.clear_npcs
- elsif attributes['id'] == 'room players'
- GameObj.clear_pcs
- elsif attributes['id'] == 'room exits'
- @room_exits = Array.new
- @room_exits_string = String.new
- elsif attributes['id'] == 'room desc'
- @room_description = String.new
- GameObj.clear_room_desc
- # elsif attributes['id'] == 'sprite'
- end
- elsif name =~ /^(?:a|right|left)$/
- @obj_exist = attributes['exist']
- @obj_noun = attributes['noun']
- elsif name == 'clearContainer'
- if attributes['id'] == 'stow'
- GameObj.clear_container(@stow_container_id)
- else
- GameObj.clear_container(attributes['id'])
- end
- elsif name == 'deleteContainer'
- GameObj.delete_container(attributes['id'])
- elsif name == 'inv'
- if attributes['id'] == 'stow'
- @obj_location = @stow_container_id
- else
- @obj_location = attributes['id']
- end
- @obj_exist = nil
- @obj_noun = nil
- @obj_name = nil
- @obj_before_name = nil
- @obj_after_name = nil
- elsif name == 'progressBar'
- if attributes['id'] == 'pbarStance'
- @stance_text = attributes['text'].split.first
- @stance_value = attributes['value'].to_i
- $_CLIENT_.puts "\034GSg#{sprintf('%010d', @stance_value)}\r\n" if @send_fake_tags
- elsif attributes['id'] == 'mana'
- last_mana = @mana
- @mana, @max_mana = attributes['text'].scan(/-?\d+/).collect { |num| num.to_i }
- difference = @mana - last_mana
- # fixme: enhancives screw this up
- if (difference == noded_pulse) or (difference == unnoded_pulse) or ( (@mana == @max_mana) and (last_mana + noded_pulse > @max_mana) )
- @last_pulse = Time.now.to_i
- if @send_fake_tags
- $_CLIENT_.puts "\034GSZ#{sprintf('%010d',(@mana+1))}\n"
- $_CLIENT_.puts "\034GSZ#{sprintf('%010d',@mana)}\n"
- end
- end
- if @send_fake_tags
- $_CLIENT_.puts "\034GSV#{sprintf('%010d%010d%010d%010d%010d%010d%010d%010d', @max_health, @health, @max_spirit, @spirit, @max_mana, @mana, @wound_gsl, @scar_gsl)}\r\n"
- end
- elsif attributes['id'] == 'stamina'
- @stamina, @max_stamina = attributes['text'].scan(/-?\d+/).collect { |num| num.to_i }
- elsif attributes['id'] == 'mindState'
- @mind_text = attributes['text']
- @mind_value = attributes['value'].to_i
- $_CLIENT_.puts "\034GSr#{MINDMAP[@mind_text]}\r\n" if @send_fake_tags
- elsif attributes['id'] == 'health'
- @health, @max_health = attributes['text'].scan(/-?\d+/).collect { |num| num.to_i }
- $_CLIENT_.puts "\034GSV#{sprintf('%010d%010d%010d%010d%010d%010d%010d%010d', @max_health, @health, @max_spirit, @spirit, @max_mana, @mana, @wound_gsl, @scar_gsl)}\r\n" if @send_fake_tags
- elsif attributes['id'] == 'spirit'
- @last_spirit = @spirit if @last_spirit
- @spirit, @max_spirit = attributes['text'].scan(/-?\d+/).collect { |num| num.to_i }
- @last_spirit = @spirit unless @last_spirit
- $_CLIENT_.puts "\034GSV#{sprintf('%010d%010d%010d%010d%010d%010d%010d%010d', @max_health, @health, @max_spirit, @spirit, @max_mana, @mana, @wound_gsl, @scar_gsl)}\r\n" if @send_fake_tags
- elsif attributes['id'] == 'nextLvlPB'
- Gift.pulse unless @next_level_text == attributes['text']
- @next_level_value = attributes['value'].to_i
- @next_level_text = attributes['text']
- elsif attributes['id'] == 'encumlevel'
- @encumbrance_value = attributes['value'].to_i
- @encumbrance_text = attributes['text']
- end
- elsif name == 'roundTime'
- @roundtime_end = attributes['value'].to_i
- $_CLIENT_.puts "\034GSQ#{sprintf('%010d', @roundtime_end)}\r\n" if @send_fake_tags
- elsif name == 'castTime'
- @cast_roundtime_end = attributes['value'].to_i
- elsif name == 'indicator'
- @indicator[attributes['id']] = attributes['visible']
- if @send_fake_tags
- if attributes['id'] == 'IconPOISONED'
- if attributes['visible'] == 'y'
- $_CLIENT_.puts "\034GSJ0000000000000000000100000000001\r\n"
- else
- $_CLIENT_.puts "\034GSJ0000000000000000000000000000000\r\n"
- end
- elsif attributes['id'] == 'IconDISEASED'
- if attributes['visible'] == 'y'
- $_CLIENT_.puts "\034GSK0000000000000000000100000000001\r\n"
- else
- $_CLIENT_.puts "\034GSK0000000000000000000000000000000\r\n"
- end
- else
- gsl_prompt = String.new; ICONMAP.keys.each { |icon| gsl_prompt += ICONMAP[icon] if @indicator[icon] == 'y' }
- $_CLIENT_.puts "\034GSP#{sprintf('%-30s', gsl_prompt)}\r\n"
- end
- end
- elsif (name == 'image') and @active_ids.include?('injuries')
- if @injuries.keys.include?(attributes['id'])
- if attributes['name'] =~ /Injury/i
- @injuries[attributes['id']]['wound'] = attributes['name'].slice(/\d/).to_i
- elsif attributes['name'] =~ /Scar/i
- @injuries[attributes['id']]['wound'] = 0
- @injuries[attributes['id']]['scar'] = attributes['name'].slice(/\d/).to_i
- elsif attributes['name'] =~ /Nsys/i
- rank = attributes['name'].slice(/\d/).to_i
- if rank == 0
- @injuries['nsys']['wound'] = 0
- @injuries['nsys']['scar'] = 0
- else
- Thread.new {
- wait_while { dead? }
- action = proc { |server_string|
- if (@nerve_tracker_active == 'maybe')
- if @nerve_tracker_active == 'maybe'
- if server_string =~ /^You/
- @nerve_tracker_active = 'yes'
- @injuries['nsys']['wound'] = 0
- @injuries['nsys']['scar'] = 0
- else
- @nerve_tracker_active = 'no'
- end
- end
- end
- if @nerve_tracker_active == 'yes'
- if server_string =~ /<output class=['"]['"]\/>/
- @nerve_tracker_active = 'no'
- @nerve_tracker_num -= 1
- DownstreamHook.remove('nerve_tracker') if @nerve_tracker_num < 1
- $_CLIENT_.puts "\034GSV#{sprintf('%010d%010d%010d%010d%010d%010d%010d%010d', @max_health, @health, @max_spirit, @spirit, @max_mana, @mana, make_wound_gsl, make_scar_gsl)}\r\n" if @send_fake_tags
- server_string
- elsif server_string =~ /a case of uncontrollable convulsions/
- @injuries['nsys']['wound'] = 3
- nil
- elsif server_string =~ /a case of sporadic convulsions/
- @injuries['nsys']['wound'] = 2
- nil
- elsif server_string =~ /a strange case of muscle twitching/
- @injuries['nsys']['wound'] = 1
- nil
- elsif server_string =~ /a very difficult time with muscle control/
- @injuries['nsys']['scar'] = 3
- nil
- elsif server_string =~ /constant muscle spasms/
- @injuries['nsys']['scar'] = 2
- nil
- elsif server_string =~ /developed slurred speech/
- @injuries['nsys']['scar'] = 1
- nil
- end
- else
- if server_string =~ /<output class=['"]mono['"]\/>/
- @nerve_tracker_active = 'maybe'
- end
- server_string
- end
- }
- @nerve_tracker_num += 1
- DownstreamHook.add('nerve_tracker', action)
- $_SERVER_.puts "#{$cmd_prefix}health\n"
- }
- end
- else
- @injuries[attributes['id']]['wound'] = 0
- @injuries[attributes['id']]['scar'] = 0
- end
- end
- $_CLIENT_.puts "\034GSV#{sprintf('%010d%010d%010d%010d%010d%010d%010d%010d', @max_health, @health, @max_spirit, @spirit, @max_mana, @mana, make_wound_gsl, make_scar_gsl)}\r\n" if @send_fake_tags
- elsif name == 'compass'
- if @current_stream == 'familiar'
- @fam_mode = String.new
- elsif @room_window_disabled
- @room_exits = Array.new
- end
- elsif @room_window_disabled and (name == 'dir') and @active_tags.include?('compass')
- @room_exits.push(LONGDIR[attributes['value']])
- elsif name == 'radio'
- if attributes['id'] == 'injrRad'
- @injury_mode = 0 if attributes['value'] == '1'
- elsif attributes['id'] == 'scarRad'
- @injury_mode = 1 if attributes['value'] == '1'
- elsif attributes['id'] == 'bothRad'
- @injury_mode = 2 if attributes['value'] == '1'
- end
- elsif name == 'label'
- if attributes['id'] == 'yourLvl'
- @level = Stats.level = attributes['value'].slice(/\d+/).to_i
- elsif attributes['id'] == 'encumblurb'
- @encumbrance_full_text = attributes['value']
- elsif @active_tags[-2] == 'dialogData' and @active_ids[-2] == 'ActiveSpells'
- if (name = /^lbl(.+)$/.match(attributes['id']).captures.first) and (value = /^\s*([0-9\:]+)\s*$/.match(attributes['value']).captures.first)
- hour, minute = value.split(':')
- @active_spells[name] = Time.now + (hour.to_i * 3600) + (minute.to_i * 60)
- end
- end
- elsif (name == 'container') and (attributes['id'] == 'stow')
- @stow_container_id = attributes['target'].sub('#', '')
- elsif (name == 'clearStream')
- if attributes['id'] == 'bounty'
- @bounty_task = String.new
- end
- elsif (name == 'playerID')
- @player_id = attributes['id']
- unless $frontend =~ /^(?:wizard|avalon)$/
- LichSettings[@player_id] ||= Hash.new
- if LichSettings[@player_id]['enable_inventory_boxes']
- DownstreamHook.remove('inventory_boxes_off')
- end
- end
- elsif (name == 'app') and (@name = attributes['char'])
- @game = attributes['game']
- if @game.nil? or @game.empty?
- @game = 'unknown'
- end
- unless File.exists?("#{$data_dir}#{@game}")
- Dir.mkdir("#{$data_dir}#{@game}")
- end
- unless File.exists?("#{$data_dir}#{@game}/#{@name}")
- Dir.mkdir("#{$data_dir}#{@game}/#{@name}")
- end
- if $frontend =~ /^(?:wizard|avalon)$/
- $_SERVER_.puts "#{$cmd_prefix}_flag Display Dialog Boxes 0"
- sleep "0.05".to_f
- $_SERVER_.puts "#{$cmd_prefix}_injury 2"
- sleep "0.05".to_f
- # fixme: game name hardcoded as Gemstone IV; maybe doesn't make any difference to the client
- $_CLIENT_.puts "\034GSB0000000000#{attributes['char']}\r\n\034GSA#{Time.now.to_i.to_s}GemStone IV\034GSD\r\n"
- # Sending fake GSL tags to the Wizard FE is disabled until now, because it doesn't accept the tags and just gives errors until initialized with the above line
- @send_fake_tags = true
- # Send all the tags we missed out on
- $_CLIENT_.puts "\034GSV#{sprintf('%010d%010d%010d%010d%010d%010d%010d%010d', @max_health, @health, @max_spirit, @spirit, @max_mana, @mana, make_wound_gsl, make_scar_gsl)}\r\n"
- $_CLIENT_.puts "\034GSg#{sprintf('%010d', @stance_value)}\r\n"
- $_CLIENT_.puts "\034GSr#{MINDMAP[@mind_text]}\r\n"
- gsl_prompt = String.new
- @indicator.keys.each { |icon| gsl_prompt += ICONMAP[icon] if @indicator[icon] == 'y' }
- $_CLIENT_.puts "\034GSP#{sprintf('%-30s', gsl_prompt)}\r\n"
- gsl_prompt = nil
- gsl_exits = String.new
- @room_exits.each { |exit| gsl_exits.concat(DIRMAP[SHORTDIR[exit]].to_s) }
- $_CLIENT_.puts "\034GSj#{sprintf('%-20s', gsl_exits)}\r\n"
- gsl_exits = nil
- $_CLIENT_.puts "\034GSn#{sprintf('%-14s', @prepared_spell)}\r\n"
- $_CLIENT_.puts "\034GSm#{sprintf('%-45s', GameObj.right_hand.name)}\r\n"
- $_CLIENT_.puts "\034GSl#{sprintf('%-45s', GameObj.left_hand.name)}\r\n"
- $_CLIENT_.puts "\034GSq#{sprintf('%010d', @server_time)}\r\n"
- $_CLIENT_.puts "\034GSQ#{sprintf('%010d', @roundtime_end)}\r\n" if @roundtime_end > 0
- end
- $_SERVER_.puts("#{$cmd_prefix}_flag Display Inventory Boxes 1")
- UserVariables.init
- Alias.init
- Favorites.init
- if arg = ARGV.find { |a| a=~ /^\-\-start\-scripts=/ }
- for script_name in arg.sub('--start-scripts=', '').split(',')
- start_script(script_name)
- end
- end
- end
- rescue
- $stdout.puts "--- error: XMLParser.tag_start: #{$!}"
- $stderr.puts "error: XMLParser.tag_start: #{$!}"
- $stderr.puts $!.backtrace
- sleep "0.1".to_f
- reset
- end
- end
- def text(text_string)
- begin
- # fixme: /<stream id="Spells">.*?<\/stream>/m
- # $_CLIENT_.write(text_string) unless ($frontend != 'suks') or (@current_stream =~ /^(?:spellfront|inv|bounty|society)$/) or @active_tags.any? { |tag| tag =~ /^(?:compDef|inv|component|right|left|spell)$/ } or (@active_tags.include?('stream') and @active_ids.include?('Spells')) or (text_string == "\n" and (@last_tag =~ /^(?:popStream|prompt|compDef|dialogData|openDialog|switchQuickBar|component)$/))
- if @active_tags.last == 'prompt'
- #@prompt = text_string
- nil
- elsif @active_tags.include?('right')
- GameObj.new_right_hand(@obj_exist, @obj_noun, text_string)
- $_CLIENT_.puts "\034GSm#{sprintf('%-45s', text_string)}\r\n" if @send_fake_tags
- elsif @active_tags.include?('left')
- GameObj.new_left_hand(@obj_exist, @obj_noun, text_string)
- $_CLIENT_.puts "\034GSl#{sprintf('%-45s', text_string)}\r\n" if @send_fake_tags
- elsif @active_tags.include?('spell')
- @prepared_spell = text_string
- $_CLIENT_.puts "\034GSn#{sprintf('%-14s', text_string)}\r\n" if @send_fake_tags
- elsif @active_tags.include?('compDef') or @active_tags.include?('component')
- if @active_ids.include?('room objs')
- if @active_tags.include?('a')
- if @bold
- GameObj.new_npc(@obj_exist, @obj_noun, text_string)
- else
- GameObj.new_loot(@obj_exist, @obj_noun, text_string)
- end
- elsif (text_string =~ /that (?:is|appears) ([\w\s]+)(?:,| and|\.)/) or (text_string =~ / \(([^\(]+)\)/)
- GameObj.npcs[-1].status = $1
- end
- elsif @active_ids.include?('room players')
- if @active_tags.include?('a')
- @pc = GameObj.new_pc(@obj_exist, @obj_noun, "#{@player_title}#{text_string}", @player_status)
- @player_status = nil
- else
- if @game =~ /^DR/
- GameObj.clear_pcs
- text_string.sub(/^Also here\: /, '').sub(/ and ([^,]+)\./) { ", #{$1}" }.split(', ').each { |player|
- if player =~ / who is (.+)/
- status = $1
- player.sub!(/ who is .+/, '')
- elsif player =~ / \((.+)\)/
- status = $1
- player.sub!(/ \(.+\)/, '')
- else
- status = nil
- end
- noun = player.slice(/\b[A-Z][a-z]+$/)
- if player =~ /the body of /
- player.sub!('the body of ', '')
- if status
- status.concat ' dead'
- else
- status = 'dead'
- end
- end
- if player =~ /a stunned /
- player.sub!('a stunned ', '')
- if status
- status.concat ' stunned'
- else
- status = 'stunned'
- end
- end
- GameObj.new_pc(nil, noun, player, status)
- }
- else
- if (text_string =~ /^ who (?:is|appears) ([\w\s]+)(?:,| and|\.|$)/) or (text_string =~ / \(([\w\s]+)\)(?: \(([\w\s]+)\))?/)
- if @pc.status
- @pc.status.concat " #{$1}"
- else
- @pc.status = $1
- end
- @pc.status.concat " #{$2}" if $2
- end
- if text_string =~ /(?:^Also here: |, )(?:a )?([a-z\s]+)?([\w\s\-!\?',]+)?$/
- @player_status = ($1.strip.gsub('the body of', 'dead')) if $1
- @player_title = $2
- end
- end
- end
- elsif @active_ids.include?('room desc')
- if text_string == '[Room window disabled at this location.]'
- @room_window_disabled = true
- else
- @room_window_disabled = false
- @room_description.concat(text_string)
- if @active_tags.include?('a')
- GameObj.new_room_desc(@obj_exist, @obj_noun, text_string)
- end
- end
- elsif @active_ids.include?('room exits')
- @room_exits_string.concat(text_string)
- @room_exits.push(text_string) if @active_tags.include?('d')
- end
- elsif @active_tags.include?('inv')
- if @active_tags[-1] == 'a'
- @obj_name = text_string
- elsif @obj_name.nil?
- @obj_before_name = text_string.strip
- else
- @obj_after_name = text_string.strip
- end
- elsif @current_stream == 'bounty'
- @bounty_task += text_string
- elsif @current_stream == 'society'
- @society_task = text_string
- elsif (@current_stream == 'inv') and @active_tags.include?('a')
- GameObj.new_inv(@obj_exist, @obj_noun, text_string, nil)
- elsif @current_stream == 'familiar'
- # fixme: familiar room tracking does not (can not?) auto update, status of pcs and npcs isn't tracked at all, titles of pcs aren't tracked
- if @current_style == 'roomName'
- @familiar_room_title = text_string
- @familiar_room_description = String.new
- @familiar_room_exits = Array.new
- GameObj.clear_fam_room_desc
- GameObj.clear_fam_loot
- GameObj.clear_fam_npcs
- GameObj.clear_fam_pcs
- @fam_mode = String.new
- elsif @current_style == 'roomDesc'
- @familiar_room_description.concat(text_string)
- if @active_tags.include?('a')
- GameObj.new_fam_room_desc(@obj_exist, @obj_noun, text_string)
- end
- elsif text_string =~ /^You also see/
- @fam_mode = 'things'
- elsif text_string =~ /^Also here/
- @fam_mode = 'people'
- elsif text_string =~ /Obvious (?:paths|exits)/
- @fam_mode = 'paths'
- elsif @fam_mode == 'things'
- if @active_tags.include?('a')
- if @bold
- GameObj.new_fam_npc(@obj_exist, @obj_noun, text_string)
- else
- GameObj.new_fam_loot(@obj_exist, @obj_noun, text_string)
- end
- end
- # puts 'things: ' + text_string
- elsif @fam_mode == 'people' and @active_tags.include?('a')
- GameObj.new_fam_pc(@obj_exist, @obj_noun, text_string)
- # puts 'people: ' + text_string
- elsif (@fam_mode == 'paths') and @active_tags.include?('a')
- @familiar_room_exits.push(text_string)
- end
- elsif @room_window_disabled
- if @current_style == 'roomDesc'
- @room_description.concat(text_string)
- if @active_tags.include?('a')
- GameObj.new_room_desc(@obj_exist, @obj_noun, text_string)
- end
- elsif text_string =~ /^Obvious (?:paths|exits): (?:none)?$/
- @room_exits_string = text_string.strip
- end
- end
- rescue
- $stdout.puts "--- error: XMLParser.text: #{$!}"
- $stderr.puts "error: XMLParser.text: #{$!}"
- $stderr.puts $!.backtrace
- sleep "0.1".to_f
- reset
- end
- end
- def tag_end(name)
- begin
- if name == 'inv'
- if @obj_exist == @obj_location
- if @obj_after_name == 'is closed.'
- GameObj.delete_container(@stow_container_id)
- end
- elsif @obj_exist
- GameObj.new_inv(@obj_exist, @obj_noun, @obj_name, @obj_location, @obj_before_name, @obj_after_name)
- end
- elsif @send_fake_tags and (@active_ids.last == 'room exits')
- gsl_exits = String.new
- @room_exits.each { |exit| gsl_exits.concat(DIRMAP[SHORTDIR[exit]].to_s) }
- $_CLIENT_.puts "\034GSj#{sprintf('%-20s', gsl_exits)}\r\n"
- gsl_exits = nil
- elsif @room_window_disabled and (name == 'compass')
- @room_window_disabled = false
- @room_description = @room_description.strip
- @room_exits_string.concat " #{@room_exits.join(', ')}" unless @room_exits.empty?
- gsl_exits = String.new
- @room_exits.each { |exit| gsl_exits.concat(DIRMAP[SHORTDIR[exit]].to_s) }
- $_CLIENT_.puts "\034GSj#{sprintf('%-20s', gsl_exits)}\r\n"
- gsl_exits = nil
- end
- @last_tag = @active_tags.pop
- @last_id = @active_ids.pop
- rescue
- $stdout.puts "--- error: XMLParser.tag_end: #{$!}"
- $stderr.puts "error: XMLParser.tag_end: #{$!}"
- $stderr.puts $!.backtrace
- sleep "0.1".to_f
- reset
- end
- end
- # here for backwards compatibility, but spellfront xml isn't sent by the game anymore
- def spellfront
- unless @@warned_depreciated_spellfront
- @@warned_depreciated_spellfront = true
- unless script_name = Script.self.name
- script_name = 'unknown script'
- end
- respond "--- warning: #{script_name} is using depreciated method XMLData.spellfront"
- end
- @active_spells.keys
- end
- end
- XMLData = XMLParser.new
- class UpstreamHook
- @@upstream_hooks ||= Hash.new
- def UpstreamHook.add(name, action)
- unless action.class == Proc
- echo "UpstreamHook: not a Proc (#{action})"
- return false
- end
- @@upstream_hooks[name] = action
- end
- def UpstreamHook.run(client_string)
- for key in @@upstream_hooks.keys
- begin
- client_string = @@upstream_hooks[key].call(client_string)
- rescue
- @@upstream_hooks.delete(key)
- respond "--- Lich: UpstreamHook: #{$!}"
- respond $!.backtrace.first
- end
- return nil if client_string.nil?
- end
- return client_string
- end
- def UpstreamHook.remove(name)
- @@upstream_hooks.delete(name)
- end
- def UpstreamHook.list
- @@upstream_hooks.keys.dup
- end
- end
- class DownstreamHook
- @@downstream_hooks ||= Hash.new
- def DownstreamHook.add(name, action)
- unless action.class == Proc
- echo "DownstreamHook: not a Proc (#{action})"
- return false
- end
- @@downstream_hooks[name] = action
- end
- def DownstreamHook.run(server_string)
- for key in @@downstream_hooks.keys
- begin
- server_string = @@downstream_hooks[key].call(server_string.dup)
- rescue
- @@downstream_hooks.delete(key)
- respond "--- Lich: DownstreamHook: #{$!}"
- respond $!.backtrace.first
- end
- return nil if server_string.nil?
- end
- return server_string
- end
- def DownstreamHook.remove(name)
- @@downstream_hooks.delete(name)
- end
- def DownstreamHook.list
- @@downstream_hooks.keys.dup
- end
- end
- class LichSettings
- @@settings ||= Hash.new
- def LichSettings.load
- if File.exists?("#{$data_dir}lich.sav")
- begin
- File.open("#{$data_dir}lich.sav", 'rb') { |f|
- @@settings = Marshal.load(f.read)['lichsettings']
- }
- rescue
- respond "--- error: LichSettings.load: #{$!}"
- $stderr.puts "error: LichSettings.load: #{$!}"
- $stderr.puts $!.backtrace
- end
- end
- @@settings ||= Hash.new
- end
- def LichSettings.save
- begin
- all_settings = Hash.new
- if File.exists?("#{$data_dir}lich.sav")
- File.open("#{$data_dir}lich.sav", 'rb') { |f| all_settings = Marshal.load(f.read) }
- end
- all_settings['lichsettings'] = @@settings
- File.open("#{$data_dir}lich.sav", 'wb') { |f| f.write(Marshal.dump(all_settings)) }
- true
- rescue
- false
- end
- end
- def LichSettings.list
- settings = @@settings.dup
- if caller.any? { |line| line =~ /start_script|start_exec_script|main_with_queue/ }
- settings.delete('quick_game_entry')
- settings.delete('trusted_scripts')
- settings.delete('untrusted_scripts')
- end
- settings
- end
- def LichSettings.clear
- @@settings = Hash.new
- end
- def LichSettings.[](setting_name)
- if (setting_name == 'quick_game_entry') and (($SAFE != 0) or caller.any? { |line| line =~ /start_script|start_exec_script|main_with_queue/})
- nil
- elsif (setting_name == 'trusted_scripts' or setting_name == 'untrusted_scripts') and (($SAFE != 0) or (caller.any? { |line| line =~ /start_script|start_exec_script|main_with_queue/} and Script.self.name !~ /^updater$|^repository$/))
- @@settings[setting_name].dup
- else
- @@settings[setting_name]
- end
- end
- def LichSettings.[]=(setting_name, setting_value)
- if (setting_name == 'quick_game_entry') and (($SAFE != 0) or caller.any? { |line| line =~ /start_script|start_exec_script|main_with_queue/})
- nil
- elsif (setting_name == 'trusted_scripts' or setting_name == 'untrusted_scripts') and (($SAFE != 0) or (caller.any? { |line| line =~ /start_script|start_exec_script|main_with_queue/} and Script.self.name !~ /^updater$|^repository$/))
- nil
- else
- @@settings[setting_name] = setting_value
- end
- end
- end
- class Settings
- @@settings ||= Hash.new
- @@timestamps ||= Hash.new
- @@md5 ||= Hash.new
- def Settings.load
- if $SAFE == 0
- if script = Script.self
- if script.class == Script
- if script.name =~ /^lich$/i
- respond '--- Lich: If you insist, you may have a script named \'lich\', but it cannot use Settings, because it will conflict with Lich\'s settings.'
- return false
- end
- filename = "#{$data_dir}#{script.name}.sav"
- if File.exists?(filename) and (@@timestamps[script.name].nil? or (@@timestamps[script.name] < File.mtime(filename)))
- begin
- File.open(filename, 'rb') { |f| @@settings[script.name] = Marshal.load(f.read) }
- @@timestamps[script] = File.mtime(filename)
- @@md5[script.name] = Digest::MD5.hexdigest(@@settings[script.name].inspect)
- rescue
- respond "--- error: Settings.load: #{$!}"
- $stderr.puts "error: Settings.load: #{$!}"
- $stderr.puts $!.backtrace
- begin
- filename.concat '~'
- if File.exists?(filename)
- File.open(filename, 'rb') { |f| @@settings[script.name] = Marshal.load(f.read) }
- @@timestamps[script] = File.mtime(filename)
- @@md5[script.name] = Digest::MD5.hexdigest(@@settings[script.name].inspect)
- end
- rescue
- respond "--- error: Settings.load: #{$!}"
- $stderr.puts "error: Settings.load: #{$!}"
- $stderr.puts $!.backtrace
- end
- end
- end
- end
- else
- respond '--- Lich: Settings: cannot identify calling script'
- end
- else
- UNTRUSTED_SETTINGS_LOAD.call
- end
- nil
- end
- def Settings.save
- if $SAFE == 0
- if script = Script.self
- if (script.class == Script) or (script.class == WizardScript)
- if script.name =~ /^lich$/i
- respond '--- Lich: If you insist, you may have a script named \'lich\', but it cannot use Settings, because it will conflict with Lich\'s settings.'
- return false
- end
- filename = "#{$data_dir}#{script.name}.sav"
- @@settings[script.name] ||= Hash.new
- unless (@@settings[script.name].empty? and not File.exists?(filename))
- md5 = Digest::MD5.hexdigest(@@settings[…
Large files files are truncated, but you can click here to view the full file