-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlichrb.c
More file actions
5 lines (5 loc) · 200 KB
/
lichrb.c
File metadata and controls
5 lines (5 loc) · 200 KB
1
2
3
4
5
void Init_lich_frame()
{
rb_compile_string("Lich", rb_str_new2("$\".push(\"sfdialog.rb\", \"sfparser.rb\", \"gemlib.rb\", \"libhooks.rb\", \"libcharsheet.rb\", \"lich-lib.rb\", \"libsettings.rb\", \"lich-libcritter.rb\", \"stringformatting.rb\", \"lich-libmap.rb\", \"lich-libmatch.rb\", \"lich.rb\")\n# Code related to displaying windows in StormFront\n\n\n#\n# defaults are only safeguards against empty values being sent.\n\n\nclass SFDialog\nattr_accessor :type, :id, :title, :location, :height, :width, :resident\nattr_accessor :label, :link, :progressBar\n\nLabel = Struct.new(:id, :value, :top, :left, :align, :width, :height, :anchor_top, :anchor_left, :anchor_right, :justify, :tooltip)\nLink = Struct.new(:id, :value, :cmd, :top, :left, :align, :echo, :width, :anchor_top, :anchor_left, :anchor_right, :justify, :tooltip)\nProgressBar = Struct.new(:id, :value, :text, :top, :left, :align, :width, :height, :anchor_top, :anchor_left, :anchor_right, :justify, :tooltip)\n\ndef initialize\n@label, @link, @progressBar = [],[],[]\n@id = @title = \"lichDialog\" + self.hash.abs.to_s\n@height, @width = '105', '450'\n@type = \"dynamic\"\n@location = 'right'\n@resident = 'true'\nend\n\ndef contains\n@label + @link + @progressBar\nend\n\ndef build_element(key)\nstring = String.new\ninstance_variable_get(key).each { |el|\nstring += %[<#{key.to_s[1..-1]}]\nel.members.each { |attr|\nstring += %[ #{attr.to_s}='#{el[attr].to_s}'] if el[attr]\n}\nstring += \"/>\"\n}\nstring\nend\nprotected :build_element\n\ndef elementData\nary = [:@label, :@link, :@progressBar]\nary.inject(\"\") { |prev,cur| prev + build_element(cur) }\nend\n\ndef open\nputs sprintf(%[<openDialog type='%s' id='%s' title='%s' location='%s' height='%s' width='%s' resident='%s'/>],\n *([@type,@id,@title,@location,@height,@width,@resident].collect { |val| val.to_s }) ) if not @open_p\n@open_p = true\nend\n\ndef closed\n@open_p = false\nend\n\ndef update\nopen\nstring = sprintf(%[<dialogData id='%s'>], @id.to_s)\nstring += elementData\nstring += %[</dialogData>]\nputs string\nend\n\ndef attach_label(lbl)\n@label.push(lbl)\n@label.uniq!\nend\n\ndef attach_link(lnk)\n@link.push(lnk)\n@link.uniq!\nend\n\ndef attach_progressbar(pbar)\npbar.value = '50%' if pbar.respond_to? :value=\n@progressBar.push(pbar)\n@progressBar.uniq!\nend\n\ndef init_attachment(obj)\nobj.id = obj.value = \"lichObj\" + obj.hash.abs.to_s unless obj.id or obj.value rescue()\nobj.top = '0' unless obj.top rescue() #if obj.respond_to? :top=\nobj.left = '0' unless obj.left rescue() #if obj.respond_to? :left=\nobj.width = '150' unless obj.width rescue() #if obj.respond_to? :width=\nobj.height = '15' unless obj.height rescue() #if obj.respond_to? :height=\nobj.align = 'n' unless obj.align rescue() #if obj.respond_to? :align=\nobj.cmd = \"daydream #{Char.name} good\" unless obj.cmd rescue() #if defined?(Char) and Char.respond_to? :name and obj.respond_to? :cmd=\nobj.echo = '(no command echo specified)' unless obj.echo rescue() #if obj.respond_to? :echo=\nobj.text = '(no text specified)' unless obj.text rescue() #if obj.respond_to? :text=\nobj.tooltip = 'Lich dialog item.' unless obj.tooltip rescue() #if obj.respond_to? :tooltip=\nend\nprotected :init_attachment\n\ndef attach(*objects)\nobjects.flatten.each { |obj|\ninit_attachment(obj)\ntarget = obj.class.to_s.slice(/\\w+$/o)\nsym = sprintf(\"attach_%s\", target.downcase)\nsend(sym, obj)\n}\nend\n\nend\n\n=begin\n\nsfparser.rb:\n\nRoutines to convert SF metadata to Wizard format,\nas well as update Lich's tracking accordingly.\n\nThe method names correspond to the XML element names\nthat result in triggering them.\n=end\n\nmodule SFMetadata\nDIRMAP = {\n'out' => 'K',\n'ne' => 'B',\n'se' => 'D',\n'sw' => 'F',\n'nw' => 'H',\n'up' => 'I',\n'down' => 'J',\n'u' => 'I',\n'd' => 'J',\n'n' => 'A',\n'e' => 'C',\n's' => 'E',\n'w' => 'G',\n}\nMINDMAP = {\n/^clear as a bell$/io => 'A',\n/^fresh and clear$/io => 'B',\n/^clear$/io => 'C',\n/^muddled$/io => 'D',\n/^becoming numbed$/io => 'E',\n/^numbed$/io => 'F',\n/^fried$/io => 'G',\n/^saturated$/io => 'H',\n}\n@@bold = false\n@@objs = false\n@@streams = []\ndef endline\n@@bold = @@objs = false\nend\n\ndef a(hash={})\n$npcs.push(hash['noun']) if @@bold and @@objs and hash['noun']\nend\n\ndef pushBold(hash={})\n@@bold = true\nend\n\ndef popBold(hash={})\n@@bold = false\nend\ndef pushStream(hash={})\n$room_count += 1 if hash['id'] == 'room'\n@@streams.push(hash['id'])\nend\n\ndef popStream(hash={})\nif hash['id']\n@@streams.delete(hash['id'])\nelse\n@@streams.pop\nend\nend\n\ndef current_stream\n@@streams.last\nend\n\ndef room_objs(hash={})\n@@objs = true\n$npcs = []\n$room_items = []\nend\n\ndef app(hash={})\nChar.init(hash['char'])\nend\ndef dir(hash={})\n$_TAGHASH_['GSj'].concat(DIRMAP[hash['value']])\nend\ndef compass(hash={})\n$_TAGHASH_['GSj'] = String.new\nend\ndef prompt(hash={})\n$_TAGHASH_[\"GSq\"] = hash['time'].to_i\n$_TIMEOFFSET_ = (Time.now.to_i - $_TAGHASH_[\"GSq\"].to_i)\nend\ndef roundTime(hash={})\n$_TAGHASH_[\"GSQ\"] = hash['value']\nend\ndef castTime(hash={})\nroundTime(hash)\nend\ndef right(hash={})\n$_TAGHASH_['GSm'] = hash['noun']\nend\ndef left(hash={})\n$_TAGHASH_['GSl'] = hash['noun']\nend\ndef spell(hash={})\n$_TAGHASH_['GSn'] = /<spell[^>]*?>([\\s\\w]+)<\\/spell>/o.match($_SERVERSTRING_).captures.first\nend\n# (notice that any spaces are replaced by underscores for proper compatibility)\ndef call_id_as_meth(hash={})\nhash['id'].gsub!(' ', '_')\nsend(hash['id'], hash) rescue()\nend\n# and the \"id\" attribute should be used to determine what method applies\n[ :dialogData,\n:progressBar,\n:compDef,\n:component,\n:indicator,\n:label,\n:style,\n].each { |sym| alias_method(sym, :call_id_as_meth) rescue() }\ndef icon_parse(char, hash={})\nif hash['visible'].include?('y')\n$_TAGHASH_['GSP'].concat(char)\nelse\n$_TAGHASH_['GSP'].gsub!(char,'')\nend\nend\n# They all go through icon_parse(). The spaces allow distinguishing between 'GH' and 'G' or 'H'\ndef IconKNEELING(hash={}); icon_parse(' GH ', hash); end\ndef IconPRONE(hash={}); icon_parse(' G ', hash); end\ndef IconSITTING(hash={}); icon_parse(' H ', hash); end\ndef IconSTANDING(hash={}); icon_parse(' T ', hash); end\ndef IconSTUNNED(hash={}); icon_parse(' I ', hash); end\ndef IconHIDDEN(hash={}); icon_parse(' N ', hash); end\ndef IconINVISIBLE(hash={}); icon_parse(' D ', hash); end\ndef IconDEAD(hash={}); icon_parse(' B ', hash); end\ndef IconWEBBED(hash={}); icon_parse(' C ', hash); end\ndef IconJOINED(hash={}); icon_parse(' P ', hash); end\ndef roomName(hash={})\n$roomtitle = $_SERVERSTRING_.gsub(/<[^>]+>/o,'')\nif $roomtitle.include?(',')\n$roomarea = $roomtitle.split(',').first\nend\nend\ndef roomDesc(hash={})\n$roomdescription = $_SERVERSTRING_.slice(/<style id=['\"]roomDesc['\"]\\/>.*<style id=['\"]['\"]\\/>/o).gsub(/<[^>]+>/o,'')\nend\n\ndef room_players(hash={})\n$pcs = $_SERVERSTRING_.scan(/noun=['\"](\\w+)['\"]/o).flatten\nend\n\ndef room_exits(hash={})\n$_PATHSLINE_ = $_SERVERSTRING_.gsub(/<[^>]+>/o, '')\nend\ndef yourLvl(hash={})\nStats.level = hash['value'].slice(/\\d+/o)\nend\ndef image(hash={})\nif hash['id'] =~ /nsys|Hand|Arm|Leg|Eye|back|abdomen|chest|head|neck/o\nStatus.sf_update(hash['id'], hash['name'])\nend\nend\ndef health(hash={})\n$_TAGHASH_['GSX'], $_TAGHASH_['MGSX'] = hash['text'].scan(/\\d+/o)\nend\ndef mana(hash={})\n$_TAGHASH_['GSZ'], $_TAGHASH_['MGSZ'] = hash['text'].scan(/\\d+/o)\nend\ndef spirit(hash={})\n$_TAGHASH_['GSY'], $_TAGHASH_['MGSY'] = hash['text'].scan(/\\d+/o)\nend\ndef stamina(hash={})\n$_TAGHASH_['stamina'], $_TAGHASH_['Mstamina'] = hash['text'].scan(/\\d+/o)\nend\ndef pbarStance(hash={})\n$_TAGHASH_['GSg'] = hash['value']\nend\ndef mindState(hash={})\nMINDMAP.keys.each { |key|\nif hash['text'] =~ key\n$_TAGHASH_['GSr'] = MINDMAP[key]\nreturn\nend\n}\n$_TAGHASH_['GSr'] = 'H'\nend\n\nend\n\nclass FixNum\ndef match(num)\nself == num\nend\nend\n\nclass Gem\ninclude Comparable\ninclude Enumerable\n\nattr_accessor :noun, :value, :description, :geo, :purifiable, :count, :quality\nalias :price :value\nalias :price= :value=\nalias :string :description\nalias :string= :description=\n\n@@hash ||= {}\n\ndef initialize(desc, noun = desc.slice(/[^\\s]+$/))\n@description, @noun = desc, noun\n@@hash[@description] = self\nend\n\ndef to_s\n@noun\nend\n\ndef <=>(gem)\n@value <=> gem.value\nend\n\ndef Gem.list\n@@hash\nend\n\ndef Gem.hash\n@@hash\nend\n\ndef Gem.[](obj)\nname = obj.to_s\nndup = name.dup\nGem.load if @@hash.empty?\n\nunless value = @@hash[name] || @@hash[name.strip.downcase] ||\n @@hash[ndup.sub!(/[^\\s]+\\s*/, '')] ||\n @@hash[ndup.strip.downcase]\nary = @@hash.values.sort\nvalue = ary.find { |gem| gem.noun =~ /\\b#{name}\\b/i } ||\n ary.find { |gem| gem.noun =~ /^#{name}/i } ||\n ary.find { |gem| gem.description =~ /\\b#{name}\\b/i } ||\n ary.find { |gem| gem.description =~ /^#{name}/i }\nend\n\nvalue\nend\n\ndef Gem.load(fname = File.join($data_dir, \"gems.dat\"))\nbindata = File.open(fname, \"rb\") { |f| f.read }\n@@hash = Marshal.load(bindata)\nend\n\ndef Gem.dump(fname = File.join($data_dir, \"gems.dat\"))\nbindata = Marshal.dump(@@hash)\nraise RuntimeError, \"no gem data to dump to file!\" if @@hash.empty?\nFile.open(fname, \"wb\") { |f| f.write bindata }\nend\n\nend\n\nGems = Gem\n\n# Strings typed by the user that Lich recognizes, along with what to do if they're seen. Note that any string not starting with the standard Lich character (default is a semicolon) is ignored without being checked, and that the Lich character is stripped off the user-input before matching begins\n\nmodule UserHooks\nHook.register(/^help[\\s\\w]*$/i) { |str|\ncmdhelp = Hash.new\n\ncmdhelp['wrap'] = <<WRAP\nAs of v3.44, this command is only available in *NIX environments.\n\nLich itself can interpret and execute Wizard, StormFront, and Ruby scripts; this command allows the program to act as a \"middleman\" of sorts to any external program (which is presumably an interpreter -- Python or Perl, for instance). It essentially \"wraps\" itself around the external interpreter, communicating through the external program's STANDARD INPUT and STANDARD OUTPUT.\n\nIf you don't know what \"standard input\" and \"standard output\" are referring to, you likely won't be needing this command; it's meant primarily for advanced end-users who'd rather write scripts in their favorite language than in Ruby. If your curiosity prevails, google \"programming standard input tutorial\" and read to your heart's content.\n\nFor a detailed explanation of how to do this, see the text file \"#{$lich_dir}Wrapping a Script.txt\". For an example using PERL, see the script \"template.pl\" in your Lich script directory. Supported options:\n\n ;wrap {scriptname}.{ext} .... Start a given script. For example: ;wrap myscript.pl\nWRAP\n\ncmdhelp['log'] = <<LOG\nThis command will copy the entire contents of Lich's buffers and caches (which is effectively the entire history of sent and received lines since you logged in) to a file that, unlike the temporary caches, Lich will not delete automatically when you log out.\n\nNote that absolutely all data Lich has been witness to is included, which means raw and unfiltered status data sent by the game to your local client (and in some cases vice versa) will also be included in the resulting text file.\nLOG\n\ncmdhelp['stats'] = <<STATS\nThis just echoes some internal program statistics and current environment info; it's purely for debugging purposes (and to satisfy curiosity).\nSTATS\n\ncmdhelp['magic'] = <<MAGIC\nThis allows you to view Lich's tracking of your currently active spells (and spell-like effects) and their remaining duration(s). Recognized options include:\n\n ;magic help ....... Further information about the related options.\n \n ;magic save ....... Save a copy of your currently active spells and Gift of Lumnis information on Lich's server for later use.\n \n ;magic load ....... Load a copy of your information that you've previously saved on the server. This is primarily of use to people who find themselves going back and forth between computers.\n\n ;magic set {#} {#} . Set the given spell # to the given minutes remaining. For example, `;magic set 406 120' would set 406 (brights) as having 120 minutes remaining.\n \n ;magic clear [#] .. Without a spell number, clears the entire list of currently-active spells. If given a spell #, clears only the given spell.\n \n ;magic reset [#] .. Synonym for clear.\n\nAll of the previous commands are implemented by the `infomonitor.lic' script, and as such, it must be running for both spell tracking and viewing to be usable.\n\nA related feature (but one implemented by a different script) is the automatic spelling up of yourself, a target, or even anyone who comes along and taps you. For info about those and listing all known spell-like effects, refer to `;spellup help'.\nMAGIC\n\ncmdhelp['send'] = <<SEND\nUnlike conventional Wizard and StormFront scripts, what the user types and sends to the game is not (by default) seen by active scripts -- despite it appearing in the user's game window, the mechanics of how Lich works makes it totally invisible to running scripts unless they specifically request to be sent local user commands.\n\nInstead, you can make use of this command: it sends whatever you specify to all currently active scripts; scripts receive this user-defined data in the same exact way they receive game-sent data, so you can use it to \"trick\" scripts into seeing lines from the game that weren't actually sent, to respond to a script's question, or for anything else you can come up with to use it for.\n\nNote that any lines sent with this command are *not* sent to the game; Lich simply passes the data to its scripts and nothing more. Options:\n\n ;send {data} .............. Send all active scripts whatever you enter as {data}\n \n ;send to {script} {data} .. Instead of sending all running scripts data, send it to only the specified script.\n \n ;s ........................ Can be used instead of the whole word \"send.\" Is otherwise identical to the above.\nSEND\n\ncmdhelp['list'] = <<LIST\nThis command lists the currently active scripts; any paused scripts are labeled as such, and the order in which the scripts were originally started is preserved. Can be appreviated as `;l' for convenience.\nLIST\n\ncmdhelp['kill'] = cmdhelp['stop'] = <<KILL\nThis command halts a running script and essentially clears out the resources that were being used by it; options:\n\n ;kill [script] ..... If no script name is given, kills the most recently started script. If a name is given, kills only the specified script.\n \n ;kill all .......... Stops all active scripts; any script that has registered itself with the `no_kill_all' command is ignored by this command.\n \n ;stop .............. Synonym for `;kill', otherwise identical to above.\n \n ;k ................. Also a synonym.\n \n\nNote that there is no way to make a script ignore a targetted or untargetted `;kill' command; only `;kill all' can be set as being ignored. For example, `lichnet.lic' registers itself as needing to ignore a `;kill all', but will be stopped just like any other script by a `;kill lichnet' command.\n\nFor further information on `no_kill_all', refer to `;man no_kill_all'.\nKILL\n\ncmdhelp['pause'] = <<PAUSE\nThis command will temporarily pause a script; for info about unpausing scripts, refer to `;help unpause'. Options:\n\n ;pause [script] ..... Pause the most recently started (but not already paused) script, or the specified script if a name is provided.\n \n ;pause all .......... Pause all currently active scripts that have not registered themselves with the `no_pause_all' command.\n \n ;p .................. Synonym for `;pause', otherwise identical to above.\n\nRefer to `;help kill' for more info on ignoring `;pause all' and behavior thereof, as it's identical to the `;kill all' behavior. For more on the `no_pause_all' command, refer to `;man no_pause_all'.\nPAUSE\n\ncmdhelp['purge'] = <<PURGE\nThis command deletes the currently cached history of game-sent and user-sent data (both the RAM and hard disk portions), empties all active script's input/output memory buffers (note that this can result in game data not being seen by scripts, since it may be deleted before they have a chance to check it), and unconditionally triggers a general \"garbage collection\" sweep (details on this are beyond the scope of this documentation, but suffice it to say it's done automatically and there's no need to initiate it with this command).\n\nNote that a subsequent `;log' command will not save any data that was previously deleted by a `;purge' command.\nPURGE\n\ncmdhelp['exec'] = cmdhelp['execq'] = <<EXEC\nThis handy little feature allows you to write and run a script truly \"on-the-fly,\" without any files on the hard disk or switching to/from programs being involved. Simply type out whatever script you want Lich to run following the `;exec' command (simulating moving down to the next line with semicolons), and when finished hit enter. A semicolon is the same thing as hitting enter to move down a line in the Ruby language, so you can have as long and complex an \"exec\" script as you want to bother typing. Options:\n\n ;exec {miniscript} ... Execute the specified `miniscript' exactly like any other script, but without any files being involved.\n \n ;execq {miniscript} .. Identical to above, but will silence the notification Lich generates when a script starts and ends.\n \n ;e ................... Synonym for `;exec'.\n \n ;eq .................. Synonym for `;execq'.\n\nNote that since `;eq' generates no actual notice that a script starts or ends, it can be used along with aliases to simulate your own complex \"Lich commands.\" For example, I keep an alias for `get' that starts a miniscript with nothing but the command `fetchloot', which is a Lich script command that picks up and puts away loot on the ground (refer to `;man fetchloot' for more on that).\n\nThere is no limit to how many exec scripts can be running simultaneously; they'll be numbered starting at 1 and can be referred to if necessary by supplying their full name (e.g., `;kill exec3', etc.).\nEXEC\n\ncmdhelp['spellshorts'] = <<SPELLSHORTS\nThis will alter the lines you send to the game if they consist only of \"{number}\" or \"{number} target\". For example, \"401\" would be changed to \"incant 401\" before being sent. \"406 somebody\" would be altered to be \"prep 406 (carriage-return) cast at somebody\", and so on.\nSPELLSHORTS\n\ncmdhelp['tips'] = <<TIPS\n- The program website -- http://lich.gs4groups.com-- has a good deal of information; don't overlook the links page for quick references.\n\n- There are a number of informational text files (including brief script tutorials) in your Lich program directory (Lich is currently installed in `#{$lich_dir}').\n\n- Lich has a manual that's viewable in your game window; just type `#{$clean_lich_char}man' for details.\n\n- The script repository is the best place to look for both user-written scripts as well as the latest versions of all the scripts the author of Lich wrote (including those bundled with the install). Type `#{$clean_lich_char}repository' for further details.\n\n- Lich's aliases have regular expression support.\n\n- The author's most-used (and favorite) script is \"goto.lic\", which is an `any-room-to-any-room movement script' that utilizes Lich's built-in pathfinding capabilities to plot the shortest route between your origin and your destination. If you haven't done so yet, have a look at it (type `#{$clean_lich_char}goto' for more details).\n\n- The author's almost-as-often-used (and second favorite) script is \"healme.lic\", which requires no interaction whatsoever to heal you completely using herbs. Type `#{$clean_lich_char}repository info healme' for further details.\n\n- Lich can properly recognize and execute Wizard and StormFront scripts it finds in its script directory no matter which front-end you're playing with.\n\n- Lich can compensate for your type ahead limit, to a certain extent; unfortunately there is no way to do this perfectly, but it's particularly useful to avoid type ahead errors from scripts sending data at the same time the user is sending game commands. Type `#{$clean_lich_char}ta help' for further details.\n\n- The list of magical effects that Lich tracks the duration of is defined by a plain-text file in your Lich script directory. The program installs with the file including all the necessary information for a number of combat maneuvers, CoL signs, Voln symbols, and Bard spellsongs, but that may not be enough for you. The name of the file is \"spell-list.txt\" and you can add to/remove from/edit the file to your heart's content; if you make changes to it, simply kill and restart the \"infomonitor.lic\" script for the changes to take effect.\nTIPS\n\ncmdhelp['gui'] = <<GUI\nThis command can be used in either of two methods. The first allows for the display of most recognized Lich commands in a graphical popup console (including help information). The second allows a script to be started and for its text output to be piped to a graphical popup console. Examples:\n\n ;gui repository list ... This would execute the \"repository\" script, and display its output in a GUI console.\n ;gui help .............. This would display the main \"help\" information in a GUI console.\nGUI\n\ncmdhelp['mapbrowser'] = <<MAPBROWSER\nThis allows for the viewing and editing of the map database (as used by the goto.lic script). It should be self-explanatory, so give it a try.\n\nNote, however, that any changes made do not effect the currently running Lich process -- what this means is that any changes you make can be written out to a file, but they won't take effect until you deliberately (re)load the map database (e.g. by typing `;goto reload' after manually moving the Map Browser's \"map-mod.dat\" file to the standard \"map.dat\" file).\nMAPBROWSER\n\ncmdhelp['topics'] = proc { |string|\nn = '0'\nrespond \"The currently available Lich help topics are:\"\nfor t in cmdhelp.keys.sort\nrespond sprintf(\"%s. %s\", n.succ!, t.capitalize)\nend\n}\n\ncmd = str.chomp.sub(/\\w+\\s+/, '')\nif specific_info = cmdhelp[cmd]\nif specific_info.kind_of? Proc\nspecific_info.call(cmd)\nelse\nrespond\nrespond \"#{$clean_lich_char}%s\" % cmd.upcase\nrespond specific_info\nend\nreturn\nend\n\nrespond <<HELP\n--- The Lich Scripting Utility, v#{$version} ---\n\n Script Control:\nKILL [name] ..... Kill last script, or optionally by name. Also recognized as 'stop' and 'k'.\nKILL ALL ........ Stops all scripts. Also works as 'ka' and 'STOP ALL' (abbreviations accepted).\nPAUSE [name] .... Pause the given script, or the last unpaused script. Also 'p'.\nPAUSE ALL ....... Pause all active scripts. This is also recognized as 'pa'.\nUNPAUSE [name] .. Unpause the given script, or the last paused one. Also 'u' and 'up'.\nUNPAUSE ALL ..... Unpause all currently paused scripts. Also recognized as 'ua' and 'upa'.\nLIST ............ Lists all currently running scripts. Also recognized as 'l'.\nSEND {string} ... Local commands aren't seen by scripts. This sends all scripts a line. Also 's'.\nSEND TO {name} .. Identical to SEND, but sends to only one script. Ex: ';send to calc [message]'.\nDEBUG ........... Toggles on/off script verbose error reporting (slightly more info about errors).\n\n Remote Info Querying and other LichNet Features:\nMAGIC ........... For use with the 'infomonitor' script; 'magic help' for specific information.\nCHAT ............ LichNet's chat command. ';chat help' for more detailed information about usage.\nTUNEIN .......... Tune in to the server's chat net; character name will be part of the WHO list.\nTUNEOUT ......... Ignore the chat and hide char's name; private chats and info-checks still work.\nWHO ............. List the people tuned to the LNet chat. The 'lichnet' script must be running.\nWHO {person} .... Check if a person is linked to LNet and whether they're tuned to the chat.\nLOCATE {name} ... Locate someone currently linked to LichNet; you also have to be linked.\nSPELLS {name} ... Display the active spells of someone linked to LichNet (their MAGIC data).\nINFO {name} ..... Display the statistics of someone linked to LichNet (their INFO data).\nSKILLS {name} ... Display the skills of someone linked to LichNet (their SKILLS data).\nHEALTH {name} ... Display the health, mana, and possibly stamina of someone (their HEALTH data).\nREPOSITORY ...... This will show help info pertaining to the script repository and its usage.\n\n Core Program Configuration:\nTYPEAHEAD ....... Set your typeahead limit compensation. `typeahead help' for more. Also 'ta'.\nTAC ............. TypeAheadClear, discards any queued commands in the type ahead buffer.\nALIAS ........... Lists your Lich aliases; these are global (i.e. they apply to all your chars).\nALIAS ADD/DEL ... Add or delete aliases from your Lich aliases list. 'alias help' for more info.\nFAVS ............ List the scripts currently on your favorites list (these are started at login).\nFAVS ALL ........ Lists any global favorites as well as all character-specific lists.\nFAVS ADD {name} . Add a script to your character-specific favorites list; abbreviations accepted.\nFAVS ADD ALL .... Same as 'FAVS ADD', but script is added to the global list (effects all chars).\nFAVS DEL {name} . Delete a script from your character-specific favorites list; abbreviations okay.\nFAVS DEL ALL .... Same as 'FAVS DEL', but delete the script from your global favorites list.\nFAVS LOAD ....... Load the scripts on your favorites list at anytime (the way Lich does at login).\nRELOAD .......... Will reload the settings from your settings file and aliases file.\n\n Help and Miscellaneous Program Features:\nGUI [parameters] . See `help gui' for detailed information.\nMAPBROWSER ....... See `help mapbrowser' for detailed information.\nHELP [command] .. Displays this list, or if available the specified command's help information.\nHELP TOPICS ..... List all available `;help {cmd}' topics that can be viewed for specific info.\nNOTES ........... Display a number of miscellaneous program notes and tips.\nABOUT ........... A paragraph about the program (application manifest): version, contact info, etc.\nLOG ............. Save temp caches to a persistent .txt file (dumps entire history as ASCII text).\nSTATS ........... CPU time used, size of Lich's RAM/HDD caches, time since login, etc.\nPURGE ........... Empty all temporary caches, both RAM/HDD. Unnecessary and rarely of use.\nMAN {command} ... Lookup an in-script command in Lich's manual script. `;man help' for more info.\n\n Starting Scripts:\nEXEC {code} ..... Quickscript. Run a whole line as a script: separate lines with ' ; '. Also 'e'.\nEXECQ {code} .... Same as 'exec', but doesn't echo the 'active'/'finished' messages (q = quiet).\nWIZ {script} .... Run a Wizard or StormFront formatted script; near-100% compatibility. Also 'w'.\nWRAP {script} ... Wrap any interpreter's script (e.g. Perl, Python, etc.); see HELP WRAP for more.\nFORCE {script} .. Bypass the 'already running' error and start another instance of a running script.\n(ANYTHING ELSE) . If a line is unrecognized, it's interpreted as the script to run (abbrev. okay).\n\nAny line sent to Lich must always begin with a '#{$clean_lich_char}' or it will be ignored (aliases are the only exception to this).\n\nHELP\n}\nHook.register(/^notes?$/i) { |str|\nrespond(\"NOTES:\")\nrespond(\"- Just like every in-script command, aliases have full regular expression support (if you don't know what a 'Regular Expression' is, no need to trouble yourself with the details of what this means -- you can safely ignore it).\")\nrespond(\"\")\nrespond(\"- Lich supports passing a script command line arguments in the standard Wizard style (for example, '#{$clean_lich_char}ScriptName VariableOne VariableTwo \\\"Variable Three is all of these words, since they're in quotes\\\" etc').\")\nrespond(\"\")\nrespond(\"- You may abbreviate the name of the script you wish to launch, but the order of files could interfere (the first matching name found is launched). If this happens, use the full name.\")\nrespond(\"\")\nrespond(\"- With The Wizard option 'buffered screen updates' set to on, some scripts that 'echo' information can look like they're causing lag on some systems -- setting this option to 'off' will correct this.\")\nrespond(\"\")\nrespond(\"- For Wizard and StormFront scripts, Lich first checks its own directory. If a matching file isn't found, it attempts to find a match in the default Wizard installation directory. If it still can't find a matching name, it gives up and tells you it can't find the script.\")\nrespond(\"\")\nrespond(\"- You can enter 'reverse' as the first command line argument to execute a Wizard script in reverse; this will run a script from the end to the start instead of start to end, and will reverse all cardinal directions in 'move' commands (for instance, 'move up' is turned into 'move down', etc.). This only works on movement scripts w/o any label jumps, and since it can't reverse something like 'go crevice', you'll get stuck if the return-trip command isn't the same; use it accordingly.\")\nrespond(\"\")\nrespond(\"- Lich has no match limit whatsoever. This applies to both the Lich-script 'match' commands and the Wizard/StormFront-script 'match' commands (as well as 'matchre' in SF); if you either run a Wizard/SF script with Lich or use 'match' in a Lich script, you can use as many as you can tolerate typing.\")\nrespond(\"\")\nrespond(\"- You can start a script without having Lich announce that it's active by entering 'quiet' as the first command line argument, if you want to. For example, to start 'calcredux.lic' without cluttering your game window with the announcement, type '#{$clean_lich_char}calcredux quiet'.\")\nrespond(\"\")\nrespond(\"- Try not to use the '#{$clean_lich_char}force {scriptname}' command unnecessarily: it sounds a lot more useful than it really is, and a number of advanced scripts are built on the assumption that there's only going to be a single instance of that script executing at any given time (infomonitor.lic, for example, tends to short-circuit every running copy of itself when more than one is active at a time). Also, be extremely cautious with the 'force_start_script' command when using it inside a script; Lich has a great many different kinds of 'loops', and if you don't pay close attention, you may find that your script loops over the 'force_start_script' command and within a second or less has started thousands of copies of a single script -- which, by the way, Lich would be happy to do for you just before it effectively froze your computer while hogging the CPU to try and manage more scripts than your processor can deal with at once. In the end it's little more than a major inconvenience, but it's possible that the operating system will become unstable and end up crashing in the process, so bear it in mind.\")\nrespond\nrespond(\"- When executing a Wizard script, Lich is able to use a number of its handier features and still properly execute the script -- for example, Lich knows the difference between moving from one room to another and the 'look' output (not when run in SF, however); can usually recognize command-rejection due to type ahead, RT, stuns, webs, etc., and successfully execute the action when possible; will stand and repeat a rejected command if it's necessary, and various other random niceties. Lich cannot, however, modify Wizard's (or StormFront's) highlight strings or other settings when running one of their scripts, and any such attempts will be silently ignored.\")\nrespond(\"\")\nrespond(\"- Since Lich by default places no restrictions on what a script can do, there is an optional 'safe mode' that you can run a script under. When run in safe mode, if a script tries to do something that might be unwanted (change the local system somehow, access files on the hard drive, make a connection to a remote computer, etc.), Lich will immediately abort execution of the script without performing whatever command looks suspicious (no questions asked, the script dies instantly) and notify you of what it was about to do. Note that this will disable a number of commands that could, in actuality, be totally harmless (basically it prevents MOST non-GemStone-specific functions from being usable, though some of the completely harmless functions Lich provides to scripts can still be used). Since it's impossible to tell whether something is malicious or just making proper use of Lich's features, a number of features commonly used by advanced scripts are disabled despite being totally harmless in a given context (scripts are not allowed to access the hard disk and therefore are unable to load/save settings, for example). To run a script this way, enter 'safe' as the first command line argument -- so to run calcredux in safe mode, you would type `#{$clean_lich_char}calcredux safe' (without the apostrophes of course).\")\nrespond(\"\")\nrespond(\"\\034GSL\") if !$stormfront\nrespond(\"- Lich provides scripts with enormous power: if you don't completely trust the source of a given script, execute it in safe mode. You should remember that when given free reign, Lich scripts can do a great many things that may be both very unwanted and very destructive. Bear in mind that Ruby is a powerful high-level programming language, and unlike conventional scripting applications, Lich's scripts have the potential to be harmful and should not be run carelessly.\" + if !$stormfront then \"\\034GSM\" else \"\" end)\nrespond(\"\")\n}\n# Scroll the prog \"about\" info\nHook.register(/^about$/i) { |str|\nrespond(\"--- The Lich Scripting Utility, v#{$version} ---\\n Author: Murray Miron (Shaelun in GemStone IV)\\n EMail: GS4Lich@yahoo.com\\n Project Website: http://lich.sudolife.com\\n AIM/Y! Screen Name: GS4Lich (I'm rarely on).\\n\\n The Lich program is, in its most basic sense, a modified Ruby interpreter designed specifically for use with text-based MUDs.\\n\\n Ruby is a high-level programming language that's very much like Perl and Python in the sense that they're all `interpreted' languages. This means it isn't necessary to `compile' source code into 'program.exe' format, and that programs (which are often misleadingly referred to as `scripts') can be executed -- that is, `interpreted' -- directly from plain-text files.\\n\\n Ruby itself was designed and developed primarily by Yukihiro Matsumoto ('Matz'), originated in Japan, and was released to the general public in 1995.\")\n}\n# Purge the caches\nHook.register(/^purge$/i) { |str|\n Script.index.each { |ssock|\n ssock.clear\n }\n $_SERVERBUFFER_.dump\n $_SERVERBUFFER_.purge\n $_CLIENTBUFFER_.dump\n $_CLIENTBUFFER_.purge\n GC.start\nrespond(\"--- Lich: memory and disk caches have been discarded.\")\n}\n# External wrapper\nHook.register(/^wrap .+$/) { |str|\nif $LICHCONFIG['OS'] =~ /win/i or ENV['OS'] =~ /win/i\nrespond \"--- Lich: this command is only available in *NIX environments.\"\nreturn\nend\ncmdline = str.sub(/[^\\s]+\\s*/, '')\nfname = cmdline.slice(/[^\\s]+/)\nbegin\nf = File.open($script_dir + fname)\nrescue\nrespond(\"--- Lich: #{$!}\")\nreturn\nend\niname = f.gets\nf.close\nif iname !~ /^#!/\nrespond(\"--- Lich: script does not begin with `#!' Read the help information.\")\nreturn\nend\niname = iname.sub(/^#!\\s*/, '').strip\nunless File.exists? iname\nrespond(\"--- Lich: interpreter #{iname} does not exist.\")\nreturn\nend\nScript.wrap_script(fname, iname, cmdline.sub(/[^\\s]+\\s*/, ''))\n}\n# Pause all scripts\nHook.register(/^(?:pause all|pa)$/i) {\nif Script.index.empty?\nrespond \"--- Lich: no currently active scripts!\"\nelsif (list = Script.index.find_all { |s| !s.paused }).empty?\nrespond \"--- Lich: all currently active scripts are already paused.\"\nelsif !list.find { |s| !s.no_pause }\nrespond \"--- Lich: all active scripts have toggled their `no_pause_all' values on. Please pause them by name if desired.\"\nelse\nlist.each { |s| s.pause unless s.no_pause }\nend\n}\n# Pause a script\nHook.register(/^(?:pause|p)(?! all)(?: [\\d\\w]+)?$/i) { |str|\ntarget = str.split[1].strip\nif Script.index.empty?\nrespond \"--- Lich: no scripts running!\"\nreturn\nelsif !target or target.empty?\ntarget = Script.index.find_all { |s| !s.paused }.last\nunless target\nrespond \"--- Lich: all active scripts are already paused!\"\nreturn\nend\nelse\nunless target = Script.find(target)\nrespond \"--- Lich: #{str.split[1].strip} does not match any active script.\"\nreturn\nend\nend\ntarget.pause\n}\n# Unpause all scripts\nHook.register(/^(?:unpause all|upa|ua)$/i) {\nif Script.index.empty?\nrespond \"--- Lich: no currently active scripts!\"\nelsif (list = Script.index.find_all { |s| s.paused }).empty?\nrespond \"--- Lich: there are no currently paused scripts to unpause!\"\nelse\nlist.each { |s| s.unpause }\nend\n}\n# Unpause a script\nHook.register(/^(?:unpause|up|u)(?! all)(?: [\\d\\w]+)?$/i) { |str|\ntarget = str.split[1].strip\nif Script.index.empty?\nrespond \"--- Lich: no scripts running!\"\nreturn\nelsif !target or target.empty?\ntarget = Script.index.find_all { |s| s.paused }.last\nunless target\nrespond \"--- Lich: there are no currently paused scripts to unpause.\"\nreturn\nend\nelse\nunless target = Script.find(target)\nrespond \"--- Lich: #{str.split[1].strip} does not match any active script.\"\nreturn\nend\nend\ntarget.unpause\n}\n# Change the character that Lich displays to signify a script has sent something to the game\nHook.register(/^sendchar .$/) { |str|\n$SEND_CHARACTER = str[-1..-1]\nrespond \"--- Lich: script-sent data is now signified by the `#{$SEND_CHARACTER}' character. As an example...\"\nrespond\nrespond \"[LichScript]#{$SEND_CHARACTER}look\"\n}\n\nend\n\nclass Char\ndef Char.info\nary = []\nary.push sprintf(\"Name: %s Race: %s Profession: %s\", Char.name, Stats.race, Stats.prof)\nary.push sprintf(\"Gender: %s Age: %d Expr: %d Level: %d\", Stats.gender, Stats.age, Stats.exp, Stats.level)\nary.push sprintf(\"%017.17s Normal (Bonus) ... Enhanced (Bonus)\", \"\")\n%w[ Strength Constitution Dexterity Agility Discipline Aura Logic Intuition Wisdom Influence ].each { |stat|\nval, bon = Stats.send(stat[0..2].downcase)\nspc = \" \" * (4 - bon.to_s.length)\nary.push sprintf(\"%012s (%s): %05s (%d) %s ... %05s (%d)\", stat, stat[0..2].upcase, val, bon, spc, val, bon)\n}\nary.push sprintf(\"Mana: %04s\", mana)\nary\nend\ndef Char.skills\nary = []\nary.push sprintf(\"%s (at level %d), your current skill bonuses and ranks (including all modifiers) are:\", Char.name, Stats.level)\nary.push sprintf(\" %-035s| Current Current\", 'Skill Name')\nary.push sprintf(\" %-035s|%08s%08s\", '', 'Bonus', 'Ranks')\nfmt = [ [ 'Two Weapon Combat', 'Armor Use', 'Shield Use', 'Combat Maneuvers', 'Edged Weapons', 'Blunt Weapons', 'Two-Handed Weapons', 'Ranged Weapons', 'Thrown Weapons', 'Polearm Weapons', 'Brawling', 'Ambush', 'Multi Opponent Combat', 'Combat Leadership', 'Physical Fitness', 'Dodging', 'Arcane Symbols', 'Magic Item Use', 'Spell Aiming', 'Harness Power', 'Elemental Mana Control', 'Mental Mana Control', 'Spirit Mana Control', 'Elemental Lore - Air', 'Elemental Lore - Earth', 'Elemental Lore - Fire', 'Elemental Lore - Water', 'Spiritual Lore - Blessings', 'Spiritual Lore - Religion', 'Spiritual Lore - Summoning', 'Sorcerous Lore - Demonology', 'Sorcerous Lore - Necromancy', 'Mental Lore - Divination', 'Mental Lore - Manipulation', 'Mental Lore - Telepathy', 'Mental Lore - Transference', 'Mental Lore - Transformation', 'Survival', 'Disarming Traps', 'Picking Locks', 'Stalking and Hiding', 'Perception', 'Climbing', 'Swimming', 'First Aid', 'Trading', 'Pickpocketing' ], [ 'twoweaponcombat', 'armoruse', 'shielduse', 'combatmaneuvers', 'edgedweapons', 'bluntweapons', 'twohandedweapons', 'rangedweapons', 'thrownweapons', 'polearmweapons', 'brawling', 'ambush', 'multiopponentcombat', 'combatleadership', 'physicalfitness', 'dodging', 'arcanesymbols', 'magicitemuse', 'spellaiming', 'harnesspower', 'emc', 'mmc', 'smc', 'elair', 'elearth', 'elfire', 'elwater', 'slblessings', 'slreligion', 'slsummoning', 'sldemonology', 'slnecromancy', 'mldivination', 'mlmanipulation', 'mltelepathy', 'mltransference', 'mltransformation', 'survival', 'disarmingtraps', 'pickinglocks', 'stalkingandhiding', 'perception', 'climbing', 'swimming', 'firstaid', 'trading', 'pickpocketing' ] ]\n0.upto(fmt.first.length - 1) { |n|\ndots = '.' * (35 - fmt[0][n].length)\nrnk = Skills.send(fmt[1][n])\nary.push sprintf(\" %s%s|%08s%08s\", fmt[0][n], dots, Skills.to_bonus(rnk), rnk) unless rnk.zero?\n}\n%[Minor Elemental,Major Elemental,Minor Spirit,Major Spirit,Bard,Cleric,Empath,Paladin,Ranger,Sorcerer,Wizard].split(',').each { |circ|\nrnk = Spells.send(circ.gsub(\" \", '').downcase)\nif rnk.nonzero?\nary.push ''\nary.push \"Spell Lists\"\ndots = '.' * (35 - circ.length)\nary.push sprintf(\" %s%s|%016s\", circ, dots, rnk)\nend\n}\nary\nend\nend\n\n# The vast majority of script 'commands' are defined here. A lot of random garbage is tossed in because over the long months I've been writing Lich, I've been learning as I added to it. For now I'm too lazy to clean it up, so it's only real use is to serve as an example (if you really want to, go ahead and try to use this to extend your own project, but good luck, lol). If you want to peek at what happens when you use a command in a script, just do a search for 'def (command)' [replace (command) with the command you want to look at, of course].\n\nat_exit { Process.waitall }\n\nclass NilClass\ndef dup\nnil\nend\ndef method_missing(*args)\nnil\nend\nend\n\nmodule Enumerable\ndef qfind(obj)\nfind { |el| el.match obj }\nend\nend\n\nclass String\ndef split_as_list\nstring = self\nstring.sub!(/^You (?:also see|notice) |^In the .+ you see /, ',')\nstring.sub('.','').sub(/ and (an?|some|the)/, ', \\1').split(',').reject { |str|\nstr.strip.empty?\n}.collect { |str| str.lstrip }\nend\nend\n\n\nclass Char\n@@cha ||= nil\n@@name ||= nil\nprivate_class_method :new\ndef Char.init(name)\n@@name == nil ? @@name = name.strip : @@name.dup\nend\ndef Char.name\n@@name or $_TAGHASH_['GSB'].slice(/[A-Z][a-z]+/)\nend\ndef Char.health(*args)\ncheckhealth(*args)\nend\ndef Char.mana(*args)\ncheckmana(*args)\nend\ndef Char.spirit(*args)\ncheckspirit(*args)\nend\ndef Char.maxhealth\nObject.module_eval { maxhealth }\nend\ndef Char.maxmana\nObject.module_eval { maxmana }\nend\ndef Char.maxspirit\nObject.module_eval { maxspirit }\nend\ndef Char.stamina(*args)\ncheckstamina(*args)\nend\ndef Char.maxstamina\nObject.module_eval { maxstamina }\nend\ndef Char.cha(val=nil)\nval == nil ? @@cha : @@cha = val\nend\ndef Char.dump_info\nsave = Thread.critical\nbegin\nMarshal.dump([\nSpell.detailed?,\nSpell.serialize,\nSpellsong.serialize,\nStats.serialize,\nSkills.serialize,\nSpells.serialize,\nGift.serialize,\nSociety.serialize,\n])\nensure\nThread.critical = save\nend\nend\ndef Char.load_info(string)\nsave = Char.dump_info\nbegin\nSpell.load_detailed,\nSpell.load_active,\nSpellsong.load_serialized,\nStats.load_serialized,\nSkills.load_serialized,\nSpells.load_serialized,\nGift.load_serialized,\nSociety.load_serialized = Marshal.load(string)\nrescue\nraise $! if string == save\nstring = save\nretry\nend\nend\ndef Char.method_missing(meth, *args)\n[ Stats, Skills, Spellsong, Society ].each { |klass|\nbegin\nresult = klass.__send__(meth, *args)\nreturn result\nrescue\nend\n}\nraise NoMethodError\nend\nend\n\nclass Society\n@@status ||= String.new\n@@rank ||= 0\ndef Society.serialize\n[@@status,@@rank]\nend\ndef Society.load_serialized=(val)\n@@status,@@rank = val\nend\ndef Society.status=(val)\n@@status = val\nend\ndef Society.status\n@@status.dup\nend\ndef Society.rank=(val)\nif val =~ /Master/\nif @@status =~ /Voln/ then @@rank = 26 elsif @@status =~ /Council of Light/ then @@rank = 20 else @@rank = val.to_i end\nelse\n@@rank = val.slice(/[0-9]+/).to_i\nend\nend\ndef Society.step\n@@rank\nend\ndef Society.member\n@@status.dup\nend\ndef Society.rank\n@@rank\nend\nend\n\nclass Spellsong\n@@renewed ||= Time.at(Time.now.to_i - 1200)\ndef Spellsong.renewed\n@@renewed = Time.now\nend\ndef Spellsong.renewed=(val)\n@@renewed = val\nend\ndef Spellsong.renewed_at\n@@renewed\nend\ndef Spellsong.timeleft\nif Time.now - @@renewed > Spellsong.duration\n@@renewed = Time.now\nend\n(Spellsong.duration - (Time.now - @@renewed)) / 60.00\nend\ndef Spellsong.serialize\nSpellsong.timeleft\nend\ndef Spellsong.load_serialized=(old)\nThread.new {\nn = 0\nwhile Stats.level == 0\nsleep 0.25\nn += 1\nbreak if n >= 4\nend\nunless n >= 4\n@@renewed = Time.at(Time.now.to_f - (Spellsong.duration - old * 60.00))\nelse\n@@renewed = Time.now\nend\n}\nnil\nend\ndef Spellsong.duration\ntotal = 120\n1.upto(Stats.level.to_i) { |n|\nif n < 26\ntotal += 4\nelsif n < 51\ntotal += 3\nelsif n < 76\ntotal += 2\nelse\ntotal += 1\nend\n}\n(total + Stats.log[1].to_i + (Stats.inf[1].to_i * 3) + (Skills.mltelepathy.to_i * 2))\nend\ndef Spellsong.tonisdodgebonus\nthresholds = [1,2,3,5,8,10,14,17,21,26,31,36,42,49,55,63,70,78,87,96]\nbonus = 20\nthresholds.each { |val| if Skills.elair >= val then bonus += 1 end }\nbonus\nend\ndef Spellsong.tonishastebonus\nbonus = -1\nthresholds = [30,75]\nthresholds.each { |val| if Skills.elair >= val then bonus -= 1 end }\nbonus\nend\ndef Spellsong.mirrorsdodgebonus\n20 + ((Spells.bard - 19) / 2).round\nend\ndef Spellsong.mirrorscost\n[19 + ((Spells.bard - 19) / 5).truncate, 8 + ((Spells.bard - 19) / 10).truncate]\nend\ndef Spellsong.depressionpushdown\n20 + Skills.mltelepathy\nend\ndef Spellsong.depressionslow\nthresholds = [10,25,45,70,100]\nbonus = -2\nthresholds.each { |val| if Skills.mltelepathy >= val then bonus -= 1 end }\nbonus\nend\ndef Spellsong.sonicarmordurability\n210 + (Stats.level / 2).round + Skills.to_bonus(Skills.elair)\nend\ndef Spellsong.sonicbladedurability\n160 + (Stats.level / 2).round + Skills.to_bonus(Skills.elair)\nend\ndef Spellsong.sonicshielddurability\n125 + (Stats.level / 2).round + Skills.to_bonus(Skills.elair)\nend\ndef Spellsong.sonicbonus\n(Spells.bard / 2).round\nend\ndef Spellsong.sonicarmorbonus\nSpellsong.sonicbonus + 15\nend\ndef Spellsong.sonicbladebonus\nSpellsong.sonicbonus + 10\nend\ndef Spellsong.sonicshieldbonus\nSpellsong.sonicbonus + 10\nend\ndef Spellsong.valorbonus\n10 + (Spells.bard / 2).round\nend\ndef Spellsong.valorcost\n[10 + (Spellsong.valorbonus / 2), 3 + (Spellsong.valorbonus / 5)]\nend\ndef Spellsong.luckcost\n[6 + ((Spells.bard - 6) / 4),(6 + ((Spells.bard - 6) / 4) / 2).round]\nend\ndef Spellsong.holdingtargets\n1 + ((Spells.bard - 1) / 7).truncate\nend\nend\n\nclass Skills\n@@twoweaponcombat ||= 0\n@@armoruse ||= 0\n@@shielduse ||= 0\n@@combatmaneuvers ||= 0\n@@edgedweapons ||= 0\n@@bluntweapons ||= 0\n@@twohandedweapons ||= 0\n@@rangedweapons ||= 0\n@@thrownweapons ||= 0\n@@polearmweapons ||= 0\n@@brawling ||= 0\n@@ambush ||= 0\n@@multiopponentcombat ||= 0\n@@combatleadership ||= 0\n@@physicalfitness ||= 0\n@@dodging ||= 0\n@@arcanesymbols ||= 0\n@@magicitemuse ||= 0\n@@spellaiming ||= 0\n@@harnesspower ||= 0\n@@emc ||= 0\n@@mmc ||= 0\n@@smc ||= 0\n@@elair ||= 0\n@@elearth ||= 0\n@@elfire ||= 0\n@@elwater ||= 0\n@@slblessings ||= 0\n@@slreligion ||= 0\n@@slsummoning ||= 0\n@@sldemonology ||= 0\n@@slnecromancy ||= 0\n@@mldivination ||= 0\n@@mlmanipulation ||= 0\n@@mltelepathy ||= 0\n@@mltransference ||= 0\n@@mltransformation ||= 0\n@@survival ||= 0\n@@disarmingtraps ||= 0\n@@pickinglocks ||= 0\n@@stalkingandhiding ||= 0\n@@perception ||= 0\n@@climbing ||= 0\n@@swimming ||= 0\n@@firstaid ||= 0\n@@trading ||= 0\n@@pickpocketing ||= 0\ndef Skills.serialize\n[@@twoweaponcombat, @@armoruse, @@shielduse, @@combatmaneuvers, @@edgedweapons, @@bluntweapons, @@twohandedweapons, @@rangedweapons, @@thrownweapons, @@polearmweapons, @@brawling, @@ambush, @@multiopponentcombat, @@combatleadership, @@physicalfitness, @@dodging, @@arcanesymbols, @@magicitemuse, @@spellaiming, @@harnesspower, @@emc, @@mmc, @@smc, @@elair, @@elearth, @@elfire, @@elwater, @@slblessings, @@slreligion, @@slsummoning, @@sldemonology, @@slnecromancy, @@mldivination, @@mlmanipulation, @@mltelepathy, @@mltransference, @@mltransformation, @@survival, @@disarmingtraps, @@pickinglocks, @@stalkingandhiding, @@perception, @@climbing, @@swimming, @@firstaid, @@trading, @@pickpocketing]\nend\ndef Skills.load_serialized=(array)\n@@twoweaponcombat, @@armoruse, @@shielduse, @@combatmaneuvers, @@edgedweapons, @@bluntweapons, @@twohandedweapons, @@rangedweapons, @@thrownweapons, @@polearmweapons, @@brawling, @@ambush, @@multiopponentcombat, @@combatleadership, @@physicalfitness, @@dodging, @@arcanesymbols, @@magicitemuse, @@spellaiming, @@harnesspower, @@emc, @@mmc, @@smc, @@elair, @@elearth, @@elfire, @@elwater, @@slblessings, @@slreligion, @@slsummoning, @@sldemonology, @@slnecromancy, @@mldivination, @@mlmanipulation, @@mltelepathy, @@mltransference, @@mltransformation, @@survival, @@disarmingtraps, @@pickinglocks, @@stalkingandhiding, @@perception, @@climbing, @@swimming, @@firstaid, @@trading, @@pickpocketing = array\nend\ndef Skills.method_missing(arg1, arg2='')\ninstance_eval(\"@@#{arg1}#{arg2}\", if Script.self then Script.self.name else \"Lich\" end)\nend\ndef Skills.to_bonus(ranks)\nbonus = 0\nwhile ranks > 0\nif ranks > 40\nbonus += (ranks - 40)\nranks = 40\nelsif ranks > 30\nbonus += (ranks - 30) * 2\nranks = 30\nelsif ranks > 20\nbonus += (ranks - 20) * 3\nranks = 20\nelsif ranks > 10\nbonus += (ranks - 10) * 4\nranks = 10\nelse\nbonus += (ranks * 5)\nranks = 0\nend\nend\nbonus\nend\nend\n\nclass Spells\n@@minorelemental ||= 0\n@@majorelemental ||= 0\n@@minorspiritual ||= 0\n@@majorspiritual ||= 0\n@@wizard ||= 0\n@@sorcerer ||= 0\n@@ranger ||= 0\n@@paladin ||= 0\n@@empath ||= 0\n@@cleric ||= 0\n@@bard ||= 0\ndef Spells.method_missing(arg1, arg2='')\ninstance_eval(\"@@#{arg1}#{arg2}\")\nend\ndef Spells.minorspirit\n@@minorspiritual\nend\ndef Spells.minorspirit=(val)\n@@minorspiritual = val\nend\ndef Spells.majorspirit\n@@majorspiritual\nend\ndef Spells.majorspirit=(val)\n@@majorspiritual = val\nend\ndef Spells.get_circle_name(num)\nval = num.to_s\nif val == \"1\" then \"Minor Spirit\"\nelsif val == \"2\" then \"Major Spirit\"\nelsif val == \"3\" then \"Cleric\"\nelsif val == \"4\" then \"Minor Elemental\"\nelsif val == \"5\" then \"Major Elemental\"\nelsif val == \"6\" then \"Ranger\"\nelsif val == \"7\" then \"Sorcerer\"\nelsif val == \"9\" then \"Wizard\"\nelsif val == \"10\" then \"Bard\"\nelsif val == \"11\" then \"Empath\"\nelsif val == \"16\" then \"Paladin\"\nelsif val == \"66\" then \"Death\"\nelsif val == \"65\" then \"Imbedded Enchantment\"\nelsif val == \"96\" then \"Combat Maneuvers\"\nelsif val == \"98\" then \"Order of Voln\"\nelsif val == \"99\" then \"Council of Light\"\nelsif val == \"cm\" then \"Combat Maneuvers\"\nelsif val == \"mi\" then \"Miscellaneous\"\nelse 'Unknown Circle' end\nend\ndef Spells.active\nSpell.active\nend\ndef Spells.known\nary = []\nSpell.list.each { |sp_obj|\ncirclename = Spells.get_circle_name(sp_obj.circle)\nsym = circlename.delete(\"\\s\").downcase\nranks = Spells.send(sym).to_i rescue()\nnext unless ranks.nonzero?\nnum = sp_obj.num.to_s[-2..-1].to_i\nary.push sp_obj if ranks >= num\n}\nary\nend\ndef Spells.serialize\n[@@minorelemental,@@majorelemental,@@minorspiritual,@@majorspiritual,@@wizard,@@sorcerer,@@ranger,@@paladin,@@empath,@@cleric,@@bard]\nend\ndef Spells.load_serialized=(val)\n@@minorelemental,@@majorelemental,@@minorspiritual,@@majorspiritual,@@wizard,@@sorcerer,@@ranger,@@paladin,@@empath,@@cleric,@@bard = val\nend\nend\n\nclass Spell\n@@active ||= Array.new\n@@list ||= Array.new\n@@active_loaded ||= false\n@@detailed ||= true\nattr_reader :timestamp, :num, :name, :duration, :timeleft, :msgup, :msgdn, :stacks, :circle, :circlename, :selfonly, :cost\ndef initialize(num,name,duration,cost,misc,msgup,msgdn)\n@name,@duration,@cost,@msgup,@msgdn = name,duration,cost,msgup,msgdn\nif num.to_i.nonzero? then @num = num.to_i else @num = num end\n@timestamp = Time.now\n@selfonly = misc.include?('1')\n@stacks = misc.include?('~')\n@active = false\n@timeleft = 0\n@msgup = msgup\n@msgdn = msgdn\n@circle = (num.to_s.length == 3 ? num.to_s[0..0] : num.to_s[0..1])\n@circlename = Spells.get_circle_name(@circle)\n@@list.push(self) unless @@list.find { |spell| spell.name == @name }\nend\ndef Spell.serialize\nspell = nil; @@active.each { |spell| spell.touch }\n@@active\nend\ndef Spell.load_active=(data)\ndata.each { |oldobject|\nspell = @@list.find { |newobject| oldobject.name == newobject.name }\nunless @@active.include?(spell)\nspell.timeleft = oldobject.timeleft\nspell.active = true\n@@active.push(spell)\nend\n}\nend\ndef Spell.load_detailed=(data)\n@@detailed = data\nend\ndef Spell.detailed?\n@@detailed\nend\ndef Spell.increment_detailed\n@@detailed = !@@detailed\nend\ndef active=(val)\n@active = val\nend\ndef Spell.active\n@@active\nend\ndef Spell.list\n@@list\nend\ndef Spell.upmsgs\n@@list.collect { |spell| spell.msgup }\nend\ndef Spell.dnmsgs\n@@list.collect { |spell| spell.msgdn }\nend\ndef timeleft=(val)\n@timeleft = val\n@timestamp = Time.now\nend\ndef touch\nif @duration.to_s == \"Spellsong.timeleft\"\n@timeleft = Spellsong.timeleft\nelse\n@timeleft = @timeleft - ((Time.now - @timestamp) / 60.00)\nif @timeleft.to_f <= 0\nself.putdown\nreturn 0.0\nend\nend\n@timestamp = Time.now\n@timeleft\nend\ndef Spell.[](val)\nif val.class == Spell\nval\nelsif val.class == Fixnum\n@@list.find { |spell| spell.num == val }\nelse\nif ret = @@list.find { |spell| spell.name =~ /^#{val}$/i } then ret\nelsif ret = @@list.find { |spell| spell.name =~ /^#{val}/i } then ret\nelse @@list.find { |spell| spell.msgup =~ /#{val}/i or spell.msgdn =~ /#{val}/i } end\nend\nend\ndef Spell.active?(val)\nSpell[val].active?\nend\ndef active?\ntouch\n@active\nend\ndef minsleft\ntouch\nend\ndef secsleft\ntouch * 60\nend\ndef to_s\n@name.to_s\nend\ndef putup\ntouch\n@stacks ? @timeleft += eval(@duration).to_f : @timeleft = eval(@duration).to_f\nif @timeleft > 240 then @timeleft = 239.983 end\n@@active.push(self) unless @@active.include?(self)\n@active = true\nend\ndef putdown\n@active = false\n@timeleft = 0\n@timestamp = Time.now\n@@active.delete(self)\nend\ndef remaining\nself.touch.as_time\nend\nend\n\nclass Stats\n@@race ||= 'unknown'\n@@prof ||= 'unknown'\n@@gender ||= 'unknown'\n@@age ||= 0\n@@exp ||= 0\n@@level ||= 0\n@@str ||= [0,0]\n@@con ||= [0,0]\n@@dex ||= [0,0]\n@@agi ||= [0,0]\n@@dis ||= [0,0]\n@@aur ||= [0,0]\n@@log ||= [0,0]\n@@int ||= [0,0]\n@@wis ||= [0,0]\n@@inf ||= [0,0]\ndef Stats.method_missing(arg1, arg2='')\nif arg2.class == Array\ninstance_eval(\"@@#{arg1}[#{arg2.join(',')}]\", if Script.self then Script.self.name else \"Lich\" end)\nelsif arg2.to_s =~ /^\\d+$/\ninstance_eval(\"@@#{arg1}#{arg2}\", if Script.self then Script.self.name else \"Lich\" end)\nelsif arg2.empty?\ninstance_eval(\"@@#{arg1}\", if Script.self then Script.self.name else \"Lich\" end)\nelse\ninstance_eval(\"@@#{arg1}'#{arg2}'\", if Script.self then Script.self.name else \"Lich\" end)\nend\nend\ndef Stats.serialize\n[@@race,@@prof,@@gender,@@age,@@exp,@@level,@@str,@@con,@@dex,@@agi,@@dis,@@aur,@@log,@@int,@@wis,@@inf]\nend\ndef Stats.load_serialized=(array)\n@@race,@@prof,@@gender,@@age,@@exp,@@level,@@str,@@con,@@dex,@@agi,@@dis,@@aur,@@log,@@int,@@wis,@@inf = array\nend\nend\n\nclass Gift\n@@began ||= Time.now\n@@timer ||= 0\n@@running ||= false\n@@stopwatch ||= Time.now\n@@tracked ||= false\ndef Gift.serialize\n[@@began,@@timer]\nend\ndef Gift.load_serialized=(array)\n@@tracked = true\n@@began,@@timer = array\nend\ndef Gift.touch\nover = @@began + 604800\nif Time.now > over\n@@timer = 0\n@@running = false\n@@stopwatch = Time.now\nend\nGift.stopwatch\nend\ndef Gift.stopwatch\nif $_TAGHASH_['GSr'] =~ /^A/\nif @@running then @@timer += (Time.now.to_f - @@stopwatch.to_f) end\n@@running = false\nelse\nif @@running\n@@timer += (Time.now.to_f - @@stopwatch.to_f)\nend\n@@running = true\n@@stopwatch = Time.now\nend\nend\ndef Gift.remaining\nGift.touch\nunless @@tracked then return 0 end\n21600 - @@timer\nend\ndef Gift.restarts_on\n@@began + 604800\nend\ndef Gift.ended\n@@timer = 21601\nend\ndef Gift.started\n@@began = Time.now\n@@timer = 0\n@@stopwatch = Time.now\nGift.stopwatch\nend\nend\n\nclass Lich\ndef Lich.reload_settings\nbegin\nif Char.name\nfile = File.open($lich_dir + \"settings-#{Char.name}.txt\")\nelse\nfile = File.open($lich_dir + \"settings.txt\")\nend\nfile.readlines.collect { |line| line.strip }.find_all { |line| line !~ /^#/ and !line.empty? }.each { |line|\nset, val = line.split(':').collect { |piece| piece.strip }\nif val.include?(',')\nLich.module_eval(\"@@#{set} = ['#{val.gsub(/, |,/, \"', '\")}']\", if Script.self then Script.self.name else \"Lich\" end)\nelse\nLich.module_eval(\"@@#{set} = '#{val}'\", if Script.self then Script.self.name else \"Lich\" end)\nend\n}\nrescue SystemCallError\nfile ? file.close : nil\nif Char.name\nfile = File.open($lich_dir + \"settings-#{Char.name}.txt\", 'w')\nelse\nfile = File.open($lich_dir + \"settings.txt\", 'w')\nend\nfile.write %[# These are your Lich settings. Note that each character will have their own file called \"settings-(charname).txt\" if you're using Lich with GemStone. If you're using it with another MUD, the template file (just plain \"settings.txt\") will be used globally since Lich has no clue what your char name is. You can access any of these from within a script with `Lich.(settingname)' exactly as though it were a variable. So for example, `Lich.lootsack' in a script would be replaced with whatever you put here. Note that as of v3.26+, you can make up any setting you want and just stick it in this file; once you reload the settings (see the `;help' menu or use settings.lic), it'll work like any of the previously available ones (e.g., `Lich.i_am_weirdly_named' would work if you put `i_am_weirdly_named: stuff' in here). These really aren't very convenient; the primary reason they exist is so that Wizard scripts that try to use the standard Wizard script variables can still be run properly by Lich, and also so that when you're writing a script that you think may be of use to others you have a way of making sure it will work for anybody who properly sets their Lich settings instead of only working if they manually change the script.\\r\\nlootsack:\\r\\nboxsack:\\r\\nscrollsack:\\r\\nwandsack:\\r\\nsheath:\\r\\ngemsack:\\r\\npuregemsack:\\r\\nherbsack:\\r\\nmagicsack:\\r\\nshield:\\r\\nweapon:\\r\\nuser0:\\r\\nuser1:\\r\\nuser2:\\r\\nuser3:\\r\\nuser4:\\r\\nuser5:\\r\\nuser6:\\r\\nuser7:\\r\\nuser8:\\r\\nuser9:\\r\\ntreasure:\\r\\nexcludeloot:\\r\\n]\nfile.close\nretry\nrescue SyntaxError\n$stderr.puts($!)\nrescue\n$stderr.puts($!)\n$stderr.puts($!.backtrace)\nensure\nfile ? file.close : nil\nend\nend\ndef Lich.method_missing(arg1, arg2='')\ninstance_eval(\"@@#{arg1}#{arg2}\", if Script.self then Script.self.name else \"Lich\" end)\nend\ndef Lich.fetchloot\nif items = checkloot.find_all { |item| item =~ /#{@@treasure.join('|')}/ }\ntake(items)\nelse\nreturn false\nend\nend\nend\n\nclass Status\n@@head = -2..-1\n@@neck = -4..-3\n@@rarm = -6..-5\n@@larm = -8..-7\n@@rleg = -10..-9\n@@lleg = -12..-11\n@@rhand = -14..-13\n@@lhand = -16..-15\n@@chest = -18..-17\n@@abs = -20..-19\n@@back = -22..-21\n@@reye = -24..-23\n@@leye = -26..-25\n@@nerves = -28..-27\nSFMAP = Hash[ 'nsys', 'nerves', 'leftArm', 'larm', 'rightArm', 'rarm', 'rightLeg', 'rleg', 'leftLeg', 'lleg',\n'rightHand', 'rhand', 'leftHand', 'lhand', 'rightEye', 'reye', 'leftEye', 'leye', 'abdomen', 'abs',\n]\ndef Status.method_missing(arg)\narg = arg.to_s\narg =~ /scar/ ? tag = \"GSb\" : tag = \"GSa\"\ninstance_eval(\"dec2bin($_TAGHASH_[tag].to_i).to_s[@@#{arg.slice(/head|neck|rarm|larm|rleg|lleg|rhand|lhand|chest|abs|back|reye|leye|nerves/)}]\",\nif Script.self then Script.self.name else \"Lich\" end)\nend\ndef Status.rank(val)\nbin2dec(val.to_i)\nend\ndef Status.sf_update(area, rank)\nrange = Status.class_eval(sprintf(\"@@%s\", SFMAP.fetch(area) { |query| query }))\nif rank =~ /Injury/i\ntag = [ 'GSa' ]\nelsif rank =~ /Scar/i\ntag = [ 'GSb' ]\nelse\ntag = [ 'GSa', 'GSb' ]\nend\ntag.each { |t|\nbuf = sprintf(\"%028b\", $_TAGHASH_[t].to_i)\nbuf[range] = dec2bin(rank.slice(/\\d/).to_i).to_s[-2..-1]\n$_TAGHASH_[t] = sprintf(\"%010d\", sprintf(\"0b0%s\", buf))\n}\nend\nend\n\nclass Wounds\ndef Wounds.method_missing(arg)\nbin2dec(Status.send(\"woundshift#{arg}\").to_i)\nend\ndef Wounds.arms\n[Wounds.larm,Wounds.rarm,Wounds.lhand,Wounds.rhand].max\nend\ndef Wounds.limbs\n[Wounds.larm,Wounds.rarm,Wounds.lhand,Wounds.rhand,Wounds.rleg,Wounds.lleg].max\nend\ndef Wounds.torso\n[Wounds.reye,Wounds.leye,Wounds.chest,Wounds.abs,Wounds.back].max\nend\nend\n\nclass Scars\ndef Scars.method_missing(arg)\nbin2dec(Status.send(\"scarshift#{arg}\").to_i)\nend\ndef Scars.arms\n[Scars.larm,Scars.rarm,Scars.lhand,Scars.rhand].max\nend\ndef Scars.limbs\n[Scars.larm,Scars.rarm,Scars.lhand,Scars.rhand,Scars.lleg,Scars.rleg].max\nend\ndef Scars.torso\n[Scars.reye,Scars.leye,Scars.chest,Scars.abs,Scars.back].max\nend\nend\nclass Watchfor\ndef Watchfor.method_missing(*args)\nnil\nend\nend\n\nclass Array\ndef method_missing(*usersave)\nself\nend\nend\n\nclass NilClass\ndef split(*val)\nArray.new\nend\ndef to_s\n\"\"\nend\ndef strip\n\"\"\nend\nend\n\nclass FalseClass\ndef method_missing(*usersave)\nnil\nend\nend\n\nclass String\ndef method_missing(*usersave)\n\"\"\nend\ndef silent\nfalse\nend\ndef to_s\nself.dup\nend\nend\n\nclass TrueClass\ndef method_missing(*usersave)\ntrue\nend\nend\n\nclass Script\n@@wizard_save_last ||= String.new\n@@Watchfors ||= Array.new\n@@wizard_save ||= Hash.new\nat_exit { Script.shutdown_reap }\n@@wizard_cmds ||= Hash.new\nattr_accessor :opts\n\ndef wizard_cmds_init\nundef :wizard_cmds_init\n@@wizard_cmds['counter'] ||= <<-'COUNTER'\nif @lines[@stackptr] =~ /add/i\nif (csub = @lines[@stackptr].slice(/-?\\d+/).to_i).nonzero?\n@wizard_counter += csub\nelse\n@wizard_counter += 1\nend\nelsif @lines[@stackptr] =~ /subtract|sub/i\nif (csub = @lines[@stackptr].slice(/-?\\d+/).to_i).nonzero?\n@wizard_counter -= csub\nelse\n@wizard_counter -= 1\nend\nelsif @lines[@stackptr] =~ /divide/i\n@wizard_counter = (@wizard_counter / @lines[@stackptr].split.last.chomp.to_i).truncate\nelsif @lines[@stackptr] =~ /multiply/i\n@wizard_counter = (@wizard_counter * @lines[@stackptr].split.last.chomp.to_i).truncate\nelsif @lines[@stackptr] =~ /set/i\n@wizard_counter = @lines[@stackptr].split.last.chomp.to_i\nend\nCOUNTER\n@@wizard_cmds['echo'] ||= <<-'ECHO'\nrespond(@lines[@stackptr].sub(/echo\\s?/i, '').strip)\nECHO\n@@wizard_cmds['save'] ||= <<-'SAVE'\n@wizard_save = @lines[@stackptr].sub(/save /i, '').gsub('\"','').gsub('_',\"\\s\").strip\n@@wizard_save[@name.to_s] = @wizard_save.dup; @@wizard_save_last = @wizard_save.dup\nSAVE\n@@wizard_cmds['put'] ||= <<-'PUT'\nif @lines[@stackptr] =~ /^\\s*put\\s+\\.([^\\s]+)(\\s.+)?/i\nif start_wizard_script($1.dup,$2.split)\nname = $1\nwait_until { Script.find(name) }\nexit\nend\nelse\nwaitrt?\nfput(\"#{@lines[@stackptr].sub(/put /i, '').strip}\")\nend\nPUT\n@@wizard_cmds['pause'] ||= <<-'PAUSE'\nif @lines[@stackptr].split[1].to_f <= 0\nptime = 1\nelse\nptime = @lines[@stackptr].split[1].to_f\nend\nsleep(ptime)\nwaitrt?\nPAUSE\n@@wizard_cmds['goto'] ||= <<-'GOTO'\ntarget = @lines[@stackptr].split[1]\ntarget.delete!(\":\")\nif (found_label = @labels[target]).nil?\nif (found_label = @labels[@labels.keys.find { |val| val =~ /\\b#{target}\\b/i }]).nil?\nif (found_label = @labels[@labels.keys.find { |val| val =~ /labelerror/i }]).nil?\necho(\"Fatal label error; '#{target}' not found, labelerror not found.\")\nexit\nend\nsleep 0.02\nend\nend\nFile.open($lich_dir + \"lich_debug.txt\", \"a\") { |f| f.puts \"JUMP:%04s:%04s\" % [ @stackptr, found_label ] } if $WIZARD_DEBUG\n@stackptr = found_label\nGOTO\n@@wizard_cmds['waitforre'] ||= <<-'WAITFORRE'\nregexp = eval(@lines[@stackptr].sub(/waitforre /i,'').strip); waitforre(regexp)\nWAITFORRE\n@@wizard_cmds['waitfor'] ||= <<-'WAITFOR'\nwaitfor(Regexp.escape(@lines[@stackptr].sub(/waitfor /i, '').strip))\nWAITFOR\n@@wizard_cmds['wait'] ||= <<-'WAIT'\nclear\nget\nWAIT\n@@wizard_cmds['setvariable'] ||= <<-'SETVARIABLE'\n@setvars[@lines[@stackptr].split[1]] = @lines[@stackptr].sub(/^\\s*setvariable\\s+[^\\s]+/i,'').strip\nSETVARIABLE\n@@wizard_cmds['deletevariable'] ||= <<-'DELETEVARIABLE'\n@setvars[@lines[@stackptr].split[1]] = String.new\nDELETEVARIABLE\n@@wizard_cmds['matchre'] ||= @@wizard_cmds['match'] ||= <<-'MATCH'\n@match_table_labels.push(@lines[@stackptr].split[1].strip)\nif @lines[@stackptr] =~ /\\bmatch\\b/i\n@match_table_strings.push(Regexp.escape(@lines[@stackptr].sub(/\\s*match #{@match_table_labels.last} /i, '')))\nelse\n@match_table_strings.push(@lines[@stackptr].sub(/\\s*matchre #{@match_table_labels.last} /i, '').sub(/\\/i$/,'').gsub('/',''))\nend\nMATCH\n@@wizard_cmds['matchwait'] ||= <<-'MATCHWAIT'\nthe_match = waitfor(@match_table_strings.join('|'))\nidx = @match_table_strings.index(@match_table_strings.find { |val| the_match =~ /#{val}/i })\nif (found_label = @labels[@match_table_labels[idx]]).nil?\nfound_label = @labels[@labels.keys.find { |lbl| lbl =~ /\\b#{@match_table_labels[idx]}\\b/i }]\nif found_label.nil?\nif (found_label = @labels[@labels.keys.find { |lbl| lbl =~ /labelerror/i }]).nil?\necho(\"Fatal label error; '#{@match_table_labels[idx]}' not found, labelerror not found.\")\nexit\nend\nend\nend\n@stackptr = found_label\n@match_table_labels.clear\n@match_table_strings.clear\nMATCHWAIT\n@@wizard_cmds['move'] ||= <<-'MOVE'\nif @rev == true\ndir = @lines[@stackptr].sub(/move /i, '').strip\nif dir =~ /\\bu\\b|\\bup\\b/i then move(\"down\")\nelsif dir =~ /\\bd\\b|\\bdown\\b/i then move(\"up\")\nelsif dir =~ /\\bn\\b|\\bnorth\\b/i then move(\"south\")\nelsif dir =~ /\\bne\\b|\\bnortheast\\b/i then move(\"southwest\")\nelsif dir =~ /\\be\\b|\\beast\\b/i then move(\"west\")\nelsif dir =~ /\\bse\\b|\\bsoutheast\\b/i then move(\"northwest\")\nelsif dir =~ /\\bs\\b|\\bsouth\\b/i then move(\"north\")\nelsif dir =~ /\\bsw\\b|\\bsouthwest\\b/i then move(\"northeast\")\nelsif dir =~ /\\bw\\b|\\bwest\\b/i then move(\"east\")\nelsif dir =~ /\\bnw\\b|\\bnorthwest\\b/i then move(\"southeast\")\nelse move(\"#{dir}\")\nend\nelse\nmove(\"#{@lines[@stackptr].sub(/move /i, '').strip}\")\nend\nMOVE\n@@wizard_cmds['exit'] ||= <<-'EXIT'\nexit unless @rev == true\nEXIT\n@@wizard_cmds['shift'] ||= <<-'SHIFT'\n@vars.shift\nSHIFT\n@@wizard_cmds['nextroom'] ||= <<-'NEXTROOM'\nroomcount = $room_count\nwaitfor('\\[[^\\]]+\\]') while roomcount == $room_count\nNEXTROOM\nend\nprivate :wizard_cmds_init\ndef Script.namescript_incoming_stable(string)\n@@names.each { |script| script.io.push(string) }\n@@wakeme.each { |thr|\nbegin\nthr.wakeup\nrescue\nend\n}\nnil\nend\ndef Script.shutdown_reap\nif Script.self\necho \"Uh, this script is trying to shut Lich down (it's tried to execute the `cleanup' method Lich uses just before it quits). This is really of no use to scripts: the method call is being ignored.\"\nsleep 1\nreturn nil\nend\nScript.index.each { |script| script.kill }\nThread.list.each { |thr|\nbegin\nthr.join(2) if (thr.alive? and thr != Thread.current and thr != Thread.main)\nrescue\nend\n}\nnil\nend\ndef Script.wrap_script(fname, iname, cmdline)\nThread.new {\nscript = Script.new(fname)\ncatch(:reap) {\nbegin\npipe = IO.popen(\"\\\"#{iname}\\\" \\\"#{$script_dir + fname}\\\" #{cmdline}\", \"w+\")\npipe.sync = true\nscript.instance_variable_set(:@pipe, pipe)\nrescue SystemCallError\nrespond(\"--- Lich: #{$!}\")\nthrow :reap\nrescue Exception\nrespond(\"--- Lich: #{$!}\")\nthrow :reap\nrescue\nrespond(\"--- Lich: #{$!}\")\nthrow :reap\nend\nscript.set_as_good\nscript.instance_variable_get(:@dying_procs).push proc { pipe.close unless pipe.closed? }\nwhile line = pipe.gets\nif line =~ /^LICH:/\nbegin\nif resp = eval(line.sub(/[^:]+:\\s*/, ''), nil, script.name)\npipe.puts resp\nend\nrescue SyntaxError\nrespond(\"--- Lich: #{$!}\")\nthrow :reap\nrescue\nrespond(\"--- Lich: #{$!}\")\nthrow :reap\nend\nnext\nend\nfput line\nend\n}\npipe = script.instance_variable_get(:@pipe)\nscript.instance_variable_set(:@pipe, nil)\npipe.close unless pipe.closed?\nscript.kill\nrespond(\"--- Lich: #{script} has ended.\")\n}\ntrue\nend\ndef initialize(name,cli_vars=[])\n@thread = Thread.current\n@threads = ThreadGroup.new\n@threads.add(Thread.current)\nif cli_vars.first =~ /^quiet$/i\n@quiet = @quiet_exit = true\ncli_vars.shift\nelse \n@quiet = @quiet_exit = false\nend\n@safe = false\nif name == 'exec'\n@num = '1'\nwhile @@index.find { |script| script.name == \"exec%s\" % @num }\n@num.succ!\nend\n@name = \"exec#{@num}\"\nelse\n@name = name\n@keysig = \"#{@name}#{self.object_id.abs}\"\n@labels = Hash.new\n@vars = Array.new\nunless cli_vars.empty?\ncli_vars.each_index { |idx|\n@vars[idx+1] = cli_vars[idx]\n}\n@vars[0] = @vars[1..-1].join(' ')\nend\nend\n@wizard = false\n@io = []\n@unique_io = []\n@upstream_io = []\n@opts = []\nend\ndef init_wizard_script(name)\n@name = name.dup\n@keysig = \"#{@name}#{self.object_id.abs}\"\n@setvars = Hash.new\n@labels = Hash.new\n@stackptr = 0\n@wizard = true\n@wizard_counter = 0\n@wizard_save = @@wizard_save[@name.to_s]\n@wizard_save = @@wizard_save_last if @wizard_save.nil?\n@match_table_labels = Array.new\n@match_table_strings = Array.new\ntrue\nend\ndef load_wizard_lines(file,rev=false)\nbegin\nline = \"\"\nbegin\ngzfile = Zlib::GzipReader.open(file)\nFile.open(\"uncompress.cmd\", \"w\") { |f| f.write gzfile.read }\ngzfile.close\nfile = \"uncompress.cmd\"\nrescue\nend\nfile = File.open(file)\nwizard_parse_file(file)\nfile.close\nFile.unlink \"uncompress.cmd\" if File.exists? \"uncompress.cmd\"\nfile = nil\nrescue\nrespond(\"--- Lich: fatal error opening file: #{file}\")\n return false\nensure\nfile.close unless !file or file.closed? \nend\nif rev == true\n@rev = true\n@lines.reverse!\n@lines.each_index { |idx| if @lines[idx] =~ /^[0-9A-z_]+:$/ then @labels[@lines[idx].chop] = idx end }\nelse\n@rev = false\nend\ntrue\nend\ndef process_next_wizard_line(script=nil)\nwhile @paused\n@sleeplist.push(Thread.current)\nsleep\n@sleeplist.delete(Thread.current)\nend\nreturn nil if @lines[@stackptr].nil?\n@real_line = @lines[@stackptr].dup\n@real_ptr = @stackptr\nif @lines[@stackptr] =~ /\\%user([0-9])(?:\\%)?/i then @lines[@stackptr].gsub!(/\\%user[0-9](?:\\%)?/i, Lich.user($1)) end\n@lines[@stackptr] = @lines[@stackptr].gsub(/\\%container(?:\\%)?/i, \"#{Lich.lootsack}\").gsub(/\\%c(?:\\%)?/i, @wizard_counter.to_s.strip).gsub(/%s(?:\\%)?/i, @wizard_save.to_s.strip)\nwhile @lines[@stackptr] =~ /\\bif\\_(\\d)\\b/i\nif @vars[$1.to_i].nil?\n@lines[@stackptr] = \"\"\nelse\n@lines[@stackptr] = @lines[@stackptr].sub(/\\bif\\_\\d\\b/i, \"\")\nend\nend\nif @lines[@stackptr] =~ /\\%0(?:\\%)?/\n@lines[@stackptr] = @lines[@stackptr].sub(/\\%0(?:\\%)?/,@vars[1..-1].join(' ').gsub('_',\"\\s\"))\nend\nwhile @lines[@stackptr] =~ /\\%([1-9])(?:\\%)?/\n@lines[@stackptr] = @lines[@stackptr].sub(/\\%[1-9](?:\\%)?/, @vars[$1.to_i].to_s)\nend\nwhile @lines[@stackptr] =~ /\\%(#{@setvars.keys.join('|')})\\%?/i\nrepvar = $1.dup\n@lines[@stackptr] = @lines[@stackptr].sub(/\\%#{$1}\\%?/,@setvars[repvar])\nend\nwizcmd = @lines[@stackptr].slice(/[^\\s]+/).downcase\nwizard_cmds_init rescue()\nif wizprc = @@wizard_cmds[wizcmd]\neval(wizprc, nil, Script.self.name, @stackptr)\nelsif @lines[@stackptr] =~ /^LICH\\s*\\{/\nlichcode = Array.new; lichcode.push('$SAFE = 3')\nline = @lines[@stackptr]\nuntil line =~ /^\\}\\s*LICH/\n@stackptr += 1; break if @lines[@stackptr].nil?\nlichcode.push(@lines[@stackptr])\nline = @lines[@stackptr]\nend\neval(lichcode[0..-2].join(\"\\n\"), nil, Script.self.name, 0)\nend\n@lines[@real_ptr] = @real_line\n@stackptr += 1\ntrue\nend\ndef setup_labels(filename,run_safe = false)\nbegin\ncrit = Thread.critical\nThread.critical = true\nbegin\nfile = nil\nfile = Zlib::GzipReader.open(filename)\nrescue\nfile.close rescue()\nfile = File.open(filename)\nend\nif file.gets =~ /^[\\t\\s]*#?[\\t\\s]*(?:quiet|hush)\\r?$/i\n@quiet = @quiet_exit = true\nend\nfile.rewind\nary = file.read.split(/\\r?\\n([\\d_\\w]+):\\s*\\r?\\n/)\nensure\nfile.close\nfile = nil\nThread.critical = crit\nend\n@labels,@label_order,@line_no,line = Hash.new,Hash.new,Hash.new,String.new\n@current_label = @jump_label = @keysig\n@labels[@current_label] = String.new\n@labels[@current_label] = \"script.set_as_good\" + (run_safe ? \"\\n$SAFE = 3\\necho 'This script is being run in SAFE mode'\\nsleep 0.5\\n\" : \"\\n\")\n@line_no[@current_label] = 0\n@labels[@current_label] += ary.shift\ndata = false\nwhile line = ary.shift\nif data\n@labels[@current_label] = line\ndata = false\nelse\nline_no += @labels[@current_label].count(\"\\n\")\n@label_order[@current_label] = line\n@current_label = line\n@line_no[@current_label] = line_no\ndata = true\nend\nend\ntrue\nend\ndef fetch_next_label\nif !@jump_label\n@current_label = @label_order[@current_label]\n@current_label.nil? ? nil : @labels[@current_label]\nelse\nif lbl = @labels.keys.find { |val| val =~ /^#{@jump_label}$/ } then @current_label = lbl\nelsif lbl = @labels.keys.find { |val| val =~ /^#{@jump_label}$/i } then @current_label = lbl\nelsif lbl = @labels.keys.find { |val| val =~ /^labelerror$/i } then @current_label = lbl\nelse @current_label = nil; return JUMP_ERROR\nend\n@jump_label = nil\nsleep 0.001\n@labels[@current_label]\nend\nend\ndef set_as_good\n@timestamp = Time.now\n@upstream = false\n@stand_alone = false\n@sleeplist = Array.new\n@pause_pushback = Array.new\n@dying_procs = Array.new\n@die_with = Array.new\n@match_stack_labels = Array.new\n@match_stack_strings = Array.new\n@paused = false\n@no_ka = false\n@status = false\n@unique = false\n@silent = false\n@no_echo = false\n@no_pause = false\n@@thread_hash[Thread.current.group] = self\n@@index.push self\n@@names.push self\nsetpriority(-10) if @wizard\nrespond(\"--- Lich: #{@name} active.\") unless @quiet\nend\ndef i_stand_alone\n@@names.delete(self)\n@@status_scripts.delete(self)\n@@uniques.delete(self)\n@@upstream_index.delete(self)\nself.clear\n@stand_alone = true\necho(\"Script has removed itself from Lich's data queues and is functioning independently\")\nend\ndef take_me_back\n@@names.push(self)\n@stand_alone = false\necho(\"Script is no longer functioning independently and is receiving data as normal\")\nend\ndef toggle_upstream\nif @upstream\n@upstream = false\n@@upstream_index.delete(self)\n@@names.push(self) unless @@names.include?(self)\nelse\n@upstream = true\n@@upstream_index.push(self) unless @@upstream_index.include?(self)\n@@names.delete(self)\nend\necho(\"Listening to upstream (from local computer to remote server) data is now: #{@upstream.to_s.sub('false', 'off').sub('true', 'on')}\")\nend\ndef feedme_upstream\nif @upstream\n@upstream = false\n@@upstream_index.delete(self)\nelse\n@upstream = true\n@@upstream_index.push(self) unless @@upstream_index.include?(self)\nend\nend\ndef kill\n@name.sub!(/ \\(paused\\)$/,'')\nkill_thread = Thread.current\ncleanup_thread = Thread.new {\n@threads.add(Thread.current)\n@paused = false\n$SAFE = 3 if @safe\ndying_procs_thread = Thread.new {\n@dying_procs.each { |runme|\nbegin\nrunme.call\nrescue SyntaxError\necho(\"Syntax error in dying code block: #{$!}\")\nrescue SystemExit\nnil\nrescue Exception\nif $! == JUMP or $! == JUMP_ERROR\necho(\"Cannot execute jumps in before_dying code blocks...!\")\nelse\necho(\"Fatal error in dying code block: #{$!}\")\nend\nrescue\necho(\"Fatal error in dying code block: #{$!}\")\nend\n}\n}\ndying_procs_thread.join(2) if dying_procs_thread.alive? and !(@dying_procs.nil? or @dying_procs.empty?)\ndying_procs_thread.kill if dying_procs_thread.alive?\n@dying_procs.clear\n@dying_procs = nil\n@threads.list.each { |thr|\n@@wakeme.delete(thr)\n@@wakeme_status.delete(thr)\n@@wakeme_uniques.delete(thr)\n@@wakeme_upstream.delete(thr)\n@@thread_hash.delete(thr.group)\n}\n@sleeplist.clear\n@sleeplist = nil\n@data.clear; @labels.clear; @lines.clear; @label_order.clear; @match_table_labels.clear; @match_table_strings.clear\n@io.clear; @unique_io.clear; @upstream_io.clear; @match_stack_labels.clear; @match_stack_strings.clear\n@data, @labels, @lines, @label_order, @match_table_labels, @match_table_strings = nil, nil, nil, nil, nil, nil\n@io, @unique_io, @upstream_io, @match_stack_labels, @match_stack_strings, @jump_label, @current_label = nil,nil,nil,nil,nil,nil,nil\n@pause_pushback.clear; @pause_pushback = nil\n@@index.delete(self)\n@@names.delete(self)\n@@status_scripts.delete(self)\n@@uniques.delete(self)\n@@upstream_index.delete(self)\n@threads.list.each { |thr|\nif thr != Thread.current and thr != kill_thread and thr.alive?\nthr.kill rescue()\nend\n}\nGC.start\n}\n@die_with.each { |killit|\ntodie = Script.find(killit)\ntodie.kill unless todie == Script.self\n}\n@die_with.clear\n@die_with = nil\n@name\nend\ndef toggle_unique\nif @unique\n@@uniques.delete(self)\n@@names.push(self)\n@unique = false\necho(\"This script is now receiving game data in the normal fashion\")\nelse\n@@names.delete(self)\n@@uniques.push(self)\n@unique = true\necho(\"This script will now only see data sent to it specifically\")\nend\nend\ndef unique?\n@unique\nend\ndef toggle_no_ka\n@no_ka = !@no_ka\nend\ndef toggle_pausable\n@no_pause = !@no_pause\nend\ndef help?\nself.vars.qfind(/\\bhelp\\b/) or self.vars.empty? ? true : false\nend\ndef to_s\n@name\nend\ndef pause\nif @paused\nrespond(\"--- Lich: #{@name.sub(' (paused)', '')} is already paused.\")\nelse\n@paused = true\nrespond(\"--- Lich: #{@name} paused.\")\n@name = \"#{@name} (paused)\"\nend\nend\ndef unpause\nif !@paused\nrespond \"--- Lich: but #{@name} isn't paused!\"\nreturn\nend\n@name.sub!(/ \\(paused\\)$/,'')\nrespond(\"--- Lich: #{@name} unpaused.\")\n@paused = false\n@sleeplist.each { |sleeping|\nbegin\nsleeping.wakeup if sleeping.alive?\nrescue\nrespond(\"--- Lich: error while unpausing #{self}: #{$!.strip}.\") if $LICH_DEBUG\nend\n}\ntrue\nend\ndef Script.find(f_name=nil)\nif f_name.nil?\nScript.self\nelse\nscript = @@index.find { |scr| scr.name == f_name.to_s } ||\n@@index.find { |scr| scr.name =~ /^#{f_name.to_s}$/i } ||\n@@index.find { |scr| scr.name =~ /^#{f_name.to_s}/i }\nScript.self\nscript\nend\nend\ndef Script.wakelist\n(@@wakeme + @@wakeme_upstream +\n@@wakeme_uniques + @@wakeme_status).collect { |thr|\n@@thread_hash[thr.group]\n}.uniq\nend\ndef match_stack_add(label,string)\n@match_stack_labels.push(label)\n@match_stack_strings.push(string)\nend\ndef match_stack_clear\n@match_stack_labels.clear\n@match_stack_strings.clear\nend\nend\n\nclass Opt\nattr_accessor :block, :help, :names, :params\ndef initialize(names, params = 0, help = nil, &block)\n@help, @params, @block = help, params, block\n@names = names.to_a.flatten.collect { |val| val.strip }\nend\ndef call(*args)\n@block.call(args)\nend\ndef Opt.opt(*args, &block)\nobj = new(*args)\nobj.block = block\nScript.self.opts.push obj\nend\ndef Opt.on(*args, &block)\nOpt.opt(*args, &block)\nend\ndef Opt.add_help\nscript = Script.self\nif !script.opts.find { |opt| opt.names.find { |op| op =~ /^h(?:elp)?$/i } }\nOpt.opt([\"help\", \"h\"]) {\necho\nrespond \"Usage: #{$clean_lich_char}#{Script.self} [option].\"\necho\nrespond \"Options are:\"\nScript.self.opts.each { |op|\necho\nrespond \" #{op.names.join(', ')}\"\nrespond \"\\t#{op.help}\"\n}\nexit\n}\nend\nend\ndef Opt.parse(ary = Script.self.vars)\nOpt.add_help\nscript = Script.self\nscript.vars[1..-1].each { |uservar|\nif opt_provided = script.opts.find { |op| op.names.find { |nm| nm =~ /\\b#{uservar}\\b/i } }\nidx = script.vars.index(uservar)\nargary = []\nargary.push script.vars.delete_at(idx)\nopt_provided.params.times { argary.push script.vars.delete_at(idx) }\nargary.compact!\nopt_provided.call(*argary)\nend\n}\nend\nend\n#\n\ndef debug(*args)\nif $LICH_DEBUG\nif block_given?\nyield(*args)\nelse\necho(*args)\nend\nend\nend\n\ndef timetest(*contestants)\ncontestants.collect { |code| start = Time.now; 5000.times { code.call }; Time.now - start }\nend\n\ndef goto(label)\nscript = Script.self\nscript.jump_label = label.to_s\n$! = JUMP\nraise $!\nend\n\ndef dec2bin(n)\n\"0\" + [n].pack(\"N\").unpack(\"B32\")[0].sub(/^0+(?=\\d)/, '')\nend\n\ndef bin2dec(n)\n[(\"0\"*32+n.to_s)[-32..-1]].pack(\"B32\").unpack(\"N\")[0]\nend\n\ndef parse_list(string)\nstring.split_as_list\nend\n\ndef idle?(time = 60)\nTime.now - $_IDLETIMESTAMP_ >= time\nend\n\ndef selectput(string, success, failure, timeout = nil)\ntimeout = timeout.to_f if timeout and !timeout.kind_of?(Numeric)\nsuccess = success.to_a if success.kind_of? String\nfailure = failure.to_a if failure.kind_of? String\nraise ArgumentError, \"usage is: selectput(game_command,success_array,failure_array[,timeout_in_secs])\" if\n!string.kind_of?(String) or !success.kind_of?(Array) or\n!failure.kind_of?(Array) or timeout && !timeout.kind_of?(Numeric)\n\nsuccess.flatten!\nfailure.flatten!\nregex = /#{(success + failure).join('|')}/i\nsuccessre = /#{success.join('|')}/i\nfailurere = /#{failure.join('|')}/i\nthr = Thread.current\n\ntimethr = Thread.new {\ntimeout -= sleep(0.1) until timeout <= 0\nthr.raise(StandardError)\n} if timeout\n\nbegin\nloop {\nfput(string)\nresponse = waitforre(regex)\nif successre.match(response.to_s)\ntimethr.kill if timethr.alive?\nbreak(response.string)\nend\nyield(response.string) if block_given?\n}\nrescue\nnil\nend\nend\n\ndef maxhealth\n$_TAGHASH_['MGSX'].to_i or 0\nend\n\ndef maxmana\n$_TAGHASH_['MGSZ'].to_i or 0\nend\n\ndef maxspirit\n$_TAGHASH_['MGSY'].to_i or 0\nend\n\ndef toggle_unique\nScript.self.toggle_unique\nend\n\ndef no_kill_all\nScript.self.toggle_no_ka\nend\n\ndef die_with_me(*vals)\nunless scr = Script.self\nrespond(\"In 'die_with_me' -- cannot identify calling script! Killing thread\")\nThread.current.kill\nend\nscr.die_with.push vals\nscr.die_with.flatten!\necho(\"The following script(s) will now die when I do: #{scr.die_with.join(', ')}\") unless scr.die_with.empty?\nend\n\ndef silence_me\nunless scr = Script.self\nrespond \"Cannot identify what script is calling the `silence_me' method; killing this unrecognizable thread.\"\nThread.current.kill\nend\nif scr.safe? then echo(\"WARNING: 'safe' script attempted to silence itself. Ignoring the request.\")\nsleep 1\nreturn true\nend\nscr.silent = !scr.silent\nend\n\ndef upstream_get\nport = Script.self\nunless port.upstream then echo(\"This script wants to listen to the upstream, but it isn't set as receiving the upstream! This will cause a permanent hang, aborting (ask for the upstream with 'toggle_upstream' in the script)\") ; return false end\nport.upstream_gets\nend\n\ndef upstream_waitfor(*strings)\nstrings.flatten!\nport = Script.self\nunless port.upstream then echo(\"This script wants to listen to the upstream, but it isn't set as receiving the upstream! This will cause a permanent hang, aborting (ask for the upstream with 'toggle_upstream' in the script)\") ; return false end\nregexpstr = strings.join('|')\nwhile line = port.upstream_gets\nif line =~ /#{regexpstr}/i\nreturn line\nend\nend\nend\n\ndef toggle_upstream\nunless script = Script.self\nrespond \"Unable to identify the script attempting to toggle upstream data! Cannot comply with call; killing this unrecognized thread.\"\nThread.current.kill\nend\nscript.toggle_upstream\nend\n\ndef toggle_status\nunless script = Script.self\nrespond \"Unable to identify the script attempting to enable status-tag data! Cannot comply with call; killing this unrecognized thread.\"\nThread.current.kill\nend\nscript.toggle_status\nend\n\ndef checkpoison\nif $_TAGHASH_['GSJ'].nil? then return false end\np_per = $_TAGHASH_['GSJ'].to_s[-15..-13].to_i\np_dis = $_TAGHASH_['GSJ'].to_s[-3..-1].to_i\nif p_per.zero? then return false end\nreturn [p_per,p_dis]\nend\n\ndef checkdisease\nif $_TAGHASH_['GSK'].nil? then return false end\nd_per = $_TAGHASH_['GSK'].to_s[-15..-13].to_i\nd_dis = $_TAGHASH_['GSK'].to_s[-3..-1].to_i\nif d_per.zero? then return false end\nreturn [d_per,d_dis]\nend\n\ndef survivepoison?\nif checkpoison\nrate,dissipation = checkpoison\nelse\nreturn true\nend\nhealth = checkhealth\nn = 0\nuntil rate <= 0\nhealth -= rate\nrate -= dissipation\nn += 1\nif health <= 0 then return false end\nend\ntrue\nend\n\ndef before_dying(&code)\nunless script = Script.self\nrespond \"Unable to identify the script that's attempting to register a before_dying code block -- cannot comply with call!\"\nThread.current.kill\nend\nif code.nil?\necho \"No code block was given to the `before_dying' command! (a \\\"code block\\\" is the stuff inside squiggly-brackets); cannot register a block of code to run when this script dies unless it provides the code block.\"\nsleep 1\nreturn nil\nend\nscript.dying_procs.push(code)\ntrue\nend\n\ndef undo_before_dying\nunless script = Script.self\nrespond \"Unable to identify the script that's attempting to clear its before_dying code blocks!\"\nThread.current.kill\nend\nscript.dying_procs.clear\nnil\nend\n\ndef abort!\nif script = Script.index.find { |scr| scr.threads == Thread.current.group }\nscript.dying_procs.clear\nexit\nelse\nrespond \"--- Lich: a script that is not being properly tracked has requested that it be `abort!'ed. This shouldn't happen; please email the Lich `;log' dump to GS4Lich@yahoo.com so that I can track down the possible cause for this. Killing this (unknown) script thread...\"\nThread.current.kill\nend\nend\n\ndef survivedisease?\nif checkdisease\nrate,dissipation = checkdisease\nelse\nreturn true\nend\nhealth = checkhealth\nn = 0\ndeadat = 0\nuntil rate <= 0\nhealth -= rate\nrate -= dissipation\nn += 1\nif health <= 0 then return false end\nend\ntrue\nend\n\ndef send_to_script(*values)\nvalues.flatten!\nif scr = Script.index.find { |val| val.name =~ /^#{values.first}/i }\nvalues[1..-1].each { |val| scr.puts(val) }\necho(\"Sent to #{scr} -- '#{values[1..-1].join(' ; ')}'\")\nreturn true\nelse\necho(\"'#{values.first}' does not match any active scripts!\")\nreturn false\nend\nend\n\ndef unique_send_to_script(*values)\nvalues.flatten!\nif scr = Script.index.find { |val| val.name =~ /^#{values.first}/i }\nvalues[1..-1].each { |val| scr.unique_puts(val) }\necho(\"sent to #{scr}: #{values[1..-1].join(' ; ')}\")\nreturn true\nelse\necho(\"'#{values.first}' does not match any active scripts!\")\nreturn false\nend\nend\n\ndef unique_waitfor(*strings)\nstrings.flatten!\nscr = Script.self\nregexp = /#{strings.join('|')}/\nwhile true\nstr = scr.unique_gets\nif str =~ regexp\nreturn str\nend\nend\nend\n\ndef unique_get\nScript.self.unique_gets\nend\n\ndef send_lichnet_string(string)\nif running = Script.index.find { |script| script.name =~ /lichnet/i }\nrunning.unique_puts(string)\nelse\nrespond(\"You aren't running the `LichNet' client script! Type `#{$clean_lich_char}lichnet' to start it.\")\nend\nend\n\ndef multimove(*dirs)\ndirs.flatten.each { |dir| move(dir) }\nend\n\ndef n\n'north'\nend\ndef ne\n'northeast'\nend\ndef e\n'east'\nend\ndef se\n'southeast'\nend\ndef s\n'south'\nend\ndef sw\n'southwest'\nend\ndef w\n'west'\nend\ndef nw\n'northwest'\nend\ndef u\n'up'\nend\ndef up\n'up'\nend\ndef down\n'down'\nend\ndef d\n'down'\nend\ndef o\n'out'\nend\ndef out\n'out'\nend\n\ndef move(dir=\"none\")\nattempts = 0\nif dir == \"none\"\necho(\"Error! Move without a direction to move in!\")\nreturn false\nelse\nroomcount = $room_count\nclear\nmoveflag = true\nput(\"#{dir}\")\nwhile feed = get\nif feed =~ /can't go there|Where are you trying to go|What were you referring to\\?| appears to be closed\\.|I could not find what you were referring to\\.|You can't climb that\\./\necho(\"Error, can't go in the direction specified!\")\nScript.self.io.unshift(feed)\nreturn false\nelsif feed =~ /Sorry, you may only type ahead/\nsleep(1)\nclear\nput(\"#{dir}\")\nnext\nelsif feed =~ /will have to stand up first|must be standing first/\nclear\nput(\"stand\")\nwhile feed = get\nif feed =~ /struggle.+stand/\nclear\nput(\"stand\")\nnext\nelsif feed =~ /stand back up|You scoot your chair back and stand up\\./\nclear\nput(\"#{dir}\")\nbreak\nelsif feed =~ /\\.\\.\\.wait /\nwait = $'.split.first.to_i\nsleep(wait)\nclear\nput(\"stand\")\nnext\nelsif feed =~ /Sorry, you may only type ahead/\nsleep(1)\nclear\nput(\"stand\")\nnext\nelsif feed =~ /can't do that while|can't seem to|don't seem|stunned/\nsleep(1)\nclear\nput(\"stand\")\nnext\nelsif feed =~ /are already standing/\nclear\nput(\"#{dir}\")\nbreak\nelse\nstand_attempts = 0 if stand_attempts.nil?\nif stand_attempts >= 10\necho(\"Error! #{stand_attempts} unrecognized responses, assuming a script hang...\")\nScript.self.io.unshift(feed)\nreturn false\nend\nstand_attempts += 1\nsleep(1)\nclear\nput(\"stand\")\nnext\nend\nend\nelsif feed =~ /\\.\\.\\.wait |Wait /\nwait_time = $'.split.first.to_i\nsleep(wait_time)\nclear\nput(\"#{dir}\")\nnext\nelsif feed =~ /stunned/\nwait_while { stunned? }\nclear\nput(\"#{dir}\")\nnext\nelsif feed =~ /can't do that|can't seem to|don't seem /\nsleep(1)\nclear\nput(\"#{dir}\")\nnext\nelsif feed =~ /Please rephrase that command/\necho(\"error! Cannot go '#{dir}', game did not understand the command.\")\nScript.self.io.unshift(feed)\nreturn false\nelsif feed =~ /seems as though all the tables here are/\nsleep 1\nclear\nput(\"#{dir}\")\nnext\nelsif feed =~ /You head over to the .+ Table/\nScript.self.io.unshift(feed)\nreturn feed\nelsif feed =~ /Running heedlessly through the icy terrain, you slip on a patch of ice and flail uselessly as you land on your rear!/\nwaitrt?\nfput('stand') unless standing?; waitrt?; fput(dir); next\nelse\nif attempts >= 35\necho(\"#{attempts} unrecognized lines, assuming a script hang; move command has exited.\")\nScript.self.io.unshift(feed)\nreturn false\n#if feed =~ /\\[[^\\]]+\\]/ && feed !~ /\\[.*d\\s?100.*\\]/i or $room_count > roomcount\n#return feed\n#attempts += 1; next\nelse\nif $room_count > roomcount\nScript.self.io.unshift(feed)\nreturn feed\nelse\nattempts += 1; next\nend\nend\nend\nend\nend\nend\n\ndef checkloot\nfput(\"look\")\nitems = Array.new\nlinein = matchwait(\"You also see \", \"You notice \", \"Obvious exits:|Obvious paths:|Also here:\")\nif linein =~ /Obvious (exits|paths): |Also here: / then return false end\nlinein = linein.slice(/You (?:also see|notice) [^\\.]+\\./)\nlinein.sub!(/You (?:also see|notice) /, ',')\nlinein.sub('.','').sub(/ and (?:an?|some|the)/, ',').split(',').each { |full_name| items.push(full_name.slice(/[^\\s]+$/)) }\nif Lich.excludeloot.empty? then (regexpstr = nil) else (regexpstr = Lich.excludeloot.join('|')) end\nitems.shift\nif items.empty?\nreturn false\nelse\nreturn items\nend\nend\n\ndef fetchloot(userbagchoice=Lich.lootsack)\nfput(\"look\")\nitems = Array.new\nlinein = matchwait(\"You also see \", \"You notice \", \"Obvious exits:|Obvious paths:|Also here:\")\nif linein =~ /Obvious (?:exits|paths): |Also here: / then return false end\nlinein = linein.slice(/You (?:also see|notice) [^\\.]+\\./)\nunless Lich.excludeloot.empty? then (regexpstr = Lich.excludeloot.join('|')) end\nlinein.sub!(/You (?:also see|notice) /, ',')\nunless Lich.excludeloot.empty?\nlinein.sub('.','').sub(/ and (?:an?|some|the)/, ',').split(',').each { |full_name| items.push(full_name.slice(/[^\\s]+$/)) unless (full_name =~ /#{regexpstr}/) }\nelse\nlinein.sub('.','').sub(/ and (?:an?|some|the)/, ',').split(',').each { |full_name| items.push(full_name.slice(/[^\\s]+$/)) }\nend\nitems.shift\nitems.delete_if { |val| val =~ /^\\s*$|^\\s*and\\s*$/i || val.nil? }\nif items.empty?\nreturn false\nend\nif (righthand? && lefthand? && !$stormfront)\nweap = checkright\nfput \"put my #{checkright} in my #{Lich.lootsack}\"\nunsh = true\nelse\nunsh = false\nend\nitems.each { |trinket|\nfput \"take #{trinket}\"\nfput(\"put my #{trinket} in my #{userbagchoice}\") if (righthand? || lefthand? || $stormfront)\n}\nif unsh then fput(\"take my #{weap} from my #{Lich.lootsack}\") end\nend\n\ndef no_pause_all\nscript = Script.self\nscript.toggle_pausable\nend\n\ndef pause_script(*names)\nnames.flatten!\nif names.empty?\nScript.self.pause\nScript.self\nelse\nnames.each { |scr|\nfnd = Script.index.find { |nm| nm.name =~ /^#{scr}/i }\nfnd.pause unless (fnd.paused || fnd.nil?)\n}\nend\nend\n\ndef unpause_script(*names)\nnames.flatten!\nnames.each { |scr| fnd = Script.index.find { |nm| nm.name =~ /^#{scr}/i } ; fnd.unpause if (fnd.paused && !(fnd.nil?)) }\nend\n\ndef i_stand_alone\nloner = Script.self\nif loner.stand_alone\nloner.take_me_back\nelse\nloner.i_stand_alone\nend\nloner.stand_alone\nend\n\ndef take(*items)\nitems.flatten!\nif (righthand? && lefthand? && !$stormfront)\nweap = checkright\nfput \"put my #{checkright} in my #{Lich.lootsack}\"\nunsh = true\nelse\nunsh = false\nend\nitems.each { |trinket|\nfput \"take #{trinket}\"\nfput(\"put my #{trinket} in my #{Lich.lootsack}\") if (righthand? || lefthand? || $stormfront)\n}\nif unsh then fput(\"take my #{weap} from my #{Lich.lootsack}\") end\nend\n\ndef watchhealth(value, theproc=nil, &block)\nvalue = value.to_i\nif block.nil?\nif !theproc.respond_to? :call\nrespond \"`watchhealth' was not given a block or a proc to execute!\"\nreturn nil\nelse\nblock = theproc\nend\nend\nThread.new {\nwait_while { health(value) }\nblock.call\n}\nend\n\ndef waitrt\nuntil $_TAGHASH_[\"GSQ\"].to_i > $_TAGHASH_[\"GSq\"].to_i\nsleep 0.1\nend\nif $_TAGHASH_[\"GSq\"].to_i >= $_TAGHASH_[\"GSQ\"].to_i then return end\nsleep(($_TAGHASH_[\"GSQ\"].to_f - (Time.now.to_f - $_TIMEOFFSET_.to_f) + 0.6).abs)\nend\n\ndef waitrt?\nif $_TAGHASH_[\"GSQ\"].to_i > $_TAGHASH_[\"GSq\"].to_i then waitrt end\nend\n\ndef wait_until(announce=nil)\npriosave = Thread.current.priority\nThread.current.priority = -10\nunless announce.nil? or yield\nrespond(announce)\nend\nuntil yield\nsleep 0.25\nend\nThread.current.priority = priosave\nend\n\ndef wait_while(announce=nil)\npriosave = Thread.current.priority\nThread.current.priority = -10\nunless announce.nil? or !yield\nrespond(announce)\nend\nwhile yield\nsleep 0.25\nend\nThread.current.priority = priosave\nend\n\ndef checkpaths(dir=\"none\")\nif dir == \"none\"\ndirs = Array.new\n$_TAGHASH_[\"GSj\"].chomp.split('').each { |char|\nif char == \"A\"\ndirs.push(\"n\")\nelsif char == \"B\"\ndirs.push(\"ne\")\nelsif char == \"C\"\ndirs.push(\"e\")\nelsif char == \"D\"\ndirs.push(\"se\")\nelsif char == \"E\"\ndirs.push(\"s\")\nelsif char == \"F\"\ndirs.push(\"sw\")\nelsif char == \"G\"\ndirs.push(\"w\")\nelsif char == \"H\"\ndirs.push(\"nw\")\nelsif char == \"I\"\ndirs.push(\"up\")\nelsif char == \"J\"\ndirs.push(\"down\")\nelsif char == \"K\"\ndirs.push(\"out\")\nend\n}\nif dirs.empty?\nreturn false\nelse\nreturn dirs.to_a\nend\nelse\ndirs = Array.new\n$_TAGHASH_[\"GSj\"].chomp.split('').each { |char|\nif char == \"A\"\ndirs.push(\"n\",n)\nelsif char == \"B\"\ndirs.push(\"ne\",ne)\nelsif char == \"C\"\ndirs.push(\"e\",e)\nelsif char == \"D\"\ndirs.push(\"se\",se)\nelsif char == \"E\"\ndirs.push(\"s\",s)\nelsif char == \"F\"\ndirs.push(\"sw\",sw)\nelsif char == \"G\"\ndirs.push(\"w\",w)\nelsif char == \"H\"\ndirs.push(\"nw\",nw)\nelsif char == \"I\"\ndirs.push(\"up\",u)\nelsif char == \"J\"\ndirs.push(\"down\",d)\nelsif char == \"K\"\ndirs.push(\"out\",'o')\nend\n}\ndirs.include?(dir)\nend\nend\n\ndef reverse_direction(dir)\nif dir == \"n\" then 's'\nelsif dir == \"ne\" then 'sw'\nelsif dir == \"e\" then 'w'\nelsif dir == \"se\" then 'nw'\nelsif dir == \"s\" then 'n'\nelsif dir == \"sw\" then 'ne'\nelsif dir == \"w\" then 'e'\nelsif dir == \"nw\" then 'se'\nelsif dir == \"up\" then 'down'\nelsif dir == \"down\" then 'up'\nelsif dir == \"out\" then 'out'\nelsif dir == 'o' then out\nelsif dir == 'u' then 'down'\nelsif dir == 'd' then up\nelsif dir == n then s\nelsif dir == ne then sw\nelsif dir == e then w\nelsif dir == se then nw\nelsif dir == s then n\nelsif dir == sw then ne\nelsif dir == w then e\nelsif dir == nw then se\nelsif dir == u then d\nelsif dir == d then u\nelse echo(\"Cannot recognize direction to properly reverse it!\"); false\nend\nend\n\ndef walk(*boundaries, &block)\nboundaries.flatten!\nunless block.nil?\nuntil val = yield\nwalk(*boundaries)\nend\nreturn val\nend\nif $last_dir and !boundaries.empty? and checkroomdescrip =~ /#{boundaries.join('|')}/i\nmove($last_dir)\n$last_dir = reverse_direction($last_dir)\nreturn checknpcs\nend\ndirs = checkpaths\ndirs.delete($last_dir) unless dirs.length < 2\nthis_time = rand(dirs.length)\n$last_dir = reverse_direction(dirs[this_time])\nmove(dirs[this_time])\nchecknpcs\nend\n\ndef toggle_echo(onoff=\"none\")\nscript = Script.self\nif script.no_echo then script.no_echo = false else script.no_echo = true end\nend\n\ndef run\nloop { break unless walk }\nend\n\ndef checkfried\ncheckmind(8) or checkmind(9)\nend\n\ndef checkmind(string=nil)\nchkmind = $_TAGHASH_[\"GSr\"].chomp.strip\nif chkmind == \"A\"\nmind = \"clear as a bell\"\nelsif chkmind == \"B\"\nmind = \"fresh and clear\"\nelsif chkmind == \"C\"\nmind = \"clear\"\nelsif chkmind == \"D\"\nmind = \"muddled\"\nelsif chkmind == \"E\"\nmind = \"becoming numbed\"\nelsif chkmind == \"F\"\nmind = \"numbed\"\nelsif chkmind == \"G\"\nmind = \"fried\"\nelsif chkmind == \"H\"\nmind = \"fried\"\nelse\nmind = \"beyond fried\"\nend\nif string.nil?\nreturn mind\nelsif string.class == String and string.to_i == 0\nif string =~ /#{mind}/i\nreturn true\nelse\nreturn false\nend\nelsif string.to_i.between?(1,9)\nary = %w[A B C D E F G H]\nmind = ary.index($_TAGHASH_['GSr'].strip) + 1\nreturn string.to_i <= mind\nelse\necho(\"Checkmind error! You must provide an integer ranging from 1-9 (7 is fried, 8 is 100% fried, 9 is extremely rare and is impossible through normal means to reach but does exist), the common abbreviation of how full your head is, or provide no input to have checkmind return an abbreviation of how filled your head is.\") ; sleep 1\nreturn false\nend\nend\n\ndef checkarea(*strings)\nstrings.flatten! ; if strings.empty? then return $roomarea.sub('[','') end\n$roomarea =~ /#{strings.join('|')}/i\nend\n\ndef checkroom(*strings)\nstrings.flatten! ; if strings.empty? then return $roomtitle.chomp end\n$roomtitle =~ /#{strings.join('|')}/i\nend\n\ndef outside?\n$_PATHSLINE_ =~ /Obvious paths:/\nend\n\ndef checkfamarea(*strings)\nstrings.flatten!\nif strings.empty? then return $familiar_area.sub('[','') end\n$familiar_area =~ /#{strings.join('|')}/i\nend\n\ndef checkfampaths(dir=\"none\")\nif dir == \"none\"\ndirs = Array.new\n$familiar_paths.chomp.split('').each { |char|\nif char == \"A\"\ndirs.push(\"n\")\nelsif char == \"B\"\ndirs.push(\"ne\")\nelsif char == \"C\"\ndirs.push(\"e\")\nelsif char == \"D\"\ndirs.push(\"se\")\nelsif char == \"E\"\ndirs.push(\"s\")\nelsif char == \"F\"\ndirs.push(\"sw\")\nelsif char == \"G\"\ndirs.push(\"w\")\nelsif char == \"H\"\ndirs.push(\"nw\")\nelsif char == \"I\"\ndirs.push(\"up\")\nelsif char == \"J\"\ndirs.push(\"down\")\nelsif char == \"K\"\ndirs.push(\"out\")\nend\n}\nif dirs.empty?\nreturn false\nelse\nreturn dirs.to_a\nend\nelse\ndirs = Array.new\n$familiar_paths.split('').each { |char|\nif char == \"A\"\ndirs.push(\"n\")\nelsif char == \"B\"\ndirs.push(\"ne\")\nelsif char == \"C\"\ndirs.push(\"e\")\nelsif char == \"D\"\ndirs.push(\"se\")\nelsif char == \"E\"\ndirs.push(\"s\")\nelsif char == \"F\"\ndirs.push(\"sw\")\nelsif char == \"G\"\ndirs.push(\"w\")\nelsif char == \"H\"\ndirs.push(\"nw\")\nelsif char == \"I\"\ndirs.push(\"up\")\nelsif char == \"J\"\ndirs.push(\"down\")\nelsif char == \"K\"\ndirs.push(\"out\")\nend\n}\nif dirs.find { |val| val =~ /#{dir}/i }\nreturn true\nelse\nreturn false\nend\nend\nend\n\ndef checkfamroom(*strings)\nstrings.flatten! ; if strings.empty? then return $familiar_room.chomp end\n$familiar_room =~ /#{strings.join('|')}/i\nend\n\ndef checkfamnpcs(*strings)\nparsed = Array.new\n$familiar_npcs.each { |val| parsed.push(val.split.last) }\nif strings.empty?\nif parsed.empty?\nreturn false\nelse\nreturn parsed\nend\nelse\nif mtch = strings.find { |lookfor| parsed.find { |critter| critter =~ /#{lookfor}/ } }\nreturn mtch\nelse\nreturn false\nend\nend\nend\n\ndef checksitting\n$_TAGHASH_[\"GSP\"].include?('H') and !$_TAGHASH_[\"GSP\"].include?('G')\nend\n\ndef checkkneeling\n$_TAGHASH_[\"GSP\"] =~ /GH/\nend\n\ndef checkstunned\n$_TAGHASH_[\"GSP\"].include?('I')\nend\n\ndef checkbleeding\n$_TAGHASH_[\"GSP\"].include?('O')\nend\n\ndef checkgrouped\n$_TAGHASH_[\"GSP\"].include?('P')\nend\n\ndef checkdead\n$_TAGHASH_[\"GSP\"].include?('B')\nend\n\ndef checkreallybleeding\ncheckbleeding and !$_TAGHASH_['GSP'].include?('W')\nend\n\ndef muckled?\ncheckwebbed or checkdead or checkstunned\nend\n\ndef checkhidden\n$_TAGHASH_[\"GSP\"].include?('N')\nend\n\ndef checkwebbed\n$_TAGHASH_[\"GSP\"].include?('C')\nend\n\ndef checkprone\n$_TAGHASH_['GSP'].include?('G') and !$_TAGHASH_['GSP'].include?('H')\nend\n\ndef checknotstanding\n$_TAGHASH_['GSP'].include?('H') or $_TAGHASH_['GSP'].include?('G')\nend\n\ndef checkstanding\n!checknotstanding\nend\n\ndef checkname(*strings)\nstrings.flatten!\nif strings.empty?\nif Char.name\nChar.name\nelse\nname = $_SERVERBUFFER_.find { |line| line =~ /\\034GSB\\d+(\\w+)/ }\nif name\n$1.strip\nelse\nnil\nend\nend\nelse\nChar.name =~ /^(?:#{strings.join('|')})/i\nend\nend\n\ndef checkfampcs(*strings)\nfamiliar_pcs = Array.new\n$familiar_pcs.to_s.gsub(/Lord |Lady |Great |High |Renowned |Grand |Apprentice |Novice |Journeyman /,'').split(',').each { |line| familiar_pcs.push(line.slice(/[A-Z][a-z]+/)) }\nif familiar_pcs.empty?\nreturn false\nelsif strings.empty?\nreturn familiar_pcs\nelse\nregexpstr = strings.join('|\\b')\npeeps = familiar_pcs.find_all { |val| val =~ /\\b#{regexpstr}/i }\nif peeps.empty?\nreturn false\nelse\nreturn peeps\nend\nend\nend\n\ndef checkpcs(*strings)\nif $pcs.empty?\nif strings.empty? then return nil else return false end\nend\npcs = $pcs.gsub(/Novice |Apprentice |Journeyman |Lord |Lady |Great |High |Renowned |Grand /, '').scan(/[A-Z][a-z]+/)\nstrings.flatten!\nif strings.empty?\nif pcs.empty? then return nil else return pcs end\nelse\nregexpstr = strings.join(' ')\nif (here = pcs.find { |pc| regexpstr =~ /\\b#{pc}/i }) then return here else return false end\nend\nend\n\ndef checknpcs(*string)\nif $npcs.empty?\nreturn nil\nelsif string.empty?\n$npcs.delete_if { |n| n =~ /[0-9]+/ }\nunless (npcs = $npcs.collect { |val| val.split.last }).empty? then return npcs.dup else return nil end\nelse\n$npcs.delete_if { |n| n =~ /[0-9]+/ }\nregexpstr = string.join(' ')\nif (here = $npcs.find { |npc| regexpstr =~ /\\b#{npc.split.last}/i }) then return here.dup else return false end\nend\nend\n\ndef count_npcs\nchecknpcs.length\nend\n\ndef checkright(*hand)\nif $_TAGHASH_[\"GSm\"].nil? then return nil end\nhand.flatten!\nif $stormfront\nright_hand = $_TAGHASH_[\"GSm\"].strip\nelse\nright_hand = $_TAGHASH_[\"GSm\"].scan(/[^\\r\\n]{1,15}/).collect { |val| val.strip }.join(\"\\s\")\nend\nif right_hand == \"Empty\" or right_hand.empty?\nnil\nelsif hand.empty?\nright_hand.slice(/[^\\s]+$/)\nelse\nhand.find { |instance| right_hand =~ /#{instance}/i }\nend\nend\n\ndef checkleft(*hand)\nif $_TAGHASH_[\"GSl\"].nil? then return nil end\nhand.flatten!\nif $stormfront\nleft_hand = $_TAGHASH_[\"GSl\"].strip\nelse\nleft_hand = $_TAGHASH_[\"GSl\"].scan(/[^\\r\\n]{1,15}/).collect { |val| val.strip }.join(\"\\s\")\nend\nif left_hand == \"Empty\" or left_hand.empty?\nnil\nelsif hand.empty?\nleft_hand.slice(/[^\\s]+$/)\nelse\nhand.find { |instance| left_hand =~ /#{instance}/i }\nend\nend\n\ndef percentstamina(num=nil)\nunless ($_FAKE_STORMFRONT || $stormfront) then echo(\"Stamina tracking is only available in StormFront! Returning...\") ; sleep 1 ; return false end\nunless num.nil?\n((checkstamina.to_f / maxstamina.to_f) * 100).to_i >= num.to_i\nelse\n((checkstamina.to_f / maxstamina.to_f) * 100).to_i >= num.to_i\nend\nend\n\ndef percenthealth(num=nil)\nunless num.nil?\n((checkhealth.to_f / maxhealth.to_f) * 100).to_i >= num.to_i\nelse\n((checkhealth.to_f / maxhealth.to_f) * 100).to_i\nend\nend\n\ndef percentmana(num=nil)\nunless num.nil? then ((checkmana.to_f / maxmana.to_f) * 100).to_i >= num.to_i\nelse ((checkmana.to_f / maxmana.to_f) * 100).to_i end\nend\n\ndef percentspirit(num=nil)\nunless num.nil? then ((checkspirit.to_f / maxspirit.to_f) * 100).to_i >= num.to_i\nelse ((checkspirit.to_f / maxspirit.to_f) * 100).to_i end\nend\n\ndef checkmana(num=nil)\nif num.nil? then $_TAGHASH_['GSZ'].to_i else $_TAGHASH_['GSZ'].to_i >= num.to_i end\nend\n\ndef checkroomdescrip(*val)\nval.flatten!\nif val.empty? then return $roomdescription.sub(/\\034.*/,'').strip end\n$roomdescription.sub(/\\034.*/,'').strip =~ /#{val.join('|')}/i\nend\n\ndef checkstance(num=nil)\nif num.nil?\ncheckstance = $_TAGHASH_[\"GSg\"].to_i\nif checkstance == 00 then 'offensive'\nelsif checkstance.between?(01, 20) then 'advancing'\nelsif checkstance.between?(21, 40) then 'forward'\nelsif checkstance.between?(41, 60) then 'neutral'\nelsif checkstance.between?(61, 80) then 'guarded'\nelsif checkstance.between?(81, 100) then 'defensive'\nelse 'unknown' end\nelsif (num.class == String && num.to_i == 0)\nstance = $_TAGHASH_[\"GSg\"].to_i\nif num =~ /off/i then stance == 00\nelsif num =~ /adv/i then stance.between?(01, 20)\nelsif num =~ /for/i then stance.between?(21, 40)\nelsif num =~ /neu/i then stance.between?(41, 60)\nelsif num =~ /gua/i then stance.between?(61, 80)\nelsif num =~ /def/i then stance == 100\nelse echo(\"Unrecognized stance! Must be off/adv/for/neu/gua/def\"); nil end\nelse\necho(\"Script warning, checkstance was passed an argument of unknown type, assuming type integer and comparing...\")\n$_TAGHASH_[\"GSg\"].to_i == num.to_i\nend\nend\n\ndef checkspell(*spells)\nspells.flatten!\nif Spell.active.empty? then return false end\nspells.each { |spell|\nunless Spell[spell].active? then return false end\n}\ntrue\nend\n\ndef checkprep(spell=nil)\nif spell.nil? then $_TAGHASH_[\"GSn\"].strip\nelsif spell.class != String then echo(\"Checkprep error, spell # not implemented! You must use the spell name\"); false\nelse $_TAGHASH_['GSn'].strip =~ /^#{spell}/i end\nend\n\ndef checkspirit(num=nil)\nif num.nil? then $_TAGHASH_['GSY'].to_i else $_TAGHASH_['GSY'].to_i >= num.to_i end\nend\n\ndef checkhealth(num=nil)\nif num.nil? then $_TAGHASH_['GSX'].to_i else $_TAGHASH_['GSX'].to_i >= num.to_i end\nend\n\ndef setpriority(val=nil)\nif val.nil? then return Thread.current.priority end\nif val.to_i > 3\necho(\"You're trying to set a script's priority as being higher than the send/recv threads (this is telling Lich to run the script before it even gets data to give the script, and is useless); the limit is 3\")\nreturn Thread.current.priority\nelse\nThread.current.group.list.each { |thr| thr.priority = val.to_i }\nreturn Thread.current.priority\nend\nend\n\ndef checkstamina(num=nil)\nif $_TAGHASH_['stamina'].nil? then echo(\"Stamina tracking is only functional when you're using StormFront!\"); nil elsif num.nil? then $_TAGHASH_['stamina'].to_i else $_TAGHASH_['stamina'].to_i >= num.to_i end\nend\n\ndef variable\nScript.self.vars\nend\n\ndef maxstamina(num=0)\nunless ($_FAKE_STORMFRONT || $stormfront) then echo(\"Stamina is only tracked in StormFront! Unable to continue; returning\") ; sleep 1 ; return false end\nif num.zero?\n$_TAGHASH_['Mstamina'].to_i\nelse\n$_TAGHASH_['Mstamina'].to_i >= num.to_i\nend\nend\n\ndef pause(num=1)\nif num =~ /m/\nsleep((num.sub(/m/, '').to_f * 60))\nelsif num =~ /h/\nsleep((num.sub(/h/, '').to_f * 3600))\nelsif num =~ /d/\nsleep((num.sub(/d/, '').to_f * 86400))\nelse\nsleep(num.to_f)\nend\nend\n\ndef cast(spell,*targets)\npushback_ary = []\nregex = Regexp.new([\"Spell Hindrance for\",\n\"(?:Cast|Sing) Roundtime [0-9]+ Seconds\",\n\"You don't have a spell prepared\\!\",\n\"already have a spell prepared\",\n\"The searing pain in your throat makes that impossible\",\n].join('|'), \"i\")\n\nif !Spell[spell.to_i].nil?\ncost = eval(Spell[spell.to_i].cost)\nelsif spell == 1030\nif targets.empty?\ncost = 20\nelse\ncost = 15\nend\nelse\ncost = spell.to_s[-2..-1].to_i\nend\nif targets.empty?\nwhile mana?(cost)\nfput \"incant #{spell}\"\nchk = \"\"\nwhile chk !~ regex\nchk = get\npushback_ary.push chk\nend\nunless chk =~ /spell hindrance for|The searing pain in your throat makes that impossible|don't have a spell prep/i\nsleep(3)\nScript.self.io.unshift(pushback_ary).flatten!\nreturn true\nend\nend\nreturn false\nelse\nlast = 0\ntargets.each_with_index { |target,idx|\nwhile mana?(cost)\nfput \"prep #{spell}\"\nfput \"cast at #{target}\"\nchk = \"\"\nwhile chk !~ regex\nchk = get\npushback_ary.push chk\nend\nunless chk =~ /spell hindrance for|The searing pain in your throat makes that impossible|don't have a spell prep/i\nsleep(3)\nlast = idx\nbreak\nend\nend\n}\nScript.self.io.unshift(pushback_ary).flatten!\nif mana?(cost) and targets.length.eql?((last + 1))\nreturn true\nelse\nreturn false\nend\nend\nend\n\ndef clear(opt=0)\n port = Script.self\nto_return = port.io.dup\nport.clear\nto_return\nend\n\ndef matchwait(*strings)\nstrings.flatten!\nunless port = Script.self\necho(\"An unknown script thread tried to fetch a game line from the queue, but Lich can't process the call without knowing which script is calling! Aborting...\")\nThread.current.kill\nreturn false\nend\nif port.unique\necho(\"this script is set as unique -- a 'match' will cause it to hang permanently! Aborting\")\nsleep 1\nreturn false\nend\nunless strings.empty?\nregexpstr = strings.collect { |str| str.kind_of?(Regexp) ? str.source : str }.join('|')\nregexobj = /#{regexpstr}/\nwhile line_in = port.gets\nreturn line_in if line_in =~ regexobj\nend\nelse\nstrings = port.match_stack_strings\nlabels = port.match_stack_labels\nregexpstr = /#{strings.join('|')}/i\nwhile line_in = port.gets\nif mdata = regexpstr.match(line_in)\njmp = labels[strings.index(mdata.to_s) || strings.index(strings.find { |str| line_in =~ /#{str}/i })]\nport.match_stack_clear\ngoto jmp\nend\nend\nend\nend\n\ndef waitforre(regexp)\nunless regexp.class == Regexp then echo(\"Script error! You have given 'waitforre' something to wait for, but it isn't a Regular Expression! Use 'waitfor' if you want to wait for a string.\"); sleep 1; return nil end\nunless script = Script.self then echo(\"An unknown script thread tried to fetch a game line from the queue with the 'waitforre' command, but Lich can't process the call without knowing which script is calling! Aborting...\"); Thread.current.kill; return nil end\nif script.unique then echo(\"WARNING: This script is set to NOT be fed any game data with the 'unique' setting and is trying to look at incoming game data! This probably isn't what you want it to do, but attempting command anyway...\") end\nwhile true\nif regobj = regexp.match(script.gets) then return regobj end\nend\nend\n\ndef waitfor(*strings)\nstrings.flatten!\nunless port = Script.self then echo(\"An unknown script thread tried to fetch a game line from the queue, but Lich can't process the call without knowing which script is calling! Aborting...\") ; Thread.current.kill ; return false end\nif port.wizard and strings.length == 1 and strings.first.strip == '>'\nreturn port.gets\nend\nif strings.empty? then echo(\"waitfor without any strings to wait for!\") ; return false end\nif port.unique then echo(\"this script is set as unique -- a 'match' will cause it to hang permanently! Aborting\") ; sleep 1 ; return false end\nregexpstr = strings.join('|')\nwhile true\nline_in = port.gets\nif (line_in =~ /#{regexpstr}/i) then return line_in end\nend\nend\n\ndef wait\nunless port = Script.self then echo(\"An unknown script thread tried to fetch a game line from the queue, but Lich can't process the call without knowing which script is calling! Aborting...\") ; Thread.current.kill ; return false end\nif port.unique then echo(\"this script is set as unique -- a 'match' will cause it to hang permanently! Aborting\") ; sleep 1 ; return false end\nport.clear\nreturn port.gets\nend\n\ndef get\nScript.self.gets\nend\n\ndef reget(*lines)\nlines.flatten!\nif caller.find { |c| c =~ /regetall/ }\nhistory = ($_SERVERBUFFER_.history + $_SERVERBUFFER_)\nelse\nhistory = $_SERVERBUFFER_.dup\nend\nunless Script.status_scripts.include?(Script.self)\nif $stormfront\nhistory.collect! { |line|\nline = line.strip.gsub(/<[^>]+>/, '')\nline.empty? ? nil : line\n}.compact!\nelse\nhistory.collect! { |line|\nline = line.strip.gsub(/\\034.*/, '')\nline.empty? ? nil : line\n}.compact!\nend\nend\nif lines.first.kind_of? Numeric or lines.first.to_i.nonzero?\nnum = lines.shift.to_i\nelse\nnum = history.length\nend\nunless lines.empty?\nregex = /#{lines.join('|')}/i\nhistory = history[-num..-1].find_all { |line| line =~ regex }\nend\nhistory.empty? ? nil : history\nend\n\ndef regetall(*lines)\nreget(*lines)\nend\n\ndef multifput(*cmds)\ncmds.flatten.compact.each { |cmd| fput(cmd) }\nend\n\ndef fput(message, *waitingfor)\nwaitingfor.flatten!\nclear\nput(message)\n\nif Script.self.unique\ndebug(\"(debug info) This script is set as `unique' and is not aware of what is happening in the game; as such, `fput' cannot function properly. Executing a `put' instead.\")\nreturn\nend\n\nwhile string = get\nif string =~ /(?:\\.\\.\\.wait |Wait )[0-9]+/\nhold_up = string.slice(/[0-9]+/).to_i\nsleep(hold_up) unless hold_up.nil?\nclear\nput(message)\nnext\nelsif string =~ /struggle.+stand/\nclear\nfput(\"stand\")\nnext\nelsif string =~ /stunned|can't do that while|cannot seem|can't seem|don't seem|Sorry, you may only type ahead/\nif dead?\necho(\"You're dead...! You can't do that!\")\nsleep 1\nScript.self.io.unshift(string)\nreturn false\nelsif checkstunned\nwhile checkstunned\nsleep(0.25)\nend\nelsif checkwebbed\nwhile checkwebbed\nsleep(0.25)\nend\nelse\nsleep(1)\nend\nclear\nput(message)\nnext\nelse\nif waitingfor.empty?\nScript.self.io.unshift(string)\nreturn string\nelse\nif foundit = waitingfor.find { |val| string =~ /#{val}/i } then Script.self.io.unshift(string) ; return foundit end\nsleep 1\nclear\nput(message)\nnext\nend\nend\nend\nend\n\ndef put(*messages)\n $TALIMIT = 0 if $TALIMIT.nil?\nmessages.each { |message|\nmessage.chomp!\nunless scr = Script.self then scr = \"(script unknown)\" end\nunless $stormfront\nif message.strip.empty?\nnext\nelsif $TA_waiting_on_resp >= $TALIMIT and $TALIMIT.nonzero?\n$_TA_BUFFER_.push message\n$_CLIENTBUFFER_.push(\"[#{scr}]#{$SEND_CHARACTER}#{message}\\r\\n\")\n$_CLIENT_.write(\"(queued): [#{scr}]#{$SEND_CHARACTER}#{message}\\r\\n\") unless scr.silent\n$_LASTUPSTREAM_ = \"[#{scr}]#{$SEND_CHARACTER}#{message}\"\nelse\n$TA_waiting_on_resp += 1\n$_CLIENTBUFFER_.push(\"[#{scr}]#{$SEND_CHARACTER}#{message}\\r\\n\")\n$_CLIENT_.write(\"[#{scr}]#{$SEND_CHARACTER}#{message}\\r\\n\") unless scr.silent\n$_SERVER_.write(\"#{message}\\n\")\n$_LASTUPSTREAM_ = \"[#{scr}]#{$SEND_CHARACTER}#{message}\"\nend\nelse\n$_CLIENTBUFFER_.push(\"[#{scr}]#{$SEND_CHARACTER}<c>#{message}\\r\\n\")\nrespond(\"[#{scr}]#{$SEND_CHARACTER}#{message}\\r\\n\") unless scr.silent\n$_SERVER_.write(\"<c>#{message}\\n\")\n$_LASTUPSTREAM_ = \"[#{scr}]#{$SEND_CHARACTER}#{message}\"\nend\n}\nend\n\ndef echo(*messages)\nscr = Script.self || '(unknown script)'\nmessages = messages.flatten.compact\nrespond if messages.empty?\nmessages.each { |message| respond(\"[#{scr}: #{message.to_s.chomp}]\") } unless scr.no_echo\nnil\nend\n\ndef quiet_exit\nscript = Script.self\nscript.quiet_exit = !(script.quiet_exit)\nend\n\ndef matchfindexact(*strings)\nstrings.flatten!\n unless port = Script.self then echo(\"An unknown script thread tried to fetch a game line from the queue, but Lich can't process the call without knowing which script is calling! Aborting...\") ; Thread.current.kill ; return false end\nif strings.empty? then echo(\"error! 'matchfind' with no strings to look for!\") ; sleep 1 ; return false end\nlooking = Array.new\nstrings.each { |str| looking.push(str.gsub('?', '(\\b.+\\b)')) }\nif looking.empty? then echo(\"matchfind without any strings to wait for!\") ; return false end\nif port.unique then echo(\"this script is set as unique -- a 'match' will cause it to hang permanently! Aborting\") ; sleep 1 ; return false end\nregexpstr = looking.join('|')\nwhile line_in = port.gets\nif gotit = line_in.slice(/#{regexpstr}/)\nmatches = Array.new\nlooking.each_with_index { |str,idx|\nif gotit =~ /#{str}/i\nstrings[idx].count('?').times { |n| matches.push(eval(\"$#{n+1}\")) }\nend\n}\nbreak\nend\nend\nif matches.length == 1\nreturn matches.first\nelse\nreturn matches.compact\nend\nend\n\ndef matchfind(*strings)\nregex = /#{strings.flatten.join('|').gsub('?', '(.+)')}/i\nunless script = Script.self\nrespond \"Unknown script is asking to use matchfind! Cannot process request without identifying the calling script; killing this thread.\"\nThread.current.kill\nend\nwhile true\nif reobj = regex.match(script.gets)\nret = reobj.captures.compact\nif ret.length < 2\nreturn ret.first\nelse\nreturn ret\nend\nend\nend\nend\n\ndef matchfindword(*strings)\nregex = /#{strings.flatten.join('|').gsub('?', '([\\w\\d]+)')}/i\nunless script = Script.self\nrespond \"Unknown script is asking to use matchfindword! Cannot process request without identifying the calling script; killing this thread.\"\nThread.current.kill\nend\nwhile true\nif reobj = regex.match(script.gets)\nret = reobj.captures.compact\nif ret.length < 2\nreturn ret.first\nelse\nreturn ret\nend\nend\nend\nend\n\ndef send_scripts(*messages)\nmessages.flatten!\nmessages.each { |message|\nScript.namescript_incoming(message)\n}\ntrue\nend\n\ndef status_tags(onoff=\"none\")\ntarget = Script.self\nif onoff == \"on\"\nScript.status_scripts.push(target) unless Script.status_scripts.include?(target)\necho(\"Status tags will be sent to this script.\")\nelsif onoff == \"off\"\nScript.status_scripts.delete(target)\necho(\"Status tags will no longer be sent to this script.\")\nelse\nif Script.status_scripts.include?(target)\nScript.status_scripts.delete(target)\necho(\"Status tags will no longer be sent to this script.\")\nelse\nScript.status_scripts.push(target)\necho(\"Status tags will be sent to this script.\")\nend\nend\nend\n\ndef stop_script(*target_names)\n numkilled = 0\n target_names.each { |target_name|\ncondemned = Script.index.find { |s_sock| s_sock.name =~ /^#{target_name}/i }\nif condemned.nil?\nrespond(\"--- Lich: '#{Script.self}' tried to stop '#{target_name}', but it isn't running!\")\nelse\nif condemned.name =~ /^#{Script.self.name}$/i\nexit\nend\ncondemned.kill\nrespond(\"--- Lich: '#{condemned}' has been stopped by #{Script.self}.\")\nnumkilled += 1\nend\n }\n if numkilled == 0\n return false\n else\n return numkilled\n end\nend\n\ndef running?(*snames)\nsnames.each { |checking| (return false) unless (Script.index.find { |lscr| lscr.name =~ /^#{checking}$/i } || Script.index.find { |lscr| lscr.name =~ /^#{checking}/i }) }\ntrue\nend\n\ndef force_start_script(script_name,cli_vars=[])\nbase_name = script_name.dup\nscript_name = \"#{$script_dir}#{script_name}.lic\"\ns_files = Dir.entries($script_dir)\nif (fname = s_files.find { |exists| exists =~ /\\b#{base_name}\\.lic/i }).nil?\nif (fname = s_files.find { |exists| exists =~ /\\b#{base_name}.+\\.lic/i }).nil?\necho(\"#{base_name} was not found!\")\nreturn false\nend\nend\nstart_script($script_dir + fname,cli_vars)\nend\n\ndef start_scripts(*script_names)\nscript_names.flatten.each { |script_name|\nstart_script(script_name)\nsleep 0.02\n}\nend\n\ndef start_script(script_name,*cli_vars)\ncli_vars.flatten!\nif cli_vars.first.downcase == \"safe\"\nrun_safe = true\ncli_vars.shift\nelse\nrun_safe = false\nend\nif script_name.split(/\\/|\\\\/).length == 1\nbase_name = script_name\nscript_name = \"#{$script_dir}#{script_name}.lic\"\ns_files = Dir.entries($script_dir)\nif Script.index.find { |running| running.name.to_s.chomp == base_name.to_s or running.name.to_s.chomp == base_name.to_s + \" (paused)\" }\necho(\"#{base_name} is already running!\")\nreturn nil\nelsif !(s_files.find { |exists| exists =~ /\\b#{base_name}\\.(?:lic|rbw?|gz|Z)(?:gz|Z)?/i })\necho(\"#{base_name} was not found!\")\nreturn nil\nend\nelse\nbase_name = script_name.split(/\\/|\\\\/).last.gsub(/\\.(?:lic|rbw?|gz|Z)(?:gz|Z)?/i, '')\nend\nThread.new {\nbegin\nscript = Script.new(base_name,cli_vars)\nscript.setup_labels(script_name, run_safe)\nrescue\nrespond(\"--- Lich: error reading script file: #{$!}\")\nThread.current.kill\nend\nscript.set_safe if defined?(All_Safe)\n_current_label_ = script.fetch_next_label\nbegin\nif script.safe?\nwhile _current_label_\neval(\"$SAFE = 3\\n#{_current_label_}\",nil,script.name,script.line_no[script.current_label])\nscript = Script.self\n_current_label_ = script.fetch_next_label\nend\nelse\nwhile _current_label_\neval(_current_label_,nil,script.name,script.line_no[script.current_label])\nscript = Script.self\n_current_label_ = script.fetch_next_label\nend\nend\nscript.kill\nrespond(\"--- Lich: #{script} finished.\") unless script.quiet_exit\nrescue SystemExit\nscript.kill\nrespond(\"--- Lich: #{script} has exited.\") unless script.quiet_exit\nrescue SyntaxError\nscript.kill\nrespond(\"--- SyntaxError: #{$!}\")\nrespond($!.backtrace[0..2]) if $LICH_DEBUG\nrespond(\"--- Lich: cannot execute #{script}, aborting.\")\nrescue ScriptError\nscript.kill\nrespond(\"--- ScriptError: #{$!}\")\nrespond($!.backtrace[0..2]) if $LICH_DEBUG\nrespond(\"--- Lich: #{script} has exited.\")\nrescue\nscript.kill\nrespond(\"--- Error: #{script}: #{$!}\")\nrespond($!.backtrace[0..2]) if $LICH_DEBUG\nrespond(\"--- Lich: #{script} has exited.\")\nrescue NoMemoryError\nscript.kill\nrespond(\"--- NoMemoryError: #{$!}\")\nrespond($!.backtrace[0..2]) if $LICH_DEBUG\nrespond(\"--- Lich: #{script} has exited.\")\nrescue Exception\nif $! == JUMP\nretry if (_current_label_ = script.fetch_next_label) and _current_label_ != JUMP_ERROR\nscript.kill\nrespond(\"--- Label Error: `#{script.jump_label}' was not found, and no `LabelError' label was found!\")\nrespond($!.backtrace[0..2]) if $LICH_DEBUG\nrespond(\"--- Lich: #{script} has exited.\")\nelse\nscript.kill\nrespond(\"--- Exception: #{$!}\")\nrespond($!.backtrace[0..2]) if $LICH_DEBUG\n respond(\"--- Lich: #{script} has exited.\")\nend\nend\n_current_label_, script = nil, nil\n}\nsleep 0.1 if Script.self\ntrue\nend\n\ndef force_start_wizard_script(name,cli_vars=[])\nstart_wizard_script(name,cli_vars,true)\nend\n\ndef start_wizard_script(name, cli_vars=[], force=false, wiz_dir = $LICHCONFIG['Wizard Directory'])\nscript_dir = $script_dir.dup\nfile_list = Dir.entries(script_dir)[2..-1]\nfile_dir = script_dir.dup\nif wiz_dir.nil?\nwiz_dir = ''\nend\nfound = file_list.find { |val| val =~ /^#{name}\\.(?:cmd|wiz)/i }\nif found.nil?\nfound = file_list.find { |val| val.downcase =~ /^#{name}.+\\.(?:cmd|wiz)/i }\nend\nif found.nil?\nif File.exists?(File.join(wiz_dir, \"Gemstone\", \"Scripts\"))\nfile_dir = File.join(wiz_dir, \"Gemstone\", \"Scripts\")\nwiz_file_list = Dir.entries(file_dir)\nelsif File.exists?(ENV['HOME'] + \"/.wine/drive_c/Program Files/SIMU/WIZARD/Gemstone/Scripts\")\nwiz_file_list = Dir.entries(ENV['HOME'] + \"/.wine/drive_c/Program Files/SIMU/WIZARD/Gemstone/Scripts\")[2..-1]\nfile_dir = ENV['HOME'] + \"/.wine/drive_c/Program Files/SIMU/WIZARD/Gemstone/Scripts\"\nelsif File.exists?(\"/Program Files/SIMU/WIZARD/Gemstone/Scripts\")\nwiz_file_list = Dir.entries(\"/Program Files/SIMU/WIZARD/Gemstone/Scripts\")\nfile_dir = \"/Program Files/SIMU/WIZARD/Gemstone/Scripts\"\nelse\nwiz_file_list = []\nend\nwiz_file_list = wiz_file_list.find_all { |wfile| wfile =~ /\\.cmd$|\\.wiz$/i }\nif wiz_file_list.empty?\nrespond(\"--- Lich: unable to locate `#{name}'! If you're sure it exists, copy it to your Lich directory.\")\nreturn nil\nelse\nfound = wiz_file_list.find { |wfile| wfile =~ /^#{name}\\.(?:cmd|wiz)/i }\nif found.nil?\nfound = wiz_file_list.find { |wfile| wfile =~ /^#{name}.+\\.(?:cmd|wiz)/i }\nend\nend\nif found.nil?\nrespond(\"--- Lich: unable to locate `#{name}'! If you're sure it exists, copy it to your Lich directory.\")\nreturn nil\nend\nend\nif cli_vars.first =~ /reverse/i\nrev = true\ncli_vars.shift\nelse\nrev = false\nend\nif (Script.index.find { |runcheck| runcheck.name == found } and force == false) then respond(\"--- Lich: #{found} is already running!\"); return nil end\nThread.new {\n begin\nscript = Script.new(found,cli_vars)\nscript.init_wizard_script(found)\nif rev == true\nscript.load_wizard_lines(file_dir + \"/#{found}\",true)\nelse\nscript.load_wizard_lines(file_dir + \"/#{found}\")\nend\nscript.set_as_good\nscript.set_safe\nwhile true\nbreak if script.process_next_wizard_line.nil?\nend\nscript.kill\n respond(\"--- Lich: #{script} has finished.\")\n rescue SyntaxError\nscript.kill\n respond(\"--- Lich: error around line #{script.stackptr}: #{$!}\")\n rescue SystemExit\nscript.kill\n respond(\"--- Lich: #{script} has exited.\")\n rescue SystemCallError\nscript.kill\n respond(\"--- Lich: error around line #{script.stackptr}: #{$!}\")\n rescue\nscript.kill\n respond(\"--- Lich: error around line #{script.stackptr}: #{$!}\")\n rescue NoMemoryError\n script.kill\nrespond(\"--- Lich: #{$!}\")\n end\n script = nil; found = nil; wiz_file_list = nil; file_list = nil; found = nil\n}\ntrue\nend\n\ndef load_favs(lich_dir, script_dir, q=nil)\ns_files = Dir.entries(script_dir)[2..-1]\nbegin\nfile = File.open(\"#{lich_dir}favorites.txt\"); favoritesdata = file.readlines; file.close; file = nil\nfavorites = Array.new\nif checkname\nfavorites = favoritesdata.find_all { |line| line =~ /^(?:ALL|#{checkname}):/ }\nfavorites.sort!\nif (favorites.empty? && favoritesdata.find_all{ |line| line =~ /^\\w+:/ }.empty?)\nfavoritesdata.each { |val| favorites.push('ALL:' + val) }\nfile = File.open(lich_dir + 'favorites.txt','w'); file.puts(favorites); file.close; file = nil\nelsif favorites.empty?\nfavorites = favoritesdata.dup\nend\nelse\nfavorites = favoritesdata.reject { |line| line !~ /^ALL:/ }\nend\nfavorites.compact!\nrescue\nfavorites = []\nend\nfavorites = favorites.collect { |line| line.sub(/[^:]+:/, '') }\nloaded = Array.new\nuntil favorites.empty?\nnew_script = favorites.shift.strip\nif Script.find(new_script)\nnil# Already running, skip it\nelsif s_files.find { |exists| exists =~ /^#{new_script}.*\\.lic/ }\nif q == true\nstart_script(\"#{script_dir}#{new_script}.lic\",[\"quiet\"])\nelse\nstart_script(\"#{script_dir}#{new_script}.lic\")\nend\nloaded.push(new_script)\nelse\nrespond(\"--- Lich: '#{new_script}' was not found!\")\nend\nend\nloaded\nend\n\ndef dump_to_log(log_dir)\n begin\nfile = File.open(\"#{log_dir}lich-log.txt\", \"w\")\nfile.print(\"--- Dump of the up- and down-streams of data as seen by the Lich (this includes all status lines, etc.) ---\\r\\n\")\nfile.print(\"\\tLich v#{$version} \" + Time.now.to_s)\nfile.print(\"\\r\\n\\r\\n\\r\\n===========\\r\\nFrom the Game Host to Your Computer\\r\\n==========\\r\\n\\r\\n\")\nfile.puts($_SERVERBUFFER_.history + $_SERVERBUFFER_)\nfile.print(\"\\r\\n\\r\\n\\r\\n\\r\\n==========\\r\\nFrom Your Computer to the Game Host\\r\\n==========\\r\\n\\r\\n\")\nfile.puts($_CLIENTBUFFER_.history + $_CLIENTBUFFER_)\nrespond(\"--- Lich: '#{log_dir}lich-log.txt' written successfully. If you want to keep it, don't forget to rename it or next time it'll be overwritten!\")\n rescue\n$stderr.puts(\"--- Lich encountered an error and cannot write to log; message was:\\n--- #{$!}\")\n ensure\n psinet_log = nil\nsimu_log = nil\nfile.close\n end\nend\n\ndef respond(first = \"\", *messages)\nif $_CLIENT_.closed? then return end\nstr = ''\nbegin\nif first.class == Array\nfirst.flatten.each { |ln| str += sprintf(\"%s\\r\\n\", ln.to_s.chomp) }\nelse\nstr += sprintf(\"%s\\r\\n\", first.to_s.chomp)\nend\nmessages.flatten.each { |message| str += sprintf(\"%s\\r\\n\", message.to_s.chomp) }\nif $stormfront\nstr = \"<output class=\\\"mono\\\"/>\\r\\n#{str.gsub('&', '&').gsub('<', '<').gsub('>', '>')}<output class=\\\"\\\"/>\\r\\n\"\nelsif $SIMUGAME\nstr = \"\\034GSw00008ignore this\\r\\n#{str}\\034GSw00008ignore this\\r\\n\"\nend\n$_CLIENT_.write(str)\nrescue\nputs $!.to_s if $LICH_DEBUG\nend\nend\n\ndef find_hosts_file(windir = nil)\nif !windir.nil?\nwinxp = \"\\\\system32\\\\drivers\\\\etc\\\\\"\nwin98 = \"\\\\\"\nif File.exists?(windir + winxp + \"hosts.bak\")\nheal_hosts(windir + winxp)\nreturn windir + winxp\nelsif File.exists?(windir + win98 + \"hosts.bak\")\nheal_hosts(windir + win98)\nreturn windir + win98\nelsif File.exists?(windir + winxp + \"hosts\")\nreturn windir + winxp\nelsif File.exists?(windir + win98 + \"hosts\")\nreturn windir + win98\nend\nend\nif Dir.pwd.to_s[0..1] =~ /(C|D|E|F|G|H)/\nprefix = \"#{$1.dup}:\"\nelse\nprefix = String.new\nend\nwinxp_pro = \"#{prefix}\\\\winnt\\\\system32\\\\drivers\\\\etc\\\\\"\nwinxp_home = \"#{prefix}\\\\windows\\\\system32\\\\drivers\\\\etc\\\\\"\nwin98 = \"#{prefix}\\\\windows\\\\\"\nnix = \"/etc/\"\n[ winxp_pro, winxp_home, win98, nix ].each { |windir|\nif File.exists?(windir + \"hosts.bak\") or File.exists?(windir + \"hosts\")\nheal_hosts(windir)\nreturn windir\nend\n}\nwinxp_pro.sub!(/[A-Z]:/, '')\nwinxp_home.sub!(/[A-Z]:/, '')\nwin98.sub!(/[A-Z]:/, '')\n[ \"hosts\" ].each { |fname|\n[ \"C:\", \"D:\", \"E:\", \"F:\", \"G:\" ].each { |drive|\n[ winxp_pro, winxp_home, win98 ].each { |windir|\nif File.exists?(drive + windir + \"hosts.bak\") or File.exists?(drive + windir + \"hosts\")\nheal_hosts(drive + windir)\nreturn drive + windir\nend\n}\n}\n}\n$stderr.puts(\"Fatal error! Your local hosts file cannot be located!\")\nnil\nend\n\ndef hack_hosts(hosts_dir, simu_ip)\nif hosts_dir[-1..-1] !~ /\\/\\\\/\nhosts_dir += File::SEPARATOR\nend\nat_exit { heal_hosts(hosts_dir) }\nbegin\nbegin\nunless File.exists?(\"%shosts.bak\" % hosts_dir)\nFile.open(\"%shosts\" % hosts_dir) { |file|\nFile.open(\"%shosts.sav\" % $lich_dir, 'w') { |f|\nf.write(file.read)\n}\n}\nend\nrescue\nFile.unlink(\"#{$lich_dir}hosts.sav\") if File.exists?(\"#{$lich_dir}hosts.sav\")\nend\nif File.exists?(\"%shosts.bak\" % hosts_dir)\nsleep 1\nif File.exists?(\"%shosts.bak\" % hosts_dir)\nheal_hosts(hosts_dir)\nend\nend\nFile.open(\"%shosts\" % hosts_dir) { |file|\nFile.open(\"%shosts.bak\" % hosts_dir, 'w') { |f|\nf.write(file.read)\n}\n}\nFile.open(\"%shosts\" % hosts_dir, 'w') { |file|\nfile.puts \"127.0.0.1\\t\\tlocalhost\\r\\n127.0.0.1\\t\\t%s\" % simu_ip\n}\nrescue SystemCallError\n$stderr.puts $!\n$stderr.puts $!.backtrace\nexit(1)\nend\nend\n\ndef heal_hosts(hosts_dir)\nif hosts_dir[-1..-1] !~ /\\/\\\\/\nhosts_dir += File::SEPARATOR\nend\nbegin\nif File.exists? \"%shosts.bak\" % hosts_dir\nFile.open(\"%shosts.bak\" % hosts_dir) { |file|\nFile.open(\"%shosts\" % hosts_dir, 'w') { |f|\nf.write(file.read)\n}\n}\nFile.unlink \"%shosts.bak\" % hosts_dir\nend\nrescue\n$stderr.puts $!\n$stderr.puts $!.backtrace\nexit(1)\nend\nend\n\ndef open_gs(simu_quad_ip, simu_port)\nputs(\"Connecting to the real game host...\")\nif ARGV.find { |val| val =~ /\\-\\-fake\\-sf/i }\nputs(\"Identifying as SF.\")\n$_SERVER_ = TCPSocket.open(\"storm.gs4.game.play.net\", 10024)\n$_FAKE_STORMFRONT = true\n$stormfront = true\nelse\n$_SERVER_ = TCPSocket.open(simu_quad_ip, simu_port)\nend\nputs(\"Connection with the game host is open.\")\nend\n\ndef open_client(listener)\nputs(\"Pretending to be the game host, and waiting for game client to connect to us...\")\n$_CLIENT_ = listener.accept\nputs(\"Connection with the local game client is open.\")\nend\n\nif ENV['OS'] =~ /win/i\ndef launch_sge_win(server)\nif $LICHCONFIG['SGE Directory'] and File.exists? File.join($LICHCONFIG['SGE Directory'], 'Sge.exe')\nfile = File.join($LICHCONFIG['SGE Directory'], \"Sge.exe\")\nelse\nfile = \"/Program Files/SIMU/SGE/Sge.exe\"\nend\nThread.new { system(file) }\nnil\nend\nelse\ndef launch_sge_nix(server)\nbegin\nfork {\nserver.close\nProcess.euid = Process.uid\nProcess.egid = Process.gid\nexec(\"wine '#{ENV['HOME']}/.wine/drive_c/Program Files/SIMU/SGE/Sge.exe'\")\n}\nrescue NotImplementedError\nThread.new {\nsystem(\"sudo -u `id -run` wine '#{ENV['HOME']}/.wine/drive_c/Program Files/SIMU/SGE/Sge.exe'\")\n}\nend\nnil\nend\nend\n\ndef launch_sge_user(lich_dir)\nbegin\ndata = File.open(\"#{lich_dir}launch.txt\") { |file| file.readlines }\nrescue SystemCallError\nreturn false\nend\n launchn = 0\ndata.delete_if { |line| line =~ /^#/ }\ndata.each { |prog|\nunless prog.chomp.empty?\nif ENV['OS'] =~ /win/i\nThread.new { system(\"#{prog.chomp}\") }\nelse\nfork {\nbegin\nProcess.euid = Process.uid\nrescue\n$stderr.puts $!\n$stderr.puts $!.backtrace\nend\nexec(\"#{prog.chomp}\")\n}\nend\nend\n}\nnil\nend\n\nclass File\ndef File.expand_registry_str(string)\nre = /%([^%]+)%/.match(string)\nre.captures.compact.each { |var| string.sub!(/%#{var}%/, ENV[var]) }\nstring\nend\nend\n\nbegin\nundef :abort\nalias :mana :checkmana\nalias :mana? :checkmana\nalias :health :checkhealth\nalias :health? :checkhealth\nalias :spirit :checkspirit\nalias :spirit? :checkspirit\nalias :stamina :checkstamina\nalias :stamina? :checkstamina\nalias :stunned? :checkstunned\nalias :bleeding? :checkbleeding\nalias :reallybleeding? :checkreallybleeding\nalias :dead? :checkdead\nalias :hiding? :checkhidden\nalias :hidden? :checkhidden\nalias :hidden :checkhidden\nalias :checkhiding :checkhidden\nalias :standing? :checkstanding\nalias :stance? :checkstance\nalias :stance :checkstance\nalias :joined? :checkgrouped\nalias :checkjoined :checkgrouped\nalias :group? :checkgrouped\nalias :myname? :checkname\nalias :active? :checkspell\nalias :righthand? :checkright\nalias :lefthand? :checkleft\nalias :righthand :checkright\nalias :lefthand :checkleft\nalias :mind? :checkmind\nalias :checkactive :checkspell\nalias :forceput :fput\nalias :send_script :send_scripts\nalias :stop_scripts :stop_script\nalias :kill_scripts :stop_script\nalias :kill_script :stop_script\nalias :fried? :checkfried\nalias :webbed? :checkwebbed\nalias :pause_scripts :pause_script\nalias :roomdescription? :checkroomdescrip\nalias :prepped? :checkprep\nalias :checkprepared :checkprep\nalias :unpause_scripts :unpause_script\nalias :priority? :setpriority\nalias :checkoutside :outside?\nalias :toggle_status :status_tags\nrescue\nSTDERR.puts($!)\nSTDERR.puts($!.backtrace)\nend\n\nclass Settings\n@@hash ||= {}\n@@auto ||= false\n@@stamp ||= Hash.new\ndef Settings.auto=(val)\n@@auto = val\nend\ndef Settings.auto\n@@auto\nend\ndef Settings.save\nif script = Script.self\nif File.exists?($script_dir + script.to_s + \".sav\")\nFile.rename($script_dir + script.to_s + \".sav\",\n $data_dir + script.to_s + \".sav\")\nend\nfile = File.open($data_dir + script.to_s + '.sav', 'wb')\nfile.write(Marshal.dump(if @@hash[script.to_s] then @@hash[script.to_s] else {} end))\nfile.close\nelse\nraise Exception.exception(\"SettingsError\"), \"The script trying to save its data cannot be identified!\"\nend\nend\ndef Settings.autoload\nif File.exists?($script_dir + Script.self.to_s + \".sav\")\nFile.rename($script_dir + Script.self.to_s + \".sav\",\n $data_dir + Script.self.to_s + \".sav\")\nend\nfname = $data_dir + Script.self.to_s + '.sav'\nif File.mtime(fname) > @@stamp[Script.self.to_s]\nSettings.load\ntrue\nelse\nfalse\nend\nend\ndef Settings.load(who = nil)\n@@stamp[Script.self.to_s] = Time.now\nif !who.nil?\nunless who.include?(\".\")\nwho += \".sav\"\nend\nbegin\nif File.exists?($script_dir + who)\nFile.rename($script_dir + who, $data_dir + who)\nend\nfile = File.open($data_dir + who, 'rb')\n@@hash[who.sub(/\\..*/, '')] = Marshal.load(file.read)\nrescue\n$stderr.puts $!\n$stderr.puts $!.backtrace\nensure\nfile.close unless file.closed?\nend\nreturn\nend\nif script = Script.self\nif File.exists?($script_dir + script.to_s + '.sav')\nFile.rename($script_dir + script.to_s + \".sav\",\n $data_dir + script.to_s + \".sav\")\nend\nif File.exists?($data_dir + script.to_s + \".sav\")\nbegin\nfile = File.open($data_dir + script.to_s + '.sav', 'rb')\ndata = Marshal.load(file.read)\nfile.close\n@@hash[script.to_s] = data\nrescue\nputs $!\nensure\nfile.close unless file.closed?\nend\nelse\nnil\nend\nelse\nraise Exception.exception(\"SettingsError\"), \"The script trying to load data cannot be identified!\"\nend\nend\ndef Settings.clear\nunless script = Script.self then raise Exception.exception(\"SettingsError\"), \"The script trying to access settings cannot be identified!\" end\nunless @@hash[script.to_s] then @@hash[script.to_s] = {} end\n@@hash[script.to_s].clear\nend\ndef Settings.[](val)\nSettings.autoload if @@auto\nunless script = Script.self then raise Exception.exception(\"SettingsError\"), \"The script trying to access settings cannot be identified!\" end\nunless @@hash[script.to_s] then @@hash[script.to_s] = {} end\n@@hash[script.to_s][val]\nend\ndef Settings.[]=(setting, val)\nunless script = Script.self then raise Exception.exception(\"SettingsError\"), \"The script trying to access settings cannot be identified!\" end\nunless @@hash[script.to_s] then @@hash[script.to_s] = {} end\n@@hash[script.to_s][setting] = val\nSettings.save if @@auto\n@@hash[script.to_s][setting]\nend\ndef Settings.to_hash\nunless script = Script.self then raise Exception.exception(\"SettingsError\"), \"The script trying to access settings cannot be identified!\" end\nunless @@hash[script.to_s] then @@hash[script.to_s] = {} end\n@@hash[script.to_s]\nend\nend\n\nclass Critter\nunless defined?(LIST)\nLIST = []\nend\nattr_reader :id, :name, :level, :race, :type, :undead, :geo\nattr_accessor :as, :ds, :cs, :td, :attacks, :mb\ndef initialize(id,name,level,race,type,undead=false,geo=nil)\n@id,@name,@level,@race,@type,@undead,@geo = id,name,level.to_i,race,type,undead,geo\nLIST.push(self) unless LIST.find { |critter| critter.name == @name }\nend\nend\n\nmodule StringFormatting\n def as_time\n sprintf(\"%d:%02d:%02d\", (self / 60).truncate, self.truncate % 60, ((self % 1) * 60).truncate)\n end\nend\n\nclass Numeric\ninclude StringFormatting\nend\n\n\n\nrequire 'pathfind' if !defined?(Pathfind)\n\nclass Map\nattr_accessor :pause\ninclude Pathfind\n\ndef initialize(id, title, desc, paths, wayto={}, timeto={}, geo=nil, pause = nil)\n@id, @title, @desc, @paths, @wayto, @timeto, @geo = id, title, desc, paths, wayto, timeto, geo\n@pause = pause\n@@list.push(self)\nend\ndef outside?\n@paths =~ /Obvious paths:/\nend\ndef Map.uniq_new(id, title, desc, paths, wayto={}, timeto={}, geo=nil)\nchkre = /#{desc.strip.chop.gsub(/\\.(?:\\.\\.)?/, '|')}/\nunless duplicate = @@list.find { |obj| obj.title == title and obj.desc =~ chkre and obj.paths == paths }\nreturn Map.new(id, title, desc, paths, wayto, timeto, geo)\nend\nreturn duplicate\nend\ndef Map.uniq!\nlist = []\n@@list.each { |room|\nchkre = /#{desc.strip.chop.gsub(/\\.(?:\\.\\.)?/, '|')}/\nunless list.find { |duproom| duproom.desc =~ chkre and room.title == duproom.title and room.paths == duproom.paths }\nlist.push(room)\nend\n}\n@@list = list\nnil\nend\ndef Map.list\n@@list\nend\ndef Map.[](val)\nif (val.class == Fixnum) or (val.class == Bignum)\n@@list.find { |room| room.id == val }\nelse\nchkre = /#{val.strip.sub(/\\.$/, '').gsub(/\\.(?:\\.\\.)?/, '|')}/i\nchk = /#{Regexp.escape(val.strip)}/i\n@@list.find { |room| room.title =~ chk } or @@list.find { |room| room.desc =~ chk } or @@list.find { |room| room.desc =~ chkre }\nend\nend\ndef Map.current\nctitle = checkroom\ncdescre = /#{checkroomdescrip.strip.chop.gsub(/\\.(?:\\.\\.)?/, '|')}/\n@@list.find { |room| room.desc =~ cdescre and room.title == ctitle and $_PATHSLINE_.chomp == room.paths }\nend\ndef Map.reload\nMap.load\nPathfind.reassoc_nodes\nMap.load_unique\nPathfind.reassoc_nodes\nGC.start\nend\ndef Map.load(file=($script_dir.to_s + \"map.dat\"))\nunless File.exists?(file)\nraise Exception.exception(\"MapDatabaseError\"), \"Fatal error: file `#{file}' does not exist!\"\nend\nfd = File.open(file, 'rb')\n@@list = Marshal.load(fd.read)\nfd.close\nfd = nil\n@@list.each { |rm|\nrm.pause = nil\n}\nGC.start\nend\ndef Map.load_unique(file=($script_dir.to_s + 'unique_map_movements.txt'))\nif File.exists?($script_dir + \"unique_map_movements.txt\")\nfile = File.open($script_dir + \"unique_map_movements.txt\")\nudata = file.readlines.collect { |line| line.strip }.find_all { |line| line !~ /^#/ and !line.empty? }\nfile.close\nnewmini, mazerooms = [], []\nudata.shift\nuntil udata.first == 'END'\nmazerooms.push(udata.shift)\nend\nudata.shift\npause_rooms = []\nudata.shift\nuntil udata.first == 'END'\npause_rooms.push udata.shift\nend\nudata.shift\n\nwhile udata[0] =~ /\\d+->\\d+/\nrmfrom, rmto = udata.shift.split('->')\nif rmto.include?(':')\nrmto, time_estimate = rmto.split(':')\nelse\ntime_estimate = nil\nend \nif rmfrom.nil? or rmto.nil? then respond \"There's an error in the 'roomfrom->roomto' line of the script!\"; exit end\nuntil (newline = udata.shift) == \"END\"\nnewmini.push(newline)\nend\nif sroom = Pathfind.find_node(rmfrom.to_i) and droom = Pathfind.find_node(rmto.to_i)\nsroom.wayto[rmto.to_s] = StringProc.new(newmini.join(\"\\n\"))\nif time_estimate\nsroom.timeto ||= {}\nsroom.timeto[rmto.to_s] = time_estimate\nend\nelse\nrespond sprintf(\"Unrecoverable error, cannot identify one or both of the rooms with IDs %s and %s!\", rmfrom, rmto)\nexit\nend\nnewmini.clear\nend\nmazerooms.each { |num|\nnode = Pathfind.find_node(num.to_i)\nnode.wayto.clear\nnode.maze = true\n}\npause_rooms.each { |str|\nnum, val = str.split(':')\nPathfind.find_node(num.to_i).pause = val || 5\n}\nend\nend\ndef Map.save(filename=($script_dir.to_s + \"map.dat\"))\nif File.exists?(filename)\nrespond \"File exists! Backing it up before proceeding...\"\nbegin\nfile = nil\nbakfile = nil\nfile = File.open(filename, 'rb')\nbakfile = File.open(filename + \".bak\", \"wb\")\nbakfile.write(file.read)\nrescue\nrespond $!\nensure\nfile ? file.close : nil\nbakfile ? bakfile.close : nil\nend\nend\nbegin\nfile = nil\nfile = File.open(filename, 'wb')\nfile.write(Marshal.dump(@@list))\nrespond \"The current map database has been saved!\"\nrescue\nrespond $!\nensure\nfile ? file.close : nil\nend\nGC.start\nend\ndef Map.smart_check\nerror_rooms = []\n@@list.each { |room|\nif room.wayto.keys.include?(room.id.to_s)\nerror_rooms.push(\"Room references itself as adjacent:\\n#{room}\")\nend\nroom.wayto.dup.each { |torm, way|\nif way =~ /^(?:g |go )?(?:n|no|nor|nort|north)$/ and !(room.paths =~ /\\bnorth,?\\b/)\nputs(\"Dir error in room:\\n#{room}\\n... cannot reach room #{torm} by going #{way}!\")\nroom.wayto.delete(torm)\nelsif way =~ /^(?:g |go )?(?:ne|northeast|northeas|northea|northe)$/ and !(room.paths =~ /\\bnortheast,?\\b/)\nputs(\"Dir error in room:\\n#{room}\\n... cannot reach room #{torm} by going #{way}!\")\nroom.wayto.delete(torm)\nelsif way =~ /^(?:g |go )?(?:e|ea|eas|east)$/ and !(room.paths =~ /\\beast,?\\b/)\nputs(\"Dir error in room:\\n#{room}\\n... cannot reach room #{torm} by going #{way}!\")\nroom.wayto.delete(torm)\nelsif way =~ /^(?:g |go )?(?:southeast|southeas|southea|southe)$/ and !(room.paths =~ /\\bsoutheast,?\\b/)\nputs(\"Dir error in room:\\n#{room}\\n... cannot reach room #{torm} by going #{way}!\")\nroom.wayto.delete(torm)\nelsif way =~ /^(?:g |go )?(?:south|sout|sou|so|s)$/ and !(room.paths =~ /\\bsouth,?\\b/)\nputs(\"Dir error in room:\\n#{room}\\n... cannot reach room #{torm} by going #{way}!\")\nroom.wayto.delete(torm)\nelsif way =~ /^(?:g |go )?(?:sw|southwest|southwes|southwe|southw)$/ and !(room.paths =~ /\\bsouthwest,?\\b/)\nputs(\"Dir error in room:\\n#{room}\\n... cannot reach room #{torm} by going #{way}!\")\nroom.wayto.delete(torm)\nelsif way =~ /^(?:g |go )?(?:west|wes|we|w)$/ and !(room.paths =~ /\\bwest,?\\b/)\nputs(\"Dir error in room:\\n#{room}\\n... cannot reach room #{torm} by going #{way}!\")\nroom.wayto.delete(torm)\nelsif way =~ /^(?:g |go )?(?:nw|northwest|northwes|northwe|northw)$/ and !(room.paths =~ /\\bnorthwest,?\\b/)\nputs(\"Dir error in room:\\n#{room}\\n... cannot reach room #{torm} by going #{way}!\")\nroom.wayto.delete(torm)\nelsif way =~ /^(?:g |go )?(?:u|up)$/ and !(room.paths =~ /\\bup,?\\b/)\nputs(\"Dir error in room:\\n#{room}\\n... cannot reach room #{torm} by going #{way}!\")\nroom.wayto.delete(torm)\nelsif way =~ /^(?:g |go )?(?:d|do|dow|down)$/ and !(room.paths =~ /\\bdown,?\\b/)\nputs(\"Dir error in room:\\n#{room}\\n... cannot reach room #{torm} by going #{way}!\")\nroom.wayto.delete(torm)\nend\n}\n}\nerror_rooms\nend\ndef Map.estimate_time(array)\nunless array.class == Array\nraise Exception.exception(\"MapError\"), \"Map.estimate_time was given something not an array!\"\nend\ntime = 0.00\nuntil array.length < 2\ncroom = array.shift\nif t = Pathfind.find_node(croom).timeto[array.first.to_s]\ntime += t.to_f\nelse\ntime += 0.5\nend\nend\ntime\nend\ndef get_wayto(int)\ndir = @wayto[int.to_s]\nif dir =~ /^\\s*(?:n|north)\\s*$/i then return N\nelsif dir =~ /^\\s*(?:ne|northeast)\\s*$/i then return NE\nelsif dir =~ /^\\s*(?:e|east)\\s*$/i then return E\nelsif dir =~ /^\\s*(?:se|southeast)\\s*$/i then return SE\nelsif dir =~ /^\\s*(?:s|south)\\s*$/i then return S\nelsif dir =~ /^\\s*(?:sw|southwest)\\s*$/i then return SW\nelsif dir =~ /^\\s*(?:w|west)\\s*$/i then return W\nelsif dir =~ /^\\s*(?:nw|northwest)$\\s*/i then return NW\nelse return NODIR\nend\nend\ndef cinspect\nsprintf(\"cstruct->id: %d; cstruct->nadj: %d; cstruct->x: %d; cstruct->y: %d; cstruct->rb_obj: %d\", *(self.c_inspect))\nend\ndef to_s\n\"##{@id}:\\n#{@title}\\n#{@desc}\\n#{@paths}\"\nend\ndef inspect\nself.instance_variables.collect { |var| var.to_s + \"=\" + self.instance_variable_get(var).inspect }.join(\"\\n\")\nend\nend\n\nclass Room < Map\nprivate_class_method :new\ndef Room.method_missing(*args)\nsuper(*args)\nend\nend\n\nclass NilClass\ndef +(arg)\narg\nend\nend\nclass StringProc\ndef initialize(string)\n@string = string\nend\ndef kind_of?(type)\nProc.new {}.kind_of? type\nend\ndef class\nProc\nend\ndef call(*args)\neval(@string, nil, \"StringProc\")\nend\ndef _dump(depth = nil)\n@string\nend\ndef StringProc._load(string)\nStringProc.new(string)\nend\nend\n\ndef match(label, string)\nstrings = [ label, string ]\nstrings.flatten!\nunless port = Script.find then echo(\"An unknown script thread tried to fetch a game line from the queue, but Lich can't process the call without knowing which script is calling! Aborting...\") ; Thread.current.kill ; return false end\nif strings.empty? then echo(\"Error! 'match' was given no strings to look for!\") ; sleep 1 ; return false end\nunless strings.length == 2\nif port.unique then echo(\"this script is set as unique -- a 'match' will cause it to hang permanently! Aborting\") ; sleep 1 ; return false end\nwhile line_in = port.gets\nstrings.each { |string|\nif line_in =~ /#{string}/ then return $~.to_s end\n}\nend\nelse\nif port.respond_to?(:match_stack_add)\nport.match_stack_add(strings.first.to_s, strings.last)\nelse\nport.match_stack_labels.push(strings[0].to_s)\nport.match_stack_strings.push(strings[1])\nend\nend\nend\n\ndef matchtimeout(secs, *strings)\nunless port = Script.find then echo(\"An unknown script thread tried to fetch a game line from the queue, but Lich can't process the call without knowing which script is calling! Aborting...\") ; Thread.current.kill ; return false end\nunless (secs.class == Float || secs.class == Fixnum) then echo('matchtimeout error! You appear to have given it a string, not a #! Syntax: matchtimeout(30, \"You stand up\")') ; return false end\nif port.unique then echo(\"this script is set as unique -- a 'match' will cause it to hang permanently! Aborting\") ; sleep 1 ; return false end\nmatch_string = false\nstrings.flatten!\nif strings.empty? then echo(\"matchtimeout without any strings to wait for!\") ; sleep 1 ; return false end\nregexpstr = strings.join('|')\nwatcher_thread = Thread.new {\nwhile line_in = port.gets\nif line_in =~ /#{regexpstr}/i\nmatch_string = line_in.dup\nbreak\nend\nend\n}\nwatcher_thread.join(secs.to_f)\nwatcher_thread.kill if watcher_thread.alive?\nreturn match_string\nend\n\ndef matchbefore(*strings)\n strings.flatten!\n unless port = Script.find then echo(\"An unknown script thread tried to fetch a game line from the queue, but Lich can't process the call without knowing which script is calling! Aborting...\") ; Thread.current.kill ; return false end\n if strings.empty? then echo(\"matchbefore without any strings to wait for!\") ; return false end\n if port.unique then echo(\"this script is set as unique -- a 'match' will cause it to hang permanently! Aborting\") ; sleep 1 ; return false end\n regexpstr = strings.join('|')\n loop { if (line_in = port.gets) =~ /#{regexpstr}/ then return $`.to_s end }\nend\n\ndef matchafter(*strings)\n strings.flatten!\n unless port = Script.find then echo(\"An unknown script thread tried to fetch a game line from the queue, but Lich can't process the call without knowing which script is calling! Aborting...\") ; Thread.current.kill ; return false end\n if strings.empty? then echo(\"matchafter without any strings to wait for!\") ; return end\n if port.unique then echo(\"this script is set as unique -- a 'match' will cause it to hang permanently! Aborting\") ; sleep 1 ; return false end\n regexpstr = strings.join('|')\n loop { if (line_in = port.gets) =~ /#{regexpstr}/ then return $'.to_s end }\nend\n\ndef matchboth(*strings)\n strings.flatten!\n unless port = Script.find then echo(\"An unknown script thread tried to fetch a game line from the queue, but Lich can't process the call without knowing which script is calling! Aborting...\") ; Thread.current.kill ; return false end\n if strings.empty? then echo(\"matchafter without any strings to wait for!\") ; return end\n if port.unique then echo(\"this script is set as unique -- a 'match' will cause it to hang permanently! Aborting\") ; sleep 1 ; return false end\n regexpstr = strings.join('|')\n loop { if (line_in = port.gets) =~ /#{regexpstr}/ then break end }\n return [ $`.to_s, $'.to_s ]\nend\n\n#####\n# All rights reserved.\n# modification, are permitted provided that the following conditions\n#\n# notice, this list of conditions and the following disclaimer.\n# notice, this list of conditions and the following disclaimer in the\n#\n# may be used to endorse or promote products derived from this software\n#\n# \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n#####\n# ...... Jesus this file is a mess.......\n\nif ARGV.find { |arg| arg =~ /^--debug$/ }\n$stderr = File.open('lich_debug.txt','a')\n$stdout = $stderr\n$stderr.sync = true\n$stdout.sync = true\nARGV.delete_if { |arg| arg =~ /^--debug$/ }\nelsif $LICHCONFIG['OS'] =~ /win/i\n$stderr = File.open('lich_debug.txt','w')\n$stdout = $stderr\n$stderr.sync = true\n$stdout.sync = true\nend\n\n\n$: << '.' << 'gui'\n$:.uniq!\n\nbegin\nrequire \"gui/common.rb\"\ninclude Fox\nrescue\n$stderr.puts $!\nrescue LoadError\n$stderr.puts $!\nend\n\n\nclass NilClass\ndef +(val)\nval\nend\ndef closed?\ntrue\nend\ndef method_missing(*args)\nnil\nend\nend\n\nif ARGV.find { |arg| arg =~ /^-h$|^--help$/ } then puts <<CLIHELP\nUsage: lich [OPTION]\n\nOptions are:\n -h, --help Display this list.\n -V, --version Display the program version number and credits.\n\n -d, --directory Set the main Lich program directory (this run only).\n --script-dir Define the directory Lich will search for script files in (this run only). See example below.\n\n -s, --stormfront Run in StormFront mode (this run only). ip:port used by SF is storm.gs4.game.play.net:10024, stream encoding is XML-based\n -w, --wizard Run in Wizard mode (this run only). ip:port used by Wizard is gs3.simutronics.net:4900, stream encoding is GSL\n --dragonrealms Run in Wizard mode for the DragonRealms game (this run only).\n --platinum Platinum players connect to a different address than Prime players. This tells Lich to catch the Platinum connection.\n -g, --game Set the IP address and port of the game (this run only). See example below.\n\n -c, --compressed Do compression/decompression of the I/O data using Zlib (this is for MCCP, Mud Client Compression Protocol).\n --bare Perform no data-scanning, just pass all game lines directly to scripts. For maximizing efficiency w/ non-Simu MUDs.\n --debug Mainly of use in Windows; redirects the program's STDERR & STDOUT to the '/lich_err.txt' file.\n --uninstall Restore the hosts backup (and in Windows also launch the uninstall application).\n\nThe 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).\n\nThe 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.\n\n\nExamples:\n lich -w -d /usr/bin/lich/ (run Lich in Wizard mode using the dir '/usr/bin/lich/' as the program's home)\n lich -g gs3.simutronics.net 4000 (run Lich using the IP address 'gs3.simutronics.net' and the port number '4000')\n lich --script-dir /mydir/scripts (run Lich with its script directory set to '/mydir/scripts')\n 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')\n\nCLIHELP\nexit; end\n\nif ARGV.find { |arg| arg =~ /^-(?i:V)$|^--version$/ } then $stdout.puts <<CLIVERSION\nThe Lich, version #{$version}\n (an implementation of the Ruby interpreter by Yukihiro Matsumoto designed to be a `script engine' for text-based MUDs)\n\n- The Lich program and all material collectively referred to as \"The Lich project\" is copyright (C) 2005-2006 Murray Miron.\n- The Gemstone IV and DragonRealms games are copyright (C) Simutronics Corporation.\n- The Wizard front-end and the StormFront front-end are also copyrighted by the Simutronics Corporation.\n- Ruby is (C) Yukihiro `Matz' Matsumoto.\n- Inno Setup Compiler 5 is (C) 1997-2005 Jordan Russell (used for the Windows installation package).\n\nThanks to all those who've reported bugs and helped me track down problems on both Windows and Linux.\nCLIVERSION\nexit; end\n\nif ARGV[1] and ARGV[1] !~ /^-/\nsal_fname = ARGV[1]\nelse\nsal_fname = nil\nend\n\nsock_keepalive_proc = proc { |sock|\nerr_msg = proc { |err|\nerr ||= $!\n$stderr.puts Time.now\n$stderr.puts err\n$stderr.puts err.backtrace\n}\nbegin\nsock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)\nrescue\nerr_msg.call($!)\nrescue Exception\nerr_msg.call($!)\nend\n}\n\ntrace_var(:$_CLIENT_, sock_keepalive_proc)\ntrace_var(:$_SERVER_, sock_keepalive_proc)\n\n$LICHCONFIG.each_pair { |key, val| $LICHCONFIG[key] = File.expand_registry_str(val) }\nLich.reload_settings\nlich_dir = $lich_dir\nif File.exists?(lich_dir + 'Scripts')\nscript_dir = lich_dir + 'Scripts/'\nelsif File.exists?(lich_dir + 'scripts')\nscript_dir = lich_dir + 'scripts/'\nelse\nscript_dir = lich_dir.dup\nend\n\nif tmplich_dir = ARGV.find { |val| val =~ /^-d$|^--directory$/ }\nlich_dir = ARGV[ARGV.index(tmplich_dir).succ]\nunless lich_dir[-1..-1] =~ /\\\\|\\// then lich_dir += lich_dir.slice(/\\\\|\\//) end\nif File.exists?(lich_dir + 'scripts') then script_dir = \"#{lich_dir}scripts#{lich_dir.slice(/\\\\|\\//)}\" elsif File.exists?(lich_dir + 'Scripts') then script_dir = \"#{lich_dir}Scripts#{lich_dir.slice(/\\\\|\\//)}\" else $stdout.puts(\"WARNING, no script directory found! The 'scripts' directory must be inside the Lich directory for things to function properly.\"); script_dir = \"#{lich_dir}scripts#{lich_dir.slice(/\\\\|\\//)}\" end\n$stdout.puts(\"Lich directory set to #{lich_dir}\")\nend\n$nix = false\n\nif tmp = ARGV.find { |arg| arg =~ /^--launcher$/ }\ntmpidx = ARGV.index tmp\nARGV.delete_at(tmpidx)\nestr = \"$SAFE = 3\\n\" + ARGV[tmpidx..-1].join(\" \")\ntmp = tmpidx = nil\nestr.taint\nsimu_port = simu_ip = nil\neval(estr, nil, \"LauncherCode\")\nend\nunless sal_fname\nif ARGV.find { |arg| arg =~ /^-w$|^--wizard$/ }\nsimu_port = 4900; simu_ip = 'gs3.simutronics.net'; $stormfront = false\nelsif ARGV.find { |arg| arg =~ /^-s$|^--stormfront$/ }\nsimu_port = 10024; simu_ip = \"storm.gs4.game.play.net\"; $stormfront = true\nelsif gameinfoargv = ARGV.find { |arg| arg =~ /^-g$|^--game$/ }\nsimu_ip = ARGV[ARGV.index(gameinfoargv) + 1]\nsimu_port = ARGV[ARGV.index(gameinfoargv) + 2].to_i\n$stdout.puts(\"Game information being used: #{simu_ip}:#{simu_port}\"); $stdout.flush; $stormfront = false\nelsif gameinfoargv = ARGV.find { |arg| arg =~ /^--platinum$/ }\nsimu_ip = \"gs-plat.simutronics.net\"\nsimu_port = 10121\nelsif ARGV.find { |arg| arg =~ /^--dragonrealms$/i }\nsimu_ip = 'dr.simutronics.net'; simu_port = 4901\nelsif File.exists?(\"#{lich_dir}game.txt\")\nfile = File.open(\"#{lich_dir}game.txt\"); data = file.readlines; file.close; file = nil\ndata.delete_if { |line| line =~ /^\#/ }\nunless data.nil?\nsimu_ip = data.shift.strip\nsimu_port = data.shift.strip\nif data.first =~ /BARE/\n$BARE_BONES = true\nelsif data.first =~ /XML/\n$stormfront = true\nelsif data.first =~ /GSL/\nnil\nend\nend\nelsif (File.exists?(\"#{lich_dir}stormfront-mode.txt\") or File.exists?(\"#{lich_dir}stormfront-mode.txt.txt\"))\nsimu_port = 10024\nsimu_ip = \"storm.gs4.game.play.net\"\n$stormfront = true\nelsif File.exists?(\"#{lich_dir}dr-mode.txt\")\nsimu_port = 4901\nsimu_ip = \"dr.simutronics.net\"\n$stormfront = false\nelsif File.exists?(lich_dir + 'wizard-mode.txt')\nsimu_port = 4900\nsimu_ip = 'gs3.simutronics.net'\n$stormfront = false\nelsif File.exists?('/Gse.~xt') or File.exists?(ENV['HOME'] + '/.wine/drive_c/Gse.~xt')\nbegin\nprint(\"No game/front-end input found, auto-detecting...\")\nif File.exists?('/Gse.~xt')\nfile = File.open('/Gse.~xt')\nelse\nfile = File.open(ENV['HOME'] + '/.wine/drive_c/Gse.~xt')\nend\nguessdata = file.readlines.collect { |line| line.strip }\nfile.close\nfile = nil\nsimu_ip = guessdata.find { |line| line =~ /^GAMEHOST/ }.split('=').last.strip\nif simu_ip == '127.0.0.1'\nsimu_ip = 'gs3.simutronics.net'\nsimu_port = 4900\nprint(\" ...PsiNet alteration of file detected; configuring for Wizard.\\n\")\n$stormfront = false\nelse\nsimu_port = guessdata.find { |line| line =~ /^GAMEPORT/ }.split('=').last.strip.to_i\nfe = guessdata.find { |line| line =~ /^GAMEFILE/ }.split('=').last.strip\nif fe == 'WIZARD.EXE'\nprint(\" ...configuring for Wizard.\\n\")\n$stormfront = false\nelse\n$stormfront = true\nprint(\" ...configuring for StormFront.\\n\")\nend\nend\nrescue\n$stderr.puts(\"Unrecoverable error during read of 'Gse.~xt' file! Falling back on defaults...\")\n$stderr.puts($!)\nsimu_port = 4900\nsimu_ip = 'gs3.simutronics.net'\n$stormfront = false\nend\nelse\nsimu_port = 4900\nsimu_ip = \"gs3.simutronics.net\"\n$stormfront = false\nend\nelse\nsimu_port = nil\nsimu_ip = nil\nend\n\nif ARGV.find { |arg| arg =~ /^--bare$/ }\nputs \"Running in bare-bones mode.\"\n$BARE_BONES = true\nend\n\nputs(sprintf(\"IP:port = %s:%d\", simu_ip, simu_port)) unless sal_fname\n\nif set_sdir = ARGV.find { |arg| arg =~ /--script-dir/i }\nscript_dir = ARGV[ARGV.index(set_sdir) + 1]\nscript_dir[-1..-1] !~ /\\/|\\\\/ ? script_dir = File.join(script_dir, '') : nil\n$stdout.puts(\"Script dir has been set to '#{script_dir}'.\"); $stdout.flush\nend\n\nif ARGV.find { |arg| arg =~ /^--?c(?:ompressed)$/i }\n$ZLIB_STREAM = true\ntrace_var :$_SERVER_, proc { |server_socket|\n$_SERVER_ = ZlibStream.wrap(server_socket) if $ZLIB_STREAM\n}\ntrace_var :$_CLIENT_, proc { |client_socket|\n$_CLIENT_ = ZlibStream.wrap(client_socket) if $ZLIB_STREAM\n}\nelse\n$ZLIB_STREAM = false\nend\nlog_dir = lich_dir\n_TAGREGEXP_ = '^GSj|^GSg|^GSr|^GSm|^GSl|^GSZ|^GSY|^GSX|^GSn|^GSJ|^GSK|^GSq|^GSQ|^GSP|^GSa|^GSb'\n_SFPARSEREGEXP_ = '<[^>]+>'\n$SEND_CHARACTER = '>'\n$_FAKE_STORMFRONT = false\n\n$_SFPARSER_ = LichXML.new\n$_SFPARSER_.extend(SFMetadata)\n\n$lich_dir ||= lich_dir\n$script_dir ||= script_dir\n$data_dir ||= $lich_dir + \"data\" + File::SEPARATOR\n\n$right_hand = String.new\n$left_hand = String.new\n\n$npcs = Array.new\n$pcs = String.new\n$roomarea = String.new\n$roomtitle = String.new\n$room_count = 0\n$last_dir = String.new\n\n$familiar_directions = String.new\n$familiar_area = String.new\n$familiar_room = String.new\n$familiar_npcs = Array.new\n$familiar_pcs = String.new\n\nJUMP = Exception.exception('JUMP')\nJUMP_ERROR = Exception.exception('JUMP_ERROR')\n\n$_LICHERRCNT_ = 0\ntrace_var :$_LICHERRCNT_, proc { |n|\nif n >= 5\nbegin\n$_SERVER_.close unless $_SERVER_.closed?\n$_CLIENT_.close unless $_CLIENT_.closed?\nrescue Exception\nrescue\nensure\nexit\nend\nend\n}\n\nSocket.do_not_reverse_lookup = true\n$_TA_BUFFER_ = []\n$_SERVERBUFFER_ = CachedArray.new\n$_CLIENTBUFFER_ = CachedArray.new\n\nDir.entries(CachedArray.dir).find_all { |f| f =~ /^\\.cache[.\\d]+/ }.each { |f|\nif (Time.now - File.mtime(File.join(CachedArray.dir, f))) > 3600\nFile.delete(File.join(CachedArray.dir, f)) rescue()\nend\n}\nif $hosts_dir\nhosts_dir = File.expand_registry_str($hosts_dir)\n$hosts_dir = nil\nelse\nif ENV['windir']\nhosts_dir = find_hosts_file(ENV['windir'])\nelsif ENV['SYSTEMROOT']\nhosts_dir = find_hosts_file(ENV['SYSTEMROOT'])\nelse\nhosts_dir = find_hosts_file\nend\nend\n\nif ARGV.find { |arg| arg =~ /--uninstall/ }\n$stdout.puts(\"Uninstalling...\")\nuninsfile = Dir.entries(lich_dir).find_all { |file| file =~ /unins[0-9]+\\.exe/i }.sort.last\nif File.exists?($lich_dir + \"launcher-uninstall.dat\")\nsystem(\"\\\"#{$lich_dir}\" + \"lichlauncher.exe\\\" -u\")\nend\nif File.exists?(hosts_dir + 'hosts') and File.exists?($lich_dir + 'hosts.sav')\nif File.mtime(hosts_dir + \"hosts\") <= File.mtime($lich_dir + \"hosts.sav\")\nFile.open($lich_dir + 'hosts.sav') { |savf|\nFile.open(hosts_dir + 'hosts', 'w') { |hostf|\nhostf.write savf.read\n}\n}\nend\nexec(lich_dir + uninsfile) rescue()\nexit\nelsif File.exists?(hosts_dir + 'hosts')\n$stdout.puts(\"Restoration is unnecessary, launching uninstall application.\")\nexec(lich_dir + uninsfile) rescue()\nexit\nelse\n$stderr.puts(\"Cannot properly locate your hosts file or hosts backup file! If they can't be found to restore them, it's nearly impossible that they were found to change in the first place. Launching uninstall application.\")\nexec(lich_dir + uninsfile) rescue()\nexit\nend\nend\n\nif hosts_dir.nil? and sal_fname.nil? then $stderr.puts(\"hosts_dir is nil (#{Time.now})\"); exit(1) end\n\nunless sal_fname\nsimu_quad_ip = IPSocket.getaddress(simu_ip)\nbegin\nlistener = TCPServer.new(\"localhost\", simu_port)\nbegin\nlistener.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR,1)\nrescue\n$stderr.puts(\"Error during setsockopt, aborting setting of SO_REUSEADDR: #{$!}\")\nend\nrescue\n$temp_error ||= 0\n$temp_error += 1\nsleep 1\nretry unless $temp_error >= 30\n$stderr.puts(\"Lich cannot bind to the proper port, aborting execution.\")\nexit!\nend\n$temp_error = nil\nhack_hosts(hosts_dir, simu_ip)\nif File.exists?(\"#{lich_dir}nosge.txt\")\nnil\nelsif File.exists?(\"#{lich_dir}launch.txt\")\nlaunch_sge_user(lich_dir)\nelsif ($LICHCONFIG['SGE Directory'] and File.exists? File.join($LICHCONFIG['SGE Directory'], 'Sge.exe')) or File.exists?(\"/Program Files/SIMU/SGE/Sge.exe\")\nlaunch_sge_win(listener)\nelsif File.exists?(\"/home/fallen/.wine/drive_c/Program Files/SIMU/SGE/Sge.exe\")\nlaunch_sge_nix(listener)\nelsif File.exists?(\"#{ENV['HOME']} + '/.wine/drive_c/Program Files/SIMU/SGE/Sge.exe\")\nlaunch_sge_nix(listener)\nend\ntimeout_thread = Thread.new { sleep 120 ; $stderr.puts(\"Timeout, restoring backup and exiting.\") ; heal_hosts(hosts_dir); exit 1 }\nopen_client(listener)\ntimeout_thread.kill\ntimeout_thread = nil\nProcess.wait rescue()\nheal_hosts(hosts_dir)\nelse\n$stderr.puts \"sal_fname == #{sal_fname}\"\nbegin\nsal_data = File.open(sal_fname) { |file| file.readlines }.collect { |line| line.chomp }\nrescue\n$stderr.puts \"Error opening .sal (Simutronics Auto Launch) file: #{$!}\"\nexit(1)\nend\nunless gameport = sal_data.find { |line| line =~ /GAMEPORT=/ }\n$stderr.puts \".sal file contains no GAMEPORT info\"\nexit(1)\nend\nunless gamehost = sal_data.find { |opt| opt =~ /GAMEHOST=/ }\n$stderr.puts \".sal file contains no GAMEHOST info\"\nexit(1)\nend\nunless game = sal_data.find { |opt| opt =~ /GAME=/ }\n$stderr.puts \".sal file contains no GAME info\"\nexit(1)\nend\ngameport = gameport.split('=').last\ngamehost = gamehost.split('=').last\ngame = game.split('=').last\n$stderr.puts sprintf(\"gamehost: %s gameport: %s game: %s\", gamehost, gameport, game)\nbegin\nlistener = TCPServer.new(\"localhost\", nil)\nrescue\n$stderr.puts \"Cannot bind listening socket to local port: #{$!}\"\n$stderr.puts sprintf(\"HOST: %s PORT: %s GAME: %s\", gamehost, gameport, game)\n$stderr.puts sal_fname\nexit(1)\nend\nbegin\nlistener.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)\nrescue\n$stderr.puts \"Cannot set SO_REUSEADDR sockopt\"\nend\nlocalport = listener.addr[1]\nmod_data = []\nsal_data.each { |sal_line| mod_data.push sal_line.sub(/GAMEPORT=.+/, \"GAMEPORT=#{localport}\").sub(/GAMEHOST=.+/, \"GAMEHOST=localhost\") }\nFile.open($lich_dir + \"lich.sal\", \"w\") { |f| f.puts mod_data }\nif File.exists?($lich_dir + \"autolaunch.txt\")\nlaunch = File.open($lich_dir + 'autolaunch.txt') { |f| f.read.gsub(/[\\r\\n]/, '').strip }\nif launch.empty?\nThread.new { system(File.join($LICHCONFIG['Launcher Directory'], \"Launcher.exe #{$lich_dir + 'lich.sal'}\")) }\nelse\nThread.new { system(launch + ' ' + File.join($lich_dir, \"lich.sal\")) }\nend\nelsif File.exists?($lich_dir + \"launcher-uninstall.dat\")\nlaunch = File.open($lich_dir + \"launcher-uninstall.dat\")\nprog = launch.readlines.first.chomp\nlaunch.close\nThread.new { system(prog.sub(\"%1\", File.join($lich_dir.gsub(\"/\", \"\\\\\"), \"lich.sal\"))) }\nelse\nThread.new { system(File.join($LICHCONFIG['Launcher Directory'], \"Launcher.exe \" + $lich_dir.gsub(\"/\", \"\\\\\") + \"lich.sal\")) }\nend\ntimeout_thr = Thread.new {\nsleep 15\n$stderr.puts \"timeout waiting for connection.\"\nexit(1)\n}\n$_CLIENT_ = listener.accept\nbegin\ntimeout_thr.kill\nlistener.close\nrescue\n$stderr.puts $!\nend\n$_SERVER_ = TCPSocket.open(gamehost, gameport)\nif game =~ /STORM/i\n$stormfront = true\nend\nend\n\n\n\nunless ENV['OS'] =~ /win/i\nbegin\nProcess.uid = `id -ru`.strip.to_i\nProcess.gid = `id -rg`.strip.to_i\nProcess.egid = `id -rg`.strip.to_i\nProcess.euid = `id -ru`.strip.to_i\nrescue SecurityError\n$stderr.puts \"Error dropping superuser privileges: #{$!}\"\nrescue SystemCallError\n$stderr.puts \"Error dropping superuser privileges: #{$!}\"\nrescue\n$stderr.puts \"Error dropping superuser privileges: #{$!}\"\nend\nend\n\nDir.mkdir($data_dir) rescue()\n\nerrtimeout = 1\nbegin\n# listener.shutdown\nlistener.close unless listener.closed?\nrescue\n$stderr.puts(\"error closing listener socket: #{$!}\")\nerrtimeout += 1\nif errtimeout > 20 then $stderr.puts(\"error appears unrecoverable, aborting\") end\nsleep 0.05\nretry unless errtimeout > 20\nend\n\nif ARGV.find { |arg| arg =~ /^--test|^-t/ }\n$_SERVER_ = $stdin\n$_CLIENT_.puts \"Running in test mode: host socket set to stdin.\"\nelsif !$_SERVER_\nopen_gs(simu_quad_ip, simu_port)\nend\n\nerrtimeout = nil\nlistener = timeout_thr = nil\n\ndef show_favs(who=nil)\nbegin\nfile = File.open(\"#{$lich_dir}favorites.txt\"); fav_data = file.readlines; file.close; file = nil\nif who.nil?\nfavs_all = fav_data.find_all { |line| line =~ /^ALL:/ } ; favs_all.each_with_index { |scr,idx| favs_all[idx] = scr.sub('ALL:','') }\nfavs_char = fav_data.find_all { |line| line =~ /^#{checkname}:/ } ; favs_char.each_with_index { |scr,idx| favs_char[idx] = scr.sub((checkname + ':'),'') }\nelsif who == 'all'\nfav_list = fav_data.sort_by { |name| name.split(':').first }\nrespond(\"Favorites for All Characters:\")\nrespond(fav_list)\nreturn\nend\nrescue\nfav_data = [ \"none\" ]\nfavs_all = [ 'none' ]\nfavs_char = [ 'none' ]\nend\nif fav_data.empty? or fav_data.nil?\nfav_list = \"none\"\nelse\nfav_list_all = favs_all.join(', ').gsub(/\\r|\\n/,'')\nfav_list_char = favs_char.join(', ').gsub(/\\r|\\n/,'')\nend\nrespond(\"Global Favorites (apply to all chars): #{fav_list_all}.\") unless favs_all.empty?\nrespond(\"Favorites for Character #{checkname}: #{fav_list_char}.\") unless favs_char.empty?\nend\n\nif File.exists?(\"#{lich_dir}lich-char.txt\")\nfile = File.open(\"#{lich_dir}lich-char.txt\"); arr = file.readlines; arr = arr.find_all { |line| line !~ /^#/ }; $clean_lich_char = arr.last.strip; file.close; file = nil\nelse\n$clean_lich_char = ';'\nend\nlich_char = Regexp.escape(\"#{$clean_lich_char}\")\n\nundef :exit!\n\nclient_thread = Thread.new {\n$login_time = Time.now\nalias_failed = false\naliasarray = Array.new\nif File.exists?(lich_dir + 'aliases.txt')\nfile = File.open(lich_dir + 'aliases.txt', 'rb')\nnewfile = File.open($data_dir + 'aliases.dat', 'wb')\nnewfile.write(file.read)\nfile.close\nnewfile.close\nfile = nil\nnewfile = nil\nFile.delete(lich_dir + 'aliases.txt')\nGC.start\nend\nbegin\nif File.exists?(lich_dir + 'aliases.dat')\nFile.rename(lich_dir + 'aliases.dat', $data_dir + 'aliases.dat')\nend\nfile = File.open($data_dir + 'aliases.dat', 'rb'); alias_list_hash = Marshal.load(file.read); file.close; file = nil\nalias_list = alias_list_hash.keys.dup\nrescue\nalias_list_hash = Hash.new\nalias_list = Array.new\nfile = File.open($data_dir + 'aliases.dat','wb'); file.write(Marshal.dump(alias_list_hash)); file.close; file = nil\nalias_failed = true unless alias_failed\nretry unless alias_failed\nend\naliasregexp = alias_list.join('|^(?:<c>)?')\nrunning_alias = false\nif File.exists?(lich_dir + \"spellshorts.txt\")\ndospellshorts = true\nelse\ndospellshorts = false\nend\nif !($stormfront or $_FAKE_STORMFRONT) and File.exists?(lich_dir + \"ta_limit.txt\")\nfile = File.open(lich_dir + \"ta_limit.txt\")\n$TALIMIT = file.readlines.find_all { |line| line !~ /^#/ }.join.slice(/\\d+/).to_i\nfile.close\nfile = nil\nelse\n$TALIMIT = 0\nend\n2.times {\nclient_string = $_CLIENT_.gets\n$_CLIENTBUFFER_.push(client_string.dup)\n$_SERVER_.write(client_string)\n}\nuntil $_CLIENT_.closed? or $_SERVER_.closed?\n#undef :init_hooks\n\nbegin\n\nif defined?(Hook) and not defined?(UpstreamHook)\nUpstreamHook = Hook\nelse\nclass UpstreamHook\ndef UpstreamHook.method_missing(*args)\nfalse\nend\nend\nend\n\nwhile client_string = $_CLIENT_.gets\n$_IDLETIMESTAMP_ = Time.now\nif dospellshorts and (spellshort = /^\\s*(\\d\\d\\d\\d?)(.*)?$/.match(client_string.chomp))\nif !spellshort.captures[1].empty?\n$stormfront ? $_SERVER_.send(\"<c>prep #{spellshort.captures[0]}\\n\", 0) : $_SERVER_.send(\"prep #{spellshort.captures[0]}\\n\", 0)\n$stormfront ? $_SERVER_.send(\"<c>cast at #{spellshort.captures[1].strip}\\n\", 0) : $_SERVER_.send(\"cast at #{spellshort.captures[1].strip}\\n\", 0)\nelse\n$stormfront ? $_SERVER_.send(\"<c>incant #{spellshort.captures[0]}\\n\", 0) : $_SERVER_.send(\"incant #{spellshort.captures[0]}\\n\", 0)\nend\nnext\nend\nif client_string =~ /^(?:<c>)?#{lich_char}/o and (client_string !~ /^(?:<c>)?(#{aliasregexp})\\b([^\\r\\n]+)?$/ or client_string =~ /^(?:<c>)?#{lich_char}alias\\b/i)\nclient_string = client_string.sub(/#{lich_char}/o, ';')\ncmd = $'.chomp.downcase\nif cmd =~ /^\\!/\ncleaned_psi_string = client_string.sub(/^;\\!/, ';').chomp\n$_SERVER_.puts(cleaned_psi_string)\n$_CLIENTBUFFER_.push(cleaned_psi_string)\nrespond(\"--- Lich: saw the '!' (ignore this) signal. Sent: '#{cleaned_psi_string}'.\")\nelsif UpstreamHook.call(cmd)\nnext\nelsif cmd =~ /^spellshorts?$/i\ndospellshorts = !dospellshorts\nrespond \"--- Lich: spellshorts are now %s.\" % (dospellshorts ? \"on\" : \"off\")\nif dospellshorts\nFile.open($lich_dir + \"spellshorts.txt\", \"w\") { |f| f.puts } rescue()\nelse\nFile.unlink($lich_dir + \"spellshorts.txt\") rescue()\nend\nnext\nelsif cmd.empty?\nrespond(\"--- Lich: saw no data in the Lich-command string -- ignoring!\")\nelsif cmd =~ /^list$|^l$/\nunless Script.index.empty? then respond(\"--- Lich: #{Script.index.join(\", \")}.\") else respond(\"--- Lich: no active scripts.\") end\nrespond(\"--- Lich: 'Watchfors' active are #{Watchfor.list}.\") if Watchfor.any?\nelsif cmd =~ /^debug$/\n$LICH_DEBUG = !$LICH_DEBUG\nif $LICH_DEBUG\nrespond(\"--- Lich: debugging information will now be shown for scripts.\")\nelse\nrespond(\"--- Lich: debugging information will no longer be shown for scripts.\")\nend\nelsif eobj = /^(?:exec|e)(q)? .+/.match(cmd)\nThread.new {\ncmd_data = \"execscript.set_as_good\\n#{client_string.sub(/^(?:<c>;|;)(?:exec|execq|e|eq) /i, '')}\"\nThread.current.priority = 0\nbegin\nif eobj.captures.first.nil? then execscript = Script.new('exec') else execscript = Script.new('exec',['quiet']) end\neval(cmd_data, nil, execscript.name.to_s, -1)\nrespond(\"--- Lich: #{execscript.name} has finished.\") unless execscript.quiet\nexecscript.kill\nrescue SyntaxError\nrespond(\"--- Lich SyntaxError: #{$!}\")\nrespond(\"--- Lich: #{execscript.name} has exited.\")\nexecscript.kill\nrescue SystemExit\nrespond(\"--- Lich: #{execscript.name} has exited.\") unless execscript.quiet\nexecscript.kill\nrescue SecurityError\nrespond(\"--- Lich SecurityError: #{$!}\")\nrespond(\"--- Lich: #{execscript.name} has exited.\")\nexecscript.kill\nrescue ThreadError\nrespond(\"--- Lich ThreadError: #{$!}\")\nrespond(\"--- Lich: #{execscript.name} has exited.\")\nexecscript.kill\nrescue Exception\nrespond(\"--- Exception: #{$!}\")\nrespond(\"--- Lich: #{execscript.name} has exited.\")\nexecscript.kill\nrescue ScriptError\nrespond(\"--- ScriptError: #{$!}\")\nrespond(\"--- Lich: #{execscript.name} has exited.\")\nexecscript.kill\nrescue\nrespond(\"--- Lich Error: #{$!}\")\nrespond(\"--- Lich: #{execscript.name} has exited.\")\nexecscript.kill\nend\nScript.thread_hash.delete(Thread.current.group)\n}\nelsif cmd =~ /^kill$|^stop$|^k$/\nif Script.index.empty?\nrespond(\"--- Lich: no scripts running!\")\nelse\nrespond(\"--- Lich: #{Script.index.last.kill} stopped.\")\nend\nelsif cmd =~ /^kill all$|^stop all$|^ka$/\nwf = Watchfor.any?\nif Script.index.empty?\nunless wf\nrespond(\"--- Lich: no scripts running!\")\nelse\nWatchfor.untrace_all(true)\nrespond(\"--- Lich: all 'Watchfors' have been killed.\")\nend\nelse\nif (Script.index.find_all { |val| !val.no_ka }.empty?)\nrespond(\"--- Lich: all currently running scripts have set the 'no kill all' option! Kill them individually.\")\nnext\nelse\nWatchfor.untrace_all(true)\nScript.index.find_all { |ikeelu| !ikeelu.no_ka }.each { |ikeelu| ikeelu.kill }\nend\nlist = Script.index.find_all { |scr| scr.no_ka }.collect { |scr| scr.name }\nif wf\nif list.empty?\nrespond(\"--- Lich: all 'Watchfors' and all running scripts have been stopped.\")\nelse\nrespond(\"--- Lich: all 'Watchfors' and all scripts except #{list.join(', ')} have been stopped.\")\nend\nelse\nif list.empty?\nrespond(\"--- Lich: all scripts have been stopped.\")\nelse\nrespond(\"--- Lich: all scripts except #{list.join(', ')} have been stopped.\")\nend\nend\nend\nlist.clear\nlist = nil\nGC.start\nelsif cmd =~ /^k\\s.+|^stop\\s.+|^kill\\s.+/\ntarget_name = client_string.sub('<c>', '').sub(/^;k\\s|^;s\\s|^;stop\\s|^;kill\\s/i, '').chomp\nif (condemned = Script.index.find { |s_sock| s_sock.name == target_name }).nil?\ncondemned = Script.index.find { |s_sock| s_sock.name =~ /^#{target_name}/i }\nend\nif condemned.nil?\nrespond(\"--- Lich: #{target_name}.lic does not appear to be running! Use ';list' to see what's active.\")\nelse\nname = condemned.kill\nrespond(\"--- Lich: #{name} stopped.\")\nend\nGC.start\nelsif cmd == \"log\"\ndump_to_log(log_dir)\nelsif cmd =~ /^chat .+|^,.+|^who(?: \\w+)?$|^(\\w+):.+|^(admin) .+|^locate \\w+$|^info \\w+$|^skills \\w+$|^spells \\w+$|^health \\w+$/\nnamen = $1.dup\nif $2 == \"admin\"\nadminaction = $'.dup\nif (tgt = Script.index.find { |scr| scr.name =~ /lichnet/i })\ntgt.unique_puts(client_string.sub(/^(?:<c>)?;/,''))\nelse\nrespond(\"--- Lich: the LichNet client script must be running in order to talk to the LichNet server!\")\nend\ntgt = nil\nelsif (tgt = Script.index.find { |scr| scr.name =~ /lichnet/i })\nif !namen.nil?\ntgt.unique_puts(client_string.sub(/^(?:<c>;|;)[^:]+:/,\"to #{namen} \"))\nelse\ntgt.unique_puts(client_string.sub(/^(?:<c>;|;)(?:chat|,)?/i,'').sub(/^\\s*(locate|info|skills|spells|health) (\\w+)$/i, '::\\1::\\2'))\nend\nelse\nrespond(\"--- Lich: the LichNet client script must be running in order to talk to the LichNet server!\")\nend\ntgt = nil\nelsif cmd =~ /^(?:tunein|tuneout)$/i\nif tgt = Script.index.find { |scr| scr.name =~ /lichnet/i }\ntgt.unique_puts \"::#{cmd}::\"\nelse\nrespond(\"--- Lich: the LichNet client script must be running in order to talk to the LichNet server!\")\nend\ntgt = nil\nelsif cmd =~ /^send |^s /\n$_CLIENTBUFFER_.pop\nif cmd.split[1] == \"to\"\ntgt = Script.index.find { |fscr| fscr.name == cmd.split[2].chomp.strip }\nif tgt.nil?\ntgt = Script.index.find { |fscr| fscr.name =~ /^#{cmd.split[2].chomp.strip}/i }\nend\nif tgt.nil? then respond(\"--- Lich: '#{cmd.split[2].chomp.strip}' does not match any active script!\") ; next end\nmsg = client_string.sub(/^(?:<c>;|;)(?:[Ss]|[Ss][Ee][Nn][Dd])\\s[Tt][Oo]\\s#{cmd.split[2]}\\s/, '').chomp\nif tgt.unique\ntgt.unique_puts(msg)\nrespond(\"--- Lich: sent to '#{tgt}' ('unique' stack): #{msg}\")\nelse\ntgt.puts(msg)\nrespond(\"--- Lich: sent to '#{tgt}': #{msg}\")\nend\ntgt = nil\nelse\nmessage = client_string.sub(/(?:<c>;|;)(?:[Ss]|[Ss][Ee][Nn][Dd]) /, '')\nif Script.index.empty? then respond(\"--- No active scripts to send to!\") ; next end\nrespond(\"--- Sent: #{message}\")\nScript.namescript_incoming(message)\nend\nelsif cmd == \"reload\"\nLich.reload_settings\nbegin\nfile = File.open($data_dir + 'aliases.dat', 'rb'); alias_list_hash = Marshal.load(file.read); file.close; file = nil\nalias_list = alias_list_hash.keys.dup\nrescue\nfile = File.open($data_dir + 'aliases.dat', 'wb'); file.write(Marshal.dump(Hash.new)); file.close; file = nil\nretry\nend\nrespond(\"--- Lich: settings have been reloaded from your 'settings.txt' and 'aliases.dat' files.\")\nelsif cmd =~ /^fave?s add\\s.+/\n$_CLIENTBUFFER_.pop\nif client_string.split[2] =~ /all/i\nadd = client_string.sub('<c>', '').sub(/\\;fave?s add all /i, '').chomp\ntgt = 'ALL:'\nelse\nadd = client_string.sub('<c>', '').sub(/\\;fave?s add /i, '').chomp\ntgt = \"#{checkname}:\"\nend\nbegin\n file = File.open(\"#{lich_dir}favorites.txt\"); fav_data = file.readlines; file.close; file = nil\n fav_data.compact!\n list = Dir.entries($script_dir)\n unless list.find { |sname| sname =~ /^#{add}\\.lic/i }\nadd = list.find { |sname| sname =~ /^#{add}.*\\.lic/i }\nunless add.nil? then add.sub!(/\\.lic$/i,'') end\n end\n fav_data.push(tgt + add) unless add.nil?\n file = File.open(\"#{lich_dir}favorites.txt\", \"w\"); file.puts(fav_data); file.close; file = nil\nrescue SystemCallError\n file = File.open(\"#{lich_dir}favorites.txt\", \"w\"); file.puts(tgt + add); file.close; file = nil\nrescue\n respond(\"--- Lich: unrecoverable error encountered, aborting preferences update: #{$!}\")\nend\nshow_favs\ntgt = nil\nelsif cmd =~ /^fave?s del(?:ete)?\\s.+/\n$_CLIENTBUFFER_.pop\nif client_string.split[2] =~ /all/i\ndel = client_string.sub('<c>', '').sub(/^\\;fave?s del(?:ete)? all /i, \"\").chomp\ntgt = 'all'\nelse\ndel = client_string.sub('<c>','').sub(/^\\;fave?s del(?:ete)? /i,'').chomp\ntgt = \"#{checkname}\"\nend\nbegin\n file = File.open(\"#{lich_dir}favorites.txt\"); fav_data = file.readlines; file.close; file = nil\n fav_data.delete_if { |line| line =~ /^#{tgt}:#{del}/i }\n fav_data.compact!\n file = File.open(\"#{lich_dir}favorites.txt\", \"w\"); file.puts(fav_data); file.close; file = nil\nrescue\n respond(\"--- Lich: unrecoverable error encountered, aborting preferences update: #{$!}\")\nend\nshow_favs\ntgt = nil\nelsif cmd =~ /^(?:fave?s|fave?s all)$/\n$_CLIENTBUFFER_.pop\nif cmd.split.length == 1 then show_favs else show_favs('all') end\nelsif cmd =~ /^fave?s load$/\n$_CLIENTBUFFER_.pop\nbegin\nrespond(\"\\r\\n--- Lich: started #{(load_favs(lich_dir,script_dir,true).join(', '))}.\\r\\n\")\nrescue\nrespond; respond(\"Fatal error loading favs list: #{$!}\"); respond\nend\nelsif cmd == \"stats\"\n begin\n t_struct = Process.times\nramsz = 0\nObjectSpace.each_object(Script) { |scr| ramsz += (scr.io.length + scr.unique_io.length + scr.upstream_io.length + 12) }\nif File.exists? $_SERVERBUFFER_.getfd\nsbufsz = File.size($_SERVERBUFFER_.getfd)\nelse\nsbufsz = 0\nend\nif File.exists? $_CLIENTBUFFER_.getfd\npbufsz = File.size($_CLIENTBUFFER_.getfd)\nelse\npbufsz = 0\nend\nfary = []\nary = []\nObjectSpace.each_object(File) { |f| fary.push f.path if !f.closed? }\nObjectSpace.each_object(Script) { |s| ary.push s.name unless Script.find(s.name) }\nrespond\nrespond sprintf(\"Time passed since login: %s\", ((Time.now.to_f - $login_time.to_f) / 60.00).as_time)\nrespond sprintf(\"System CPU time used (secs): %f\", t_struct.stime)\nrespond sprintf(\"User CPU time used (secs): %f\", t_struct.utime)\nrespond sprintf(\"Lich's process ID: %d\", Process.pid)\necho\nrespond sprintf(\"Lich's `home' directory is: %s\", $lich_dir)\nrespond sprintf(\"Lich's `saved data' dir is: %s\", $data_dir)\nrespond sprintf(\"Lich's script directory is: %s\", $script_dir)\nrespond sprintf(\"Wizard script directory is: %s\", File.join($LICHCONFIG['Wizard Directory'], \"Scripts\")) if $LICHCONFIG['Wizard Directory']\nrespond sprintf(\"# of room transitions counted: %d%s\", $room_count - 1, ($stormfront ? \" (not accurate when using SF)\" : \"\"))\necho\nrespond sprintf(\"Size of temporary disk caches: %.3fKB\", (sbufsz + pbufsz) / 1024.0)\nrespond sprintf(\"RAM in use by script IO buffers: %.3fKB\", ramsz / 1024.0)\nrespond sprintf(\"# of dead scripts still on heap: %d%s\", ary.length, (ary.empty? ? \"\" : \" (#{ary.join(', ')})\"))\nrespond sprintf(\"# of open file descriptors: %d%s\", fary.length, (fary.empty? ? \"\" : \" (#{fary.join(', ')})\"))\necho\nrespond sprintf(\"# of scripts in scheduling pool: %d\", Script.index.length)\nary = Script.index.reject { |s| Script.wakelist.include? s }\nrespond sprintf(\"# of scripts queued to execute: %d%s\", ary.length, (ary.empty? ? \"\" : \" (#{ary.join(', ')})\"))\nrespond sprintf(\"# of scripts blocked on IO: %d%s\", Script.wakeme.length, (Script.wakeme.empty? ? \"\" : \" (#{Script.wakeme.collect { |thr| Script.index.find { |s| s.threads.list.include? thr } }.compact.join(', ')})\"))\nary = fary = nil\n rescue\n respond(\"--- Lich: an error while checking settings has occurred: #{$!}\")\n end\nelsif cmd == \"repeat notice\"\nshow_notice\nelsif cmd =~ /^alias(?:es)?( add | add| set | set| help| del )?([^=]+)?(?:\\=)?(.+)?$/\nbegin\nif $1.nil?\nif alias_list_hash.empty?\nrespond(\"\")\nrespond(\"--- You currently have no Lich aliases.\")\nrespond(\"\")\nelse\nrespond(\"--- Your current Lich aliases are:\\r\\n\")\ntn = '0'\nalias_list.sort.each { |akakey| respond(tn.succ! + ') ' + akakey.upcase + \" => \" + alias_list_hash[akakey])}\nend\nelsif ($1 == \" add \") || ($1 == \" add\") || ($1 == \" set \") || ($1 == \" set\") || ($1 == \" help\")\nif ($2.nil? || $3.nil?)\nrespond(\"- Syntax for alias setting is `;alias set [what you will type]=[what will be sent]'.\")\nrespond(\"- Syntax for alias deleting is `;alias del [which alias to delete]'.\")\nrespond(\"- Separate lines with `\\\\r'; ex: ;alias set telloff='I hate you!\\\\r'Go away!\")\nrespond(\"- You can also use `\\\\?', and Lich will replace it with whatever came after the alias.\")\nrespond(\"- `add' is a synonym for 'set'. Also, Lich will properly recognize aliased commands; e.g.:\")\nrespond(\" `;alias set startup=;test\\\\r;echo\\\\r;calcredux' would start scripts test, echo, & calcredux.\")\nnext\nend\nhkey = $2.to_s.chomp\nhval = client_string.sub(/[^=]+=\\s*/, '')\nalias_list_hash[hkey] = hval\nfile = File.open($data_dir + 'aliases.dat','wb'); file.write(Marshal.dump(alias_list_hash)); file.close; file = nil\nalias_list = alias_list_hash.keys.dup\naliasregexp = alias_list.join('|^(?:<c>)?')\nrespond(\"--- Alias `#{hkey.chomp}' => `#{hval.chomp}' added.\")\nnext\nelsif $1 == \" del \"\nif $2.nil?\nrespond(\"- Syntax for alias setting is `;alias set [what you will type]=[what will be sent]'.\")\nrespond(\"- Syntax for alias deleting is `;alias del [which alias to delete]'.\")\nrespond(\"- Separate lines with `\\\\r'; ex: ;alias set telloff='I hate you!\\\\r'Go away!\")\nrespond(\"- You can also use `\\\\?', and Lich will replace it with whatever came after the alias.\")\nrespond(\"- `add' is a synonym for `set'. Also, Lich will properly recognize aliased commands; e.g.:\")\nrespond(\" `;alias set startup=;test\\\\r;echo\\\\r;calcredux' would start scripts test, echo, & calcredux.\")\nnext\nend\ndelalias = $2.dup\nif (agone = alias_list_hash.keys.find { |akey| akey =~ /^#{Regexp.escape(delalias)}$/})\nvgone = alias_list_hash.delete(agone)\nelsif (agone = alias_list_hash.keys.find { |akey| akey =~ /^#{Regexp.escape(delalias)}/i })\nvgone = alias_list_hash.delete(agone)\nelse\nrespond(\"--- Alias `#{delalias}' was not found!\")\nnext\nend\nfile = File.open($data_dir + 'aliases.dat','wb'); file.write(Marshal.dump(alias_list_hash)); file.close; file = nil\nalias_list = alias_list_hash.keys.dup\naliasregexp = alias_list.join('|^(?:<c>)?')\nrespond(\"--- Alias `#{agone.chomp}' => '#{vgone.chomp}' removed.\")\nend\nrescue\nrespond \"--- Lich: unrecoverable error modifying aliases: #{$!}\"\nend\nelsif cmd =~ /^magic\\s?(?:clear|set|set .+|reset|help|details|enable|disable)?\\s?(?:[^\\s]+)?\\s?(?:[0-9]+)?$/\nif (infomon = Script.index.find { |val| val.name == 'infomonitor' })\ninfomon.puts(cmd)\ninfomon = nil\nelse\nrespond(\"--- Lich: the `infomonitor.lic' script must be running to do this!\")\nend\nelsif cmd =~ /^wiz .+|^w .+/\n begin\nnew_script = Regexp.escape(client_string.split[1]); client_string.sub!(/^(?:<c>)?;?(?i:wiz|w)(?:\\s+[^\\s]+)?(?:\\s+)?/,'')\nstart_wizard_script(new_script,client_string.scan(/\"[^\"]+\"|[^\"\\s]+/))\n rescue\n respond(\"--- Lich: #{$!}\")\n end\nelsif cmd =~ /^force .+/\nnew_script = Regexp.escape(client_string.sub(/^<c>;force |^;force /i, '').split.first.chomp)\nif File.exists?(script_dir.to_s + \"#{new_script}.lic\")\nstart_script(script_dir.to_s + \"#{new_script}.lic\",client_string.sub(/^(?:<c>;|;)(?i:force )#{new_script}\\s?/i, '').scan(/\"[^\"]+\"|[^\"\\s]+/).collect { |val| val.gsub(/(?!\\\\)?\"/,'') })\nelse\ns_files = Dir.entries(script_dir)[2..-1]\nif fname = s_files.find { |val| val =~ /^#{new_script}.+\\.(?i:lic|rbw?|cmd|gz)(?:gz)?$/ }\nstart_script(script_dir.to_s + fname.to_s,client_string.sub(/^(?:<c>;|;)(?:force )#{new_script}\\s?/i, '').scan(/\"[^\"]+\"|[^\"\\s]+/).collect { |val| val.gsub(/(?!\\\\)?\"/,'') })\nelse\nrespond(\"--- Lich: could not find script `#{new_script}' in directory #{script_dir}!\")\nnext\nend\nend\nelsif cmd =~ /^(?:ta|typeahead)(?: help| \\d)?$/i\nif cmd.include?('help') or !cmd.slice(/\\d+/)\nrespond \"--- Lich: This allows you to set your typeahead line limit threshold when you use The Wizard (this feature does not apply to StormFront). This option, when enabled, tells Lich how many typeahead lines the game will allow you to send -- if you try to send more lines than this number before the game has responded to what you've already sent, Lich will 'buffer' the commands and won't send them until the game will accept them (instead of rejecting them because of your type ahead limit). This effectively gives you an infinite number of typeahead lines. To disable this option, simply set your typeahead threshold to 0 by typing `;ta 0'. To enable it again, set your threshold to however many lines your Simutronics account is allowed. Basic subscribers -- like me -- are allowed 1 type ahead line, so to enable this feature you'd type `;ta 2' to tell Lich that if you send more than 2 commands to the game before it's responded, that it should buffer any further ones and send them as the game responds to what it's already sent.\" if cmd.include?('help')\nrespond \"Your current typeahead limit threshold is #{$TALIMIT}.\"\nelsif newnum = cmd.slice(/\\d+/)\n$TALIMIT = newnum.to_i\nif $TALIMIT.zero?\nrespond \"--- Lich: Type ahead compensation has been disabled.\"\nelse\nrespond \"--- Lich: Your typeahead limit threshold has been set to #{$TALIMIT}.\"\nend\nbegin\nfile = File.open(lich_dir + \"ta_limit.txt\", \"w\")\nfile.puts newnum\nfile.close\nrescue\nrespond \"--- Unknown error while trying to save the updated TA limit to disk: #{$!}\"\nend\nend\nelsif cmd =~ /^(?:tac|ta clear|typeahead clear)$/i\n$_TA_BUFFER_.clear\n$TA_waiting_on_resp = 0\nrespond \"--- Lich: current typeahead buffer has been discarded.\"\nelse\nnew_script = Regexp.escape(client_string.sub(/^<c>;|^;/, '').split.first.chomp)\nif File.exists?(script_dir.to_s + \"#{new_script}.lic\")\nif alrdy_name = Script.index.find { |running| running.name.sub(/ \\(paused\\)$/,'') == new_script }\nrespond(\"--- Lich: #{alrdy_name.name.sub(/ \\(paused\\)$/,'')} is already running (use #{$clean_lich_char}force [ScriptName] if desired).\")\nnext\nend\nstart_script(script_dir.to_s + \"#{new_script}.lic\",client_string.sub(/^(?:<c>;|;)#{new_script}\\s?/, '').scan(/\"[^\"]+\"|[^\"\\s]+/).collect { |val| val.gsub(/(?!\\\\)?\"/,'') })\nelse\ns_files = Dir.entries(script_dir)[2..-1]\nif fname = s_files.find { |val| val =~ /^#{new_script}\\.(?:lic|rbw?)(?:\\.gz|\\.Z)?$/i } ||\ns_files.find { |val| val =~ /^#{new_script}[^.]+\\.(?i:lic|rbw?)(?:\\.gz|\\.Z)?$/ } ||\ns_files.find { |val| val =~ /^#{new_script}[^.]+\\.(?:lic|rbw?)(?:\\.gz|\\.Z)?$/i } ||\ns_files.find { |val| val =~ /^#{new_script}$/i }\ns_files = nil\nif alrdy_name = Script.index.find { |running| running.name.sub(/ \\(paused\\)$/,'') == fname.gsub(/\\.(?:lic|rbw?|gz|Z)(?:gz|Z)?/i,'') }\nrespond(\"--- Lich: #{alrdy_name.name.sub(/ \\(paused\\)$/,'')} is already running (use #{$clean_lich_char}force [ScriptName] if desired).\")\nnext\nend\nstart_script(script_dir.to_s + fname.to_s,client_string.sub(/^(?:<c>;|;)#{new_script}\\s?/, '').scan(/\"[^\"]+\"|[^\"\\s]+/).collect { |val| val.gsub(/(?!\\\\)?\"/,'') })\nelse\nrespond \"--- Lich: unable to find a matching script, trying to load it as a Wizard .cmd file (toggle debug off to get rid of this notice)...\" if $LICH_DEBUG\nclient_string = \";wiz #{client_string[1..-1]}\"\nredo\nrespond(\"--- Lich: could not find script `#{new_script}' in directory #{script_dir}!\")\ns_files = nil\nnext\nend\nend\nend\nelsif (!running_alias and client_string =~ /^(?:<c>)?(#{aliasregexp})\\b([^\\r\\n]+)?$/ and !aliasregexp.empty?)\n if client_string == \"\\n\" then ($_SERVER_.write(\"\\n\") ; next) end\n begin\nif $2.nil? then aliastarget = String.new else aliastarget = $2.dup end\naliased = alias_list_hash[$2].dup\nif aliased.nil?\naliaskey = alias_list_hash.keys.find { |key| client_string.chomp.sub(/^<c>/,'').sub(/#{Regexp.escape(aliastarget)}/,'') =~ /#{key}/ }\naliased = alias_list_hash[aliaskey].dup\nend\nif aliased.nil? then respond(\"--- Alias error: recognized the command as being an alias, but couldn't identify which one!\") ; next end\nif aliased.include?('\\?')\naliased.gsub!('\\?',aliastarget)\nelse\naliased.concat(aliastarget)\nend\n$_CLIENTBUFFER_.push((client_string.chomp + '=>' + aliased))\naliasarray = aliased.split('\\r') ; aliasarray.each_with_index { |val,idx| aliasarray[idx] = (val.chomp + \"\\n\") }\nclient_string = aliasarray.shift\nrunning_alias = true\nredo\n rescue\n respond(\"--- Lich: alias error: #{$!}\")\n end\nelse\nif $TALIMIT.nil?\n$TALIMIT = 0\nend\n begin\n if $TALIMIT == 0\n$_SERVER_.puts client_string\nelsif client_string.strip.empty?\nnext\n elsif $TA_waiting_on_resp >= $TALIMIT\n ta_cmd = client_string.dup\nif ta_cmd.strip.empty?\nnext\nend\n$_TA_BUFFER_.push ta_cmd\n$_CLIENT_.puts \"(queued: #{ta_cmd.chomp})\"\nelse\n$TA_waiting_on_resp += 1\n$_SERVER_.puts client_string\nend\n$_CLIENTBUFFER_.push(client_string.dup)\n$_LASTUPSTREAM_ = client_string.dup\nScript.upscript_incoming(client_string)\n rescue\nrespond(\"--- Lich: error writing to #{$_SERVER_.inspect}: #{$!}\")\n end\nend\nunless aliasarray.empty?\nclient_string = aliasarray.shift\nredo\nelse\nrunning_alias = false\nend\nend\nbreak\nrescue Exception\nrespond \"Lich bug: #{$!}\"\nrespond $!.backtrace.join(\"\\r\\n\")\n$_LICHERRCNT_ += 1\nrescue SystemExit\nrespond \"Lich bug: #{$!}\"\nrespond $!.backtrace.join(\"\\r\\n\")\n$_LICHERRCNT_ += 1\nrescue NoMemoryError\nrespond \"Lich bug: #{$!}\"\nrespond $!.backtrace.join(\"\\r\\n\")\n$_LICHERRCNT_ += 1\nrescue\nrespond \"Lich bug: #{$!}\"\nrespond $!.backtrace.join(\"\\r\\n\")\n$_LICHERRCNT_ += 1\nend\nend\n$_SERVER_.puts(\"quit\") unless $_SERVER_.closed?\n$_SERVER_.close unless $_SERVER_.closed?\n$_CLIENT_.close unless $_CLIENT_.closed?\nexit\n}\nif $BARE_BONES\nserver_thread = Thread.new {\nbegin\nLichParser.bare_loop\nrescue\nrespond(\"--- Lich encountered an error: #{$!}\")\nrespond($!.backtrace.join(\"\\r\\n\"))\n$_LICHERRCNT_ += 1\nrespond(\"--- Lich: this error is non-fatal, continuing as usual.\")\nend\n}\nelsif !$stormfront\n server_thread = Thread.new {\nuntil $_CLIENT_.closed? or $_SERVER_.closed?\nbegin\nLichParser.gsl_loop\n break\nrescue\n$_CLIENT_.puts(\"Lich #{$version} StandardError: #{$!}\")\n$_CLIENT_.puts($!.backtrace.join(\"\\r\\n\"))\n$_LICHERRCNT_ += 1\nrescue Exception\nif $!.to_s =~ /invalid argument/io\n$_CLIENT_.puts(\"Lich #{$version}: the file descriptor for Lich's game socket is no longer recognized by Windows as a valid connection; either the game has crashed or you were dropped for inactivity and Lich wasn't notified that the socket has been closed. There isn't much I can do to get around this random quirk in Windows.\") if $LICH_DEBUG\n$_CLIENT_.puts($!.to_s) if $LICH_DEBUG\n$_CLIENT_.puts($!.backtrace.join(\"\\r\\n\")) if $LICH_DEBUG\n$_LICHERRCNT_ = 5\nelse\n$_CLIENT_.puts(\"Lich #{$version} Exception: #{$!}\") if $LICH_DEBUG\n$_CLIENT_.puts($!.backtrace.join(\"\\r\\n\")) if $LICH_DEBUG\n$_LICHERRCNT_ += 1\nend\nrescue SystemExit\n$_CLIENT_.puts(\"Lich #{$version} SystemExit: #{$!}\") if $LICH_DEBUG\n$_CLIENT_.puts($!.backtrace.join(\"\\r\\n\")) if $LICH_DEBUG\n$_LICHERRCNT_ += 1\nend\nend\n respond(\"--- Lich's link to the game was closed, closing clients...\") if $LICH_DEBUG\n respond(\"\\r\\n\\r\\n\") if $LICH_DEBUG\n $_CLIENT_.close unless $_CLIENT_.closed?\n $_SERVER_.puts(\"quit\") unless $_SERVER_.closed?\n $_SERVER_.close unless $_SERVER_.closed?\n exit\n }\nelse\n server_thread = Thread.new {\n$_TAGHASH_['GSa'] = '0000000000'\n$_TAGHASH_['GSb'] = '0000000000'\n$_TAGHASH_['GSP'] = String.new\nstat = String.new; xpdata = String.new\nthoughtflag = false\nuntil $_SERVER_.closed? or $_CLIENT_.closed?\nbegin\n while $_SERVERSTRING_ = $_SERVER_.gets\n$_CLIENT_.write($_SERVERSTRING_)\n$_SERVERBUFFER_.push($_SERVERSTRING_)\n$_SFPARSER_.parse($_SERVERSTRING_)\n$_SFPARSER_.endline\n_simustring_ = $_SERVERSTRING_.gsub(/#{_SFPARSEREGEXP_}/o, '')\nScript.statscript_incoming($_SERVERSTRING_)\nunless _simustring_ =~ /^\\s\\*\\s[A-Z][a-z]+ (?:returns home from a hard day of adventuring|joins the adventure|just bit the dust)|^\\r*\\n*$/o\nScript.namescript_incoming(_simustring_) unless _simustring_.empty? or $_SFPARSER_.current_stream\nend\n end\n break\nrescue Exception\nif $!.to_s =~ /invalid argument/oi\n$_CLIENT_.puts \"Lich #{$version}: the file descriptor for Lich's game socket is no longer recognized by Windows as a valid connection; either the game has crashed or you were dropped for inactivity and Lich wasn't notified that the socket has been closed. There isn't much I can do to get around this random quirk in Windows.\" if $LICH_DEBUG\n$_CLIENT_.puts($!.to_s) if $LICH_DEBUG\n$_CLIENT_.puts($!.backtrace.join(\"\\r\\n\")) if $LICH_DEBUG\n$_LICHERRCNT_ = 5\nelse\n$_CLIENT_.puts(\"Lich #{$version} Exception: #{$!}\") if $LICH_DEBUG\n$_CLIENT_.puts($!.backtrace.join(\"\\r\\n\")) if $LICH_DEBUG\n$_LICHERRCNT_ += 1\nend\nrescue SystemExit\n$_CLIENT_.puts(\"Lich #{$version} SystemError: #{$!}\") if $LICH_DEBUG\n$_CLIENT_.puts($!.backtrace.join(\"\\r\\n\")) if $LICH_DEBUG\n$_LICHERRCNT_ += 1\nrescue\n$_CLIENT_.puts(\"Lich #{$version} StandardError: #{$!}\") if $LICH_DEBUG\n$_CLIENT_.puts($!.backtrace.join(\"\\r\\n\")) if $LICH_DEBUG\n$_LICHERRCNT_ += 1\nend\nend\n respond(\"--- Lich's connection to the game has been closed.\") if $LICH_DEBUG and !$_CLIENT_.closed?\n respond(\"\\r\\n\\r\\n\") if $LICH_DEBUG and !$_CLIENT_.closed?\n $_CLIENT_.close unless $_CLIENT_.closed?\n $_SERVER_.puts(\"<c>quit\") unless $_SERVER_.closed?\n $_SERVER_.close unless $_SERVER_.closed?\n exit\n }\nend\n\nserver_thread.priority = 4\nclient_thread.priority = 3\n\nif ARGV.find { |arg| arg =~ /^--debug$/ }\n$stderr.close unless $stderr.closed?\nend\n$stdout = $_CLIENT_\nunless ARGV.find { |arg| arg =~ /^--stderr$/ }\n$stderr = $_CLIENT_\nelse\n$stderr.puts \"$stderr will not be redirected.\"\nend\n\n$_CLIENT_.sync = true\n$_SERVER_.sync = true\n\n$_CLIENT_.write(\"--- Lich v#{$version} caught the connection and is active. Type #{$clean_lich_char}help for usage info.\\r\\n\\r\\n\")\nfav_timeout = 0\nuntil $_SERVERBUFFER_.find { |line| line =~ /Welcome to GemStone/i } or $_SERVERBUFFER_.to_a.length > 5\nsleep 1\nend\nif $_SERVERBUFFER_.find { |line| line =~ /Welcome to GemStone.+Platinum/i }\n$_GSPLAT_ = true\nend\nsleep 1\nif ($stormfront || $_FAKE_STORMFRONT)\nnametimeout = 0\ncharacter_name = $_SERVERBUFFER_.find { |line| line =~ /<app char=\"\\w+\"/ }\nwhile character_name.nil?\ncharacter_name = $_SERVERBUFFER_.find { |line| line =~ /<app char=\"\\w+\"/ }\nnametimeout += 1\nbreak if nametimeout >= 15\nsleep 0.5\nend\nunless character_name.nil?\nChar.init(character_name.dup.chomp.slice(/<app char\\=\\\"\\w+\\\"/).gsub(/<app char\\=\\\"|\\\"/,''))\nLich.reload_settings\nend\nelse\nnametimeout = 0\ncharacter_name = $_TAGHASH_['GSB'].slice(/[A-Z][a-z]+/) || $_SERVERBUFFER_.find { |line| line =~ /\\034GSB/ }.slice(/[A-Z][a-z]+/)\ncha = $_SERVERBUFFER_.find { |line| line =~ /^\\034GSA/ }\nunless cha.nil? then Char.cha(cha) end\nunless character_name.nil? then Char.init(character_name.dup.chomp.sub(/\\034GSB[0-9]+/, '')); Lich.reload_settings end\nend\n$_CLIENTBUFFER_[0] = \"*** (encrypted login key would be here, but it is erased by Lich immediately after use) ***\"\nGC.start\n\ndef show_notice\nunless $stormfront\nrespond(\"\\034GSL\")\nelse\n#respond('<pushBold/>')\nend\nrespond\nrespond(\"** NOTICE:\")\nrespond(\"** Lich is not intended to facilitate AFK scripting.\")\nrespond(\"** The author does not condone violation of game policy,\")\nrespond(\"** nor is he in any way attempting to encourage it.\")\nrespond\nif $stormfront\nrespond(\"** (this notice will never repeat, it's one-time-only)\")\nelse\nrespond(\"** (this notice will never repeat, it's one-time-only)\\034GSM\")\n#respond('<output class=\"\"/>')\nend\nrespond(\"\\r\\n\")\nend\n\nunless File.exists?(\"#{lich_dir}notfirst.txt\") or !$_SERVERBUFFER_.find { |line| line =~ /GemStone|DragonRealm/i }\nbegin\nshow_notice\nfile = File.open(\"#{lich_dir}notfirst.txt\", \"w\"); file.puts(\"just tracks if this is your first run or not\"); file.close; file = nil\nif File.exists?(lich_dir + 'favorites.txt')\nfile = File.open(lich_dir + 'favorites.txt'); data = file.readlines; file.close; file = nil\ndata.unshift(\"ALL:infomonitor\\nALL:lichnet\\n\") unless data.find { |line| line =~ /^ALL:infomonitor/ || line =~ /^ALL:lichnet/ }\nfile = File.open(lich_dir + 'favorites.txt','w'); file.puts(data); file.close; file = nil\nelse\nfile = File.open(lich_dir + 'favorites.txt','w'); file.puts(\"ALL:infomonitor\\nALL:lichnet\"); file.close; file = nil\nend\nrespond\nrespond(\"For your convenience, the 'InfoMonitor' and 'LichNet' scripts have been added as global favorites. 'infomonitor.lic' is responsible for tracking your character statistics, active spells, and just about everything else that isn't automatically updated by the game server for the front-end's use (meaning basically anything your front-end does not have a graphical display for). 'lichnet.lic' is the client for the LichNet chat server, which is a blatant clone of PsiNet's OOC chat channel; I wrote the server just to learn how to do it, and since I have it, might as well run it. If you'd like to remove either of these from your favorites list, you can do so at anytime by typing: '#{$clean_lich_char}favs del all lichnet' or '#{$clean_lich_char}favs del all infomonitor' (type '#{$clean_lich_char}help' for further information).\")\nrescue\nrespond(\"There's been an unknown error recording that you've seen this notice. I'm sorry, but it appears Lich will\")\nrespond(\"have to repeat this notice every login: #{$!.chomp}.\")\nend\nend\n\nif fav_timeout <= 15 or $BARE_BONES or $_SERVERBUFFER_.length > 5\nif $_SERVERBUFFER_.find { |line| line =~ /GemStone|DragonRealm|Modus/i }\n$SIMUGAME = true\nend\nbegin\nsleep 0.5\nlist = load_favs(lich_dir,script_dir,true).join(', ')\nsleep 0.25\nrespond(\"\\r\\n--- Lich: started #{list}.\\r\\n\")\nrescue\nrespond; respond(\"Fatal error trying to start the scripts on your favorites list: #{$!}\"); respond\nend\nelse\nrespond(\"--- Lich: favorites list will not be auto loaded; timed out while trying to verify an active link to the game.\")\nend\nfav_timeout = nil\n\nundef :hack_hosts\nundef :open_client\nundef :open_gs\nif defined?(launch_sge_win)\nundef :launch_sge_win\nend\nif defined?(launch_sge_nix)\nundef :launch_sge_nix\nend\nundef :launch_sge_user\n\nbegin\nserver_thread.join\nrescue Exception\n$_LICHERRCNT_ += 1\nif server_thread.alive? and !$_CLIENT_.closed? and !$_SERVER_.closed?\nrespond \"Exception bug: #{$!}\" if $LICH_DEBUG\nrespond $!.backtrace.join(\"\\r\\n\") if $LICH_DEBUG\nretry\nend\nrespond \"Fatal (non-recoverable) error during execution: #{$!}\" if $LICH_DEBUG\nrespond $!.backtrace.join(\"\\r\\n\") if $LICH_DEBUG\nrescue SystemExit\n$_LICHERRCNT_ += 1\nif server_thread.alive? and !$_CLIENT_.closed? and !$_SERVER_.closed?\nrespond \"SystemExit bug: #{$!}\" if $LICH_DEBUG\nrespond $!.backtrace.join(\"\\r\\n\") if $LICH_DEBUG\nretry\nend\nrespond \"Fatal (non-recoverable) error during execution: #{$!}\" if $LICH_DEBUG\nrespond $!.backtrace.join(\"\\r\\n\") if $LICH_DEBUG\nrescue\n$_LICHERRCNT_ += 1\nif server_thread.alive? and !$_CLIENT_.closed? and !$_SERVER_.closed?\nrespond \"StandardError bug: #{$!}\" if $LICH_DEBUG\nrespond $!.backtrace.join(\"\\r\\n\") if $LICH_DEBUG\nretry\nend\nrespond \"Fatal (non-recoverable) error during execution: #{$!}\" if $LICH_DEBUG\nrespond $!.backtrace.join(\"\\r\\n\") if $LICH_DEBUG\nend\nexit\n\n"), 0);
return;
}