view plugins/xxinit2.py @ 75:f518bc709fd1 ornery-dev

Continuing refinement and clean-up. Nearly done.
author sirebral
date Fri, 21 Aug 2009 21:49:42 -0500
parents c54768cffbd4
children
line wrap: on
line source

# Copyright (C) 2000-2001 The OpenRPG Project
#
#        openrpg-dev@lists.sourceforge.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
##############################################################################

from string import find, replace
import wx

import os
from orpg.dirpath import dir_struct
import orpg.orpg_version
import orpg.plugindb
import orpg.pluginhandler
import hashlib
import random

__version__ = "2.2.8"

class v:
    init_list = []

class Plugin(orpg.pluginhandler.PluginHandler):
    # Initialization subroutine.
    #
    # !self : instance of self
    # !openrpg : instance of the the base openrpg control
    def __init__(self, plugindb, parent):
        orpg.pluginhandler.PluginHandler.__init__(self, plugindb, parent)

        self.name = "Initiative Tool " + str(__version__)
        self.author = "Woody, Darloth, updated by mDuo13, Veggiesama, magoo"
        self.help =  "Type /inittool2 to load a help node into your game tree. Check out\n"
        self.help += "the readme, and double-click Commands & Help if you want to see examples."

        self.version = __version__
        self.orpg_min_version="1.7.0"


    def plugin_enabled(self):
        # Setup variables.
        self.loaded = 0     #  1 = config has been loaded on startup
        self.init_debug = 0
        v.init_list = [] # now an array of "Dict" (associative array):
                       #   name : name of the effect of character
                       #   type : 0 = character; 1 = effect
                       #   init : init score
                       #   duration : round remaining to the effect
                       #   hidden : type 0 = shows up in public list only on init
                       #            type 1 = does not print to public remaing rounds of effect
                       #   pass : for SR4
                       #   rank : when multiple character at same init count, the plugin
                       #          use the 'rank' to order them
                       #   tag  : unique tag assigned to each entry.
        self.init_round = 1
        self.init_recording = 1
        self.init_title = ""
        self.init_count = 0
        self.init_pass = 0
        self.init_listslot = 0
        self.init_listtag = ""
        self.init_end = 0
        self.init_system = "D20" # D20 (default), SR4, RQ
        self.sandglass_count = 0 # current count of the number of seconds of a character's turn
                            # Well, more or less true, it is a count of the number of time the function
                            # refresh_counter() has been called, which itself is supposed to be call each seconds.
        self.sandglass_delay = 0 # 0 = off, anything else is the timer delay
        self.sandglass_status = 0# 0 = running, 1 = pause
        self.sandglass_skip_interval = 0 # after a number of interval, do a init_next(). 0 = disable
        self.autosave_delay  = 60 # 0 = off, else, will autosave every 'autosave' seconds - VEG 2.2.8 (set to 60)
        self.autosave_count  = 0 # current count of the number of seconds since last autosave
        self.hide_id       = 0 # 1 = do not show IDs to everyone; 0 = show IDs to everyone
        self.hide_ondeck   = 0 # 1 = do not show "on deck" message after every turn; 0 = show it
        self.hide_justmovement = 0 # [SR4] 1 = do not show "just movement" messages
        self.message_nowup = "NEXT UP FOR THE KILLING" # customizable "now up" message
        self.msghand_timer = 0 # 0-60 = cycles the message handler to all players once every 60 seconds - VEG 2.2.6
        self.msghand_warning = 1 # 1 = manual override, removes the message warning if someone else in room has xxinit2 loaded - VEG 2.2.6
        self.reroll_type = 0 # 0 = reroll every round (normal), 1 = no reroll and keep last round inits - VEG 2.2.7
        self.ip_order = 0 # 0 = 1/2/3/4 (normal), 1 = 3/1/4/2 (Serbitar's house rule), 2 = 4/1/3/2 (TinkerGnome's house rule) - VEG 2.2.7

        # Add commands
        self.plugin_addcommand("/inittool2",            self.CMD_inittool2,             "Loads the help/example node into your game tree")
        self.plugin_addcommand("/inittoggle",           self.CMD_inittoggle,            "Toggles initiative system (D20/SR4/RuneQuest")
        self.plugin_addcommand("/initdebug",            self.CMD_initdebug,             "???")
        self.plugin_addcommand("/initgui",              self.CMD_initgui,               "???")
        #self.plugin_addcommand("/initsaveconfig",       self.CMD_initsaveconfig,        "Saves current configuration") #VEG 2.2.6
        #self.plugin_addcommand("/initloadconfig",       self.CMD_initloadconfig,        "Loads a previous configuration") #VEG 2.2.6
        self.plugin_addcommand("/initdefaultconfig",    self.CMD_initdefaultconfig,     "Loads the default configuration")
        self.plugin_addcommand("/initshowconfig",       self.CMD_initshowconfig,        "Displays current configuration")
        self.plugin_addcommand("/togglehideid",         self.CMD_togglehideid,          "Shows IDs in a public list")
        self.plugin_addcommand("/togglehideondeck",     self.CMD_togglehideondeck,          "Shows 'On Deck' message after every turn")
        self.plugin_addcommand("/togglehidejustmovement", self.CMD_togglehidejustmovement,  "Shows 'Just Movement' messages in SR4")
        self.plugin_addcommand("/init",                 self.CMD_init,                  "Toggles whether to record typed intiativies and textual initiative commands")
        self.plugin_addcommand("/clear",                self.CMD_clear,                 "Clears current initiative list")
        self.plugin_addcommand("/start",                self.CMD_start,                 "Clears current initiative list and prompts players to roll")
        self.plugin_addcommand("/addfxhidden",          self.CMD_addfxhidden,           "Adds a hidden effect to the list")
        self.plugin_addcommand("/addfx",                self.CMD_addfx,                 "Adds an effect to the list")
        self.plugin_addcommand("/addhidden",            self.CMD_addhidden,             "Adds a hidden character to the list")
        self.plugin_addcommand("/add",                  self.CMD_add,                   "Adds a character to the list")
        self.plugin_addcommand("/del",                  self.CMD_del,                   "Deletes a character/effect from the list")
        self.plugin_addcommand("/changepass",           self.CMD_changepass,            "Changes current initiative pass [SR4-only]")
        self.plugin_addcommand("/changedur",            self.CMD_changedur,             "Changes duration of specific effect")
        #self.plugin_addcommand("/changefx",             self.CMD_changefx,              "Changes the initiative count of a specific effect")
        self.plugin_addcommand("/change",               self.CMD_change,                "Changes the initiative count of a specific character")
        self.plugin_addcommand("/togglehidden",         self.CMD_togglehidden,          "Toggles whether to show hidden characters in initiative list or not")
        self.plugin_addcommand("/initsetslot",          self.CMD_initsetslot,           "Sets the initiative turn to the designated list slot")
        self.plugin_addcommand("/rankup",               self.CMD_rankup,                "Alters turn priority of one character/effect in the same initiative count as other characters/effects (higher)")
        self.plugin_addcommand("/rankdown",             self.CMD_rankdown,              "Alters turn priority of one character/effect in the same initiative count as other characters/effects (lower)")
        self.plugin_addcommand("/initdelaynow",         self.CMD_initdelaynow,          "???")
        self.plugin_addcommand("/sandglass",            self.CMD_sandglass,             "Toggles sandglass functionality and sets duration of timer")
        self.plugin_addcommand("/sandglasspause",       self.CMD_sandglasspause,        "Pauses the sandglass")
        self.plugin_addcommand("/sandglassresume",      self.CMD_sandglassresume,       "Resumes the sandglass from pause")
        self.plugin_addcommand("/sandglassforceskip",   self.CMD_sandglassforceskip,    "Forces the turn to skip after a number of sandglass duration intervals")
        self.plugin_addcommand("/list",                 self.CMD_list,                  "Displays a list of characters, effects, and turn order to yourself")
        self.plugin_addcommand("/publiclist",           self.CMD_publiclist,            "Displays a list of characters, effects, and turn order to the room")
        self.plugin_addcommand("/rnd",                  self.CMD_rnd,                   "Displays the current round")
        self.plugin_addcommand("/pass",                 self.CMD_pass,                  "Displays the current pass [SR4-only]")
        self.plugin_addcommand("/go",                   self.CMD_go,                    "Advances the turn order to the next character/effect")
        self.plugin_addcommand("/initsave",             self.CMD_initsave,              "Saves current list of initiatives")
        self.plugin_addcommand("/initload",             self.CMD_initload,              "Loads previous list of initiatives")
        self.plugin_addcommand("/initautosave",         self.CMD_initautosave,          "Toggles autosave")
        self.plugin_addcommand("/initautoload",         self.CMD_initautoload,          "Loads previous autosave of initiative")
        self.plugin_addcommand("/nowupmsg",             self.CMD_nowupmsg,              "Customize your own Now Up Message")
        self.plugin_addcommand("/msghandwarning",       self.CMD_msghandwarning,        "Toggles the message handler warning when multiple xxinit2's are loaded in the room") #VEG 2.2.6
        self.plugin_addcommand("/toggleroundreroll",    self.CMD_toggleroundreroll,     "Toggles method of round-by-round initiative in SR4") #VEG 2.2.7
        self.plugin_addcommand("/toggleiporder",        self.CMD_toggleiporder,         "Toggles order of IP execution in SR4") #VEG 2.2.7

        # Add message handlers
        self.plugin_add_msg_handler("xxinit2_checkifloaded", self.received_checkifloaded) #VEG 2.2.6
        self.plugin_add_msg_handler("xxinit2_returnloaded", self.received_returnloaded) #VEG 2.2.6

        self.init_load(0) #VEG 2.2.8 - auto-loads last autosave at startup (helps if you crash)

    def plugin_disabled(self):
        self.plugin_removecmd("/inittool2")
        self.plugin_removecmd("/inittoggle")
        self.plugin_removecmd("/initdebug")
        self.plugin_removecmd("/initgui")
        #self.plugin_removecmd("/initsaveconfig") #VEG 2.2.6
        #self.plugin_removecmd("/initloadconfig") #VEG 2.2.6
        self.plugin_removecmd("/initdefaultconfig")
        self.plugin_removecmd("/initshowconfig")
        self.plugin_removecmd("/togglehideid")
        self.plugin_removecmd("/togglehideondeck")
        self.plugin_removecmd("/togglehidejustmovement")
        self.plugin_removecmd("/init")
        self.plugin_removecmd("/clear")
        self.plugin_removecmd("/start")
        self.plugin_removecmd("/addfxhidden")
        self.plugin_removecmd("/addfx")
        self.plugin_removecmd("/addhidden")
        self.plugin_removecmd("/add")
        self.plugin_removecmd("/del")
        self.plugin_removecmd("/changepass")
        self.plugin_removecmd("/changedur")
        #self.plugin_removecmd("/changefx")
        self.plugin_removecmd("/change")
        self.plugin_removecmd("/togglehidden")
        self.plugin_removecmd("/initsetslot")
        self.plugin_removecmd("/rankup")
        self.plugin_removecmd("/rankdown")
        self.plugin_removecmd("/initdelaynow")
        self.plugin_removecmd("/sandglass")
        self.plugin_removecmd("/sandglasspause")
        self.plugin_removecmd("/sandglassresume")
        self.plugin_removecmd("/sandglassforceskip")
        self.plugin_removecmd("/list")
        self.plugin_removecmd("/publiclist")
        self.plugin_removecmd("/rnd")
        self.plugin_removecmd("/pass")
        self.plugin_removecmd("/go")
        self.plugin_removecmd("/initsave")
        self.plugin_removecmd("/initload")
        self.plugin_removecmd("/initautosave")
        self.plugin_removecmd("/initautoload")
        self.plugin_removecmd("/nowupmsg")
        self.plugin_removecmd("/msghandwarning")
        self.plugin_removecmd("/toggleroundreroll")
        self.plugin_removecmd("/toggleiporder")

        self.plugin_delete_msg_handler("xxinit2_checkifloaded") #VEG 2.2.6
        self.plugin_delete_msg_handler("xxinit2_returnloaded") #VEG 2.2.6

        self.loaded = 0

        try:
            self.frame.Destroy()
        except:
            pass

    # Just received a query from another player; returning a "yes, I have xxinit2 loaded" message
    def received_checkifloaded(self, playerid, data, xml_dom): #VEG 2.2.6
        #print "receive_checkifloaded called: id=" + str(playerid) + "... data = " + str(data)
        self.plugin_send_msg(playerid, "<xxinit2_returnloaded/>")

    # Just received the "yes, I have xxinit2 loaded" message; printing a warning message
    def received_returnloaded(self, playerid, data, xml_dom): #VEG 2.2.6
        if self.msghand_warning:
            self.post_syserror("Warning: xxinit2 plugin detected on "
                               + str(self.session.get_player_by_player_id(playerid)[0]) + "'s system. \
                               Having multiple copies of the xxinit2 plugin in the same room is not \
                               advisable, because it may cause problems. It is suggested that only \
                               one person in the room (the GM) have the xxinit2 plugin loaded. Go to \
                               Tools-Plugins and uncheck the Initiative Tool 2.x to unload it. Type \
                               /msghandwarning to simply turn this warning reminder off.")

    def post_msg(self, text, myself):
        #This is called whenever a message from anyone is about to be posted
        #to chat; it doesn't affect the copy of the message that gets sent to others
        #Be careful; system and info messages trigger this too.
        if self.init_recording:
            if text.lower().find("showinits") != -1:
                self.inits_list(1)
                return text
            elif text.lower().find("nextinit") != -1:
                self.init_next()
                return text
            # all collected, certain values only valid in following systems:
            # d20: player, init, effect, duration
            # sr4: player, init, passes
            # VEG 2.2.7 - The lines that "won't work with macros" are unable to function properly because of the double-layers
            #             of <font> tags that accompany macro sends. The string parsing gets screwed up. It's probably fixable
            #             without changing the way macros are handled, but it's beyond my skill level. =( =( =(
            if myself==1:
                if text.find(" effect ") != -1:
                    effect=text[text.rfind("'>")+2:text.find("[")]
                    duration=text[text.rfind("effect ")+7:text.find("</font>")]
                    init=text[text.rfind("=> ")+3:text.rfind(" effect")]
                elif text.find(" init") != -1:
                    player=text[:text.find("[")]
                    passes=text[text.find("init ")+5:text.find("</font>")]
                    init=text[text.rfind("(")+1:text.rfind(")")]
            else:
                if text.find(" effect ") != -1:
                    effect=text[text.find("</b>: ")+6:text.find("[")]
                    duration=text[text.find("effect ")+7:-7]
                    init=text[text.rfind("=> ")+3:text.rfind(" effect")]
                elif text.find(" init") != -1:
                    player=text[text.find("</b>: ")+6:text.find("[")]
                    passes=text[text.find("init ")+5:-7]
                    init=text[text.rfind("(")+1:text.rfind(")")]
            try:
                if self.isD20() or self.isRQ():
                    if text.find(" effect ") != -1:
                        init=int(init)
                        duration=int(duration)
                        self.init_add([init,duration,effect], 1, 0)
                    elif text.find(" init") != -1:
                        init=int(init)
                        self.init_add([init,player], 0, 0)
                elif self.isSR4():
                    if text.find(" init ") != -1:
                        init=int(init)
                        passes=int(passes)
                        self.init_add([init,passes,player], 0, 0)
            except:
                print("Detected some sort of initiative input... failed!")

        return text


    def refresh_counter(self):
        #This is called once per second. That's all you need to know.
        # load system config once at startup
        if not self.loaded:
            self.loaded = 1
            self.config_load()
            self.config_show()
            self.check_version()

        # do sandglass stuff it has to do
        if not self.isEnded():
           self.sandglass()

        # do the autosaving stuff if needed
        self.autosave()

        # VEG 2.2.6
        self.msghand_timer += 1
        if self.msghand_timer >= 60: # cycle playerlist only 1x every 60 sec
            self.msghand_timer = 0
            self.plugin_send_msg("all", "<xxinit2_checkifloaded/>")

    ###############################################################
    ####    Command Functions
    ###############################################################

    def CMD_inittool2(self, cmdargs):
        f = open(dir_struct["plugins"]+ "inittool2.xml","r")
        f2 = open(dir_struct["plugins"]+ "inittool2_player.xml","r")
        self.gametree.insert_xml(f.read())
        self.gametree.insert_xml(f2.read())
        f.close()
        f2.close()
        return 1

    def CMD_inittoggle(self, cmdargs):
        if self.isSR4():
            self.init_system = "RQ"
            self.init_clear()
            self.post_sysmsg("Now using <font color='#0000ff'><i>RuneQuest</font></i> system.", 1)
        elif self.isD20():
            self.init_system = "SR4"
            self.init_clear()
            self.post_sysmsg("Now using <font color='#0000ff'><i>Shadowrun 4th</font></i> system.", 1)
        elif self.isRQ():
            self.init_system = "D20"
            self.init_clear()
            self.post_sysmsg("Now using <font color='#0000ff'><i>d20</font></i> system.", 1)
        self.config_save() # VEG 2.2.6

    def CMD_initdebug(self, cmdargs):
       cmdargs = cmdargs.split(None,-1)
       if cmdargs[0] == "on":
          self.init_debug = 1
          self.post_sysmsg("Init debugging <font color='#0000ff'><i>on</i></font>.",1)
       elif cmdargs[0] == "off":
          self.init_debug = 0
          self.post_sysmsg("Init debugging <font color='#0000ff'><i>off</i></font>.",1)

    #def CMD_initsaveconfig(self, cmdargs):
    #    self.config_save()

    #def CMD_initloadconfig(self, cmdargs):
    #    self.config_load()

    def CMD_initdefaultconfig(self, cmdargs):
        self.config_default()

    def CMD_initshowconfig(self, cmdargs):
        self.config_show()

    def CMD_togglehideid(self, cmdargs):
        self.hide_id = not self.hide_id
        self.post_sysmsg("Hide IDs: " + str(self.hide_id), 1)
        self.config_save() # VEG 2.2.6

    def CMD_togglehideondeck(self, cmdargs):
        self.hide_ondeck = not self.hide_ondeck
        self.post_sysmsg("Hide 'On Deck': " + str(self.hide_ondeck), 1)
        self.config_save() # VEG 2.2.6

    def CMD_togglehidejustmovement(self, cmdargs):
        self.hide_justmovement = not self.hide_justmovement
        self.post_sysmsg("Hide 'Just Movement' [SR4-only]: " + str(self.hide_justmovement), 1)
        self.config_save() # VEG 2.2.6

    def CMD_init(self, cmdargs):
        if self.init_recording:
            self.init_recording=0
            self.post_sysmsg("Init recording <font color='#0000ff'><i>off</i></font>.")
        else:
            self.init_recording=1
            self.post_sysmsg("Init recording <font color='#0000ff'><i>on</i></font>.")
        self.config_save() # VEG 2.2.6

    def CMD_clear(self, cmdargs):
        self.init_clear()
        self.post_sysmsg("Clearing Initiative List !", 1)

    def CMD_start(self, cmdargs):
        self.init_clear()
        self.post_my_msg("<hr><font color='#ff0000'><b>Starting Round</font> <font color='#0000ff'># <i>1</font> [" + self.init_system + "]</b>", 1)
        self.post_my_msg("<font color='#0000ff'><b>Roll New Initiatives</b></font><hr>", 1)

    def CMD_addfxhidden(self, cmdargs):
        cmdargs = cmdargs.split(None,2)
        if self.isD20() or self.isRQ():
            self.init_add(cmdargs, 1, 1)
        else:
            self.post_syserror("Invalid input. Effects and durations do not work with " + str(self.init_system) + " system.")

    def CMD_addfx(self, cmdargs):
        cmdargs = cmdargs.split(None,2)
        if self.isD20() or self.isRQ():
            self.init_add(cmdargs, 1, 0)
        else:
            self.post_syserror("Invalid input. Effects and durations do not work with " + str(self.init_system) + " system.")

    def CMD_addhidden(self, cmdargs):
        if self.isD20() or self.isRQ():
            cmdargs = cmdargs.split(None,1)
        elif self.isSR4():
            cmdargs = cmdargs.split(None,2)
        self.init_add(cmdargs, 0, 1)

    def CMD_add(self, cmdargs):
        if self.isD20() or self.isRQ():
            cmdargs = cmdargs.split(None,1)
        elif self.isSR4():
            cmdargs = cmdargs.split(None,2)
        self.init_add(cmdargs, 0, 0)

    def CMD_del(self, cmdargs):
        id = int(cmdargs)
        self.init_delete(id-1, 1) # subtract 1 because public lists shows with +1

    def CMD_changepass(self, cmdargs):
        cmdargs = cmdargs.split(None,-1)
        if self.isSR4():
            try:
                id       = int(cmdargs[0])-1 # subtract 1 because public lists shows with +1
                new_pass = int(cmdargs[1])
                if new_pass < 1 or new_pass > 4:
                    self.post_syserror("Invalid input [" + str(self.init_system) + "]. Passes must be greater than/equal to 1 and less than/equal to 4.")
                else:
                    v.init_list[id]["passes"] = new_pass
                    self.post_sysmsg("<font color='#0000ff'><i>" + str(v.init_list[id]["name"]) + "</font></i>'s initiative \
                        pass total has been changed to <font color='#0000ff'><i>" + str(new_pass) + "</font></i> passes !", 1)
            except:
                self.post_syserror("Invalid input [" + str(self.init_system) + "]. That command only works on passes. Correct command is /changepass init_# new_pass_#")

        else:
            self.post_syserror("Invalid input. There are no initiative passes in the " + str(self.init_system) + " system. Try SR4 !")

    def CMD_changedur(self, cmdargs):
        cmdargs = cmdargs.split(None,-1)
        if self.isD20() or self.isRQ():
            try:
                id           = int(cmdargs[0])-1 # subtract 1 because public lists shows with +1
                new_duration = int(cmdargs[1])
                hidden       = v.init_list[id]["hidden"]

                if self.init_debug == 1:
                   print "id : " + str(id)
                   print "new_dur : " + str(new_duration)
                   print "hidden : " + str(hidden)

                if new_duration == 0:
                    self.post_syserror("Invalid input [" + str(self.init_system) + "]. Set duration to '1' if you want the effect to end next time it comes up. \
                        Otherwise, delete the effect with the /del command.", not hidden)
                    return
                elif new_duration < 0:
                    self.post_syserror("Invalid input [" + str(self.init_system) + "]. New duration must be greater than or equal to 1.", not hidden)
                    return

                if self.isEffect(id):
                    v.init_list[id]["duration"] = new_duration
                    self.post_sysmsg("<font color='#0000ff'><i>" + str(v.init_list[id]["name"]) + "</font></i> has been changed \
                        to last <font color='#0000ff'><i>" + str(new_duration) + "</font></i> round(s) !", not hidden)
                else:
                    self.post_syserror("Invalid input [" + str(self.init_system) + "]. That command only works on effects. Correct command is /changedur init_# new_duration_#")

            except Exception, e:
                #print str(e)
                self.post_syserror("Invalid input [" + str(self.init_system) + "]. That command only works on effects. Correct command is /changedur init_# new_duration_#")

        else:
            self.post_syserror("Invalid input. Effects and durations do not work with " + str(self.init_system) + ".")

    #def CMD_changefx(self, cmdargs):
    #    cmdargs = cmdargs.split(None,-1)
    #    if self.isD20() or self.isRQ():
    #        self.init_change(cmdargs,1)
    #    else:
    #        self.post_syserror(">Invalid input. Effects and durations do not work with " + str(self.init_system) + ".")

    #def CMD_change(self, cmdargs):
    #    cmdargs = cmdargs.split(None,-1)
    #    self.init_change(cmdargs,0)

    def CMD_change(self, cmdargs): # VEG 2.2.7
        cmdargs = cmdargs.split(None,-1)
        id = int(cmdargs[0])-1
        new_init = int(cmdargs[1])
        if self.isEffect(id):
            if self.isD20() or self.isRQ():
                self.init_change(id, new_init, 1)
            else:
                self.post_syserror(">Invalid input. Effects and durations do not work with " + str(self.init_system) + ".")
        else:
            self.init_change(id, new_init, 0)

    def CMD_togglehidden(self, cmdargs):
        id = int(cmdargs) - 1
        self.toggle_hidden(id)

    def CMD_initsetslot(self, cmdargs):
        id = int(cmdargs) - 1
        self.init_set_slot(id)

    def CMD_rankup(self, cmdargs):
        cmdargs = cmdargs.split(None,-1)
        id = int(cmdargs[0]) - 1
        try:
            cmdargs[1]
            n = 0 - int(cmdargs[1])
        except:
           # default num of steps to move
           n = -1
        self.init_move(id, n)

    def CMD_rankdown(self, cmdargs):
        cmdargs = cmdargs.split(None,-1)
        id = int(cmdargs[0]) - 1
        try:
            cmdargs[1]
            n = int(cmdargs[1])
        except:
            n = 1
        self.init_move(id, n)

    def CMD_initdelaynow(self, cmdargs):
        id = int(cmdargs) - 1
        self.init_delay_now(id)

    def CMD_sandglass(self, cmdargs):
        try:
            delay = int(cmdargs)
            self.set_sandglass(delay)
        except:
            self.post_syserror("Invalid input. /sandglass (#_of_secs)")

    def CMD_sandglasspause(self, cmdargs):
       self.sandglass_pause()
       self.post_sysmsg("<br><b>Sandglass paused</b>", 1)

    def CMD_sandglassresume(self, cmdargs):
       self.sandglass_resume()
       self.post_sysmsg("<br><b>Sandglass resumed</b>", 1)

    def CMD_sandglassforceskip(self, cmdargs):
        try:
           interval = int(cmdargs)
           self.set_sandglass_force_skip(interval)
        except Exception, e:
          #self.post_my_msg(str(e))
          self.post_syserror("Invalid input. /sandglassforceskip (#_of_intervals)")

    def CMD_list(self, cmdargs):
        self.inits_list(0)

    def CMD_publiclist(self, cmdargs):
        self.inits_list(1)

    def CMD_rnd(self, cmdargs):
        try:
            new_round = int(cmdargs)
            self.init_round = new_round
            self.post_sysmsg("Round number has been changed. It is now Round # <font color='#ff0000'>" + str(new_round) + "</font>",1)
        except:
            self.post_sysmsg("It is currently Round # <font color='#ff0000'>" + str(self.init_round) + "</font>",1)

    def CMD_pass(self, cmdargs):
        try:
            new_pass = int(cmdargs)
            if self.isSR4() and new_pass >= 1 and new_pass <= 4:
                self.init_pass = new_pass
                self.init_count = 0
                self.init_listslot = 0
                self.post_sysmsg("Initiative Pass has been changed. It is currently Pass # <font color='#ff0000'>" + str(self.init_pass) + "</font>",1)
            elif not self.isSR4():
                self.post_syserror("Invalid input. Initiative system must be set to SR4 for passes to function.")
            else:
                self.post_syserror("Invalid input. Passes must be greater than/equal to 1 and less than/equal to 4.")
        except:
            if self.isSR4():
                self.post_sysmsg("It is currently Pass # <font color='#ff0000'>" + str(self.init_pass) + "</font>",1)
            else:
                self.post_syserror("Invalid input. Initiative system must be set to SR4 for passes to function.")

    def CMD_go(self, cmdargs):
        self.init_next()

    def CMD_initsave(self, cmdargs):
        cmdargs = cmdargs.split(None,1)
        slot = int(cmdargs[0])
        try:
            self.init_title = cmdargs[1]
        except:
            self.init_title = "untitled"

        if slot < 1 or slot > 5:
            self.post_syserror("Invalid input. Slot # must be between 1 and 5.")
        else:
            self.init_save(slot, 1)

    def CMD_initload(self, cmdargs):
        try:
            slot = int(cmdargs)
        except:
            slot = 1

        if slot < 1 or slot > 5:
            self.post_syserror("Invalid input. Slot # must be between 1 and 5.")
        else:
            self.init_load(slot)

    def CMD_initautosave(self, cmdargs):
        delay = int(cmdargs)
        self.set_autosave(delay)

    def CMD_initautoload(self, cmdargs):
        self.init_load(0)

    def CMD_nowupmsg(self, cmdargs):
        self.message_nowup = str(cmdargs)
        self.post_my_msg("<b>Now Up Message: </b>" + self.message_nowup)
        self.config_save() # VEG 2.2.6

    def CMD_msghandwarning(self, cmdargs): # VEG 2.2.6
        if self.msghand_warning:
            self.msghand_warning=0
            self.post_sysmsg("Message handler warning <font color='#0000ff'><i>off</i></font>.")
        else:
            self.msghand_warning=1
            self.post_sysmsg("Message handler warning <font color='#0000ff'><i>on</i></font>.")
        self.config_save()

    def CMD_toggleroundreroll(self, cmdargs): # VEG 2.2.7
        if self.reroll_type:
            self.reroll_type=0
            self.post_sysmsg("Reroll toggle set to <font color='#0000ff'><i>Reroll Every Round (default)</i></font>.")
        else:
            self.reroll_type=1
            self.post_sysmsg("Reroll toggle set to <font color='#0000ff'><i>Keep Same Inits Every Round (house rule)</i></font>.")
        self.config_save()

    def CMD_toggleiporder(self, cmdargs): # VEG 2.2.7
        if self.ip_order == 0: # changes IP order to 3/1/4/2 (Serbitar's house rule)
            self.ip_order=1
            self.post_sysmsg("IP order set to <font color='#0000ff'><i>3/1/4/2 (Serbitar's)</i></font>.")
        elif self.ip_order == 1: # changes IP order to 4/1/3/2 (TinkerGnome's house rule)
            self.ip_order=2
            self.post_sysmsg("IP order set to <font color='#0000ff'><i>4/1/3/2 (TinkerGnome's)</i></font>.")
        elif self.ip_order == 2: # changes IP order to 1/2/3/4
            self.ip_order=0
            self.post_sysmsg("IP order set to <font color='#0000ff'><i>1/2/3/4 (normal)</i></font>.")
        self.config_save()

    ###############################################################
    ####    Base Functions
    ###############################################################

    def init_clear(self):
        v.init_list = []
        self.init_round = 1
        self.init_count = 0
        self.init_pass = 0
        self.init_listslot = 0
        self.init_listtag = ""
        self.init_end = 0
        self.sandglass_count = 0
        self.sandglass_status = 0


    def init_add(self, text, effect, hidden):
        if (effect==1) and (self.isD20() or self.isRQ()): #d20 effects
            try:
                if len(text) == 3:
                    h = {}
                    count = int(text[0]) + 1 # effects only decrement (and end)
                                             # before the count in which they
                                             # were added on, so add 1
                    h["type"]     = 1 # effect
                    h["init"]     = count
                    h["name"]     = "*" + str(text[2])
                    h["duration"] = int(text[1])
                    h["rank"]     = self.get_next_rank(h["init"])
                    h["hidden"]   = hidden
                    h["tag"]      = self.get_unique_tag(h["name"])

                    if hidden == 1:
                       duration_str = "?"
                    else:
                       duration_str = str(h["duration"])

                    v.init_list += [h]

                    self.post_sysmsg("<i><font color='#0000ff'>" + h["name"] + "</font></i> effect added to list at init count <i><font color='#0000ff'>" + str(h["init"]) + "</font></i>, lasting <font color='#0000ff'><i>" + duration_str + "</i></font> round(s) !", 1)
                    self.init_sort()
            except:
                self.post_syserror("Invalid input [" + str(self.init_system) + "]. Correct command is: /addfx init_# duration_# description")

        elif self.isD20(): #d20 characters
            try:
                if len(text) == 2:
                    h = {}
                    h["type"]     = 0 # character
                    h["init"]     = int(text[0])
                    h["name"]     = str(text[1])
                    h["rank"]     = self.get_next_rank(h["init"])
                    h["hidden"]   = hidden
                    h["tag"]      = self.get_unique_tag(h["name"])

                    v.init_list += [h]

                    self.post_sysmsg("<font color='#0000ff'><i>" + h["name"] + "</font></i> added to list at init count <i><font color='#0000ff'>" + str(h["init"]) + "</font></i> !", not hidden)
                    self.init_sort()
            except:
                self.post_syserror("Invalid input [" + str(self.init_system) + "]. Correct command is: /add init_# description", not hidden)

        elif self.isSR4(): #SR4 characters
            try:
                if len(text) == 3:
                    h = {}
                    h["type"]     = 0 # character
                    h["init"]     = int(text[0])
                    h["name"]     = str(text[2])
                    h["passes"]   = int(text[1])
                    h["rank"]     = self.get_next_rank(h["init"])
                    h["hidden"]   = hidden
                    h["tag"]      = self.get_unique_tag(h["name"])

                    v.init_list+=[h]

                    self.post_sysmsg("<font color='#0000ff'><i>" + h["name"] + "</font></i> added to list at init count \
                        <font color='#0000ff'><i>" + str(h["init"]) + "</font></i> with <font color='#0000ff'><i>" + str(h["passes"])+ "</font></i> \
                        passes !", not hidden)

                    self.init_sort()

            except:
                self.post_syserror("Invalid input [" + str(self.init_system) + "]. Correct command is: /add init_# passes_# description", not hidden)

        elif self.isRQ(): #RQ characters
            try:
                if len(text) == 2:
                    h = {}
                    h["type"]     = 0 # character
                    h["init"]     = int(text[0])
                    h["name"]     = str(text[1])
                    h["rank"]     = self.get_next_rank(h["init"])
                    h["hidden"]   = hidden
                    h["tag"]      = self.get_unique_tag(h["name"])

                    v.init_list += [h]

                    self.post_sysmsg("<font color='#0000ff'><i>" + h["name"] + "</font></i> added to list at Strike Rank \
                        <font color='#0000ff'><i>" + str(h["init"]) + "</font></i> !", not hidden)

                    self.init_sort()
            except:
                self.post_syserror("Invalid input [" + str(self.init_system) + "]. Correct command is: /add SR description", not hidden)


    def init_delete(self, id, public):
        try:
            name = v.init_list[id]["name"]
            is_last = 0
            is_self = 0
            is_effect = 0
            if public == 1:
                self.post_sysmsg("<font color='#0000ff'><i>" + name + "</font></i> has been removed from the initiative list !", not v.init_list[id]["hidden"])
            if self.isLast(id):
                is_last = 1
            if self.init_listslot == id:
                is_self = 1
            if self.isEffect(id):
                is_effect = 1

            del v.init_list[id]

            if is_effect:
                self.init_listslot -= 1
            elif is_last and is_self:
                self.init_next()
            elif is_self == 1:
                self.init_listslot -= 1
                self.init_next()
            self.init_sort()
        except:
            if public == 1:
                self.post_syserror("Invalid input [" + str(self.init_system) + "]. Correct command is: /del list_number")


    def init_change(self, id, new_init, effect):
        try:
            proceed = 0
            old_name = v.init_list[id]["name"]

            if v.init_list[id]["init"] == new_init:
               self.post_syserror("Invalid input [" + str(self.init_system) + "]. Already at init count <font color='#0000ff'>" + str(new_init) + "</font>. Try /rankup or /rankdown instead.")
               return

            if effect == 1 and (self.isD20() or self.isRQ()): #d20 and RQ effects
                if self.isEffect(id):
                    proceed  = 1
                    self.post_sysmsg("<font color='#0000ff'><i>" + old_name + "</font></i> has had its init count changed \
                        to <font color='#0000ff'><i>" + str(new_init) + "</font></i>.", not v.init_list[id]["hidden"])
                else:
                    self.post_syserror("Invalid input [" + str(self.init_system) + "]. Not an effect. Try /change instead.")

            else: #d20, SR4, RQ characters
                if self.isEffect(id):
                    self.post_syserror("Invalid input [" + str(self.init_system) + "]. Not a character. Try /changefx instead.")
                else:
                    proceed  = 1
                    self.post_sysmsg("<font color='#0000ff'><i>" + old_name + "</font></i>'s init count has changed to \
                        <font color='#0000ff'><i>" + str(new_init) + "</font></i>.", not v.init_list[id]["hidden"])
            if proceed==1:

                listslot_as_changed = 0
                # hacks if currently selected listslot /change's, we select the next tag
                if not self.isEnded() and id == self.init_listslot:
                    if not self.isLast(id):
                       self.init_listtag = v.init_list[id + 1]["tag"]
                       listslot_as_changed = 1
                       self.sandglass_reset()
                    else:
                       self.init_listtag = v.init_list[self.getLast()]["tag"]
                       self.init_next()

                v.init_list[id]["init"] = new_init

                # set the rank to the lowest rank available...
                v.init_list[id]["rank"] = self.get_next_rank(v.init_list[id]["init"])

                self.init_sort()

                # tell if listslot as changed
                if listslot_as_changed == 1:
                   if self.init_count >= v.init_list[id]['init']:
                       self.init_listslot -= 1
                       self.init_count = v.init_list[self.init_listslot]['init']
                   else:
                       self.init_listslot -= 1
                       self.init_next()
                   #self.post_now_up(self.init_listslot, self.message_nowup + ": ")
                   #self.init_next()

        except:
            if effect == 1:
                self.post_syserror("Invalid input [" + str(self.init_system) + "]. Correct command is: /changefx list_# new_init_# (example: /change 1 4)")
            else:
                self.post_syserror("Invalid input [" + str(self.init_system) + "]. Correct command \
                    is: /change list_# new_init_# (example: /change 1 4)")


    def init_sort(self):
        if self.isD20():
            self.sort_high()
        elif self.isSR4():
            self.sort_high()
        elif self.isRQ():
            self.sort_low()
        else:
            self.sort_high()


    def sort_low(self):
        v.init_list.sort(self.sort_low_initlist)
        # this part readjusts the listslot when other objects are
        # changed, added, moved or deleted from v.init_list
        #count = self.init_count
        if self.isEnded():
           return
        n = 0
        last_tag = self.init_listtag
        last_id = self.init_listslot
        found = 0

        for m in v.init_list:
            id = v.init_list.index(m) # first listslot's id

            if v.init_list[id]["tag"] == last_tag:
               self.init_listslot = id
               found = 1
               break

        # if can't find, it got /del'd
        if found == 0 and self.init_listtag != "":
            # next slot should've been bumped up, we re selecting it
            try:
               self.init_listtag = v.init_list[self.init_listslot]["tag"]
            except:
               #we del'd the last one... do nuttin
               pass


    def sort_high(self):
        v.init_list.sort(self.sort_high_initlist)
        v.init_list.reverse()
        # this part readjusts the listslot when other objects are
        # changed, added, moved or deleted from v.init_list
        #count = self.init_count

        if self.isEnded():
           return
        n = 0
        last_tag = self.init_listtag
        last_id = self.init_listslot
        found = 0
        for m in v.init_list:
            id = v.init_list.index(m) # first listslot's id
            #print "sid : " + str(id)
            if v.init_list[id]["tag"] == last_tag:
               self.init_listslot = id
               found = 1
               break
        # if can't find, it got /del'd
        if found == 0 and self.init_listtag != "":
            # next slot should've been bumped up, we re selecting it
            try:
               self.init_listtag = v.init_list[self.init_listslot]["tag"]
            except:
               #we del'd the last one... do nuttin
               pass


    def sort_low_initlist(self, x, y):
       if x['init'] < y['init']:
          return -1
       elif x['init'] > y['init']:
          return 1
       elif x['init'] == y['init']:
          # if same init, we sort by rank
          if x['rank'] > y['rank']:
             return -1
          elif x['rank'] < y['rank']:
             return 1
          else:
                return 0
       else: # shouldnt enter here
          return 0


    def sort_high_initlist(self, x, y):
        if x['init'] < y['init']:
            return -1
        elif x['init'] > y['init']:
            return 1
        elif x['init'] == y['init']:
            # if same init, we sort by rank
            if x['rank'] < y['rank']:
                return -1
            elif x['rank'] > y['rank']:
                return 1
            else:
                return 0
        else: # shouldnt enter here
            return 0


    def init_next(self):
       if self.isD20():
          self.init_next_D20()
       elif self.isSR4():
          self.init_next_SR4()
       elif self.isRQ():
          self.init_next_RQ()


    def init_next_D20(self):

        if self.isEnded():
            self.init_listslot = -1  # offsets +1 coming later, starts 0
            self.init_listtag = ""
            self.init_end = 1

        if not self.isEnded():
            try:
                self.init_listslot += 1 #toldya
                id   = self.init_listslot
                init = str(v.init_list[id]["init"])
                name = str(v.init_list[id]["name"])
                self.init_listtag = v.init_list[id]["tag"]
                self.init_count = v.init_list[id]["init"]

                if self.isEffect(id):
                    v.init_list[id]["duration"] -= 1     #subtract 1 rnd from duration
                    if self.getRoundsLeftInEffect(id) == 0:
                        self.post_effect(id, "EFFECT ENDING:")
                        self.init_delete(id, 0)

                        #nextslot = id #this effect got deleted, so id = former id+1

                        # hack if two consecutive effects and this one has ended
                        #if self.isEffect(nextslot):
                        #    print "nextslot IS an effect"
                        #    self.init_listslot -= 1 # zalarian
                        #    id = self.init_listslot # zalarian
                        #else:
                        #    print "nextslot IS NOT an effect"
                        #    v.init_list[id]["hidden"] = 0
                        #    self.post_now_up(id, self.message_nowup + ": ")

                        # reset the sandglass for this turn
                        self.sandglass_reset()
                    else:
                        if v.init_list[id]["hidden"] == 1:
                           rounds_left = "?"
                        else:
                           rounds_left = self.getRoundsLeftInEffect(id)

                        if self.hide_id == 0:
                           str_id = str(id+1) + ":"
                        else:
                           str_id = ""

                        self.post_effect(id, "EFFECT:")

                        nextslot = id + 1

                        if self.isEffect(nextslot):
                            self.init_next_D20()
                else:   #normal character, not effect
                    v.init_list[id]["hidden"] = 0

                    self.post_now_up(id, self.message_nowup + ": ")

                    # reset the sandglass for this turn
                    self.sandglass_reset()

            except Exception, e:

                self.init_end = 0
                self.init_round += 1
                self.init_count = 0
                self.post_sysmsg("<hr><font color='#ff0000'>End of Round<br>Starting Round <font color='#0000ff'><i># " + str(self.init_round) + "</font></i></font><hr>",1)


    def init_next_SR4(self, do_not_advance_past_init=0):
        if self.isEnded():
            self.init_listslot = -1  # offsets +1 coming later, starts 0
            self.init_listtag = ""
            self.init_end = 1

        if not self.isEnded():
            if self.init_pass == 0:
                self.init_pass +=1
            if self.init_pass > 4:
                self.init_end = 0
                self.init_round += 1
                self.init_pass = 0
                msg = "<hr><font color='#ff0000'>End of Round<br>Starting Round <font color='#0000ff'><i># " + str(self.init_round) + "</font></i></font><hr>"

                if self.reroll_type == 0: # Reroll Every Round (default) - VEG 2.2.7
                    v.init_list = []
                    msg += "<font color='#0000ff'>Roll New Initiatives</font><hr>"
                else: # Keep Same Inits Every Round - VEG 2.2.7
                    pass
                self.post_sysmsg(msg,1)

            else:
                try:
                    valid_char = 0
                    just_movement = 0
                    while valid_char != 1:
                        self.init_listslot += 1 #toldya
                        id   = self.init_listslot
                        passes = v.init_list[id]["passes"] #exception raised here if noone left

                        if self.ip_order == 0: # ip order: 1/2/3/4 (normal) - VEG 2.2.7
                            if passes >= self.init_pass:
                                valid_char = 1
                            elif self.hide_justmovement != 1: # not enough passes, but still gain movement
                                just_movement = 1
                                valid_char = 1

                        else: # ip order: 3/1/4/2 (Serbitar's house rule) or 4/1/3/2 (TinkerGnome's house rule) - VEG 2.2.7
                            if self.init_pass == 1: # first pass: only people with 3+ (S) or 4+ (TG) IPs go
                                if self.ip_order == 1 and passes >= 3: # ip order: 3/1/4/2
                                    valid_char = 1
                                elif self.ip_order == 2 and passes >= 4: # ip order: 4/1/3/2
                                    valid_char = 1
                                elif self.hide_justmovement != 1:
                                    just_movement = 1
                                    valid_char = 1
                            elif self.init_pass == 2: # second pass: only people with 1+ IPs go
                                if passes >= 1:
                                    valid_char = 1
                                elif self.hide_justmovement != 1:
                                    just_movement = 1
                                    valid_char = 1
                            elif self.init_pass == 3: # third pass: only people with 4+ (S) or 3+ (TG) IPs go
                                if self.ip_order == 1 and passes >= 4: # ip order: 4/1/4/2
                                    valid_char = 1
                                elif self.ip_order == 2 and passes >= 3: # ip order: 3/1/3/2
                                    valid_char = 1
                                elif self.hide_justmovement != 1:
                                    just_movement = 1
                                    valid_char = 1
                            elif self.init_pass == 4: # fourth pass: only people with 2+ IPs go
                                if passes >= 2:
                                    valid_char = 1
                                elif self.hide_justmovement != 1:
                                    just_movement = 1
                                    valid_char = 1

                    init = str(v.init_list[id]["init"])
                    name = str(v.init_list[id]["name"])
                    self.init_listtag = v.init_list[id]["tag"]
                    self.init_count = v.init_list[id]["init"]
                    v.init_list[id]["hidden"] = 0  # no more hidden

                    if just_movement == 0:
                        self.post_now_up(id, self.message_nowup + ": ")
                    else:
                        self.post_movement(id, " JUST MOVEMENT:")

                    # reset the sandglass for this turn
                    self.sandglass_reset()

                    if just_movement==1 and self.isMovement(id + 1) and do_not_advance_past_init==0:
                        self.init_next_SR4() # for convenience sake, characters who only have movement
                                        #     will be grouped together.
                                        # do_not_advance_past_init is a hackfix to avoid some
                                        #     problems found with this trick
                    elif self.sharesInit(id + 1):  # rules say characters who share an init take
                        self.init_next_SR4(1)      # actions simultaneously (for the most part)

                except:
                    old_pass = self.init_pass
                    self.init_pass += 1
                    self.init_count = 0
                    self.init_end = 0
                    if self.init_pass != 5:
                        self.post_sysmsg("<hr><font color='#ff0000'>End of Pass <font color='#0000ff'><i>#"\
                            + str(old_pass) + "</font></i><br>Starting New Pass <font color='#0000ff'><i># " \
                            + str(self.init_pass) + "</font></i></font><hr>", 1)
                    else:
                        self.init_next_SR4()


    def init_next_RQ(self):
        if self.isEnded():
            self.init_listslot = -1  # offsets +1 coming later, starts 0
            self.init_listtag = ""
            self.init_end = 1

        if not self.isEnded():
            try:
                self.init_listslot += 1 #toldya
                id   = self.init_listslot
                init = str(v.init_list[id]["init"])
                name = str(v.init_list[id]["name"])
                self.init_listtag = v.init_list[id]["tag"]
                self.init_count = v.init_list[id]["init"]

                if self.isEffect(id):
                    v.init_list[id]["duration"] -= 1     #subtract 1 rnd from duration
                    if self.getRoundsLeftInEffect(id) == 0:
                        self.post_effect(id, "EFFECT ENDING:")
                        self.init_delete(id, 0)

                        # hack if two consecutive effects and this one has ended
                        #if self.isEffect(nextslot):
                        #   self.init_listslot -= 1 # zalarian
                        #   id = self.init_listslot # zalarian
                        #else:
                        #   v.init_list[id]["hidden"] = 0

                        #   self.post_now_up(id, self.message_nowup + ": ")

                        # reset the sandglass for this turn
                        self.sandglass_reset()
                    else:
                        if v.init_list[id]["hidden"] == 1:
                           rounds_left = "?"
                        else:
                           rounds_left = self.getRoundsLeftInEffect(id)


                        if self.hide_id == 0:
                           str_id = str(id+1) + ":"
                        else:
                           str_id = ""

                        self.post_effect(id, "EFFECT:")

                        nextslot = id + 1

                    if self.isEffect(nextslot):
                       self.init_next_RQ()
                else:   #normal character, not effect
                    v.init_list[id]["hidden"] = 0

                    self.post_now_up(id, self.message_nowup + ": ")

                    # reset the sandglass for this turn
                    self.sandglass_reset()

            except Exception, e:
                self.init_end = 0
                self.init_round += 1
                self.init_count = 0
                self.post_sysmsg("<hr><font color='#ff0000'>End of Round<br>Starting Round <font color='#0000ff'><i># " + str(self.init_round) + "</font></i></font><hr>",1)

    def init_set_slot(self, id):
       try:
          if self.isEnded():
             raise
          else:
             if id == self.init_listslot:
                self.post_syserror("Slot <font color='#0000ff'>#" + str(id + 1) + "</font> already selected.")
             else:
                self.init_listtag = v.init_list[id]["tag"]
                self.init_listslot = id
                self.sandglass_reset()
                self.post_sysmsg("<font color='#0000ff'><i>" + v.init_list[id]["name"] + "</font></i> selected !")

       except:
          self.post_syserror("Can't select slot <font color='#0000ff'>#" + str(id) + "</font>. Round must be started.")

    def init_move(self, id, n):
       try:
          # how many inits we have so far
          list_size = len(v.init_list)

          if self.init_debug == 1:
             print "list_size : " + str(list_size)
             print "id : " + str(id)
             print "pre n : " + str(n)

          if id >= list_size:
             self.post_syserror("Can't move an unknown ID (<font color='#0000ff'>" + str(id+1) + "</font>) !")
             return

          # are we going UP or DOWN ?
          if n > 0:
             up = 0
             step = 1
             # can't move down last object
             if self.isLast(id):
                self.post_syserror("Can't move <font color='#0000ff'>" + v.init_list[id]["name"] + "</font> this way! (Already at the last init)")
                return

             max_move = list_size - id # maximum num of moves we could possibly do
             # cant move more than max_move position!!
             if n > max_move:
                n = max_move
          elif n < 0:
             up = 1
             step = -1
             #can't move up first object
             if self.isFirst(id):
                self.post_syserror("Can't move <font color='#0000ff'>" + v.init_list[id]["name"] + "</font> this way ! (Already at the first init)")
                return

             max_move = id # maximum num of move we could possibly do
             # cant move more than max_move position!!
             if n > max_move:
                n = -max_move
          else:
             # shouldnt be here
             self.post_syserror("Can't move <font color='#0000ff'>" + v.init_list[id]["name"] + "</font> !")
             return 1

          if self.init_debug == 1:
             print "n : " + str(n)
             print "step : " + str(step)
             print "max_move : " + str(max_move)
             print "up : " + str(up)
             print "len : " + str(len(range(id , (id + n + step), step)))

          # suppose we're not moving, id_n will be the target id we're moving to
          id_n = id

          # find the last init we can go with 'n' steps and staying on the init count of object 'id'
          for k in range(id + step , (id + n + step), step):
             #print "k... " + str(k)
             if v.init_list[k]["init"] != v.init_list[id]["init"]:
                # cant more further than k + step.. go away
                break
             else:
                id_n = k

          # we can't move if id_n is the same as id. probably only alone on this init count
          if id_n == id:
             self.post_syserror("Can't move <font color='#0000ff'>" + v.init_list[id]["name"] + "</font> ! (you may only rankup/rankdown within the same init value)</i>")
             return

          if self.init_debug == 1:
             print "id_n : " + str(id_n)

          # now we have to set the rank correctly
          if up and self.isFirst(id_n):
             # going up and we're moving in first position of all inits
             v.init_list[id]["rank"] = v.init_list[id_n]["rank"] + 1
          elif not up and self.isLast(id_n):
             # going down and we're moving in last position of all inits
             v.init_list[id]["rank"] = v.init_list[id_n]["rank"] - 1
          elif v.init_list[id_n + step]["init"] != v.init_list[id]["init"]:
             # moving in first (up) or last (down) position of the current init count
             v.init_list[id]["rank"] = v.init_list[id_n]["rank"] - step
          elif up and v.init_list[id_n + step]["init"] == v.init_list[id]["init"]:
             # moving up somewhere between multiple id of the same init count
             v.init_list[id]["rank"] = v.init_list[id_n]["rank"] - (step * (v.init_list[id_n + step]["rank"] - v.init_list[id_n]["rank"]) / 2.0)
          elif not up and v.init_list[id_n + step]["init"] == v.init_list[id]["init"]:
             # moving down somewhere between multiple id of the same init count
             v.init_list[id]["rank"] = v.init_list[id_n]["rank"] + (step * (v.init_list[id_n + step]["rank"] - v.init_list[id_n]["rank"]) / 2.0)

          self.post_sysmsg("<font color='#0000ff'><i>" + v.init_list[id]["name"] + "</font></i> moved !", not v.init_list[id]["hidden"])

          # hack, if we're moving the selected slot or at the selected
          # slot, we're staying at the same position.
          if not self.isEnded():
             if id_n == self.init_listslot:
                # we're moving AT the selected slot
                self.init_listtag = v.init_list[id]["tag"]
                self.sandglass_reset()
                self.post_now_up(id, self.message_nowup + ": ")
                # works fine unless you try to rank by more than one step, so
                # currently this section (or the other) is bugged
             if id == self.init_listslot:
                # we're moving THE selected slot
                if up:
                   self.init_listtag = v.init_list[id-1]["tag"]
                else:
                   self.init_listtag = v.init_list[id+1]["tag"]
                self.post_now_up(id_n, self.message_nowup + ": ")
                # works fine unless you try to rank by more than one step, so
                # currently this section (or the other) is bugged
                self.sandglass_reset()


          # sort that list again
          self.init_sort()

       except:
          self.post_syserror("Can't move <font color='#0000ff'>" + v.init_list[id]["name"] + "</font> ! (May only rankup/rankdown within the same init value)")


    def init_delay_now(self, id):
       try:
          # can't delay if round ended
          if self.isEnded():
             self.post_syserror("Can't delay until the round has started.", 0)
             return

          if self.isD20() or self.isRQ():
             # get current tag
             tag_current = v.init_list[self.init_listslot]["tag"]
             tag_delay = v.init_list[id]["tag"]
             name_delay = v.init_list[id]["name"]

             # can't delay current listslot
             if self.init_listslot == id:
                self.post_syserror("Can't delay current selected slot.", 0)
                return

             # if not on the same init count
             if v.init_list[self.init_listslot]["init"] != v.init_list[id]["init"]:
                # generate array needed by init_change() as first parameter
                tab = [ 0, id + 1, v.init_list[self.init_listslot]["init"]]
                self.init_change(tab, v.init_list[id]["type"])
                self.init_sort()

             #print "name_delay : " + str(name_delay)
             #print "tag_delay : " + str(tag_delay)
             #print "tag_current : " + str(tag_current)
             #print "slot_tag : " + str(v.init_list[self.init_listslot]["tag"])
             #print "listslot : " + str(self.init_listslot)

             # must rankup if needed (init_change() move at the end of a group if at same init count
             if not (v.init_list[self.init_listslot]["tag"] == tag_delay):
                # must find where is tag_delay and move it before tag_current
                for m in v.init_list:
                   if m["tag"] == tag_delay:
                      new_id_delay = v.init_list.index(m)
                      break

                step = (self.init_listslot - new_id_delay)
                self.init_move(new_id_delay, step)
                #self.init_listtag = tag_delay
                self.init_sort()

             self.post_now_up(self.init_listslot, "NOW UP FOR THE KILLING (DELAYED ACTION):")

             self.sandglass_reset()

          else:
             self.post_syserror(">Can't delay in " + str(self.init_system) + ".")

       except Exception, e:
          print str(e)
          self.post_syserror("Invalid input [" + str(self.init_system) + "]. Correct command is: /initdelaynow list_number")

    ###############################################################
    ####    Sandglass Functions
    ###############################################################

    # will send reminder to the chat if needed
    def sandglass(self):
       try:
          if self.sandglass_delay == 0 or self.sandglass_status == 1:
             pass
          # send reminder except for effect
          elif not self.isEnded() and not self.isEffect(self.init_listslot):
             self.sandglass_count += 1
             if self.sandglass_skip_interval and not (self.sandglass_count % (self.sandglass_delay * self.sandglass_skip_interval)):
                uname = v.init_list[self.init_listslot]["name"]
                self.post_sysmsg("TIME'S UP <font color='#0000ff'><i>" + uname + "</font></i>!!! (<i>" + str(self.sandglass_count) + " seconds elapsed</i>)", 1)
                self.init_next()
             elif not (self.sandglass_count % self.sandglass_delay):
                self.post_sysmsg("REMINDER to <font color='#0000ff'><i>" + v.init_list[self.init_listslot]["name"] + "</font></i> : IT'S YOUR TURN! (<i>" + str(self.sandglass_count) + " seconds elapsed</i>)", 1)

       except:
          pass


    def sandglass_reset(self):
       try:
          if self.sandglass_delay > 0:
             self.sandglass_count = 0
             self.sandglass_resume()
       except:
          pass


    # status :  0 = running
    #           1 = pause
    def sandglass_pause(self):
       try:
          if self.sandglass_delay > 0:
             self.sandglass_status = 1
       except:
          pass


    def sandglass_resume(self):
       try:
          if self.sandglass_delay > 0:
             self.sandglass_status = 0
       except:
          pass

    def set_sandglass_force_skip(self,interval):
       self.sandglass_skip_interval = interval
       if interval == 0:
          self.post_sysmsg("Sandglass force turn skip is now off.", 1)
       else:
          self.post_sysmsg("Sandglass force turn skip is now set to " + str(interval) + " interval(s).", 1)
       self.config_save() # VEG 2.2.6

    def set_sandglass(self, delay):
       self.sandglass_delay = delay
       if delay == 0:
          self.post_sysmsg("Sandglass is now off.", 1)
       else:
          self.post_sysmsg("Sandglass is now set to " + str(delay) + " seconds.", 1)
       self.config_save() # VEG 2.2.6

    ###############################################################
    ####    Config/Save/Load Functions
    ###############################################################

    def init_save(self, slot, public):
       try:
          modname = "xxinit2-" + str(slot)

          self.plugindb.SetString(modname, "init_recording",  str(self.init_recording))
          self.plugindb.SetString(modname, "init_round",      str(self.init_round))
          self.plugindb.SetString(modname, "init_title",      str(self.init_title))
          self.plugindb.SetString(modname, "init_count",      str(self.init_count))
          self.plugindb.SetString(modname, "init_pass",       str(self.init_pass))
          self.plugindb.SetString(modname, "init_listslot",   str(self.init_listslot))
          self.plugindb.SetString(modname, "init_listtag",    str(self.init_listtag))
          self.plugindb.SetString(modname, "init_end",        str(self.init_end))
          self.plugindb.SetString(modname, "init_system",     str(self.init_system))
          self.plugindb.SetString(modname, "init_list",       repr(v.init_list))
          self.plugindb.SetString(modname, "sandglass_status",       repr(self.sandglass_status))

          if self.init_title != "":
             title_str = " <i>[" + self.init_title + "]</i>"
          else:
             title_str = ""

          if slot == 0:
             #self.post_sysmsg("Initiative list autosaved successfully.", public)
             # this is annoying, so i disabled the text output
             pass
          else:
             self.post_sysmsg("Initiative list saved successfully on slot #" + str(slot) + title_str + ".", public)

       except Exception, e:
          print "err saving : " + str(e)


    def init_load(self, slot):
       try:
          self.init_clear()

          modname = "xxinit2-" + str(slot)

          self.init_recording  = int(self.plugindb.GetString(modname, "init_recording","0"))
          self.init_round      = int(self.plugindb.GetString(modname, "init_round", "0"))
          self.init_title      = str(self.plugindb.GetString(modname, "init_title", ""))
          self.init_count      = int(self.plugindb.GetString(modname, "init_count", "0"))
          self.init_pass       = int(self.plugindb.GetString(modname, "init_pass", "0"))
          self.init_listslot   = int(self.plugindb.GetString(modname, "init_listslot", "0"))
          self.init_listtag    = str(self.plugindb.GetString(modname, "init_listtag", ""))
          self.init_end        = int(self.plugindb.GetString(modname, "init_end", "0"))
          self.init_system     = str(self.plugindb.GetString(modname, "init_system", ""))
          v.init_list          = eval(self.plugindb.GetString(modname, "init_list", ""))
          self.sandglass_status = eval(self.plugindb.GetString(modname, "sandglass_status", "0"))

          if self.init_title != "":
             title_str = " <i>[" + self.init_title + "]</i>"
          else:
             title_str = ""

          if slot == 0:
             self.post_sysmsg("Last autosaved initiative list loaded successfully.")
          else:
             self.post_sysmsg("Initiative list slot #" + str(slot) + title_str + " loaded successfully.")
          self.config_save() # VEG 2.2.6

       except Exception, e:
          #print "err loading: " + e
          self.post_syserror("Error loading Initiative list.")


    def autosave(self):
       try:
          slot = 0
          if self.autosave_delay != 0:
             self.autosave_count += 1
             if not (self.autosave_count % self.autosave_delay):
                self.autosave_count = 0
                self.init_save(slot, 0)

       except Exception, e:
          print str(e)


    def set_autosave(self, delay):

       self.autosave_delay = delay

       if delay == 0:
          self.post_sysmsg("Autosave is now off.")
       else:
          self.post_sysmsg("Autosave is now done every " + str(delay) + " seconds.")


    def config_save(self):
       try:
          modname = "xxinit2-config"

          # don't want python to convert this to the strings "TRUE" or "FALSE"
          if self.init_recording == 0:
             self.init_recording = 0
          else:
             self.init_recording = 1

          if self.hide_id == 0:
             self.hide_id = 0
          else:
             self.hide_id = 1

          if self.hide_ondeck == 0:
             self.hide_ondeck = 0
          else:
             self.hide_ondeck = 1

          if self.hide_justmovement == 0:
             self.hide_justmovement = 0
          else:
             self.hide_justmovement = 1

          self.plugindb.SetString(modname, "init_recording",  str(self.init_recording))
          self.plugindb.SetString(modname, "init_system",     str(self.init_system))
          self.plugindb.SetString(modname, "sandglass_count", str(self.sandglass_count))
          self.plugindb.SetString(modname, "sandglass_delay", str(self.sandglass_delay))
          self.plugindb.SetString(modname, "sandglass_skip_interval", str(self.sandglass_skip_interval))
          self.plugindb.SetString(modname, "autosave_delay",  str(self.autosave_delay))
          self.plugindb.SetString(modname, "autosave_count",  str(self.autosave_count))
          self.plugindb.SetString(modname, "hide_id",         str(self.hide_id))
          self.plugindb.SetString(modname, "hide_ondeck",     str(self.hide_ondeck))
          self.plugindb.SetString(modname, "hide_justmovement",str(self.hide_justmovement))
          self.plugindb.SetString(modname, "message_nowup",   str(self.message_nowup))
          self.plugindb.SetString(modname, "msghand_timer",   str(self.msghand_timer)) # VEG 2.2.6
          self.plugindb.SetString(modname, "msghand_warning", str(self.msghand_warning)) # VEG 2.2.6
          self.plugindb.SetString(modname, "reroll_type",     str(self.reroll_type)) # VEG 2.2.7
          self.plugindb.SetString(modname, "ip_order",      str(self.ip_order)) # VEG 2.2.7

          #self.post_sysmsg("Configuration saved successfully.",0)
          print "Initiative configuration saved successfully."

       except:
          self.post_syserror("Error saving configuration.",0)


    def config_load(self):

       try:
          modname = "xxinit2-config"

          self.init_recording  = int(self.plugindb.GetString(modname, "init_recording","0"))
          self.init_system     = str(self.plugindb.GetString(modname, "init_system", ""))
          self.sandglass_count = int(self.plugindb.GetString(modname, "sandglass_count", "0"))
          self.sandglass_delay = int(self.plugindb.GetString(modname, "sandglass_delay", "0"))
          self.sandglass_skip_interval = int(self.plugindb.GetString(modname, "sandglass_skip_interval", "0"))
          self.autosave_delay  = int(self.plugindb.GetString(modname, "autosave_delay", "0"))
          self.autosave_count  = int(self.plugindb.GetString(modname, "autosave_count", "0"))
          self.hide_id         = int(self.plugindb.GetString(modname, "hide_id", "0"))
          self.hide_ondeck     = int(self.plugindb.GetString(modname, "hide_ondeck", "0"))
          self.hide_justmovement = int(self.plugindb.GetString(modname, "hide_justmovement", "0"))
          self.message_nowup   = str(self.plugindb.GetString(modname, "message_nowup", ""))
          self.msghand_timer   = int(self.plugindb.GetString(modname, "msghand_timer", "0")) # VEG 2.2.6
          self.msghand_warning = int(self.plugindb.GetString(modname, "msghand_warning", "0")) # VEG 2.2.6
          self.reroll_type     = int(self.plugindb.GetString(modname, "reroll_type", "0")) # VEG 2.2.7
          self.ip_order      = int(self.plugindb.GetString(modname, "ip_order", "0")) # VEG 2.2.7

          # if system blank... no config was loaded
          if self.init_system == "":
             raise

          self.post_sysmsg("Configuration loaded successfully.",0)
          self.config_save() # VEG 2.2.6

       except:
          self.config_default()


    def config_default(self):

          self.init_recording = 1
          self.init_system = "D20"
          self.sandglass_count = 0
          self.sandglass_delay = 0
          self.sandglass_status = 0
          self.sandglass_skip_interval = 0
          self.autosave_delay = 60
          self.autosave_count = 0
          self.hide_id = 0
          self.hide_ondeck = 0
          self.hide_justmovement = 0
          self.message_nowup = "NEXT UP FOR THE KILLING"
          self.msghand_timer = 0
          self.msghand_warning = 1
          self.reroll_type = 0
          self.ip_order = 0

          self.post_sysmsg("Default configuration loaded successfully.",0)
          self.config_save() # VEG 2.2.6

    def config_show(self):

       try:
          self.post_sysmsg("<br><u>Init Tool system config v" + str(self.version) + "</u><br>")

          if self.init_recording == 0:
             buf = "disabled";
          else:
             buf = "enabled";
          self.post_my_msg("<b>Init Recording:</b> " + str(buf))

          self.post_my_msg("<b>System:</b> " + str(self.init_system))

          if self.sandglass_delay == 0:
             buf = "disabled";
          else:
             buf = str(self.sandglass_delay) + " seconds"
             if self.sandglass_status == 1:
                buf += str(" [ paused ]")

          self.post_my_msg("<b>Sandglass:</b> " + str(buf))

          if self.sandglass_skip_interval == 0:
             buf = "disabled";
          else:
             buf = "every " + str(self.sandglass_skip_interval) + " interval(s)."
          self.post_my_msg("<b>Sandglass force skip:</b> " + str(buf))

          if self.autosave_delay == 0:
             buf = "disabled";
          else:
             buf = str(self.autosave_delay) + " seconds"
          self.post_my_msg("<b>Autosave:</b> " + str(buf))

          if self.msghand_warning == 0: # VEG 2.2.6
             buf = "disabled";
          else:
             buf = "enabled";
          self.post_my_msg("<b>Show Message Handler Warning:</b> " + str(buf))

          if self.hide_id == 0:
             buf = "disabled";
          else:
             buf = "enabled";
          self.post_my_msg("<b>Hide IDs:</b> " + str(buf))

          if self.hide_ondeck == 0:
             buf = "disabled";
          else:
             buf = "enabled";
          self.post_my_msg("<b>Hide 'On Deck':</b> " + str(buf))

          if self.hide_justmovement == 0:
             buf = "disabled";
          else:
             buf = "enabled";
          self.post_my_msg("<b>[SR4-only] Hide 'Just Movement':</b> " + str(buf))

          if self.reroll_type == 0: # VEG 2.2.7
             buf = "reroll inits every round (normal)";
          else:
             buf = "keep same inits every round (house rule)";
          self.post_my_msg("<b>[SR4-only] Init Reroll?:</b> " + str(buf))

          if self.ip_order == 0: # VEG 2.2.7
             buf = "1/2/3/4 (normal)";
          elif self.ip_order == 1:
             buf = "3/1/4/2 (Serbitar's house rule)"
          elif self.ip_order == 2:
             buf = "4/1/3/2 (TinkerGnome's house rule)";
          self.post_my_msg("<b>[SR4-only] IP Order:</b> " + str(buf))

          self.post_my_msg("<b>Now Up Message:</b> " + str(self.message_nowup))

       except:
          self.post_syserror("Error reading configuration.",0)

    ###############################################################
    ####    Display Functions
    ###############################################################

    def inits_list(self, public):
       if self.isD20():
          self.inits_list_D20(public)
       elif self.isSR4():
          self.inits_list_SR4(public)
       elif self.isRQ():
          self.inits_list_RQ(public)

    def inits_list_D20(self, public):

        current_count = str(self.init_count)

        if self.sandglass_delay == 0:
           current_sandglass = "off"
        else:
           current_sandglass = str(self.sandglass_delay) + " sec."
           if self.sandglass_status == 1:
              current_sandglass += str(" [ paused ]")

        msg = "<br><br><b>Initiatives (Current Count: " + current_count + "; Sandglass: " + current_sandglass + "):</b><br>"
        for m in v.init_list:

            # dont show public if character (type 0)
            if public and m["type"] == 0 and m["hidden"] == 1:
               continue

            # id in the list appears as 1 to (N+1), in reality it's 0 to N
            id   = v.init_list.index(m)
            idplusone = str(id+1)

            if not self.isEnded() and self.init_listslot == id:
               msg += "<b>"

            # we dont want to show IDs to everyone
            if public and self.hide_id:
               msg += " "
            else:
               msg += " <font color='#ff0000'>" + idplusone + ") :</font>"

            if self.isEffect(id):
                # example
                #   5: (*14) <i>Effect: Tlasco's Bless (4)</i>
                if public and m["hidden"] == 1:
                   duration = "?"
                else:
                   duration = m["duration"]
                msg += " [" + str(m["init"])+"] "
                msg += "<font color='#0000ff'><i>Effect: " + str(m["name"]) + " (" + str(duration) + ")</i>"
            else:
                # example
                #   6: (14) Tlasco
                msg+= " ["+str(m["init"])+"] <font color='#0000ff'>" + str(m["name"])
            if self.init_debug:
               #msg+= " [rank:" + str(m["rank"]) + "; tag: " + str(m["tag"]) + "]</font>"
               msg+= " [rank:" + str(m["rank"]) + "; type: " + str(m["type"]) + "]</font>"
               #msg+= " [rank:" + str(m["rank"]) + "]</font>"
            else:
               msg+= "</font>"

            if not public and m["hidden"] == 1:
               msg += " <i>[H]</i>"

            if not self.isEnded() and self.init_listslot == id:
               msg += "</b>"

            msg += "<br>"
        self.post_my_msg(msg,public)


    def inits_list_SR4(self, public):

        if self.sandglass_delay == 0:
           current_sandglass = "off"
        else:
           current_sandglass = str(self.sandglass_delay) + " sec."
           if self.sandglass_status == 1:
              current_sandglass += str(" [ paused ]")

        if self.ip_order == 0:
            current_ip_order = "1/2/3/4 (normal)"
        elif self.ip_order == 1:
            current_ip_order = "3/1/4/2 (Serbitar's house rule)"
        else:
            current_ip_order = "4/1/3/2 (TinkerGnome's house rule)"
        current_count = str(self.init_count)
        current_pass  = str(self.init_pass)

        msg = "<br><br><b>Initiatives (Current Pass: " + current_pass \
            + "; Current Count: " + current_count + "), Sandglass: " \
            + current_sandglass + ", IP Order: " + current_ip_order \
            + "</b><br>"
        msg += "<table border='1'><tr>&nbsp;<th></th>"

        if self.init_pass==1:
            msg += "<th><b><font color='#ff0000'>Pass 1</font></b></th>"
        else:
            msg += "<th>Pass 1</th>"

        if self.init_pass==2:
            msg += "<th><b><font color='#ff0000'>Pass 2</font></b></th>"
        else:
            msg += "<th>Pass 2</th>"

        if self.init_pass==3:
            msg += "<th><b><font color='#ff0000'>Pass 3</font></b></th>"
        else:
            msg += "<th>Pass 3</th>"

        if self.init_pass==4:
            msg += "<th><b><font color='#ff0000'>Pass 4</font></b></th></tr>"
        else:
            msg += "<th>Pass 4</th></tr>"

        for m in v.init_list:
            # id in the list appears as 1 to (N+1), in reality it's 0 to N
            id   = v.init_list.index(m)
            idplusone = str(id+1)
            # example
            #             | Init Pass 1 | Init Pass 2 | etc...  |          |
            #   ---------------------------
            #   6: Tlasco |     13      |     13      | (blank) |  (blank) |

            # we dont want to show IDs to everyone
            if public and self.hide_id:
               msg += " <tr><th>"
            else:
               msg += " <tr><td><font color='#ff0000'>" + idplusone + ":</font> "

            # bold the current player's name
            if not self.isEnded() and self.init_listslot == id:
               msg += "<b><font color='#0000ff'>" + str(m["name"]) + "</font></b></td>"
            else:
               msg += "<font color='#0000ff'>" + str(m["name"]) + "</font></td>"

            ip = self.ip_order #ip==0 is 1/2/3/4 (normal) - VEG 2.2.7
                               #ip==1 is 3/1/4/2 (Serbitar's house rule)
                               #ip==2 is 4/1/3/2 (TinkerGnome's house rule)
            passes = m["passes"]
            # pass one
            if (ip==0 and passes >= 1) or (ip==1 and passes >= 3) or (ip==2 and passes >= 4):
                msg += "<td align=center>"+str(m["init"]) + "</td>"
            else:
                msg += "<td>&nbsp;</td>"
            # pass two
            if (ip==0 and passes >= 2) or (ip==1 and passes >= 1) or (ip==2 and passes >= 1):
                msg += "<td align=center>"+str(m["init"]) + "</td>"
            else:
                msg += "<td>&nbsp;</td>"
            # pass three
            if (ip==0 and passes >= 3) or (ip==1 and passes >= 4) or (ip==2 and passes >= 3):
                msg += "<td align=center>" + str(m["init"]) + "</td>"
            else:
                msg += "<td>&nbsp;</td>"
            # pass four
            if (ip==0 and passes >= 4) or (ip==1 and passes >= 2) or (ip==2 and passes >= 2):
                msg += "<td align=center>" + str(m["init"]) + "</td>"
            else:
                msg += "<td>&nbsp;</td>"

            msg += "</tr>"

        msg += "</table>"
        self.post_my_msg(msg, public)


    def inits_list_RQ(self, public):
        current_count = str(self.init_count)

        if self.sandglass_delay == 0:
           current_sandglass = "off"
        else:
           current_sandglass = str(self.sandglass_delay) + " sec."
           if self.sandglass_status == 1:
              current_sandglass += str(" [ paused ]")

        msg = "<br><br><b>Initiatives (Current Strike Rank: " + current_count + "; Sandglass: " + current_sandglass + "):</b><br>"
        for m in v.init_list:

            # dont show public if character (type 0)
            if public and m["type"] == 0 and m["hidden"] == 1:
               continue

            # id in the list appears as 1 to (N+1), in reality it's 0 to N
            id   = v.init_list.index(m)
            idplusone = str(id+1)

            if not self.isEnded() and self.init_listslot == id:
               msg += "<b>"

            # we dont want to show IDs to everyone
            if public and self.hide_id:
               msg += " "
            else:
               msg += " <font color='#ff0000'>" + idplusone + ") :</font>"

            if self.isEffect(id):
                # example
                #   5: (*14) <i>Effect: Tlasco's Bless (4)</i>
                if public and m["hidden"] == 1:
                   duration = "?"
                else:
                   duration = m["duration"]
                msg += " [" + str(m["init"])+"] "
                msg += "<font color='#0000ff'><i>Effect: " + str(m["name"]) + " (" + str(duration) + ")</i>"
            else:
                # example
                #   6: (14) Tlasco
                msg+= " ["+str(m["init"])+"]  <font color='#0000ff'>" + str(m["name"])
            if self.init_debug:
               #msg+= " [rank:" + str(m["rank"]) + "; tag: " + str(m["tag"]) + "]</font>"
               msg+= " [rank:" + str(m["rank"]) + "; type: " + str(m["type"]) + "]</font>"
               #msg+= " [rank:" + str(m["rank"]) + "]</font>"
            else:
               msg+= "</font>"

            if not public and m["hidden"] == 1:
               msg += " <i>[H]</i>"

            if not self.isEnded() and self.init_listslot == id:
               msg += "</b>"

            msg += "<br>"
        self.post_my_msg(msg, public)


    def post_now_up(self, id, text):

       #if v.init_list[id]["hidden"] == 0:
       if self.hide_id == 0:
          id_str = "<font color='#000000'><b>" + str(id + 1) + ")</b></font>"
       else:
          id_str = ""

       try:
          id_next = self.getNext(id)
          str_on_deck = ""
          if id_next != -1:
             if self.hide_ondeck != 1:
                 str_on_deck = "<br><i><font color='#000000'>(on deck: [" + str(v.init_list[id_next]["init"]) + "] " + str(v.init_list[id_next]["name"]) + ")</font></i>"

       except Exception, e:
          print str(e)

       self.post_my_msg("<table border=1 width=100% align='center'>\
                     <tr>\
                      <td>" + str(id_str) + " <font color='#ff0000'><b><u>" + str(text)\
                            + "</u></b></font><font color='#0000ff'><b>"\
                            + " <font color='#000000'></b>[" + str(v.init_list[id]["init"]) + "]<b></font> "\
                             + str(v.init_list[id]["name"]) + "</b></font> " + str_on_deck + "\
                      </td>\
                     </tr>\
                    </table>", 1)

    def post_movement(self, id, text):

       #if v.init_list[id]["hidden"] == 0:
       if self.hide_id == 0:
          id_str = "<font color='#000000'><b>" + str(id + 1) + ")</b></font>"
       else:
          id_str = ""

       self.post_my_msg("<table border=1 width=100% align='center'>\
                     <tr>\
                      <td>" + str(id_str) + " <font color='#0000ff'><b><i>" + str(text)\
                            + "</i></b></font><font color='#0000ff'><b>"\
                            + " <font color='#000000'></b>[" + str(v.init_list[id]["init"]) + "]<b></font> "\
                             + str(v.init_list[id]["name"]) + "</b></font> \
                      </td>\
                     </tr>\
                    </table>", 1)

    def post_effect(self, id, text):

       if self.hide_id == 0:
          id_str = "<font color='#000000'><b>" + str(id + 1) + ")</b></font>"
       else:
          id_str = ""

       if v.init_list[id]["hidden"] == 1:
          rounds_left = "?"
       else:
          rounds_left = self.getRoundsLeftInEffect(id)

       if(self.getRoundsLeftInEffect(id) > 0):
          self.post_my_msg("<table border=1 width=100% align='center'>\
                     <tr>\
                      <td>" + str(id_str) + " <font color='#0000ff'><b><i>" + str(text)\
                             + "</i></b></font><font color='#0000ff'>"\
                             + " <font color='#000000'>[" + str(v.init_list[id]["init"]) + "]</font> <i>"\
                             + str(v.init_list[id]["name"]) + "</i></font> : <b>" + str(rounds_left) + "</b> round(s) remaining.\
                      </td>\
                     </tr>\
                    </table>", 1)
       else:
          self.post_my_msg("<table border=1 width=100% align='center'>\
                     <tr>\
                      <td>" + str(id_str) + " <font color='#0000ff'><b><i>" + str(text)\
                             + "</i></b></font><i>"\
                             + " [" + str(v.init_list[id]["init"]) + "] <font color='#0000ff'>"\
                             + str(v.init_list[id]["name"]) + "</font></i>\
                      </td>\
                     </tr>\
                    </table>", 1)


    def post_my_msg(self, msg,send=0):
        tmp = self.init_recording

        # have to disable the tool in order to post or else you get a clone
        self.init_recording = 0
        self.chat.Post(msg,send)

        self.init_recording = tmp


    def post_syserror(self, msg, send=0):
       self.post_my_msg("<font color='#ff0000'><b><i>" + msg + "</i></b></font>", send)


    def post_sysmsg(self, msg,send=0):
       self.post_my_msg("<b>" + msg + "</b>", send)


    def toggle_hidden(self, id):
       try:
          #print "id : " + str(id)
          v.init_list[id]["hidden"] = not v.init_list[id]["hidden"]
          if v.init_list[id]["hidden"] == 1:
             tmp = "hidden"
          else:
             tmp = "visible"
          self.post_sysmsg(str(v.init_list[id]["name"]) + " now " + tmp + ".")

       except:
          self.post_syserror("Invalid format. Correct command is: /togglehidden init_#")

    ###############################################################
    ####    Conditional + Other Functions
    ###############################################################

    def check_version(self):
       if(int(replace(orpg.orpg_version.VERSION, ".", "")) < int(replace(self.orpg_min_version, ".", ""))):
          self.post_sysmsg("<br><font color='#ff0000'>WARNING</font>: You are currently using OpenRPG " + str(orpg.orpg_version.VERSION) + " but you need OpenRPG " + str(self.orpg_min_version) + " or greater to run the Initiative Tool " + str(self.version) + "<br>Use it at your own risk!</b><br>")

    def isD20(self):
        if self.init_system == "D20":
            return 1
        else:
            return 0

    def isSR4(self):
        if self.init_system == "SR4":
            return 1
        else:
            return 0

    def isRQ(self):
        if self.init_system == "RQ":
            return 1
        else:
            return 0

    def isEnded(self):
       if self.init_end == 0:
          return 1
       else:
          return 0

    def getFirst(self):
       return(0)

    def getLast(self):
       return(len(v.init_list)-1)

    def isEffect(self, id):
        # if it's type 1, then it's an effect
        try:
            if (self.isD20() or self.isRQ()) and v.init_list[id]["type"] == 1:
                return 1
            else:
                return 0
        except:
            return 0

    def getRoundsLeftInEffect(self, id):
        rounds_left = v.init_list[id]["duration"]
        return rounds_left

    def isMovement(self, id): # sr4 only, used to spam movement-only characters
        try:                  # in passes where they receive no actions
            passes = v.init_list[id]["passes"]
            if self.ip_order == 0: # Normal 1/2/3/4 --- VEG 2.2.7
                if passes < self.init_pass:
                    return 1
                else:
                    return 0
            elif self.ip_order == 1: # Serbitar's 3/1/4/2 --- VEG 2.2.7
                if (self.init_pass == 1) and (passes < 3):
                    return 1
                elif (self.init_pass == 2) and (passes < 1):
                    return 1
                elif (self.init_pass == 3) and (passes < 4):
                    return 1
                elif (self.init_pass == 4) and (passes < 2):
                    return 1
                else:
                    return 0
            elif self.ip_order == 2: # TinkerGnome's 4/1/3/2 --- VEG 2.2.7
                if (self.init_pass == 1) and (passes < 4):
                    return 1
                elif (self.init_pass == 2) and (passes < 1):
                    return 1
                elif (self.init_pass == 3) and (passes < 3):
                    return 1
                elif (self.init_pass == 4) and (passes < 2):
                    return 1
                else:
                    return 0
        except:
            return 0

    def sharesInit(self,id):
        try:
            if v.init_list[id]["init"] == v.init_list[self.init_listslot]["init"]:
                return 1
            else:
                return 0
        except: # last init will be an error, don't start new round yet
            return 0

    # get the id of the next (non effect) non-hidden character after the given id
    # return -1 : no character is next
    def getNext(self, id):
       lst_size = len(v.init_list)

       # list empty
       if lst_size < 2:
          return(-1)

       lst = []
       id_found = -1

       # if we'r at the last init, the next one is at the beginning of the list
       if self.isLast(id):
          for m in v.init_list:
             id_m = v.init_list.index(m)
             if self.isHidden(id_m) or self.isEffect(id_m):
                pass
             else:
                id_found = id_m
                break;
       else:
          try:
             for id_m in range(id+1, self.getLast()+1):
                if self.isHidden(id_m) or self.isEffect(id_m):
                   pass
                else:
                   id_found = id_m
                   break;

          except Exception, e:
             return(-1)

          # maybe we haven't found yet, must search at the beginning of the list
          if id_found == -1 and not self.isFirst(id):
             for id_m in range(0, id):
                if self.isHidden(id_m) or self.isEffect(id_m):
                   pass
                else:
                   id_found = id_m
                   break;

       if id_found != id:
          return(id_found)
       else:
          return(-1)

    def isHidden(self, id):
       try:
          if v.init_list[id]["hidden"] == 1:
             return(1)
          else:
             return(0)

       except:
             return(0)

    def isFirst(self, id):
       if id == 0:
          return 1
       else:
          return 0

    def isLast(self, id):
       if id == (len(v.init_list)-1):
          return 1
       else:
          return 0

    def get_next_rank(self, init):
       # when adding/changing an object, this function is used to get a unique rank of this init count
       # new object are added at the end of an init count, so we need a 'rank' lower than the
       # other object of the same init count.
       rank = 0;
       for m in v.init_list:
          if m["init"] == init:
             x = m["rank"] - 1
             if rank > x:
                rank = x
       return rank

    def get_unique_tag(self, name):
        m = hashlib.md5()
        m.update(str(random.random()) + str(name))
        return m.hexdigest()

