User:Ilde/MajorTimer

From Discworld MUD Wiki
Jump to: navigation, search
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<!-- Original by Garrion  -->
<!-- Version 3.2 by Ilde, built upon version 2.1 -->
<!-- MuClient version 4.40 -->
<!--
 
This plugin keeps track of how much Major Shield you have.  "help majortimer" will display a helpfile with available commands.  It's fine to make your own version of this plugin or use parts of it.
 
NOTE: Before using for the first time (that is, after installing, but before performing Major Shield), use the mtchar command to tell it what character you're logged in as, and use the mtset command to tell it your bonus.  The syntax is "mtchar <name>" and "mtset <number>".

Please note that this may not work correctly when using Minor Shield, or when mixing Major and Minor Shield.

-->
 
<muclient>
<plugin
  name="MajorTimer"
  author="Ilde"
  id="f6685da86910795e76a1884c"
  language="Lua"
  purpose="Shows time remaining on Major shield"
  save_state="y"
  date_written="2013-04-28 10:45:35"
  requires="4.40"
  version="3.1"
  >
<description trim="y">
<![CDATA[
Shows the time remaining on your major shield.
]]>
</description>
</plugin>
 
 
 
<!--  Triggers  -->
<triggers>
  <trigger
   enabled="y"
   match="^You are ((barely |really |perfectly )?protected by the (power|grace) of|shielded by the protective armour of) (.*)\.$"
   name="major_up"
   script="Add_Timer"
   regexp="y"
   send_to="12"
   sequence="100"
 >
  </trigger>
 
  <trigger
   enabled="y"
   ignore_case="y"
   match="^your (divine protection|protective aura) (is|has been) (strengthened|extended|weakened)\.$"
   name="major_extend"
   script="Add_Timer"
   regexp="y"
   sequence="100"
 >
  </trigger>
 
  <!-- These next two signal that you failed the ritual, OR the ritual was on someone else.  In either case the ritual modifiers become irrelevant. -->
  <trigger
   enabled="y"
   ignore_case="y"
   match="^((.*) loses interest in your ritual\.|You sense (.*)'s attention is elsewhere.).$"
   name="major_fail"
   script="Canceled"
   regexp="y"
   sequence="100"
 >
  </trigger>
 
  <trigger
   enabled="y"
   ignore_case="y"
   match="^((.*) is (protected|really protected) by the (power|grace) of (.*)|(Its|His|Her) divine protection has been (extended|strengthened|weakened)\.)$"
   name="major_on_another"
   script="Canceled"
   regexp="y"
   sequence="100"
 >
  </trigger>
 
  <trigger
   enabled="y"
   ignore_case="y"
   match="^ ?\* You are ((barely |really |perfectly )?protected by the (power|grace) of|shielded by the protective armour of) (.*)\.  You will be protected for (.*)\.$"
   name="shield_check"
   script="Adjust_Timer"
   regexp="y"
   sequence="100"
 >
  </trigger>
 
  <trigger
   enabled="y"
   match="Your divine protection expires."
   name="major_down"
   script="Clear_Timer"
   sequence="100"
 >
  </trigger>
 
 
  <trigger
   enabled="y"
   match="Your divine protection is weakening."
   name="major_warning"
   script="Warning"
   sequence="100"
 >
  </trigger>
 
 
  <trigger
   enabled="y"
   match="You do not have any arcane protection."
   name="no_major"
   script="Clear_Timer"
   sequence="100"
 >
  </trigger>
 
  <!-- Message for the various ritual bonuses and penalties. -->
  <trigger
   enabled="y"
   ignore_case="y"
   match="^(It seems|You are finding it|You find it) (.*) to perform major shield because (.*)\.$"
   name="modifier_check"
   script="adjust_bonus"
   regexp="y"
   sequence="100"
 >
  </trigger>
 
 
  <!-- Message when you've just logged in.-->
  <trigger
   enabled="y"
   ignore_case="y"
   match="You last logged in from * and are currently logged in from *."
   name="logged_on"
   script="Unpause_Timer"
   sequence="100"
 >
  </trigger>
 
  <!-- Message for logging in after disconnecting but not quitting or idling out -->
  <trigger
   enabled="y"
   ignore_case="y"
   match="You are already playing."
   name="logged_on_already_playing"
   script="Already_Playing"
   sequence="100"
 >
  </trigger>
 
  <!-- Message indicating the next thing you enter will be your character name.-->
  <trigger
   enabled="y"
   ignore_case="y"
   match="Or, enter your current character's name"
   name="about_to_log_in"
   script="Enter_Name"
   sequence="100"
 >
  </trigger>
 
  <!-- Message indicating the next thing you enter will be your password.  Makes the alias to capture stuff turn OFF.-->
  <trigger
   enabled="y"
   ignore_case="y"
   match="                     Where all your dreams can't come true."
   name="about_to_enter_pass"
   script="Enter_Password"
   sequence="100"
 >
  </trigger>
 
   
   <!-- shouldn't need this anymore  -->
  <trigger
   enabled="n"
   ignore_case="y"
   match="^You are (Miss |Mr |Mrs |Ms |Mx |Lonely |Mournful |Scary |Spooky |Wandering |Cheating |Cowardly |Brother |Sister |Mostly Reverend |Reverend |Blessed |Blessed father |Blessed mother |Blessed brother |Blessed sister |Venerable |Venerable brother |Venerable sister |Venerable father |Venerable mother |Holy |Holy brother |Holy sister |Beatus |Saint |Dr |Doctor |Professor |Butterfingers |Crafty |Crooked |Dastardly |Dishonest |Dodgy |Elusive |Evasive |Fingers |Furtive |Greased |Honest |Latent |Light-fingered |Quick-fingered |Quiet |Shady |Shifty |Silent |Slick |Sly |Tricky |Aunty |Biddy |Black |Gammer |Goodie |Goody |Gramma |Granny |Mama |Mistress |Mother |Mss |Nanna |Nanny |Old |Sister |Wee |Wicked |Young |Fat |Stuffed |Over-fed |Gimlet-eyed |Robust |Bearded |Burly |Plump |Rotund |Thin |Tiny |Mystic |Obscure |Complex |Learned |Potent |Wise |Grumpy |Cryptic |Dark |Scholarly |Grey-haired |Greybeard |Master |Mistress |Adroit |Dire |Maven |Quantum |Savant |Unseen |Archmaster |mistress |Archmage |Dame |Sir |Lady |Lord |the Amazing |the Civic-Minded |the Elegant |the Eloquent |the Helpful |the Helpful Citizen |the Stylish |the Upstanding Citizen |the Utterly Fluffy |the Wonderful |appallingly filthy |corpse looter |dull |feebleminded |i got punished |i got punished and all i got was this lousy title |i promise i won't do it again |insignificant |lying |malingering |naughty spawn |necrokleptomaniac |offensive |pillock |pointless |repentant |reprobate |shopkeeper murderer |silly spammy git |sitting in the corner |smelly |tantrum thrower |too stupid to live |vagrant |very sorry |very very sorry |waste of space |whinging |Sultan |Sultana |Shai |Sitt al-khasa |Shai |Sitt al-ri'asa |Shai |Sitt ishquaraya |Shai |Sitt a'daha |Nawab |Qasar |Mazrat |Effendi |Ya'uq |Mutasharid |Ishqaraya |Naughty Spawn |Kill Stealer |Feebleminded |Idiotic |Offensive |Corpse Looter |Cat Hating |Heathen |Foreign Dog |Infidel |Shopkeeper Murderer |Destitute |Parasitic |Hated |Cowardly |Criminal |Felon |M |Mlle |Mme |Contender |Venomous |Ancient |Exterminator |Rouge |Axe-master |Champion |Centurion |Flatulent |Competent |Cultured |Healer |Cutthroat |Knifey |Archaic |Lethal |Elementalist |Energetic |Festive |Gifted |Chef |Bloodthirsty |Impaler |Templar |Paranoid |Saintly |Crusher |Mysterious |Arcana |Diplomatic |Uncreative |Duelist |Perverse |Masterful |Fingers |Medical |Destined |Unburiable |Nasty |Nimble |Nurse |Murse |Obsolete |Old Woman |Old Man |Old |Crimewave |Antiquated |Fossilized |Mythical |Unlucky |Pious |Staffmistress |Staffmaster |Prehistoric |Rock-hard |Bruiser |Pulveriser |Literate |Wealthy |Senile |Shieldmistress |Shieldmaster |Filthy |Erratic Mechanic |Legendary |Swordmistress |Swordmaster |Terrible |Ruinous |Decrepit |Player Of Games |Headmistress |Headmaster |Multilingual |Golden |Unexpected |Unstoppable |Versatile |Virtuoso |Venerable |Opulent |Miner |Persistent |Well Travelled )?([a-z]+) ([a-z]+ )?the (.*)\.$"
   name="whoami"
   script="Set_Character_From_Whoami"
   regexp="y"
   sequence="100"
 >
  </trigger>
 
 
  <!-- Messages you get when you quit, shout 8, or idle out.-->
  <trigger
   enabled="y"
   ignore_case="y"
   match="^(Do come again!|Thanks for playing.  See you next time.|You idled out, sorry.)$"
   name="logged_off"
   script="Pause_Timer"
   regexp="y"
   sequence="100"
 >
  </trigger>
 
  <!-- Skills output.  Used to set bonus.-->
  <trigger
   enabled="n"
   ignore_case="y"
   match="faith.rituals.defensive.self    *   *"
   name="skills"
   script="Set_Bonus_From_Skills"
   sequence="100"
 >
  </trigger>
 
 
