comparison orpg/chat/chatwnd.py @ 191:a3d7e05085da beta

Traipse Beta 'OpenRPG' {100201-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 (Beta) New Features: New Bookmarks Feature New 'boot' command to remote admin New confirmation window for sent nodes Miniatures Layer pop up box allows users to turn off Mini labels, from FlexiRPG New Zoom Mouse plugin added New Images added to Plugin UI Switching to Element Tree New Map efficiency, from FlexiRPG New Status Bar to Update Manager New TrueDebug Class in orpg_log (See documentation for usage) New Portable Mercurial New Tip of the Day, from Core and community New Reference Syntax added for custom PC sheets New Child Reference for gametree New Parent Reference for gametree New Gametree Recursion method, mapping, context sensitivity, and effeciency.. New Features node with bonus nodes and Node Referencing help added New Dieroller structure from Core New DieRoller portability for odd Dice New 7th Sea die roller; ie [7k3] = [7d10.takeHighest(3).open(10)] New 'Mythos' System die roller added New vs. die roller method for WoD; ie [3v3] = [3d10.vs(3)]. Included for Mythos roller also New Warhammer FRPG Die Roller (Special thanks to Puu-san for the support) New EZ_Tree Reference system. Push a button, Traipse the tree, get a reference (Beta!) New Grids act more like Spreadsheets in Use mode, with Auto Calc Fixes: Fix to allow for portability to an OpenSUSE linux OS Fix to mplay_client for Fedora and OpenSUSE Fix to Text based Server Fix to Remote Admin Commands Fix to Pretty Print, from Core Fix to Splitter Nodes not being created Fix to massive amounts of images loading, from Core Fix to Map from gametree not showing to all clients Fix to gametree about menus Fix to Password Manager check on startup Fix to PC Sheets from tool nodes. They now use the tabber_panel Fix to Whiteboard ID to prevent random line or text deleting. Fixes to Server, Remote Server, and Server GUI Fix to Update Manager; cleaner clode for saved repositories Fixes made to Settings Panel and now reactive settings when Ok is pressed Fixes to Alternity roller's attack roll. Uses a simple Tuple instead of a Splice Fix to Use panel of Forms and Tabbers. Now longer enters design mode Fix made Image Fetching. New fetching image and new failed image Fix to whiteboard ID's to prevent non updated clients from ruining the fix. default_manifest.xml renamed to default_upmana.xml
author sirebral
date Mon, 01 Feb 2010 09:57:07 -0600
parents dcae32e219f1
children fb08f5731b5e
comparison
equal deleted inserted replaced
190:15488fe94f52 191:a3d7e05085da
59 import orpg.tools.inputValidator 59 import orpg.tools.inputValidator
60 from orpg.tools.validate import validate 60 from orpg.tools.validate import validate
61 from orpg.tools.orpg_settings import settings 61 from orpg.tools.orpg_settings import settings
62 import orpg.tools.predTextCtrl 62 import orpg.tools.predTextCtrl
63 from orpg.tools.orpg_log import logger, debug 63 from orpg.tools.orpg_log import logger, debug
64 from orpg.tools.InterParse import Parse
64 from orpg.orpgCore import component 65 from orpg.orpgCore import component
65 from xml.etree.ElementTree import tostring 66 from xml.etree.ElementTree import tostring
66 67
67 from orpg.networking.mplay_client import MPLAY_CONNECTED 68 from orpg.networking.mplay_client import MPLAY_CONNECTED
68 NEWCHAT = False 69 NEWCHAT = False
1027 1028
1028 # play sound 1029 # play sound
1029 sound_file = self.settings.get_setting("SendSound") 1030 sound_file = self.settings.get_setting("SendSound")
1030 if sound_file != '': component.get('sound').play(sound_file) 1031 if sound_file != '': component.get('sound').play(sound_file)
1031 if s[0] != "/": ## it's not a slash command 1032 if s[0] != "/": ## it's not a slash command
1032 s = self.ParsePost( s, True, True ) 1033 s = Parse.Post( s, True, True )
1033 else: self.chat_cmds.docmd(s) # emote is in chatutils.py 1034 else: self.chat_cmds.docmd(s) # emote is in chatutils.py
1034 1035
1035 def on_chat_key_down(self, event): 1036 def on_chat_key_down(self, event):
1036 s = self.chattxt.GetValue() 1037 s = self.chattxt.GetValue()
1037 if event.GetKeyCode() == wx.WXK_RETURN and not event.ShiftDown(): 1038 if event.GetKeyCode() == wx.WXK_RETURN and not event.ShiftDown():
1166 id = evt.GetId() 1167 id = evt.GetId()
1167 if self.dieIDs.has_key(id): dieText += self.dieIDs[id] 1168 if self.dieIDs.has_key(id): dieText += self.dieIDs[id]
1168 if len(dieMod) and dieMod[0] not in "*/-+": dieMod = "+" + dieMod 1169 if len(dieMod) and dieMod[0] not in "*/-+": dieMod = "+" + dieMod
1169 dieText += dieMod 1170 dieText += dieMod
1170 dieText = "[" + dieText + "]" 1171 dieText = "[" + dieText + "]"
1171 self.ParsePost(dieText, 1, 1) 1172 Parse.Post(dieText, 1, 1)
1172 self.chattxt.SetFocus() 1173 self.chattxt.SetFocus()
1173 1174
1174 def on_chat_save(self, evt): 1175 def on_chat_save(self, evt):
1175 f = wx.FileDialog(self,"Save Chat Buffer",".","","HTM* (*.htm*)|*.htm*|HTML (*.html)|*.html|HTM (*.htm)|*.htm",wx.SAVE) 1176 f = wx.FileDialog(self,"Save Chat Buffer",".","","HTM* (*.htm*)|*.htm*|HTML (*.html)|*.html|HTM (*.htm)|*.htm",wx.SAVE)
1176 if f.ShowModal() == wx.ID_OK: 1177 if f.ShowModal() == wx.ID_OK:
1296 newmatch = re.sub(rule[0], rule[1], match) 1297 newmatch = re.sub(rule[0], rule[1], match)
1297 text = text.replace(match, newmatch) 1298 text = text.replace(match, newmatch)
1298 return text 1299 return text
1299 1300
1300 def emote_message(self, text): 1301 def emote_message(self, text):
1301 text = self.NormalizeParse(text) 1302 text = Parse.Normalize(text)
1302 text = self.colorize(self.emotecolor, text) 1303 text = self.colorize(self.emotecolor, text)
1303 if self.type == MAIN_TAB and self.sendtarget == 'all': self.send_chat_message(text,chat_msg.EMOTE_MESSAGE) 1304 if self.type == MAIN_TAB and self.sendtarget == 'all': self.send_chat_message(text,chat_msg.EMOTE_MESSAGE)
1304 elif self.type == MAIN_TAB and self.sendtarget == "gm": 1305 elif self.type == MAIN_TAB and self.sendtarget == "gm":
1305 msg_type = chat_msg.WHISPER_EMOTE_MESSAGE 1306 msg_type = chat_msg.WHISPER_EMOTE_MESSAGE
1306 the_gms = self.get_gms() 1307 the_gms = self.get_gms()
1314 text = "** " + name + " " + text + " **" 1315 text = "** " + name + " " + text + " **"
1315 self.EmotePost(text) 1316 self.EmotePost(text)
1316 1317
1317 def whisper_to_players(self, text, player_ids): 1318 def whisper_to_players(self, text, player_ids):
1318 tabbed_whispers_p = self.settings.get_setting("tabbedwhispers") 1319 tabbed_whispers_p = self.settings.get_setting("tabbedwhispers")
1319 text = self.NormalizeParse(text) 1320 text = Parse.Normalize(text)
1320 player_names = "" 1321 player_names = ""
1321 for m in player_ids: 1322 for m in player_ids:
1322 id = m.strip() 1323 id = m.strip()
1323 if self.session.is_valid_id(id): 1324 if self.session.is_valid_id(id):
1324 returned_name = self.session.get_player_by_player_id(id)[0] 1325 returned_name = self.session.get_player_by_player_id(id)[0]
1586 except Exception, e: 1587 except Exception, e:
1587 logger.general(traceback.format_exc()) 1588 logger.general(traceback.format_exc())
1588 logger.general("EXCEPTION: " + str(e)) 1589 logger.general("EXCEPTION: " + str(e))
1589 return "[ERROR]" 1590 return "[ERROR]"
1590 1591
1591 #### Post with parsing dice ####
1592
1593 def ParsePost(self, s, send=False, myself=False):
1594 s = self.NormalizeParse(s)
1595 self.set_colors()
1596 self.Post(s,send,myself)
1597
1598 def NormalizeParse(self, s):
1599 for plugin_fname in self.activeplugins.keys():
1600 plugin = self.activeplugins[plugin_fname]
1601 try: s = plugin.pre_parse(s)
1602 except Exception, e:
1603 if str(e) != "'module' object has no attribute 'post_msg'":
1604 logger.general(traceback.format_exc())
1605 logger.general("EXCEPTION: " + str(e))
1606 if self.parsed == 0:
1607 s = self.ParseNode(s)
1608 s = self.ParseDice(s)
1609 s = self.ParseFilter(s)
1610 self.parsed = 1
1611 return s
1612
1613 def ParseFilter(self, s):
1614 s = self.GetFilteredText(s)
1615 return s
1616
1617 def ParseNode(self, s):
1618 """Parses player input for embedded nodes rolls"""
1619 cur_loc = 0
1620 #[a-zA-Z0-9 _\-\.]
1621 reg = re.compile("(!@(.*?)@!)")
1622 matches = reg.findall(s)
1623 for i in xrange(0,len(matches)):
1624 newstr = self.ParseNode(self.resolve_nodes(matches[i][1]))
1625 s = s.replace(matches[i][0], newstr, 1)
1626 return s
1627
1628 def ParseDice(self, s):
1629 """Parses player input for embedded dice rolls"""
1630 reg = re.compile("\[([^]]*?)\]")
1631 matches = reg.findall(s)
1632 for i in xrange(0,len(matches)):
1633 newstr = self.PraseUnknowns(matches[i])
1634 qmode = 0
1635 newstr1 = newstr
1636 if newstr[0].lower() == 'q':
1637 newstr = newstr[1:]
1638 qmode = 1
1639 if newstr[0].lower() == '#':
1640 newstr = newstr[1:]
1641 qmode = 2
1642 try: newstr = component.get('DiceManager').proccessRoll(newstr)
1643 except: pass
1644 if qmode == 1:
1645 s = s.replace("[" + matches[i] + "]",
1646 "<!-- Official Roll [" + newstr1 + "] => " + newstr + "-->" + newstr, 1)
1647 elif qmode == 2:
1648 s = s.replace("[" + matches[i] + "]", newstr[len(newstr)-2:-1], 1)
1649 else: s = s.replace("[" + matches[i] + "]",
1650 "[" + newstr1 + "<!-- Official Roll -->] => " + newstr, 1)
1651 return s
1652
1653 def PraseUnknowns(self, s):
1654 # Uses a tuple. Usage: ?Label}dY. If no Label is assigned then use ?}DY
1655 newstr = "0"
1656 reg = re.compile("(\?\{*)([a-zA-Z ]*)(\}*)")
1657 matches = reg.findall(s)
1658 for i in xrange(0,len(matches)):
1659 lb = "Replace '?' with: "
1660 if len(matches[i][0]):
1661 lb = matches[i][1] + "?: "
1662 dlg = wx.TextEntryDialog(self, lb, "Missing Value?")
1663 dlg.SetValue('')
1664 if matches[i][0] != '':
1665 dlg.SetTitle("Enter Value for " + matches[i][1])
1666 if dlg.ShowModal() == wx.ID_OK: newstr = dlg.GetValue()
1667 if newstr == '': newstr = '0'
1668 s = s.replace(matches[i][0], newstr, 1).replace(matches[i][1], '', 1).replace(matches[i][2], '', 1)
1669 dlg.Destroy()
1670 return s
1671 1592
1672 # This subroutine builds a chat display name. 1593 # This subroutine builds a chat display name.
1673 # 1594 #
1674 def chat_display_name(self, player): 1595 def chat_display_name(self, player):
1675 if self.settings.get_setting("ShowIDInChat") == "0": 1596 if self.settings.get_setting("ShowIDInChat") == "0":
1706 elif c == '"': 1627 elif c == '"':
1707 if in_tag: rs = rs[:i] + "'" + rs[i+1:] 1628 if in_tag: rs = rs[:i] + "'" + rs[i+1:]
1708 i += 1 1629 i += 1
1709 return rs 1630 return rs
1710 1631
1711 def resolve_loop(self, node, path, step, depth):
1712 if step == depth:
1713 return self.resolution(node)
1714 else:
1715 child_list = node.findall('nodehandler')
1716 for child in child_list:
1717 if step == depth: break
1718 if child.get('name') == path[step]:
1719 node = child
1720 step += 1
1721 if node.get('class') in ('dnd35char_handler',
1722 "SWd20char_handler",
1723 "d20char_handler",
1724 "dnd3echar_handler"): self.resolve_cust_loop(node, path, step, depth)
1725 elif node.get('class') == 'rpg_grid_handler': self.resolve_grid(node, path, step, depth)
1726 else: self.resolve_loop(node, path, step, depth)
1727
1728 def resolve_grid(self, node, path, step, depth):
1729 if step == depth:
1730 self.data = 'Invalid Grid Reference!'
1731 return
1732 cell = tuple(path[step].strip('(').strip(')').split(','))
1733 grid = node.find('grid')
1734 rows = grid.findall('row')
1735 col = rows[int(self.ParseDice(cell[0]))-1].findall('cell')
1736 try: self.data = self.ParseMap(col[int(self.ParseDice(cell[1]))-1].text, node) or 'No Cell Data'
1737 except: self.data = 'Invalid Grid Reference!'
1738 return
1739
1740 def resolution(self, node):
1741 if self.passed == False:
1742 self.passed = True
1743 if node.get('class') == 'textctrl_handler':
1744 s = str(node.find('text').text)
1745 else: s = 'Nodehandler for '+ node.get('class') + ' not done!' or 'Invalid Reference!'
1746 else:
1747 s = ''
1748 s = self.ParseMap(s, node)
1749 s = self.ParseParent(s, node.get('map'))
1750 self.data = s
1751
1752 def ParseMap(self, s, node):
1753 """Parses player input for embedded nodes rolls"""
1754 cur_loc = 0
1755 reg = re.compile("(!!(.*?)!!)")
1756 matches = reg.findall(s)
1757 for i in xrange(0,len(matches)):
1758 tree_map = node.get('map')
1759 tree_map = tree_map + '::' + matches[i][1]
1760 newstr = '!@'+ tree_map +'@!'
1761 s = s.replace(matches[i][0], newstr, 1)
1762 s = self.ParseNode(s)
1763 s = self.ParseParent(s, tree_map)
1764 return s
1765
1766 def ParseParent(self, s, tree_map):
1767 """Parses player input for embedded nodes rolls"""
1768 cur_loc = 0
1769 reg = re.compile("(!#(.*?)#!)")
1770 matches = reg.findall(s)
1771 for i in xrange(0,len(matches)):
1772 ## Build the new tree_map
1773 new_map = tree_map.split('::')
1774 del new_map[len(new_map)-1]
1775 parent_map = matches[i][1].split('::')
1776 ## Find an index or use 1 for ease of use.
1777 try: index = new_map.index(parent_map[0])
1778 except: index = 1
1779 ## Just replace the old tree_map from the index.
1780 new_map[index:len(new_map)] = parent_map
1781 newstr = '::'.join(new_map)
1782 newstr = '!@'+ newstr +'@!'
1783 s = s.replace(matches[i][0], newstr, 1)
1784 s = self.ParseNode(s)
1785 return s
1786
1787 def resolve_nodes(self, s):
1788 self.passed = False
1789 self.data = 'Invalid Reference!'
1790 value = ""
1791 path = s.split('::')
1792 depth = len(path)
1793 self.gametree = component.get('tree')
1794 try: node = self.gametree.tree_map[path[0]]['node']
1795 except Exception, e: return self.data
1796 if node.get('class') in ('dnd35char_handler',
1797 "SWd20char_handler",
1798 "d20char_handler",
1799 "dnd3echar_handler"): self.resolve_cust_loop(node, path, 1, depth)
1800 elif node.get('class') == 'rpg_grid_handler': self.resolve_grid(node, path, 1, depth)
1801 else: self.resolve_loop(node, path, 1, depth)
1802 return self.data
1803
1804 def resolve_cust_loop(self, node, path, step, depth):
1805 node_class = node.get('class')
1806 ## Code needs clean up. Either choose .lower() or .title(), then reset the path list's content ##
1807 if step == depth: self.resolution(node)
1808 ##Build Abilities dictionary##
1809 if node_class not in ('d20char_handler', "SWd20char_handler"): ab = node.find('character').find('abilities')
1810 else: ab = node.find('abilities')
1811 ab_list = ab.findall('stat'); pc_stats = {}
1812
1813 for ability in ab_list:
1814 pc_stats[ability.get('name')] = (
1815 str(ability.get('base')),
1816 str((int(ability.get('base'))-10)/2) )
1817 pc_stats[ability.get('abbr')] = (
1818 str(ability.get('base')),
1819 str((int(ability.get('base'))-10)/2) )
1820
1821 if node_class not in ('d20char_handler', "SWd20char_handler"): ab = node.find('character').find('saves')
1822 else: ab = node.find('saves')
1823 ab_list = ab.findall('save')
1824 for save in ab_list:
1825 pc_stats[save.get('name')] = (str(save.get('base')), str(int(save.get('magmod')) + int(save.get('miscmod')) + int(pc_stats[save.get('stat')][1]) ) )
1826 if save.get('name') == 'Fortitude': abbr = 'Fort'
1827 if save.get('name') == 'Reflex': abbr = 'Ref'
1828 if save.get('name') == 'Will': abbr = 'Will'
1829 pc_stats[abbr] = ( str(save.get('base')), str(int(save.get('magmod')) + int(save.get('miscmod')) + int(pc_stats[save.get('stat')][1]) ) )
1830
1831 if path[step].lower() == 'skill':
1832 if node_class not in ('d20char_handler', "SWd20char_handler"): node = node.find('snf')
1833 node = node.find('skills')
1834 child_list = node.findall('skill')
1835 for child in child_list:
1836 if path[step+1].lower() == child.get('name').lower():
1837 if step+2 == depth: self.data = child.get('rank')
1838 elif path[step+2].lower() == 'check':
1839 self.data = '<b>Skill Check:</b> ' + child.get('name') + ' [1d20+'+str( int(child.get('rank')) + int(pc_stats[child.get('stat')][1]) )+']'
1840 return
1841
1842 if path[step].lower() == 'feat':
1843 if node_class not in ('d20char_handler', "SWd20char_handler"): node = node.find('snf')
1844 node = node.find('feats')
1845 child_list = node.findall('feat')
1846 for child in child_list:
1847 if path[step+1].lower() == child.get('name').lower():
1848 if step+2 == depth: self.data = '<b>'+child.get('name')+'</b>'+': '+child.get('desc')
1849 return
1850 if path[step].lower() == 'cast':
1851 if node_class not in ('d20char_handler', "SWd20char_handler"): node = node.find('snp')
1852 node = node.find('spells')
1853 child_list = node.findall('spell')
1854 for child in child_list:
1855 if path[step+1].lower() == child.get('name').lower():
1856 if step+2 == depth: self.data = '<b>'+child.get('name')+'</b>'+': '+child.get('desc')
1857 return
1858 if path[step].lower() == 'attack':
1859 if node_class not in ('d20char_handler', "SWd20char_handler"): node = node.find('combat')
1860 if path[step+1].lower() == 'melee' or path[step+1].lower() == 'm':
1861 bonus_text = '(Melee)'
1862 bonus = node.find('attacks')
1863 bonus = bonus.find('melee')
1864 bonus = bonus.attrib; d = int(pc_stats['Str'][1])
1865 elif path[step+1].lower() == 'ranged' or path[step+1].lower() == 'r':
1866 bonus_text = '(Ranged)'
1867 bonus = node.find('attacks')
1868 bonus = bonus.find('ranged')
1869 bonus = bonus.attrib; d = int(pc_stats['Dex'][1])
1870 for b in bonus:
1871 d += int(bonus[b])
1872 bonus = str(d)
1873 if path[step+2] == None: self.data = bonus
1874 else:
1875 weapons = node.find('attacks')
1876 weapons = weapons.findall('weapon')
1877 for child in weapons:
1878 if path[step+2].lower() == child.get('name').lower():
1879 self.data = '<b>Attack: '+bonus_text+'</b> '+child.get('name')+' [1d20+'+bonus+'] ' + 'Damage: ['+child.get('damage')+']'
1880 return
1881 elif pc_stats.has_key(path[step].title()):
1882 if step+1 == depth: self.data = pc_stats[path[step].title()][0] + ' +('+pc_stats[path[step].title()][1]+')'
1883 elif path[step+1].title() == 'Mod': self.data = pc_stats[path[step].title()][1]
1884 elif path[step+1].title() == 'Check': self.data = '<b>'+path[step].title()+' Check:</b> [1d20+'+str(pc_stats[path[step].title()][1])+']'
1885 return
1886