### VEG 2.2.6
### Init GUI stuff below

    def CMD_initgui(self, cmdargs):
        self.frame = InitToolFrame(NULL, -1, "Initiative Tool GUI")
        self.frame.Show(true)

class InitToolFrame(wx.Frame):
    def __init__(self, parent, ID, title):
        wx.Frame.__init__(self, parent, ID, title, size=(400, 300))

        self.panel = wx.Panel(self,-1) #, style=wx.SUNKEN_BORDER)

        #self.panel.SetBackgroundColour("RED")

        self.x_id = 5
        self.x_name = 25
        self.x_init = 200
        self.x_change = 225
        self.x_delete = 275
        self.y = 0
        b = 0

        wx.StaticText ( self.panel, -1, "ID", (self.x_id, self.y) )
        wx.StaticText ( self.panel, -1, "Name", (self.x_name, self.y) )
        wx.StaticText ( self.panel, -1, "Init#", (self.x_init, self.y) )
        self.y+=20

        var_ID=0
        var_Name=0
        var_Init=0

        for m in v.init_list:
            var_ID = str(v.init_list.index(m)+1) + ")"
            var_Name = self.strip_html(m["name"])
            var_Init = str(m["init"])

            wx.StaticText ( self.panel, -1, var_ID, (self.x_id, self.y) )
            wx.StaticText ( self.panel, -1, var_Name, (self.x_name, self.y) )
            wx.TextCtrl ( self.panel, -1, var_Init, (self.x_init, self.y), (20,20), wx.TE_READONLY)
            wx.Button(self.panel, -1, "Change", (self.x_change, self.y), (50,20) )
            wx.Button(self.panel, -1, "Delete", (self.x_delete, self.y), (50,20) )
            self.y+=20

        #for i in range(0,4):
        #    if i == 0:
        #        var_ID   = str(0)
        #        var_Name = "Hignar"
        #        var_Init = str(14)
        #    elif i == 1:
        #        var_ID   = str(1)
        #        var_Name = "Aiur"
        #        var_Init = str(12)
        #    elif i == 2:
        #        var_ID   = str(2)
        #        var_Name = "Effect: Tlasco's Bless"
        #        var_Init = str(4)
        #    elif i == 3:
        #        var_ID   = str(3)
        #        var_Name = "Tlasco"
        #        var_Init = str(3)
        #    wxStaticText ( self.panel, -1, var_ID, (self.x_id, self.y) )
        #    wxStaticText ( self.panel, -1, var_Name, (self.x_name, self.y) )
        #    wxStaticText ( self.panel, -1, var_Init, (self.x_init, self.y) )
        #    wxButton(self.panel, -1, "Change", (self.x_change, self.y), (50,20) )
        #    wxButton(self.panel, -1, "Delete", (self.x_delete, self.y), (50,20) )
        #    self.y+=20

    def strip_html(self, text):
        finished = 0
        while not finished:
            finished = 1
            # check if there is an open tag left
            start = text.find("<")
            if start >= 0:
                # if there is, check if the tag gets closed
                stop = text[start:].find(">")
                if stop >= 0:
                    # if it does, strip it, and continue loop
                    text = text[:start] + text[start+stop+1:]
                    finished = 0
        return text