</triggers>
 
<!--  Aliases  -->
<aliases>
  <alias
   match="mtreset"
   enabled="y"
   send_to="12"
   script="Force_Reset_Timer"
   name="force_reset"
   sequence="100"
 >
  </alias>
 
  <alias
   match="mtset *"
   enabled="y"
   send_to="12"
   script="set_bonus"
   name="set_length"
   sequence="100"
 >
  </alias>
 
  <alias
   match="mtadd *"
   enabled="y"
   send_to="12"
   script="Man_Add_Timer"
   name="force_add"
   sequence="100"
 >
  </alias>
 
  <alias
   match="mtminus *"
   enabled="y"
   send_to="12"
   script="Man_Minus_Timer"
   name="force_minus"
   sequence="100"
 >
  </alias>
 
  <alias
   match="mtchar *"
   enabled="y"
   send_to="12"
   script="Man_Set_Character"
   name="set_char"
   sequence="100"
 >
  </alias>
 
  <alias
   enabled="y"
   ignore_case="y"
   match="help majortimer"
   name="helpmajor"
   send_to="12"
   sequence="100">
    <send>print_help()</send>
  </alias>
 
 
  <alias
   match="su *"
   enabled="y"
   send_to="12"
   script="Switch_User"
   name="su"
   sequence="100"
 >
  </alias>
 
  <!-- Commands to, respectively, turn the whoami alias on and off. -->
  <alias
   match="mtwhoami on"
   enabled="y"
   send_to="12"
   script="Whoami_On"
   name="whoamion"
   sequence="100"
 >
  </alias>
 
  <alias
   match="mtwhoami off"
   enabled="y"
   send_to="12"
   script="Whoami_Off"
   name="whoamioff"
   sequence="100"
 >
  </alias>
 
  <!-- Commands to, respectively, turn the skills alias on and off. -->
  <alias
   match="mtskills on"
   enabled="y"
   send_to="12"
   script="Skills_On"
   name="skillson"
   sequence="100"
 >
  </alias>
 
  <alias
   match="mtskills off"
   enabled="y"
   send_to="12"
   script="Skills_Off"
   name="skillsoff"
   sequence="100"
 >
  </alias>
 
  <!-- Commands to, respectively, turn verbose mode on and off. -->
  <alias
   match="mtverbose on"
   enabled="y"
   send_to="12"
   script="Verbose_On"
   name="verboseon"
   sequence="100"
 >
  </alias>
 
  <alias
   match="mtverbose off"
   enabled="y"
   send_to="12"
   script="Verbose_Off"
   name="verboseoff"
   sequence="100"
 >
  </alias>
 
  <alias
   match="mtverbose debug"
   enabled="y"
   send_to="12"
   script="Verbose_Debug"
   name="verbosedebug"
   sequence="100"
 >
  </alias>
 
  <!-- Command to change the verbosity of the window. -->
  <alias
   match="mtwintext *"
   enabled="y"
   send_to="12"
   script="Window_Verbosity"
   name="windowverbosity"
   sequence="100"
 >
  </alias>
 
  <!-- Command to change the window position. -->
  <alias
   match="mtmove *"
   enabled="y"
   send_to="12"
   script="Move_Window"
   name="movewindow"
   sequence="100"
 >
  </alias>
 
  <!-- If you use mtmove without any arguments, it displays a mini helpfile. -->
  <alias
   match="mtmove"
   enabled="y"
   send_to="12"
   script="Move_Window_Help"
   name="movewindowhelp"
   sequence="100"
 >
  </alias>
 
  <!-- Commands to change the window position. -->
  <alias
   match="mtposx *"
   enabled="y"
   send_to="12"
   script="Set_X_Position"
   name="setxposition"
   sequence="100"
 >
  </alias>
 
  <alias
   match="mtposy *"
   enabled="y"
   send_to="12"
   script="Set_Y_Position"
   name="setyposition"
   sequence="100"
 >
  </alias>
 
 
  <!-- Command to add one performance's worth of shielding to the timer. -->
  <alias
   match="mtaddone"
   enabled="y"
   send_to="12"
   script="Add_Timer"
   name="mtaddone"
   sequence="100"
 >
  </alias>
 
  <!-- Command to reset the timer to zero. -->
  <alias
   match="mtclear"
   enabled="y"
   send_to="12"
   script="Clear_Timer"
   name="mtclear"
   sequence="100"
 >
  </alias>
 
  <!-- This is ONLY turned on when it sees the text indicating the next thing you enter will be your username. -->
  <alias
   match="*"
   enabled="n"
   send_to="12"
   script="Login"
   name="login_name"
   sequence="100"
 >
  </alias>
