# HG changeset patch
# User sirebral
# Date 1276675580 18000
# Node ID 9230a33defd9882b79b7230a48da4b069a25e495
# Parent b29454610f36663dad8506ab3935878e0acddaa6
Traipse Beta 'OpenRPG' {100616-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 (Closing/Closed)
New Features:
New to Map, can re-order Grid, Miniatures, and Whiteboard layer draw order
New to Server GUI, can now clear log
Updates:
Update to Warhammer PC Sheet. Rollers set as macros. Should work with little maintanence.
Update to Browser Server window. Display rooms with ' " & cleaner
Update to Server. Handles ' " & cleaner.
Fixes:
Fix to InterParse that was causing an Infernal Loop with Namespace Internal
Fix to XML data, removed old Minidom and switched to Element Tree
Fix to Server that was causing eternal attempt to find a Server ID, in Register Rooms thread
Fix to metaservers.xml file not being created
Fix to Single and Double quotes in Whiteboard text
Fix to Background images not showing when using the Image Server
Fix to Duplicate chat names appearing
Fix to Server GUI's logging output
Fix to FNB.COLORFUL_TABS bug.
diff -r b29454610f36 -r 9230a33defd9 orpg/dieroller/rollers/d20.py
--- a/orpg/dieroller/rollers/d20.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/dieroller/rollers/d20.py Wed Jun 16 03:06:20 2010 -0500
@@ -41,19 +41,20 @@
class d20dc(std):
def __init__(self,source=[],DC=10,mod=0):
std.__init__(self,source)
+ print "Source", source
self.DC = DC
self.mod = mod
- self.append(static_di(mod))
+ #self.append(static_di(mod))
def is_success(self):
- return ((self.sum() >= self.DC or self.data[0] == 20) and self.data[0] != 1)
+ return ((self.sum()+self.mod >= self.DC or self.data[0] == 20) and self.data[0] != 1)
def __str__(self):
myStr = "[" + str(self.data[0])
for a in self.data[1:]:
- myStr += ","
+ myStr += ", "
myStr += str(a)
- myStr += "] = (" + str(self.sum()) + ")"
+ myStr += ", "+str(self.mod)+ "] = (" + str(self.sum()+self.mod) + ")"
myStr += " vs DC " + str(self.DC)
if self.is_success(): myStr += " Success!"
else: myStr += " Failure!"
diff -r b29454610f36 -r 9230a33defd9 orpg/dieroller/rollers/std.py
--- a/orpg/dieroller/rollers/std.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/dieroller/rollers/std.py Wed Jun 16 03:06:20 2010 -0500
@@ -38,7 +38,7 @@
if self.data[i].lastroll() >= num:
self.data[i].extraroll()
done = 0
- if done: return self
+ if done: print self; return self
else: return self.open(num)
def minroll(self,min):
diff -r b29454610f36 -r 9230a33defd9 orpg/gametree/gametree.py
--- a/orpg/gametree/gametree.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/gametree/gametree.py Wed Jun 16 03:06:20 2010 -0500
@@ -47,7 +47,7 @@
import string, urllib, time, os
from shutil import copytree, copystat, copy, copyfile
-from orpg.orpg_xml import xml
+#from orpg.orpg_xml import xml
from orpg.tools.validate import validate
from orpg.tools.orpg_log import logger, debug
from orpg.tools.orpg_settings import settings
diff -r b29454610f36 -r 9230a33defd9 orpg/gametree/nodehandlers/chatmacro.py
--- a/orpg/gametree/nodehandlers/chatmacro.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/gametree/nodehandlers/chatmacro.py Wed Jun 16 03:06:20 2010 -0500
@@ -42,15 +42,12 @@
def __init__(self,xml,tree_node):
node_handler.__init__(self,xml,tree_node)
self.xml = xml
- self.text_elem = self.xml.find('text')
- self.text = self.text_elem.text
- def set_text(self,txt):
- self.text = txt
+ def set_text(self, txt):
+ self.xml.find('text').text = txt
def on_use(self,evt):
- txt = self.text
- actionlist = txt.split("\n")
+ actionlist = self.xml.find('text').text.split("\n")
for line in actionlist:
if(line != ""):
if line[0] != "/": ## it's not a slash command
@@ -65,8 +62,7 @@
def tohtml(self):
title = self.xml.get("name")
- txt = self.text
- txt = string.replace(txt,'\n'," ")
+ txt = string.replace(self.xml.find('text').text,'\n'," ")
return "
"+title+": "+txt
P_TITLE = wx.NewId()
@@ -80,7 +76,7 @@
sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, "Chat Macro"), wx.VERTICAL)
self.text = {}
self.text[P_TITLE] = wx.TextCtrl(self, P_TITLE, handler.xml.get('name'))
- self.text[P_BODY] = wx.TextCtrl(self, P_BODY, handler.text, style=wx.TE_MULTILINE)
+ self.text[P_BODY] = wx.TextCtrl(self, P_BODY, handler.xml.find('text').text, 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)
diff -r b29454610f36 -r 9230a33defd9 orpg/gametree/nodehandlers/core.py
--- a/orpg/gametree/nodehandlers/core.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/gametree/nodehandlers/core.py Wed Jun 16 03:06:20 2010 -0500
@@ -345,6 +345,7 @@
if bad_txt_found:
wx.MessageBox("Some non 7-bit ASCII characters found and stripped","Warning!")
txt = u_txt
+ print txt, self.handler, self.handler.xml
self.handler.text._set_nodeValue(txt)
diff -r b29454610f36 -r 9230a33defd9 orpg/gametree/nodehandlers/forms.py
--- a/orpg/gametree/nodehandlers/forms.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/gametree/nodehandlers/forms.py Wed Jun 16 03:06:20 2010 -0500
@@ -30,7 +30,7 @@
from containers import *
import orpg.minidom as minidom
-from orpg.orpg_xml import xml
+#from orpg.orpg_xml import xml
from wx.lib.scrolledpanel import ScrolledPanel
from orpg.tools.settings import settings
from orpg.tools.InterParse import Parse
diff -r b29454610f36 -r 9230a33defd9 orpg/gametree/nodehandlers/minilib.py
--- a/orpg/gametree/nodehandlers/minilib.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/gametree/nodehandlers/minilib.py Wed Jun 16 03:06:20 2010 -0500
@@ -37,6 +37,7 @@
import map_miniature_nodehandler
import orpg.mapper.map_msg
import orpg.minidom as minidom
+from orpg.tools.InterParse import Parse
# import scriptkit
# Constants
@@ -44,17 +45,6 @@
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'
#
#
#
@@ -100,11 +90,11 @@
"""
str = '
'
str += "Label Image URL Unique "
- for mini in self.xml.findall(TAG_MINIATURE):
- url = mini.get(ATTRIBUTE_URL)
- label = mini.get(ATTRIBUTE_NAME)
+ for mini in self.xml.findall('miniature'):
+ url = mini.get('url')
+ label = mini.get('name')
flag = 0
- try: flag = eval( mini.get(ATTRIBUTE_UNIQUE) )
+ try: flag = eval( mini.get('unique') )
except: pass
show = 'yes'
if flag: show = 'no'
@@ -133,13 +123,13 @@
for attrib in obj.keys():
key = TO_MINILIB_MAP.get( attrib, attrib )
if key != None: dict[ key ] = obj.get( attrib )
- dict[ ATTRIBUTE_UNIQUE ] = unique
+ dict[ 'unique' ] = unique
self.new_mini( dict )
else: node_handler.on_drop(self, evt)
def new_mini( self, data={}, add=1 ):
- mini = Element( TAG_MINIATURE )
+ mini = Element( 'miniature' )
for key in data.keys(): mini.set( key, data[ key ] )
for key in CORE_ATTRIBUTES:
if mini.get( key ) == ('' or None): mini.set( key, '0' )
@@ -154,37 +144,39 @@
def add_leaf( self, mini, icon='gear' ):
tree = self.tree
icons = tree.icons
- key = mini.get( ATTRIBUTE_NAME )
+ key = mini.get( 'name' )
self.mydata.append( mini )
def update_leaves( self ):
self.mydata = []
- for n in self.xml.findall(TAG_MINIATURE): self.add_leaf( n )
+ for n in self.xml.findall('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 ) )) )
+ if mini.get( 'url' ) == '' or mini.get( 'url' ) == 'http://':
+ Parse.Post( self.chat.colorize(self.chat.syscolor,
+ '"%s" is not a valid URL, the mini "%s" will not be added to the map' % (
+ mini.get( 'url' ), mini.get( 'name' ) )) )
return
- session = component.get( COMPONENT_SESSION )
+ session = component.get( '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)
+ canvas = component.get('map')
for loop in range( count ):
msg = self.get_miniature_XML( mini, addName)
msg = str("" + msg + " ")
- map.new_data( msg )
+ canvas.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() )
+ canvas = component.get( 'map' )
+ session = component.get( 'session' )
+ msg.init_prop( '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
@@ -193,24 +185,24 @@
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 )
+ if addName: label = mini_xml.get( 'name' )
else: label = ''
return msg.get_all_xml()
def is_unique( self, mini ):
- unique = mini.get( ATTRIBUTE_UNIQUE )
+ unique = mini.get( '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' )
+ for node in self.xml.findall('miniature'):
+ if node.get( 'posx' ) == '': node.set( 'posx', '0' )
+ if node.get( 'posy' ) == '': node.set( 'posy', '0' )
def get_mini( self, index ):
- try: return self.xml.findall(TAG_MINIATURE)[index]
+ try: return self.xml.findall('miniature')[index]
except: return None
class mini_handler( node_handler ):
@@ -279,7 +271,7 @@
"""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 ) )
+ for mini in self.handler.xml.findall('miniature'): self.list.append( mini.get( 'name' ) )
return self.list
def on_close(self, evt):
@@ -407,7 +399,7 @@
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):
+ for n in self.handler.xml.findall('miniature'):
for k in n.keys():
if k not in self.keys: self.keys.append( k )
@@ -426,7 +418,7 @@
"""Returns the list of 'miniature' DOM elements associated with this
miniature library.
"""
- return self.handler.xml.findall( TAG_MINIATURE )
+ return self.handler.xml.findall( 'miniature' )
def add_row( self, count = 1 ):
"""creates a new miniature node, and then adds it to the current
@@ -434,8 +426,8 @@
"""
self.AppendRows( count )
node = self.handler.new_mini( {
- ATTRIBUTE_NAME :' ',
- ATTRIBUTE_URL :'http://'} )# minidom.Element( TAG_MINIATURE )
+ 'name' :' ',
+ 'url' :'http://'} )# minidom.Element( 'miniature' )
self.update_all()
#self.handler.xml.append( node )
@@ -446,7 +438,7 @@
"""
if self.selectedRow > -1:
pos = self.selectedRow
- list = self.handler.xml.findall(TAG_MINIATURE)
+ list = self.handler.xml.findall('miniature')
self.handler.xml.remove( list[pos] )
self.DeleteRows( pos, 1 )
@@ -492,7 +484,7 @@
return self.GetTable().GetValue( self.selectedRow, 1 )
def getSelectedSerial( self ):
- """Returns the ATTRIBUTE_UNIQUE value for the selected row
+ """Returns the 'unique' value for the selected row
"""
return self.GetTable().GetValue( self.selectedRow, 2 )
diff -r b29454610f36 -r 9230a33defd9 orpg/gametree/nodehandlers/voxchat.py
--- a/orpg/gametree/nodehandlers/voxchat.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/gametree/nodehandlers/voxchat.py Wed Jun 16 03:06:20 2010 -0500
@@ -31,7 +31,6 @@
import re, os, string, core
from orpg.orpg_windows import *
-import core
import orpg.tools.scriptkit
import orpg.tools.predTextCtrl
import orpg.tools.rgbhex
diff -r b29454610f36 -r 9230a33defd9 orpg/main.py
--- a/orpg/main.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/main.py Wed Jun 16 03:06:20 2010 -0500
@@ -63,7 +63,7 @@
from xml.etree.ElementTree import ElementTree, Element, parse
from xml.etree.ElementTree import fromstring, tostring
-from orpg.orpg_xml import xml #to be replaced by etree
+#from orpg.orpg_xml import xml #to be replaced by etree
####################################
@@ -162,6 +162,11 @@
settings.add('Tip of the Day', 'tipotday_enabled', '1', '0|1', 'Show Tip of the Day on startup')
logger.info('New Settings added', True)
self.TraipseSuiteWarn('debug')
+ if setting == 'Meta Servers':
+ settings.add('Networking', 'MetaServers', 'metaservers.xml', '.xml file', 'Contains a list of Meta Servers')
+ logger.info('New Settings added', True)
+ self.validate.config_file("metaservers.xml","default_metaservers.xml")
+ self.TraipseSuiteWarn('debug')
def get_activeplugins(self):
try: tmp = self.pluginsFrame.get_activeplugins()
@@ -202,7 +207,7 @@
[' -'],
[' Tab Styles'],
[' Slanted'],
- [' Colorful', "check"],
+ #[' Colorful', "check"],
[' Black and White', "check"],
[' Aqua', "check"],
[' Custom', "check"],
@@ -252,7 +257,8 @@
self.mainmenu.SetMenuState('ToolsPasswordManager', True if settings.get('PWMannager') == 'On' else False)
tabtheme = settings.get('TabTheme') #This change is stable. TaS.
- self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedColorful", tabtheme == 'slanted&colorful')
+ if tabtheme == 'slanted&colorful': tabtheme = 'customflat'; settings.change('TabTheme', 'customflat')
+ #self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedColorful", tabtheme == 'slanted&colorful')
self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedBlackandWhite", tabtheme == 'slanted&bw')
self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedAqua", tabtheme == 'slanted&aqua')
self.mainmenu.SetMenuState("OpenRPGTabStylesFlatBlackandWhite", tabtheme == 'flat&bw')
@@ -318,9 +324,8 @@
#Tab Styles Menus
def SetTabStyles(self, *args, **kwargs):
-
tabtheme = settings.get('TabTheme') #This change is stable. TaS.
- self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedColorful", tabtheme == 'slanted&colorful')
+ #self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedColorful", tabtheme == 'slanted&colorful')
self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedBlackandWhite", tabtheme == 'slanted&bw')
self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedAqua", tabtheme == 'slanted&aqua')
self.mainmenu.SetMenuState("OpenRPGTabStylesFlatBlackandWhite", tabtheme == 'flat&bw')
@@ -336,7 +341,6 @@
else:
try: menu = args[0]
except: logger.general('Invalid Syntax for orpgFrame->SetTabStyles(self, *args, **kwargs)'); return
-
if kwargs.has_key('graidentTo'): graidentTo = kwargs['graidentTo']
else: graidentTo = None
if kwargs.has_key('graidentFrom'): graidentFrom = kwargs['graidentFrom']
@@ -360,7 +364,7 @@
for wnd in tabbedwindows:
style = wnd.GetWindowStyleFlag()
# remove old tabs style
- mirror = ~(FNB.FNB_VC71 | FNB.FNB_VC8 | FNB.FNB_FANCY_TABS | FNB.FNB_COLORFUL_TABS)
+ mirror = ~(FNB.FNB_VC71 | FNB.FNB_VC8 | FNB.FNB_FANCY_TABS )
style &= mirror
style |= newstyle
wnd.SetWindowStyleFlag(style)
@@ -747,11 +751,11 @@
def do_tab_window(self, xml_dom, parent_wnd):
# if container window loop through childern and do a recursive call
- temp_wnd = orpgTabberWnd(parent_wnd, style=FNB.FNB_ALLOW_FOREIGN_DND)
+ temp_wnd = orpgTabberWnd(parent_wnd)
children = xml_dom.getchildren()
for c in children:
- wnd = self.build_window(c,temp_wnd)
+ wnd = self.build_window(c, temp_wnd)
name = c.get("name")
temp_wnd.AddPage(wnd, name, False)
return temp_wnd
@@ -931,12 +935,15 @@
except: etreeEl.text = data
display_name = self.chat.chat_display_name(player)
- if etreeEl.text:self.chat.Post(display_name+etreeEl.text)
+ if etreeEl.text:
+ if "is creating room" in etreeEl.text: self.chat.Post(etreeEl.text)
+ else: self.chat.Post(display_name+etreeEl.text)
for child in etreeEl.getchildren():
if child.tag == 'tree':
- dlg = wx.MessageDialog(None, component.strip_html(display_name) + ' is trying to send you a tree node. Accept?', 'Question',
- wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+ dlg = wx.MessageDialog(None,
+ component.strip_html(display_name) + ' is trying to send you a tree node. Accept?',
+ 'Question', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
if dlg.ShowModal() == wx.ID_YES:
dlg.Destroy()
self.tree.on_receive_data(tostring(child))
@@ -1124,7 +1131,7 @@
def OnInit(self):
component.add('log', logger)
- component.add('xml', xml)
+ #component.add('xml', xml)
component.add('settings', settings)
component.add('validate', validate)
component.add("tabbedWindows", [])
diff -r b29454610f36 -r 9230a33defd9 orpg/mapper/background.py
--- a/orpg/mapper/background.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/mapper/background.py Wed Jun 16 03:06:20 2010 -0500
@@ -211,28 +211,31 @@
else: return ''
def layerTakeDOM(self, xml_dom):
- type = BG_COLOR
- color = xml_dom.getAttribute("color")
- logger.debug("color=" + color)
- path = urllib.unquote(xml_dom.getAttribute("path"))
- logger.debug("path=" + path)
- # Begin ted's map changes
- if xml_dom.hasAttribute("color"):
- r,g,b = self.r_h.rgb_tuple(xml_dom.getAttribute("color"))
+ bg_type = xml_dom.get("type")
+ urlpath = xml_dom.get('path')
+ color = xml_dom.get("color")
+
+ if urlpath != None:
+ path = urllib.unquote(xml_dom.get("path"))
+ logger.debug("path=" + path)
+
+ if color != None:
+ logger.debug("color=" + color)
+ r,g,b = self.r_h.rgb_tuple(color)
self.set_color(cmpColour(r,g,b))
- # End ted's map changes
- if xml_dom.hasAttribute("type"):
- type = int(xml_dom.getAttribute("type"))
- logger.debug("type=" + str(type))
- if type == BG_TEXTURE:
+
+ if bg_type != None:
+ logger.debug("type=" + bg_type)
+ bg_type = int(xml_dom.get("type"))
+ if bg_type == BG_TEXTURE:
if path != "": self.set_texture(path)
- elif type == BG_IMAGE:
+ elif bg_type == BG_IMAGE:
if path != "": self.set_image(path, 1)
- elif type == BG_NONE: self.clear()
- if xml_dom.hasAttribute('local') and xml_dom.getAttribute('local') == 'True' and os.path.exists(urllib.unquote(xml_dom.getAttribute('localPath'))):
- self.localPath = urllib.unquote(xml_dom.getAttribute('localPath'))
+ elif bg_type == BG_NONE: self.clear()
+ if xml_dom.get('local') == 'True' and os.path.exists(urllib.unquote(xml_dom.get('localPath'))):
+ self.localPath = urllib.unquote(xml_dom.get('localPath'))
self.local = True
- self.localTime = int(xml_dom.getAttribute('localTime'))
+ self.localTime = int(xml_dom.get('localTime'))
if self.localTime-time.time() <= 144000:
file = open(self.localPath, "rb")
imgdata = file.read()
diff -r b29454610f36 -r 9230a33defd9 orpg/mapper/background_handler.py
--- a/orpg/mapper/background_handler.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/mapper/background_handler.py Wed Jun 16 03:06:20 2010 -0500
@@ -83,7 +83,7 @@
postdata = urllib.urlencode({'filename':filename, 'imgdata':imgdata, 'imgtype':imgtype})
if self.settings.get_setting('LocalorRemote') == 'Remote':
- thread.start_new_thread(self.canvas.layers['bg'].upload,
+ thread.start_new_thread(self.upload,
(postdata, dlg.GetPath(), self.bg_type.GetStringSelection()))
else:
try: min_url = component.get("cherrypy") + filename
@@ -97,6 +97,13 @@
self.canvas.send_map_data()
self.canvas.Refresh(False)
+ def upload(self, postdata, filename, imgtype):
+ self.canvas.layers['bg'].upload(postdata, filename, imgtype)
+ self.update_info()
+ self.canvas.send_map_data()
+ self.canvas.Refresh(False)
+ return
+
def update_info(self):
bg_type = self.canvas.layers['bg'].get_type()
session=self.canvas.frame.session
diff -r b29454610f36 -r 9230a33defd9 orpg/mapper/base_msg.py
--- a/orpg/mapper/base_msg.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/mapper/base_msg.py Wed Jun 16 03:06:20 2010 -0500
@@ -29,7 +29,7 @@
from threading import RLock
from orpg.networking.mplay_client import *
-from xml.etree.ElementTree import ElementTree, Element
+from xml.etree.ElementTree import ElementTree, Element, fromstring
class map_element_msg_base:
# This is a base class
@@ -216,20 +216,24 @@
self._from_dom(xml_dom,self.set_prop)
def init_from_xml(self,xml):
- xml_dom = parseXml(xml)
- node_list = xml_dom.getElementsByTagName(self.tagname)
+ #xml_dom = parseXml(xml)
+ xml_dom = fromstring(xml)
+ #node_list = xml_dom.getElementsByTagName(self.tagname)
+ node_list = xml_dom.findall(self.tagname)
if len(node_list) < 1: print "Warning: no <" + self.tagname + "/> elements found in DOM."
else:
while len(node_list): self.init_from_dom(node_list.pop())
- if xml_dom: xml_dom.unlink()
+ #if xml_dom: xml_dom.unlink()
def set_from_xml(self,xml):
- xml_dom = parseXml(xml)
- node_list = xml_dom.getElementsByTagName(self.tagname)
+ #xml_dom = parseXml(xml)
+ xml_dom = fromstring(xml)
+ #node_list = xml_dom.getElementsByTagName(self.tagname)
+ node_list = xml_dom.findall(self.tagname)
if len(node_list) < 1: print "Warning: no <" + self.tagname + "/> elements found in DOM."
else:
while len(node_list): self.set_from_dom(node_list.pop())
- if xml_dom: xml_dom.unlink()
+ #if xml_dom: xml_dom.unlink()
# XML importers end
#########################################
diff -r b29454610f36 -r 9230a33defd9 orpg/mapper/fog.py
--- a/orpg/mapper/fog.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/mapper/fog.py Wed Jun 16 03:06:20 2010 -0500
@@ -27,7 +27,7 @@
from base import *
from random import Random
from region import *
-from orpg.minidom import Element
+from xml.etree.ElementTree import Element, tostring
import traceback
COURSE = 10
@@ -47,8 +47,8 @@
for pairs in string.split( points, ';' ):
pair = string.split( pairs, ',' )
p = Element( "point" )
- p.setAttribute( "x", pair[0] )
- p.setAttribute( "y", pair[1] )
+ p.set( "x", pair[0] )
+ p.set( "y", pair[1] )
result.append( p )
return result
@@ -59,23 +59,20 @@
localOutline = "points"
elem = Element( "poly" )
if action == "del":
- elem.setAttribute( "action", action )
- elem.setAttribute( "outline", localOutline )
+ elem.set( "action", action )
+ elem.set( "outline", localOutline )
if localOutline == 'points':
- list = self.points_to_elements( self.outline )
- for p in list: elem.appendChild( p )
- str = elem.toxml()
- elem.unlink()
- return str
- elem.setAttribute( "action", action )
+ foglist = self.points_to_elements( self.outline )
+ for p in foglist: elem.append( p )
+ return tostring(elem)
+ elem.set( "action", action )
if localOutline != None:
- elem.setAttribute( "outline", localOutline )
+ elem.set( "outline", localOutline )
if localOutline == 'points':
- list = self.points_to_elements( self.outline )
- for p in list: elem.appendChild( p )
- xml_str = elem.toxml()
- elem.unlink()
- return xml_str
+ foglist = self.points_to_elements( self.outline )
+ for p in foglist: elem.append( p )
+ #xml_str = elem.toxml()
+ return tostring(elem)
class fog_layer(layer_base):
def __init__(self, canvas):
@@ -223,11 +220,12 @@
if not self.use_fog:
self.use_fog = True
self.recompute_fog()
- if xml_dom.hasAttribute('serial'): self.serial_number = int(xml_dom.getAttribute('serial'))
- children = xml_dom._get_childNodes()
+ serial = xml_dom.get('serial')
+ if serial != None: self.serial_number = int(serial)
+ children = xml_dom.getchildren()
for l in children:
- action = l.getAttribute("action")
- outline = l.getAttribute("outline")
+ action = l.get("action")
+ outline = l.get("outline")
if (outline == "all"):
polyline = [IPoint().make(0,0), IPoint().make(self.width-1, 0),
IPoint().make(self.width-1, self.height-1),
@@ -240,10 +238,10 @@
polyline = []
lastx = None
lasty = None
- list = l._get_childNodes()
+ list = l.getchildren()
for point in list:
- x = point.getAttribute( "x" )
- y = point.getAttribute( "y" )
+ x = point.get( "x" )
+ y = point.get( "y" )
if (x != lastx or y != lasty):
polyline.append(IPoint().make(int(x), int(y)))
lastx = x
diff -r b29454610f36 -r 9230a33defd9 orpg/mapper/fog_msg.py
--- a/orpg/mapper/fog_msg.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/mapper/fog_msg.py Wed Jun 16 03:06:20 2010 -0500
@@ -27,7 +27,7 @@
from base_msg import *
from region import *
-from orpg.minidom import Element
+from xml.etree.ElementTree import Element, tostring
import string
class fog_msg(map_element_msg_base):
@@ -41,19 +41,17 @@
def get_line(self,outline,action,output_act):
elem = Element( "poly" )
- if ( output_act ): elem.setAttribute( "action", action )
- if ( outline == 'all' ) or ( outline == 'none' ): elem.setAttribute( "outline", outline )
+ if ( output_act ): elem.set( "action", action )
+ if ( outline == 'all' ) or ( outline == 'none' ): elem.set( "outline", outline )
else:
- elem.setAttribute( "outline", "points" )
+ elem.set( "outline", "points" )
for pair in string.split( outline, ";" ):
p = string.split( pair, "," )
point = Element( "point" )
- point.setAttribute( "x", p[ 0 ] )
- point.setAttribute( "y", p[ 1 ] )
- elem.appendChild( point )
- str = elem.toxml()
- elem.unlink()
- return str
+ point.set( "x", p[ 0 ] )
+ point.set( "y", p[ 1 ] )
+ elem.append( point )
+ return tostring(elem)
# convenience method to use if only this line is modified
# outputs a element containing only the changes to this line
@@ -83,23 +81,18 @@
str(x2)+","+str(y1)+";"+
str(x2)+","+str(y2)+";"+
str(x1)+","+str(y2),action,output_action)
- s = ""
s += fog_string
- s += " "
- else: s+="/>"
+ s += ""
return s
def interpret_dom(self,xml_dom):
self.use_fog=1
- #print 'fog_msg.interpret_dom called'
- children = xml_dom._get_childNodes()
- #print "children",children
+ children = xml_dom.getchildren()
for l in children:
- action = l.getAttribute("action")
- outline = l.getAttribute("outline")
- #print "action/outline",action, outline
+ action = l.get("action")
+ outline = l.get("outline")
if (outline=="all"):
polyline=[]
self.fogregion.Clear()
@@ -109,14 +102,13 @@
self.fogregion.Clear()
else:
polyline=[]
- list = l._get_childNodes()
+ list = l.getchildren()
for node in list:
- polyline.append( IPoint().make( int(node.getAttribute("x")), int(node.getAttribute("y")) ) )
+ polyline.append( IPoint().make( int(node.get("x")), int(node.get("y")) ) )
# pointarray = outline.split(";")
# for m in range(len(pointarray)):
# pt=pointarray[m].split(",")
# polyline.append(IPoint().make(int(pt[0]),int(pt[1])))
- #print "length of polyline", len(polyline)
if (len(polyline)>2):
if action=="del": self.fogregion.FromPolygon(polyline,0)
else: self.fogregion.FromPolygon(polyline,1)
diff -r b29454610f36 -r 9230a33defd9 orpg/mapper/grid.py
--- a/orpg/mapper/grid.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/mapper/grid.py Wed Jun 16 03:06:20 2010 -0500
@@ -406,19 +406,20 @@
else: return ''
def layerTakeDOM(self, xml_dom):
- if xml_dom.hasAttribute("color"):
- r,g,b = self.r_h.rgb_tuple(xml_dom.getAttribute("color"))
+ color = xml_dom.get('color')
+ if color != None:
+ r,g,b = self.r_h.rgb_tuple(color)
self.set_color(cmpColour(r,g,b))
#backwards compatible with non-isometric map formated clients
- ratio = RATIO_DEFAULT
- if xml_dom.hasAttribute("ratio"): ratio = xml_dom.getAttribute("ratio")
- if xml_dom.hasAttribute("mode"):
- self.SetMode(int(xml_dom.getAttribute("mode")))
- if xml_dom.hasAttribute("size"):
- self.unit_size = int(xml_dom.getAttribute("size"))
+ ratio = RATIO_DEFAULT if xml_dom.get("ratio") == None else xml_dom.get('ratio')
+ mode = xml_dom.get('mode')
+ if mode != None: self.SetMode(int(mode))
+ size = xml_dom.get('size')
+ if size != None:
+ self.unit_size = int(size)
self.unit_size_y = self.unit_size
- if xml_dom.hasAttribute("snap"):
- if (xml_dom.getAttribute("snap") == 'True') or (xml_dom.getAttribute("snap") == "1"): self.snap = True
- else: self.snap = False
- if xml_dom.hasAttribute("line"):
- self.SetLine(int(xml_dom.getAttribute("line")))
+ if (xml_dom.get("snap") == 'True') or (xml_dom.get("snap") == "1"): self.snap = True
+ else: self.snap = False
+ line = xml_dom.get('line')
+ if line != None: self.SetLine(int(line))
+
diff -r b29454610f36 -r 9230a33defd9 orpg/mapper/map.py
--- a/orpg/mapper/map.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/mapper/map.py Wed Jun 16 03:06:20 2010 -0500
@@ -45,6 +45,8 @@
from images import ImageHandler
from orpg.orpgCore import component
from orpg.tools.orpg_settings import settings
+from xml.etree.ElementTree import ElementTree, Element, parse
+from xml.etree.ElementTree import fromstring, tostring
# Various marker modes for player tools on the map
MARKER_MODE_NONE = 0
@@ -259,21 +261,26 @@
dc.DrawRectangle(0,0,clientsize[0]+1,clientsize[1]+1)
dc.SetDeviceOrigin(-topleft[0], -topleft[1])
dc.SetUserScale(scale, scale)
+
+ layer_order = []
+ for i in xrange (0, len(self.parent.layer_handlers)-1):
+ if self.parent.layer_tabs.GetPageText(i) in ('Background', 'Fog', 'General'): pass
+ else: layer_order.append(self.parent.layer_tabs.GetPageText(i))
self.layers['bg'].layerDraw(dc, scale, topleft, clientsize)
- self.layers['grid'].layerDraw(dc, [topleft[0]/scale, topleft[1]/scale],
- [clientsize[0]/scale, clientsize[1]/scale])
- self.layers['miniatures'].layerDraw(dc, [topleft[0]/scale, topleft[1]/scale],
- [clientsize[0]/scale, clientsize[1]/scale])
- self.layers['whiteboard'].layerDraw(dc)
+
+ for layer in layer_order:
+ if layer == 'Grid': self.layers['grid'].layerDraw(dc, [topleft[0]/scale, topleft[1]/scale],
+ [clientsize[0]/scale, clientsize[1]/scale])
+ if layer == 'Miniatures': self.layers['miniatures'].layerDraw(dc, [topleft[0]/scale, topleft[1]/scale],
+ [clientsize[0]/scale, clientsize[1]/scale])
+ if layer == 'Whiteboard': self.layers['whiteboard'].layerDraw(dc)
+
self.layers['fog'].layerDraw(dc, topleft, clientsize)
dc.SetPen(wx.NullPen)
dc.SetBrush(wx.NullBrush)
-
- dc.SelectObject(wx.NullBitmap)
- del dc
+ dc.SelectObject(wx.NullBitmap); del dc
wdc = self.preppaint()
wdc.DrawBitmap(bmp, topleft[0], topleft[1])
-
if settings.get_setting("AlwaysShowMapScale") == "1":
self.showmapscale(wdc)
try: evt.Skip()
@@ -622,15 +629,16 @@
--Snowdog 5/27/03
"""
try:
- #parse the map DOM
- xml_dom = parseXml(xml)
+ xml_dom = fromstring(xml)
if xml_dom == None: return
- node_list = xml_dom.getElementsByTagName("map")
+ node_list = xml_dom.findall("map")
+ if len(node_list) < 1:
+ if xml_dom.tag == 'map': node_list = [xml_dom]
if len(node_list) < 1: pass
else:
# set map version to incoming data so layers can convert
- self.map_version = node_list[0].getAttribute("version")
- action = node_list[0].getAttribute("action")
+ self.map_version = node_list[0].get("version")
+ action = node_list[0].get("action")
if action == "new":
self.layers = {}
try: self.layers['bg'] = layer_back_ground(self)
@@ -643,37 +651,38 @@
except: pass
try: self.layers['fog'] = fog_layer(self)
except: pass
- sizex = node_list[0].getAttribute("sizex")
+ sizex = node_list[0].get("sizex") or ''
if sizex != "":
sizex = int(float(sizex))
sizey = self.size[1]
self.set_size((sizex,sizey))
self.size_changed = 0
- sizey = node_list[0].getAttribute("sizey")
+ sizey = node_list[0].get("sizey") or ''
if sizey != "":
sizey = int(float(sizey))
sizex = self.size[0]
self.set_size((sizex,sizey))
self.size_changed = 0
- children = node_list[0]._get_childNodes()
+ children = node_list[0].getchildren()
#fog layer must be computed first, so that no data is inadvertently revealed
for c in children:
- name = c._get_nodeName()
+ name = c.tag
if name == "fog": self.layers[name].layerTakeDOM(c)
for c in children:
- name = c._get_nodeName()
+ name = c.tag
if name != "fog": self.layers[name].layerTakeDOM(c)
# all map data should be converted, set map version to current version
self.map_version = MAP_VERSION
- self.Refresh(False)
- xml_dom.unlink() # eliminate circular refs
+ self.Refresh(True)
except: pass
def re_ids_in_xml(self, xml):
+ exception = "\nDeprecated call to: Function re_ids_in_xml, line 679, map.py\nThis can mangle XML, please report!"
+ logger.exception(exception)
new_xml = ""
tmp_map = map_msg()
- xml_dom = parseXml(str(xml))
- node_list = xml_dom.getElementsByTagName("map")
+ xml_dom = fromstring(xml)
+ node_list = xml_dom.findall("map")
if len(node_list) < 1: pass
else:
tmp_map.init_from_dom(node_list[0])
@@ -701,8 +710,7 @@
elif l.tagname == 'circle': id = 'circle-' + self.frame.session.get_next_id()
l.init_prop("id", id)
new_xml = tmp_map.get_all_xml()
- if xml_dom: xml_dom.unlink()
- return str(new_xml)
+ return new_xml
class map_wnd(wx.Panel):
def __init__(self, parent, id):
@@ -714,20 +722,23 @@
self.root_dir = os.getcwd()
self.current_layer = 2
self.layer_tabs = orpgTabberWnd(self, style=FNB.FNB_NO_X_BUTTON|FNB.FNB_BOTTOM|FNB.FNB_NO_NAV_BUTTONS)
+
self.layer_handlers = []
- self.layer_handlers.append(background_handler(self.layer_tabs,-1,self.canvas))
- self.layer_tabs.AddPage(self.layer_handlers[0],"Background")
- self.layer_handlers.append(grid_handler(self.layer_tabs,-1,self.canvas))
- self.layer_tabs.AddPage(self.layer_handlers[1],"Grid")
- self.layer_handlers.append(miniatures_handler(self.layer_tabs,-1,self.canvas))
- self.layer_tabs.AddPage(self.layer_handlers[2],"Miniatures", True)
- self.layer_handlers.append(whiteboard_handler(self.layer_tabs,-1,self.canvas))
- self.layer_tabs.AddPage(self.layer_handlers[3],"Whiteboard")
- self.layer_handlers.append(fog_handler(self.layer_tabs,-1,self.canvas))
- self.layer_tabs.AddPage(self.layer_handlers[4],"Fog")
- self.layer_handlers.append(map_handler(self.layer_tabs,-1,self.canvas))
- self.layer_tabs.AddPage(self.layer_handlers[5],"General")
+ self.layer_handlers.append(background_handler(self.layer_tabs, -1, self.canvas))
+ self.layer_tabs.AddPage(self.layer_handlers[0], "Background")
+ self.layer_handlers.append(grid_handler(self.layer_tabs, -1, self.canvas))
+ self.layer_tabs.AddPage(self.layer_handlers[1], "Grid")
+ self.layer_handlers.append(miniatures_handler(self.layer_tabs, -1, self.canvas))
+ self.layer_tabs.AddPage(self.layer_handlers[2], "Miniatures", True)
+ self.layer_handlers.append(whiteboard_handler(self.layer_tabs, -1, self.canvas))
+ self.layer_tabs.AddPage(self.layer_handlers[3], "Whiteboard")
+ self.layer_handlers.append(fog_handler(self.layer_tabs, -1, self.canvas))
+ self.layer_tabs.AddPage(self.layer_handlers[4], "Fog")
+ self.layer_handlers.append(map_handler(self.layer_tabs, -1, self.canvas))
+ self.layer_tabs.AddPage(self.layer_handlers[5], "General")
self.layer_tabs.SetSelection(2)
+
+ self.layer_order = {1: 'grid', 2: 'miniatures', 3: 'whiteboard'}
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.canvas, 1, wx.EXPAND)
self.sizer.Add(self.layer_tabs, 0, wx.EXPAND)
@@ -778,9 +789,9 @@
d = wx.FileDialog(self.GetParent(), "Select a file", dir_struct["user"], "", "*.xml", wx.OPEN)
if d.ShowModal() == wx.ID_OK:
f = open(d.GetPath())
- map_string = f.read()
- new_xml = self.canvas.re_ids_in_xml(map_string)
- if new_xml:
+ new_xml = f.read()
+ f.close()
+ if new_xml != None:
self.canvas.takexml(new_xml)
self.canvas.send_map_data("new")
self.update_tools()
diff -r b29454610f36 -r 9230a33defd9 orpg/mapper/map_msg.py
--- a/orpg/mapper/map_msg.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/mapper/map_msg.py Wed Jun 16 03:06:20 2010 -0500
@@ -53,17 +53,17 @@
def init_from_dom(self,xml_dom):
self.p_lock.acquire()
- if xml_dom.tagName == self.tagname:
+ if xml_dom.tag == self.tagname:
# If this is a map message, look for the "action=new"
# Notice we only do this when the root is a map tag
- if self.tagname == "map" and xml_dom.hasAttribute("action") and xml_dom.getAttribute("action") == "new":
+ if self.tagname == "map" and xml_dom.get("action") == "new":
self.clear()
# Process all of the properties in each tag
- if xml_dom.getAttributeKeys():
- for k in xml_dom.getAttributeKeys():
- self.init_prop(k,xml_dom.getAttribute(k))
- for c in xml_dom._get_childNodes():
- name = c._get_nodeName()
+ if xml_dom.keys():
+ for k in xml_dom.keys():
+ self.init_prop(k,xml_dom.get(k))
+ for c in xml_dom.getchildren():
+ name = c.tag
if not self.children.has_key(name):
if name == "miniatures": self.children[name] = minis_msg(self.p_lock)
elif name == "grid": self.children[name] = grid_msg(self.p_lock)
@@ -84,16 +84,16 @@
def set_from_dom(self,xml_dom):
self.p_lock.acquire()
- if xml_dom.tagName == self.tagname:
+ if xml_dom.tag == self.tagname:
# If this is a map message, look for the "action=new"
# Notice we only do this when the root is a map tag
- if self.tagname == "map" and xml_dom.hasAttribute("action") and xml_dom.getAttribute("action") == "new":
+ if self.tagname == "map" and xml_dom.get("action") == "new":
self.clear()
# Process all of the properties in each tag
- if xml_dom.getAttributeKeys():
- for k in xml_dom.getAttributeKeys(): self.set_prop(k,xml_dom.getAttribute(k))
- for c in xml_dom._get_childNodes():
- name = c._get_nodeName()
+ if xml_dom.keys():
+ for k in xml_dom.keys(): self.set_prop(k,xml_dom.get(k))
+ for c in xml_dom.getchildren():
+ name = c.tag
if not self.children.has_key(name):
if name == "miniatures": self.children[name] = minis_msg(self.p_lock)
elif name == "grid": self.children[name] = grid_msg(self.p_lock)
diff -r b29454610f36 -r 9230a33defd9 orpg/mapper/miniatures.py
--- a/orpg/mapper/miniatures.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/mapper/miniatures.py Wed Jun 16 03:06:20 2010 -0500
@@ -344,27 +344,27 @@
else: return ''
def takedom(self, xml_dom):
- self.id = xml_dom.getAttribute("id")
- if xml_dom.hasAttribute("posx"): self.pos.x = int(xml_dom.getAttribute("posx"))
- if xml_dom.hasAttribute("posy"): self.pos.y = int(xml_dom.getAttribute("posy"))
- if xml_dom.hasAttribute("heading"): self.heading = int(xml_dom.getAttribute("heading"))
- if xml_dom.hasAttribute("face"): self.face = int(xml_dom.getAttribute("face"))
- if xml_dom.hasAttribute("path"):
- self.path = urllib.unquote(xml_dom.getAttribute("path"))
+ self.id = xml_dom.get("id")
+ if xml_dom.get("posx") != None: self.pos.x = int(xml_dom.get("posx"))
+ if xml_dom.get("posy") != None: self.pos.y = int(xml_dom.get("posy"))
+ if xml_dom.get("heading") != None: self.heading = int(xml_dom.get("heading"))
+ if xml_dom.get("face") != None: self.face = int(xml_dom.get("face"))
+ if xml_dom.get("path") != None:
+ self.path = urllib.unquote(xml_dom.get("path"))
self.set_bmp(ImageHandler.load(self.path, 'miniature', self.id))
- if xml_dom.hasAttribute("locked"):
- if xml_dom.getAttribute("locked") == '1' or xml_dom.getAttribute("locked") == 'True': self.locked = True
+ if xml_dom.get("locked") != None:
+ if xml_dom.get("locked") == '1' or xml_dom.get("locked") == 'True': self.locked = True
else: self.locked = False
- if xml_dom.hasAttribute("hide"):
- if xml_dom.getAttribute("hide") == '1' or xml_dom.getAttribute("hide") == 'True': self.hide = True
+ if xml_dom.get("hide") != None:
+ if xml_dom.get("hide") == '1' or xml_dom.get("hide") == 'True': self.hide = True
else: self.hide = False
- if xml_dom.hasAttribute("label"): self.label = xml_dom.getAttribute("label")
- if xml_dom.hasAttribute("zorder"): self.zorder = int(xml_dom.getAttribute("zorder"))
- if xml_dom.hasAttribute("align"):
- if xml_dom.getAttribute("align") == '1' or xml_dom.getAttribute("align") == 'True': self.snap_to_align = 1
+ if xml_dom.get("label") != None: self.label = xml_dom.get("label")
+ if xml_dom.get("zorder") != None: self.zorder = int(xml_dom.get("zorder"))
+ if xml_dom.get("align") != None:
+ if xml_dom.get("align") == '1' or xml_dom.get("align") == 'True': self.snap_to_align = 1
else: self.snap_to_align = 0
- if xml_dom.hasAttribute("width"): self.width = int(xml_dom.getAttribute("width"))
- if xml_dom.hasAttribute("height"): self.height = int(xml_dom.getAttribute("height"))
+ if xml_dom.get("width") != None: self.width = int(xml_dom.get("width"))
+ if xml_dom.get("height") != None: self.height = int(xml_dom.get("height"))
##-----------------------------
## miniature layer
@@ -489,36 +489,36 @@
else: return ""
def layerTakeDOM(self, xml_dom):
- if xml_dom.hasAttribute('serial'):
- self.serial_number = int(xml_dom.getAttribute('serial'))
- children = xml_dom._get_childNodes()
+ if xml_dom.get('serial') != None:
+ self.serial_number = int(xml_dom.get('serial'))
+ children = xml_dom.getchildren()
for c in children:
- action = c.getAttribute("action")
- id = c.getAttribute('id')
+ action = c.get("action")
+ id = c.get('id')
if action == "del":
mini = self.get_miniature_by_id(id)
if mini: self.miniatures.remove(mini); del mini
elif action == "new":
- pos = cmpPoint(int(c.getAttribute('posx')),int(c.getAttribute('posy')))
- path = urllib.unquote(c.getAttribute('path'))
- label = c.getAttribute('label')
+ pos = cmpPoint(int(c.get('posx')),int(c.get('posy')))
+ path = urllib.unquote(c.get('path'))
+ label = c.get('label')
height = width = heading = face = snap_to_align = zorder = 0
locked = hide = False
- if c.hasAttribute('height'): height = int(c.getAttribute('height'))
- if c.hasAttribute('width'): width = int(c.getAttribute('width'))
- if c.getAttribute('locked') == 'True' or c.getAttribute('locked') == '1': locked = True
- if c.getAttribute('hide') == 'True' or c.getAttribute('hide') == '1': hide = True
- if c.getAttribute('heading'): heading = int(c.getAttribute('heading'))
- if c.hasAttribute('face'): face = int(c.getAttribute('face'))
- if c.hasAttribute('align'): snap_to_align = int(c.getAttribute('align'))
- if c.getAttribute('zorder'): zorder = int(c.getAttribute('zorder'))
+ if c.get('height') != None: height = int(c.get('height'))
+ if c.get('width') != None: width = int(c.get('width'))
+ if c.get('locked') == 'True' or c.get('locked') == '1': locked = True
+ if c.get('hide') == 'True' or c.get('hide') == '1': hide = True
+ if c.get('heading') != None: heading = int(c.get('heading'))
+ if c.get('face') != None: face = int(c.get('face'))
+ if c.get('align') != None: snap_to_align = int(c.get('align'))
+ if c.get('zorder') != None: zorder = int(c.get('zorder'))
min = BmpMiniature(id, path, ImageHandler.load(path, 'miniature', id), pos, heading,
face, label, locked, hide, snap_to_align, zorder, width, height)
self.miniatures.append(min)
- if c.hasAttribute('local') and c.getAttribute('local') == 'True' and os.path.exists(urllib.unquote(c.getAttribute('localPath'))):
- localPath = urllib.unquote(c.getAttribute('localPath'))
+ if c.get('local') == 'True' and os.path.exists(urllib.unquote(c.get('localPath'))):
+ localPath = urllib.unquote(c.get('localPath'))
local = True
- localTime = float(c.getAttribute('localTime'))
+ localTime = float(c.get('localTime'))
if localTime-time.time() <= 144000:
file = open(localPath, "rb")
imgdata = file.read()
diff -r b29454610f36 -r 9230a33defd9 orpg/mapper/miniatures_msg.py
--- a/orpg/mapper/miniatures_msg.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/mapper/miniatures_msg.py Wed Jun 16 03:06:20 2010 -0500
@@ -79,11 +79,11 @@
def init_from_dom(self,xml_dom):
self.p_lock.acquire()
- if xml_dom.tagName == self.tagname:
- if xml_dom.getAttributeKeys():
- for k in xml_dom.getAttributeKeys(): self.init_prop(k,xml_dom.getAttribute(k))
+ if xml_dom.tag == self.tagname:
+ if xml_dom.keys():
+ for k in xml_dom.keys(): self.init_prop(k,xml_dom.get(k))
- for c in xml_dom._get_childNodes():
+ for c in xml_dom.getchildren():
mini = mini_msg(self.p_lock)
try: mini.init_from_dom(c)
except Exception, e: print e; continue
@@ -103,10 +103,10 @@
def set_from_dom(self,xml_dom):
self.p_lock.acquire()
- if xml_dom.tagName == self.tagname:
- if xml_dom.getAttributeKeys():
- for k in xml_dom.getAttributeKeys(): self.set_prop(k,xml_dom.getAttribute(k))
- for c in xml_dom._get_childNodes():
+ if xml_dom.tag == self.tagname:
+ if xml_dom.keys():
+ for k in xml_dom.keys(): self.set_prop(k,xml_dom.get(k))
+ for c in xml_dom.getchildren():
mini = mini_msg(self.p_lock)
try: mini.set_from_dom(c)
except Exception, e: print e; continue
diff -r b29454610f36 -r 9230a33defd9 orpg/mapper/whiteboard.py
--- a/orpg/mapper/whiteboard.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/mapper/whiteboard.py Wed Jun 16 03:06:20 2010 -0500
@@ -46,7 +46,7 @@
self.scale = 1
self.r_h = RGBHex()
self.selected = False
- self.text_string = text_string
+ self.text_string = text_string.replace('"', '"').replace("'", ''')
self.id = id
self.weight = int(weight)
self.pointsize = int(pointsize)
@@ -95,7 +95,8 @@
# Draw text
(w,x,y,z) = self.get_rect(dc)
dc.SetFont(self.font)
- dc.DrawText(self.text_string, self.posx, self.posy)
+ text_string = self.text_string.replace('"', '"').replace(''', "'")
+ dc.DrawText(text_string, self.posx, self.posy)
dc.SetTextForeground(wx.Colour(0,0,0))
def toxml(self, action="update"):
@@ -119,21 +120,21 @@
else: return ''
def takedom(self, xml_dom):
- self.text_string = xml_dom.getAttribute("text_string")
- self.id = xml_dom.getAttribute("id")
- if xml_dom.hasAttribute("posy"): self.posy = int(xml_dom.getAttribute("posy"))
- if xml_dom.hasAttribute("posx"): self.posx = int(xml_dom.getAttribute("posx"))
- if xml_dom.hasAttribute("weight"):
- self.weight = int(xml_dom.getAttribute("weight"))
+ self.text_string = xml_dom.get("text_string")
+ self.id = xml_dom.get("id")
+ if xml_dom.get("posy") != None: self.posy = int(xml_dom.get("posy"))
+ if xml_dom.get("posx") != None: self.posx = int(xml_dom.get("posx"))
+ if xml_dom.get("weight"):
+ self.weight = int(xml_dom.get("weight"))
self.font.SetWeight(self.weight)
- if xml_dom.hasAttribute("style"):
- self.style = int(xml_dom.getAttribute("style"))
+ if xml_dom.get("style") != None:
+ self.style = int(xml_dom.get("style"))
self.font.SetStyle(self.style)
- if xml_dom.hasAttribute("pointsize"):
- self.pointsize = int(xml_dom.getAttribute("pointsize"))
+ if xml_dom.get("pointsize") != None:
+ self.pointsize = int(xml_dom.get("pointsize"))
self.font.SetPointSize(self.pointsize)
- if xml_dom.hasAttribute("color") and xml_dom.getAttribute("color") != '':
- self.textcolor = xml_dom.getAttribute("color")
+ if xml_dom.get("color") != None and xml_dom.get("color") != '':
+ self.textcolor = xml_dom.get("color")
if self.textcolor == '#0000000': self.textcolor = '#000000'
class WhiteboardLine:
@@ -231,16 +232,16 @@
return ''
def takedom(self, xml_dom):
- self.line_string = xml_dom.getAttribute("line_string")
- self.id = xml_dom.getAttribute("id")
- if xml_dom.hasAttribute("upperleftx"): self.upperleft.x = int(xml_dom.getAttribute("upperleftx"))
- if xml_dom.hasAttribute("upperlefty"): self.upperleft.y = int(xml_dom.getAttribute("upperlefty"))
- if xml_dom.hasAttribute("lowerrightx"): self.lowerright.x = int(xml_dom.getAttribute("lowerrightx"))
- if xml_dom.hasAttribute("lowerrighty"): self.lowerright.y = int(xml_dom.getAttribute("lowerrighty"))
- if xml_dom.hasAttribute("color") and xml_dom.getAttribute("color") != '':
- self.linecolor = xml_dom.getAttribute("color")
+ self.line_string = xml_dom.get("line_string")
+ self.id = xml_dom.get("id")
+ if xml_dom.get("upperleftx") != None: self.upperleft.x = int(xml_dom.get("upperleftx"))
+ if xml_dom.get("upperlefty") != None: self.upperleft.y = int(xml_dom.get("upperlefty"))
+ if xml_dom.get("lowerrightx") != None: self.lowerright.x = int(xml_dom.get("lowerrightx"))
+ if xml_dom.get("lowerrighty") != None: self.lowerright.y = int(xml_dom.get("lowerrighty"))
+ if xml_dom.get("color") != None and xml_dom.get("color") != '':
+ self.linecolor = xml_dom.get("color")
if self.linecolor == '#0000000': self.linecolor = '#000000'
- if xml_dom.hasAttribute("width"): self.linewidth = int(xml_dom.getAttribute("width"))
+ if xml_dom.get("width") != None: self.linewidth = int(xml_dom.get("width"))
##-----------------------------
## whiteboard layer
@@ -369,7 +370,7 @@
def add_text(self, text_string, pos, style, pointsize, weight, color="#000000"):
id = 'text-' + self.canvas.session.get_next_id()
- text = WhiteboardText(id,text_string, pos, style, pointsize, weight, color)
+ text = WhiteboardText(id, text_string, pos, style, pointsize, weight, color)
self.texts.append(text)
xml_str = ""
xml_str += text.toxml("new")
@@ -416,13 +417,13 @@
else: return ""
def layerTakeDOM(self, xml_dom):
- serial_number = xml_dom.getAttribute('serial')
- if serial_number != "": self.serial_number = int(serial_number)
- children = xml_dom._get_childNodes()
+ serial_number = xml_dom.get('serial')
+ if serial_number != None: self.serial_number = int(serial_number)
+ children = xml_dom.getchildren()
for l in children:
- nodename = l._get_nodeName()
- action = l.getAttribute("action")
- id = l.getAttribute('id')
+ nodename = l.tag
+ action = l.get("action")
+ id = l.get('id')
try:
if self.serial_number < int(id.split('-')[2]): self.serial_number = int(id.split('-')[2])
except: pass
@@ -436,17 +437,17 @@
elif action == "new":
if nodename == "line":
try:
- line_string = l.getAttribute('line_string')
- upperleftx = l.getAttribute('upperleftx')
- upperlefty = l.getAttribute('upperlefty')
- lowerrightx = l.getAttribute('lowerrightx')
- lowerrighty = l.getAttribute('lowerrighty')
+ line_string = l.get('line_string')
+ upperleftx = l.get('upperleftx')
+ upperlefty = l.get('upperlefty')
+ lowerrightx = l.get('lowerrightx')
+ lowerrighty = l.get('lowerrighty')
upperleft = wx.Point(int(upperleftx),int(upperlefty))
lowerright = wx.Point(int(lowerrightx),int(lowerrighty))
- color = l.getAttribute('color')
+ color = l.get('color')
if color == '#0000000': color = '#000000'
- id = l.getAttribute('id')
- width = int(l.getAttribute('width'))
+ id = l.get('id')
+ width = int(l.get('width'))
except:
line_string = upperleftx = upperlefty = lowerrightx = lowerrighty = color = 0
continue
@@ -454,15 +455,15 @@
self.lines.append(line)
elif nodename == "text":
try:
- text_string = l.getAttribute('text_string')
- style = l.getAttribute('style')
- pointsize = l.getAttribute('pointsize')
- weight = l.getAttribute('weight')
- color = l.getAttribute('color')
+ text_string = l.get('text_string')
+ style = l.get('style')
+ pointsize = l.get('pointsize')
+ weight = l.get('weight')
+ color = l.get('color')
if color == '#0000000': color = '#000000'
- id = l.getAttribute('id')
- posx = l.getAttribute('posx')
- posy = l.getAttribute('posy')
+ id = l.get('id')
+ posx = l.get('posx')
+ posy = l.get('posy')
pos = wx.Point(0,0)
pos.x = int(posx)
pos.y = int(posy)
diff -r b29454610f36 -r 9230a33defd9 orpg/mapper/whiteboard_msg.py
--- a/orpg/mapper/whiteboard_msg.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/mapper/whiteboard_msg.py Wed Jun 16 03:06:20 2010 -0500
@@ -80,11 +80,11 @@
def init_from_dom(self,xml_dom):
self.p_lock.acquire()
- if xml_dom.tagName == self.tagname:
- if xml_dom.getAttributeKeys():
- for k in xml_dom.getAttributeKeys(): self.init_prop(k,xml_dom.getAttribute(k))
- for c in xml_dom._get_childNodes():
- item = item_msg(self.p_lock,c._get_nodeName())
+ if xml_dom.tag == self.tagname:
+ if xml_dom.keys():
+ for k in xml_dom.keys(): self.init_prop(k,xml_dom.get(k))
+ for c in xml_dom.getchildren():
+ item = item_msg(self.p_lock,c.tag)
try: item.init_from_dom(c)
except Exception, e:
print e
@@ -105,11 +105,11 @@
def set_from_dom(self,xml_dom):
self.p_lock.acquire()
- if xml_dom.tagName == self.tagname:
- if xml_dom.getAttributeKeys():
- for k in xml_dom.getAttributeKeys(): self.set_prop(k,xml_dom.getAttribute(k))
- for c in xml_dom._get_childNodes():
- item = item_msg(self.p_lock, c._get_nodeName())
+ if xml_dom.tag == self.tagname:
+ if xml_dom.keys():
+ for k in xml_dom.keys(): self.set_prop(k,xml_dom.get(k))
+ for c in xml_dom.getchildren():
+ item = item_msg(self.p_lock, c.tag)
try: item.set_from_dom(c)
except Exception, e:
print e
diff -r b29454610f36 -r 9230a33defd9 orpg/networking/gsclient.py
--- a/orpg/networking/gsclient.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/networking/gsclient.py Wed Jun 16 03:06:20 2010 -0500
@@ -349,7 +349,9 @@
i = self.room_list.GetItemCount()
if (data[2]=="1") or (data[2]=="True"): pwd="yes"
else: pwd="no"
- self.room_list.InsertStringItem(i,data[1])
+ name = data[1].replace('&', "&")
+ name = name.replace('"', '"e;').replace("'", ''').replace("<", "<").replace(">", ">").replace('"', '"').replace(''', "'")
+ self.room_list.InsertStringItem(i, name)
self.room_list.SetStringItem(i,1,data[3])
self.room_list.SetStringItem(i,2,pwd)
self.room_list.SetItemData(i,int(data[0]))
@@ -441,7 +443,9 @@
i = self.room_list.GetItemCount()
if (g[2]=="True") or (g[2]=="1") : pwd="yes"
else: pwd="no"
- self.room_list.InsertStringItem(i, g[1])
+ name = g[1].replace('&', "&")
+ name = name.replace('"', '"e;').replace("'", ''').replace("<", "<").replace(">", ">")
+ self.room_list.InsertStringItem(i, name)
self.room_list.SetStringItem(i, 1, g[3])
self.room_list.SetStringItem(i, 2, pwd)
self.room_list.SetItemData(i, int(g[0]))
@@ -481,7 +485,9 @@
rooms = n.findall('room')
for room in rooms:
- self.rmList[address].append((room.get("id"), room.get("name"),
+ name = room.get('name').replace('&', "&")
+ name = name.replace('"', '"e;').replace("'", ''').replace("<", "<").replace(">", ">")
+ self.rmList[address].append((room.get("id"), name,
room.get("pwd"), room.get("num_users")))
self.svrList.sort(server_instance_compare)
@@ -568,36 +574,7 @@
name = self.texts["room_name"].GetValue()
boot_pwd = self.texts["room_boot_pwd"].GetValue()
minversion = self.texts["room_min_version"].GetValue()
- #
- # Check for & in name. We want to allow this becaus of its common use in D&D.
- #
- loc = name.find("&")
- oldloc=0
- while loc > -1:
- loc = name.find("&",oldloc)
- if loc > -1:
- b = name[:loc]
- e = name[loc+1:]
- name = b + "&" + e
- oldloc = loc+1
- loc = name.find('"')
- oldloc=0
- while loc > -1:
- loc = name.find('"',oldloc)
- if loc > -1:
- b = name[:loc]
- e = name[loc+1:]
- name = b + ""e;" + e
- oldloc = loc+1
- loc = name.find("'")
- oldloc=0
- while loc > -1:
- loc = name.find("'",oldloc)
- if loc > -1:
- b = name[:loc]
- e = name[loc+1:]
- name = b + "'" + e
- oldloc = loc+1
+
if self.buttons['gs_pwd'].GetValue(): pwd = self.texts["room_pwd"].GetValue()
else: pwd = ""
if name == "": wx.MessageBox("Invalid Name","Error");
diff -r b29454610f36 -r 9230a33defd9 orpg/networking/meta_server_lib.py
--- a/orpg/networking/meta_server_lib.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/networking/meta_server_lib.py Wed Jun 16 03:06:20 2010 -0500
@@ -29,7 +29,7 @@
#added debug flag for meta messages to cut console server spam --Snowdog
-META_DEBUG = 0
+META_DEBUG = False
__version__ = "$Id: meta_server_lib.py,v 1.40 2007/04/04 01:18:42 digitalxero Exp $"
@@ -42,17 +42,15 @@
from threading import *
from random import uniform
+import urllib2
from urllib import urlopen, urlencode
from orpg.tools.orpg_log import debug
-from xml.etree.ElementTree import Element, fromstring
+from xml.etree.ElementTree import Element, fromstring, parse, tostring
metacache_lock = RLock()
-def get_server_dom(data=None,path=None, string=False):
- # post data at server and get the resulting DOM
- if path == None:
- # get meta server URI
- path = getMetaServerBaseURL()
+def get_server_dom(data=None, path=None, string=False):
+ if path[len(path)-1] != "/": path += '/'
# POST the data
if META_DEBUG:
@@ -61,9 +59,9 @@
print "=========================================="
print data
print
- file = urlopen(path, data)
- data = file.read()
- file.close()
+ #recvdata = urllib2.Request(path, data)
+ response = urllib2.urlopen(path, data)
+ data = response.read()
# Remove any leading or trailing data. This can happen on some satellite connections
p = re.compile('(.*? )',re.DOTALL|re.IGNORECASE)
@@ -77,9 +75,8 @@
print data
print
# build dom
- etreeEl = data
- if not string: return fromstring(etreeEl)
- else: return etreeEl
+ if string: return data
+ else: return fromstring(data)
def post_server_data(name, realHostName=None):
if realHostName: data = urlencode({"server_data[name]":name,
@@ -90,7 +87,7 @@
else: data = urlencode({"server_data[name]":name,
"server_data[version]":PROTOCOL_VERSION,
"act":"new"})
- path = component.get('settings').get('MetaServerBaseURL') #getMetaServerBaseURL()
+ path = component.get('settings').get('MetaServerBaseURL') #getMetaServerList()
etreeEl = get_server_dom(data, path)
return int(etreeEl.get('id'))
@@ -111,49 +108,38 @@
def byStartAttribute(first, second):
# This function is used to easily sort a list of nodes by their start time
# Ensure there is something to sort with for each
-
first_start = int(first.get('start')) or 0
second_start = int(second.get('start')) or 0
-
# Return the result of the cmp function on the two strings
return cmp(first_start, second_start)
def byNameAttribute(first, second):
# This function is used to easily sort a list of nodes by their name attribute
# Ensure there is something to sort with for each
-
first_name = first.get('name') or ''
second_name = second.get('name') or ''
-
# Return the result of the cmp function on the two strings
return cmp(first_name,second_name)
-def get_server_list(versions = None, sort_by="start"):
+def get_server_list(versions=None, sort_by="start"):
data = urlencode({"version":PROTOCOL_VERSION,"ports":"%"})
- all_metas = getMetaServers(versions, 1) # get the list of metas
- base_meta = getMetaServerBaseURL()
-
+ #all_metas = getMetaServers(versions, False) # get the list of metas
+ meta_list = getMetaServerList()
#all_metas.reverse() # The last one checked will take precedence, so reverse the order
# so that the top one on the actual list is checked last
return_hash = {} # this will end up with an amalgamated list of servers
- for meta in all_metas: # check all of the metas
+ for meta in meta_list: # check all of the metas
#get the server's xml from the current meta
bad_meta = 0
#print "Getting server list from " + meta + "..."
- try: xml_dom = get_server_dom(data=data, path=meta)
- except: bad_meta = 1 #print "Trouble getting servers from " + meta + "..."
+ try: xml_dom = get_server_dom(data, meta.get('url'))
+ except: bad_meta = 1; print "Trouble getting servers from " + meta.get('url') + "..."
if bad_meta: continue
- if base_meta == meta: updateMetaCache(xml_dom) #print "This is our base meta: " + meta
node_list = xml_dom.findall('server')
- if len(node_list): # if there are entries in the node list
- # otherwise, just loop to next meta
-
- # for each node found, we're going to check the nodes from prior
- # metas in the list. If a match is found, then use the new values.
+ if len(node_list):
for n in node_list:
- # set them from current node
if not n.get('name'): n.set('name','NO_NAME_GIVEN')
name = n.get('name')
if not n.get('num_users'): n.set('num_users','N/A')
@@ -165,24 +151,15 @@
n.set('meta',meta)
end_point = str(address) + ":" + str(port)
if return_hash.has_key(end_point):
+ print end_point
+ print n
+
if META_DEBUG: print "Replacing duplicate server entry at " + end_point
return_hash[end_point] = n
-
- # At this point, we have an amalgamated list of servers
- # Now, we have to construct a new DOM to pass back.
-
- # Create a servers element
server_list = Element('servers')
-
- # get the nodes stored in return_hash
sort_list = return_hash.values()
-
- # sort them by their name attribute. Uses byNameAttribute()
- # defined above as a comparison function
if sort_by == "start": sort_list.sort(byStartAttribute)
elif sort_by == "name": sort_list.sort(byNameAttribute)
-
- # Add each node to the DOM
for n in sort_list: server_list.append(n)
return server_list
@@ -216,122 +193,32 @@
if META_DEBUG: traceback.print_exc()
print "Meta Server Lib: UpdateMetaCache(): " + str(e)
-def getRawMetaList(path=None):
- ### Alpha ###
- """This code will allow for a list of metas to be created. Future developement will more integrate the list of metas"""
- if path != None:
- metas = []
- data = urlencode({"version":PROTOCOL_VERSION,"ports":"%"})
- xml_dom = get_server_dom(data, path)
- node_list = fromstring(xml_dom).findall('meta_server')
- if len(node_list):
- for n in node_list:
- metas.append(n.get('path'))
- return metas
+
+def getMetaServerList():
+ # get meta server URL
+ meta_list = fromstring("""
+
+
+
+ """
+ )
try:
- try:
- metacache_lock.acquire()
- # Read in the metas
- validate.config_file("metaservers.cache","metaservers.cache")
- ini = open(dir_struct["user"]+"metaservers.cache","r")
- metas = ini.readlines()
- ini.close()
- return metas
- finally:
- metacache_lock.release()
- except Exception, e:
- if META_DEBUG: traceback.print_exc()
- print "Meta Server Lib: getRawMetaList(): " + str(e)
- return []
-
-def getMetaServers(versions = None, pick_random=0):
- """
- get meta server URLs as a list
- versions is a list of acceptable version numbers.
- A False truth value will use getMetaServerBaseURL()
- set a default if we have weird reading problems
- default_url = "http://www.openrpg.com/openrpg_servers.php"
- """
-
- ### Pre Alpha Design ###
- """ Here is how to handle Multiple Meta servers, and probably the best way to do it. Create an XML file that contains nodes with the various servers. Users will grab that meta data and have the option to connect to multiple meta servers which will allow them to find all the rooms. A check box should be used so if one server faile the users can continue without much lag. When creating a server hosts will need to select a meta to go too. This should be in the final of Ornery Orc."""
- meta_names = []
- if(versions): # If versions are supplied, then look in metaservers.conf
- try:
- """
- read in the metas from file
- format of file is one meta entry per line
- each entry will be the meta url, followed by one or more version numbers that it
- handle. Generally, this will be either a 1 for the original Meta format, or
- 2 for the new one.
- """
- # Read in the metas
- #Adding a path object will attempt to look for a meta_network.
- metas = getRawMetaList()
-
- # go through each one to check if it should be returned, based on the
- # version numbers allowed.
- for meta in metas:
- # split the line on whitespace
- # obviously, your meta servers urls shouldn't contain whitespace. duh.
- words = meta.split()
- success = 0 # init success flag for version check
- for version in versions: # run through each allowed version from caller
- if version in words[1:]: # if the allowed version token was found
- success += 1 # then increment the success indicator
- if success: # if the meta entry is acceptable to the caller
- meta_names.append(words[0]) # add the entry
- if META_DEBUG: print "adding metaserver " + meta
-
- # at this point, we should have at least one name from the cache. If not ...
- if not meta_names:
- default_meta = getMetaServerBaseURL() # grab the meta from ini.xml
- meta_names.append(default_meta) # add it to the return list
- # print "Warning!!\nNo valid metaservers cached."
- # print "Using meta from MetaServerBaseURL: " + default_meta + "\n"
- # if we have more than one and want a random one
- elif pick_random:
- if META_DEBUG: print "choosing random meta from: " + str(meta_names)
- i = int(uniform(0,len(meta_names)))
- #meta = meta_names[i]
- meta_names = [meta_names[i]]
- if META_DEBUG: print "using: " + str(meta_names)
- else:
- if META_DEBUG: print "using all metas: " + str(meta_names)
- return meta_names
- except Exception,e:
- print e
- #print "using default meta server URI: " + default_url
- metas = []
- #metas.append(default_url)
- return metas # return an empty list
- else: # otherwise, use MetaServerBaseURL()
- url = getMetaServerBaseURL()
- meta_names.append(url)
- return meta_names
-
-def getMetaServerBaseURL():
- # get meta server URL
- url = "http://orpgmeta.appspot.com/"
- try:
+ component.get('validate').config_file("metaservers.xml","default_metaservers.xml")
component.get('validate').config_file("settings.xml","default_settings.xml")
- ini = open(dir_struct["user"]+"settings.xml","r")
- txt = ini.read()
- xml = component.get('xml')
- tree = xml.parseXml(txt)._get_documentElement()
- ini.close()
- node_list = tree.getElementsByTagName("MetaServerBaseURL")
- if node_list:
- url = node_list[0].getAttribute("value")
- print url
- # allow tree to be collected
- try: tree.unlink()
- except: pass
-
- except Exception,e:
- print e
- #print "using meta server URI: " + url
- return url
+ setting = parse(dir_struct["user"]+"settings.xml")
+ tree = setting.getroot()
+ node_list = tree.getiterator("MetaServers")
+ if len(node_list) == 0:
+ component.get('frame').add_setting('Meta Servers')
+ setting = parse(dir_struct["user"]+"settings.xml")
+ metas = parse(dir_struct["user"]+'metaservers.xml').getroot()
+ else:
+ meta = node_list[0].get("value")
+ metas = parse(dir_struct["user"]+meta).getroot()
+ meta_list = metas.findall('meta')
+ return meta_list
+ except Exception,e: print e
+ return meta_list
"""
Beginning of Class registerThread
@@ -386,6 +273,7 @@
self.destroy = 0 # Used to flag that this thread should die
self.port = str(port)
self.register_callback = register_callback # set a method to call to report result of register
+ self.IdAttempts = 0
"""
This thread will communicate with one and only one
Meta. If the Meta in ini.xml is changed after
@@ -396,7 +284,7 @@
it easier to have multiple registerThreads going to keep the server registered
on multiple (compatible) Metas.
"""
- if MetaPath == None: self.path = getMetaServerBaseURL() # Do this if no Meta specified
+ if MetaPath == None: self.path = getMetaServerList() # Do this if no Meta specified
else: self.path = MetaPath
def getIdAndCookie(self):
@@ -469,16 +357,18 @@
if not self.isAlive(): # check to see if this thread is dead
return 1 # If so, return an error result
# Do the actual unregistering here
- data = urlencode( {"server_data[id]":self.id,
+ data = urlencode( { "server_data[id]":self.id,
"server_data[cookie]":self.cookie,
"server_data[version]":PROTOCOL_VERSION,
"act":"unregister"} )
- try: # this POSTS the request and returns the result
- xml_dom = get_server_dom(data=data, path=self.path)
- if xml_dom.get("errmsg"):
- print "Error durring unregistration: " + xml_dom.get("errmsg")
- except:
- if META_DEBUG: print "Problem talking to Meta. Will go ahead and die, letting Meta remove us."
+ for path in getMetaServerList():
+ try: # this POSTS the request and returns the result
+ etreeEl = get_server_dom(data, path.get('url'))
+ if etreeEl.get("errmsg") != None:
+ print "Error durring unregistration: " + etreeEl.get("errmsg")
+ except Exception, e:
+ if META_DEBUG: print "Problem talking to Meta. Will go ahead and die, letting Meta remove us."
+ if META_DEBUG: print e
# If there's an error, echo it to the console
# No special handling is required. If the de-registration worked we're done. If
@@ -509,11 +399,14 @@
# Set the server's attibutes, if specified.
if name: self.name = name
- if num_users != None: self.num_users = num_users
+ if num_users != None:
+ try: self.num_users = len(num_users)
+ except: self.num_users = num_users
+ else: self.num_users = 0
if realHostName: self.realHostName = realHostName
# build POST data
if self.realHostName:
- data = urlencode( {"server_data[id]":self.id,
+ data = urlencode( { "server_data[id]":self.id,
"server_data[cookie]":self.cookie,
"server_data[name]":self.name,
"server_data[port]":self.port,
@@ -523,7 +416,7 @@
"server_data[address]": self.realHostName } )
else:
if META_DEBUG: print "Letting meta server decide the hostname to list..."
- data = urlencode( {"server_data[id]":self.id,
+ data = urlencode( { "server_data[id]":self.id,
"server_data[cookie]":self.cookie,
"server_data[name]":self.name,
"server_data[port]":self.port,
@@ -531,11 +424,11 @@
"server_data[num_users]":self.num_users,
"act":"register"} )
try: # this POSTS the request and returns the result
- etreeEl = get_server_dom(data=data, path=self.path)
- except:
+ etreeEl = get_server_dom(data, self.path)
+ except Exception, e:
if META_DEBUG: print "Problem talking to server. Setting interval for retry ..."
if META_DEBUG: print data
- if META_DEBUG: print
+ if META_DEBUG: print e
self.interval = 0
"""
If we are in the registerThread thread, then setting interval to 0
@@ -552,10 +445,10 @@
return 0 # indicates that it was okay to call, not that no errors occurred
# If there is a DOM returned ....
- if etreeEl:
+ if etreeEl != None:
# If there's an error, echo it to the console
- if etreeEl.get("errmsg"):
- print "Error durring registration: " + etreeEl.get("errmsg")
+ if etreeEl.get("errmsg") != None:
+ print "Error durring registration at: " +path.get('url')+ " Error: " +etreeEl.get("errmsg")
if META_DEBUG: print data
if META_DEBUG: print
"""
@@ -580,11 +473,12 @@
self.interval = int(etreeEl.get("interval"))
self.id = etreeEl.get("id")
self.cookie = etreeEl.get("cookie")
- if not etreeEl.get("errmsg"): updateMetaCache(xml_dom)
- except:
+ #if etreeEl.get("errmsg") == None: updateMetaCache(xml_dom)
+ except Exception, e:
if META_DEBUG: print
if META_DEBUG: print "OOPS! Is the Meta okay? It should be returning an id, cookie, and interval."
if META_DEBUG: print "Check to see what it really returned.\n"
+ if META_DEBUG: print e
# Let xml_dom get garbage collected
try: xml_dom.unlink()
except: pass
diff -r b29454610f36 -r 9230a33defd9 orpg/networking/mplay_client.py
--- a/orpg/networking/mplay_client.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/networking/mplay_client.py Wed Jun 16 03:06:20 2010 -0500
@@ -38,7 +38,7 @@
from orpg.orpg_version import CLIENT_STRING, PROTOCOL_VERSION, VERSION
from orpg.orpgCore import component
-from orpg.orpg_xml import xml
+#from orpg.orpg_xml import xml
from orpg.tools.orpg_log import debug
from orpg.tools.settings import settings
@@ -81,9 +81,9 @@
STATUS_SET_URL = 1
def parseXml(data):
+ debug(('Developers note. Deprecated call to parseXml!!'), parents=True)
"parse and return doc"
- doc = xml.parseXml(data)
- doc.normalize()
+ doc = fromstring(data)
return doc
def myescape(data):
@@ -387,7 +387,6 @@
def __init__(self,name,callbacks):
client_base.__init__(self)
component.add('mp_client', self)
- self.xml = component.get('xml')
self.set_name(name)
self.on_receive = callbacks['on_receive']
self.on_mplay_event = callbacks['on_mplay_event']
diff -r b29454610f36 -r 9230a33defd9 orpg/networking/mplay_server.py
--- a/orpg/networking/mplay_server.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/networking/mplay_server.py Wed Jun 16 03:06:20 2010 -0500
@@ -51,6 +51,7 @@
from mplay_client import MPLAY_LENSIZE
from orpg.dirpath import dir_struct
import orpg.tools.validate
+import htmlentitydefs
from orpg.mapper.map_msg import *
from threading import Lock, RLock
@@ -316,7 +317,7 @@
self.banDoc = self.banDom.getroot()
for element in self.banDom.findall('banned'):
- playerName = element.get('name').replace("&", "&").replace("<", "<").replace('"', """).replace(">", ">")
+ playerName = element.get('name').replace("&", "&").replace("<", "<").replace('"', "").replace(">", ">")
playerIP = element.get('ip')
self.ban_list[playerIP] = {}
self.ban_list[playerIP]['ip'] = playerIP
@@ -336,7 +337,7 @@
etreeEl = Element('server')
for ip in self.ban_list:
el = Element('banned')
- el.set('name', str(self.ban_list[ip]['name'].replace("&", "&").replace("<", "<").replace(""", '"').replace(">", ">")))
+ el.set('name', str(self.ban_list[ip]['name'].replace("&", "&").replace("<", "<").replace("", '"').replace(">", ">")))
el.set('ip', str(self.ban_list[ip]['ip']))
etreeEl.append(el)
file = open(self.userPath + self.banFile ,"w")
@@ -616,7 +617,7 @@
def register_callback(instance, xml_dom = None, source=None):
if xml_dom: # if we get something
- if source == getMetaServerBaseURL(): # if the source of this DOM is the authoritative meta
+ if source == getMetaServerList(): # if the source of this DOM is the authoritative meta
try:
metacache_lock.acquire()
curlist = getRawMetaList() # read the raw meta cache lines into a list
@@ -648,21 +649,40 @@
def registerRooms(self, args=None):
rooms = ''
- id = '0'
- time.sleep(500)
- for rnum in self.groups.keys():
- rooms += urllib.urlencode( {"room_data[rooms][" + str(rnum) + "][name]":self.groups[rnum].name,
- "room_data[rooms][" + str(rnum) + "][pwd]":str(self.groups[rnum].pwd != "")})+'&'
- for pid in self.groups[rnum].players:
- rooms += urllib.urlencode( {"room_data[rooms][" + str(rnum) + "][players]["+str(pid)+"]":self.players[pid].name,})+'&'
+ serverId = '0'
+ x = 0
+ cache = {}
for meta in self.metas.keys():
- while id == '0':
- id, cookie = self.metas[meta].getIdAndCookie()
- data = urllib.urlencode( {"room_data[server_id]":id,
- "act":'registerrooms'})
- get_server_dom(data+'&'+rooms, self.metas[meta].path, string=True)
+ # There is no point in wasting our planetary resources on attempting to register rooms to a meta
+ # that does not provide that service. When they eventually get their head out of the elitist clouds
+ # this work around can be removed.
+ if meta.get('url') != 'http://orpgmeta.appspot.com':
+ cache[meta] = self.metas[meta]
+ for meta in cache.keys():
+ self.log_msg("Registering rooms too: " +meta.get('url'))
+ self.log_msg("Obtaining Server ID from: " +meta.get('url'))
+ for x in range (1, 100):
+ serverId, cookie = self.metas[meta].getIdAndCookie()
+ if serverId == '0':
+ self.metas[meta].IdAttempts += 100
+ if self.metas[meta].IdAttempts > 1000:
+ self.metas[meta].unregister()
+ self.log_msg("Deleting Meta: " +meta.get('url')+ " after 1000 attempts.")
+ del self.metas[meta]
+ break
+ if serverId != '0':
+ self.log_msg("Obtained Server ID: " +serverId+ " from: " +meta.get('url'))
+ self.metas[meta].IdAttempts = 0
+ for rnum in self.groups.keys():
+ rooms += urllib.urlencode({"room_data[rooms][" +str(rnum)+ "][name]":self.groups[rnum].name,
+ "room_data[rooms][" +str(rnum)+ "][pwd]":str(self.groups[rnum].pwd != ""),
+ "room_data[rooms][" +str(rnum)+ "][players]":str(len(self.groups[rnum].players))
+ })+'&'
+ data = urllib.urlencode({"room_data[server_id]":serverId,
+ "act":'registerrooms'})
+ get_server_dom(data+'&'+rooms, meta.get('url'), string=True)
- def register(self,name_given=None):
+ def register(self, name_given=None):
if name_given == None: name = self.name
else: self.name = name = name_given
name = self.clean_published_servername(name)
@@ -672,11 +692,10 @@
else: num_players = 0
# request only Meta servers compatible with version 2
- metalist = getMetaServers(versions=["2"])
+ metalist = getMetaServerList()
if self.show_meta_messages != 0:
self.log_msg("Found these valid metas:")
- for meta in metalist: self.log_msg("Meta:" + meta)
-
+ for meta in metalist: self.log_msg("Meta:" + meta.get('url'))
"""
# Go through the list and see if there is already a running register
# thread for the meta.
@@ -686,26 +705,27 @@
# iterate through the currently running metas and prune any
# not currently listed in the Meta Server list.
"""
-
if self.show_meta_messages != 0: self.log_msg( "Checking running register threads for outdated metas.")
for meta in self.metas.keys():
if self.show_meta_messages != 0: self.log_msg("meta:" + meta + ": ")
if not meta in metalist: # if the meta entry running is not in the list
if self.show_meta_messages != 0: self.log_msg( "Outdated. Unregistering and removing")
self.metas[meta].unregister()
+ self.log_msg("Unregistering from: " +meta.get('url'))
del self.metas[meta]
else:
if self.show_meta_messages != 0: self.log_msg( "Found in current meta list. Leaving intact.")
# Now call register() for alive metas or start one if we need one
for meta in metalist:
- if self.metas.has_key(meta) and self.metas[meta] and self.metas[meta].isAlive():
+ self.log_msg("Registering too: " +meta.get('url'))
+ if (self.metas.has_key(meta) and self.metas[meta] and self.metas[meta].isAlive()):
self.metas[meta].register(name=name,
realHostName=self.server_address,
num_users=num_players)
else:
self.metas[meta] = registerThread(name=name, realHostName=self.server_address,
- num_users=num_players, MetaPath=meta, port=self.server_port,
+ num_users=num_players, MetaPath=meta.get('url'), port=self.server_port,
register_callback=self.register_callback)
self.metas[meta].start()
@@ -723,8 +743,9 @@
# Instead, loop through all existing meta threads and unregister them
"""
- for meta in self.metas.values():
- if meta and meta.isAlive(): meta.unregister()
+ for meta in self.metas.keys():
+ self.log_msg("Unregistering from: " +meta.get('url'))
+ if self.metas[meta] and self.metas[meta].isAlive(): self.metas[meta].unregister()
self.be_registered = 0
"""
@@ -1211,11 +1232,9 @@
self.log_msg(("update_group", (self.groups[LOBBY_ID].name, LOBBY_ID, len(self.groups[LOBBY_ID].players) ) ))
cmsg = ("connect", props) #################################################
self.log_msg(cmsg)
-
- # If already registered then re-register, thereby updating the Meta
- # on the number of players
- if self.be_registered:
- self.register()
+ for meta in self.metas.keys():
+ self.metas[meta].num_users = len(self.players)
+ thread.start_new_thread(self.registerRooms,(0,))
except:
traceback.print_exc()
@@ -1673,33 +1692,8 @@
# Check for & in name. We want to allow this because of its common
# use in d&d games.
try:
- loc = name.find("&")
- oldloc = 0
- while loc > -1:
- loc = name.find("&",oldloc)
- if loc > -1:
- b = name[:loc]
- e = name[loc+1:]
- value = b + "&" + e
- oldloc = loc+1
- loc = name.find("'")
- oldloc = 0
- while loc > -1:
- loc = name.find("'",oldloc)
- if loc > -1:
- b = name[:loc]
- e = name[loc+1:]
- name = b + "'" + e
- oldloc = loc+1
- loc = name.find('"')
- oldloc = 0
- while loc > -1:
- loc = name.find('"',oldloc)
- if loc > -1:
- b = name[:loc]
- e = name[loc+1:]
- name = b + """ + e
- oldloc = loc+1
+ name = name.replace('&', '&')
+ name = name.replace('"', '"e;').replace("'", ''').replace("<", "<").replace(">", ">")
oldroomname = self.groups[gid].name
self.groups[gid].name = str(name)
lmessage = "Room name changed to from \"" + oldroomname + "\" to \"" + name + "\""
@@ -1726,34 +1720,8 @@
# Check for & in name. We want to allow this because of its common
# use in d&d games.
-
- loc = name.find("&")
- oldloc = 0
- while loc > -1:
- loc = name.find("&",oldloc)
- if loc > -1:
- b = name[:loc]
- e = name[loc+1:]
- name = b + "&" + e
- oldloc = loc+1
- loc = name.find("'")
- oldloc = 0
- while loc > -1:
- loc = name.find("'",oldloc)
- if loc > -1:
- b = name[:loc]
- e = name[loc+1:]
- name = b + "'" + e
- oldloc = loc+1
- loc = name.find('"')
- oldloc = 0
- while loc > -1:
- loc = name.find('"',oldloc)
- if loc > -1:
- b = name[:loc]
- e = name[loc+1:]
- name = b + """ + e
- oldloc = loc+1
+ name = name.replace('&', '&')
+ name = name.replace('"', '"e;').replace("'", ''').replace("<", "<").replace(">", ">")
group_id = str(self.next_group_id)
self.next_group_id += 1
@@ -1809,14 +1777,10 @@
del self.players[id]
self.log_msg(dmsg)
self.log_msg(("disconnect",id))
- """
- # If already registered then re-register, thereby updating the Meta
- # on the number of players
- # Note: Upon server shutdown, the server is first unregistered, so
- # this code won't be repeated for each player being deleted.
- """
- if self.be_registered:
- self.register()
+ self.log_msg(("update_group", (self.groups[group_id].name, group_id, len(self.groups[group_id].players) )))
+ for meta in self.metas.keys():
+ self.metas[meta].num_users = len(self.players)
+ thread.start_new_thread(self.registerRooms,(0,))
except Exception, e: self.log_msg( ('exception', str(e)) )
self.log_msg("Explicit garbage collection shows %s undeletable items." % str(gc.collect()))
@@ -1825,30 +1789,32 @@
act = xml_dom.get("action")
group_id = self.players[id].group_id
ip = self.players[id].ip
- self.log_msg("Player with IP: " + str(ip) + " joined.")
ServerPlugins.setPlayer(self.players[id])
self.send_to_group(id,group_id,data)
if act=="new":
try:
self.send_player_list(id,group_id)
self.send_group_list(id)
+ self.log_msg("Player with IP: " + str(ip) + " connected.")
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)
+ self.log_msg("Player with IP: " + str(ip) + " disconnected.")
elif act=="update":
self.players[id].take_dom(xml_dom)
+ self.log_msg("Player with IP: " + str(ip) + " updated.")
self.log_msg(("update", {"id": id,
- "name": xml_dom.get("name"),
- "status": xml_dom.get("status"),
- "role": xml_dom.get("role"),
- "ip": str(ip),
- "group": xml_dom.get("group_id"),
- "room": xml_dom.get("name"),
- "boot": xml_dom.get("rm_boot"),
- "version": xml_dom.get("version"),
- "ping": xml_dom.get("time") \
- }))
+ "name": xml_dom.get("name"),
+ "status": xml_dom.get("status"),
+ "role": xml_dom.get("role"),
+ "ip": str(ip),
+ "group": xml_dom.get("group_id"),
+ "room": xml_dom.get("name"),
+ "boot": xml_dom.get("rm_boot"),
+ "version": xml_dom.get("version"),
+ "ping": xml_dom.get("time") \
+ }))
def strip_cheat_roll(self, string):
try:
@@ -1988,7 +1954,7 @@
given_boot_pwd = None
try:
xml_dom = XML(msg)
- given_boot_pwd = xml_dom.get("boot_pwd")
+ given_boot_pwd = xml_dom.find('boot').get("boot_pwd")
except Exception, e:
print "Error in parse of boot message, Ignoring."
@@ -2011,31 +1977,31 @@
"""
if given_boot_pwd == server_admin_pwd:
# Send a message to everyone in the room, letting them know someone has been booted
- boot_msg = "Booting '(%s) %s' from server... " % (from_id, group_id, to_id, self.players[to_id].name)
+ msg = ''
+ msg += 'Booting (' +str(to_id)+ ') ' +self.players[to_id].name+ ' from server... '
+
+ boot_msg = self.buildMsg('all', '0', group_id, msg)
self.log_msg("boot_msg:" + boot_msg)
self.send_to_group( "0", group_id, boot_msg )
time.sleep( 1 )
self.log_msg("Booting player " + str(to_id) + " from server.")
-
# Send delete player event to all
self.send_to_group("0",group_id,self.players[to_id].toxml("del"))
-
# Remove the player from local data structures
self.del_player(to_id,group_id)
-
# Refresh the group data
self.check_group(to_id, group_id)
elif actual_boot_pwd == given_boot_pwd:
# Send a message to everyone in the room, letting them know someone has been booted
- boot_msg = "Booting '(%s) %s' from room... " % (from_id, group_id, to_id, self.players[to_id].name)
+ msg = ''
+ msg += 'Booting (' +str(to_id)+ ') ' +self.players[to_id].name+ ' from server... '
+ boot_msg = self.buildMsg('all', from_id, group_id, msg)
self.log_msg("boot_msg:" + boot_msg)
self.send_to_group( "0", group_id, boot_msg )
time.sleep( 1 )
-
#dump player into the lobby
self.move_player(to_id,"0")
-
# Refresh the group data
self.check_group(to_id, group_id)
else:
@@ -2051,7 +2017,9 @@
finally:
try:
- if xml_dom: xml_dom.unlink()
+ try:
+ if xml_dom: xml_dom.unlink()
+ except: pass
except Exception, e:
traceback.print_exc()
self.log_msg('Exception in xml_dom.unlink() ' + str(e))
@@ -2094,7 +2062,7 @@
configDom = parse(dir_struct["user"] + 'ban_list.xml')
self.ban_list = {}
for element in configDom.findall('banned'):
- player = element.get('name').replace("&", "&").replace("<", "<").replace('"', """).replace(">", ">")
+ player = element.get('name').replace("&", "&").replace("<", "<").replace('"', "").replace(">", ">")
ip = element.get('ip')
self.ban_list[ip] = {}
self.ban_list[ip]['ip'] = ip
diff -r b29454610f36 -r 9230a33defd9 orpg/networking/mplay_server_gui.py
--- a/orpg/networking/mplay_server_gui.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/networking/mplay_server_gui.py Wed Jun 16 03:06:20 2010 -0500
@@ -348,7 +348,10 @@
chat.set('type', '1')
chat.set('version', '1.0')
chat.text = broadcast
- msg = self.main.server.server.buildMsg('all', '0', '1', tostring(chat))
+ msg = self.main.server.server.buildMsg('all',
+ '0',
+ str(self.main.server.server.players[playerID]),
+ tostring(chat))
if len(msg): self.main.server.server.send_to_group('0', str(groupID), msg )
elif menuItem == 6:
@@ -420,6 +423,7 @@
menu = wx.Menu()
menu.Append(1, 'Start', 'Start server.')
menu.Append(2, 'Stop', 'Shutdown server.')
+ menu.Append(16, 'Clear Log', 'Empty server log')
menu.AppendSeparator()
menu.Append(3, 'E&xit', 'Exit application.')
self.Bind(wx.EVT_MENU, self.OnStart, id=1)
@@ -455,6 +459,7 @@
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.Bind(wx.EVT_MENU, self.ClearLog, id=16)
self.mainMenu.Append( menu, '&Configuration')
# Traipse Suite of Additions.
@@ -562,6 +567,9 @@
def LogToggle(self, event):
self.do_log = event.IsChecked()
+ def ClearLog(self, event):
+ self.log.SetValue('')
+
def OnLogMessage( self, event ):
self.Log( event.message )
@@ -658,19 +666,20 @@
wx.EndBusyCursor()
else: self.show_error("Server is already running.", "Error Starting Server")
- def OnStop(self, event = None):
+ def OnStop(self, event=None):
""" Stop server. """
if self.STATUS == SERVER_RUNNING:
- self.OnUnregister()
+ self.OnUnregister(event)
self.server.stop()
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.conns.DeleteAllItems()
+ if event != 'Quit':
+ 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.conns.DeleteAllItems()
def OnRegister(self, event = None):
""" Call into mplay_server's register() function.
@@ -695,11 +704,12 @@
"""
wx.BeginBusyCursor()
self.server.server.unregister()
- 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)")
+ if event != 'Quit':
+ 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()
def ModifyBanList(self, event):
@@ -726,7 +736,7 @@
def ExitConfirmed(self, event=None):
""" Quit the program. """
- self.OnStop()
+ self.OnStop('Quit')
self.BanListDialog.Destroy()
wx.CallAfter(self.Destroy)
diff -r b29454610f36 -r 9230a33defd9 orpg/orpg_version.py
--- a/orpg/orpg_version.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/orpg_version.py Wed Jun 16 03:06:20 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 Alpha"
+DISTRO = "Traipse Beta"
DIS_VER = "Ornery Orc"
-BUILD = "100503-01"
+BUILD = "100616-00"
# This version is for network capability.
PROTOCOL_VERSION = "1.2"
diff -r b29454610f36 -r 9230a33defd9 orpg/orpg_windows.py
--- a/orpg/orpg_windows.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/orpg_windows.py Wed Jun 16 03:06:20 2010 -0500
@@ -56,49 +56,54 @@
pos = string.rfind(file_name,'.')
ext = string.lower(file_name[pos+1:])
img_type = 0
- recycle_bin = {"gif": wx.BITMAP_TYPE_GIF, "jpg": wx.BITMAP_TYPE_JPEG,
+ recycle_bin = {"gif": wx.BITMAP_TYPE_GIF, "jpg": wx.BITMAP_TYPE_JPEG,
"jpeg": wx.BITMAP_TYPE_JPEG, "bmp": wx.BITMAP_TYPE_BMP, "png": wx.BITMAP_TYPE_PNG}
- if recycle_bin.has_key(ext): img_type = recycle_bin[ext]
- else: img_type = None
- del recycle_bin; return img_type
+ if recycle_bin.has_key(ext): img_type = recycle_bin[ext]
+ else: img_type = None
+ del recycle_bin; return img_type
################################
## Tabs
################################
class orpgTabberWnd(FNB.FlatNotebook):
- def __init__(self, parent, closeable=False, size=wx.DefaultSize, style = False):
+ def __init__(self, parent, closeable=False, size=wx.DefaultSize, style=False):
nbstyle = FNB.FNB_HIDE_ON_SINGLE_TAB|FNB.FNB_BACKGROUND_GRADIENT
+ if style: nbstyle |= style
FNB.FlatNotebook.__init__(self, parent, -1, size=size, style=nbstyle)
rgbcovert = orpg.tools.rgbhex.RGBHex()
self.log = component.get("log")
self.log.log("Enter orpgTabberWnd", ORPG_DEBUG)
self.settings = component.get("settings")
- tabtheme = self.settings.get_setting('TabTheme')
- tabtext = self.settings.get_setting('TabTextColor')
+ tabtheme = self.settings.get('TabTheme')
+ tabtext = self.settings.get('TabTextColor')
(tred, tgreen, tblue) = rgbcovert.rgb_tuple(tabtext)
- tabbedwindows = component.get("tabbedWindows")
- tabbedwindows.append(self)
- component.add("tabbedWindows", tabbedwindows)
+ component.get("tabbedWindows").append(self)
theme_dict = {'slanted&aqua': FNB.FNB_VC8, 'slanted&bw': FNB.FNB_VC8, 'flat&aqua': FNB.FNB_FANCY_TABS,
- 'flat&bw': FNB.FNB_FANCY_TABS, 'customflat': FNB.FNB_FANCY_TABS, 'customslant': FNB.FNB_VC8,
- 'slanted&colorful': FNB.FNB_VC8|FNB.FNB_COLORFUL_TABS, 'slant&colorful': FNB.FNB_VC8|FNB.FNB_COLORFUL_TABS}
- nbstyle |= theme_dict[tabtheme]
- if style: nbstyle |= style
- self.SetWindowStyleFlag(nbstyle)
+ 'flat&bw': FNB.FNB_FANCY_TABS, 'customflat': FNB.FNB_FANCY_TABS, 'customslant': FNB.FNB_VC8}
+ #'slanted&colorful': FNB.FNB_VC8|FNB.FNB_COLORFUL_TABS, 'slant&colorful': FNB.FNB_VC8|FNB.FNB_COLORFUL_TABS}
+ if theme_dict.has_key(tabtheme): style |= theme_dict[tabtheme]
+ else: style |= theme_dict['customflat']; self.settings.change('TabTheme', 'customflat')
+ self.SetWindowStyleFlag(style)
+
+ tabbg = self.settings.get('TabBackgroundGradient')
+ (red, green, blue) = rgbcovert.rgb_tuple(tabbg)
+ self.SetTabAreaColour(wx.Color(red, green, blue))
# Tas - sirebral. Planned changes to the huge statement below.
if tabtheme == 'slanted&aqua':
self.SetGradientColourTo(wx.Color(0, 128, 255))
self.SetGradientColourFrom(wx.WHITE)
+ self.SetNonActiveTabTextColour(wx.BLACK)
elif tabtheme == 'slanted&bw':
self.SetGradientColourTo(wx.WHITE)
self.SetGradientColourFrom(wx.WHITE)
+ self.SetNonActiveTabTextColour(wx.BLACK)
elif tabtheme == 'flat&aqua':
- self.SetGradientColourFrom(wx.Color(0, 128, 255))
- self.SetGradientColourTo(wx.WHITE)
+ self.SetGradientColourTo(wx.Color(0, 128, 255))
+ self.SetGradientColourFrom(wx.WHITE)
self.SetNonActiveTabTextColour(wx.BLACK)
elif tabtheme == 'flat&bw':
@@ -107,28 +112,21 @@
self.SetNonActiveTabTextColour(wx.BLACK)
elif tabtheme == 'customflat':
- gfrom = self.settings.get_setting('TabGradientFrom')
- (red, green, blue) = rgbcovert.rgb_tuple(gfrom)
- self.SetGradientColourFrom(wx.Color(red, green, blue))
+ (red, green, blue) = rgbcovert.rgb_tuple(self.settings.get_setting('TabGradientTo'))
+ self.SetGradientColourTo(wx.Color(red, green, blue))
- gto = self.settings.get_setting('TabGradientTo')
- (red, green, blue) = rgbcovert.rgb_tuple(gto)
- self.SetGradientColourTo(wx.Color(red, green, blue))
+ (red, green, blue) = rgbcovert.rgb_tuple(self.settings.get_setting('TabGradientFrom'))
+ self.SetGradientColourFrom(wx.Color(red, green, blue))
self.SetNonActiveTabTextColour(wx.Color(tred, tgreen, tblue))
elif tabtheme == 'customslant':
- gfrom = self.settings.get_setting('TabGradientFrom')
- (red, green, blue) = rgbcovert.rgb_tuple(gfrom)
- self.SetGradientColourFrom(wx.Color(red, green, blue))
+ (red, green, blue) = rgbcovert.rgb_tuple(self.settings.get_setting('TabGradientTo'))
+ self.SetGradientColourTo(wx.Color(red, green, blue))
- gto = self.settings.get_setting('TabGradientTo')
- (red, green, blue) = rgbcovert.rgb_tuple(gto)
- self.SetGradientColourTo(wx.Color(red, green, blue))
+ (red, green, blue) = rgbcovert.rgb_tuple(self.settings.get_setting('TabGradientFrom'))
+ self.SetGradientColourFrom(wx.Color(red, green, blue))
self.SetNonActiveTabTextColour(wx.Color(tred, tgreen, tblue))
- tabbg = self.settings.get_setting('TabBackgroundGradient')
- (red, green, blue) = rgbcovert.rgb_tuple(tabbg)
- self.SetTabAreaColour(wx.Color(red, green, blue))
self.Refresh()
self.log.log("Exit orpgTabberWnd", ORPG_DEBUG)
diff -r b29454610f36 -r 9230a33defd9 orpg/orpg_xml.py
--- a/orpg/orpg_xml.py Mon May 03 03:29:14 2010 -0500
+++ b/orpg/orpg_xml.py Wed Jun 16 03:06:20 2010 -0500
@@ -31,7 +31,7 @@
from orpg.tools.orpg_log import logger, debug
class xml:
- debug('Developers note. Deprecated call to orpg_xml!!')
+ debug(('Developers note. Deprecated call to orpg_xml!!'), parents=True)
def __init__(self):
pass
diff -r b29454610f36 -r 9230a33defd9 orpg/templates/default_settings.xml
--- a/orpg/templates/default_settings.xml Mon May 03 03:29:14 2010 -0500
+++ b/orpg/templates/default_settings.xml Wed Jun 16 03:06:20 2010 -0500
@@ -2,9 +2,9 @@
-
+
-
+
diff -r b29454610f36 -r 9230a33defd9 orpg/templates/feature.xml
--- a/orpg/templates/feature.xml Mon May 03 03:29:14 2010 -0500
+++ b/orpg/templates/feature.xml Wed Jun 16 03:06:20 2010 -0500
@@ -65,40 +65,84 @@
-
- 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
+ Namespace 2.0
- 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.
+Internal Namespace: !=NodeName=! or !=GridName::Row,Colum=!
+External Namespace: !&Container::NodeName&! or !&Container::GridName::Row,Colum&!
+
+Namespace 2.0 now has two different ways of finding nodes in your gametree: Internal and
+External. The new version will find nodes differently based on which method you use. With External you start looking from the gametree but can define a "container" to be more specific. With Internal you start looking from the "node" the reference is in and look through the tree backwards. You can now reference cells within a grid using either.
-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=!
+*An explanation of terms*
+Gametree: The list of objects on the left-hand side of the window that holds all your nodes.
+Node: Refers to any object in the gametree.
+Container: Refers only to those nodes that are capable of storing other nodes (Splitter,
+Tabbers, Folder, Forms, etc.). Used here, it usually refers to the largest container, i.e. the
+one that shows up in the gametree when fully collapsed. A Container can contain other
+containers.
+ Internal Namespace: !=NodeName=!
+-Used from within a node to call another node in the same container, such as a list node
+calling a text node or grid. *Note* Will not work if multiple nodes within the same container
+have the same name. Multiple nodes within the entirety of the gametree can have the same names
+though as long as they are in different containers.
+-Uses the !=NodeName=! syntax for normal nodes.
+-Uses !=GridName::Row,Colum=! to reference cells within a grid
+
+Examples:
+!=Strength=!
+(Will find the node named “Strength” in the same container and insert it in place of
+!=Strength=!)
-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
+!=Ability Scores::3,5=!
+(Will find cell in Row number 3 and Colum number 5 and insert the information contained there
+in place of !=Ability Scores::3,5=! ) External Namespace: !&Container::NodeName&!
+-Can only be used from chat (currently) to call a node from anywhere in the gametree. You must
+specify the container the node is in. You only have to specify the ‘largest’ container
+(typically the one that shows in the gametree when fully collapsed). It doesn’t matter how
+many smaller containers within the same container it’s in, you need only reference the
+largest. *Note* Will not work if multiple nodes within the same container have the same name.
+Multiple nodes within the entirety of the gametree can have the same names though as long as
+they are in different containers.
+-Uses the !&Container::NodeName&! syntax for normal nodes.
+-Uses !&Container::NodeName::Row,Colum&! to reference cells within a grid.
- 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.
+Examples:
+!&3.5 Character Sheet::Strength&!
+(Will find the node named “Strength” within the larger container “3.5 Character Sheet” and
+insert it in place of !&3.5 Character Sheet::Strength&!)
-Namespace External:
-(Syntax) !&Top Node::Node&!
+!&3.5 Character Sheet::Ability Scores::3,5&!
+(Will find the cell in Row 3, Colum 5 in within the larger container “3.5 Character Sheet” and
+insert its contents in place of !&3.5 Character Sheet::Ability Scores::3,5&!) Other Notes:
+If you have similar nodes (i.e. have a lot of the same node names in them) located within the
+same Larger container, Internal Namespace will still work as normal. With External Namespace,
+you will have to specify which of the smaller containers you wish to call from.
-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.
+For example, if you have a folder container that has two nodes in it, Internal will still work
+fine from within either. However, if you are trying to use External, it you will have to
+specify which of smaller containers you want like so:
+!&LargerContainer::SmallerContainer::NodeName&!
- 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.
+I.E.:
+The Largest container is called “Character Sheets.” It contains three other, Smaller
+containers called “Luke,” “Leia,” and “Vader.” If you want to call the “Strength” node located
+in “Leia” you will have to specify it like so: !&Character Sheets::Leia::Strength&!. The Namespace 2.0 is so far my greatest gift to OpenRPG. Namespace 2.0 surpasses the other Namespaces styles because it has a lightning bolt effect.
+
+In 1.7.1 you could use a reference such as !@Name@! to get the name of the Behir (Example Node). The biggest problem with the Namespace was it would only look at the top most node.
+
+Traipse changes how Namespace works by allowing users to be more specific without needing to be too specific. Namespace 2.0 works in a similar fashion, by finding the top most node with a similar name and attempting to use it. Yet, if you want to be more specific you can add node names to the reference and Namespace will find them in order.
+
+Below are some examples uses of the new Namespace. To try them out, create a 4e PC Sheet node and press the Send button.
<b>1:</b> !&4e PC Sheet::Slot 1&!
-<b>2:</b> !&4e PC Sheet::Belt:: 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)
-
-
+(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&!
@@ -518,12 +562,14 @@
-
-
-
+
+
+
+
+
diff -r b29454610f36 -r 9230a33defd9 orpg/templates/nodes/4e_char_sheet.xml
--- a/orpg/templates/nodes/4e_char_sheet.xml Mon May 03 03:29:14 2010 -0500
+++ b/orpg/templates/nodes/4e_char_sheet.xml Wed Jun 16 03:06:20 2010 -0500
@@ -32,27 +32,27 @@
This bears repeating:
It comes with a Back Pack text node that you can clone to make bags and other containers.
- Climb [1d20+5+!=Abilities::(1,3)=!+(!=General::Level=!/2)]
- Hide [1d20+5+!=Abilities::(2,3)=!+(!=General::Level=!/2)]
- Spot [1d20+5+!=Abilities::(4,3)=!+(!=General::Level=!/2)]
+ <b>Climb:</b> [1d20+5+!=Abilities::(1,3)=!+(!=General::Level=!/2)]
+ <b>Hide:</b> [1d20+5+!=Abilities::(2,3)=!+(!=General::Level=!/2)]
+ <b>Spot:</b> [1d20+5+!=Abilities::(4,3)=!+(!=General::Level=!/2)]
-
+
- <b>Attack</b> !=Combat::Weapons::2,1=! [1d20+!=Combat::To Hit::2,2=!] <b>Damage:</b> [1!=Combat::Weapons::2,2=!+!=Abilities::1,3!]
- <b>Attack</b> !=Combat::Weapons::3,1=! [1d20+!Combat::To Hit::2,2=!] <b>Damage:</b> [1!=Combat::Weapons::3,2=!+!=Abilities::1,3=!]
+ <b>Attack</b> !=Combat::Weapons::2,1=! [1d20+!=Combat::To Hit::2,2=!] <b>Damage:</b> [1!=Combat::Weapons::2,2=!+!=Abilities::1,3=!]
+ <b>Attack</b> !=Combat::Weapons::3,1=! [1d20+!=Combat::To Hit::2,2=!] <b>Damage:</b> [1!=Combat::Weapons::3,2=!+!=Abilities::1,3=!]
Option Text III
- !=At Wills::At Will=!
+ !=Utilities::At Wills::At Will=!
- !=Encounters::Encounter=!
+ !=Utilities::Encounters::Encounter=!
- !=Dailys::Daily=!
+ !=Utilities::Dailys::Daily=!
text
@@ -104,7 +104,7 @@
Total |
!=AC Bonus::(3,2)=!+!=AC Bonus::(4,2)=! |
- Armor | !!Armor::(2,2)!! |
Misc | 0 |
| |
| |
| |
+ Armor | !=Armor::(2,2)=! |
Misc | 0 |
| |
| |
| |
diff -r b29454610f36 -r 9230a33defd9 orpg/templates/nodes/Traipse_User_Guide.xml
--- a/orpg/templates/nodes/Traipse_User_Guide.xml Mon May 03 03:29:14 2010 -0500
+++ b/orpg/templates/nodes/Traipse_User_Guide.xml Wed Jun 16 03:06:20 2010 -0500
@@ -1,5 +1,6 @@
-
- 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.
@@ -16,14 +17,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.
@@ -55,8 +61,100 @@
Fog:
The fog layer hides the entire map from the prying eyes of players.
-
- Quick Help:
+
+
+
+ Namespace 2.0
+
+Internal Namespace: !=NodeName=! or !=GridName::Row,Colum=!
+External Namespace: !&Container::NodeName&! or !&Container::GridName::Row,Colum&!
+
+Namespace 2.0 now has two different ways of finding nodes in your gametree: Internal and
+External. The new version will find nodes differently based on which method you use. With External you start looking from the gametree but can define a "container" to be more specific. With Internal you start looking from the "node" the reference is in and look through the tree backwards. You can now reference cells within a grid using either.
+
+*An explanation of terms*
+Gametree: The list of objects on the left-hand side of the window that holds all your nodes.
+Node: Refers to any object in the gametree.
+Container: Refers only to those nodes that are capable of storing other nodes (Splitter,
+Tabbers, Folder, Forms, etc.). Used here, it usually refers to the largest container, i.e. the
+one that shows up in the gametree when fully collapsed. A Container can contain other
+containers.
+ Internal Namespace: !=NodeName=!
+-Used from within a node to call another node in the same container, such as a list node
+calling a text node or grid. *Note* Will not work if multiple nodes within the same container
+have the same name. Multiple nodes within the entirety of the gametree can have the same names
+though as long as they are in different containers.
+-Uses the !=NodeName=! syntax for normal nodes.
+-Uses !=GridName::Row,Colum=! to reference cells within a grid
+
+Examples:
+!=Strength=!
+(Will find the node named “Strength” in the same container and insert it in place of
+!=Strength=!)
+
+!=Ability Scores::3,5=!
+(Will find cell in Row number 3 and Colum number 5 and insert the information contained there
+in place of !=Ability Scores::3,5=! ) External Namespace: !&Container::NodeName&!
+-Can only be used from chat (currently) to call a node from anywhere in the gametree. You must
+specify the container the node is in. You only have to specify the ‘largest’ container
+(typically the one that shows in the gametree when fully collapsed). It doesn’t matter how
+many smaller containers within the same container it’s in, you need only reference the
+largest. *Note* Will not work if multiple nodes within the same container have the same name.
+Multiple nodes within the entirety of the gametree can have the same names though as long as
+they are in different containers.
+-Uses the !&Container::NodeName&! syntax for normal nodes.
+-Uses !&Container::NodeName::Row,Colum&! to reference cells within a grid.
+
+Examples:
+!&3.5 Character Sheet::Strength&!
+(Will find the node named “Strength” within the larger container “3.5 Character Sheet” and
+insert it in place of !&3.5 Character Sheet::Strength&!)
+
+!&3.5 Character Sheet::Ability Scores::3,5&!
+(Will find the cell in Row 3, Colum 5 in within the larger container “3.5 Character Sheet” and
+insert its contents in place of !&3.5 Character Sheet::Ability Scores::3,5&!) Other Notes:
+If you have similar nodes (i.e. have a lot of the same node names in them) located within the
+same Larger container, Internal Namespace will still work as normal. With External Namespace,
+you will have to specify which of the smaller containers you wish to call from.
+
+For example, if you have a folder container that has two nodes in it, Internal will still work
+fine from within either. However, if you are trying to use External, it you will have to
+specify which of smaller containers you want like so:
+!&LargerContainer::SmallerContainer::NodeName&!
+
+I.E.:
+The Largest container is called “Character Sheets.” It contains three other, Smaller
+containers called “Luke,” “Leia,” and “Vader.” If you want to call the “Strength” node located
+in “Leia” you will have to specify it like so: !&Character Sheets::Leia::Strength&!. The Namespace 2.0 is so far my greatest gift to OpenRPG. Namespace 2.0 surpasses the other Namespaces styles because it has a lightning bolt effect.
+
+In 1.7.1 you could use a reference such as !@Name@! to get the name of the Behir (Example Node). The biggest problem with the Namespace was it would only look at the top most node.
+
+Traipse changes how Namespace works by allowing users to be more specific without needing to be too specific. Namespace 2.0 works in a similar fashion, by finding the top most node with a similar name and attempting to use it. Yet, if you want to be more specific you can add node names to the reference and Namespace will find them in order.
+
+I hope you enjoy the new Namespace very much. I tried to capture an OpenRPG users idea of how Namespace 'should' work and this time I think I set a new bar. I even created a plugin so Traipse users can use the Standard namespace references!
+
+Below are some examples uses of the new Namespace. To try them out, create a 4e PC Sheet node and press the Send button.
+
+<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
@@ -113,86 +211,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)
@@ -244,8 +325,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.
@@ -256,10 +375,15 @@
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!
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff -r b29454610f36 -r 9230a33defd9 orpg/templates/nodes/Warhammerv2CS2-Traipse.xml
--- a/orpg/templates/nodes/Warhammerv2CS2-Traipse.xml Mon May 03 03:29:14 2010 -0500
+++ b/orpg/templates/nodes/Warhammerv2CS2-Traipse.xml Wed Jun 16 03:06:20 2010 -0500
@@ -1,71 +1,69 @@
-
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
-
+
+ 1
-
+
-
+
-
+
-
+
1
-
+
-
+
-
+
-
+
Meduim
-
+
-
+
-
+
-
-
-
+
+
-
+
-
+
-
+
Copper |
@@ -93,227 +91,216 @@
-
-
-
+
+
[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
-
-
- Strength: [Q1d20 + (!=Strength=! / 2 - 5) + (!=Character Level=! / 2)]
- Constitution: [Q1d20 + (!=Constitution=! / 2 - 5) + (!=Character Level=! / 2)]
- Dexterity: [Q1d20 + (!=Dexterity=! / 2 - 5) + (!=Character Level=! / 2)]
- Intelligence: [Q1d20 + (!=Intelligence=! / 2 - 5) + (!=Character Level=! / 2)]
- Wisdom: [Q1d20 + (!=Wisdom=! / 2 - 5) + (!=Character Level=! / 2)]
- Charisma: [Q1d20 + (!=Charisma=! / 2 - 5) + (!=Character Level=! / 2)]
+
+
+ Strength: [Q1d20 + (!=Strength=! / 2 - 5) + (!=Character Level=! / 2)]
+ Constitution: [Q1d20 + (!=Constitution=! / 2 - 5) + (!=Character Level=! / 2)]
+ Dexterity: [Q1d20 + (!=Dexterity=! / 2 - 5) + (!=Character Level=! / 2)]
+ Intelligence: [Q1d20 + (!=Intelligence=! / 2 - 5) + (!=Character Level=! / 2)]
+ Wisdom: [Q1d20 + (!=Wisdom=! / 2 - 5) + (!=Character Level=! / 2)]
+ Charisma: [Q1d20 + (!=Charisma=! / 2 - 5) + (!=Character Level=! / 2)]
-
-
-
+
+
10
-
+
10
-
-
- Acrobatics: [1d20 + ((!=Dexterity=! / 2) - 5) + (!=Character Level=! / 2) + !=Acrobatics Training=! + !=Acrobatics Bonus=!]
- Arcana: [1d20 + ((!=Intelligence=! / 2) - 5) + (!=Character Level=! / 2) + !=Arcana Training=! + !=Arcana Bonus=!]
- Athletics: [1d20 + ((!=Strength=! / 2) - 5) + (!=Character Level=! / 2) + !=Athletics Training=! + !=Athletics Bonus=!]
- Bluff: [1d20 + ((!=Charisma=! / 2) - 5) + (!=Character Level=! / 2) + !=Bluff Training=! + !=Bluff Bonus=!]
- Diplomacy: [1d20 + ((!=Charisma=! / 2) - 5) + (!=Character Level=! / 2) + !=Diplomacy Training=! + !=Diplomacy Bonus=!]
- Dungeoneering: [1d20 + ((!=Wisdom=! / 2) - 5) + (!=Character Level=! / 2) + !=Dungeoneering Training=! + !=Dungeoneering Bonus=!]
- Endurance Roll: [1d20 + ((!=Constitution=! / 2) - 5) + (!=Character Level=! / 2) + !=Endurance Training=! + !=Endurance Bonus=!]
- Heal: [1d20 + ((!=Wisdom=! / 2) - 5) + (!=Character Level=! / 2) + !=Heal Training=! + !=Heal Bonus=!]
- History: [1d20 + ((!=Intelligence=! / 2) - 5) + (!=Character Level=! / 2) + !=History Training=! + !=History Bonus=!]
- Insight: [1d20 + ((!=Wisdom=! / 2) - 5) + (!=Character Level=! / 2) + !=Insight Training=! + !=Insight Bonus=!]
- Intimidate: [1d20 + ((!=Charisma=! / 2) - 5) + (!=Character Level=! / 2) + !=Intimidate Training=! + !=Intimidate Bonus=!]
- Nature: [1d20 + ((!=Wisdom=! / 2) - 5) + (!=Character Level=! / 2) + !=Nature Training=! + !=Nature Bonus=!]
- Perception: [1d20 + ((!=Wisdom=! / 2) - 5) + (!=Character Level=! / 2) + !=Perception Training=! + !=Perception Bonus=!]
- Religion: [1d20 + ((!=Intelligence=! / 2) - 5) + (!=Character Level=! / 2) + !=Religion Training=! + !=Religion Bonus=!]
- Stealth: [1d20 + ((!=Dexterity=! / 2) - 5) + (!=Character Level=! / 2) + !=Stealth Training=! + !=Stealth Bonus=!]
- Streetwise: [1d20 + ((!=Charisma=! / 2) - 5) + (!=Character Level=! / 2) + !=Streetwise Training=! + !=Streetwise Bonus=!]
- Thievery: [1d20 + ((!=Dexterity=! / 2) - 5) + (!=Character Level=! / 2) + !=Thievery Training=! + !=Thievery Bonus=!]
+
+
+ Acrobatics: [1d20 + ((!=Dexterity=! / 2) - 5) + (!=Character Level=! / 2) + !=Acrobatics Training=! + !=Acrobatics Bonus=!]
+ Arcana: [1d20 + ((!=Intelligence=! / 2) - 5) + (!=Character Level=! / 2) + !=Arcana Training=! + !=Arcana Bonus=!]
+ Athletics: [1d20 + ((!=Strength=! / 2) - 5) + (!=Character Level=! / 2) + !=Athletics Training=! + !=Athletics Bonus=!]
+ Bluff: [1d20 + ((!=Charisma=! / 2) - 5) + (!=Character Level=! / 2) + !=Bluff Training=! + !=Bluff Bonus=!]
+ Diplomacy: [1d20 + ((!=Charisma=! / 2) - 5) + (!=Character Level=! / 2) + !=Diplomacy Training=! + !=Diplomacy Bonus=!]
+ Dungeoneering: [1d20 + ((!=Wisdom=! / 2) - 5) + (!=Character Level=! / 2) + !=Dungeoneering Training=! + !=Dungeoneering Bonus=!]
+ Endurance Roll: [1d20 + ((!=Constitution=! / 2) - 5) + (!=Character Level=! / 2) + !=Endurance Training=! + !=Endurance Bonus=!]
+ Heal: [1d20 + ((!=Wisdom=! / 2) - 5) + (!=Character Level=! / 2) + !=Heal Training=! + !=Heal Bonus=!]
+ History: [1d20 + ((!=Intelligence=! / 2) - 5) + (!=Character Level=! / 2) + !=History Training=! + !=History Bonus=!]
+ Insight: [1d20 + ((!=Wisdom=! / 2) - 5) + (!=Character Level=! / 2) + !=Insight Training=! + !=Insight Bonus=!]
+ Intimidate: [1d20 + ((!=Charisma=! / 2) - 5) + (!=Character Level=! / 2) + !=Intimidate Training=! + !=Intimidate Bonus=!]
+ Nature: [1d20 + ((!=Wisdom=! / 2) - 5) + (!=Character Level=! / 2) + !=Nature Training=! + !=Nature Bonus=!]
+ Perception: [1d20 + ((!=Wisdom=! / 2) - 5) + (!=Character Level=! / 2) + !=Perception Training=! + !=Perception Bonus=!]
+ Religion: [1d20 + ((!=Intelligence=! / 2) - 5) + (!=Character Level=! / 2) + !=Religion Training=! + !=Religion Bonus=!]
+ Stealth: [1d20 + ((!=Dexterity=! / 2) - 5) + (!=Character Level=! / 2) + !=Stealth Training=! + !=Stealth Bonus=!]
+ Streetwise: [1d20 + ((!=Charisma=! / 2) - 5) + (!=Character Level=! / 2) + !=Streetwise Training=! + !=Streetwise Bonus=!]
+ Thievery: [1d20 + ((!=Dexterity=! / 2) - 5) + (!=Character Level=! / 2) + !=Thievery Training=! + !=Thievery Bonus=!]
-
-
-
-
-
-
+
+
+
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
-
-
- 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 |
@@ -360,7 +347,7 @@
-
+
Armor Class |
@@ -384,694 +371,652 @@
-
- [1d20 + !=Class Save Bonus=! + !=Racial Save Bonus=! + !=Feat Save Bonus=! + !=Misc Save Bonus=!]
+
+ [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=!]
+
+ [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=!]
+
+ [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=!]
+
+ [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=!]
+
+ [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
-
-
-
- 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=!]
-
-
+
+ !=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=!]
+
+ !=Weapon 1 Name=!: [Q((!=Weapon 1 Damage Multiplier=!*!=Weapon 1 Damage Dice Count=!)d!=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=!]
-
-
+
+ !=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=!]
+
+ !=Weapon 1 Name=!: [Q((!=Weapon 1 Damage Multiplier=!*!=Weapon 1 Damage Dice Count=!)d!=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=!]
-
-
+
+ 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 1 Name=!: [Q((!=Weapon 1 Damage Multiplier=!*!=Weapon 1 Damage Dice Count=!)d!=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
-
-
-
- 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=!]
-
-
+
+ !=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=!]
+
+ !=Weapon 2 Name=!: [Q((!=Weapon 2 Damage Multiplier=!*!=Weapon 2 Damage Dice Count=!)d!=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=!]
-
-
+
+ !=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=!]
+
+ !=Weapon 2 Name=!: [Q((!=Weapon 2 Damage Multiplier=!*!=Weapon 2 Damage Dice Count=!)d!=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=!]
-
-
+
+ 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 2 Name=!: [Q((!=Weapon 2 Damage Multiplier=!*!=Weapon 2 Damage Dice Count=!)d!=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
-
-
-
- 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=!]
-
-
+
+ !=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=!]
+
+ !=Weapon 3 Name=!: [Q((!=Weapon 3 Damage Multiplier=!*!=Weapon 3 Damage Dice Count=!)d!=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=!]
-
-
+
+ !=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=!]
+
+ !=Weapon 3 Name=!: [Q((!=Weapon 3 Damage Multiplier=!*!=Weapon 3 Damage Dice Count=!)d!=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=!]
-
-
+
+ 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 3 Name=!: [Q((!=Weapon 3 Damage Multiplier=!*!=Weapon 3 Damage Dice Count=!)d!=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
-
-
-
- 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=!]
-
-
+
+ !=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=!]
+
+ !=Weapon 4 Name=!: [Q((!=Weapon 4 Damage Multiplier=!*!=Weapon 4 Damage Dice Count=!)d!=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=!]
-
-
+
+ !=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=!]
+
+ !=Weapon 4 Name=!: [Q((!=Weapon 4 Damage Multiplier=!*!=Weapon 4 Damage Dice Count=!)d!=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=!]
-
-
+
+ 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 4 Name=!: [Q((!=Weapon 4 Damage Multiplier=!*!=Weapon 4 Damage Dice Count=!)d!=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
-
-
-
- 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=!]
-
-
+
+ !=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=!]
+
+ !=Weapon 5 Name=!: [Q((!=Weapon 5 Damage Multiplier=!*!=Weapon 5 Damage Dice Count=!)d!=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=!]
-
-
+
+ !=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=!]
+
+ !=Weapon 5 Name=!: [Q((!=Weapon 5 Damage Multiplier=!*!=Weapon 5 Damage Dice Count=!)d!=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=!]
-
-
+
+ 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=!]
+
+ !=Weapon 5 Name=!: [Q((!=Weapon 5 Damage Multiplier=!*!=Weapon 5 Damage Dice Count=!)d!=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
+
+
+ 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=!]
+
+ 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=!]
+
+ 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
-
-
+
+ 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=!
+
+ 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 |
@@ -1138,7 +1083,7 @@
-
+