# HG changeset patch # User sirebral # Date 1272875411 18000 # Node ID fc48380f0c9f97b320a981274b5aba13656d09e2 # Parent 8e77f169f3243f09e77899f88c4c11acffe672df Traipse Beta 'OpenRPG' {100503-00} 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 Namespace 2.0 documentation in the User Manual New Namespace plugin, Allows Traipse users to use the Standard syntax !@ :: @! New Mini Library with minis from Devin Knight New PluginDB access for URL2Link plugin New to Forms, they now show their content in Design Mode New to Update Manager, checks Repo for updates on software start New to Mini Lib node, change title in design mode New to Game Tree, never lose a node, appends a number to the end of corrupted trees New to Server GUI, Traipse Suite's Debug Console New Warhammer PC Sheet Updates: Update to White Board layer, uses a pencil image for color button Update to Grid Layer, uses a grid image for color button Update to Chat Window, size of drop down menus Update to default lobby message Update to template Text node Update to 4e PC Sheet node Update to how display names are acquired Update to Server, added some 'Pious' technology Update to features node 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 Fix to InterParse and logger requiring traceback Fix to Update Manager Status Bar Fix to failed image and erroneous pop up Fix to Mini Lib node that was preventing use Fix to plugins that parce dice but did not call InterParse Fix to nodes for name changing by double click Fix to Game Tree, node ordering on drag and drop corrected Fix to Game Tree, corrupted error message was not showing Fix to Update Manager, checks for internet connection Fix to Update Manager, Auto Update corrections Fix to Server GUI's broadcast, room, player messaging diff -r 8e77f169f324 -r fc48380f0c9f data/tips.txt --- a/data/tips.txt Fri Feb 19 19:10:25 2010 -0600 +++ b/data/tips.txt Mon May 03 03:30:11 2010 -0500 @@ -43,3 +43,6 @@ You can search your chat session by opening the text view of it (click the button to the right of the dice). Type in the search term and hit "find". The map has a tape measure. Hold down SHIFT and drag with the left mouse button down. + +Traipse's Namespace 2.0 offers much more control over your gametree references. With the Standard Namespace plugin you can play with your friends who use the older model + diff -r 8e77f169f324 -r fc48380f0c9f images/draw.png Binary file images/draw.png has changed diff -r 8e77f169f324 -r fc48380f0c9f images/grid.png Binary file images/grid.png has changed diff -r 8e77f169f324 -r fc48380f0c9f orpg/chat/chatwnd.py --- a/orpg/chat/chatwnd.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/chat/chatwnd.py Mon May 03 03:30:11 2010 -0500 @@ -502,7 +502,7 @@ wx.WXK_F9: 'event.GetKeyCode() == wx.WXK_F9', wx.WXK_F10: 'event.GetKeyCode() == wx.WXK_F10', wx.WXK_F11: 'event.GetKeyCode() == wx.WXK_F11', wx.WXK_F12: 'event.GetKeyCode() == wx.WXK_F12'} #Alias Lib stuff - self.defaultAliasName = 'Use Real Name' + self.defaultAliasName = 'Real Name' self.defaultFilterName = 'No Filter' self.advancedFilter = False self.lastSend = 0 # this is used to help implement the player typing indicator @@ -763,7 +763,7 @@ self.chatwnd = chat_html_window(self,-1) self.set_colors() wx.CallAfter(self.chatwnd.SetPage, self.chatwnd.Header()) - welcome = "Welcome to " + welcome = "Welcome to " welcome += DISTRO +' '+ DIS_VER +' {'+BUILD+'},' welcome += ' built on OpenRPG '+ VERSION +'' if (self.sendtarget == "all"): @@ -827,7 +827,7 @@ def build_alias(self): self.aliasSizer = wx.BoxSizer(wx.HORIZONTAL) - self.aliasList = wx.Choice(self, wx.ID_ANY, size=(100, 25), choices=[self.defaultAliasName]) + self.aliasList = wx.Choice(self, wx.ID_ANY, size=(120, 25), choices=[self.defaultAliasName]) self.aliasButton = createMaskedButton( self, dir_struct["icon"] + 'player.gif', 'Refresh list of aliases from Game Tree', wx.ID_ANY, '#bdbdbd' ) @@ -1032,7 +1032,7 @@ sound_file = self.settings.get_setting("SendSound") if sound_file != '': component.get('sound').play(sound_file) if s[0] != "/": ## it's not a slash command - s = self.ParsePost( s, True, True ) + s = Parse.Post(s, self, True, True) else: self.chat_cmds.docmd(s) # emote is in chatutils.py def on_chat_key_down(self, event): @@ -1171,7 +1171,7 @@ if len(dieMod) and dieMod[0] not in "*/-+": dieMod = "+" + dieMod dieText += dieMod dieText = "[" + dieText + "]" - self.ParsePost(dieText, 1, 1) + Parse.Post(dieText, self, 1, 1) self.chattxt.SetFocus() def on_chat_save(self, evt): @@ -1301,7 +1301,7 @@ return text def emote_message(self, text): - text = Parse.Normalize(text) + text = Parse.Normalize(text, self) text = self.colorize(self.emotecolor, text) if self.type == MAIN_TAB and self.sendtarget == 'all': self.send_chat_message(text,chat_msg.EMOTE_MESSAGE) elif self.type == MAIN_TAB and self.sendtarget == "gm": @@ -1319,7 +1319,7 @@ def whisper_to_players(self, text, player_ids): tabbed_whispers_p = self.settings.get_setting("tabbedwhispers") - text = Parse.Normalize(text) + text = Parse.Normalize(text, self) player_names = "" for m in player_ids: id = m.strip() @@ -1371,8 +1371,7 @@ alias = msg.get_alias() # who sent us the message? if alias: display_name = self.chat_display_name([alias, player[1], player[2]]) - elif player: display_name = self.chat_display_name(player) - else: display_name = "Server Administrator" + else: display_name = self.chat_display_name(player) ######### START plugin_incoming_msg() ########### for plugin_fname in self.activeplugins.keys(): @@ -1386,6 +1385,7 @@ if (strip_img == "0"): display_name = chat_util.strip_img_tags(display_name) recvSound = "RecvSound" # act on the type of messsage + if (type == chat_msg.CHAT_MESSAGE): text = "" + display_name + ": " + text self.Post(text) @@ -1591,18 +1591,13 @@ logger.general("EXCEPTION: " + str(e)) return "[ERROR]" - def ParsePost(self, s, send=False, myself=False): - s = Parse.Normalize(s) - self.set_colors() - self.Post(s,send,myself) - # This subroutine builds a chat display name. # def chat_display_name(self, player): - if self.settings.get_setting("ShowIDInChat") == "0": - display_name = player[0] - else: - display_name = "("+player[2]+") " + player[0] + if player == None: + player = ['Server Administrator-> ', '127.0.0.1', '0'] + if self.settings.get_setting("ShowIDInChat") == "0": display_name = player[0] + else: display_name = "("+player[2]+") " + player[0] return display_name # This subroutine will get a hex color and return it, or return nothing diff -r 8e77f169f324 -r fc48380f0c9f orpg/chat/commands.py --- a/orpg/chat/commands.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/chat/commands.py Mon May 03 03:30:11 2010 -0500 @@ -214,14 +214,18 @@ def on_dieroller(self, cmdargs): args = string.split(cmdargs,None,-1) rm = component.get('DiceManager') + cur_die = rm.getRoller() + if len(args) == 0: self.chat.InfoPost('You are using the "' +cur_die+ '" die roller.'); return try: rm.setRoller(args[0]) - self.chat.SystemPost("You have changed your die roller to the \"" + args[0] + "\" roller.") - self.settings.set_setting('dieroller',args[0]) + self.chat.SystemPost('You have changed your die roller to the "' +rm.getRoller()+ '" roller.') + self.settings.change('dieroller', rm.getRoller()) except Exception, e: - print e - self.chat.InfoPost("Available die rollers: " + str(rm.listRollers())) - self.chat.InfoPost("You are using the \"" + rm.getRoller() + "\" die roller.") + rm.setRoller(cur_die) + self.settings.change('dieroller', str(cur_die)) + if args[0] != '': self.chat.SystemPost(args[0]+ ' is an invalid roller.') + self.chat.InfoPost('Available die rollers: ' +str(rm.listRollers()) ) + self.chat.InfoPost('You are using the "' +cur_die+ '" die roller.') def on_ping(self, cmdargs): ct = time.clock() diff -r 8e77f169f324 -r fc48380f0c9f orpg/dieroller/base.py --- a/orpg/dieroller/base.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/dieroller/base.py Mon May 03 03:30:11 2010 -0500 @@ -126,7 +126,6 @@ elif hasattr(other,"sum"): return cmp(self.sum(), other.sum()) else: return UserList.UserList.__cmp__(self,other) - def __rcmp__(self,other): return self.__cmp__(other) @@ -230,7 +229,7 @@ ### di class to handle actual dice class di: - def __init__(self,sides,min=1): + def __init__(self, sides, min=1): self.sides = sides self.history = None self.value = None @@ -264,7 +263,6 @@ def __int__(self): return self.value - def __lt__(self,other): if type(other) == type(3) or type(other) == type(3.0): return self.value < other elif hasattr(other,"value"): return self.value < other.value diff -r 8e77f169f324 -r fc48380f0c9f orpg/dieroller/rollers/std.py --- a/orpg/dieroller/rollers/std.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/dieroller/rollers/std.py Mon May 03 03:30:11 2010 -0500 @@ -3,8 +3,8 @@ class std(die_base): name = "std" - def __init__(self,source=[]): - die_base.__init__(self,source) + def __init__(self, source=[]): + die_base.__init__(self, source) # Examples of adding member functions through inheritance. @@ -19,30 +19,27 @@ result.reverse() return result - def takeHighest(self,num_dice): + def takeHighest(self, num_dice): return self.descending()[:num_dice] def takeLowest(self,num_dice): return self.ascending()[:num_dice] - def extra(self,num): + def extra(self, num): for i in range(len(self.data)): if self.data[i].lastroll() >= num: self.data[i].extraroll() return self - def open(self,num): - if num <= 1: - self + def open(self, num): + if num <= 1: self done = 1 for i in range(len(self.data)): if self.data[i].lastroll() >= num: self.data[i].extraroll() done = 0 - if done: - return self - else: - return self.open(num) + if done: return self + else: return self.open(num) def minroll(self,min): for i in range(len(self.data)): @@ -50,13 +47,12 @@ self.data[i].roll(min) return self - def each(self,mod): + def each(self, mod): mod = int(mod) for i in range(len(self.data)): self.data[i].modify(mod) return self - def vs(self, target): for dn in self.data: dn.target = target @@ -72,12 +68,10 @@ for dn in self.data: setValue = reduce( lambda x, y : int(x)+int(y), dn.history ) if dn.target: - if setValue >= dn.target: - retValue += 1 - + for dv in dn.history: + if int(dv) >= dn.target: retValue += 1 else: retValue += setValue - return retValue die_rollers.register(std) diff -r 8e77f169f324 -r fc48380f0c9f orpg/gametree/gametree.py --- a/orpg/gametree/gametree.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/gametree/gametree.py Mon May 03 03:30:11 2010 -0500 @@ -21,11 +21,18 @@ # Author: Chris Davis # Maintainer: # Version: -# $Id: gametree.py,v Traipse 'Ornery-Orc' prof.ebral Exp $ +# $Id: gametree.py,v 1.68 2007/12/07 20:39:48 digitalxero Exp $ # # Description: The file contains code fore the game tree shell # -# Traipse EZ_Tree Reference System (TaS - Prof.Ebral): v Traipse 'Ornery-Orc' prof.ebral Exp +# Traipse EZ_Tree Reference System (TaS - Prof.Ebral): +# +# The new EZ_Tree Reference System being implemented takes full advantage of +# Python's OOP Language. The entire tree code is being reused, but a new ID is +# being created which 'shuts off' some of the features of the tree and adds new ones. +# This new feature will allow users to quickly add a Reference button to new node +# handlers. The button will show a faximile of the tree and users can then create a +# node reference with ease! # from __future__ import with_statement @@ -37,7 +44,8 @@ from orpg.orpgCore import component from orpg.dirpath import dir_struct from nodehandlers import core -import string, urllib, time, os, shutil +import string, urllib, time, os +from shutil import copytree, copystat, copy, copyfile from orpg.orpg_xml import xml from orpg.tools.validate import validate @@ -53,6 +61,12 @@ from xml.etree.ElementTree import fromstring, tostring, XML, iselement from xml.parsers.expat import ExpatError +def exists(path): + try: + os.stat(path) + return True + except: return False + STD_MENU_DELETE = wx.NewId() STD_MENU_DESIGN = wx.NewId() STD_MENU_USE = wx.NewId() @@ -82,12 +96,6 @@ TOP_FEATURES = wx.NewId() EZ_REF = wx.NewId() -def exists(path): - try: - os.stat(path) - return True - except: return False - class game_tree(wx.TreeCtrl): def __init__(self, parent, id): @@ -205,9 +213,9 @@ self.EditLabel(curSelection) evt.Skip() - def locate_valid_tree(self, error, msg): ## --Snowdog 3/05 + def locate_valid_tree(self, error, msg, filename): ## --Snowdog 3/05 """prompts the user to locate a new tree file or create a new one""" - response = wx.MessageDialog(self, msg, error, wx.YES|wx.NO|wx.ICON_ERROR) + response = wx.MessageBox(msg, error, wx.YES|wx.NO|wx.ICON_ERROR) if response == wx.YES: file = None dlg = wx.FileDialog(self, "Locate Gametree file", dir_struct["user"], @@ -220,6 +228,7 @@ else: self.load_tree(file) return else: + copyfile(dir_struct['template']+'default_tree.xml', filename) validate.config_file("tree.xml","default_tree.xml") self.load_tree(error=1) return @@ -233,27 +242,25 @@ self.locate_valid_tree("Gametree Error", emsg) return try: + self.xml_root = False tree = parse(filename) self.xml_root = tree.getroot() - except: - self.xml_root = None - + except: self.xml_root = False if not self.xml_root: count = 1 - while exists(filename[:len(filename)-4]+'-bad-'+str(count)+'.xml'): count += 1 - shutil.copy(filename, filename[:len(filename)-4]+'-bad-'+str(count)+'.xml') - shutil.copyfile(dir_struct["template"]+'default_tree.xml', filename) + while exists(filename[:len(filename)-4]+'-'+str(count)+'.xml'): count += 1 + corrupt_tree = filename[:len(filename)-4]+'-'+str(count)+'.xml' + copyfile(filename, corrupt_tree) emsg = "Your gametree is being regenerated.\n\n"\ "To salvage a recent version of your gametree\n"\ - "exit OpenRPG and copy the lastgood.xml file in\n"\ - "your myfiles directory to "+filename[:len(filename)-4]+'-bad-'+str(count)+'.xml'+ "\n"\ + "exit OpenRPG and copy the one of the tree-# files in\n"\ + "your myfiles directory to "+filename+ "\n"\ "in your myfiles directory.\n\n"\ "lastgood.xml WILL BE OVERWRITTEN NEXT TIME YOU RUN OPENRPG.\n\n"\ "Would you like to select a different gametree file to use?\n"\ "(Selecting 'No' will cause a new default gametree to be generated)" - self.locate_valid_tree("Corrupt Gametree!", emsg) + self.locate_valid_tree("Corrupt Gametree!", emsg, filename) return - if self.xml_root.tag != "gametree": emsg = filename+" does not appear to be a valid gametree file.\n\n"\ "Would you like to select a different gametree file to use?\n"\ @@ -288,11 +295,22 @@ except Exception, e: logger.exception(traceback.format_exc()) + count = 1 - while exists(filename[:len(filename)-4]+'-bad-'+str(count)+'.xml'): count += 1 - shutil.copy(filename, filename[:len(filename)-4]+'-bad-'+str(count)+'.xml') - shutil.copyfile(dir_struct["template"]+'default_tree.xml', filename) - wx.MessageBox("Corrupt Tree!\nYour game tree is being regenerated. To\nsalvage a recent version of your gametree\nexit OpenRPG and copy the lastgood.xml\nfile in your myfiles directory\nto "+filename+ "\nin your myfiles directory.\nlastgood.xml WILL BE OVERWRITTEN NEXT TIME YOU RUN OPENRPG.") + while exists(filename[:len(filename)-4]+'-'+str(count)+'.xml'): count += 1 + corrupt_tree = filename[:len(filename)-4]+'-'+str(count)+'.xml' + copyfile(filename, corrupt_tree) + wx.MessageBox("Your gametree is being regenerated.\n\n"\ + "To salvage a recent version of your gametree\n"\ + "exit OpenRPG and copy the one of the tree-# files in\n"\ + "your myfiles directory to "+filename+ "\n"\ + "in your myfiles directory.\n\n"\ + "lastgood.xml WILL BE OVERWRITTEN NEXT TIME YOU RUN OPENRPG.\n\n") + + count = 1 + while exists(filename[:len(filename)-4]+'-'+str(count)+'.xml'): count += 1 + corrupt_tree = filename[:len(filename)-4]+'-'+str(count)+'.xml' + copyfile(filename, corrupt_tree) validate.config_file("tree.xml","default_tree.xml") self.load_tree(error=1) @@ -680,7 +698,7 @@ family_tree.append(parent) return family_tree - def load_xml(self, xml_element, parent_node, prev_node=None): + def load_xml(self, xml_element, parent_node, prev_node=None, drag_drop=False): if parent_node == self.root: self.tree_map[xml_element.get('name')] = {} self.tree_map[xml_element.get('name')]['node'] = xml_element @@ -703,6 +721,8 @@ if prev_node: if prev_node == parent_node: new_tree_node = self.PrependItem(parent_node, name, i, i) else: new_tree_node = self.InsertItem(parent_node, prev_node, name, i, i) + elif drag_drop: + new_tree_node = self.InsertItemBefore(parent_node, 0, name, i) else: new_tree_node = self.AppendItem(parent_node, name, i, i) logger.debug("Node Added to tree") @@ -809,7 +829,7 @@ self.rename_flag = 0 if txt != "": obj = self.GetPyData(item) - obj.xml_root.setAttribute('name',txt) + obj.xml_root.set('name',txt) else: evt.Veto() def on_label_begin(self, evt): diff -r 8e77f169f324 -r fc48380f0c9f orpg/gametree/nodehandlers/chatmacro.py --- a/orpg/gametree/nodehandlers/chatmacro.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/gametree/nodehandlers/chatmacro.py Mon May 03 03:30:11 2010 -0500 @@ -98,6 +98,6 @@ txt = self.text[id].GetValue() if txt == "": return if id == P_TITLE: - self.handler.xml.setAttribute('name',txt) + self.handler.xml.set('name',txt) self.handler.rename(txt) elif id == P_BODY: self.handler.set_text(txt) diff -r 8e77f169f324 -r fc48380f0c9f orpg/gametree/nodehandlers/containers.py --- a/orpg/gametree/nodehandlers/containers.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/gametree/nodehandlers/containers.py Mon May 03 03:30:11 2010 -0500 @@ -67,11 +67,11 @@ drag_obj = self.tree.drag_obj if drag_obj == self or self.tree.is_parent_node(self.mytree_node,drag_obj.mytree_node): return opt = wx.MessageBox("Add node as child?","Container Node",wx.YES_NO|wx.CANCEL) + prev_sib = self.tree.GetPrevSibling(drag_obj.mytree_node) if opt == wx.YES: drop_xml = self.tree.drag_obj.delete() self.xml.insert(0, drop_xml) - self.tree.load_xml(drop_xml, self.mytree_node) - self.tree.Expand(self.mytree_node) + self.tree.load_xml(drop_xml, self.mytree_node, drag_drop=True) elif opt == wx.NO: node_handler.on_drop(self,evt) def gen_html(self, treenode, evt): @@ -245,7 +245,6 @@ parent.SetSize(self.GetBestSize()) self.Bind(wx.EVT_TEXT, self.on_text, id=1) - def on_text(self,evt): txt = self.title.GetValue() if txt != "": @@ -281,19 +280,19 @@ container_handler.on_drop(self,evt) def build_splitter_wnd(self, parent, mode): + self.parent = parent self.split = self.xml.get("horizontal") - self.pane = splitter_panel(parent, self) + self.pane = splitter_panel(parent, self, mode) + self.frame = self.pane.frame self.splitter = MultiSplitterWindow(self.pane, -1, style=wx.SP_LIVE_UPDATE|wx.SP_3DSASH|wx.SP_NO_XP_THEME) + self.splitter.parent = self if self.split == '1': self.splitter.SetOrientation(wx.VERTICAL) else: self.splitter.SetOrientation(wx.HORIZONTAL) - self.bestSizex = -1 - self.bestSizey = -1 self.tree.traverse(self.mytree_node, self.doSplit, mode, False) - self.pane.sizer.Add(self.splitter, 1, wx.EXPAND) - if mode != 1: self.pane.hozCheck.Hide() - self.pane.SetSize((self.bestSizex, self.bestSizey)) - self.pane.Layout() + self.pane.sizer.Add(self.splitter, -1, wx.EXPAND) + self.pane.SetAutoLayout(True) + self.pane.Fit() parent.SetSize(self.pane.GetSize()) return self.pane @@ -301,41 +300,40 @@ node = self.tree.GetPyData(treenode) if mode == 1: tmp = node.get_design_panel(self.splitter) else: tmp = node.get_use_panel(self.splitter) - if self.split == '1': - sash = tmp.GetBestSize()[1]+1 - self.bestSizey += sash+11 - if self.bestSizex < tmp.GetBestSize()[0]: self.bestSizex = tmp.GetBestSize()[0]+10 - else: - sash = tmp.GetBestSize()[0]+1 - self.bestSizex += sash - if self.bestSizey < tmp.GetBestSize()[1]: self.bestSizey = tmp.GetBestSize()[1]+31 + if self.split == '1': sash = self.frame.GetSize()[1]/len(self.xml.findall('nodehandler')) + else: sash = self.frame.GetSize()[0]/len(self.xml.findall('nodehandler')) self.splitter.AppendWindow(tmp, sash) + def get_size_constraint(self): return 1 class splitter_panel(wx.Panel): - def __init__(self, parent, handler): + def __init__(self, parent, handler, mode): wx.Panel.__init__(self, parent, -1) + self.parent = parent self.handler = handler - sizer = wx.BoxSizer(wx.VERTICAL) - self.title = wx.TextCtrl(self, 1, handler.xml.get('name')) - - self.hozCheck = wx.CheckBox(self, -1, "Horizontal Split") - hoz = self.handler.xml.get("horizontal") + self.sizer = wx.BoxSizer(wx.VERTICAL) + if mode == 0: self.title = wx.StaticText(self, 1, handler.xml.get('name')) + elif mode == 1: self.title = wx.TextCtrl(self, 1, handler.xml.get('name')) + self.frame = self.GetParent() + while self.frame.GetName() != 'frame': + self.frame = self.frame.GetParent() - if hoz == '1': self.hozCheck.SetValue(True) - else: self.hozCheck.SetValue(False) + if mode == 1: + self.hozCheck = wx.CheckBox(self, -1, "Horizontal Split") + hoz = self.handler.xml.get("horizontal") + if hoz == '1': self.hozCheck.SetValue(True) + else: self.hozCheck.SetValue(False) - sizer.Add(wx.StaticText(self, -1, "Title:"), 0, wx.EXPAND) - sizer.Add(self.title, 0) - sizer.Add(self.hozCheck, 0, wx.EXPAND) - sizer.Add(wx.Size(10,0)) + if mode == 1: self.sizer.Add(wx.StaticText(self, -1, "Title:"), 0, wx.EXPAND) + self.sizer.Add(self.title, 0) + if mode == 1: self.sizer.Add(self.hozCheck, 0, wx.EXPAND) + self.sizer.Add(wx.Size(10,0)) - self.sizer = sizer self.SetSizer(self.sizer) self.SetAutoLayout(True) self.Bind(wx.EVT_TEXT, self.on_text, id=1) - self.Bind(wx.EVT_CHECKBOX, self.on_check_box, id=self.hozCheck.GetId()) + if mode == 1: self.Bind(wx.EVT_CHECKBOX, self.on_check_box, id=self.hozCheck.GetId()) def on_check_box(self,evt): state = self.hozCheck.GetValue() diff -r 8e77f169f324 -r fc48380f0c9f orpg/gametree/nodehandlers/core.py~ --- a/orpg/gametree/nodehandlers/core.py~ Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/gametree/nodehandlers/core.py~ Mon May 03 03:30:11 2010 -0500 @@ -1,462 +1,452 @@ -# 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. -# -- -# -# File: core.py -# Author: Chris Davis -# Maintainer: -# Version: -# $Id: core.py,v 1.49 2007/12/07 20:39:48 digitalxero Exp $ -# -# Description: The file contains code for the core nodehanlers -# - -__version__ = "$Id: core.py,v 1.49 2007/12/07 20:39:48 digitalxero Exp $" - -from nodehandler_version import NODEHANDLER_VERSION -try: - from orpg.orpg_windows import * - import orpg.dirpath - from orpg.orpg_xml import * - from orpg.orpgCore import open_rpg - import webbrowser - from orpg.mapper import map - import os -except: - import wx - - - - -#html defaults -TH_BG = "#E9E9E9" -########################## -## base node handler -########################## -class node_handler: - """ Base nodehandler with virtual functions and standard implmentations """ - def __init__(self,xml_dom,tree_node): - self.master_dom = xml_dom - self.mytree_node = tree_node - self.tree = open_rpg.get_component('tree') - self.frame = open_rpg.get_component('frame') - self.chat = open_rpg.get_component('chat') - self.xml = open_rpg.get_component('xml') - self.drag = True - self.myeditor = None # designing - self.myviewer = None # prett print - self.mywindow = None # using - # call version hook - self.on_version(self.master_dom.getAttribute("version")) - # set to current version - self.master_dom.setAttribute("version",NODEHANDLER_VERSION) - # null events - - def on_version(self,old_version): - ## added version control code here or implement a new on_version in your derived class. - ## always call the base class on_version ! - pass - - def on_rclick(self,evt): - self.tree.do_std_menu(evt,self) - - def on_ldclick(self,evt): - return 0 - - def traverse(self, traverseroot, function, cookie=0, event=None, recursive=True): - """ walk tree control """ - if traverseroot.IsOk(): - # step in subtree if there are items or ... - if self.tree.ItemHasChildren(traverseroot) and recursive: - firstchild, cookie = self.tree.GetFirstChild(traverseroot) - obj = self.tree.GetPyData(firstchild) - function(obj, event) - self.traverse(firstchild, function, cookie, event, recursive) - - # ... loop siblings - obj = self.tree.GetPyData(traverseroot) - function(obj, event) - - child = self.tree.GetNextSibling(traverseroot) - if child.IsOk(): - self.traverse(child, function, cookie, event, recursive) - - - def usefulness(self,text): - if text=="useful": - self.master_dom.setAttribute('status',"useful") - elif text=="useless": - self.master_dom.setAttribute('status',"useless") - elif text=="indifferent": - self.master_dom.setAttribute('status',"indifferent") - - def on_design(self,evt): - try: - self.myeditor.Show() - self.myeditor.Raise() - except: - del self.myeditor - if self.create_designframe(): - self.myeditor.Show() - self.myeditor.Raise() - else: - return - wx.CallAfter(self.myeditor.Layout) - - - def create_designframe(self): - title = self.master_dom.getAttribute('name') + " Editor" - self.myeditor = wx.Frame(None, -1, title) - self.myeditor.Freeze() - if wx.Platform == '__WXMSW__': - icon = wx.Icon(orpg.dirpath.dir_struct["icon"] + 'grid.ico', wx.BITMAP_TYPE_ICO) - self.myeditor.SetIcon(icon) - del icon - - self.myeditor.panel = self.get_design_panel(self.myeditor) - if self.myeditor.panel == None: - self.myeditor.Destroy() - return False - sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(self.myeditor.panel, 1, wx.EXPAND) - - self.myeditor.SetSizer(sizer) - self.myeditor.SetAutoLayout(True) - - (x, y) = self.myeditor.GetSize() - if x < 400: - x = 400 - if y < 400: - y = 400 - - self.myeditor.SetSize((x, y)) - self.myeditor.Layout() - self.myeditor.Thaw() - - return True - - def on_use(self,evt): - try: - self.mywindow.Show() - self.mywindow.Raise() - except: - del self.mywindow - if self.create_useframe(): - self.mywindow.Show() - self.mywindow.Raise() - else: - return - wx.CallAfter(self.mywindow.Layout) - - - def create_useframe(self): - caption = self.master_dom.getAttribute('name') - self.mywindow = wx.Frame(None, -1, caption) - self.mywindow.Freeze() - - if wx.Platform == '__WXMSW__': - icon = wx.Icon(orpg.dirpath.dir_struct["icon"] + 'note.ico', wx.BITMAP_TYPE_ICO) - self.mywindow.SetIcon(icon) - del icon - self.mywindow.panel = self.get_use_panel(self.mywindow) - if self.mywindow.panel == None: - self.mywindow.Destroy() - return False - sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(self.mywindow.panel, 2, wx.EXPAND) - - self.mywindow.SetSizer(sizer) - self.mywindow.SetAutoLayout(True) - - (x, y) = self.mywindow.GetSize() - if x < 400: - x = 400 - if y < 400: - y = 400 - - self.mywindow.SetSize((x, y)) - self.mywindow.Layout() - self.mywindow.Thaw() - - return True - - - def on_html_view(self,evt): - try: - self.myviewer.Raise() - except: - caption = self.master_dom.getAttribute('name') - self.myviewer = wx.Frame(None, -1, caption) - if wx.Platform == '__WXMSW__': - icon = wx.Icon(orpg.dirpath.dir_struct["icon"] + 'grid.ico', wx.BITMAP_TYPE_ICO) - self.myviewer.SetIcon(icon) - del icon - self.myviewer.panel = self.get_html_panel(self.myviewer) - self.myviewer.Show() - - def map_aware(self): - return 0 - - def can_clone(self): - return 1; - - def on_del(self,evt): - print "on del" - - def on_new_data(self,xml_dom): - pass - - def get_scaled_bitmap(self,x,y): - return None - - def on_send_to_map(self,evt): - pass - - def on_send_to_chat(self,evt): - self.chat.ParsePost(self.tohtml(),True,True) - - def on_drop(self,evt): - drag_obj = self.tree.drag_obj - if drag_obj == self or self.tree.is_parent_node(self.mytree_node,drag_obj.mytree_node): - return - #if self.is_my_child(self.mytree_node,drag_obj.mytree_node): - # return - xml_dom = self.tree.drag_obj.delete() - parent = self.master_dom._get_parentNode() - xml_dom = parent.insertBefore(xml_dom,self.master_dom) - parent_node = self.tree.GetItemParent(self.mytree_node) - prev_sib = self.tree.GetPrevSibling(self.mytree_node) - if not prev_sib.IsOk(): - prev_sib = parent_node - self.tree.load_xml(xml_dom, parent_node, prev_sib) - - def toxml(self,pretty=0): - return toxml(self.master_dom,pretty) - - def tohtml(self): - return self.master_dom.getAttribute("name") - - def delete(self): - """ removes the tree_node and xml_node, and returns the removed xml_node """ - - self.tree.Delete(self.mytree_node) - parent = self.master_dom._get_parentNode() - return parent.removeChild(self.master_dom) - - def rename(self,name): - if len(name): - self.tree.SetItemText(self.mytree_node,name) - self.master_dom.setAttribute('name', name) - - def change_icon(self,icon): - self.master_dom.setAttribute("icon",icon) - self.tree.SetItemImage(self.mytree_node, self.tree.icons[icon]) - self.tree.SetItemImage(self.mytree_node, self.tree.icons[icon], wx.TreeItemIcon_Selected) - self.tree.Refresh() - - def on_save(self,evt): - f = wx.FileDialog(self.tree,"Select a file", orpg.dirpath.dir_struct["user"],"","XML files (*.xml)|*.xml",wx.SAVE) - if f.ShowModal() == wx.ID_OK: - type = f.GetFilterIndex() - file = open(f.GetPath(),"w") - file.write(self.toxml(1)) - file.close() - f.Destroy() - - def get_design_panel(self,parent): - return None - - def get_use_panel(self,parent): - return None - - def get_html_panel(self,parent): - html_str = ""+self.tohtml()+"" - wnd = wx.HTMLpanel(parent,-1) - html_str = self.chat.ParseDice(html_str) - wnd.load_text(html_str) - return wnd - - def get_size_constraint(self): - return 0 - - def about(self): - html_str = ""+ self.master_dom.getAttribute('class') - html_str += " Applet
by Chris Davis
chris@rpgarchive.com" - return html_str - -P_TITLE = 10 -P_BODY = 20 -class text_edit_panel(wx.Panel): - def __init__(self, parent, handler): - wx.Panel.__init__(self, parent, -1) - self.handler = handler - sizer = wx.BoxSizer(wx.VERTICAL) - self.text = { P_TITLE : wx.TextCtrl(self, P_TITLE, handler.master_dom.getAttribute('name')), - P_BODY : html_text_edit(self,P_BODY,handler.text._get_nodeValue(),self.on_text) - } - #P_BODY : wx.TextCtrl(self, P_BODY,handler.text._get_nodeValue(), style=wx.TE_MULTILINE) - - sizer.Add(wx.StaticText(self, -1, "Title:"), 0, wx.EXPAND) - sizer.Add(self.text[P_TITLE], 0, wx.EXPAND) - sizer.Add(wx.StaticText(self, -1, "Text Body:"), 0, wx.EXPAND) - sizer.Add(self.text[P_BODY], 1, wx.EXPAND) - self.sizer = sizer - self.outline = wx.StaticBox(self,-1,"Text Block") - self.Bind(wx.EVT_TEXT, self.on_text, id=P_TITLE) - - def on_text(self,evt): - id = evt.GetId() - if id == P_TITLE: - txt = self.text[id].GetValue() - # The following block strips out 8-bit characters - u_txt = "" - bad_txt_found = 0 - for c in txt: - if ord(c) < 128: - u_txt += c - else: - bad_txt_found = 1 - if bad_txt_found: - wx.MessageBox("Some non 7-bit ASCII characters found and stripped","Warning!") - txt = u_txt - if txt != "": - self.handler.master_dom.setAttribute('name',txt) - self.handler.rename(txt) - elif id == P_BODY: - txt = self.text[id].get_text() - u_txt = "" - bad_txt_found = 0 - for c in txt: - if ord(c) < 128: - u_txt += c - else: - bad_txt_found = 1 - - if bad_txt_found: - wx.MessageBox("Some non 7-bit ASCII characters found and stripped","Warning!") - txt = u_txt - self.handler.text._set_nodeValue(txt) - - - -########################## -## node loader -########################## -class node_loader(node_handler): - """ clones childe node and insert it at top of tree - - """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - - def on_rclick(self,evt): - pass - - def on_ldclick(self,evt): - title = self.master_dom.getAttribute('name') - new_node = self.master_dom._get_firstChild() - new_node = new_node.cloneNode(True) - child = self.tree.master_dom._get_firstChild() - new_node = self.tree.master_dom.insertBefore(new_node,child) - tree_node = self.tree.load_xml(new_node,self.tree.root,self.tree.root) - obj = self.tree.GetPyData(tree_node) - return 1 - #obj.on_design(None) - -########################## -## file loader -########################## - -class file_loader(node_handler): - """ loads file and insert into game tree - - - - """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.file_node = self.master_dom._get_firstChild() - self.frame = open_rpg.get_component('frame') - - def on_ldclick(self,evt): - file_name = self.file_node.getAttribute("name") - self.tree.insert_xml(open(orpg.dirpath.dir_struct["nodes"] + file_name,"r").read()) - return 1 - - def on_design(self,evt): - tlist = ['Title','File Name'] - vlist = [self.master_dom.getAttribute("name"), - self.file_node.getAttribute("name")] - dlg = orpgMultiTextEntry(self.tree.GetParent(),tlist,vlist,"File Loader Edit") - if dlg.ShowModal() == wx.ID_OK: - vlist = dlg.get_values() - self.file_node.setAttribute('name', vlist[1]) - self.master_dom.setAttribute('name', vlist[0]) - self.tree.SetItemText(self.mytree_node,vlist[0]) - dlg.Destroy() - -########################## -## URL loader -########################## - -class url_loader(node_handler): - """ loads file from url and insert into game tree - - - - """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.file_node = self.master_dom._get_firstChild() - self.frame = open_rpg.get_component('frame') - - def on_ldclick(self,evt): - file_name = self.file_node.getAttribute("url") - file = urllib.urlopen(file_name) - self.tree.insert_xml(file.read()) - return 1 - - def on_design(self,evt): - tlist = ['Title','URL'] - print "design filename",self.master_dom.getAttribute('name') - vlist = [self.master_dom.getAttribute("name"), - self.file_node.getAttribute("url")] - dlg = orpgMultiTextEntry(self.tree.GetParent(),tlist,vlist,"File Loader Edit") - if dlg.ShowModal() == wx.ID_OK: - vlist = dlg.get_values() - self.file_node.setAttribute('url', vlist[1]) - self.master_dom.setAttribute('name', vlist[0]) - self.tree.SetItemText(self.mytree_node,vlist[0]) - dlg.Destroy() - - -########################## -## minature map loader -########################## -class min_map(node_handler): - """ clones childe node and insert it at top of tree - - """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.map = open_rpg.get_component('map') - self.mapdata = self.master_dom._get_firstChild() - - def on_ldclick(self,evt): - self.map.new_data(toxml(self.mapdata)) - return 1 +# 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. +# -- +# +# File: core.py +# Author: Chris Davis +# Maintainer: +# Version: +# $Id: core.py,v 1.49 2007/12/07 20:39:48 digitalxero Exp $ +# +# Description: The file contains code for the core nodehanlers +# + +__version__ = "$Id: core.py,v 1.49 2007/12/07 20:39:48 digitalxero Exp $" + +from nodehandler_version import NODEHANDLER_VERSION + +try: + from orpg.orpg_windows import * + from orpg.dirpath import dir_struct + from orpg.orpgCore import component + import webbrowser, os + from orpg.mapper import map + from wx import DisplaySize +except: + import wx + +from xml.etree.ElementTree import ElementTree, Element, tostring, XML +from orpg.tools.orpg_log import debug + +#html defaults +TH_BG = "#E9E9E9" +########################## +## base node handler +########################## +class node_handler: + """ Base nodehandler with virtual functions and standard implmentations """ + def __init__(self,xml,tree_node): + self.xml = xml + self.mytree_node = tree_node + self.tree = open_rpg.get_component('tree') + self.frame = open_rpg.get_component('frame') + self.chat = open_rpg.get_component('chat') + self.drag = True + self.myeditor = None # designing + self.myviewer = None # prett print + self.mywindow = None # using + # call version hook + self.on_version(self.xml.get("version")) + # set to current version + self.xml.set("version",NODEHANDLER_VERSION) + # null events + self.frame_size = None + self.frame_pos = None + try: + frame = self.xml.get("frame") + if len(frame): + (sx,sy,px,py) = [int(value) for value in frame.split(',')] + self.frame_size = (sx, sy) + (maxx, maxy) = DisplaySize() + if px < maxx-80 and py < maxy-50: self.frame_pos = (px, py) #if it's off screen ignore the saved pos + except: pass + + def on_version(self,old_version): + ## added version control code here or implement a new on_version in your derived class. + ## always call the base class on_version ! + pass + + def on_rclick(self,evt): + self.tree.do_std_menu(evt,self) + + def on_ldclick(self,evt): + return 0 + + def usefulness(self,text): + if text=="useful": self.xml.set('status',"useful") + elif text=="useless": self.xml.set('status',"useless") + elif text=="indifferent": self.xml.set('status',"indifferent") + + def on_design(self,evt): + try: + self.myeditor.Show() + self.myeditor.Raise() + except: + del self.myeditor + if self.create_designframe(): + self.myeditor.Show() + self.myeditor.Raise() + else: return + wx.CallAfter(self.myeditor.Layout) + + def create_designframe(self): + title = self.xml.get('name') + " Editor" + self.myeditor = wx.Frame(None, -1, title) + self.myeditor.Freeze() + if wx.Platform == '__WXMSW__': + icon = wx.Icon(orpg.dirpath.dir_struct["icon"] + 'grid.ico', wx.BITMAP_TYPE_ICO) + self.myeditor.SetIcon(icon) + del icon + + self.myeditor.panel = self.get_design_panel(self.myeditor) + if self.myeditor.panel == None: + self.myeditor.Destroy() + return False + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(self.myeditor.panel, 1, wx.EXPAND) + + self.myeditor.SetSizer(sizer) + self.myeditor.SetAutoLayout(True) + + (x, y) = self.myeditor.GetSize() + if x < 400: x = 400 + if y < 400: y = 400 + + self.myeditor.SetSize((x, y)) + self.myeditor.Layout() + self.myeditor.Thaw() + return True + + def on_use(self,evt): + try: + self.mywindow.Show() + self.mywindow.Raise() + except: + del self.mywindow + if self.create_useframe(): + self.mywindow.SetSize(self.frame_size) + if self.frame_pos: self.mywindow.SetPosition(self.frame_pos) + self.mywindow.Show() + self.mywindow.Raise() + else: return + wx.CallAfter(self.mywindow.Layout) + + def create_useframe(self): + caption = self.xml.get('name', '') + self.mywindow = wx.Frame(None, -1, caption) + self.mywindow.Freeze() + if wx.Platform == '__WXMSW__': + icon = wx.Icon(orpg.dirpath.dir_struct["icon"] + 'note.ico', wx.BITMAP_TYPE_ICO) + self.mywindow.SetIcon(icon) + del icon + self.mywindow.panel = self.get_use_panel(self.mywindow) + if self.mywindow.panel == None: + self.mywindow.Destroy() + return False + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(self.mywindow.panel, 2, wx.EXPAND) + + self.mywindow.SetSizer(sizer) + self.mywindow.SetAutoLayout(True) + + if self.frame_size is None: + self.frame_size = self.mywindow.GetSize() + if self.frame_size.x < 400: self.frame_size.x = 400 + if self.frame_size.y < 400: self.frame_size.y = 400 + + self.mywindow.Layout() + self.mywindow.Thaw() + self.mywindow.Bind(wx.EVT_CLOSE, self.close_useframe) + return True + + def close_useframe(self, evt): + self.frame_size = self.mywindow.GetSize() + self.frame_pos = self.mywindow.GetPosition() + frame_values = str(self.frame_size.x)+','+str(self.frame_size.y)+','+str(self.frame_pos.x)+','+str(self.frame_pos.y) + self.xml.set("frame", frame_values) + self.mywindow.Destroy() + + + def on_html_view(self,evt): + try: self.myviewer.Raise() + except: + caption = self.xml.get('name') + self.myviewer = wx.Frame(None, -1, caption) + if wx.Platform == '__WXMSW__': + icon = wx.Icon(orpg.dirpath.dir_struct["icon"] + 'grid.ico', wx.BITMAP_TYPE_ICO) + self.myviewer.SetIcon(icon) + del icon + self.myviewer.panel = self.get_html_panel(self.myviewer) + self.myviewer.Show() + + def map_aware(self): + return 0 + + def can_clone(self): + return 1; + + def on_del(self,evt): + print "on del" + + def on_new_data(self,xml): + pass + + def get_scaled_bitmap(self,x,y): + return None + + def on_send_to_map(self,evt): + pass + + def on_send_to_chat(self,evt): + self.chat.ParsePost(self.tohtml(),True,True) + + def on_drop(self, evt): + drag_obj = self.tree.drag_obj + if drag_obj == self or self.tree.is_parent_node(self.mytree_node, drag_obj.mytree_node): + return + drop_xml = self.tree.drag_obj.delete() + parent_node = self.tree.GetItemParent(self.mytree_node) + prev_sib = self.tree.GetPrevSibling(self.mytree_node) + if parent_node == self.tree.root: parent_xml = self.tree.GetPyData(parent_node) + else: parent_xml = self.tree.GetPyData(parent_node).xml + for i in range(len(parent_xml)): + if parent_xml[i] is self.xml: + parent_xml.insert(i, drop_xml) + break + if not prev_sib.IsOk(): + prev_sib = parent_node + self.tree.load_xml(drop_xml, parent_node, prev_sib) + + def get_tree(self): + family = [] + test = treenode + while test != self.tree.root: + test = self.tree.GetItemParent(test) + parent = self.tree.GetItemText(test) + family.append(parent) + return family + + def toxml(self,pretty=0): + return tostring(self.xml) #toxml(self.master_dom,pretty) + + def tohtml(self): + return self.xml.get("name") + + def delete(self): + """ removes the tree_node and xml_node, and returns the removed xml_node """ + parent_node = self.tree.GetItemParent(self.mytree_node) + if parent_node == self.tree.root: parent_xml = self.tree.GetPyData(parent_node) + else: parent_xml = self.tree.GetPyData(parent_node).xml + parent_xml.remove(self.xml) + self.tree.Delete(self.mytree_node) + return self.xml + + def rename(self,name): + if len(name): + self.tree.SetItemText(self.mytree_node,name) + self.xml.set('name', name) + + def change_icon(self,icon): + self.xml.set("icon",icon) + self.tree.SetItemImage(self.mytree_node, self.tree.icons[icon]) + self.tree.SetItemImage(self.mytree_node, self.tree.icons[icon], wx.TreeItemIcon_Selected) + self.tree.Refresh() + + def on_save(self,evt): + f = wx.FileDialog(self.tree,"Select a file", orpg.dirpath.dir_struct["user"],"","XML files (*.xml)|*.xml",wx.SAVE) + if f.ShowModal() == wx.ID_OK: ElementTree(self.xml).write(f.GetPath()) + f.Destroy() + + def get_design_panel(self,parent): + return None + + def get_use_panel(self,parent): + return None + + def get_html_panel(self,parent): + html_str = ""+self.tohtml()+"" + wnd = wx.html.HtmlWindow(parent,-1) + html_str = self.chat.ParseDice(html_str) + wnd.SetPage(html_str) + return wnd + + def get_size_constraint(self): + return 0 + + def about(self): + """html_str = ""+ self.xml.get('class') + html_str += " Applet
by Chris Davis
chris@rpgarchive.com" + return html_str""" + text = self.xml.get('class') + 'Applet\n' + text += 'by Chris Davis chris@rpgarchive.com' + return text + + def get_value(self): + return None + +P_TITLE = 10 +P_BODY = 20 + +class text_edit_panel(wx.Panel): + def __init__(self, parent, handler): + wx.Panel.__init__(self, parent, -1) + self.handler = handler + sizer = wx.BoxSizer(wx.VERTICAL) + self.text = { P_TITLE : wx.TextCtrl(self, P_TITLE, handler.xml.get('name')), + P_BODY : html_text_edit(self,P_BODY,handler.text,self.on_text) + } + sizer.Add(wx.StaticText(self, -1, "Title:"), 0, wx.EXPAND) + sizer.Add(self.text[P_TITLE], 0, wx.EXPAND) + sizer.Add(wx.StaticText(self, -1, "Text Body:"), 0, wx.EXPAND) + sizer.Add(self.text[P_BODY], 1, wx.EXPAND) + self.sizer = sizer + self.outline = wx.StaticBox(self,-1,"Text Block") + self.Bind(wx.EVT_TEXT, self.on_text, id=P_TITLE) + + def on_text(self,evt): + id = evt.GetId() + if id == P_TITLE: + txt = self.text[id].GetValue() + # The following block strips out 8-bit characters + u_txt = "" + bad_txt_found = 0 + for c in txt: + if ord(c) < 128: u_txt += c + else: bad_txt_found = 1 + if bad_txt_found: + wx.MessageBox("Some non 7-bit ASCII characters found and stripped","Warning!") + txt = u_txt + if txt != "": + self.handler.xml.set('name',txt) + self.handler.rename(txt) + elif id == P_BODY: + txt = self.text[id].get_text() + u_txt = "" + bad_txt_found = 0 + for c in txt: + if ord(c) < 128: u_txt += c + else: bad_txt_found = 1 + if bad_txt_found: + wx.MessageBox("Some non 7-bit ASCII characters found and stripped","Warning!") + txt = u_txt + self.handler.text._set_nodeValue(txt) + + + +########################## +## node loader +########################## +class node_loader(node_handler): + """ clones childe node and insert it at top of tree + + """ + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) + + def on_rclick(self,evt): + pass + + def on_ldclick(self,evt): + title = self.xml.get('name') + new_xml = XML(tostring(self.xml[0])) + self.tree.root_xml.insert(0, new_xml) + tree_node = self.tree.load_xml(new_xml,self.tree.root,self.tree.root) + return 1 + +########################## +## file loader +########################## + +class file_loader(node_handler): + """ loads file and insert into game tree + + + + """ + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) + self.file_node = self.xml[0] + self.frame = open_rpg.get_component('frame') + + def on_ldclick(self,evt): + file_name = self.file_node.get("name") + self.tree.insert_xml(open(orpg.dirpath.dir_struct["nodes"] + file_name,"r").read()) + return 1 + + def on_design(self,evt): + tlist = ['Title','File Name'] + vlist = [self.xml.get("name"), + self.file_node.get("name")] + dlg = orpgMultiTextEntry(self.tree.GetParent(),tlist,vlist,"File Loader Edit") + if dlg.ShowModal() == wx.ID_OK: + vlist = dlg.get_values() + self.file_node.set('name', vlist[1]) + self.xml.set('name', vlist[0]) + self.tree.SetItemText(self.mytree_node,vlist[0]) + dlg.Destroy() + +########################## +## URL loader +########################## + +class url_loader(node_handler): + """ loads file from url and insert into game tree + + + + """ + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) + self.file_node = self.xml[0] + self.frame = open_rpg.get_component('frame') + + def on_ldclick(self,evt): + file_name = self.file_node.get("url") + file = urllib.urlopen(file_name) + self.tree.insert_xml(file.read()) + return 1 + + def on_design(self,evt): + tlist = ['Title','URL'] + print "design filename",self.xml.get('name') + vlist = [self.xml.get("name"), + self.file_node.get("url")] + dlg = orpgMultiTextEntry(self.tree.GetParent(),tlist,vlist,"File Loader Edit") + if dlg.ShowModal() == wx.ID_OK: + vlist = dlg.get_values() + self.file_node.set('url', vlist[1]) + self.xml.set('name', vlist[0]) + self.tree.SetItemText(self.mytree_node,vlist[0]) + dlg.Destroy() + + +########################## +## minature map loader +########################## +class min_map(node_handler): + """ clones childe node and insert it at top of tree + + """ + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) + self.map = open_rpg.get_component('map') + self.mapdata = self.xml[0] + + def on_ldclick(self,evt): + self.map.new_data(tostring(self.mapdata)) + return 1 diff -r 8e77f169f324 -r fc48380f0c9f orpg/gametree/nodehandlers/forms.py --- a/orpg/gametree/nodehandlers/forms.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/gametree/nodehandlers/forms.py Mon May 03 03:30:11 2010 -0500 @@ -109,11 +109,11 @@ F_HEIGHT = wx.NewId() F_WIDTH = wx.NewId() -class form_edit_panel(wx.Panel): +class form_edit_panel(ScrolledPanel): def __init__(self, parent, handler): - wx.Panel.__init__(self, parent, -1) + ScrolledPanel.__init__(self, parent, wx.ID_ANY, style=wx.NO_BORDER|wx.VSCROLL|wx.HSCROLL) self.handler = handler - sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, "Form Properties"), wx.VERTICAL) + self.main_sizer = wx.BoxSizer(wx.VERTICAL) wh_sizer = wx.BoxSizer(wx.HORIZONTAL) self.text = { P_TITLE : wx.TextCtrl(self, P_TITLE, handler.xml.get('name')), F_HEIGHT : wx.TextCtrl(self, F_HEIGHT, handler.xml.get('height')), @@ -128,15 +128,17 @@ wh_sizer.Add(wx.Size(10,10)) wh_sizer.Add(self.text[F_HEIGHT], 0, wx.EXPAND) - sizer.Add(wx.StaticText(self, -1, "Title:"), 0, wx.EXPAND) - sizer.Add(self.text[P_TITLE], 0, wx.EXPAND) - sizer.Add(wx.Size(10,10)) - sizer.Add(wh_sizer,0,wx.EXPAND) + self.main_sizer.Add(wx.StaticText(self, -1, "Title:"), 0, wx.EXPAND) + self.main_sizer.Add(self.text[P_TITLE], 0, wx.EXPAND) + self.main_sizer.Add(wx.Size(10,10)) + self.main_sizer.Add(wh_sizer,0,wx.EXPAND) + handler.tree.traverse(handler.mytree_node, self.create_child_wnd, None, False) - self.SetSizer(sizer) + self.SetSizer(self.main_sizer) self.SetAutoLayout(True) + self.SetupScrolling() + parent.SetSize(self.GetSize()) self.Fit() - parent.SetSize(self.GetBestSize()) self.Bind(wx.EVT_TEXT, self.on_text, id=P_TITLE) self.Bind(wx.EVT_TEXT, self.on_text, id=F_HEIGHT) @@ -155,6 +157,14 @@ if id == F_HEIGHT: self.handler.xml.set("height",txt) elif id == F_WIDTH: self.handler.xml.set("width",txt) + def create_child_wnd(self, treenode, evt): + node = self.handler.tree.GetPyData(treenode) + panel = node.get_design_panel(self) + size = node.get_size_constraint() + if panel: + self.main_sizer.Add(panel, size, wx.EXPAND) + self.main_sizer.Add(wx.Size(10,10)) + ########################## ## control handler ########################## @@ -263,16 +273,16 @@ def on_send(self, evt): txt = self.text.GetValue() - txt = Parse.NodeMap(txt, self.handler.xml) - txt = Parse.NodeParent(txt, self.handler.xml.get('map')) + txt = Parse.ParseLogic(txt, self.handler.xml) if not self.handler.is_raw_send(): - Parse.Post(self.handler.tohtml(), True, True) + Parse.Post(self.handler.tohtml(), self.chat, True, True) return 1 actionlist = txt.split("\n") for line in actionlist: + line = Parse.ParseLogic(line, self.handler.xml) if(line != ""): if line[0] != "/": ## it's not a slash command - Parse.Post(line, True, True) + Parse.Post(line, self.chat, True, True) else: action = line self.chat.chat_cmds.docmd(action) @@ -320,7 +330,7 @@ sizer_style=wx.EXPAND text_style = 0 multi = 0 - self.text = wx.TextCtrl(self, F_TEXT, handler.get_value(),style=text_style) + self.text = wx.TextCtrl(self, F_TEXT, handler.get_value() or '',style=text_style) sizer.Add(wx.Size(5,0)) sizer.Add(self.text, multi, sizer_style) self.SetSizer(sizer) @@ -596,18 +606,16 @@ def on_send_to_chat(self, evt): txt = self.get_selected_text() - txt = Parse.NodeMap(txt, self.xml) - txt = Parse.NodeParent(txt, self.xml.get('map')) + txt = Parse.ParseLogic(txt, self.xml) if not self.is_raw_send(): - Parse.Post(self.tohtml(), True, True) + Parse.Post(self.tohtml(), self.chat, True, True) return 1 actionlist = self.get_selections_text() for line in actionlist: - line = Parse.NodeMap(line, self.xml) - line = Parse.NodeParent(line, self.xml.get('map')) + line = Parse.ParseLogic(line, self.xml) if(line != ""): if line[0] != "/": ## it's not a slash command - Parse.Post(line, True, True) + Parse.Post(line, self.chat, True, True) else: action = line self.chat.chat_cmds.docmd(action) @@ -621,6 +629,7 @@ class listbox_panel(wx.Panel): def __init__(self, parent, handler): wx.Panel.__init__(self, parent, -1) + #ScrolledPanel.__init__(self, parent, wx.ID_ANY, style=wx.NO_BORDER|wx.VSCROLL|wx.HSCROLL) self.handler = handler self.chat = handler.chat opts = [] @@ -638,8 +647,8 @@ if self.list.GetSize()[0] > 200: self.list.Destroy() self.list = wx.ComboBox(self, F_LIST, cur_opt, size=(200, -1), choices=opts, style=wx.CB_READONLY) - elif type == L_LIST: self.list = wx.ListBox(self,F_LIST,choices=opts) - elif type == L_RADIO: self.list = wx.RadioBox(self,F_LIST,label,choices=opts,majorDimension=3) + elif type == L_LIST: self.list = wx.ListBox(self, F_LIST, choices=opts) + elif type == L_RADIO: self.list = wx.RadioBox(self, F_LIST, label, choices=opts, majorDimension=3) elif type == L_CHECK: self.list = wx.CheckListBox(self,F_LIST,choices=opts) self.set_checks() @@ -651,17 +660,17 @@ else: sizer = wx.BoxSizer(wx.VERTICAL) if type != L_RADIO: - sizer.Add(wx.StaticText(self, -1, label+": "), 0, wx.EXPAND) - sizer.Add(wx.Size(5,0)) - sizer.Add(self.list, 1, wx.EXPAND) + sizer.Add(wx.StaticText(self, -1, label+": "), 0, wx.EXPAND|wx.ALL) + sizer.Add(self.list, 1, wx.EXPAND|wx.ALL) if handler.has_send_button(): - sizer.Add(wx.Button(self, F_SEND, "Send"), 0, wx.EXPAND) + sizer.Add(wx.Button(self, F_SEND, "Send"), 0, wx.EXPAND|wx.ALL) self.Bind(wx.EVT_BUTTON, self.handler.on_send_to_chat, id=F_SEND) self.sizer = sizer self.SetSizer(sizer) self.SetAutoLayout(True) + #self.SetupScrolling() + #parent.SetSize(self.GetBestSize()) self.Fit() - parent.SetSize(self.GetBestSize()) if type == L_DROP: self.Bind(wx.EVT_COMBOBOX, self.on_change, id=F_LIST) elif type == L_LIST: self.Bind(wx.EVT_LISTBOX, self.on_change, id=F_LIST) diff -r 8e77f169f324 -r fc48380f0c9f orpg/gametree/nodehandlers/minilib.py --- a/orpg/gametree/nodehandlers/minilib.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/gametree/nodehandlers/minilib.py Mon May 03 03:30:11 2010 -0500 @@ -142,7 +142,7 @@ mini = Element( TAG_MINIATURE ) for key in data.keys(): mini.set( key, data[ key ] ) for key in CORE_ATTRIBUTES: - if mini.get( key ) == '': mini.set( key, '0' ) + if mini.get( key ) == ('' or None): mini.set( key, '0' ) if add: self.add_mini( mini ) self.add_leaf( mini ) @@ -314,6 +314,11 @@ self.frame = frame self.sizer = wx.BoxSizer( wx.VERTICAL ) + + self.text = wx.TextCtrl(self, 3, handler.xml.get('name')) + self.sizer.Add(wx.StaticText(self, -1, "Title:"), 0, wx.EXPAND) + self.sizer.Add(self.text, 0, wx.EXPAND) + self.grid = minilib_grid( self, handler ) bbox = wx.BoxSizer( wx.HORIZONTAL ) @@ -333,6 +338,7 @@ self.SetAutoLayout(True) self.Fit() + self.Bind(wx.EVT_TEXT, self.on_text, id=3) self.Bind(wx.EVT_BUTTON, self.add_mini, newMiniBtn) self.Bind(wx.EVT_BUTTON, self.del_mini, delMiniBtn) self.Bind(wx.EVT_BUTTON, self.send_to_map, addMiniBtn) @@ -354,7 +360,7 @@ """Event handler for the 'Add 1' button. Sends the miniature defined by the currently selected row to the map, once. """ - index = self.grid.GetGridCursorRow() + index = self.grid.GetSelectedRows()[0] if len(self.grid.GetSelectedRows()) > 0 else 0 self.handler.send_mini_to_map( self.handler.get_mini( index ) ) def send_group_to_map( self, evt=None ): @@ -370,10 +376,16 @@ try: value = eval( dlg.GetValue() ) except: value = 0 print 'getting selected index for batch send' - index = self.grid.GetGridCursorRow() + index = self.grid.GetSelectedRows()[0] if len(self.grid.GetSelectedRows()) > 0 else 0 print 'sending batch to map' self.handler.send_mini_to_map( self.handler.get_mini( index ), value ) + def on_text(self, evt): + txt = self.text.GetValue() + if txt != "": + self.handler.xml.set('name',txt) + self.handler.rename(txt) + class minilib_grid(wx.grid.Grid): """A wxGrid subclass designed for editing game tree miniature library nodes. @@ -392,6 +404,7 @@ self.AutoSizeColumns() self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.select_cell) + self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.select_cell) def update_cols( self ): for n in self.handler.xml.findall(TAG_MINIATURE): @@ -436,8 +449,6 @@ list = self.handler.xml.findall(TAG_MINIATURE) self.handler.xml.remove( list[pos] ) self.DeleteRows( pos, 1 ) - list = self.getList() - del list[ pos ] def on_cell_change( self, evt ): """Event handler for cell selection changes. selected row is used diff -r 8e77f169f324 -r fc48380f0c9f orpg/gametree/nodehandlers/minilib.py~ --- a/orpg/gametree/nodehandlers/minilib.py~ Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/gametree/nodehandlers/minilib.py~ Mon May 03 03:30:11 2010 -0500 @@ -1,578 +1,543 @@ -# 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. -# -- -# -# File: minilib.py -# Author: Ted Berg -# Maintainer: -# Version: -# $Id: minilib.py,v 1.28 2007/04/22 22:00:18 digitalxero Exp $ -# -# Description: nodehandler for a collection of miniatures. -# - -__version__ = "$Id: minilib.py,v 1.28 2007/04/22 22:00:18 digitalxero Exp $" - -"""Nodehandler for collections of miniatures. User can add, delete, edit -miniatures as sending them to the map singly or in batches. -""" -from core import * -import orpg.dirpath -import string -import map_miniature_nodehandler +# 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. +# -- +# +# File: minilib.py +# Author: Ted Berg +# Maintainer: +# Version: +# $Id: minilib.py,v 1.28 2007/04/22 22:00:18 digitalxero Exp $ +# +# Description: nodehandler for a collection of miniatures. +# + +__version__ = "$Id: minilib.py,v 1.28 2007/04/22 22:00:18 digitalxero Exp $" + +"""Nodehandler for collections of miniatures. User can add, delete, edit +miniatures as sending them to the map singly or in batches. +""" +from core import * +from orpg.dirpath import dir_struct +import string +import map_miniature_nodehandler import orpg.mapper.map_msg -from orpg.mapper.images import ImageHandler -# import scriptkit - -# Constants -TO_MINILIB_MAP = {'path':'url', 'label':'name', 'id':None, 'action':None} -FROM_MINILIB_MAP = {'url':'path', 'name':'label', 'unique':None} -CORE_ATTRIBUTES = ['name', 'url', 'unique', 'posy', 'posx', 'hide', 'face', 'heading', 'align', 'locked', 'width', 'height'] - -ATTRIBUTE_NAME = 'name' -ATTRIBUTE_URL = 'url' -ATTRIBUTE_UNIQUE = 'unique' -ATTRIBUTE_ID = 'id' -ATTRIBUTE_POSX = 'posx' -ATTRIBUTE_POSY = 'posy' - -TAG_MINIATURE = 'miniature' - -COMPONENT_MAP = 'map' -COMPONENT_SESSION = 'session' -# -# -# - -class minilib_handler( node_handler ): - """A nodehandler that manages a collection of miniatures for the - map. -
-        <nodehandler name='?' module='minilib' class='minilib_handler'>
-            <miniature name='?' url='?' unique='?'></miniature>
-        </nodehandler>
-    
- """ - def __init__(self, xml_dom, tree_node): - """Instantiates the class, and sets all vars to their default state - """ - node_handler.__init__(self, xml_dom, tree_node) - - self.myeditor = None - self.mywindow = None - self.tree_node = tree_node - # self.xml_dom = xml_dom - self.update_leaves() - self.sanity_check_nodes() - - def get_design_panel( self, parent ): - """returns an instance of the miniature library edit control ( see - on_design ). This is for use with the the 'edit multiple nodes in a - single frame' code. - """ - return minpedit( parent, self ) - - def get_use_panel( self, parent ): - """returns an instance of the miniature library view control ( see - on_use ). This is for use with the the 'view multiple nodes in a - single frame' code. - """ - return minilib_use_panel( parent, self ) - - def tohtml( self ): - """Returns an HTML representation of this node in string format. - The table columnwidths are currently being forced, as the wxHTML - widgets being used don't handle cells wider than the widgets are - expecting for a given column. - """ - str = '' - list = self.master_dom.getElementsByTagName(TAG_MINIATURE) - str += "" - for mini in list: - url = mini.getAttribute(ATTRIBUTE_URL) - label = mini.getAttribute(ATTRIBUTE_NAME) - flag = 0 - try: - flag = eval( mini.getAttribute(ATTRIBUTE_UNIQUE) ) - except: - pass - - show = 'yes' - if flag: - show = 'no' - - str += """ - - - - - """ % ( label, url, url, show ) - - str += "
LabelImageURLUnique
%s %s %s
" - print str - return str - - def html_view( self ): - """see to_html - """ - return self.tohtml() - - def on_drop(self, evt): - drag_obj = self.tree.drag_obj - if drag_obj == self or self.tree.is_parent_node( self.mytree_node, drag_obj.mytree_node ): - return - if isinstance( drag_obj, minilib_handler ): - item = self.tree.GetSelection() - name = self.tree.GetItemText( item ) - if isinstance( drag_obj, map_miniature_nodehandler.map_miniature_handler ): - xml_dom = self.tree.drag_obj.master_dom#.delete() - obj = xml_dom.firstChild - print obj.getAttributeKeys() - dict = {} - unique = '' - for attrib in obj.getAttributeKeys(): - key = TO_MINILIB_MAP.get( attrib, attrib ) - if key != None: - dict[ key ] = obj.getAttribute( attrib ) - dict[ ATTRIBUTE_UNIQUE ] = unique - self.new_mini( dict ) - - - def new_mini( self, data={}, add=1 ): - mini = minidom.Element( TAG_MINIATURE ) - for key in data.keys(): - mini.setAttribute( key, data[ key ] ) - for key in CORE_ATTRIBUTES: - if mini.getAttribute( key ) == '': - mini.setAttribute( key, '0' ) - if add: - self.add_mini( mini ) - self.add_leaf( mini ) - return mini - - def add_mini( self, mini ): - self.master_dom.appendChild( mini ) - - def add_leaf( self, mini, icon='gear' ): - tree = self.tree - icons = tree.icons - key = mini.getAttribute( ATTRIBUTE_NAME ) - self.mydata.append( mini ) - - def update_leaves( self ): - self.mydata = [] - nl = self.master_dom.getElementsByTagName( TAG_MINIATURE ) - for n in nl: - self.add_leaf( n ) - - - def on_drag( self, evt ): - print 'drag event caught' - - def send_mini_to_map( self, mini, count=1, addName=True ): - if mini == None: - return - if mini.getAttribute( ATTRIBUTE_URL ) == '' or mini.getAttribute( ATTRIBUTE_URL ) == 'http://': - self.chat.ParsePost( self.chat.colorize(self.chat.syscolor, '"%s" is not a valid URL, the mini "%s" will not be added to the map' % ( mini.getAttribute( ATTRIBUTE_URL ), mini.getAttribute( ATTRIBUTE_NAME ) )) ) - return - session = open_rpg.get_component( COMPONENT_SESSION ) - if (session.my_role() != session.ROLE_GM) and (session.my_role() != session.ROLE_PLAYER): - open_rpg.get_component("chat").InfoPost("You must be either a player or GM to use the miniature Layer") - return - map = open_rpg.get_component(COMPONENT_MAP) - for loop in range( count ): - msg = self.get_miniature_XML( mini, addName) - msg = str("" + msg + "") - map.new_data( msg ) - session.send( msg ) - - def get_miniature_XML( self, mini, addName = True ): - msg = orpg.mapper.map_msg.mini_msg() - map = open_rpg.get_component( COMPONENT_MAP ) - session = open_rpg.get_component( COMPONENT_SESSION ) - msg.init_prop( ATTRIBUTE_ID, session.get_next_id() ) - for k in mini.getAttributeKeys(): - # translate our attributes to map attributes - key = FROM_MINILIB_MAP.get( k, k ) - if key != None: - if not addName and k == 'name': - pass - else: - msg.init_prop( key, mini.getAttribute( k ) ) - unique = self.is_unique( mini ) - if addName: - label = mini.getAttribute( ATTRIBUTE_NAME ) - else: - label = '' - return msg.get_all_xml() - - def is_unique( self, mini ): - unique = mini.getAttribute( ATTRIBUTE_UNIQUE ) - val = 0 - try: - val = eval( unique ) - except: - val = len( unique ) - return val - - def sanity_check_nodes( self ): - nl = self.master_dom.getElementsByTagName( TAG_MINIATURE ) - for node in nl: - if node.getAttribute( ATTRIBUTE_POSX ) == '': - node.setAttribute( ATTRIBUTE_POSX, '0' ) - if node.getAttribute( ATTRIBUTE_POSY ) == '': - node.setAttribute( ATTRIBUTE_POSY, '0' ) - - def get_mini( self, index ): - try: - nl = self.master_dom.getElementsByTagName( TAG_MINIATURE ) - return nl[ index ] - except: - return None - -class mini_handler( node_handler ): - def __init__( self, xml_dom, tree_node, handler ): - node_handler.__init__( self, xml_dom, tree_node) - self.handler = handler - - def on_ldclick( self, evt ): - self.handler.send_mini_to_map( self.master_dom ) - - def on_drop( self, evt ): - pass - - def on_lclick( self, evt ): - print 'hi' - evt.Skip() - -class minilib_use_panel(wx.Panel): - """This panel will be displayed when the user double clicks on the - miniature library node. It is a sorted listbox of miniature labels, - a text field for entering a count ( for batch adds ) and 'add'/'done' - buttons. - """ - def __init__( self, frame, handler ): - """Constructor. - """ - wx.Panel.__init__( self, frame, -1 ) - self.handler = handler - self.frame = frame - - self.map = open_rpg.get_component('map') - names = self.buildList() - # self.keys = self.list.keys() - # self.keys.sort() - - - s = self.GetClientSizeTuple() - - self.sizer = wx.BoxSizer(wx.VERTICAL) - box = wx.BoxSizer(wx.HORIZONTAL) - self.listbox = wx.BitmapButton(self, wx.ID_ANY, names, wx.LB_EXTENDED) - self.count = wx.TextCtrl(self, wx.ID_ANY, '1') - - box.Add( wx.StaticText( self, -1, 'Minis to add' ), 0, wx.EXPAND ) - box.Add(wx.Size(10,10)) - box.Add(self.count, 1, wx.EXPAND) - - self.sizer.Add( self.listbox, 1, wx.EXPAND ) - self.sizer.Add( box, 0, wx.EXPAND ) - - box = wx.BoxSizer( wx.HORIZONTAL ) - self.okBtn = wx.Button(self, wx.ID_ANY, 'Add') - box.Add(self.okBtn, 0, wx.EXPAND) - self.addBtn = wx.Button(self, wx.ID_ANY, 'Add No Label') - box.Add(self.addBtn, 0, wx.EXPAND) - self.cancleBtn = wx.Button(self, wx.ID_ANY, 'Done') - box.Add(self.cancleBtn, 0, wx.EXPAND) - - self.sizer.Add(wx.Size(10,10)) - self.sizer.Add(box, 0, wx.EXPAND) - self.Bind(wx.EVT_BUTTON, self.on_ok, self.okBtn) - self.Bind(wx.EVT_BUTTON, self.on_ok, self.addBtn) - self.Bind(wx.EVT_BUTTON, self.on_close, self.cancleBtn) - self.SetSizer(self.sizer) - self.SetAutoLayout(True) - self.Fit() - - def buildList( self ): - """Returns a dictionary of label => game tree miniature DOM node mappings. - """ - list = self.handler.master_dom.getElementsByTagName(TAG_MINIATURE) - self.list = [] - for mini in list: - min_path = mini.getAttribute( ATTRIBUTE_URL ) - min_img = ImageHandler.load( min_path, 'miniature', 1) - self.list.append( mini.getAttribute( min_img ) ) - return self.list - # self.list = {} - # for mini in list: - # name = mini.getAttribute( ATTRIBUTE_NAME ) - # if name == '': - # name = self.map.canvas.get_label_from_url( mini.getAttribute( ATTRIBUTE_URL ) ) - # self.list[ name ] = mini - - def on_close(self, evt): - self.frame.Close() - - def on_ok( self, evt ): - """Event handler for the 'add' button. - """ - btn = self.FindWindowById(evt.GetId()) - sendName = True - try: - count = eval( self.count.GetValue() ) - except: - count = 1 - - try: - if eval( unique ): - count = 1 - unique = eval( unique ) - except: - pass - - if btn.GetLabel() == 'Add No Label': - sendName = False - for index in self.listbox.GetSelections(): - self.handler.send_mini_to_map( self.handler.get_mini( index ), count, sendName ) - - -class minpedit(wx.Panel): - """Panel for editing game tree miniature nodes. Node information - is displayed in a grid, and buttons are provided for adding, deleting - nodes, and for sending minis to the map ( singly and in batches ). - """ - def __init__( self, frame, handler ): - """Constructor. - """ - wx.Panel.__init__( self, frame, -1 ) - self.handler = handler - self.frame = frame - - self.sizer = wx.BoxSizer( wx.VERTICAL ) - self.grid = minilib_grid( self, handler ) - - bbox = wx.BoxSizer( wx.HORIZONTAL ) - newMiniBtn = wx.Button( self, wx.ID_ANY, "New mini" ) - delMiniBtn = wx.Button( self, wx.ID_ANY, "Del mini" ) - addMiniBtn = wx.Button( self, wx.ID_ANY, "Add 1" ) - addBatchBtn = wx.Button( self, wx.ID_ANY, "Add Batch" ) - bbox.Add(newMiniBtn, 0, wx.EXPAND ) - bbox.Add(delMiniBtn, 0, wx.EXPAND ) - bbox.Add(wx.Size(10,10)) - bbox.Add(addMiniBtn, 0, wx.EXPAND ) - bbox.Add(addBatchBtn, 0, wx.EXPAND ) - - self.sizer.Add( self.grid, 1, wx.EXPAND) - self.sizer.Add( bbox, 0) - self.SetSizer(self.sizer) - self.SetAutoLayout(True) - self.Fit() - - self.Bind(wx.EVT_BUTTON, self.add_mini, newMiniBtn) - self.Bind(wx.EVT_BUTTON, self.del_mini, delMiniBtn) - self.Bind(wx.EVT_BUTTON, self.send_to_map, addMiniBtn) - self.Bind(wx.EVT_BUTTON, self.send_group_to_map, addBatchBtn) - - def add_mini( self, evt=None ): - """Event handler for the 'New mini' button. It calls - minilib_grid.add_row - """ - self.grid.add_row() - - def del_mini( self, evt=None ): - """Event handler for the 'Del mini' button. It calls - minilib_grid.del_row - """ - self.grid.del_row() - - def send_to_map( self, evt=None ): - """Event handler for the 'Add 1' button. Sends the - miniature defined by the currently selected row to the map, once. - """ - index = self.grid.GetGridCursorRow() - self.handler.send_mini_to_map( self.handler.get_mini( index ) ) - - def send_group_to_map( self, evt=None ): - """Event handler for the 'Add batch' button. Querys the user - for a mini count and sends the miniature defined by the currently - selected row to the map, the specified number of times. - """ - if self.grid.GetNumberRows() > 0: - dlg = wx.TextEntryDialog( self.frame, - 'How many %s\'s do you want to add?' % - ( self.grid.getSelectedLabel() ), 'Batch mini add', '2' ) - if dlg.ShowModal() == wx.ID_OK: - try: - value = eval( dlg.GetValue() ) - except: - value = 0 - # for loop in range( 0, value ): - # self.send_to_map() - print 'getting selected index for batch send' - index = self.grid.GetGridCursorRow() - print 'sending batch to map' - self.handler.send_mini_to_map( self.handler.get_mini( index ), value ) - -class minilib_grid(wx.grid.Grid): - """A wxGrid subclass designed for editing game tree miniature library - nodes. - """ - def __init__( self, parent, handler ): - """Constructor. - """ - wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS ) - self.parent = parent - self.handler = handler - #self.keys = [ ATTRIBUTE_NAME, ATTRIBUTE_URL, ATTRIBUTE_UNIQUE ] - self.keys = CORE_ATTRIBUTES - self.CreateGrid( 1, len( self.keys ) ) - # self.SetColLabelValue( 0, 'Name' ) - # self.SetColLabelValue( 1, 'URL' ) - # self.SetColSize( 1, 250 ) - # self.SetColLabelValue( 2, 'Unique' ) - for key in self.keys: - self.SetColLabelValue( self.keys.index( key ), key ) - self.update_all() - self.selectedRow = 0 - self.AutoSizeColumns() - self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.select_cell) - - def update_cols( self ): - nl = self.handler.master_dom.getElementsByTagName( TAG_MINIATURE ) - for n in nl: - for k in n.getAttributeKeys(): - if k not in self.keys: - self.keys.append( k ) - - def select_cell( self, evt ): - """Event handler for grid cell selection changes. It stores the - last selected row in a variable for use by the add[*] and del_row - operations. - """ - self.BeginBatch() - self.selectedRow = evt.GetRow() - self.SelectRow( self.selectedRow ) - self.EndBatch() - evt.Skip() - - def getList( self ): - """Returns the list of 'miniature' DOM elements associated with this - miniature library. - """ - return self.handler.master_dom.getElementsByTagName( TAG_MINIATURE ) - - def add_row( self, count = 1 ): - """creates a new miniature node, and then adds it to the current - miniature library, and to the grid. - """ - self.AppendRows( count ) - node = self.handler.new_mini( { - ATTRIBUTE_NAME :' ', - ATTRIBUTE_URL :'http://'} )# minidom.Element( TAG_MINIATURE ) - self.update_all() - #self.handler.master_dom.appendChild( node ) - - def del_row( self ): - """deletes the miniature associated with the currently selected - row. BUG BUG BUG this method should drop a child from the DOM but - does not. - """ - if self.selectedRow > -1: - pos = self.selectedRow - list = self.handler.master_dom.getElementsByTagName(TAG_MINIATURE) - self.handler.master_dom.removeChild( list[pos] ) - self.DeleteRows( pos, 1 ) - list = self.getList() - del list[ pos ] - - def on_cell_change( self, evt ): - """Event handler for cell selection changes. selected row is used - to update data for that row. - """ - row = evt.GetRow() - self.update_data_row( row ) - - def update_all( self ): - """ensures that the grid is displaying the correct number of - rows, and then updates all data displayed by the grid - """ - list = self.getList() - count = 0 - for n in list: - for k in n.getAttributeKeys(): - if k not in self.keys: - self.keys.append( k ) - count = len( self.keys ) - if self.GetNumberCols() < count: - self.AppendCols( count - self.GetNumberCols() ) - for k in self.keys: - self.SetColLabelValue( self.keys.index( k ), k ) - count = len( list ) - rowcount = self.GetNumberRows() - if ( count > rowcount ): - total = count - rowcount - self.AppendRows( total ) - elif ( count < rowcount ): - total = rowcount - count - self.DeleteRows( 0, total ); - for index in range( 0, count ): - self.update_grid_row( index ) - - def getSelectedLabel( self ): - """Returns the label for the selected row - """ - return self.GetTable().GetValue( self.selectedRow, 0 ) - - def getSelectedURL( self ): - """Returns the URL for the selected row - """ - return self.GetTable().GetValue( self.selectedRow, 1 ) - - def getSelectedSerial( self ): - """Returns the ATTRIBUTE_UNIQUE value for the selected row - """ - return self.GetTable().GetValue( self.selectedRow, 2 ) - - def update_grid_row( self, row ): - """Updates the specified grid row with data from the DOM node - specified by 'row' - """ - list = self.getList() - item = list[ row ] - # self.GetTable().SetValue( row, 0, item.getAttribute(ATTRIBUTE_NAME) ) - # self.GetTable().SetValue( row, 1, item.getAttribute(ATTRIBUTE_URL) ) - # self.GetTable().SetValue( row, 2, item.getAttribute(ATTRIBUTE_UNIQUE) ) - for key in self.keys: - self.GetTable().SetValue( row, self.keys.index( key ), item.getAttribute( key ) ) - - def update_data_row( self, row ): - """Updates the DOM nodw 'row' with grid data from 'row' - """ - list = self.getList() - item = list[ row ] - for key in self.keys: - item.setAttribute( key, string.strip( self.GetTable().GetValue( row, self.keys.index( key ) ) ) ) - # item.setAttribute( ATTRIBUTE_NAME, string.strip( self.GetTable().GetValue( row, 0 ) ) ) - # item.setAttribute( ATTRIBUTE_URL, string.strip( self.GetTable().GetValue( row, 1 ) ) ) - # item.setAttribute( ATTRIBUTE_UNIQUE, string.strip( self.GetTable().GetValue( row, 2 ) ) ) - # self.GetTable().SetValue( row, 0, item.getAttribute(ATTRIBUTE_NAME) ) - # self.GetTable().SetValue( row, 1, item.getAttribute(ATTRIBUTE_URL) ) - # self.GetTable().SetValue( row, 2, item.getAttribute(ATTRIBUTE_UNIQUE) ) +import orpg.minidom as minidom +# import scriptkit + +# Constants +TO_MINILIB_MAP = {'path':'url', 'label':'name', 'id':None, 'action':None} +FROM_MINILIB_MAP = {'url':'path', 'name':'label', 'unique':None} +CORE_ATTRIBUTES = ['name', 'url', 'unique', 'posy', 'posx', 'hide', 'face', 'heading', 'align', 'locked', 'width', 'height'] + +ATTRIBUTE_NAME = 'name' +ATTRIBUTE_URL = 'url' +ATTRIBUTE_UNIQUE = 'unique' +ATTRIBUTE_ID = 'id' +ATTRIBUTE_POSX = 'posx' +ATTRIBUTE_POSY = 'posy' + +TAG_MINIATURE = 'miniature' + +COMPONENT_MAP = 'map' +COMPONENT_SESSION = 'session' +# +# +# + +class minilib_handler( node_handler ): + """A nodehandler that manages a collection of miniatures for the + map. +
+        <nodehandler name='?' module='minilib' class='minilib_handler'>
+            <miniature name='?' url='?' unique='?'></miniature>
+        </nodehandler>
+    
+ """ + def __init__(self, xml, tree_node): + """Instantiates the class, and sets all vars to their default state + """ + node_handler.__init__(self, xml, tree_node) + self.myeditor = None + self.mywindow = None + self.tree_node = tree_node + self.update_leaves() + self.sanity_check_nodes() + + def get_design_panel( self, parent ): + """returns an instance of the miniature library edit control ( see + on_design ). This is for use with the the 'edit multiple nodes in a + single frame' code. + """ + return minpedit( parent, self ) + + def get_use_panel( self, parent ): + """returns an instance of the miniature library view control ( see + on_use ). This is for use with the the 'view multiple nodes in a + single frame' code. + """ + return minilib_use_panel( parent, self ) + + def tohtml( self ): + """Returns an HTML representation of this node in string format. + The table columnwidths are currently being forced, as the wxHTML + widgets being used don't handle cells wider than the widgets are + expecting for a given column. + """ + str = '' + str += "" + for mini in self.xml.findall(TAG_MINIATURE): + url = mini.get(ATTRIBUTE_URL) + label = mini.get(ATTRIBUTE_NAME) + flag = 0 + try: + flag = eval( mini.get(ATTRIBUTE_UNIQUE) ) + except: + pass + show = 'yes' + if flag: + show = 'no' + + str += """ + + + + + """ % ( label, url, url, show ) + + str += "
LabelImageURLUnique
%s %s %s
" + return str + + def html_view( self ): + """see to_html + """ + return self.tohtml() + + def on_drop(self, evt): + drag_obj = self.tree.drag_obj + if drag_obj == self or self.tree.is_parent_node( self.mytree_node, drag_obj.mytree_node ): + return + elif isinstance( drag_obj, map_miniature_nodehandler.map_miniature_handler ): + drop_xml = self.tree.drag_obj.xml#.delete() + obj = drop_xml[0] + dict = {} + unique = '' + for attrib in obj.keys(): + key = TO_MINILIB_MAP.get( attrib, attrib ) + if key != None: + dict[ key ] = obj.get( attrib ) + dict[ ATTRIBUTE_UNIQUE ] = unique + self.new_mini( dict ) + else: + node_handler.on_drop(self, evt) + + + def new_mini( self, data={}, add=1 ): + mini = Element( TAG_MINIATURE ) + for key in data.keys(): + mini.set( key, data[ key ] ) + for key in CORE_ATTRIBUTES: + if mini.get( key ) == '': + mini.set( key, '0' ) + if add: + self.add_mini( mini ) + self.add_leaf( mini ) + return mini + + def add_mini( self, mini ): + self.xml.append( mini ) + + def add_leaf( self, mini, icon='gear' ): + tree = self.tree + icons = tree.icons + key = mini.get( ATTRIBUTE_NAME ) + self.mydata.append( mini ) + + def update_leaves( self ): + self.mydata = [] + for n in self.xml.findall(TAG_MINIATURE): + self.add_leaf( n ) + + def on_drag( self, evt ): + print 'drag event caught' + + def send_mini_to_map( self, mini, count=1, addName=True ): + if mini == None: + return + if mini.get( ATTRIBUTE_URL ) == '' or mini.get( ATTRIBUTE_URL ) == 'http://': + self.chat.ParsePost( self.chat.colorize(self.chat.syscolor, '"%s" is not a valid URL, the mini "%s" will not be added to the map' % ( mini.get( ATTRIBUTE_URL ), mini.get( ATTRIBUTE_NAME ) )) ) + return + session = component.get( COMPONENT_SESSION ) + if (session.my_role() != session.ROLE_GM) and (session.my_role() != session.ROLE_PLAYER): + component.get("chat").InfoPost("You must be either a player or GM to use the miniature Layer") + return + map = component.get(COMPONENT_MAP) + for loop in range( count ): + msg = self.get_miniature_XML( mini, addName) + msg = str("" + msg + "") + map.new_data( msg ) + session.send( msg ) + + def get_miniature_XML( self, mini_xml, addName = True ): + msg = orpg.mapper.map_msg.mini_msg() + map = component.get( COMPONENT_MAP ) + session = component.get( COMPONENT_SESSION ) + msg.init_prop( ATTRIBUTE_ID, session.get_next_id() ) + msg.init_prop('selected', '1')# this will make the mini initially selected + for k in mini_xml.keys(): + # translate our attributes to map attributes + key = FROM_MINILIB_MAP.get( k, k ) + if key != None: + if not addName and k == 'name': + pass + else: + msg.init_prop( key, mini_xml.get( k ) ) + unique = self.is_unique( mini_xml ) + if addName: + label = mini_xml.get( ATTRIBUTE_NAME ) + else: + label = '' + return msg.get_all_xml() + + def is_unique( self, mini ): + unique = mini.get( ATTRIBUTE_UNIQUE ) + val = 0 + try: val = eval( unique ) + except: val = len( unique ) + return val + + def sanity_check_nodes( self ): + for node in self.xml.findall(TAG_MINIATURE): + if node.get( ATTRIBUTE_POSX ) == '': node.set( ATTRIBUTE_POSX, '0' ) + if node.get( ATTRIBUTE_POSY ) == '': node.set( ATTRIBUTE_POSY, '0' ) + + def get_mini( self, index ): + try: return self.xml.findall(TAG_MINIATURE)[index] + except: return None + +class mini_handler( node_handler ): + def __init__( self, xml, tree_node, handler ): + node_handler.__init__( self, xml, tree_node) + self.handler = handler + + def on_ldclick( self, evt ): + self.handler.send_mini_to_map( self.xml ) + + def on_drop( self, evt ): + pass + + def on_lclick( self, evt ): + print 'hi' + evt.Skip() + +class minilib_use_panel(wx.Panel): + """This panel will be displayed when the user double clicks on the + miniature library node. It is a sorted listbox of miniature labels, + a text field for entering a count ( for batch adds ) and 'add'/'done' + buttons. + """ + def __init__( self, frame, handler ): + """Constructor. + """ + wx.Panel.__init__( self, frame, -1 ) + self.handler = handler + self.frame = frame + + self.map = component.get('map') + names = self.buildList() + # self.keys = self.list.keys() + # self.keys.sort() + + + s = self.GetClientSizeTuple() + + self.sizer = wx.BoxSizer(wx.VERTICAL) + box = wx.BoxSizer(wx.HORIZONTAL) + self.listbox = wx.ListBox(self, wx.ID_ANY, (10, 10), (s[0] - 10, s[1] - 30 ), names, wx.LB_EXTENDED) + self.count = wx.TextCtrl(self, wx.ID_ANY, '1') + + box.Add( wx.StaticText( self, -1, 'Minis to add' ), 0, wx.EXPAND ) + box.Add(wx.Size(10,10)) + box.Add(self.count, 1, wx.EXPAND) + + self.sizer.Add( self.listbox, 1, wx.EXPAND ) + self.sizer.Add( box, 0, wx.EXPAND ) + + box = wx.BoxSizer( wx.HORIZONTAL ) + self.okBtn = wx.Button(self, wx.ID_ANY, 'Add') + box.Add(self.okBtn, 0, wx.EXPAND) + self.addBtn = wx.Button(self, wx.ID_ANY, 'Add No Label') + box.Add(self.addBtn, 0, wx.EXPAND) + self.cancleBtn = wx.Button(self, wx.ID_ANY, 'Done') + box.Add(self.cancleBtn, 0, wx.EXPAND) + + self.sizer.Add(wx.Size(10,10)) + self.sizer.Add(box, 0, wx.EXPAND) + self.Bind(wx.EVT_BUTTON, self.on_ok, self.okBtn) + self.Bind(wx.EVT_BUTTON, self.on_ok, self.addBtn) + self.Bind(wx.EVT_BUTTON, self.on_close, self.cancleBtn) + self.SetSizer(self.sizer) + self.SetAutoLayout(True) + self.Fit() + + def buildList( self ): + """Returns a dictionary of label => game tree miniature DOM node mappings. + """ + self.list = [] + for mini in self.handler.xml.findall(TAG_MINIATURE): + self.list.append( mini.get( ATTRIBUTE_NAME ) ) + return self.list + + def on_close(self, evt): + self.frame.Close() + + def on_ok( self, evt ): + """Event handler for the 'add' button. + """ + btn = self.FindWindowById(evt.GetId()) + sendName = True + try: + count = eval( self.count.GetValue() ) + except: + count = 1 + + try: + if eval( unique ): + count = 1 + unique = eval( unique ) + except: + pass + + if btn.GetLabel() == 'Add No Label': + sendName = False + for index in self.listbox.GetSelections(): + self.handler.send_mini_to_map( self.handler.get_mini( index ), count, sendName ) + + +class minpedit(wx.Panel): + """Panel for editing game tree miniature nodes. Node information + is displayed in a grid, and buttons are provided for adding, deleting + nodes, and for sending minis to the map ( singly and in batches ). + """ + def __init__( self, frame, handler ): + """Constructor. + """ + wx.Panel.__init__( self, frame, -1 ) + self.handler = handler + self.frame = frame + + self.sizer = wx.BoxSizer( wx.VERTICAL ) + self.grid = minilib_grid( self, handler ) + + bbox = wx.BoxSizer( wx.HORIZONTAL ) + newMiniBtn = wx.Button( self, wx.ID_ANY, "New mini" ) + delMiniBtn = wx.Button( self, wx.ID_ANY, "Del mini" ) + addMiniBtn = wx.Button( self, wx.ID_ANY, "Add 1" ) + addBatchBtn = wx.Button( self, wx.ID_ANY, "Add Batch" ) + bbox.Add(newMiniBtn, 0, wx.EXPAND ) + bbox.Add(delMiniBtn, 0, wx.EXPAND ) + bbox.Add(wx.Size(10,10)) + bbox.Add(addMiniBtn, 0, wx.EXPAND ) + bbox.Add(addBatchBtn, 0, wx.EXPAND ) + + self.sizer.Add( self.grid, 1, wx.EXPAND) + self.sizer.Add( bbox, 0) + self.SetSizer(self.sizer) + self.SetAutoLayout(True) + self.Fit() + + self.Bind(wx.EVT_BUTTON, self.add_mini, newMiniBtn) + self.Bind(wx.EVT_BUTTON, self.del_mini, delMiniBtn) + self.Bind(wx.EVT_BUTTON, self.send_to_map, addMiniBtn) + self.Bind(wx.EVT_BUTTON, self.send_group_to_map, addBatchBtn) + + def add_mini( self, evt=None ): + """Event handler for the 'New mini' button. It calls + minilib_grid.add_row + """ + self.grid.add_row() + + def del_mini( self, evt=None ): + """Event handler for the 'Del mini' button. It calls + minilib_grid.del_row + """ + self.grid.del_row() + + def send_to_map( self, evt=None ): + """Event handler for the 'Add 1' button. Sends the + miniature defined by the currently selected row to the map, once. + """ + index = self.grid.GetGridCursorRow() + self.handler.send_mini_to_map( self.handler.get_mini( index ) ) + + def send_group_to_map( self, evt=None ): + """Event handler for the 'Add batch' button. Querys the user + for a mini count and sends the miniature defined by the currently + selected row to the map, the specified number of times. + """ + if self.grid.GetNumberRows() > 0: + dlg = wx.TextEntryDialog( self.frame, + 'How many %s\'s do you want to add?' % + ( self.grid.getSelectedLabel() ), 'Batch mini add', '2' ) + if dlg.ShowModal() == wx.ID_OK: + try: + value = eval( dlg.GetValue() ) + except: + value = 0 + # for loop in range( 0, value ): + # self.send_to_map() + print 'getting selected index for batch send' + index = self.grid.GetGridCursorRow() + print 'sending batch to map' + self.handler.send_mini_to_map( self.handler.get_mini( index ), value ) + +class minilib_grid(wx.grid.Grid): + """A wxGrid subclass designed for editing game tree miniature library + nodes. + """ + def __init__( self, parent, handler ): + """Constructor. + """ + wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS ) + self.parent = parent + self.handler = handler + #self.keys = [ ATTRIBUTE_NAME, ATTRIBUTE_URL, ATTRIBUTE_UNIQUE ] + self.keys = CORE_ATTRIBUTES + self.CreateGrid( 1, len( self.keys ) ) + # self.SetColLabelValue( 0, 'Name' ) + # self.SetColLabelValue( 1, 'URL' ) + # self.SetColSize( 1, 250 ) + # self.SetColLabelValue( 2, 'Unique' ) + for key in self.keys: + self.SetColLabelValue( self.keys.index( key ), key ) + self.update_all() + self.selectedRow = 0 + self.AutoSizeColumns() + self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) + self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.select_cell) + + def update_cols( self ): + for n in self.handler.xml.findall(TAG_MINIATURE): + for k in n.keys(): + if k not in self.keys: + self.keys.append( k ) + + def select_cell( self, evt ): + """Event handler for grid cell selection changes. It stores the + last selected row in a variable for use by the add[*] and del_row + operations. + """ + self.BeginBatch() + self.selectedRow = evt.GetRow() + self.SelectRow( self.selectedRow ) + self.EndBatch() + evt.Skip() + + def getList( self ): + """Returns the list of 'miniature' DOM elements associated with this + miniature library. + """ + return self.handler.xml.findall( TAG_MINIATURE ) + + def add_row( self, count = 1 ): + """creates a new miniature node, and then adds it to the current + miniature library, and to the grid. + """ + self.AppendRows( count ) + node = self.handler.new_mini( { + ATTRIBUTE_NAME :' ', + ATTRIBUTE_URL :'http://'} )# minidom.Element( TAG_MINIATURE ) + self.update_all() + #self.handler.xml.append( node ) + + def del_row( self ): + """deletes the miniature associated with the currently selected + row. BUG BUG BUG this method should drop a child from the DOM but + does not. + """ + if self.selectedRow > -1: + pos = self.selectedRow + list = self.handler.xml.findall(TAG_MINIATURE) + self.handler.xml.remove( list[pos] ) + self.DeleteRows( pos, 1 ) + list = self.getList() + del list[ pos ] + + def on_cell_change( self, evt ): + """Event handler for cell selection changes. selected row is used + to update data for that row. + """ + row = evt.GetRow() + self.update_data_row( row ) + + def update_all( self ): + """ensures that the grid is displaying the correct number of + rows, and then updates all data displayed by the grid + """ + list = self.getList() + count = 0 + for n in list: + for k in n.keys(): + if k not in self.keys: + self.keys.append( k ) + count = len( self.keys ) + if self.GetNumberCols() < count: + self.AppendCols( count - self.GetNumberCols() ) + for k in self.keys: + self.SetColLabelValue( self.keys.index( k ), k ) + count = len( list ) + rowcount = self.GetNumberRows() + if ( count > rowcount ): + total = count - rowcount + self.AppendRows( total ) + elif ( count < rowcount ): + total = rowcount - count + self.DeleteRows( 0, total ); + for index in range( 0, count ): + self.update_grid_row( index ) + + def getSelectedLabel( self ): + """Returns the label for the selected row + """ + return self.GetTable().GetValue( self.selectedRow, 0 ) + + def getSelectedURL( self ): + """Returns the URL for the selected row + """ + return self.GetTable().GetValue( self.selectedRow, 1 ) + + def getSelectedSerial( self ): + """Returns the ATTRIBUTE_UNIQUE value for the selected row + """ + return self.GetTable().GetValue( self.selectedRow, 2 ) + + def update_grid_row( self, row ): + """Updates the specified grid row with data from the DOM node + specified by 'row' + """ + list = self.getList() + item = list[ row ] + for key in self.keys: + self.GetTable().SetValue( row, self.keys.index( key ), item.get( key ) ) + + def update_data_row( self, row ): + """Updates the DOM nodw 'row' with grid data from 'row' + """ + list = self.getList() + item = list[ row ] + for key in self.keys: + item.set( key, string.strip( self.GetTable().GetValue( row, self.keys.index( key ) ) ) ) diff -r 8e77f169f324 -r fc48380f0c9f orpg/gametree/nodehandlers/rpg_grid.py --- a/orpg/gametree/nodehandlers/rpg_grid.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/gametree/nodehandlers/rpg_grid.py Mon May 03 03:30:11 2010 -0500 @@ -89,7 +89,7 @@ html_str += "" text = c.text if text == None or text == '': text = '
' - s = Parse.NodeMap(text, self.xml) + s = Parse.ParseLogic(text, self.xml) s = Parse.Normalize(s) try: text = str(eval(s)) except: text = s @@ -327,8 +327,7 @@ text = '' cells[i].text = text if self.mode == 0: - s = Parse.NodeMap(text, self.handler.xml) - s = Parse.NodeParent(s, self.handler.xml.get('map')) + s = Parse.ParseLogic(text, self.handler.xml) try: text = str(eval(s)) except: text = s self.SetCellValue(rowi,i,text) diff -r 8e77f169f324 -r fc48380f0c9f orpg/main.py --- a/orpg/main.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/main.py Mon May 03 03:30:11 2010 -0500 @@ -622,7 +622,7 @@ # Update Manager #self.manifest = manifest.ManifestChanges() self.updateMana = upmana.updatemana.updaterFrame(self, - "OpenRPG Update Manager 1.0", component, manifest, True) + "OpenRPG Update Manager 1.2", component, manifest, True) component.add('upmana-win', self.updateMana) logger.debug("Menu Created") h = int(xml_dom.get("height")) @@ -790,7 +790,7 @@ temp_wnd = orpg.chat.chatwnd.chat_notebook(parent_wnd, wx.DefaultSize) self.chattabs = temp_wnd self.chat = temp_wnd.MainChatPanel - component.add("chat",self.chat) + component.add("chat", self.chat) elif name == "player": temp_wnd = orpg.player_list.player_list(parent_wnd) @@ -929,10 +929,9 @@ etreeEl = Element('msg') try: etreeEl.append(fromstring(data)) except: etreeEl.text = data - if player: display_name = self.chat.chat_display_name(player) - else: display_name = "Server Administrator" - if etreeEl.text: self.chat.Post(etreeEl.text) + display_name = self.chat.chat_display_name(player) + if etreeEl.text:self.chat.Post(display_name+etreeEl.text) for child in etreeEl.getchildren(): if child.tag == 'tree': diff -r 8e77f169f324 -r fc48380f0c9f orpg/mapper/background.py --- a/orpg/mapper/background.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/mapper/background.py Mon May 03 03:30:11 2010 -0500 @@ -34,6 +34,7 @@ from orpg.orpgCore import component from orpg.tools.orpg_log import logger from orpg.tools.orpg_settings import settings +from xml.etree.ElementTree import fromstring ##----------------------------- ## background layer @@ -244,22 +245,21 @@ def upload(self, postdata, filename, type): self.lock.acquire() if type == 'Image' or type == 'Texture': - url = component.get('settings').get_setting('ImageServerBaseURL') + url = settings.get_setting('ImageServerBaseURL') file = urllib.urlopen(url, postdata) recvdata = file.read() file.close() try: - xml_dom = minidom.parseString(recvdata)._get_documentElement() - if xml_dom.nodeName == 'path': - path = xml_dom.getAttribute('url') + xml_dom = fromstring(recvdata) + if xml_dom.tag == 'path': + path = xml_dom.get('url') path = urllib.unquote(path) if type == 'Image': self.set_image(path, 1) else: self.set_texture(path) self.localPath = filename self.local = True self.localTime = time.time() - else: - print xml_dom.getAttribute('msg') + else: print xml_dom.get('msg') except Exception, e: print e print recvdata diff -r 8e77f169f324 -r fc48380f0c9f orpg/mapper/grid_handler.py --- a/orpg/mapper/grid_handler.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/mapper/grid_handler.py Mon May 03 03:30:11 2010 -0500 @@ -40,10 +40,11 @@ self.grid_snap = wx.CheckBox(self, wx.ID_ANY, " Snap") self.grid_size = wx.TextCtrl(self, wx.ID_ANY, size=(32,-1) ) self.grid_ratio = wx.TextCtrl(self, wx.ID_ANY, size=(32,-1) ) - self.color_button = wx.Button(self, wx.ID_ANY, "Color", style=wx.BU_EXACTFIT) + self.color_button = createMaskedButton(self, dir_struct["icon"]+'grid.png', + 'Grid Color', wx.ID_ANY, '#bdbdbd', + wx.BITMAP_TYPE_PNG) self.apply_button = wx.Button(self, wx.ID_OK, "Apply", style=wx.BU_EXACTFIT) self.color_button.SetBackgroundColour(wx.BLACK) - self.color_button.SetForegroundColour(wx.WHITE) self.sizer.Add(wx.StaticText(self, -1, "Size: "), 0, wx.ALIGN_CENTER) self.sizer.Add(self.grid_size, 0, wx.ALIGN_CENTER) self.sizer.Add((6,0)) diff -r 8e77f169f324 -r fc48380f0c9f orpg/mapper/map.py --- a/orpg/mapper/map.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/mapper/map.py Mon May 03 03:30:11 2010 -0500 @@ -156,7 +156,8 @@ else: pass if not ImageHandler.Queue.empty(): (path, image_type, imageId) = ImageHandler.Queue.get() - if path == 'failed': img = wx.Image(dir_struct["icon"] + "failed.png", wx.BITMAP_TYPE_PNG) + if (path == 'failed' or path == dir_struct["icon"] + "failed.png"): + img = wx.Image(dir_struct["icon"] + "failed.png", wx.BITMAP_TYPE_PNG) else: img = wx.ImageFromMime(path[1], path[2]) try: # Now, apply the image to the proper object @@ -250,8 +251,9 @@ topleft1 = self.GetViewStart() topleft = [topleft1[0]*scrollsize[0], topleft1[1]*scrollsize[1]] if (clientsize[0] > 1) and (clientsize[1] > 1): - self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) - dc = wx.AutoBufferedPaintDC(self) + dc = wx.MemoryDC() + bmp = wx.EmptyBitmap(clientsize[0]+1, clientsize[1]+1) + dc.SelectObject(bmp) dc.SetPen(wx.TRANSPARENT_PEN) dc.SetBrush(wx.Brush(self.GetBackgroundColour(), wx.SOLID)) dc.DrawRectangle(0,0,clientsize[0]+1,clientsize[1]+1) @@ -266,11 +268,22 @@ self.layers['fog'].layerDraw(dc, topleft, clientsize) dc.SetPen(wx.NullPen) dc.SetBrush(wx.NullBrush) + + dc.SelectObject(wx.NullBitmap) + del dc + wdc = self.preppaint() + wdc.DrawBitmap(bmp, topleft[0], topleft[1]) + if settings.get_setting("AlwaysShowMapScale") == "1": - self.showmapscale(dc) + self.showmapscale(wdc) try: evt.Skip() except: pass + def preppaint(self): + dc = wx.PaintDC(self) + self.PrepareDC(dc) + return (dc) + def showmapscale(self, dc): scalestring = "Scale x" + `self.layers['grid'].mapscale`[:3] (textWidth, textHeight) = dc.GetTextExtent(scalestring) diff -r 8e77f169f324 -r fc48380f0c9f orpg/mapper/whiteboard_handler.py --- a/orpg/mapper/whiteboard_handler.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/mapper/whiteboard_handler.py Mon May 03 03:30:11 2010 -0500 @@ -57,14 +57,16 @@ def build_ctrls(self): base_layer_handler.build_ctrls(self) - self.color_button = wx.Button(self, wx.ID_ANY, "Pen Color", style=wx.BU_EXACTFIT) + self.color_button = createMaskedButton(self, dir_struct["icon"]+'draw.png', + 'Pen Color', wx.ID_ANY, '#bdbdbd', + wx.BITMAP_TYPE_PNG) self.color_button.SetBackgroundColour(wx.BLACK) self.color_button.SetForegroundColour(wx.WHITE) self.drawmode_ctrl = wx.Choice(self, wx.ID_ANY, choices = ["Freeform", "Polyline","Text", "Cone", "Circle"]) self.drawmode_ctrl.SetSelection(0) #always start showing "Freeform" self.radius = wx.TextCtrl(self, wx.ID_ANY, size=(32,-1) ) self.radius.SetValue("15") - self.live_refresh = wx.CheckBox(self, wx.ID_ANY, " Live Refresh") + self.live_refresh = wx.CheckBox(self, wx.ID_ANY, " Dynamic") self.live_refresh.SetValue(True) self.widthList= wx.Choice(self, wx.ID_ANY, size= wx.Size(40, 20), choices=['1','2','3','4','5','6','7','8','9','10']) @@ -78,7 +80,7 @@ self.sizer.Add(self.radius, 0, wx.EXPAND|wx.ALL, 2) self.sizer.Add(wx.Size(10,25)) self.sizer.Add(self.live_refresh, 0, wx.EXPAND) - self.sizer.Add(wx.Size(20,25)) + self.sizer.Add(wx.Size(10,25)) self.sizer.Add(self.color_button, 0, wx.EXPAND) self.sizer.Add(wx.Size(20,25)) self.Bind(wx.EVT_MOTION, self.on_motion) @@ -754,9 +756,9 @@ pos = self.get_snapped_to_logical_pos(evt) size = self.canvas.layers['grid'].unit_size #60 radius = int(int(self.radius.GetValue())/5) - center = wx.Point(pos.x, pos.y+size*radius) + center = wx.Point(pos.x, pos.y) curve = self.calculate_circle(center, radius, size) - if(self.temp_circle): + if self.temp_circle: self.canvas.layers['whiteboard'].del_temp_line(self.temp_circle) self.selected = None self.temp_circle = self.canvas.layers['whiteboard'].add_temp_line(curve) diff -r 8e77f169f324 -r fc48380f0c9f orpg/networking/mplay_server.py --- a/orpg/networking/mplay_server.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/networking/mplay_server.py Mon May 03 03:30:11 2010 -0500 @@ -325,6 +325,7 @@ self.saveBanList() except Exception, e: self.log_msg("Exception in initBanList() " + str(e)) + self.log_msg( ('exception', str(e)) ) # This method writes out the server's ban list added by Darren def saveBanList( self ): @@ -343,6 +344,7 @@ file.close() except Exception, e: self.log_msg("Exception in saveBanList() " + str(e)) + self.log_msg( ('exception', str(e)) ) # This method reads in the server's configuration file and reconfigs the server # as needed, over-riding any default values as requested. @@ -526,6 +528,7 @@ except Exception, e: traceback.print_exc() self.log_msg("Exception in initServerConfig() " + str(e)) + self.log_msg( ('exception', str(e)) ) def makePersistentRooms(self): 'Creates rooms on the server as defined in the server config file.' @@ -571,6 +574,7 @@ return pr except: self.log_msg("Exception occured in isPersistentRoom(self,id)") + self.log_msg( ('exception', str(e)) ) return 0 #----------------------------------------------------- @@ -775,8 +779,8 @@ try: sentl = sock.send( lp ) # Send the encoded length sentm = sock.send( msg ) # Now, send the message the the length was describing - except socket.error, e: self.log_msg( e ) - except Exception, e: self.log_msg( e ) + except socket.error, e: self.log_msg( ('exception', str(e)) ); self.log_msg( e ) + except Exception, e: self.log_msg( e ); self.log_msg( ('exception', str(e)) ) def recvData( self, sock, readSize ): @@ -818,7 +822,7 @@ try: if useCompression and cmpType != None: msgData = cmpType.decompress(msgData) except: traceback.print_exc() - except Exception, e: self.log_msg( "Exception: recvMsg(): " + str(e) ) + except Exception, e: self.log_msg( "Exception: recvMsg(): " + str(e) ); self.log_msg( ('exception', str(e)) ) return msgData def kill_server(self): @@ -886,8 +890,14 @@ print "'help' or '?' or 'h' - for this help message" print - def broadcast(self,msg): - self.send_to_all("0","" + msg + "") + def broadcast(self, msg): + broadcast = '' +msg+ '' + chat = Element('chat') + chat.set('type', '1') + chat.set('version', '1.0') + chat.text = broadcast + msg = self.buildMsg('all', '0', '1', tostring(chat)) + self.send_to_all('0', msg) def console_log(self): if self.log_to_console == 1: @@ -910,6 +920,7 @@ print except Exception, e: self.log_msg(str(e)) + self.log_msg( ('exception', str(e)) ) self.p_lock.release() """ @@ -996,6 +1007,7 @@ print except Exception, e: self.log_msg(str(e)) + self.log_msg( ('exception', str(e)) ) self.p_lock.release() """ @@ -1028,6 +1040,7 @@ print "\nStatistics: groups: " + str(len(self.groups)) + " players: " + str(len(self.players)) except Exception, e: self.log_msg(str(e)) + self.log_msg( ('exception', str(e)) ) self.p_lock.release() @@ -1045,6 +1058,7 @@ print "Bad Player Ref (#" + id + ") in group" except Exception, e: self.log_msg(str(e)) + self.log_msg( ('exception', str(e)) ) self.p_lock.release() def update_request(self,newsock, xml_dom): @@ -1069,7 +1083,7 @@ msg = self.groups[group_id].game_map.get_all_xml() self.send(msg,id,group_id) - def new_request(self,newsock, xml_dom, LOBBY_ID='0'): + def new_request(self, newsock, xml_dom, LOBBY_ID='0'): #build client stub props = {} # Don't trust what the client tells us...trust what they connected as! @@ -1101,7 +1115,8 @@ # send confirmation data = self.recvMsg(newsock, new_stub.useCompression, new_stub.compressionType) - try: xml_dom = XML(data) + try: + xml_dom = XML(data) except Exception, e: print e (remote_host,remote_port) = newsock.getpeername() @@ -1109,14 +1124,15 @@ bad_xml_string += "Please report this bug to the development team at:
" bad_xml_string += "Traipse-Dev " bad_xml_string += "(http://www.assembla.com/spaces/traipse_dev/tickets/)
" - self.sendMsg( newsock, "" + bad_xml_string, - new_stub.useCompression, new_stub.compressionType) + msg = self.buildMsg(props['id'], props['id'], '0', bad_xml_string) + self.sendMsg( newsock, msg, new_stub.useCompression, new_stub.compressionType) time.sleep(2) newsock.close() print "Error in parse found from " + str(remote_host) + ". Disconnected." print " Offending data(" + str(len(data)) + "bytes)=" + data print "Exception=" + str(e) + self.log_msg( ('exception', str(e)) ) #if xml_dom: xml_dom.unlink() return @@ -1214,7 +1230,7 @@ newsock.close() # Display the lobby message - self.SendLobbyMessage(newsock,props['id']) + self.SendLobbyMessage(newsock, props['id']) def checkClientVersion(self, clientversion): minv = self.minClientVersion.split('.') @@ -1234,7 +1250,7 @@ # prepend this server's version string to the the lobby message """ try: - lobbyMsg = "You have connected to a " + lobbyMsg = "You have connected to a " lobbyMsg += DISTRO +' '+ DIS_VER +' {'+ BUILD+'}' lobbyMsg += " server, built on OpenRPG version '" + VERSION + "'" @@ -1251,7 +1267,6 @@ open_msg = open( self.userPath + "LobbyMessage.html", "r" ) lobbyMsg += open_msg.read() open_msg.close() - # Send the server's lobby message to the client no matter what self.sendMsg(socket, "" + lobbyMsg, self.players[player_id].useCompression, self.players[player_id].compressionType) @@ -1276,6 +1291,7 @@ except Exception, e: self.log_msg(("Error binding request socket!", e)) + self.log_msg( ('exception', str(e)) ) self.alive = 0 while self.alive: @@ -1292,9 +1308,10 @@ """ thread.start_new_thread(self.acceptedNewConnectionThread, ( newsock, addr )) - except: + except Exception, e: print "The following exception caught accepting new connection:" traceback.print_exc() + self.log_msg( ('exception', str(e)) ) # At this point, we're done and cleaning up. self.log_msg("server socket listening thread exiting...") @@ -1315,6 +1332,7 @@ try: newsock.close() except Exception, e: self.log_msg( str(e) ) + self.log_msg( ('exception', str(e)) ) print str(e) return #returning causes connection thread instance to terminate if data == "": @@ -1336,10 +1354,11 @@ except: try: newsock.close() - except: pass + except Exception, e: pass self.log_msg( "Error in parse found from " + str(addr) + ". Disconnected.") self.log_msg(" Offending data(" + str(len(data)) + "bytes)=" + data) self.log_msg( "Exception:") + self.log_msg( ('exception', str(e)) ) traceback.print_exc() return #returning causes connection thread instance to terminate @@ -1357,6 +1376,7 @@ print "The following message: " + str(data) print "from " + str(addr) + " created the following exception: " traceback.print_exc() + self.log_msg( ('exception', str(e)) ) return #returning causes connection thread instance to terminate """ @@ -1391,26 +1411,33 @@ data = None except Exception, e: self.log_msg(str(e)) + self.log_msg( ('exception', str(e)) ) self.log_msg("message handler thread exiting...") self.incoming_event.set() def parse_incoming_dom(self, data): - end = data.find(">") #locate end of first element of message + end = data.find(">") head = data[:end+1] - xml_dom = None + msg = data[end+1:] + ### This if statement should help close invalid messages. ### + if head[end:] != '/': + if head[end:] != '>': head = head[:end] + '/>' try: - xml_dom = XML(head) + try: xml_dom = fromstring(head) + except: xml_dom = fromstring(head[:end] +'/>') self.message_action(xml_dom, data) - except Exception, e: print "Error in parse of inbound message. Ignoring message." print " Offending data(" + str(len(data)) + "bytes)=" + data print "Exception=" + str(e) + self.log_msg( ('exception', str(e)) ) def message_action(self, xml_dom, data): tag_name = xml_dom.tag if self.svrcmds.has_key(tag_name): self.svrcmds[tag_name]['function'](xml_dom, data) - else: raise Exception, "Not a valid header!" + else: + raise Exception, "Not a valid header!" + self.log_msg( ('exception', 'Not a valid header!') ) #Message Action thread expires and closes here. return @@ -1514,6 +1541,7 @@ print "Bad input: " + data except Exception,e: self.log_msg(str(e)) + self.log_msg( ('exception', str(e)) ) def join_group(self, xml_dom, data): try: @@ -1547,6 +1575,7 @@ self.move_player(from_id, group_id) except Exception, e: self.log_msg(str(e)) + self.log_msg( ('exception', str(e)) ) """ # move_player function -- added by Snowdog 4/03 @@ -1564,6 +1593,7 @@ else: self.players[from_id].role = "Lurker" except Exception, e: print "exception in move_player() " + self.log_msg( ('exception', str(e)) ) traceback.print_exc() old_group_id = self.players[from_id].change_group(group_id, self.groups) @@ -1592,6 +1622,7 @@ except Exception, e: roomMsg = "" self.log_msg(str(e)) + self.log_msg( ('exception', str(e)) ) # Spit that darn message out now! self.players[from_id].outbox.put("" + roomMsg) @@ -1604,10 +1635,12 @@ #notify user about others in the room self.return_room_roles(from_id,group_id) self.log_msg(("join_group", (self.groups[group_id].name, group_id, from_id))) + self.log_msg(("update_group", (self.groups[old_group_id].name, old_group_id, len(self.groups[old_group_id].players) ))) self.log_msg(("update_group", (self.groups[group_id].name, group_id, len(self.groups[group_id].players) ))) self.handle_role("set", from_id, self.players[from_id].role, self.groups[group_id].boot_pwd, group_id) except Exception, e: self.log_msg(str(e)) + self.log_msg( ('exception', str(e)) ) thread.start_new_thread(self.registerRooms,(0,)) def return_room_roles(self, from_id, group_id): @@ -1633,7 +1666,7 @@ if persist !=0: ins="Persistant " lmsg = "Creating " + ins + "Group... (" + str(group_id) + ") " + str(name) self.log_msg( lmsg ) - self.log_msg(("create_group", (str(name), int(group_id), pwd, 0) )) + self.log_msg(("create_group", (str(name), int(group_id), 0, 'No' if pwd == '' else 'Yes') )) def change_group_name(self, gid, name, pid): "Change the name of a group" @@ -1766,7 +1799,7 @@ else: self.send_to_all("0",self.groups[group_id].toxml('update')) #The register Rooms thread thread.start_new_thread(self.registerRooms,(0,)) - except Exception, e: self.log_msg(str(e)) + except Exception, e: self.log_msg( ('exception', str(e)) ) def del_player(self, id, group_id): try: @@ -1784,7 +1817,7 @@ """ if self.be_registered: self.register() - except Exception, e: self.log_msg(str(e)) + except Exception, e: self.log_msg( ('exception', str(e)) ) self.log_msg("Explicit garbage collection shows %s undeletable items." % str(gc.collect())) def incoming_player_handler(self, xml_dom, data): @@ -1799,7 +1832,7 @@ try: self.send_player_list(id,group_id) self.send_group_list(id) - except Exception, e: traceback.print_exc() + except Exception, e: self.log_msg( ('exception', str(e)) ); traceback.print_exc() elif act=="del": self.del_player(id,group_id) self.check_group(id, group_id) @@ -1841,8 +1874,11 @@ to_id = xml_dom.get("to") from_id = xml_dom.get("from") group_id = xml_dom.get("group_id") + ## Backwards compatibility with older clients end = data.find(">") msg = data[end+1:] + if msg[-6:] == '': msg = msg[:-6] + data = msg if from_id == "0" or len(from_id) == 0: print "WARNING!! Message received with an invalid from_id. Message dropped." @@ -1866,21 +1902,27 @@ self.players[from_id].self_message('The lobby map may not be altered.') elif to_id.lower() == 'all': #valid map for all players that is not the lobby. - self.send_to_group(from_id,group_id,data) + msg = self.buildMsg('all', from_id, group_id, data) + self.send_to_group(from_id,group_id,msg) self.groups[group_id].game_map.init_from_xml(msg) else: #attempting to send map to specific individuals which is not supported. self.players[from_id].self_message('Invalid map message. Message not sent to others.') elif msg[:6] == '' + msg = self.buildMsg('all', '0', group_id, msg) self.log_msg("ban_msg:" + ban_msg) if (silent == 0): self.send_to_group("0", group_id, ban_msg) time.sleep( .1 ) @@ -2097,6 +2146,7 @@ except Exception, e: traceback.print_exc() self.log_msg('Exception in admin_ban() ' + str(e)) + self.log_msg( ('exception', str(e)) ) def admin_unban(self, ip): self.admin_build_banlist() @@ -2107,6 +2157,7 @@ except Exception, e: traceback.print_exc() self.log_msg('Exception in admin_unban() ' + str(e)) + self.log_msg( ('exception', str(e)) ) def admin_banlist(self): msg = [] @@ -2158,6 +2209,7 @@ except Exception, e: traceback.print_exc() self.log_msg("Exception: send_to_all(): " + str(e)) + self.log_msg( ('exception', str(e)) ) def send_to_group(self, from_id, group_id, data): #data = ("" + data + "") @@ -2171,6 +2223,7 @@ except Exception, e: traceback.print_exc() self.log_msg("Exception: send_to_group(): " + str(e)) + self.log_msg( ('exception', str(e)) ) def send_player_list(self,to_id,group_id): try: @@ -2182,17 +2235,17 @@ except Exception, e: traceback.print_exc() self.log_msg("Exception: send_player_list(): " + str(e)) + self.log_msg( ('exception', str(e)) ) def send_group_list(self, to_id, action="new"): try: - print self.groups for key in self.groups: xml = self.groups[key].toxml(action) - print xml, key self.players[to_id].outbox.put(xml) except Exception, e: self.log_msg("Exception: send_group_list(): (client #"+to_id+") : " + str(e)) traceback.print_exc() + self.log_msg( ('exception', str(e)) ) """ # KICK_ALL_CLIENTS() @@ -2210,6 +2263,7 @@ except Exception, e: traceback.print_exc() self.log_msg("Exception: kick_all_clients(): " + str(e)) + self.log_msg( ('exception', str(e)) ) """ # This really has little value as it will only catch people that are hung @@ -2235,7 +2289,12 @@ self.admin_kick(k,"Removing dead client", self.silent_auto_kick) except Exception, e: self.log_msg("Exception: check_group_members(): " + str(e)) + self.log_msg( ('exception', str(e)) ) + def buildMsg(self, toId, fromId, roomId, data): + msg = '' + msg += data+ '' + return msg def remote_admin_handler(self,xml_dom,data): """ @@ -2284,106 +2343,112 @@ #determine action to take based on command (cmd) if cmd == "list": #return player list to this user. - msg ="" + self.player_list_remote() + msg = self.buildMsg(pid, '0', gid, self.player_list_remote()) self.players[pid].outbox.put(msg) elif cmd == "banip": ip = xml_dom.get("bip") name = xml_dom.get("bname") - msg = " Banned: " + str(ip) + msg = self.buildMsg(pid, '0', gid, str(ip)) self.admin_banip(ip, name) elif cmd == "ban": id = xml_dom.get("bid") - msg = " Banned!" + msg = self.buildMsg(id, '0', gid, 'Banned!') self.players[pid].outbox.put(msg) self.admin_ban(id, "") ### Alpha ### and untested elif cmd == "boot": id = xml_dom.get("bid") - msg = " Booted!" + msg = self.buildMsg(id, '0', gid, 'Booted!!') self.players[pid].outbox.put(msg) self.admin_kick(id, "") ############# elif cmd == "unban": ip = xml_dom.get("ip") self.admin_unban(ip) - msg = " Unbaned: " + str(ip) + msg = self.buildMsg(pid, '0', gid, str(ip)) self.players[pid].outbox.put(msg) elif cmd == "banlist": - msg = "" + self.admin_banlist() + msg = self.buildMsg(pid, '0', gid, self.admin_banlist()) self.players[pid].outbox.put(msg) elif cmd == "killgroup": ugid = xml_dom.get("gid") if ugid == "0": - m = "" - m += "Cannot Remove Lobby! Remote administrator request denied!" - self.players[pid].outbox.put(m) + m + "Cannot Remove Lobby! Remote administrator request denied!" + msg = self.buildMsg(pid, '0', gid, m) + self.players[pid].outbox.put(msg) else: result = self.prune_room(ugid) - msg = "" + str(result) + msg = self.buildMsg(pid, '0', gid, str(result)) self.players[pid].outbox.put(msg) elif cmd == "message": tuid = xml_dom.get("to_id") msg = xml_dom.get("msg") - pmsg = "" + msg + pmsg = self.buildMsg(tuid, '0', self.players[tuid].group_id, msg) try: self.players[tuid].outbox.put(pmsg) except: - msg = "" + self.uptime(1) + msg = self.uptime(1) + msg = self.buildMsg(pid, '0', gid, msg) self.players[pid].outbox.put(msg) elif cmd == "help": - msg = "" - msg += self.AdminHelpMessage() + msg = self.AdminHelpMessage() + msg = self.buildMsg(pid, '0', gid, msg) self.players[pid].outbox.put( msg) elif cmd == "roompasswords": # Toggle if room passwords are allowed on this server - msg = "" - msg += self.RoomPasswords() + msg = self.RoomPasswords() + msg = self.buildMsg(pid, '0', gid, msg) self.players[pid].outbox.put( msg) elif cmd == "createroom": rm_name = xml_dom.get("name") rm_pass = xml_dom.get("pass") rm_boot = xml_dom.get("boot") result = self.create_temporary_persistant_room(rm_name, rm_boot, rm_pass) - msg = "" + result + msg = self.buildMsg(pid, '0', gid, result) self.players[pid].outbox.put(msg) elif cmd == "nameroom": rm_id = xml_dom.get("rmid") rm_name = xml_dom.get("name") result = self.change_group_name(rm_id,rm_name,pid) - msg ="" + result + msg = self.buildMsg(pid, '0', gid, result) self.players[pid].outbox.put(msg) elif cmd == "passwd": tgid = xml_dom.get("gid") npwd = xml_dom.get("pass") if tgid == "0": - msg ="" - msg += "Server password may not be changed remotely!" + msg = "Server password may not be changed remotely!" + msg = self.buildMsg(pid, '0', gid, msg) self.players[pid].outbox.put(msg) else: try: self.groups[tgid].boot_pwd = npwd - msg ="Password changed for room " + tgid + msg = "Password changed for room " + tgid + msg = self.buildMsg(pid, '0', gid, msg) self.players[pid].outbox.put(msg) except: pass elif cmd == "savemaps": for g in self.groups.itervalues(): g.save_map() - msg ="Persistent room maps saved" + msg = "Persistent room maps saved" + msg = self.buildMsg(pid, '0', gid, msg) self.players[pid].outbox.put(msg) else: - msg ="[Unknown Remote Administration Command]" + msg = "[Unknown Remote Administration Command]" + msg = self.buildMsg(pid, '0', gid, msg) self.players[pid].outbox.put(msg) except Exception, e: self.log_msg("Exception: Remote Admin Handler Error: " + str(e)) traceback.print_exc() + self.log_msg( ('exception', str(e)) ) def toggleRemoteKill(self): if self.allowRemoteKill: self.allowRemoteKill = False @@ -2551,8 +2616,8 @@ pl += "" pl += "Statistics: groups: " + str(len(self.groups)) + " " pl += "players: " + str(len(self.players)) + "" - except Exception, e: self.log_msg(str(e)) + except Exception, e: self.log_msg(str(e)); self.log_msg( ('exception', str(e)) ) self.p_lock.release() return pl -server = mplay_server() +server = mplay_server() diff -r 8e77f169f324 -r fc48380f0c9f orpg/networking/mplay_server_gui.py --- a/orpg/networking/mplay_server_gui.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/networking/mplay_server_gui.py Mon May 03 03:30:11 2010 -0500 @@ -21,7 +21,7 @@ from xml.dom import minidom from orpg.orpgCore import component -from orpg.tools.orpg_log import debug +from orpg.tools.orpg_log import debug, DebugConsole from orpg.tools.orpg_settings import settings from xml.etree.ElementTree import ElementTree, Element, iselement @@ -151,8 +151,8 @@ (room, room_id, players, passworded) = data i = self.InsertStringItem(0, str(room_id)) self.SetStringItem(i, 1, room) - self.SetStringItem(i, 2, players) - self.SetStringItem(i, 3, str(passworded)) + self.SetStringItem(i, 2, str(players)) + self.SetStringItem(i, 3, passworded) def DeleteGroup(self, data): i = self.FindItem(-1, str(data)) @@ -162,7 +162,7 @@ (room, room_id, players) = data i = self.FindItem( -1, str(room_id)) self.SetStringItem( i, 1, room ) - if players: self.SetStringItem(i, 2, str(players)) + self.SetStringItem(i, 2, str(players)) ### Need to add room for Password Updates ### class Connections(wx.ListCtrl): @@ -321,7 +321,7 @@ BanMsg = wx.TextEntryDialog( self, "Enter A Message To Send:", "Ban Message", message, wx.OK|wx.CANCEL|wx.CENTRE ) if BanMsg.ShowModal() == wx.ID_OK: message = BanMsg.GetValue() - else: message = '' + else: return Silent = wx.MessageDialog(None, 'Silent Ban?', 'Question', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) if Silent.ShowModal() == wx.ID_YES: silent = 1 @@ -330,14 +330,30 @@ self.remove( playerID ) elif menuItem == 4: msg = self.GetMessageInput( "Send a message to player" ) - if len(msg): self.main.server.server.send( msg, playerID, str(groupID) ) + + broadcast = '' +msg+ '' + chat = Element('chat') + chat.set('type', '1') + chat.set('version', '1.0') + chat.text = broadcast + msg = self.main.server.server.buildMsg(str(playerID), '0', '1', msg) + + if len(msg): self.main.server.server.players[playerID].outbox.put(msg) #Leave this in for now. elif menuItem == 5: msg = self.GetMessageInput( "Send message to room of this player") + + broadcast = '' +msg+ '' + chat = Element('chat') + chat.set('type', '1') + chat.set('version', '1.0') + chat.text = broadcast + msg = self.main.server.server.buildMsg('all', '0', '1', tostring(chat)) + if len(msg): self.main.server.server.send_to_group('0', str(groupID), msg ) elif menuItem == 6: msg = self.GetMessageInput( "Broadcast Server Message" ) - if len(msg): self.main.server.server.broadcast( msg ) + if len(msg): self.main.server.server.broadcast(msg ) elif menuItem == 3: version_string = self.main.server.server.obtain_by_id(playerID, 'client_string') if version_string: wx.MessageBox("Running client version " + version_string,"Version") @@ -354,8 +370,8 @@ STATUS = SERVER_STOPPED def __init__(self, parent, id, title): wx.Frame.__init__(self, parent, id, title, size = (760, 560) ) - if wx.Platform == '__WXMSW__': icon = wx.Icon( dir_struct["icon"]+'WAmisc9.ico', wx.BITMAP_TYPE_ICO ) - else: icon = wx.Icon( dir_struct["icon"]+'connect.gif', wx.BITMAP_TYPE_GIF ) + if wx.Platform == '__WXMSW__': icon = wx.Icon(dir_struct["icon"]+'WAmisc9.ico', wx.BITMAP_TYPE_ICO) + else: icon = wx.Icon(dir_struct["icon"]+'connect.gif', wx.BITMAP_TYPE_GIF) self.SetIcon(icon) self.serverName = "Server Name" self.bootPwd = "" @@ -384,6 +400,7 @@ cb["delete_group"] = self.OnDeleteGroup cb["join_group"] = self.OnJoinGroup cb['update_group'] = self.OnUpdateGroup + cb['exception'] = self.OnException cb["role"] = self.OnSetRole self.callbacks = cb @@ -401,10 +418,10 @@ # File Menu menu = wx.Menu() - menu.Append( 1, 'Start', 'Start server.') - menu.Append( 2, 'Stop', 'Shutdown server.') + menu.Append(1, 'Start', 'Start server.') + menu.Append(2, 'Stop', 'Shutdown server.') menu.AppendSeparator() - menu.Append( 3, 'E&xit', 'Exit application.') + menu.Append(3, 'E&xit', 'Exit application.') self.Bind(wx.EVT_MENU, self.OnStart, id=1) self.Bind(wx.EVT_MENU, self.OnStop, id=2) self.Bind(wx.EVT_MENU, self.OnExit, id=3) @@ -412,23 +429,24 @@ # Registration Menu menu = wx.Menu() - menu.Append( 4, 'Register', 'Register with OpenRPG server directory.') - menu.Append( 5, 'Unregister', 'Unregister from OpenRPG server directory.') + menu.Append(4, 'Register', 'Register with OpenRPG server directory.') + menu.Append(5, 'Unregister', 'Unregister from OpenRPG server directory.') self.Bind(wx.EVT_MENU, self.OnRegister, id=4) self.Bind(wx.EVT_MENU, self.OnUnregister, id=5) self.mainMenu.Append( menu, '&Registration' ) # Server Configuration Menu menu = wx.Menu() - menu.Append( 6, 'Ban List', 'Modify Ban List.') + menu.Append(6, 'Ban List', 'Modify Ban List.') menu.Append(11, 'Zombies', 'Set auto-kick time for zombie clients') menu.Append(14, 'Send Size', 'Adjust the send size limit') menu.AppendSeparator() - menu.Append( 7, 'Start Ping', 'Ping players to validate remote connection.' ) - menu.Append( 8, 'Stop Ping', 'Stop validating player connections.' ) - menu.Append( 9, 'Ping Interval', 'Change the ping interval.' ) + menu.Append(7, 'Start Ping', 'Ping players to validate remote connection.' ) + menu.Append(8, 'Stop Ping', 'Stop validating player connections.' ) + menu.Append(9, 'Ping Interval', 'Change the ping interval.' ) menu.AppendSeparator() - menu.AppendCheckItem( 10, 'Server Logging', 'Turn on or off the Server GUI Log').Check(self.do_log) + menu.AppendCheckItem(10, 'Server Logging', + 'Turn on or off the Server GUI Log').Check(self.do_log) menu.AppendCheckItem(12, 'Room Passwords', 'Allow or Deny Room Passwords').Check(False) menu.AppendCheckItem(13, 'Remote Admin', 'Allow or Deny Remote Admin').Check(False) menu.AppendCheckItem(15, 'Remote Kill', 'Allow or Deny Remote Admin Server Kill').Check(False) @@ -437,37 +455,82 @@ self.Bind(wx.EVT_MENU, self.StopPingPlayers, id=8) self.Bind(wx.EVT_MENU, self.ConfigPingInterval, id=9) self.Bind(wx.EVT_MENU, self.LogToggle, id=10) - self.mainMenu.Append( menu, '&Configuration' ) - self.SetMenuBar( self.mainMenu ) + self.mainMenu.Append( menu, '&Configuration') + + # Traipse Suite of Additions. + self.traipseSuite = wx.Menu() + self.debugger = DebugConsole(self) + self.mainMenu.Insert(3, self.traipseSuite, "&Traipse Suite") - self.mainMenu.Enable( 2, False ) - self.mainMenu.Enable( 4, False ) - self.mainMenu.Enable( 5, False ) + #Debugger Console + self.debugConsole = wx.MenuItem(self.traipseSuite, -1, "Debug Console", "Debug Console") + self.Bind(wx.EVT_MENU, self.OnMB_DebugConsole, self.debugConsole) + self.traipseSuite.AppendItem(self.debugConsole) + + self.SetMenuBar(self.mainMenu) + + self.mainMenu.Enable(2, False) + self.mainMenu.Enable(4, False) + self.mainMenu.Enable(5, False) # Disable the ping menu items - self.mainMenu.Enable( 7, False ) - self.mainMenu.Enable( 8, False ) - self.mainMenu.Enable( 9, False ) + self.mainMenu.Enable(7, False) + self.mainMenu.Enable(8, False) + self.mainMenu.Enable(9, False) # Disable placeholders - self.mainMenu.Enable( 11, False ) - self.mainMenu.Enable( 14, False ) - self.mainMenu.Enable( 12, False ) - self.mainMenu.Enable( 13, False ) - self.mainMenu.Enable( 15, False ) + self.mainMenu.Enable(11, False) + self.mainMenu.Enable(14, False) + self.mainMenu.Enable(12, False) + self.mainMenu.Enable(13, False) + self.mainMenu.Enable(15, False) + + def OnException(self, error): + self.TraipseSuiteWarn('debug') + self.debugger.console.AppendText(".. " + str(error) +'\n') + + def OnMB_DebugConsole(self, evt): + self.TraipseSuiteWarnCleanup('debug') + if self.debugger.IsShown() == True: self.debugger.Hide() + else: self.debugger.Show() + + def TraipseSuiteWarn(self, menuitem): + ### Allows for the reuse of the 'Attention' menu. + ### component.get('frame').TraipseSuiteWarn('item') ### Portable + self.mainMenu.Remove(3) + self.mainMenu.Insert(3, self.traipseSuite, "&Traipse Suite!") + if menuitem == 'debug': + if self.debugger.IsShown() == True: + self.mainMenu.Remove(3) + self.mainMenu.Insert(3, self.traipseSuite, "&Traipse Suite") + else: + self.debugConsole.SetBitmap(wx.Bitmap(dir_struct["icon"] + 'spotlight.png')) + self.traipseSuite.RemoveItem(self.debugConsole) + self.traipseSuite.AppendItem(self.debugConsole) + + def TraipseSuiteWarnCleanup(self, menuitem): + ### Allows for portable cleanup of the 'Attention' menu. + ### component.get('frame').TraipseSuiteWarnCleanup('item') ### Portable + self.mainMenu.Remove(3) + self.mainMenu.Insert(3, self.traipseSuite, "&Traipse Suite") + if menuitem == 'debug': + self.traipseSuite.RemoveItem(self.debugConsole) + self.debugConsole.SetBitmap(wx.Bitmap(dir_struct["icon"] + 'clear.gif')) + self.traipseSuite.AppendItem(self.debugConsole) + def build_body(self): """ Create the ViewNotebook and logger. """ splitter = wx.SplitterWindow(self, -1, style=wx.NO_3D | wx.SP_3D) - nb = wx.Notebook( splitter, -1 ) + nb = wx.Notebook(splitter, -1) self.conns = Connections(nb, self) self.groups = Groups(nb, self) - self.msgWindow = HTMLMessageWindow( nb ) + self.msgWindow = HTMLMessageWindow(nb) nb.AddPage(self.conns, "Players") nb.AddPage(self.groups, 'Rooms') - nb.AddPage( self.msgWindow, "Messages" ) + nb.AddPage(self.msgWindow, "Messages") log = wx.TextCtrl(splitter, -1, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL) - wx.Log.SetActiveTarget( wx.LogTextCtrl(log) ) + wx.Log.SetActiveTarget(wx.LogTextCtrl(log)) splitter.SplitHorizontally(nb, log, 400) splitter.SetMinimumPaneSize(40) self.nb = nb @@ -522,12 +585,14 @@ def OnDataSent(self, bytes): self.total_messages_sent += 1 self.total_data_sent += bytes - self.sb.SetStatusText("Sent: %s (%d)" % (format_bytes(self.total_data_sent), self.total_messages_sent), 1) + self.sb.SetStatusText("Sent: %s (%d)" % (format_bytes(self.total_data_sent), + self.total_messages_sent), 1) def OnDataRecv(self, bytes): self.total_messages_received += 1 self.total_data_received += bytes - self.sb.SetStatusText("Recv: %s (%d)" % (format_bytes(self.total_data_received), self.total_messages_received), 2) + self.sb.SetStatusText("Recv: %s (%d)" % (format_bytes(self.total_data_received), + self.total_messages_received), 2) def OnCreateGroup( self, data ): (room, room_id, player, pwd) = data @@ -569,13 +634,16 @@ except: pass if self.serverName == '': self.serverName = 'Server Name' - serverNameEntry = wx.TextEntryDialog(self, "Please Enter The Server Name You Wish To Use:", - "Server's Name", self.serverName, wx.OK|wx.CANCEL|wx.CENTRE ) + serverNameEntry = wx.TextEntryDialog(self, + "Please Enter The Server Name You Wish To Use:", + "Server's Name", + self.serverName, wx.OK|wx.CANCEL|wx.CENTRE) if serverNameEntry.ShowModal() == wx.ID_OK: self.serverName = serverNameEntry.GetValue() if self.bootPwd == '': serverPasswordEntry = wx.TextEntryDialog(self, - "Please Enter The Server Admin Password:", "Server's Password", - self.bootPwd, wx.OK|wx.CANCEL|wx.CENTRE) + "Please Enter The Server Admin Password:", + "Server's Password", + self.bootPwd, wx.OK|wx.CANCEL|wx.CENTRE) if serverPasswordEntry.ShowModal() == wx.ID_OK: self.bootPwd = serverPasswordEntry.GetValue() if len(self.serverName): wx.BeginBusyCursor() @@ -584,9 +652,9 @@ self.STATUS = SERVER_RUNNING self.sb.SetStatusText("Running", 3) self.SetTitle(__appname__ + "- (running) - (unregistered)") - self.mainMenu.Enable( 1, False ) - self.mainMenu.Enable( 2, True ) - self.mainMenu.Enable( 4, True ) + self.mainMenu.Enable(1, False) + self.mainMenu.Enable(2, True) + self.mainMenu.Enable(4, True) wx.EndBusyCursor() else: self.show_error("Server is already running.", "Error Starting Server") @@ -598,10 +666,10 @@ self.STATUS = SERVER_STOPPED self.sb.SetStatusText("Stopped", 3) self.SetTitle(__appname__ + "- (stopped) - (unregistered)") - self.mainMenu.Enable( 1, True ) - self.mainMenu.Enable( 2, False ) - self.mainMenu.Enable( 4, False ) - self.mainMenu.Enable( 5, False ) + self.mainMenu.Enable(1, True) + self.mainMenu.Enable(2, False) + self.mainMenu.Enable(4, False) + self.mainMenu.Enable(5, False) self.conns.DeleteAllItems() def OnRegister(self, event = None): @@ -613,8 +681,8 @@ wx.BeginBusyCursor() self.server.server.register(self.serverName) self.sb.SetStatusText( ("%s" % (self.serverName)), 4 ) - self.mainMenu.Enable( 4, False ) - self.mainMenu.Enable( 5, True ) + self.mainMenu.Enable(4, False) + self.mainMenu.Enable(5, True) #self.mainMenu.Enable( 2, False ) self.SetTitle(__appname__ + "- (running) - (registered)") wx.EndBusyCursor() @@ -627,9 +695,9 @@ """ wx.BeginBusyCursor() self.server.server.unregister() - self.sb.SetStatusText( "Unregistered", 4 ) - self.mainMenu.Enable( 5, False ) - self.mainMenu.Enable( 4, True ) + self.sb.SetStatusText("Unregistered", 4) + self.mainMenu.Enable(5, False) + self.mainMenu.Enable(4, True) #self.mainMenu.Enable( 2, True ) self.SetTitle(__appname__ + "- (running) - (unregistered)") wx.EndBusyCursor() diff -r 8e77f169f324 -r fc48380f0c9f orpg/orpg_version.py --- a/orpg/orpg_version.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/orpg_version.py Mon May 03 03:30:11 2010 -0500 @@ -2,9 +2,9 @@ SERVER_MIN_CLIENT_VERSION = "1.7.1" #BUILD NUMBER FORMAT: "YYMMDD-##" where ## is the incremental daily build index (if needed) -DISTRO = "Traipse" +DISTRO = "Traipse Alpha" DIS_VER = "Ornery Orc" -BUILD = "100219-00" +BUILD = "100503-01" # This version is for network capability. PROTOCOL_VERSION = "1.2" diff -r 8e77f169f324 -r fc48380f0c9f orpg/pluginhandler.py --- a/orpg/pluginhandler.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/pluginhandler.py Mon May 03 03:30:11 2010 -0500 @@ -1,5 +1,7 @@ from orpg.orpg_wx import * from orpg.orpgCore import component +from xml.etree.ElementTree import ElementTree, Element, parse +from xml.etree.ElementTree import fromstring, tostring class PluginHandler: # Initialization subroutine. @@ -82,22 +84,22 @@ self.settings.add_setting(self.name, setting, value, options, help) def plugin_send_msg(self, to, plugin_msg): - xml_dom = self.xml.parseXml(plugin_msg) - xml_dom = xml_dom._get_documentElement() - xml_dom.setAttribute('from', str(self.session.id)) - xml_dom.setAttribute('to', str(to)) - xml_dom.setAttribute('group_id', str(self.session.group_id)) - tag_name = xml_dom._get_tagName() + xml_dom = fromstring(plugin_msg) + #xml_dom = xml_dom.getroot() + xml_dom.set('from', str(self.session.id)) + xml_dom.set('to', str(to)) + xml_dom.set('group_id', str(self.session.group_id)) + tag_name = xml_dom.tag if not tag_name in self.session.core_msg_handlers: xml_msg = '' + xml_dom.toxml() + xml_msg += '" />' + tostring(xml_dom) self.session.outbox.put(xml_msg) else: #Spoofing attempt pass - xml_dom.unlink() + #xml_dom.unlink() def message(self, text): return text diff -r 8e77f169f324 -r fc48380f0c9f orpg/templates/default_LobbyMessage.html --- a/orpg/templates/default_LobbyMessage.html Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/templates/default_LobbyMessage.html Mon May 03 03:30:11 2010 -0500 @@ -1,40 +1,38 @@ - - - - - - - - -
-
-
- - - - - - - -
- Many thanks goes to all of those who contributed! -
- The developers in alphabetical order are: -
- Thomas Baleno, Andrew Bennett, Lex Berezhny, Ted Berg, - Bernhard Bergbauer, Chris Blocher ,Ben Collins-Sussman, Robin Cook, Greg Copeland, - Chris Davis, Michael Edwards, Andrew Ettinger, Dj Gilcrease, Todd Faris, - Christopher Hickman, Paul Hosking, Scott Mackay, Brian Manning, - Jesse McConnell, Brian Osman, Rome Reginelli, Christopher Rouse, Dave Sanders, Mark Tarrabain, - David Byron, David, Vrabel, and Tyler Starke. -
- This product is licensed under the GNU GPL License. -
-
- - + + + + + + + + + +
+ Welcome to an OpenRPG Server +
+ Add a message here. +
+ The OpenRPG Project +
+ Who is this force? +
+ Here is an example Room Message that can be easily modified to fit the needs of your server. + The room message has two rows and two cells. One row is for headers, and one row is for cells. + The main area here can be an introduction, and the side area to the right can contain a game schedule. +

