view plugins/xxinit2.py @ 202:f1a67667d78b alpha

Traipse Alpha 'OpenRPG' {100427-03} Traipse is a distribution of OpenRPG that is designed to be easy to setup and go. Traipse also makes it easy for developers to work on code without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy' and adds fixes to the code. 'Ornery-Orc's main goal is to offer more advanced features and enhance the productivity of the user. Update Summary (Patch-2) New Features: New Namespace method with two new syntaxes New Namespace Internal is context sensitive, always! New Namespace External is 'as narrow as you make it' New Namespace FutureCheck helps ensure you don't receive an incorrect node New PluginDB access for URL2Link plugin New to Forms, they now show their content in Design Mode Fixes: Fix to Server GUI startup errors Fix to Server GUI Rooms tab updating Fix to Chat and Settings if non existant die roller is picked Fix to Dieroller and .open() used with .vs(). Successes are correctly calculated Fix to Alias Lib's Export to Tree, Open, Save features Fix to alias node, now works properly Fix to Splitter node, minor GUI cleanup Fix to Backgrounds not loading through remote loader Fix to Node name errors Fix to rolling dice in chat Whispers Fix to Splitters Sizing issues Fix to URL2Link plugin, modified regex compilation should remove memory leak Fix to mapy.py, a roll back due to zoomed grid issues Fix to whiteboard_handler, Circles work by you clicking the center of the circle Fix to Servers parse_incoming_dom which was outdated and did not respect XML Fix to a broken link in the server welcome message Daily: I was having problems with Chat tabs. All fixed now.
author sirebral
date Tue, 27 Apr 2010 06:56:52 -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