Mercurial > traipse_dev
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 |