If you have created a Wiki or forums, be sure and put links to those as well.

+
+ Thomas Baleno, Andrew Bennett, Lex Berezhny, Ted Berg, + Bernhard Bergbauer, Chris Blocher, Ben Collins-Sussman, Robin Cook, Greg Copeland, + Chris Davis, Michael Edwards, Andrew Ettinger, Dj Gilcrease, Todd Faris, + Christopher Hickman, Paul Hosking, Scott Mackay, Brian Manning, + Jesse McConnell, Brian Osman, Rome Reginelli, Christopher Rouse, Dave Sanders, Mark Tarrabain, + David Byron, David Vrabel, and Tyler Starke. +

And of course, the Community!

+

Many thanks to all who contributed!

+
+ diff -r 8e77f169f324 -r fc48380f0c9f orpg/templates/feature.xml --- a/orpg/templates/feature.xml Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/templates/feature.xml Mon May 03 03:30:11 2010 -0500 @@ -1,6 +1,7 @@ - - Welcome to Traipse OpenRPG. + + + Welcome to Traipse OpenRPG. This small user manual should help users learn about the details of OpenRPG that are often times obscure. @@ -17,14 +18,19 @@ Adding to the Manual: Do you see something that could be explained eaiser? Report the problem as a bug and it will be added to the manual. - - The Chat window is a basic HTML Parser. It understands all basic HTML tags including table, td, tr, span, font, to name a few. + + + + The Chat window is a basic HTML Parser. It understands all basic HTML tags including table, td, tr, span, font, to name a few. The chat includes a set of commands. You can learn about the commands by entering /help The chat also has Settings in the Chat menu that allow you see a Chat Time Index, Images, or strip the HTML and see raw text. - - The Tabs: + + + + + The Tabs: The Map is divided into 7 tabs. They are Background, Grid, Miniatures, Whiteboard, Fog, and General. There are 6 layers to the map, one tab for each layer except General. When you select one of the tabs you may access that map layer and it's settings. You may only select tabs based on your role. @@ -56,8 +62,54 @@ Fog: The fog layer hides the entire map from the prying eyes of players. - - Quick Help: + + + + + Persistant users who wanted the stability of Traipse and the ease of Namespace have described to me how they envision Namespace should work. When I heard this I immediately realized that my method provided the aspects users wanted, but not simplicity in design this method would provide. The Traipse Namespace is a little different than Standard but it offers more stability in it's approach + + There are two methods provided with the Traipse Namespace, and these two allow you to a great deal of control when you choose to use Namespace to reference your nodes. + +There is only one difference in how these two methods work, so once you get the hang of Namespace, you will always know how it works. The difference is, External starts looking from the Game Tree and gets more narrow where as Internal starts from the node it is inside and searchs backwards getting more broad. + + + Namespace Internal: +(Syntax) !=Node=! + +Usage: When you use Namespace Internal the software finds the tree map of the node and searches for the top node. Once that node is found it will iterate through the node and look for the reference you have assigned. If it cannot find it in that node, then it will iterate through the map, finding all successive nodes and searching them + + Namespace Internal is completely context sensitive. You can assign a reference using the Namespace Internal method and it will always find the correct PC Sheet to search in. + +Namespace Internal must be used from within a node. + +Namespace External: +(Syntax) !&Top Node::Node&! + +Usage: Namespace External is a different approach to Namespace. With Namespace External you can use the syntax in chat or in other nodes to cross reference nodes. Instead of External being context sensitive, External uses a broadscope that you can narrow down. + + It is really easy to narrow down External. External doesn't work like a string, it works like a lightning bolt. To get a good example open up the 4e PC Sheet node that comes with Traipse and try some different commands in chat. + +<b>1:</b> !&4e PC Sheet::Slot 1&! +<b>2:</b> !&4e PC Sheet::Belt:: Slot 1&! +<b>3:</b> !&4e PC Sheet::Inventory&! +<b>4:</b> !&4e PC Sheet::Inventory::Slot 1&! + + Did you see what happened with the last two? Thankfully there is more than one way to get a node! + +(Create a 4e PC Sheet node from the Templates and press Send ---v to try it) + + + Grids can now be called from by adding a Row, Column to the end of the grid reference. + +Example: !&Abilities::2,2&! + + + Quick Help: + +Designer Note: +=== +For the life span of Ornery Orc the new Child, Parent, Root reference will exist, but in Pious the reference system will not transfer. This is because of the way the new Namespace works. Namespace will become the exclusive referencing system +=== The referencing system is an update to the Core of how the Game Tree works. In it's current state I understand the syntax is difficult to pick up. Here are some tips to help you grasp the syntax further @@ -114,86 +166,69 @@ In the OpenRPG Core model your Game Tree has a lot more freedom, but only if you grant it, which I always felt was a design flaw. Comparably, with Traipse you can access any data on the Game Tree, no matter where the location. This freedom will help with future design and I feel it also frees up the hands of the GM who does not need to Index, un-Index, Namespace, un-Namspace the various creatures he or she may have in a Game Tree. - - <b>Root Reference</b> + + + <b>Root Reference</b> Works at the tree level. Must be exact. <b>Root Reference 1:</b> !@Reference Examples::Group::Child@! -<b>Root Reference 2:</b> !@Reference Examples::Grid::(2,1)@! - - <b>Grid Reference</b> +<b>Root Reference 2:</b> !@Reference Examples::Grid::2,1@! + + + <b>Grid Reference</b> Works by looking at the (Row, Column) of a Grid. -<b>Grid Reference 1:</b> !@Reference Examples::Grid::(1,1)@! -<b>Grid Reference 2:</b> !!Grid::(1,1)!! - - <b>Child Reference</b> +<b>Grid Reference 1:</b> !@Reference Examples::Grid::1,1@! +<b>Grid Reference 2:</b> !!Grid::1,1!! + + + <b>Child Reference</b> Works at the current tree location. <b>Child Reference 1:</b> !!Group::Child!! <b>Child Reference 2:</b> !!Group::Group_2::Child_2!! - - <b>Parent Reference</b> + + + <b>Parent Reference</b> Works by indexing the tree map of the node with the Reference. Allows you to start from a 'Parent'. <b>Parent Reference 1:</b> !!Group::Group_2::Child_2!! <b>Parent Reference 2:</b> !#Bonus Nodes::Deck::Draw#! - - - - !#Group::Child#! - - - !#Group::Child#! - - Child Node Data - - - - 0 - 0 - - - !!Group::Child!! - 0 - - - - - - With the new additions to the Game Tree using nodes has never been easier nor has it ever been more fluid. Included here is a list of the additions to the Game Tree referencing model as well as some tips on how to make the Game Tree work the way it was intended. - -Grid Nodes: - Grid nodes are now reference-able with the coordinates of the grid. Example: !@Grid::(1,1)@! -The example will return the top left most cell data. The grid understands coordinates like this (Row, Column) - - Grid nodes can reference node data just like any other node can. With a new added feature grids are even more useful. By using a new die rolling syntax you can draw just the number of the modified roll. While this will not pass during game play, you can use it with the grid node to create a random chart. The new die roll syntax is [#XdY]. # works just like q, yet it returns only the modified die result. - - Here is an example with a 3 x 3 Grid -Example: !@Grid::([#1d3], [#1d3])@! - -The result will be a random event from the grid. - -Bonus Node Included: A 52 Card Deck with 4 columns and 13 rows. (4 * 13 = 52) - -List Nodes: - List nodes now have a check box that allows users to send the content as a macro. List nodes are a prime reference holder because users can place a lot of references into one small node. - - For the best results from a list node my tip to users would be to create a list node and place it next to the character sheet they are using, inside a the PC Sheet. The list will then use the Child Referencing syntax, but the PC Sheet can go anywhere in the tree and the player will have easy access to all the references. - -(List Nodes inside a Tool created PC sheet vanish when moved, or I would recommend the list be placed inside these sheets also.) - - Here is an example of a Fortitude save inside the recommended list node: !!Fort::Check!! - -Text Nodes: - Text nodes remain little changed. I agree with all the 1.7.1 users who tell me, if it's not broke don't fix it. With that in mind I have some good tips for text nodes. - - Text nodes can be used in conjunction with the new grid features to create random encounters. A GM could place a list of text nodes into a folder and the grid could reference the nodes. - - Text nodes also work great when you need to have story text at hand that you don't want to type out during play. Create chapters with folder nodes and add the adventure text to different nodes. You can then use a List Node or a Grid Node to reference the different chapters. - -Bonus Node Included: A small book node with 1 Chapter and 3 Parts. Traipse node referencing is unlike other distributions of OpenRPG. The Game Tree mapping is a fluid map that changes with the location of your nodes. This allows you to create a reference node that will stay with your character sheet, and if you change the location of your character sheet the reference will still work. + + + + + !#Group::Child#! + + + + !#Group::Child#! + + + + + Child Node Data + + + + + + 0 + 0 + + + !!Group::Child!! + 0 + + + + + + + + + Traipse node referencing is unlike other distributions of OpenRPG. The Game Tree mapping is a fluid map that changes with the location of your nodes. This allows you to create a reference node that will stay with your character sheet, and if you change the location of your character sheet the reference will still work. (Note: Renaming your node causes problems with the tree mapping until you restart the software. You can just move the node and the software will reset the Game Tree map) @@ -245,8 +280,46 @@ Examples: !@Kammen-Pai::Cast::Ray of Frost@! -!@Kammen-Pai::Feat::Ability Focus@! - In Traipse starting a server has never been easier. The setup is as easy as 1., 2., 3 +!@Kammen-Pai::Feat::Ability Focus@! + + + With the new additions to the Game Tree using nodes has never been easier nor has it ever been more fluid. Included here is a list of the additions to the Game Tree referencing model as well as some tips on how to make the Game Tree work the way it was intended. + +Grid Nodes: + Grid nodes are now reference-able with the coordinates of the grid. Example: !@Grid::(1,1)@! +The example will return the top left most cell data. The grid understands coordinates like this (Row, Column) + + Grid nodes can reference node data just like any other node can. With a new added feature grids are even more useful. By using a new die rolling syntax you can draw just the number of the modified roll. While this will not pass during game play, you can use it with the grid node to create a random chart. The new die roll syntax is [#XdY]. # works just like q, yet it returns only the modified die result. + + Here is an example with a 3 x 3 Grid +Example: !@Grid::([#1d3], [#1d3])@! + +The result will be a random event from the grid. + +Bonus Node Included: A 52 Card Deck with 4 columns and 13 rows. (4 * 13 = 52) + +List Nodes: + List nodes now have a check box that allows users to send the content as a macro. List nodes are a prime reference holder because users can place a lot of references into one small node. + + For the best results from a list node my tip to users would be to create a list node and place it next to the character sheet they are using, inside a the PC Sheet. The list will then use the Child Referencing syntax, but the PC Sheet can go anywhere in the tree and the player will have easy access to all the references. + +(List Nodes inside a Tool created PC sheet vanish when moved, or I would recommend the list be placed inside these sheets also.) + + Here is an example of a Fortitude save inside the recommended list node: !!Fort::Check!! + +Text Nodes: + Text nodes remain little changed. I agree with all the 1.7.1 users who tell me, if it's not broke don't fix it. With that in mind I have some good tips for text nodes. + + Text nodes can be used in conjunction with the new grid features to create random encounters. A GM could place a list of text nodes into a folder and the grid could reference the nodes. + + Text nodes also work great when you need to have story text at hand that you don't want to type out during play. Create chapters with folder nodes and add the adventure text to different nodes. You can then use a List Node or a Grid Node to reference the different chapters. + +Bonus Node Included: A small book node with 1 Chapter and 3 Parts. + + + + + In Traipse starting a server has never been easier. The setup is as easy as 1., 2., 3 1. You will need to first start the Server GUI or the basic text based Server at least once so your software creates the server_ini.xml files in your myfiles directory. You can start it once and quit. @@ -257,174 +330,282 @@ 3. This is the hardest step. You need to make sure your selected port is forwarded by your router and open to your firewall. That's it! You can now start the server and register it to the meta for all users to enjoy! - - - - - - - - - - - - - - - - <br /> + + + + + + + + + + + + + + + + + + + + + + + <br /> <b>Chapter 1 Part 1</b> <br /><br /> An introduction to your adventure module can be placed here. - - <br /> + + + <br /> <b>Chapter 1 Part 2</b> <br /><br /> The adventurers have come this far. - - <br /> + + + <br /> <b>Chapter 1 Part 3</b> <br /><br /> Is this the end already? - - - - - AS - AD - ACAH - - KS - KD - KCKH - QSQDQCQHJSJDJCJH10S10D10C10H9S9D9C9H8S8D8C8H7S7D7C7H6S6D6C6H5S5D5C5H4S4D4C4H3S3D3C3H2S2D2C2H - - - - - !!52 Card Deck::([#1d13], [#1d4])!! - - - - - - - - - - !!Set 1::Enc 1!! - !!Set 2::Enc 1!! - - !!Set 1::Enc 2!! - !!Set 2::Enc 2!! - !!Set 1::Enc 3!! - - - - - - Hoot Hoot. It's an owl. - - Set 2 Random Encounter. - - - Dark Elves. Watch out! - - Kobolds a plenty. - - A Wandering Minotaur - - - - - - - - - - - + + + + + + + + AS + AD + AC + AH + + + KS + KD + KC + KH + + + QS + QD + QC + QH + + + JS + JD + JC + JH + + + 10S + 10D + 10C + 10H + + + 9S + 9D + 9C + 9H + + + 8S + 8D + 8C + 8H + + + 7S + 7D + 7C + 7H + + + 6S + 6D + 6C + 6H + + + 5S + 5D + 5C + 5H + + + 4S + 4D + 4C + 4H + + + 3S + 3D + 3C + 3H + + + 2S + 2D + 2C + 2H + + + + + + + + !=52 Card Deck::([#1d13], [#1d4])=! + + + + + + + + + + + + + !=Set 1::Enc 1=! + !=Set 2::Enc 1=! + + + !=Set 1::Enc 2=! + !=Set 2::Enc 2=! + + + !=Set 1::Enc 3=! + + + + + + + + + + Hoot Hoot. It's an owl. + + + Set 2 Random Encounter. + + + + + Dark Elves. Watch out! + + + Kobolds a plenty. + + + A Wandering Minotaur + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + - - - - - - - - - - - - - - - + + - - - - + + - - - - - + + - - + + - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + \ No newline at end of file diff -r 8e77f169f324 -r fc48380f0c9f orpg/templates/nodes/4e_char_sheet.xml --- a/orpg/templates/nodes/4e_char_sheet.xml Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/templates/nodes/4e_char_sheet.xml Mon May 03 03:30:11 2010 -0500 @@ -1,4 +1,4 @@ -This node is designed to be as generic as possible. It should contain the core basics for users to fill out with game information that is specific to their character. +This node is designed to be as generic as possible. It should contain the core basics for users to fill out with game information that is specific to their character. The node is also designed to be organized with speed of use in mind. Since the majority of nodes are inside Tabbers you can easily use the top node and find all of your data inside. In fact, that is how I am writing this. @@ -13,7 +13,7 @@ Combat: This contains grids so you can reference your weapon dice, armor bonuses, feat bonuses .. and they too can use references so if you change a number the software does the math. -Inventory: You guess it. It comes with a Back Pack text node that you can clone to make bags and other containers.Inside the Rollers tab you will find 5 lists. You can use these lists to add new references to nodes. The Rollers tab is also a Tabber node itself, so you can just double click the Rollers node to get all of your rollers in one window. You can also edit all of your rollers in one window when you enter into the Design mode of the Rollers tabber.This form contains a some text nodes that hold basic information about your character. In some Powers the Level and Tier are used to calculate bonuses. You can reference those nodes to make the math easierThe Abilities Grid contains the 6 abilities all set at values of 8. The third column is the math calculation for the modifier. You can reference that grid column to assist in your math calculations.The Combat Tabber contains a number of Grid nodes to assist in your combat calculations. Some grids contain a grid cell called Total. +Inventory: You guess it. It comes with a Back Pack text node that you can clone to make bags and other containers.Inside the Rollers tab you will find 5 lists. You can use these lists to add new references to nodes. The Rollers tab is also a Tabber node itself, so you can just double click the Rollers node to get all of your rollers in one window. You can also edit all of your rollers in one window when you enter into the Design mode of the Rollers tabber.This form contains a some text nodes that hold basic information about your character. In some Powers the Level and Tier are used to calculate bonuses. You can reference those nodes to make the math easierThe Abilities Grid contains the 6 abilities all set at values of 8. The third column is the math calculation for the modifier. You can reference that grid column to assist in your math calculations.The Combat Tabber contains a number of Grid nodes to assist in your combat calculations. Some grids contain a grid cell called Total. To Hit: The To Hit Bonus. This Grid contains a cell called Total. You can add new rows to the grid when you are granted bonuses and then add those references to the total. @@ -23,50 +23,50 @@ AC Bonus: This Grid contains a cell called Total. You can add new rows to the grid when you are granted bonuses and then add those references to the total. -Feats: Some feats add bonusus to hit or damage. When these bonuses are calculated you can add them here and the math easier for you.This Tabber contains a Tabber for At Will, Encounter and Daily. Each tabber contains a node for levels 0 and 1. You can add new tabbers when you reach higher levels. +Feats: Some feats add bonusus to hit or damage. When these bonuses are calculated you can add them here and the math easier for you.This Tabber contains a Tabber for At Will, Encounter and Daily. Each tabber contains a node for levels 0 and 1. You can add new tabbers when you reach higher levels. Inside the 0 Level tabber for each Utility there is a text node that contains a an attack roll and a damage roll. -** I went with this format so users could create their Utility nodes and share with others. The nodes can contain Role Play information as well as attack and damage rolls. Also, the nodes can be completely genric, referencing the Name Text node and still look specific **It's just a node to hold your Inventory +** I went with this format so users could create their Utility nodes and share with others. The nodes can contain Role Play information as well as attack and damage rolls. Also, the nodes can be completely genric, referencing the Name Text node and still look specific **It's just a node to hold your Inventory This bears repeating: -It comes with a Back Pack text node that you can clone to make bags and other containers. +It comes with a Back Pack text node that you can clone to make bags and other containers. - - - + + + - + - - + + - - + + - - + + - - + + - - - + + + text - + text - + text - + text - + 1 - + 1 - + Weapon @@ -80,7 +80,7 @@ - + Armor @@ -88,13 +88,13 @@ Total - !!To Hit::(3,2)!! + !!To Hit::(4,2)!! + !=To Hit::(3,2)=! + !=To Hit::(4,2)=! - BAB15Str Mod!#Abilities::(1,3)#! + BAB15Str Mod!=Abilities::(1,3)=! - + Armor @@ -102,13 +102,13 @@ Total - !!AC Bonus::(3,2)!!+!!AC Bonus::(4,2)!! + !=AC Bonus::(3,2)=!+!=AC Bonus::(4,2)=! - Armor!!Armor::(2,2)!! + Armor!!Armor::(2,2)!!Misc0 - + Armor @@ -116,74 +116,74 @@ Total - !!Armor::(3,2)!! + !=Armor::(3,2)=! Base10ArmorShield - + Armor - Bonus + Bonus Descripton Total - !!Feats::(3,2)!! + !=Feats::(3,2)=! Feat0 - + Str 12 - (!!Abilities::(1,2)!!-10)/2 + (!=Abilities::(1,2)=!-10)/2 Dex 8 - (!!Abilities::(2,2)!!-10)/2 + (!=Abilities::(2,2)=!-10)/2 Con 14 - (!!Abilities::(3,2)!!-10)/2 + (!=Abilities::(3,2)=!-10)/2 Int 18 - (!!Abilities::(4,2)!!-10)/2 + (!=Abilities::(4,2)=!-10)/2 Wis 8 - (!!Abilities::(5,2)!!-10)/2 + (!=Abilities::(5,2)=!-10)/2 Cha 8 - (!!Abilities::(6,2)!!-10)/2 + (!=Abilities::(6,2)=!-10)/2 - + /me uses an At Will <b>Attack:</b> [1d20+2+!#Abilities::(2,3)#!] <b>Damage:</b> [2!#Combat::Weapons::(2,2)#!] - + /me uses an Encounter <b>Attack:</b> [1d20+2+!#Abilities::(2,3)#!] <b>Damage:</b> [2!#Combat::Weapons::(2,2)#!] - + /me uses an Daily <b>Attack:</b> [1d20+2+!#Abilities::(2,3)#!] <b>Damage:</b> [2!#Combat::Weapons::(2,2)#!] - + Nothing - + Nothing - + Nothing - + Nothing \ No newline at end of file diff -r 8e77f169f324 -r fc48380f0c9f orpg/templates/nodes/Warhammerv2CS2-Traipse.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orpg/templates/nodes/Warhammerv2CS2-Traipse.xml Mon May 03 03:30:11 2010 -0500 @@ -0,0 +1,6591 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + Meduim + + + + + + + + + + + + + + + + + + + + + + + + + + Copper + + + + Silver + + + + Gold + + + + Platinum + + + + Astral Diamonds + + + + + + + + + + + + [1d20 + (!=Character Level=! / 2) + ((!=Ability Init=! / 2) - 5) + !=Feat Init Bonus=! + !=Misc Init Bonus=!] + + + 6 + + + + + + !=Dexterity=! + + + 0 + + + 0 + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + [!=Racial Speed Base=! + !=Armor Speed Bonus=! + !=Item Speed Bonus=! + !=Misc Speed Bonus=!] + + + + + + + + 10 + + + 10 + + + 10 + + + 10 + + + 10 + + + 10 + + + + + + + + + + + + + + + + 10 + + + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + Maximum Health + 10 + + + Bloodied At + 10 + + + Current Health + 5 + + + Temporary Health + 0 + + + + + + + Healing Surge Value + 5 + + + Healing Surges per Day + 6 + + + Healing Surges Remaining + 6 + + + + + + + Death Saving Throw Attempts + 0 + + + + + + + + + + Armor Class + 10 + + + Fortitude + 10 + + + Reflex + 10 + + + Willpower + 10 + + + + + + + + + [1d20 + !=Class Save Bonus=! + !=Racial Save Bonus=! + !=Feat Save Bonus=! + !=Misc Save Bonus=!] + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + !=Dexterity=! + + + 0 + + + 0 + + + 0 + + + [10 + (!=Character Level=! / 2) + ((!=AC Ability=! / 2) - 5) + !=Class AC Bonus=! + !=Feat AC Bonus=! + !=Equipment AC Bonus=! + !=Misc AC Bonus=!] + + + + + + 0 + + + !=Strength=! + + + 0 + + + 0 + + + 0 + + + [10 + (!=Character Level=! / 2) + ((!=Fort Ability=! / 2) - 5) + !=Class Fort Bonus=! + !=Feat Fort Bonus=! + !=Equipment Fort Bonus=! + !=Misc Fort Bonus=!] + + + + + + 0 + + + !=Dexterity=! + + + 0 + + + 0 + + + 0 + + + [10 + (!=Character Level=! / 2) + ((!=Ref Ability=! / 2) - 5) + !=Class Ref Bonus=! + !=Feat Ref Bonus=! + !=Equipment Ref Bonus=! + !=Misc Ref Bonus=!] + + + + + + 0 + + + !=Wisdom=! + + + 0 + + + 0 + + + 0 + + + [10 + (!=Character Level=! / 2) + ((!=Will Ability=! / 2) - 5) + !=Class Will Bonus=! + !=Feat Will Bonus=! + !=Equipment Will Bonus=! + !=Misc Will Bonus=!] + + + + + + + + + Weapon 1 Name + + + + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 1 + + + 4 + + + 1 + + + 0 + + + + + + + + !=Strength=! + + + !=Weapon 1 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 1 Attack Class and Feat Bonus=! + !=Weapon 1 Attack Proficiency Bonus=! + ((!=Weapon 1 Melee Attack Ability=! / 2) - 5) + !=Weapon 1 Attack Misc Bonus=!] + + + !=Strength=! + + + !=Weapon 1 Name=!: [Q((!=Weapon 1 Damage Multiplier=! * !=Weapon 1 Damage Dice Count=!)**std(!=Weapon 1 Damage Dice Sides=!)) + !=Weapon 1 Damage Feat Bonus=! + ((!=Weapon 1 Melee Damage Ability=! / 2) - 5) + !=Weapon 1 Damage Misc Bonus=!] + + + !=Weapon 1 Name=! = [Q(!=Weapon 1 Damage Multiplier=! * !=Weapon 1 Damage Dice Count=!*!=Weapon 1 Damage Dice Sides=!) + (!=Weapon 1 Critical Extra Damage=!) + !=Weapon 1 Damage Feat Bonus=! + ((!=Weapon 1 Melee Damage Ability=! / 2) - 5) + !=Weapon 1 Damage Misc Bonus=!] + + + + + + !=Dexterity=! + + + !=Weapon 1 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 1 Attack Class and Feat Bonus=! + !=Weapon 1 Attack Proficiency Bonus=! + ((!=Weapon 1 Range Attack Ability=! / 2) - 5) + !=Weapon 1 Attack Misc Bonus=!] + + + !=Dexterity=! + + + !=Weapon 1 Name=!: [Q((!=Weapon 1 Damage Multiplier=! * !=Weapon 1 Damage Dice Count=!)**std(!=Weapon 1 Damage Dice Sides=!)) + !=Weapon 1 Damage Feat Bonus=! + ((!=Weapon 1 Range Damage Ability=! / 2) - 5) + !=Weapon 1 Damage Misc Bonus=!] + + + !=Weapon 1 Name=! = [Q(!=Weapon 1 Damage Multiplier=! * !=Weapon 1 Damage Dice Count=!*!=Weapon 1 Damage Dice Sides=!) + (!=Weapon 1 Critical Extra Damage=!) + !=Weapon 1 Damage Feat Bonus=! + ((!=Weapon 1 Range Damage Ability=! / 2) - 5) + !=Weapon 1 Damage Misc Bonus=!] + + + + + + !=Strength=! + + + 0 + + + !=Weapon 1 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 1 Attack Class and Feat Bonus=! + !=Weapon 1 Attack Proficiency Bonus=! + ((!=Weapon 1 Charge Attack Ability=! / 2) - 5) + !=Weapon 1 Attack Misc Bonus=! + !=Weapon 1 Charge Attack Bonus=!] + + + !=Strength=! + + + 1 + + + !=Weapon 1 Name=!: [Q((!=Weapon 1 Damage Multiplier=! * !=Weapon 1 Damage Dice Count=!)**std(!=Weapon 1 Damage Dice Sides=!)) + !=Weapon 1 Damage Feat Bonus=! + ((!=Weapon 1 Charge Damage Ability=! / 2) - 5) + !=Weapon 1 Damage Misc Bonus=! + !=Weapon 1 Charge Damage Bonus=!] + + + !=Weapon 1 Name=! = [Q(!=Weapon 1 Damage Multiplier=! * !=Weapon 1 Damage Dice Count=!*!=Weapon 1 Damage Dice Sides=!) + (!=Weapon 1 Critical Extra Damage=!) + !=Weapon 1 Damage Feat Bonus=! + ((!=Weapon 1 Charge Damage Ability=! / 2) - 5) + !=Weapon 1 Damage Misc Bonus=! + !=Weapon 1 Charge Damage Bonus=!] + + + + + + + + Weapon 2 Name + + + + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 1 + + + 4 + + + 1 + + + 0 + + + + + + + + !=Strength=! + + + !=Weapon 2 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 2 Attack Class and Feat Bonus=! + !=Weapon 2 Attack Proficiency Bonus=! + ((!=Weapon 2 Melee Attack Ability=! / 2) - 5) + !=Weapon 2 Attack Misc Bonus=!] + + + !=Strength=! + + + !=Weapon 2 Name=!: [Q((!=Weapon 2 Damage Multiplier=! * !=Weapon 2 Damage Dice Count=!)**std(!=Weapon 2 Damage Dice Sides=!)) + !=Weapon 2 Damage Feat Bonus=! + ((!=Weapon 2 Melee Damage Ability=! / 2) - 5) + !=Weapon 2 Damage Misc Bonus=!] + + + !=Weapon 2 Name=! = [Q(!=Weapon 2 Damage Multiplier=! * !=Weapon 2 Damage Dice Count=!*!=Weapon 2 Damage Dice Sides=!) + (!=Weapon 2 Critical Extra Damage=!) + !=Weapon 2 Damage Feat Bonus=! + ((!=Weapon 2 Melee Damage Ability=! / 2) - 5) + !=Weapon 2 Damage Misc Bonus=!] + + + + + + !=Dexterity=! + + + !=Weapon 2 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 2 Attack Class and Feat Bonus=! + !=Weapon 2 Attack Proficiency Bonus=! + ((!=Weapon 2 Range Attack Ability=! / 2) - 5) + !=Weapon 2 Attack Misc Bonus=!] + + + !=Dexterity=! + + + !=Weapon 2 Name=!: [Q((!=Weapon 2 Damage Multiplier=! * !=Weapon 2 Damage Dice Count=!)**std(!=Weapon 2 Damage Dice Sides=!)) + !=Weapon 2 Damage Feat Bonus=! + ((!=Weapon 2 Range Damage Ability=! / 2) - 5) + !=Weapon 2 Damage Misc Bonus=!] + + + !=Weapon 2 Name=! = [Q(!=Weapon 2 Damage Multiplier=! * !=Weapon 2 Damage Dice Count=!*!=Weapon 2 Damage Dice Sides=!) + (!=Weapon 2 Critical Extra Damage=!) + !=Weapon 2 Damage Feat Bonus=! + ((!=Weapon 2 Range Damage Ability=! / 2) - 5) + !=Weapon 2 Damage Misc Bonus=!] + + + + + + !=Strength=! + + + 0 + + + !=Weapon 2 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 2 Attack Class and Feat Bonus=! + !=Weapon 2 Attack Proficiency Bonus=! + ((!=Weapon 2 Charge Attack Ability=! / 2) - 5) + !=Weapon 2 Attack Misc Bonus=! + !=Weapon 2 Charge Attack Bonus=!] + + + !=Strength=! + + + 1 + + + !=Weapon 2 Name=!: [Q((!=Weapon 2 Damage Multiplier=! * !=Weapon 2 Damage Dice Count=!)**std(!=Weapon 2 Damage Dice Sides=!)) + !=Weapon 2 Damage Feat Bonus=! + ((!=Weapon 2 Charge Damage Ability=! / 2) - 5) + !=Weapon 2 Damage Misc Bonus=! + !=Weapon 2 Charge Damage Bonus=!] + + + !=Weapon 2 Name=! = [Q(!=Weapon 2 Damage Multiplier=! * !=Weapon 2 Damage Dice Count=!*!=Weapon 2 Damage Dice Sides=!) + (!=Weapon 2 Critical Extra Damage=!) + !=Weapon 2 Damage Feat Bonus=! + ((!=Weapon 2 Charge Damage Ability=! / 2) - 5) + !=Weapon 2 Damage Misc Bonus=! + !=Weapon 2 Charge Damage Bonus=!] + + + + + + + + Weapon 3 Name + + + + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 1 + + + 4 + + + 1 + + + 0 + + + + + + + + !=Strength=! + + + !=Weapon 3 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 3 Attack Class and Feat Bonus=! + !=Weapon 3 Attack Proficiency Bonus=! + ((!=Weapon 3 Melee Attack Ability=! / 2) - 5) + !=Weapon 3 Attack Misc Bonus=!] + + + !=Strength=! + + + !=Weapon 3 Name=!: [Q((!=Weapon 3 Damage Multiplier=! * !=Weapon 3 Damage Dice Count=!)**std(!=Weapon 3 Damage Dice Sides=!)) + !=Weapon 3 Damage Feat Bonus=! + ((!=Weapon 3 Melee Damage Ability=! / 2) - 5) + !=Weapon 3 Damage Misc Bonus=!] + + + !=Weapon 3 Name=! = [Q(!=Weapon 3 Damage Multiplier=! * !=Weapon 3 Damage Dice Count=!*!=Weapon 3 Damage Dice Sides=!) + (!=Weapon 3 Critical Extra Damage=!) + !=Weapon 3 Damage Feat Bonus=! + ((!=Weapon 3 Melee Damage Ability=! / 2) - 5) + !=Weapon 3 Damage Misc Bonus=!] + + + + + + !=Dexterity=! + + + !=Weapon 3 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 3 Attack Class and Feat Bonus=! + !=Weapon 3 Attack Proficiency Bonus=! + ((!=Weapon 3 Range Attack Ability=! / 2) - 5) + !=Weapon 3 Attack Misc Bonus=!] + + + !=Dexterity=! + + + !=Weapon 3 Name=!: [Q((!=Weapon 3 Damage Multiplier=! * !=Weapon 3 Damage Dice Count=!)**std(!=Weapon 3 Damage Dice Sides=!)) + !=Weapon 3 Damage Feat Bonus=! + ((!=Weapon 3 Range Damage Ability=! / 2) - 5) + !=Weapon 3 Damage Misc Bonus=!] + + + !=Weapon 3 Name=! = [Q(!=Weapon 3 Damage Multiplier=! * !=Weapon 3 Damage Dice Count=!*!=Weapon 3 Damage Dice Sides=!) + (!=Weapon 3 Critical Extra Damage=!) + !=Weapon 3 Damage Feat Bonus=! + ((!=Weapon 3 Range Damage Ability=! / 2) - 5) + !=Weapon 3 Damage Misc Bonus=!] + + + + + + !=Strength=! + + + 0 + + + !=Weapon 3 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 3 Attack Class and Feat Bonus=! + !=Weapon 3 Attack Proficiency Bonus=! + ((!=Weapon 3 Charge Attack Ability=! / 2) - 5) + !=Weapon 3 Attack Misc Bonus=! + !=Weapon 3 Charge Attack Bonus=!] + + + !=Strength=! + + + 1 + + + !=Weapon 3 Name=!: [Q((!=Weapon 3 Damage Multiplier=! * !=Weapon 3 Damage Dice Count=!)**std(!=Weapon 3 Damage Dice Sides=!)) + !=Weapon 3 Damage Feat Bonus=! + ((!=Weapon 3 Charge Damage Ability=! / 2) - 5) + !=Weapon 3 Damage Misc Bonus=! + !=Weapon 3 Charge Damage Bonus=!] + + + !=Weapon 3 Name=! = [Q(!=Weapon 3 Damage Multiplier=! * !=Weapon 3 Damage Dice Count=!*!=Weapon 3 Damage Dice Sides=!) + (!=Weapon 3 Critical Extra Damage=!) + !=Weapon 3 Damage Feat Bonus=! + ((!=Weapon 3 Charge Damage Ability=! / 2) - 5) + !=Weapon 3 Damage Misc Bonus=! + !=Weapon 3 Charge Damage Bonus=!] + + + + + + + + Weapon 4 Name + + + + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 1 + + + 4 + + + 1 + + + 0 + + + + + + + + !=Strength=! + + + !=Weapon 4 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 4 Attack Class and Feat Bonus=! + !=Weapon 4 Attack Proficiency Bonus=! + ((!=Weapon 4 Melee Attack Ability=! / 2) - 5) + !=Weapon 4 Attack Misc Bonus=!] + + + !=Strength=! + + + !=Weapon 4 Name=!: [Q((!=Weapon 4 Damage Multiplier=! * !=Weapon 4 Damage Dice Count=!)**std(!=Weapon 4 Damage Dice Sides=!)) + !=Weapon 4 Damage Feat Bonus=! + ((!=Weapon 4 Melee Damage Ability=! / 2) - 5) + !=Weapon 4 Damage Misc Bonus=!] + + + !=Weapon 4 Name=! = [Q(!=Weapon 4 Damage Multiplier=! * !=Weapon 4 Damage Dice Count=!*!=Weapon 4 Damage Dice Sides=!) + (!=Weapon 4 Critical Extra Damage=!) + !=Weapon 4 Damage Feat Bonus=! + ((!=Weapon 4 Melee Damage Ability=! / 2) - 5) + !=Weapon 4 Damage Misc Bonus=!] + + + + + + !=Dexterity=! + + + !=Weapon 4 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 4 Attack Class and Feat Bonus=! + !=Weapon 4 Attack Proficiency Bonus=! + ((!=Weapon 4 Range Attack Ability=! / 2) - 5) + !=Weapon 4 Attack Misc Bonus=!] + + + !=Dexterity=! + + + !=Weapon 4 Name=!: [Q((!=Weapon 4 Damage Multiplier=! * !=Weapon 4 Damage Dice Count=!)**std(!=Weapon 4 Damage Dice Sides=!)) + !=Weapon 4 Damage Feat Bonus=! + ((!=Weapon 4 Range Damage Ability=! / 2) - 5) + !=Weapon 4 Damage Misc Bonus=!] + + + !=Weapon 4 Name=! = [Q(!=Weapon 4 Damage Multiplier=! * !=Weapon 4 Damage Dice Count=!*!=Weapon 4 Damage Dice Sides=!) + (!=Weapon 4 Critical Extra Damage=!) + !=Weapon 4 Damage Feat Bonus=! + ((!=Weapon 4 Range Damage Ability=! / 2) - 5) + !=Weapon 4 Damage Misc Bonus=!] + + + + + + !=Strength=! + + + 0 + + + !=Weapon 4 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 4 Attack Class and Feat Bonus=! + !=Weapon 4 Attack Proficiency Bonus=! + ((!=Weapon 4 Charge Attack Ability=! / 2) - 5) + !=Weapon 4 Attack Misc Bonus=! + !=Weapon 4 Charge Attack Bonus=!] + + + !=Strength=! + + + 1 + + + !=Weapon 4 Name=!: [Q((!=Weapon 4 Damage Multiplier=! * !=Weapon 4 Damage Dice Count=!)**std(!=Weapon 4 Damage Dice Sides=!)) + !=Weapon 4 Damage Feat Bonus=! + ((!=Weapon 4 Charge Damage Ability=! / 2) - 5) + !=Weapon 4 Damage Misc Bonus=! + !=Weapon 4 Charge Damage Bonus=!] + + + !=Weapon 4 Name=! = [Q(!=Weapon 4 Damage Multiplier=! * !=Weapon 4 Damage Dice Count=!*!=Weapon 4 Damage Dice Sides=!) + (!=Weapon 4 Critical Extra Damage=!) + !=Weapon 4 Damage Feat Bonus=! + ((!=Weapon 4 Charge Damage Ability=! / 2) - 5) + !=Weapon 4 Damage Misc Bonus=! + !=Weapon 4 Charge Damage Bonus=!] + + + + + + + + Weapon 5 Name + + + + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 1 + + + 4 + + + 1 + + + 0 + + + + + + + + !=Strength=! + + + !=Weapon 5 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 5 Attack Class and Feat Bonus=! + !=Weapon 5 Attack Proficiency Bonus=! + ((!=Weapon 5 Melee Attack Ability=! / 2) - 5) + !=Weapon 5 Attack Misc Bonus=!] + + + !=Strength=! + + + !=Weapon 5 Name=!: [Q((!=Weapon 5 Damage Multiplier=! * !=Weapon 5 Damage Dice Count=!)**std(!=Weapon 5 Damage Dice Sides=!)) + !=Weapon 5 Damage Feat Bonus=! + ((!=Weapon 5 Melee Damage Ability=! / 2) - 5) + !=Weapon 5 Damage Misc Bonus=!] + + + !=Weapon 5 Name=! = [Q(!=Weapon 5 Damage Multiplier=! * !=Weapon 5 Damage Dice Count=!*!=Weapon 5 Damage Dice Sides=!) + (!=Weapon 5 Critical Extra Damage=!) + !=Weapon 5 Damage Feat Bonus=! + ((!=Weapon 5 Melee Damage Ability=! / 2) - 5) + !=Weapon 5 Damage Misc Bonus=!] + + + + + + !=Dexterity=! + + + !=Weapon 5 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 5 Attack Class and Feat Bonus=! + !=Weapon 5 Attack Proficiency Bonus=! + ((!=Weapon 5 Range Attack Ability=! / 2) - 5) + !=Weapon 5 Attack Misc Bonus=!] + + + !=Dexterity=! + + + !=Weapon 5 Name=!: [Q((!=Weapon 5 Damage Multiplier=! * !=Weapon 5 Damage Dice Count=!)**std(!=Weapon 5 Damage Dice Sides=!)) + !=Weapon 5 Damage Feat Bonus=! + ((!=Weapon 5 Range Damage Ability=! / 2) - 5) + !=Weapon 5 Damage Misc Bonus=!] + + + !=Weapon 5 Name=! = [Q(!=Weapon 5 Damage Multiplier=! * !=Weapon 5 Damage Dice Count=!*!=Weapon 5 Damage Dice Sides=!) + (!=Weapon 5 Critical Extra Damage=!) + !=Weapon 5 Damage Feat Bonus=! + ((!=Weapon 5 Range Damage Ability=! / 2) - 5) + !=Weapon 5 Damage Misc Bonus=!] + + + + + + !=Strength=! + + + 0 + + + !=Weapon 5 Name=!: [Q1d20 + (!=Character Level=! / 2) + !=Weapon 5 Attack Class and Feat Bonus=! + !=Weapon 5 Attack Proficiency Bonus=! + ((!=Weapon 5 Charge Attack Ability=! / 2) - 5) + !=Weapon 5 Attack Misc Bonus=! + !=Weapon 5 Charge Attack Bonus=!] + + + !=Strength=! + + + 1 + + + !=Weapon 5 Name=!: [Q((!=Weapon 5 Damage Multiplier=! * !=Weapon 5 Damage Dice Count=!)**std(!=Weapon 5 Damage Dice Sides=!)) + !=Weapon 5 Damage Feat Bonus=! + ((!=Weapon 5 Charge Damage Ability=! / 2) - 5) + !=Weapon 5 Damage Misc Bonus=! + !=Weapon 5 Charge Damage Bonus=!] + + + !=Weapon 5 Name=! = [Q(!=Weapon 5 Damage Multiplier=! * !=Weapon 5 Damage Dice Count=!*!=Weapon 5 Damage Dice Sides=!) + (!=Weapon 5 Critical Extra Damage=!) + !=Weapon 5 Damage Feat Bonus=! + ((!=Weapon 5 Charge Damage Ability=! / 2) - 5) + !=Weapon 5 Damage Misc Bonus=! + !=Weapon 5 Charge Damage Bonus=!] + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + + + + !=Strength=! + + + 0 + + + Bull Rush: [Q1d20 + (!=Character Level=! / 2) + !=Unarmed Attack Class and Feat Bonus=! + !=Unarmed Attack Proficiency Bonus=! + ((!=Unarmed Rush Attack Ability=! / 2) - 5) + !=Unarmed Attack Misc Bonus=! + !=Unarmed Rush Attack Bonus=!] + + + + + + !=Strength=! + + + 0 + + + Grab: [Q1d20 + (!=Character Level=! / 2) + !=Unarmed Attack Class and Feat Bonus=! + !=Unarmed Attack Proficiency Bonus=! + ((!=Unarmed Grab Attack Ability=! / 2) - 5) + !=Unarmed Attack Misc Bonus=! + !=Unarmed Grab Attack Bonus=!] + + + + + + !=Strength=! + + + 0 + + + Fortitude + + + Escape: [Q1d20 + (!=Character Level=! / 2) + !=Unarmed Attack Class and Feat Bonus=! + !=Unarmed Attack Proficiency Bonus=! + ((!=Unarmed Escape Attack Ability=! / 2) - 5) + !=Unarmed Attack Misc Bonus=! + !=Unarmed Escape Attack Bonus=!] vs. !=Unarmed Escape Target Defense=! + + + + + + + + + + + 1 + + + + + Power Name + Uses + Remaining + + + Action Point + 1 + 1 + + + Second Wind + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + + + + + + + Power Name + Uses + Remaining + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + 1 + 1 + + + + + + + + + + Power Name + Recharge + Uses + Remain + + + + + 1 + 1 + + + + + 1 + 1 + + + + + 1 + 1 + + + + + 1 + 1 + + + + + 1 + 1 + + + + + 1 + 1 + + + + + 1 + 1 + + + + + 1 + 1 + + + + + 1 + 1 + + + + + + + + + + + + + At-Will 1 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=At-Will 1 Name=! + + + /me uses power !=At-Will 1 Name=! + + + /me uses power !=At-Will 1 Name=! + + + /me uses power !=At-Will 1 Name=! + + + + + + + + At-Will 2 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=At-Will 2 Name=! + + + /me uses power !=At-Will 2 Name=! + + + /me uses power !=At-Will 2 Name=! + + + /me uses power !=At-Will 2 Name=! + + + + + + + + At-Will 3 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=At-Will 3 Name=! + + + /me uses power !=At-Will 3 Name=! + + + /me uses power !=At-Will 3 Name=! + + + /me uses power !=At-Will 3 Name=! + + + + + + + + At-Will 4 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=At-Will 4 Name=! + + + /me uses power !=At-Will 4 Name=! + + + /me uses power !=At-Will 4 Name=! + + + /me uses power !=At-Will 4 Name=! + + + + + + + + At-Will 5 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=At-Will 5 Name=! + + + /me uses power !=At-Will 5 Name=! + + + /me uses power !=At-Will 5 Name=! + + + /me uses power !=At-Will 5 Name=! + + + + + + + + At-Will 6 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=At-Will 6 Name=! + + + /me uses power !=At-Will 6 Name=! + + + /me uses power !=At-Will 6 Name=! + + + /me uses power !=At-Will 6 Name=! + + + + + + + + At-Will 7 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=At-Will 7 Name=! + + + /me uses power !=At-Will 7 Name=! + + + /me uses power !=At-Will 7 Name=! + + + /me uses power !=At-Will 7 Name=! + + + + + + + + At-Will 8 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=At-Will 8 Name=! + + + /me uses power !=At-Will 8 Name=! + + + /me uses power !=At-Will 8 Name=! + + + /me uses power !=At-Will 8 Name=! + + + + + + + + At-Will 9 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=At-Will 9 Name=! + + + /me uses power !=At-Will 9 Name=! + + + /me uses power !=At-Will 9 Name=! + + + /me uses power !=At-Will 9 Name=! + + + + + + + + + + Encounter 1 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Encounter 1 Name=! + + + /me uses power !=Encounter 1 Name=! + + + /me uses power !=Encounter 1 Name=! + + + /me uses power !=Encounter 1 Name=! + + + + + + + + Encounter 2 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Encounter 2 Name=! + + + /me uses power !=Encounter 2 Name=! + + + /me uses power !=Encounter 2 Name=! + + + /me uses power !=Encounter 2 Name=! + + + + + + + + Encounter 3 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Encounter 3 Name=! + + + /me uses power !=Encounter 3 Name=! + + + /me uses power !=Encounter 3 Name=! + + + /me uses power !=Encounter 3 Name=! + + + + + + + + Encounter 4 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Encounter 4 Name=! + + + /me uses power !=Encounter 4 Name=! + + + /me uses power !=Encounter 4 Name=! + + + /me uses power !=Encounter 4 Name=! + + + + + + + + Encounter 5 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Encounter 5 Name=! + + + /me uses power !=Encounter 5 Name=! + + + /me uses power !=Encounter 5 Name=! + + + /me uses power !=Encounter 5 Name=! + + + + + + + + Encounter 6 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Encounter 6 Name=! + + + /me uses power !=Encounter 6 Name=! + + + /me uses power !=Encounter 6 Name=! + + + /me uses power !=Encounter 6 Name=! + + + + + + + + Encounter 7 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Encounter 7 Name=! + + + /me uses power !=Encounter 7 Name=! + + + /me uses power !=Encounter 7 Name=! + + + /me uses power !=Encounter 7 Name=! + + + + + + + + Encounter 8 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Encounter 8 Name=! + + + /me uses power !=Encounter 8 Name=! + + + /me uses power !=Encounter 8 Name=! + + + /me uses power !=Encounter 8 Name=! + + + + + + + + Encounter 9 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Encounter 9 Name=! + + + /me uses power !=Encounter 9 Name=! + + + /me uses power !=Encounter 9 Name=! + + + /me uses power !=Encounter 9 Name=! + + + + + + + + + + Daily 1 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Daily 1 Name=! + + + /me uses power !=Daily 1 Name=! + + + /me uses power !=Daily 1 Name=! + + + /me uses power !=Daily 1 Name=! + + + + + + + + Daily 2 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Daily 2 Name=! + + + /me uses power !=Daily 2 Name=! + + + /me uses power !=Daily 2 Name=! + + + /me uses power !=Daily 2 Name=! + + + + + + + + Daily 3 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Daily 3 Name=! + + + /me uses power !=Daily 3 Name=! + + + /me uses power !=Daily 3 Name=! + + + /me uses power !=Daily 3 Name=! + + + + + + + + Daily 4 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Daily 4 Name=! + + + /me uses power !=Daily 4 Name=! + + + /me uses power !=Daily 4 Name=! + + + /me uses power !=Daily 4 Name=! + + + + + + + + Daily 5 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Daily 5 Name=! + + + /me uses power !=Daily 5 Name=! + + + /me uses power !=Daily 5 Name=! + + + /me uses power !=Daily 5 Name=! + + + + + + + + Daily 6 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Daily 6 Name=! + + + /me uses power !=Daily 6 Name=! + + + /me uses power !=Daily 6 Name=! + + + /me uses power !=Daily 6 Name=! + + + + + + + + Daily 7 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Daily 7 Name=! + + + /me uses power !=Daily 7 Name=! + + + /me uses power !=Daily 7 Name=! + + + /me uses power !=Daily 7 Name=! + + + + + + + + Daily 8 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Daily 8 Name=! + + + /me uses power !=Daily 8 Name=! + + + /me uses power !=Daily 8 Name=! + + + /me uses power !=Daily 8 Name=! + + + + + + + + Daily 9 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 0 + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /me uses power !=Daily 9 Name=! + + + /me uses power !=Daily 9 Name=! + + + /me uses power !=Daily 9 Name=! + + + /me uses power !=Daily 9 Name=! + + + + + + + + + + Utility 1 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + + + + + + Utility 2 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + + + + + + Utility 3 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + + + + + + Utility 4 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + + + + + + Utility 5 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + + + + + + Utility 6 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + + + + + + Utility 7 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + + + + + + Utility 8 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + + + + + + Utility 9 Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AC + + + !=Strength=! + + + 0 + + + !=Strength=! + + + 0 + + + + + + 1 + + + 0 + + + 6 + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Passive Insight + + + + Passive Perception + + + + + + + + Acrobatics + + + + Arcana + + + + Athletics + + + + Bluff + + + + Diplomacy + + + + Dungeoneering + + + + Endurance + + + + Heal + + + + History + + + + Insight + + + + Intimidate + + + + Nature + + + + Perception + + + + Religion + + + + Stealth + + + + Streetwise + + + + Thievery + + + + + + + + + + + + + Player Name + + + + Character Name + + + + Deity + + + + Alignment + + + + Race + + + + + + + + Level + + + + Class + + + + Paragon Path + + + + Epic Destiny + + + + + + + + + + + Maximum Health + + + + Bloodied At + + + + + + + + Healing Surge Value + + + + Healing Surges per Day + + + + + + + + Aromor Class + + + + Fortitude + + + + Reflex + + + + Willpower + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + 10 + + + 10 + + + 10 + + + 10 + + + 10 + + + 10 + + + + + + + + + + + + + + + + Maximum Health + 10 + + + Bloodied At + 10 + + + Current Health + 5 + + + Temporary Health + 0 + + + + + + + Healing Surge Value + 5 + + + Healing Surges per Day + 6 + + + Healing Surges Remaining + 6 + + + + + + + Death Saving Throw Attempts + 0 + + + + + + + + + 10 + + + 10 + + + 10 + + + 10 + + + + + 0 + + + [!=Companion Base AC=! + !=Companion Level=!] + + + + + 0 + + + [!=Companion Base Fort=! + !=Companion Level=!] + + + + + 0 + + + [!=Companion Base Ref=! + !=Companion Level=!] + + + + + 0 + + + [!=Companion Base Will=! + !=Companion Level=!] + + + + + + + + + + Attack 1 Name + + + 0 + + + !=Attack 1 Name=!: [Q1d20 + !=Companion Level=! + !=Attack 1 Bonus=!] + + + !=Strength=! + + + 0 + + + 1 + + + 4 + + + 0 + + + !=Attack 1 Name=!: [Q(!=Attack 1 Damage Dice Count=!d!=Attack 1 Damage Dice Sides=!) + ((!=Attack 1 Damage Ability=! / 2) - 5) + !=Attack 1 Damage Other Bonus=!] + + + !=Attack 1 Name=! = [Q(!=Attack 1 Damage Dice Count=!*!=Attack 1 Damage Dice Sides=!) + (!=Attack 1 Critical Extra Damage=!) + ((!=Attack 1 Damage Ability=! / 2) - 5) + !=Attack 1 Damage Other Bonus=!] + + + + + + Attack 2 Name + + + 0 + + + !=Attack 2 Name=!: [Q1d20 + !=Companion Level=! + !=Attack 2 Bonus=!] + + + !=Strength=! + + + 0 + + + 1 + + + 4 + + + 0 + + + !=Attack 2 Name=!: [Q(!=Attack 2 Damage Dice Count=!d!=Attack 2 Damage Dice Sides=!) + ((!=Attack 2 Damage Ability=! / 2) - 5) + !=Attack 2 Damage Other Bonus=!] + + + !=Attack 2 Name=! = [Q(!=Attack 2 Damage Dice Count=!*!=Attack 2 Damage Dice Sides=!) + (!=Attack 2 Critical Extra Damage=!) + ((!=Attack 2 Damage Ability=! / 2) - 5) + !=Attack 2 Damage Other Bonus=!] + + + + + + \ No newline at end of file diff -r 8e77f169f324 -r fc48380f0c9f orpg/templates/nodes/split.xml --- a/orpg/templates/nodes/split.xml Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/templates/nodes/split.xml Mon May 03 03:30:11 2010 -0500 @@ -1,3 +1,2 @@ - - - \ No newline at end of file + + diff -r 8e77f169f324 -r fc48380f0c9f orpg/templates/nodes/textctrl.xml --- a/orpg/templates/nodes/textctrl.xml Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/templates/nodes/textctrl.xml Mon May 03 03:30:11 2010 -0500 @@ -1,4 +1,1 @@ - - - text - +Text \ No newline at end of file diff -r 8e77f169f324 -r fc48380f0c9f orpg/tools/InterParse.py --- a/orpg/tools/InterParse.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/tools/InterParse.py Mon May 03 03:30:11 2010 -0500 @@ -1,35 +1,75 @@ +#!/usr/bin/env python +# Copyright (C) 2000-2010 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. +# -- +# +# File: InterParse.py +# Author: +# Maintainer: Tyler Starke (Traipse) +# Version: +# $Id: InterParse.py,v Traipse 'Ornery-Orc' prof.ebral Exp $ +# +# Description: InterParse = Interpretor Parser. This class parses all of the node referencing. +# + from orpg.orpgCore import component import re from orpg.tools.orpg_log import logger from wx import TextEntryDialog, ID_OK +from xml.etree.ElementTree import iselement class InterParse(): def __init__(self): pass - def Post(self, s, send=False, myself=False): - s = self.Normalize(s) - component.get('chat').set_colors() - component.get('chat').Post(s, send, myself) + def Post(self, s, tab=False, send=False, myself=False): + if not tab: tab = component.get('chat') + s = self.Normalize(s, tab) + tab.set_colors() + tab.Post(s, send, myself) - def Normalize(self, s): - for plugin_fname in component.get('chat').activeplugins.keys(): - plugin = component.get('chat').activeplugins[plugin_fname] + def ParseLogic(self, s, node): + 'Nodes now parse through ParsLogic. Easily add new parse rules right here!!' + s = self.NameSpaceE(s) + s = self.NameSpaceI(s, node) + s = self.NodeMap(s, node) + s = self.NodeParent(s, node.get('map')) + return s + + def Normalize(self, s, tab): + for plugin_fname in tab.activeplugins.keys(): + plugin = tab.activeplugins[plugin_fname] try: s = plugin.pre_parse(s) except Exception, e: if str(e) != "'module' object has no attribute 'post_msg'": - logger.general(traceback.format_exc()) + #logger.general(traceback.format_exc()) logger.general("EXCEPTION: " + str(e)) - if component.get('chat').parsed == 0: + if tab.parsed == 0: + s = self.NameSpaceE(s) s = self.Node(s) s = self.Dice(s) - s = self.Filter(s) - component.get('chat').parsed = 1 + s = self.Filter(s, tab) + tab.parsed = 1 return s - def Filter(self, s): - s = component.get('chat').GetFilteredText(s) + def Filter(self, s, tab): + s = tab.GetFilteredText(s) return s def Node(self, s): @@ -87,6 +127,96 @@ dlg.Destroy() return s + def LocationCheck(self, node, tree_map, new_map, find): + if node == 'Invalid Reference!': return node + namespace = node.getiterator('nodehandler'); tr = tree_map.split('::') + newstr = '' + for name in namespace: + try: t = new_map.index(name.get('name'))-1 + except: t = 0 + if find[0] == name.get('name'): + s = '::'.join(new_map[:len(tr)-t])+'::'+'::'.join(find) + newstr = self.NameSpaceE('!&' +s+ '&!') + break + if newstr != '': return newstr + else: + del new_map[len(new_map)-1] + node = self.get_node(new_map) + newstr = self.LocationCheck(node, tree_map, new_map, find) + return newstr + + def FutureCheck(self, node, next): + future = node.getiterator('nodehandler') + for advance in future: + if next == advance.get('name'): return True + return False + + def NameSpaceI(self, s, node): + reg1 = re.compile('(!"(.*?)"!)') ## Easter Egg! + """If you found this you found my first easter egg. I was tired of people telling me multiple + references syntax for the game tree is confusing, so I dropped this in there without telling + anyone. Using !" :: "! will allow you to use an internal namespace from within another internal + namespace -- TaS, Prof. Ebral""" + reg2 = re.compile("(!=(.*?)=!)") + matches = reg1.findall(s) + reg2.findall(s) + tree_map = node.get('map') + for i in xrange(0,len(matches)): + ## Build the new tree_map + new_map = tree_map.split('::') + find = matches[i][1].split('::') + ## Backwards Reference the Parent Children + node = self.get_node(new_map) + newstr = self.LocationCheck(node, tree_map, new_map, find) + s = s.replace(matches[i][0], newstr, 1) + s = self.ParseLogic(s, node) + return s + + def NameSpaceE(self, s): + reg = re.compile("(!&(.*?)&!)") + matches = reg.findall(s) + newstr = False + nodeable = ['rpg_grid_handler', 'container_handler', + 'group_handler', 'tabber_handler', + 'splitter_handler', 'form_handler', 'textctrl_handler'] + for i in xrange(0,len(matches)): + find = matches[i][1].split('::') + node = component.get('tree').xml_root + if not iselement(node): + s = s.replace(matches[i][0], 'Invalid Reference!', 1); + s = self.NameSpaceE(s) + return s + for x in xrange(0, len(find)): + namespace = node.getiterator('nodehandler') + for node in namespace: + if find[x] == node.get('name'): + if node.get('class') not in nodeable: continue + if node.get('class') == 'rpg_grid_handler': + try: newstr = self.NameSpaceGrid(find[x+1], node); break + except: newstr = 'Invalid Grid Reference!' + try: + if self.FutureCheck(node, find[x+1]): break + else: continue + except: + if x == len(find)-1: + if node.find('text') != None: newstr = str(node.find('text').text) + else: newstr = 'Invalid Reference!' + break + else: break + if not newstr: newstr = 'Invalid Reference!' + s = s.replace(matches[i][0], newstr, 1) + s = self.ParseLogic(s, node) + return s + + def NameSpaceGrid(self, s, node): + cell = tuple(s.strip('(').strip(')').split(',')) + grid = node.find('grid') + rows = grid.findall('row') + try: + col = rows[int(self.Dice(cell[0]))-1].findall('cell') + s = self.ParseLogic(col[int(self.Dice(cell[1]))-1].text, node) or 'No Cell Data' + except: s = 'Invalid Grid Reference!' + return s + def NodeMap(self, s, node): """Parses player input for embedded nodes rolls""" cur_loc = 0 @@ -112,7 +242,7 @@ del new_map[len(new_map)-1] parent_map = matches[i][1].split('::') ## Backwards Reference the Parent Children - child_node = self.get_node('::'.join(new_map)) + child_node = self.get_node(new_map) newstr = self.get_root(child_node, tree_map, new_map, parent_map) s = s.replace(matches[i][0], newstr, 1) s = self.Node(s) @@ -130,14 +260,13 @@ if newstr != '': return newstr else: del new_map[len(new_map)-1] - child_node = self.get_node('::'.join(new_map)) + child_node = self.get_node(new_map) newstr = self.get_root(child_node, tree_map, new_map, parent_map) return newstr - def get_node(self, s): + def get_node(self, path): return_node = 'Invalid Reference!' value = "" - path = s.split('::') depth = len(path) try: node = component.get('tree').tree_map[path[0]]['node'] except Exception, e: return return_node @@ -147,7 +276,7 @@ def resolve_get_loop(self, node, path, step, depth): if step == depth: return node else: - child_list = node.findall('nodehandler') + child_list = node.getchildren() for child in child_list: if step == depth: break if child.get('name') == path[step]: @@ -195,10 +324,8 @@ if node.get('class') == 'textctrl_handler': s = str(node.find('text').text) else: s = 'Nodehandler for '+ node.get('class') + ' not done!' or 'Invalid Reference!' - else: - s = '' - s = self.NodeMap(s, node) - s = self.NodeParent(s, node.get('map')) + else: s = '' + s = self.ParseLogic(s, node) return s def resolve_grid(self, node, path, step, depth): @@ -208,9 +335,7 @@ grid = node.find('grid') rows = grid.findall('row') col = rows[int(self.Dice(cell[0]))-1].findall('cell') - try: s = self.NodeParent(col[int(self.Dice(cell[1]))-1].text, node.get('map')) or 'No Cell Data' - except: s = 'Invalid Grid Reference!' - try: s = self.NodeMap(col[int(self.Dice(cell[1]))-1].text, node) or 'No Cell Data' + try: s = self.ParseLogic(col[int(self.Dice(cell[1]))-1].text, node) or 'No Cell Data' except: s = 'Invalid Grid Reference!' return s diff -r 8e77f169f324 -r fc48380f0c9f orpg/tools/aliaslib.py --- a/orpg/tools/aliaslib.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/tools/aliaslib.py Mon May 03 03:30:11 2010 -0500 @@ -35,6 +35,7 @@ from orpg.dirpath import dir_struct from orpg.tools.validate import validate from orpg.tools.orpg_settings import settings +from xml.etree.ElementTree import tostring, parse import re class AliasLib(wx.Frame): @@ -458,36 +459,30 @@ self.Fit() def loadFile(self): - f = open(dir_struct["user"] + self.filename, "r") - data = f.read() - f.close() - self.alias = -1 - self.filter = -1 - xml_dom = component.get('xml').parseXml(data) - del data - aliases = xml_dom.getElementsByTagName("alias") + data = parse(dir_struct["user"] + self.filename) + xml_dom = data.getroot() + aliases = xml_dom.findall("alias") alist = [] for alias in aliases: - if alias.hasAttribute("color"): color = alias.getAttribute("color") + if alias.get("color"): color = alias.get("color") else: color = 'Default' - aname = self.MakeSafeHTML(alias.getAttribute("name")) + aname = self.MakeSafeHTML(alias.get("name")) alist.append([aname, color]) alist.sort() self.aliasList = alist - filters = xml_dom.getElementsByTagName("filter") + filters = xml_dom.findall("filter") flist = [] self.regExList = [] for filter in filters: - flist.append(filter.getAttribute("name")) - rules = filter.getElementsByTagName("rule") + flist.append(filter.get("name")) + rules = filter.findall("rule") sub = [] - for rule in rules: sub.append([self.MakeSafeHTML(rule.getAttribute("match")), - self.MakeSafeHTML(rule.getAttribute("sub"))]) + for rule in rules: sub.append([self.MakeSafeHTML(rule.get("match")), + self.MakeSafeHTML(rule.get("sub"))]) self.regExList.append(sub) self.filterList = flist - xml_dom.unlink() - self.alias = -1 - self.filter = -1 + self.alias = 0 + self.filter = 0 def MakeSafeHTML(self, str): return str.replace("&", "&").replace("<", "<").replace(""", '"').replace(">", ">").replace("'", "'") @@ -495,7 +490,7 @@ return str.replace("&", "&").replace("<", "<").replace('"', """).replace(">", ">").replace("'", "'") def ImportFromTree(self, xml_dom): oldfilename = self.filename - if xml_dom.getAttribute('name') == 'Alias Library': + if xml_dom.get('name') == 'Alias Library': dlg = wx.TextEntryDialog(self, "Please Name This Alias Lib", "New Alias Lib") if dlg.ShowModal() == wx.ID_OK: self.filename = dlg.GetValue() + '.alias' @@ -503,11 +498,11 @@ else: dlg.Destroy() return - else: self.filename = xml_dom.getAttribute('name') + '.alias' + else: self.filename = xml_dom.get('name') + '.alias' settings.set_setting('aliasfile', self.filename[:-6]) if oldfilename != self.filename: self.OnMB_FileSave(None, oldfilename) f = open(dir_struct["user"] + self.filename, "w") - f.write(xml_dom.toxml().replace('nodehandler', 'aliaslib').replace('voxchat.', '')) + f.write(tostring(xml_dom).replace('nodehandler', 'aliaslib').replace('voxchat.', '')) f.close() wx.CallAfter(self.loadFile) diff -r 8e77f169f324 -r fc48380f0c9f orpg/tools/orpg_log.py --- a/orpg/tools/orpg_log.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/tools/orpg_log.py Mon May 03 03:30:11 2010 -0500 @@ -62,7 +62,7 @@ def write(self, text): logger.stdout(text) wx.Yield() - #sys.__stdout__.write(text) + sys.__stdout__.write(text) class TrueDebug(object): """A simple debugger. Add debug() to a function and it prints the function name and any objects included. Add an object or a group of objects in ()'s. diff -r 8e77f169f324 -r fc48380f0c9f orpg/tools/orpg_settings.py --- a/orpg/tools/orpg_settings.py Fri Feb 19 19:10:25 2010 -0600 +++ b/orpg/tools/orpg_settings.py Mon May 03 03:30:11 2010 -0500 @@ -183,14 +183,17 @@ def dieroller_ok(self, changes): rm = component.get('DiceManager') + cur_die = rm.getRoller() try: rm.setRoller(changes) self.chat.SystemPost('You have changed your die roller to the "' + rm.getRoller() + '" roller.') except Exception, e: - print e rm.setRoller('std') - self.settings.change('dieroller', 'std') - self.chat.SystemPost('"' + changes + '" is an invalid roller. Setting roller to "std"') + rm.setRoller(cur_die) + self.settings.change('dieroller', cur_die) + self.chat.SystemPost('"' + changes + '" is an invalid roller.') + self.chat.InfoPost('Available die rollers: ' +str(rm.listRollers()) ) + self.chat.InfoPost('You are using the "' +cur_die+ '" die roller.') def colortree_ok(self, changes): top_frame = component.get('frame') diff -r 8e77f169f324 -r fc48380f0c9f plugins/xxhiddendice.py --- a/plugins/xxhiddendice.py Fri Feb 19 19:10:25 2010 -0600 +++ b/plugins/xxhiddendice.py Mon May 03 03:30:11 2010 -0500 @@ -1,5 +1,6 @@ import os, re, wx import orpg.pluginhandler +from orpg.tools.InterParse import Parse class Plugin(orpg.pluginhandler.PluginHandler): # Initialization subroutine. @@ -42,7 +43,7 @@ m = re.search(self.dicere, text) while m: roll = "[" + m.group(1) + "]" - self.hiddenrolls += [self.chat.ParseDice(roll)] + self.hiddenrolls += [Parse.Dice(roll)] text = text[:m.start()] + "(hidden roll)" + text[m.end():] m = re.search(self.dicere, text) return text diff -r 8e77f169f324 -r fc48380f0c9f plugins/xxsimpleinit.py --- a/plugins/xxsimpleinit.py Fri Feb 19 19:10:25 2010 -0600 +++ b/plugins/xxsimpleinit.py Mon May 03 03:30:11 2010 -0500 @@ -1,7 +1,7 @@ -import os +import os, wx import orpg.pluginhandler from orpg.orpgCore import * -import wx +from orpg.tools.InterParse import Parse class Plugin(orpg.pluginhandler.PluginHandler): # Initialization subroutine. @@ -215,7 +215,7 @@ for i in xrange(0, self.frame.initList.GetItemCount()): self.frame.currentInit = i if self.frame.currentInit.manual == 'No': - initRoll = self.chat.ParseDice('[1d20' + self.frame.currentInit.initMod + ']') + initRoll = Parse.Dice('[1d20' + self.frame.currentInit.initMod + ']') initRoll = initRoll.split('(') initRoll = initRoll[1].replace(')','') diff -r 8e77f169f324 -r fc48380f0c9f plugins/xxstdnamespace.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/xxstdnamespace.py Mon May 03 03:30:11 2010 -0500 @@ -0,0 +1,101 @@ +import os, wx, re +import orpg.pluginhandler +from orpg.tools.InterParse import Parse +from orpg.orpgCore import component +from xml.etree.ElementTree import iselement + +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) + + # The Following code should be edited to contain the proper information + self.name = 'Standard Namespace' + self.author = 'Prof. Ebral' + self.help = 'The Standard Namespace plugin allows for users of Traipse to use ' + self.help += 'the Standard Namespace syntax of !@ :: @!\n\n' + self.help += 'This plugin modifies the External method, so context sensivity\n' + self.help += 'is not calculated when using the Standard syntax. References must ' + self.help += 'have a unique name.' + + self.parseMethods = {'Traipse': Parse.NameSpaceE, 'Standard': self.NameSpaceS} + + def NameSpaceS(self, s): ## Re define NameSpace External + reg1 = re.compile("(!@(.*?)@!)") ## Inlcude 'Standard' method + reg2 = re.compile("(!&(.*?)&!)") + matches = reg1.findall(s) + reg2.findall(s) + newstr = False + nodeable = ['rpg_grid_handler', 'container_handler', + 'group_handler', 'tabber_handler', + 'splitter_handler', 'form_handler', 'textctrl_handler'] + for i in xrange(0,len(matches)): + find = matches[i][1].split('::') + node = component.get('tree').xml_root + if not iselement(node): + s = s.replace(matches[i][0], 'Invalid Reference!', 1); + s = Parse.NameSpaceE(s) + return s + for x in xrange(0, len(find)): + namespace = node.getiterator('nodehandler') + for node in namespace: + if find[x] == node.get('name'): + if node.get('class') not in nodeable: continue + if node.get('class') == 'rpg_grid_handler': + try: newstr = self.NameSpaceGrid(find[x+1], node); break + except: newstr = 'Invalid Grid Reference!' + try: + if Parse.FutureCheck(node, find[x+1]): break + else: continue + except: + if x == len(find)-1: + if node.find('text') != None: newstr = str(node.find('text').text) + else: newstr = 'Invalid Reference!' + break + else: break + if not newstr: newstr = 'Invalid Reference!' + s = s.replace(matches[i][0], newstr, 1) + s = Parse.ParseLogic(s, node) + return s + + def plugin_menu(self): + ## This is a standardized Menu item. It connects to plugin_toggle where you can set events. + self.menu = wx.Menu() + self.toggle = self.menu.AppendCheckItem(wx.ID_ANY, 'On') + self.topframe.Bind(wx.EVT_MENU, self.plugin_toggle, self.toggle) + self.toggle.Check(True) + + def plugin_toggle(self, evt): + if self.toggle.IsChecked() == True: + Parse.NameSpaceI = self.parseMethods['Standard'] + self.plugindb.SetString('xxstdnamespace', 'Standard', 'True') + if self.toggle.IsChecked() == False: + Parse.NameSpaceI = self.parseMethods['Traipse'] + self.plugindb.SetString('xxstdnamespace', 'Standard', 'False') + pass + + def plugin_enabled(self): + self.onoroff = self.plugindb.GetString('xxstdnamespace', 'Standard', '') or 'False' + self.toggle.Check(True) if self.onoroff == 'True' else self.toggle.Check(False) + Parse.NameSpaceE = self.parseMethods['Standard'] if self.onoroff == 'True' else self.parseMethods['Traipse'] + pass + + def plugin_disabled(self): + Parse.NameSpaceE = self.parseMethods['Traipse'] + + def pre_parse(self, text): + return text + + def send_msg(self, text, send): + return text, send + + def plugin_incoming_msg(self, text, type, name, player): + return text, type, name + + def post_msg(self, text, myself): + return text + + def refresh_counter(self): + pass diff -r 8e77f169f324 -r fc48380f0c9f plugins/xxurl2link.py --- a/plugins/xxurl2link.py Fri Feb 19 19:10:25 2010 -0600 +++ b/plugins/xxurl2link.py Mon May 03 03:30:11 2010 -0500 @@ -9,36 +9,36 @@ def __init__(self, plugindb, parent): orpg.pluginhandler.PluginHandler.__init__(self, plugindb, parent) - # The Following code should be edited to contain the proper information self.name = 'URL to link conversion' self.author = 'tdb30 tbaleno@wrathof.com' self.help = "This plugin automaticaly wraps urls in link tags\n" self.help += "making them clickable." - self.url_regex = None - self.mailto_regex = None - def plugin_menu(self): self.menu = wx.Menu() self.toggle = self.menu.AppendCheckItem(wx.ID_ANY, 'On') self.topframe.Bind(wx.EVT_MENU, self.plugin_toggle, self.toggle) - self.toggle.Check(True) def plugin_toggle(self, evt): + if self.toggle.IsChecked() == True: self.plugindb.SetString('xxurl2link', 'url2link', 'True') + if self.toggle.IsChecked() == False: self.plugindb.SetString('xxurl2link', 'url2link', 'False') pass def plugin_enabled(self): - #This is where you set any variables that need to be initalized when your plugin starts - self.url_regex = re.compile("(?' + m.group(0) + '' - else: - return '' + link + '' + if m.group(1) != None: return '' + m.group(0) + '' + else: return '' + link + '' diff -r 8e77f169f324 -r fc48380f0c9f readme.txt --- a/readme.txt Fri Feb 19 19:10:25 2010 -0600 +++ b/readme.txt Mon May 03 03:30:11 2010 -0500 @@ -1,9 +1,18 @@ -How to use OpenRPG version 1.7.x -Make sure you have installed python 2.5+ and wxPython 2.8+! +Welcome to Traipse 'OpenRPG' Ornery Orc +Traipse is a fork of the original OpenRPG software. Traipse +provides the best stability of any OpenRPG fork, and the best +features of any OpenRPG version + +How to use Traipse 'OpenRPG': -Launching OpenRPG: -OpenRPG can be launch by executing the OpenRPG.pyw script -located in the openrpg folder. On windows, Macs, and +Minimum software requirements +* Python 2.5.2 +* wx.Python 2.8.1.9 + +Launching Traipse 'OpenRPG': + +OpenRPG can be launch by executing the Traipse.pyw script +located in the folder you ran setup.py. On windows, Macs, and Unix with a GUI, this can be accomplished by double clicking OpenRPG.pyw. From a shell, type: python OpenRPG.pyw @@ -11,8 +20,8 @@ You want to launch your own server execute the Server.py. -For more info on how to use OpenRPG, -visit http://www.openrpg.com +For more info on how to use Traipse 'OpenRPG', +visit http://www.knowledgearcana.com/traipse-openrpg -OpenRPG Team diff -r 8e77f169f324 -r fc48380f0c9f rollback.py --- a/rollback.py Fri Feb 19 19:10:25 2010 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -#!/usr/bin/env python - -import sys -import os - -HG = os.environ["HG"] - -os.system(HG + " rollback") -os.system(HG + " revert --all") -print "Since you reverted, I am guessing there are issues with the last update" -print "PLEASE notify me on either the openrpg.com boards, or by email" -print "digitalxero@gmail.com" -print "You can continue using your rolled back version by launching openrpg from" -print "the command line with the command: OpenRPG*.py -n" -print "the -n tells the system not to update, it will still run the downloader" -print "but it will not change your files" -raw_input("press key to terminate program") \ No newline at end of file diff -r 8e77f169f324 -r fc48380f0c9f start_developer.py --- a/start_developer.py Fri Feb 19 19:10:25 2010 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -#!/usr/bin/env python - -import pyver -pyver.checkPyVersion() - -#Update Manager -from orpg.orpg_wx import * -import upmana.updatemana -app = upmana.updatemana.updateApp(0) -app.MainLoop() - -#Main Program -from orpg.orpg_wx import * -import orpg.main - -if WXLOADED: - mainapp = orpg.main.orpgApp(0) - mainapp.MainLoop() -else: - print "You really really need wx!" diff -r 8e77f169f324 -r fc48380f0c9f start_noupdate.py --- a/start_noupdate.py Fri Feb 19 19:10:25 2010 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -#!/usr/bin/env python - -import pyver -pyver.checkPyVersion() - -#Update Manager -from orpg.orpg_wx import * -import upmana.updatemana -app = upmana.updatemana.updateApp(0) -app.MainLoop() - -#Main Program -from orpg.orpg_wx import * -import orpg.main - -if WXLOADED: - mainapp = orpg.main.orpgApp(0) - mainapp.MainLoop() -else: - print "You really really need wx!" diff -r 8e77f169f324 -r fc48380f0c9f upmana/updatemana.py --- a/upmana/updatemana.py Fri Feb 19 19:10:25 2010 -0600 +++ b/upmana/updatemana.py Mon May 03 03:30:11 2010 -0500 @@ -12,8 +12,9 @@ class Term2Win(object): # A stdout redirector. Allows the messages from Mercurial to be seen in the Install Window def write(self, text): - statbar.SetStatusText(text) - wx.Yield() + self.closed = sys.__stdout__.closed + self.flush = sys.__stdout__.flush + statbar.SetStatusText(text.replace('\n', '')) sys.__stdout__.write(text) class Updater(wx.Panel): @@ -21,7 +22,10 @@ wx.Panel.__init__(self, parent) ### Status Bar ### #statbar.SetStatusText('Select a Package and Update') - statbar.SetStatusText('New Status Bar') + #statbar.SetStatusText('New Status Bar') + + self.timer = wx.Timer(self, 1) + self.count = 0 ### Update Manager self.ui = ui.ui() @@ -44,7 +48,7 @@ self.buttons['update'] = wx.Button(self, wx.ID_ANY, "Update Now") self.buttons['finish'] = wx.Button(self, wx.ID_ANY, "Finish") - self.sizer.Add(self.changelog, (0,0), span=(4,1), flag=wx.EXPAND) + self.sizer.Add(self.changelog, (0,0), span=(5,1), flag=wx.EXPAND) self.sizer.Add(self.filelist, (0,1), span=(1,3), flag=wx.EXPAND) self.sizer.Add(self.buttons['progress_bar'], (1,1), span=(1,3), flag=wx.EXPAND) @@ -55,7 +59,7 @@ self.sizer.Add(self.buttons['advanced'], (2,3), flag=wx.EXPAND) self.sizer.Add(self.buttons['update'], (3,3), flag=wx.EXPAND) self.sizer.Add(self.buttons['finish'], (4,3), flag=wx.EXPAND) - #self.buttons['finish'].Disable() + self.buttons['progress_bar'].SetValue(100) self.sizer.AddGrowableCol(0) self.sizer.AddGrowableRow(0) self.SetSizer(self.sizer) @@ -76,6 +80,37 @@ self.Bind(wx.EVT_BUTTON, self.ChooseBranch, self.buttons['advanced']) self.Bind(wx.EVT_CHECKBOX, self.ToggleAutoUpdate, self.buttons['auto_check']) self.Bind(wx.EVT_CHECKBOX, self.ToggleNoUpdate, self.buttons['no_check']) + self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer) + + def OnTimer(self, event): + statbar.SetStatusText('Checking For Updates') + self.count = self.count + 1 + self.buttons['progress_bar'].SetValue(self.count) + if self.count == 100: + self.timer.Stop() + statbar.SetStatusText('No Updates Available') + + def UpdateCheck(self): + self.timer.Start(100) + self.count = 3 + self.buttons['progress_bar'].SetValue(3) + try: + doUpdate = commands.incoming(self.ui, self.repo, + manifest.GetString('default', 'repo', ''), + force=True, bundle=False) + if doUpdate: + statbar.SetStatusText('No Updates Available') + self.buttons['progress_bar'].SetValue(100) + self.timer.Stop() + else: + statbar.SetStatusText('Refresh Repo For Updated Source') + self.buttons['progress_bar'].SetValue(100) + self.timer.Stop() + except: + statbar.SetStatusText('No Connection Found') + self.buttons['progress_bar'].SetValue(100) + self.timer.Stop() + def ToggleAutoUpdate(self, event): if self.buttons['auto_check'].GetValue() == True: @@ -122,11 +157,11 @@ os.removedirs(temp+dir1) def LoadDoc(self): - ignore = open(self.filename) + manifest = open(self.filename) self.ignorelist = [] - for i in ignore: self.ignorelist.append(str(i [:len(i)-1])) - manifest = ignore.readlines() - ignore.close() + ignore = manifest.readlines() + for i in ignore: print i; self.ignorelist.append(str(i[:len(i)-1])) + manifest.close() def Finish(self, evt=None): component.get('upmana-win').OnClose(None) @@ -584,11 +619,11 @@ dlg.Destroy(); pass def LoadDoc(self): - ignore = open(self.filename) + manifest = open(self.filename) self.ignorelist = [] - for i in ignore: self.ignorelist.append(str(i [:len(i)-1])) - manifest = ignore.readlines() - ignore.close() + ignore = manifest.readlines() + for i in ignore: print i; self.ignorelist.append(str(i[:len(i)-1])) + manifest.close() def get_packages(self, type=None): #Can be cleaner @@ -687,17 +722,18 @@ global statbar statbar = self.CreateStatusBar() + sys.stdout = Term2Win() self.Centre() # create the page windows as children of the notebook - page1 = Updater(nb, openrpg) + self.page1 = Updater(nb, openrpg) page2 = Repos(nb, openrpg) page3 = Manifest(nb) page4 = Control(nb) page5 = Help(nb) # add the pages to the notebook with the label to show on the tab - nb.AddPage(page1, "Updater") + nb.AddPage(self.page1, "Updater") nb.AddPage(page2, "Repos") nb.AddPage(page3, "Manifest") nb.AddPage(page4, "Control") @@ -719,23 +755,24 @@ class updateApp(wx.App): def OnInit(self): - self.main = False - sys.stdout = Term2Win() + self.main = False; self.autoUpdate = False; self.noUpdate = False logger._set_log_to_console(False) logger.note("Updater Start") component.add('validate', validate) - self.updater = updaterFrame(self, "OpenRPG Update Manager 1.0", + self.updater = updaterFrame(self, "OpenRPG Update Manager 1.2", component, manifest, self.main) if manifest.GetString("updatemana", "auto_update", "") == 'on' and self.main == False: - self.AutoUpdate(); self.OnExit() + self.AutoUpdate(); self.OnExit(); self.autoUpdate = True else: pass if manifest.GetString('updatemana', 'no_update', '') == 'on' and self.main == False: - self.OnExit() + self.OnExit(); self.noUpdate = True else: pass - try: - self.updater.Show() - self.updater.Fit() - except: pass + if not (self.autoUpdate or self.noUpdate): + try: + self.updater.Show() + self.updater.Fit() + if not self.main: self.updater.page1.UpdateCheck() + except: pass return True def AutoUpdate(self): @@ -744,13 +781,16 @@ self.c = self.repo.changectx('tip') self.current = self.repo.dirstate.branch() - capture = manifest.GetString('updaterepo', 'default', '') + capture = manifest.GetString('default', 'repo', '') if capture != '': - commands.pull(self.ui, self.repo, capture, rev='', update=False, force=True) + try: commands.pull(self.ui, self.repo, capture, rev='', update=False, force=True) + except: + wx.MessageBox('No Connection Found. Skipping Auto Update!', 'Info') + return filename = 'ignorelist.txt' self.filename = dir_struct["home"] + 'upmana' + os.sep + filename component.get('validate').config_file(filename, "default_ignorelist.txt") - self.mana = self.LoadDoc() + self.mana = self.LoadDoc(); ignored = [] temp = dir_struct["home"] + 'upmana' + os.sep + 'tmp' + os.sep for ignore in self.ignorelist: if len(ignore.split('/')) > 1: @@ -759,25 +799,27 @@ dir1 += ignore.split('/')[gets] + os.sep gets += 1 os.makedirs(temp+dir1) - shutil.copy(ignore, temp + dir1 + ignore.split('/')[len(ignore.split('/')) - 1]) + ignoredfile = temp + dir1 + ignore.split('/')[len(ignore.split('/')) - 1] + ignored.append(ignoredfile) + shutil.copy(ignore, ignoredfile) hg.clean(self.repo, self.current) for ignore in self.ignorelist: - shutil.copyfile(temp + ignore.split('/')[len(ignore.split('/')) - 1], ignore) - os.remove(temp + ignore.split('/')[len(ignore.split('/')) - 1]) + shutil.copyfile(ignored.index(ignore), ignore) + os.remove(ignored.index(ignore)) if len(ignore.split('/')) > 1: gets = 0; dir1 = '' while gets != len(ignore.split('/'))-1: dir1 += ignore.split('/')[gets] + os.sep gets += 1 os.removedirs(temp+dir1) - else: wx.MessageBox('No default Rpository set. Skipping Auto Update!', 'Info') + else: wx.MessageBox('Default Repo Not Found. Skipping Auto Update!', 'Info') def LoadDoc(self): - ignore = open(self.filename) + manifest = open(self.filename) self.ignorelist = [] - for i in ignore: self.ignorelist.append(str(i [:len(i)-1])) - manifest = ignore.readlines() - ignore.close() + ignore = manifest.readlines() + for i in ignore: print i; self.ignorelist.append(str(i[:len(i)-1])) + manifest.close() def OnExit(self): imported = ['manifest', 'orpg.dirpath', 'orpg.orpgCore', 'orpg.orpg_version',