From 72ec7377b946be7196c2cbff3d1a788efd84ee43 Mon Sep 17 00:00:00 2001 From: Griatch Date: Fri, 28 Aug 2009 19:07:25 +0000 Subject: [PATCH] Added some changes to revision conttrol. --- game/gamesrc/commands/examples/line_editor.py | 389 ++++++++++++++++++ game/gamesrc/commands/examples/nested_menu.py | 211 ++++++++++ 2 files changed, 600 insertions(+) create mode 100644 game/gamesrc/commands/examples/line_editor.py create mode 100644 game/gamesrc/commands/examples/nested_menu.py diff --git a/game/gamesrc/commands/examples/line_editor.py b/game/gamesrc/commands/examples/line_editor.py new file mode 100644 index 0000000000..139c2a0a43 --- /dev/null +++ b/game/gamesrc/commands/examples/line_editor.py @@ -0,0 +1,389 @@ +""" +This is an example in-game line editor. It uses the state system of Evennia (see +gamesrc/examples/state_example.py). + +Usage: + @edit object=attributename + +The editor is invoked on an object's attribute and allows some more editing +facilities over just writing everything in one go. Check the help for more info. + +If you choose to edit atribute-lists instead of simple strings, the editor adapts +by switching to line-mode. +""" + +from src.cmdtable import GLOBAL_CMD_TABLE +from src.statetable import GLOBAL_STATE_TABLE +from src.objects.models import Object, Attribute +import src.defines_global + +STATENAME = 'line_editor' + +#editor variables and settings + +BUF = '_EDITOR_EDITORBUF' +LINEMODE = '_EDITOR_LINEMODE' +NOUPDATE = '_EDITOR_NOUPDATE' +EDIT_ATTR = '_EDITOR_EDIT_ATTR' + +def cmd_edit(command): + """ + Edit an attribute using the editor. + + Usage: + @edit[/switches] [object=]attribute + + switches: + clear - if the attribute already had a value, clear it before + entering the editor. + line - work and save in line mode + noup - only updates the buffer when explicitly + listing it (avoid large screen-redraws if there's + a lot of text) + + If no object is given, attribute is assumed to be defined on the + caller. + + The editor supports modifying, replacing and inserting text into an attribute + in various ways, as well as cancelling your changes. + + Technically, the 'line' mode of the editor works and saves lines in a list-attribute. + This mode supports more formatting operations than just working on a string. When + working on a normal string (string mode) the editor has no concept of lines, + but new text is added and inserted into the string. + + See the help text in the editor for more information. + """ + + source_object = command.source_object + args = command.command_argument + switches = command.command_switches + + if not args: + source_object.emit_to("You must specify an attribute to @edit.") + return + args = args.strip() + if '=' not in args: + args = "me=" + args + + objname, attr = args.split('=',1) + obj = validate_obj(source_object, objname, attr) + if not obj: + return + + source_object.set_attribute(LINEMODE, 'line' in switches) + if not 'clear' in switches: + curr_value = obj.get_attribute_value(attr.strip()) + if type(curr_value) == type(list()): + source_object.set_attribute(LINEMODE,True) + source_object.set_attribute(BUF, curr_value) + source_object.set_attribute(NOUPDATE, 'noup' in switches) + + #setup the editor temporary attributes + source_object.set_attribute(EDIT_ATTR,args) + + #enter edit state + source_object.set_state(STATENAME) + source_object.execute_cmd('list') + +def cmd_list(command): + """ + list the buffer + """ + source_object = command.source_object + + buf = source_object.get_attribute_value(BUF) + + linemode = source_object.get_attribute_value(LINEMODE) + + header = "%s-----------------------------------------%s\n\r" % ('%cy', '%cn') + attr = source_object.get_attribute_value(EDIT_ATTR) + if linemode: + footer = "\n\r%s@edit mode (line) (%s%s%s) (h for help)" % ('%cy','%ch',attr,'%cn%cy') + else: + footer = "\n\r%s@edit mode (str) (%s%s%s) (h for help)" % ('%cy','%ch',attr,'%cn%cy') + if buf: + if type(buf)==type(list()): + s = "" + for i, line in enumerate(buf): + s += "%s%2i|%s %s\n\r" % ('%ch%cw', i+1, '%cn', line) + s = header + s[:-2] + footer + else: + if linemode: + s = "%s%2i|%s %s\n\r" % ('%ch%cw', 1, '%cn', buf) + s = header + s + footer + else: + s = header + buf + footer + else: + s = header + footer + source_object.emit_to(s) + +def cmd_append(command): + """ + Append text to buffer. + """ + source_object = command.source_object + args = command.command_argument + + buf = source_object.get_attribute_value(BUF) + + if not args: + args = " " + + if source_object.get_attribute_value(LINEMODE): + if not buf: + buf = [args] + else: + if type(buf) != type(list()): + buf = [buf] + buf.extend([args]) + else: + if not buf: + buf = "" + elif buf[-1] in '.,;:': + buf += ' ' + buf += args + source_object.set_attribute(BUF, buf) + + if not source_object.get_attribute_value(NOUPDATE): + cmd_list(command) + +def cmd_insert(command): + """ + Insert new line before given line + """ + source_object = command.source_object + args = command.command_argument + buf = source_object.get_attribute_value(BUF) + + if source_object.get_attribute_value(LINEMODE): + if args: + args = args.split(" ",1) + if len(args)>1: + linenum, text = args[0],args[1] + else: + source_object.emit_to("Usage: .i # ") + return + try: + buf.insert(int(linenum)-1, text) + except: + source_object.emit_to("%s'%s' is not a valid line number." % ('%cr',linenum)) + return + source_object.set_attribute(BUF, buf) + + if not source_object.get_attribute_value(NOUPDATE): + cmd_list(command) + else: + source_object.emit_to("%s.i only works in line mode" % '%cr') + +def cmd_clear(command): + """ + clears the buffer + """ + source_object = command.source_object + args = command.command_argument + buf = source_object.get_attribute_value(BUF) + + if source_object.get_attribute_value(LINEMODE): + if args: + try: + del buf[int(args)-1] + source_object.set_attribute(BUF,buf) + except: + source_object.emit_to("%sCould not clear line '%s'" % ('%cr',args)) + else: + source_object.set_attribute(BUF,[]) + else: + source_object.set_attribute(BUF,"") + + if not source_object.get_attribute_value(NOUPDATE): + cmd_list(command) + +def cmd_replace(command): + """ + replace all occurences of a text. + """ + source_object = command.source_object + buf = source_object.get_attribute_value(BUF) + if not buf: + return + args = command.command_argument + if not args: + source_object.emit_to("Usage: .r old or .r old=new") + return + + if source_object.get_attribute_value(LINEMODE): + if args[0].isdigit(): + num, args = args.split(" ",1) + if '=' in args: + old, new = args.split('=') + old, new = old, new + else: + old = args + new = "" + try: + buf[int(num)-1] = buf[int(num)-1].replace(old, new) + except: + source_object.emit_to("%s%s is not a valid line number." % ('%cr',num)) + return + else: + if '=' in args: + old, new = args.split('=') + old, new = old, new + else: + old = args + new = "" + newbuf = [] + for line in buf: + newbuf.append(line.replace(old,new)) + buf = newbuf + else: + if '=' in args: + old, new = args.split('=') + old, new = old, new + else: + old = args + new = "" + buf = buf.replace(old, new) + + source_object.set_attribute(BUF, buf) + + if not source_object.get_attribute_value(NOUPDATE): + cmd_list(command) + +def cmd_save(command): + source_object = command.source_object + + buf = source_object.get_attribute_value(BUF) + if not buf: buf = "" + + attr = source_object.get_attribute_value(EDIT_ATTR) + objname, attr = attr.split('=',1) + + obj = validate_obj(source_object, objname, attr) + if not obj: + cmd_exit(command) + else: + obj.set_attribute(attr, buf) + source_object.emit_to("%s%s=%s saved." % ('%cg',objname,attr)) + +def cmd_save_exit(command): + cmd_save(command) + util_exit(command) + +def cmd_exit(command): + "exits without saving." + source_object = command.source_object + attr = source_object.get_attribute_value(EDIT_ATTR) + source_object.emit_to('%s%s not saved/changed.' % ('%cr', attr)) + util_exit(command) + +def util_exit(command): + "cleans up and exits" + source_object = command.source_object + source_object.clear_attribute(BUF) + source_object.clear_attribute(LINEMODE) + source_object.clear_attribute(NOUPDATE) + source_object.clear_attribute(EDIT_ATTR) + + source_object.clear_state() + source_object.execute_cmd('look') + +def cmd_help(command): + """ + A custom help screen. + """ + source_object = command.source_object + buf = source_object.get_attribute_value(BUF) + + if source_object.get_attribute_value(LINEMODE): + s = \ + """ + %sEditor commands: + . - enter text into the buffer + .i # - insert new line before line # + .c - clear buffer + .c # - clear line # + .r - remove all matching + .r = - replace all with + .r # - replace line # with + .r # = - replace with on line # + .s - save buffer + .xs, .sx - save and exit + .x, .q - exit without saving + .l, l, list - list buffer + .h, h, help - this help + """ + else: + s = \ + """ + %sEditor commands: + . - enter text into the buffer + .c - clear buffer + .r - remove all matching + .r = - replace all with + .s - save buffer + .xs, .sx - save and exit + .x, .q - exit without saving + .l, l, list - list buffer + .h, h, help - this help + """ + source_object.emit_to(s % '%cy') + +# +# Helper function +# +def validate_obj(source_object, objname, attr): + "Helper function" + #test that object exists. + results = Object.objects.local_and_global_search(source_object, objname) + + if not results: + source_object.emit_to("No name matches found for %s." % (objname,)) + return None + if len(results) > 1: + source_object.emit_to("Multiple name matches for: %s" % (objname,)) + s = "" + for result in results: + s += " %s\n\r" % (result.get_name(fullname=False),) + s += "%d matches returned." % (len(results),) + source_object.emit_to(s) + return None + obj = results[0] + + #permission checks + if not source_object.controls_other(obj): + source_object.emit_to(defines_global.NOCONTROL_MSG) + return None + if not Attribute.objects.is_modifiable_attrib(attr): + source_object.emit_to("You can't modify that attribute.") + return None + + return obj + + +#editor entry command +GLOBAL_CMD_TABLE.add_command("@edit", cmd_edit, auto_help=True) + +#create the state +GLOBAL_STATE_TABLE.add_state(STATENAME) + +#editor commands +GLOBAL_STATE_TABLE.add_command(STATENAME, ".", cmd_append) +GLOBAL_STATE_TABLE.add_command(STATENAME, ".i", cmd_insert) +GLOBAL_STATE_TABLE.add_command(STATENAME, ".r", cmd_replace) +GLOBAL_STATE_TABLE.add_command(STATENAME, ".c", cmd_clear) +GLOBAL_STATE_TABLE.add_command(STATENAME, ".s", cmd_save) +GLOBAL_STATE_TABLE.add_command(STATENAME, ".xs", cmd_save_exit) +GLOBAL_STATE_TABLE.add_command(STATENAME, ".sx", cmd_save_exit) +GLOBAL_STATE_TABLE.add_command(STATENAME, ".x", cmd_exit) +GLOBAL_STATE_TABLE.add_command(STATENAME, ".q", cmd_exit) +GLOBAL_STATE_TABLE.add_command(STATENAME, ".l", cmd_list) +GLOBAL_STATE_TABLE.add_command(STATENAME, "l", cmd_list) +GLOBAL_STATE_TABLE.add_command(STATENAME, "list", cmd_list) +GLOBAL_STATE_TABLE.add_command(STATENAME, ".h", cmd_help) +GLOBAL_STATE_TABLE.add_command(STATENAME, "h", cmd_help) +GLOBAL_STATE_TABLE.add_command(STATENAME, "help", cmd_help) + + + diff --git a/game/gamesrc/commands/examples/nested_menu.py b/game/gamesrc/commands/examples/nested_menu.py new file mode 100644 index 0000000000..8fe51fcf9f --- /dev/null +++ b/game/gamesrc/commands/examples/nested_menu.py @@ -0,0 +1,211 @@ +""" +A more sophisticated nested menu module. This could easily be adapted for anything + from character creation to npc quest dialogues. + +Default functionality: + - Multiple-choice options, numbered 1-10 + - Any level of option nesting and structure + - Run arbitrary command in each menu node (character attributes, anybody?) + - Menu history; allows you to step back all the way to the beginning. + - Exit function + +Note that this makes full use of expanded attributes, by saving a list of objects +in a temporary attribute. So if you haven't updated/reinitialized the database +after revision 626 you should do that or this menu will not work. + +This can be used as a template for any menu. Copy this to a new module (activate in +settings.py if necessary), then change STATENAME and adjust the cmd_update() to look +the way you want. Decide if you want back-stepping functionality and be able to +exit the menu at any time (deactivate those commands if so). Finally define the menu +itself by interconnecting Node objects. +""" + +from src.cmdtable import GLOBAL_CMD_TABLE +from src.statetable import GLOBAL_STATE_TABLE + +STATENAME = 'nestedmenu' + +#temporary attribute +NODELIST = '_menu_node_history' + +# +# Defining the menu tree +# + +class Node(object): + """ + This holds one single point in the menu tree. + """ + def __init__(self,header, text="", func=None): + self.header = header + self.text = text + self.func = func + self.choices = [] +# +# functions to be run at particular nodes +# +def node_func(command): + "function to be called at a node" + source_object = command.source_object + source_object.emit_to("This is the node function being called!") +def endmenu(command): + "Exit the menu." + cmd_exit_menu(command) + +# +# The menu tree +# + +#available nodes +START = Node("Start menu", text="Welcome to the menu.") +secondnode = Node("Second menu") +thirdnode = Node("Third menu") +fourthnode = Node("Fourth menu", text="This is an extra text.") +fifthnode = Node("Fifth menu", func=node_func) +endnode = Node("Endpoint", text="Goodbye.", func=endmenu) + +#linking the nodes together +START.choices = [secondnode,thirdnode] +secondnode.choices = [fourthnode, fifthnode] +thirdnode.choices = [secondnode, fifthnode] +fourthnode.choices = [fifthnode, thirdnode] +fifthnode.choices = [START, endnode] + +# +# Menu mechanics +# + +# Menu entry command +def cmd_entermenu(command): + "entry command" + source_object = command.source_object + source_object.set_state(STATENAME) + #assumes a node START has been defined as the initial one. + source_object.set_attribute(NODELIST, [START]) + cmd_update(command) + +def cmd_update(command): + """Runs the current node in the tree and displays + its contents.""" + source_object = command.source_object + node = source_object.get_attribute_value(NODELIST)[-1] + + s = "\n\r%s%s\n\r%s" % ('%ch%cw',node.header,'%cn') + if node.text: + s += node.text + '\n\r' + + if node.choices: + for i,ch in enumerate(node.choices): + s += " %s%i %s%s\n\r" % ('%ch%cw', i+1, '%cn%cy', ch.header) + s += " (back)" + source_object.emit_to(s) + + if callable(node.func): + node.func(command) + +def cmd_exit_menu(command): + "helper command" + source_object = command.source_object + source_object.clear_attribute(NODELIST) + source_object.clear_state() + source_object.execute_cmd('look') + +def cmd_prev(command): + source_object = command.source_object + prevlist = source_object.get_attribute_value(NODELIST) + exiting = False + try: + prevlist.pop() + if not prevlist: exiting = True + except IndexError: + exiting = True + if exiting: + cmd_exit_menu(command) + return + source_object.set_attribute(NODELIST,prevlist) + cmd_update(command) + +def cmd_help(command): + source_object = command.source_object + s = \ + """ + %sMenu help + %s1-10 - Select an option + l,look - redraw the menu + b,back - back to previous + exit - leave menu""" % ('%ch%cw', '%cn%cy') + source_object.emit_to(s) + +def option(function): + "Option Decorator" + def retfunc(command): + val = function() + source_object = command.source_object + nodelist = source_object.get_attribute_value(NODELIST) + choices = nodelist[-1].choices + try: + choicenode = choices[val-1] + except IndexError: + source_object.emit_to("Not a valid option.") + return + nodelist.append(choicenode) + source_object.set_attribute(NODELIST, nodelist) + cmd_update(command) + return retfunc + +@option +def cmd_option1(): + return 1 +@option +def cmd_option2(): + return 2 +@option +def cmd_option3(): + return 3 +@option +def cmd_option4(): + return 4 +@option +def cmd_option5(): + return 5 +@option +def cmd_option6(): + return 6 +@option +def cmd_option7(): + return 7 +@option +def cmd_option8(): + return 8 +@option +def cmd_option9(): + return 9 +@option +def cmd_option10(): + return 10 + +#entry command +GLOBAL_CMD_TABLE.add_command("enter_nested", cmd_entermenu) + +#create the state +GLOBAL_STATE_TABLE.add_state(STATENAME) + +#menu commands +GLOBAL_STATE_TABLE.add_command(STATENAME, '1', cmd_option1) +GLOBAL_STATE_TABLE.add_command(STATENAME, '2', cmd_option2) +GLOBAL_STATE_TABLE.add_command(STATENAME, '3', cmd_option3) +GLOBAL_STATE_TABLE.add_command(STATENAME, '4', cmd_option4) +GLOBAL_STATE_TABLE.add_command(STATENAME, '5', cmd_option5) +GLOBAL_STATE_TABLE.add_command(STATENAME, '6', cmd_option6) +GLOBAL_STATE_TABLE.add_command(STATENAME, '7', cmd_option7) +GLOBAL_STATE_TABLE.add_command(STATENAME, '8', cmd_option8) +GLOBAL_STATE_TABLE.add_command(STATENAME, '9', cmd_option9) +GLOBAL_STATE_TABLE.add_command(STATENAME, '10',cmd_option10) +GLOBAL_STATE_TABLE.add_command(STATENAME, 'l', cmd_update) +GLOBAL_STATE_TABLE.add_command(STATENAME, 'look', cmd_update) +GLOBAL_STATE_TABLE.add_command(STATENAME, 'h', cmd_help) +GLOBAL_STATE_TABLE.add_command(STATENAME, 'help', cmd_help) + +GLOBAL_STATE_TABLE.add_command(STATENAME, 'b', cmd_prev) +GLOBAL_STATE_TABLE.add_command(STATENAME, 'back', cmd_prev) +GLOBAL_STATE_TABLE.add_command(STATENAME, 'exit', cmd_exit_menu)