</aliases>
 
<!--  Timers  -->
<timers>
  <timer
   script="show_time"
   enabled="y"
   second="1.00"
   >
  </timer>
</timers>
 
<!--  Script  -->
<script>
<![CDATA[
 
require "serialize"
 
active = ""
 
 
--function to set your bonus manually, triggered by the mtset alias
function set_bonus (name, line, wildcards)
 bonus = wildcards[1]
 Save_Setting ("bonus", bonus)
 if verbose == "on" or verbose == "debug" then
   Note("Bonus set to ", bonus, ", which gives you a base duration of ", bonus_to_duration(bonus), " seconds.")
 end --if
 --SetChanged (true)  -- world has changed... this doesn't seem necessary after all?
end -- set_bonus
 
--function to set your bonus from skills output
function Set_Bonus_From_Skills (name, line, wildcards)
 bonus = wildcards[2]
 Save_Setting ("bonus", bonus)
 if verbose == "on" or verbose == "debug" then
   Note("Bonus set from skills output to ", bonus, ", which gives you a base duration of ", bonus_to_duration(bonus), ".")
 end --if
end --Set_Bonus_From_Skills
 
--function to calculate the duration (seconds) to add from the bonus specified
function bonus_to_duration (current_bonus)
 return 2 * math.floor(((400 * (math.log(current_bonus + 75)) - 2082.5) / 2) + .5)
end -- bonus_to_duration
 
 
--function to add more time to the timer when it sees the extended/protected messages
function Add_Timer ()
 active = "yes"
 Save_Setting("active", active)
 
 --These next two: need to make sure that when it tries to do math on effective bonus, it actually EXISTS.  If not, set both to the default.
 if bonus then
 --do nothing
 else
   bonus = 307
 end --if
 if effective_bonus then
 --do nothing
 else
   effective_bonus = bonus
 end --if
 
 
 --calculate actual seconds to be added
 time_to_add = bonus_to_duration(effective_bonus)
 
 --if the finish time already exists we can just add the time to it.  Otherwise, create it as the os time plus the time to add.
 if finish_time and finish_time ~= "" then
   finish_time = finish_time + time_to_add
 else
   finish_time = os.time () + time_to_add
 end -- if
 
 if verbose == "debug" then
   Note(time_to_add, " seconds added.")
 end --if
 
 --finally, save the finish time for pause/unpause purposes, reset the effective bonus to the base, and do the window
 Save_Setting ("finish_time", finish_time)
 effective_bonus = bonus
 show_time ()
end -- Add_Timer
 
 
--function to reset the timer to one performance's worth
function Force_Reset_Timer ()
   finish_time = os.time ()
   Add_Timer ()
end -- Force_Reset_Timer
 
 
--function to manually add a specified number of minutes, triggered by the mtadd alias
function Man_Add_Timer (name, line, wildcards)
 if finish_time and finish_time ~= "" then
   finish_time = finish_time + (wildcards[1]*60)
   show_time ()
 else
   active = "yes"
   Save_Setting ("active", active)
   finish_time = os.time () + (wildcards[1]*60)
   show_time ()
 end -- if
 Note(wildcards[1] .. " mins added to your timer.")
 Save_Setting ("finish_time", finish_time)
end -- Man_Add_Timer
 
 
--function to manually subtract a specified number of minutes, triggered by the mtminus alias
function Man_Minus_Timer (name, line, wildcards)
 if finish_time and finish_time ~= "" then
   finish_time = finish_time - (wildcards[1]*60)
   show_time ()
 else
   active = ""
   Save_Setting ("active", active)
   finish_time = ""
   show_time ()
 end -- if
 Note(wildcards[1] .. " mins removed from your timer.")
 Save_Setting ("finish_time", finish_time)
end -- Man_Minus_Timer

 
--function to reset the duration, triggered by failure message, stopping the ritual, performing on someone ELSE, or target leaving.
function Canceled ()
 --reset the effective bonus because any ritual modifier message you may have gotten is now irrelevant
 effective_bonus = bonus
end -- Canceled
 
 
--a function to check what visible ritual modifiers you have and adjust the duration accordingly.
function adjust_bonus (name, line, wildcards)
 effective_bonus = bonus
 modifiers = wildcards[3]
 text_difficulty = wildcards[2]
 ritual_bonuses = 0
 ritual_penalties = 0
 bonuses_text = "Ritual bonuses detected: "
 penalties_text = "Ritual penalties detected: "
 
--messages common to all
   --prayed recently
   if string.find(modifiers, "you have been actively worshipping") then
       effective_bonus = effective_bonus + 7
       ritual_bonuses = ritual_bonuses + 7
       bonuses_text = bonuses_text .. "prayed or ritburied, "
   end -- if
   --deity pool settings
   if string.find(modifiers, "the hand of") then
       --It's 3 points for 1%.  This is very rarely used, however, because it drains the pool so quickly, and so it will probably be at 1% if it's on at all.  The highest setting, "10%", actually drains 1 dp per gp spent, so even the "1%" setting probably actually drains 10% of gp spent.  Really can't see anybody ever setting it higher.
       effective_bonus = effective_bonus + 3
       ritual_bonuses = ritual_bonuses + 3
       bonuses_text = bonuses_text .. "deity pool settings (presumed 1%), "
   end -- if
   --other high altar OR high altar... the "other high altar" message is confusable with the "at high altar" message, so do this.
   if string.find(modifiers, "you are in an area consecrated to another God") then
       effective_bonus = effective_bonus - 45
       ritual_penalties = ritual_penalties - 45
       penalties_text = penalties_text .. "at wrong high altar, "
   elseif string.find(modifiers, "you are in a") then
       effective_bonus = effective_bonus + 12
       ritual_bonuses = ritual_bonuses + 12
       bonuses_text = bonuses_text .. "at high altar, "
   end -- if
   --near high altar, this varies
   if string.find(modifiers, "you are near") then
       --This bonus actually ranges from +3 (just barely inside the zone where you get a bonus at all) to +9(right outside an altar).  I've arbitrarily put it at +6 since most of the time when you're getting this, you'll be in the same city as an altar but probably not right outside.  So, a middling value seems appropriate.
       effective_bonus = effective_bonus + 6
       ritual_bonuses = ritual_bonuses + 6
       bonuses_text = bonuses_text .. "near high altar, "
   end -- if
   --witch/wizard property
   if string.find(modifiers, "you sense that the gods have averted their eyes from this place") then
       effective_bonus = effective_bonus - 45
       ritual_penalties = ritual_penalties - 45
       penalties_text = penalties_text .. "on witch or wizard property, "
   end -- if
   --minions
   if string.find(modifiers, "you are concentrating on") then
       penalties_text = penalties_text .. "minions ("
       num_minions = 0
       
       --it's POSSIBLE that there could be multiple of the same type of whirlwind ("controlling two gigantic whirlwinds of dust") or spectre.  Also, in darkness, there can be "controlling two things".  Look for number words and count them.
       if string.find(modifiers, " two ") then
           modifiers, num_identicals = string.gsub(modifiers, " two ", "")
           num_minions = num_minions + (num_identicals * 2)
           penalties_text = penalties_text .. num_identicals .. " set(s) of two identical-looking minions. "
       end --if
       if string.find(modifiers, " three ") then
           modifiers, num_identicals = string.gsub(modifiers, " three ", "")
           num_minions = num_minions + (num_identicals * 3)
           penalties_text = penalties_text .. num_identicals .. " set(s) of three identical-looking minions, "
       end --if
       if string.find(modifiers, " four ") then
           modifiers, num_identicals = string.gsub(modifiers, " four ", "")
           num_minions = num_minions + (num_identicals * 4)
           penalties_text = penalties_text .. num_identicals .. " set(s) of four identical-looking minions. "
       end --if
       if string.find(modifiers, " five ") then
           modifiers, num_identicals = string.gsub(modifiers, " five ", "")
           num_minions = num_minions + (num_identicals * 5)
           penalties_text = penalties_text .. num_identicals .. " set(s) of five identical-looking minions. "
       end --if
       if string.find(modifiers, " six ") then
           modifiers, num_identicals = string.gsub(modifiers, " six ", "")
           num_minions = num_minions + (num_identicals * 6)
           penalties_text = penalties_text .. num_identicals .. " set(s) of six identical-looking minions. "
       end --if
       if string.find(modifiers, " seven ") then
           modifiers, num_identicals = string.gsub(modifiers, " seven ", "")
           num_minions = num_minions + (num_identicals * 7)
           penalties_text = penalties_text .. num_identicals .. " set(s) of seven identical-looking minions. "
       end --if
       if string.find(modifiers, " eight ") then
           modifiers, num_identicals = string.gsub(modifiers, " eight ", "")
           num_minions = num_minions + (num_identicals * 8)
           penalties_text = penalties_text .. num_identicals .. " set(s) of eight identical-looking minions. "
       end --if
       if string.find(modifiers, " nine ") then
           modifiers, num_identicals = string.gsub(modifiers, " nine ", "")
           num_minions = num_minions + (num_identicals * 9)
           penalties_text = penalties_text .. num_identicals .. " set(s) of nine identical-looking minions. "
       end --if
       if string.find(modifiers, " ten ") then
           modifiers, num_identicals = string.gsub(modifiers, " ten ", "")
           num_minions = num_minions + (num_identicals * 10)
           penalties_text = penalties_text .. num_identicals .. " set(s) of ten identical-looking minions. "
       end --if
       
       --count any unique devils
       if string.find(modifiers, " whirlwind of ") then
           modifiers, num_devils = string.gsub(modifiers, " whirlwind of ", "")
           num_minions = num_minions + num_devils
           penalties_text = penalties_text .. num_devils .. " unique dust devils. "
       end --if
       
       --unidentified minion in the dark (multiples will be "[number] things" and thus counted above)
       if string.find(modifiers, " something") then
           num_minions = num_minions + 1
           penalties_text = penalties_text .. "Unidentified minion in the dark (something). "
       end --if
       
       --unidentified minion in the dark
       if string.find(modifiers, " someone") then
           modifiers, num_others = string.gsub(modifiers, " someone", "")
           num_minions = num_minions + num_others
           penalties_text = penalties_text .. "Unidentified minion in the dark (someone)."
       end --if
       
       --then see if there's a totem
       if string.find(modifiers, "misty woman") or string.find(modifiers, "dust bunny") or string.find(modifiers, "tall robed man") or string.find(modifiers, "rocky creature") or string.find(modifiers, "lung fish") or string.find(modifiers, "large vulture") or string.find(modifiers, "large salamander") then
           num_minions = num_minions + 1
           penalties_text = penalties_text .. "Totem. "
       end --if
       
       
       --then see if there's a creeping doom
       if string.find(modifiers, " black cloud") then
           num_minions = num_minions + 1
           penalties_text = penalties_text .. "Doom. "
       end --if
       
       --then see if there are unique spectres (but only ONE since multiple ones will be counted above)
       if string.find(modifiers, "spectre ") or string.find(modifiers, "spectre\.") or string.find(modifiers, "spectre\, ") then
           modifiers, _ = string.gsub(modifiers, "spectres", "")
           modifiers, num_spectres = string.gsub(modifiers, " spectre", "")
           num_minions = num_minions + num_spectres
           penalties_text = penalties_text .. num_spectres .. " unique spectres."
       end --if
       
       effective_bonus = effective_bonus - (num_minions * 4)
       ritual_penalties = ritual_penalties - (num_minions * 4)
       if num_minions > 2 then
           effective_bonus = effective_bonus - 1
           ritual_penalties = ritual_penalties - 1
       end --if
       
       penalties_text = penalties_text .. num_minions .. " minion(s) total.), "
   end -- if
   --alignment
   if string.find(modifiers, "you can barely feel the presence of") then
       effective_bonus = effective_bonus - 5
       ritual_penalties = ritual_penalties - 5
       penalties_text = penalties_text .. "getting out of align, "
   end -- if
   --far from a high altar
   if string.find(modifiers, "you are far from") then
       --This penalty actually ranges from -2 (just barely in the zone where you get the penalty) to -8 (as far as you can even get from an altar--although you actually get this a lot sooner).  I've set it at -8 since that's what you get in Genua (if not a Gappic or Hattite) or the Ramtops (if not Pishite, Sekkite, or Gufnorkian).  I think most of the time, if you're getting this one at all, you're probably getting the maximum penalty.  There's a bigger "zone" that's at -8 than for anything else, I think.
       effective_bonus = effective_bonus - 8
       ritual_penalties = ritual_penalties - 8
       penalties_text = penalties_text .. "far from an altar, "
   end -- if
   --drunk
   if string.find(modifiers, "Hat approves of your inebriated state") then
       --no modifier because they cancel out
       penalties_text = penalties_text .. "drunk (but Hat likes drunkenness, so no overall penalty), "
   elseif string.find(modifiers, "you are drunk") then
       effective_bonus = effective_bonus - 33
       ritual_penalties = ritual_penalties - 33
       penalties_text = penalties_text .. "drunk, "
   end -- if
   --poisoned
   if string.find(modifiers, "poisoned") then
       effective_bonus = effective_bonus - 33
       ritual_penalties = ritual_penalties - 33
       penalties_text = penalties_text .. "poisoned, "
   end -- if
   --using baton.  This doesn't work properly, because the actual penalty varies, but it needed to be set to SOMEthing.
   if string.find(modifiers, "as an external focus") then
       effective_bonus = effective_bonus - 56
       ritual_penalties = ritual_penalties - 56
       penalties_text = penalties_text .. "using a baton, "
   end -- if
   --not a regular shield, not sure if this even exists anymore
   if string.find(modifiers, "???") then
       bonuses_text = bonuses_text .. "using a fake shield, "
   end -- if
   
   
--Pishite rain/snow/sleet bonus
   if string.find(modifiers, "you feel uplifted by the ") then
       effective_bonus = effective_bonus + 7
       ritual_bonuses = ritual_bonuses + 7
       bonuses_text = bonuses_text .. "rain, snow, or sleet, "
   end -- if
   
--Gufnorkian
   if string.find(modifiers, "you are uplifted by the fluff you carry") then
       effective_bonus = effective_bonus + 7
       ritual_bonuses = ritual_bonuses + 7
       bonuses_text = bonuses_text .. "fluff in inventory, "
   end -- if
   
   if string.find(modifiers, "you feel at home in the fluffy surroundings") then
       effective_bonus = effective_bonus + 3
       ritual_bonuses = ritual_bonuses + 3
       bonuses_text = bonuses_text .. "somewhere fluffy, "
   end -- if
   
--Gappic
   if string.find(modifiers, "you feel comfortable in your stylish clothes") then
       effective_bonus = effective_bonus + 4
       ritual_bonuses = ritual_bonuses + 4
       bonuses_text = bonuses_text .. "stylish clothes, "
   end -- if
   
   if string.find(modifiers, "you feel comfortable in your stylish surroundings") then
       effective_bonus = effective_bonus + 3
       ritual_bonuses = ritual_bonuses + 3
       bonuses_text = bonuses_text .. "stylish place, "
   end -- if
   
--Sandelfonian
   if string.find(modifiers, "you feel more comfortable praying indoors") then
       effective_bonus = effective_bonus + 7
       ritual_bonuses = ritual_bonuses + 7
       bonuses_text = bonuses_text .. "indoors, "
   end -- if
 
--Fishite
   if string.find(modifiers, "you are underwater") then
       effective_bonus = effective_bonus + 14
       ritual_bonuses = ritual_bonuses + 14
       bonuses_text = bonuses_text .. "underwater, "
   end -- if
   
   if string.find(modifiers, "you are floating in the water") then
       effective_bonus = effective_bonus + 7
       ritual_bonuses = ritual_bonuses + 7
       bonuses_text = bonuses_text .. "water surface, "
   end -- if
   
   
--Hattite
   if string.find(modifiers, "Hat approves of your trespass onto the holy ground") then
       effective_bonus = effective_bonus + 9
       ritual_bonuses = ritual_bonuses + 9
       bonuses_text = bonuses_text .. "trespasser"
   end -- if
   
   if string.find(modifiers, "Hat approves of your company") then
       effective_bonus = effective_bonus + 16
       ritual_bonuses = ritual_bonuses + 16
       bonuses_text = bonuses_text .. "with other priests, "
   end -- if
   
--Sekkite
   if string.find(modifiers, "you feel more confident as you battle your foes") then
       effective_bonus = effective_bonus + 5
       ritual_bonuses = ritual_bonuses + 5
       bonuses_text = bonuses_text .. "in combat, "
   end -- if
   
   --do some stuff here to make sure the numeric modifiers are within the ranges indicated by various difficulty messages (much harder, much easier, etc.)
   
   
   if ritual_bonuses == 0 then
       bonuses_text = "No ritual bonuses."
   else
       bonuses_text = bonuses_text .. "for a total of +" .. ritual_bonuses .. " to your bonus."
   end --if
   if ritual_penalties == 0 then
       penalties_text = "No ritual penalties."
   else
       penalties_text = penalties_text .. "for a total of " .. ritual_penalties .. " to your bonus."
   end --if
   
   if verbose == "on" or verbose == "debug" then
       Note(bonuses_text, "  ", penalties_text, "  Your bonus is ", bonus, " and your effective bonus is ", effective_bonus, ".")
   end -- if
   
end -- adjust_bonus
 
 
 
function Parse_Numbers (duration_part)
   num_days = 0 --start it at 0
   text_days = string.sub(duration_part, 11, -6)  --will look like "more than three days" so everything after "more than " up until " days"
   --if it's over a hundred
   if string.find(text_days, "hundred") then
       hundred_start, hundred_end, _ = string.find(text_days, " hundred") --find where "hundred" starts
       num_hundreds_end = hundred_start - 1
       num_hundreds = string.sub (text_days, 1, num_hundreds_end)  --grab the thing right before "hundred"
       num_days = num_days + (Parse_One_Through_Nineteen(num_hundreds) * 100)
       
       remaining_text_start = hundred_end + 2
        text_days = string.sub(text_days, remaining_text_start) --we've added the n hundred days in, so now take that bit out of the text
       --if it was evenly divisible by a hundred there should be no "and ", but if it wasn't, we want to get rid of the "and "
       if string.sub(text_days, 1, 4) == "and " then
           text_days = string.sub(text_days, 5)
       end -- if
   end -- if
   --next, find any parts that indicate twenty, thirty, etc.
   if string.find(text_days, "ty") then
       _, tens_end, _ = string.find(text_days, "ty")
       num_tens = string.sub(text_days, 1, tens_end) --grab the thing that ends in "ty"
        num_tens = Parse_Tens(num_tens) --and figure out which one it is, then add it in
       num_days = num_days + num_tens
       remaining_text_start = tens_end + 2
        text_days = string.sub(text_days, remaining_text_start) --we've added the n ten days in, so now take that bit out of the text
       
       --it doesn't actually matter whether there's a dash ("forty eight" vs "forty-eight") because we're skipping that character either way.
 
   end -- if
   --after all this the only thing even POSSIBLY left should be one, two ... nineteen.
   if text_days == "" then
       return num_days
   else
       num_days = num_days + Parse_One_Through_Nineteen(text_days)
   end -- if
   
   
   return num_days
end -- Parse_Numbers
 
function Parse_One_Through_Nineteen (text_number)
   if text_number == "one" then
       return 1
   elseif text_number == "two" then
       return 2
   elseif text_number == "three" then
       return 3
   elseif text_number == "four" then
       return 4
   elseif text_number == "five" then
       return 5
   elseif text_number == "six" then
       return 6
   elseif text_number == "seven" then
       return 7
   elseif text_number == "eight" then
       return 8
   elseif text_number == "nine" then
       return 9
   elseif text_number == "ten" then
       return 10
   elseif text_number == "eleven" then
       return 11
   elseif text_number == "twelve" then
       return 12
   elseif text_number == "thirteen" then
       return 13
   elseif text_number == "fourteen" then
       return 14
   elseif text_number == "fifteen" then
       return 15
   elseif text_number == "sixteen" then
       return 16
   elseif text_number == "seventeen" then
       return 17
   elseif text_number == "eighteen" then
       return 18
   elseif text_number == "nineteen" then
       return 19
 
   else  --this should NOT happen, however, if it does...
       return 0
   end --if
end --Parse_One_Through_Nineteen
 
 
function Parse_Tens (text_number)
   if text_number == "twenty" then
       return 20
   elseif text_number == "thirty" then
       return 30
   elseif text_number == "forty" then
       return 40
   elseif text_number == "fifty" then
       return 50
   elseif text_number == "sixty" then
       return 60
   elseif text_number == "seventy" then
       return 70
   elseif text_number == "eighty" then
       return 80
   elseif text_number == "ninety" then
       return 90
   else  --this should NOT happen, however, if it does...
       return 0
   end --if
end --Parse_Tens
 
 
function Adjust_Timer (name, line, wildcards)
 duration_part = wildcards[5] -- grab the part of the line that is the textual duration and translate into seconds
 
 if duration_part == "less than five minutes" then
   min_duration = 1
   max_duration = 299
 elseif duration_part == "at least five minutes" then
   min_duration = 300
   max_duration = 899
 elseif duration_part == "at least a quarter of an hour" then
   min_duration = 900
   max_duration = 1799
 elseif duration_part == "at least half an hour" then
   min_duration = 1800
   max_duration = 3599
 elseif duration_part == "at least an hour" then
   min_duration = 3600
   max_duration = 7199
 elseif duration_part == "at least a couple of hours" then
   min_duration = 7200
   max_duration = 21599
 elseif duration_part == "more than a quarter of a day" then
   min_duration = 21600
   max_duration = 43199
 elseif duration_part == "more than half a day" then
   min_duration = 43200
   max_duration = 86399
 elseif duration_part == "more than a day" then
   min_duration = 86400
   max_duration = 172799
 else --for values over a day, they should all follow the "more than n days" pattern, parse this
   num_days = Parse_Numbers (duration_part)
   --convert days to seconds
   min_duration = num_days * 24 * 60 * 60
   max_duration = ((num_days + 1) * 24 * 60 * 60) - 1
 end -- if
 
 
 if finish_time and finish_time ~= "" then -- figure out time the plugin thinks is left
   time_left = finish_time - os.time ()
 else
   time_left = 0
 end -- if
 
 if time_left < min_duration then   -- see if the alleged time left is within what shields says
   time_left = min_duration
 elseif time_left > max_duration then
   time_left = max_duration
 else
   -- do nothing
 end -- if
 
 active = "yes"  -- set things up for the show_time function
 Save_Setting ("active", active)
 finish_time = os.time () + time_left
 Save_Setting ("finish_time", finish_time)
 show_time ()
end -- Adjust_Timer
 
 
function Clear_Timer (name, line, wildcards)
 time_left = 0
 active = ""
 finish_time = ""
 previous_time_left = ""
 Save_Setting ("finish_time", finish_time)
 Save_Setting ("active", active)
 Save_Setting ("previous_time_left", previous_time_left)
 
end -- Clear_Timer
 
 
 --function to reset the time left to 20 seconds, triggered by the "weakening" message
function Warning (name, line, wildcards)
 active = "yes"
 Save_Setting ("active", active)
 time_left = 20
 finish_time = os.time () + 20
 show_time ()
 Save_Setting ("finish_time", finish_time)
end -- Man_Add_Timer
 
 
--Called when you log out etc.  Saves the remaining time on the timer for this character.
function Pause_Timer (name, line, wildcards)
 if active == "yes" then
   previous_time_left = finish_time - os.time ()
   Note ("Paused major timer.")
 end -- if
 Save_Saveable_Variables ()
end -- pause_timer
 
 
function Already_Playing ()
 already_playing = true
end -- Already_Playing
 
 
--Called when you login etc.  Grabs the remaining time for that character, if any, and starts the timer up where it left off from last time.
function Unpause_Timer ()
 active = Retrieve_Setting("active")
 bonus = Retrieve_Setting("bonus")
 if bonus == "" then
   bonus = 307
 end -- if
 effective_bonus = bonus
 previous_time_left = Retrieve_Setting("previous_time_left")
 if already_playing then
   finish_time = Retrieve_Setting("finish_time")
   already_playing = nil
 elseif previous_time_left ~= "" then
   finish_time = os.time () + previous_time_left
   active = "yes"
   Note ("Unpaused major timer.")
   Save_Setting ("finish_time", finish_time)
 end -- if
end -- unpause_timer
 
 
function Switch_User (name, line, wildcards)
--send whatever you entered, unaltered, on to the MUD
 Send(line)
--save whichever character you su'ed from
 Save_Saveable_Variables ()
 previous_character = current_character
--grab the name of the character you're suing to
 current_character = wildcards[1]
end -- Switch_User
 
 
--turns the whoami alias on.  Triggered by mtwhoami on
function Whoami_On ()
 world.EnableTrigger("whoami", true)
 SetVariable("whoami", "on")
end --Whoami_On
 
--turns the whoami alias off.  Triggered by mtwhoami off
function Whoami_Off ()
 world.EnableTrigger("whoami", false)
 SetVariable("whoami", "")
end --Whoami_Off
 
 
--turns the skills alias on.  Triggered by mtskills on
function Skills_On ()
 world.EnableTrigger("skills", true)
 SetVariable("skills", "on")
end --Skills_On
 
--turns the skills alias off.  Triggered by mtskills off
function Skills_Off ()
 world.EnableTrigger("skills", false)
 SetVariable("skills", "")
end --Skills_Off
 
 
--turns verbose mode on.  Triggered by mtverbose on.  This is global, not per character.  Verbose mode causes extra messages in some places.  (Extra messages: saying how your bonus is affected by whatever ritual modifiers it sees; saying what bonus you've set it (manually or via skills output) to and what base duration that gives you; telling you when it detects which character you're using.)
function Verbose_On ()
 verbose = "on"
 SetVariable("verbose", "on")
end --Verbose_On
 
--debug mode, even more messages.  (Tells you when it saves a character-specific setting, tells you how much time has been added.)
function Verbose_Debug ()
 verbose = "debug"
 SetVariable("verbose", "debug")
end --Verbose_Debug
 
--turns verbose mode off.  
function Verbose_Off ()
 verbose = ""
 SetVariable("verbose", "")
end --Verbose_Off
 
 
--Triggered by the text, "Or, enter your current character's name".  When that shows up the next thing entered might be your login, so enable the alias to grab whatever you enter.
function Enter_Name ()
 world.EnableAlias("login_name", true)
end -- Enter_Name
 
--Triggered by the text, "                     Where all your dreams can't come true.", which is part of the thing you get AFTER you enter your name.  Hopefully this will stop it grabbing your password if you enter your name too quickly.
function Enter_Password ()
 world.EnableAlias("login_name", false)
end -- Enter_Password
 
function Login (name, line, wildcards)
--send whatever you entered, unaltered, on to the MUD
 Send(line)
--save whatever you entered as your current character
 --but make sure it's not a *command* instead of a character name.  Not that it hurts per se, but it can clutter up the save file.
 if line ~= "q" and line ~= "m" and line ~= "d" and line ~= "r" and line ~= "u" and line ~= "l" and line ~= "p" and line ~= "f" and line ~= "n" and line ~= "g" then
   current_character = line
   if verbose == "on" or verbose == "debug" then
       Note("[Major Timer] Character detected: ", current_character)
   end -- if
 end -- if
--turn the alias off again.
 world.EnableAlias("login_name", false)
end -- Login
 
 
function Set_Character_From_Whoami (name, line, wildcards)
 potential_character = wildcards[2]
 if potential_character == "not" or potential_character == "already" or potential_character == "remembering" or potential_character == "keeping" or potential_character == "unsure" or potential_character == "crouching" or potential_character == "kneeling" or potential_character == "lying" or potential_character == "lounging" or potential_character == "meditating" or potential_character == "sitting" or potential_character == "squatting" or potential_character == "standing" then  
 --do nothing, this stops it from setting itself from "you are not holding", "you are already wearing the", position stuff, etc.
 else
   current_character = potential_character
   if verbose == "on" or verbose == "debug" then
       Note("[Major Timer] Character detected: ", current_character)
   end -- if
 end -- if
end -- Set_Character_From_Whoami
 
 
--manually set the character with mtchar
function Man_Set_Character (name, line, wildcards)
 current_character = wildcards[1]
 if verbose == "on" or verbose == "debug" then
   Note("Character set: ", current_character)
 end -- if
 Unpause_Timer ()
end -- Man_Set_Character

--move the window to an autoposition
function Move_Window (name, line, wildcards)
 absolute_positioning = "false"
 SetVariable("absolute_positioning", "false")
 
 --some handling to make it not throw up if you give it a string instead of a number
 window_position = tonumber(wildcards[1])
 if window_position == nil then
	window_position = 5
 end -- if
 
 SetVariable("window_position", window_position)
 show_time ()
end -- Move_Window

--set the absolute position of the window (x-position is set separately from y)
function Set_X_Position (name, line, wildcards)
 absolute_positioning = "true"
 SetVariable("absolute_positioning", "true")
 --in case of garbage input
 x_position = tonumber(wildcards[1])
 if x_position == nil then
  x_position = 0
 end -- if
 SetVariable("x_position", x_position)
 show_time ()
end -- Set_X_Position
 
 
function Set_Y_Position (name, line, wildcards)
 absolute_positioning = "true"
 SetVariable("absolute_positioning", "true")
 --in case of garbage input
 y_position = tonumber(wildcards[1])
 if y_position == nil then
  y_position = 0
 end -- if
 SetVariable("y_position", y_position)
 show_time ()
end -- Set_Y_Position
 
 
function Window_Verbosity (name, line, wildcards)
 if wildcards[1] == "verbose" or wildcards[1] == "brief" then
	window_verbosity = wildcards[1]
	SetVariable("window_verbosity", wildcards[1])
	show_time ()
 else
	Note ("Acceptable arguments are 'verbose' or 'brief'.")
 end -- if
end -- Window_Verbosity
 
 
--actually makes the window showing the remaining time.
function show_time (name, line, wildcards)
 if active == "yes" and finish_time ~= "" then
   time_left = finish_time - os.time ()
   
   if window_verbosity == "brief" then
	start_text = ""
	warning_text = "Expiring!"
   else
    start_text = "Major shield expires in: "
	warning_text = "Shield due to drop!"
   end -- if
   
   if time_left <= 0 then
     --These next two lines are so that there will not be a finish_time that's in the past.  Otherwise, if it thought it should have dropped five minutes ago and you added four minutes of shielding, it would think it should have dropped one minute ago, when it should actually think it's now due to drop in four minutes.
     time_left = 0
     finish_time = os.time ()
     text = warning_text
   elseif time_left < 60 then
     text = string.format (start_text .. time_left .. " seconds.")
   elseif time_left >= 60 and time_left < 3600 then
     mins_left = string.format ("%3.1f", (time_left / 60))
     text = string.format (start_text .. mins_left .. " minutes.")
   elseif time_left >= 3600 and time_left < 86400 then
     hrs_left = string.format ("%3.1f", ((time_left / 60)/60))
     text = string.format (start_text .. hrs_left .. " hours.")
   elseif time_left >= 86400 then
     days_left = string.format ("%i", (((time_left / 60)/60))/24)
     hrs_left = string.format ("%3.1f", (((time_left - (days_left*60*60*24))/60)/60))
     text = string.format (start_text .. days_left .. " days and " .. hrs_left .. " hours.")
   end -- if
 
   max_width = (WindowTextWidth (win, "fn", text)) + 10
   
   if window_position == null then
	 window_position = 5 -- auto-position: top middle
   end -- if
   
   if time_left < 60 then
     TEXT_COLOUR = ColourNameToRGB ("red")
   else
     TEXT_COLOUR = ColourNameToRGB ("black")
   end -- if
 
   -- recreate the window the correct size, either in an auto-position or in an absolute position
   if absolute_positioning == "true" then
	   
	   if x_position == null then
		x_position = 0
	   end -- if
	   if y_position == null then
		y_position = 0
	   end -- if
 	   WindowCreate (win,
					x_position, y_position,   -- left, top
					max_width,     -- width
					font_height + 10,  -- height
					window_position,       -- auto-position: top middle
					2,  -- flags; this means that it's using absolute positioning
					ColourNameToRGB ("lightgoldenrodyellow"))  
	   WindowSetZOrder (win, 20)
   else
	   WindowCreate (win,
					0, 0,   -- left, top (auto-positions)
					max_width,     -- width
					font_height + 10,  -- height
					window_position,       -- auto-position: top middle
					0,  -- flags
					ColourNameToRGB ("lightgoldenrodyellow"))
	   WindowSetZOrder (win, 20)
	end -- if
 
   WindowText (win, "fn",
                 text,                       -- text
                 5, 5, 0, 0,                 -- rectangle
                 TEXT_COLOUR,                -- colour
                 false)                      -- not Unicode
   WindowShow (win, true)
 else
   WindowShow (win, false)  
 end -- if active
end -- show_time
 
 
function OnPluginInstall ()
 win = GetPluginID ()
 font_id = "fn"
 font_name = "Comic Sans MS"    -- the actual font
 
 -- make win so I can grab the font info
 WindowCreate (win,
                0, 0, 1, 1,  -- 1 x 1 pixel
                1,   -- position - irrelevant
                0,   -- flags
                0)   -- background colour
               
 check (WindowFont (win, font_id, font_name, 8))  -- normal
 font_height = WindowFontInfo (win, font_id, 1)  -- height
 
 --load settings for this character
 current_character = GetVariable("current_character") or "default"
 
 active = Retrieve_Setting("active") or ""
 finish_time = Retrieve_Setting("finish_time") or ""
 previous_time_left = Retrieve_Setting("previous_time_left") or ""
 --if we have no bonus set for this character, use this arbitrary default bonus (guildmax and all 13s)
 bonus = Retrieve_Setting("bonus") or 307
 
 --and get these global settings
 verbose = GetVariable("verbose") or ""
 whoami = GetVariable("whoami") or ""
 skills = GetVariable("skills") or ""
 window_position = GetVariable("window_position") or 5
 window_position = tonumber(window_position)
 if window_position == nil then
	window_position = 5
 end -- if
 absolute_positioning = GetVariable("absolute_positioning") or ""
 x_position = GetVariable("x_position") or 0
 y_position = GetVariable("y_position") or 0
 window_verbosity = GetVariable("window_verbosity") or "verbose"
 if whoami == "on" then
   world.EnableTrigger("whoami", true)
 else
   world.EnableTrigger("whoami", false)
 end -- if
 if skills == "on" then
   world.EnableTrigger("skills", true)
 else
   world.EnableTrigger("skills", false)
 end -- if
 
end -- OnPluginInstall
 
function OnPluginDisable ()
 WindowShow (win, false)
end -- OnPluginDisable
 
 
 
-------------------------------------------------------------------------------------------
-- on saving state, convert Lua table back into string variable save_simple is for simple
-- tables that do not have cycles (self-reference) or refer to other tables
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
-- Before, it was using serialize.save_simple.  However, that seemed to be adding a bunch
-- of backslashes and quotes to the file every time, so that the variables it saved got
-- increasingly longer.  THIS seems to work fine, though.
-------------------------------------------------------------------------------------------
 
--use this by feeding them a setting NAME, as TEXT, and then the setting itself; e.g. Save_Setting("bonus", bonus)
function Save_Setting (setting_name, setting)
 variable_name = string.format (current_character .. "_" .. setting_name)
 if setting then
   SetVariable (variable_name, setting)
   if verbose == "debug" then
       Note("Saved: ", variable_name, " as ", setting)
   end--if
 else --but, it should not be nil.  This is just in case.
   SetVariable (variable_name, "")
   if verbose == "debug" then
       Note("Saved: ", variable_name, " as empty string")
   end --if
 end -- if
end -- Save_Setting
 
--This one however only needs the name (again, as TEXT); it will return the value to set as the setting itself.  E.g. bonus = Retrieve_Setting ("bonus")
function Retrieve_Setting (setting_name)
 variable_name = string.format (current_character .. "_" .. setting_name)
 setting = GetVariable(variable_name) or ""
 return setting
end -- Retrieve_Setting
 
 
function Save_Saveable_Variables ()
 Save_Setting ("window_position", window_position)
 Save_Setting ("x_position", x_position)
 Save_Setting ("y_position", y_position)
 Save_Setting ("absolute_positioning", absolute_positioning)
 Save_Setting ("window_verbosity", window_verbosity)
 SetVariable ("current_character", current_character)
 if active == "yes" and finish_time ~= "" then
   Save_Setting ("finish_time", finish_time)
   previous_time_left = finish_time - os.time ()
   Save_Setting ("previous_time_left", previous_time_left)
   Save_Setting ("active", "yes")
 else
   Save_Setting ("finish_time", "")
   Save_Setting ("previous_time_left", "")
   Save_Setting ("active", "")
 end -- if
 if bonus then
   Save_Setting ("bonus", bonus)
 end -- if
end -- function Save_Saveable_Variables
 
function OnPluginSaveState ()
   Save_Saveable_Variables ()
end -- function OnPluginSaveState
 
function OnWorldSave ()
   Save_Saveable_Variables ()
end -- function

function Move_Window_Help()
 Note([[The command mtmove sets the window position to one of a few presets.  It needs an argument, e.g. "mtmove 5" or "mtmove 10".  Here are the acceptable arguments and where they put the window:
        4 = top left
        5 = center left-right at top (the default)
        6 = top right
        7 = on right, center top-bottom
        8 = on right, at bottom
        9 = center left-right at bottom
        10 = on left, at bottom
        11 = on left, center top-bottom
        12 = centre all
If none of these are what you want, then use mtposx and mtposy instead--they let you put the window wherever you want within the main MUSHclient window.
   * mtposx <number>     Sets the x-position.  0 is all the way to the left, and the default.  Positive numbers move it rightwards.
   * mtposy <number>     Sets the y-position.  0 is all the way at the top, and the default.  Positive numbers move it downwards.
						   
Using either mtposx or mtposy will override the autoposition.

]])
end -- Move_Window_Help
 
function print_help()
 Note([[Plugin Help:Major Timer
 
This plugin produces a small window showing the time remaining on your faith shield.  This currently only works for Major Shield.  Minor Shield, although it stacks with Major Shield, has a different duration formula.  It does, however, have exactly the same success message, so using Minor Shield will confuse the plugin.  It also doesn't work well for having other people perform Major Shield on you: it will use *your* bonus to determine the duration.
 
It can set itself either from actually performing Major Shield (if you've used mtset to set your bonus), or (roughly) from the output of the "shields" command.
 
It can remember bonus information and time remaining on a shield on a per-character basis.  Therefore, you can log off with a shield running without it counting the time spent logged off.  It should detect which character you're logging in as when you log in or su (although if you have an alias for su that's something other than "su", this part will not work).
 
It can detect ritual bonuses and penalties (for example, being at a high altar) and will adjust your effective bonus--and therefore duration--accordingly.  As the bonuses and penalties for being near or far from a high altar vary somewhat depending on exact distance, it may still be off by a few seconds.
   
The following commands are recognized:
   
   * help majortimer   Displays this help file.
 
   * mtset <number>      Set your fa.ri.de.se bonus, from which durations
                           are derived.
                           Eg. A 300 bonus should be entered as 'mtset 300'.
   * mtadd <number>      Manually add time in minutes to your shield timer.
                           Eg. 'mtadd 2' will add 2 minutes to the timer.
   * mtminus <number>    Manually minus time in minutes from your shield timer.
                           Eg. 'mtminus 2' will take 2 minutes from the timer.
   * mtreset             Sets the timer back to 1 shield's duration in length.
   * mtaddone            Manually adds one performance's worth of shielding,
                           using the most recent value for effective bonus.
   * mtclear             Clears the timer.
   * mtchar <name>       Manually sets the current character and loads any
                           previous settings for that character.
   * mtwhoami [on|off]   Turns the trigger for whoami output on or off.  "On"
                           lets the plugin detect the output of the MUD's
                           "whoami" command, and set your current character
                           accordingly.  "Off" is the default, as the trigger
                           can easily be triggered incorrectly (for example by
                           whoami output in finger info, or by unrelated
                           messages with a similar format).
   * mtskills [on|off]   Turns the trigger for skills output on or off.  When
                           on, it can detect the line for f.r.d.s, but only if
                           the output is in list format.  Off by default.
   * mtverbose [on|off]  Turns verbose mode on or off.  In verbose mode: extra
                           messages are displayed when: you set your bonus (or
                           it's set from skills output), you perform MS with
                           ritual modifiers, and when you set your character
                           (manually or through whoami).  Off by default.
   * mtmove <number>     Sets the window position to one of a few 
                           auto-positions:
                           4 = top left
                           5 = center left-right at top (the default)
                           6 = top right
                           7 = on right, center top-bottom
                           8 = on right, at bottom
                           9 = center left-right at bottom
                           10 = on left, at bottom
                           11 = on left, center top-bottom
                           12 = centre all
						   
						 Typing mtmove without an argument will display a mini helpfile about using mtmove.
						   
   * mtposx <number>     Sets the x-position of the window.  0 is all the way to
                           the left, and the default.
   * mtposy <number>     Sets the y-position of the window.  0 is all the way at
                           the top, and the default.
						   
						 Using either mtposx or mtposy will override the autoposition.
						 
						    
   * mtwintext [verbose|brief]	 
                         This changes the text in the window.
                         Verbose: the default.  
                           "Major shield expires in: 4.2 hours."
                           "Shield due to drop!"
                         Brief:
                           "4.2 hours."
                           "Expiring!"
						   
]])
end
 
]]></script>
</muclient>