diff --git a/evennia/accounts/manager.py b/evennia/accounts/manager.py index c612cf930d..5d9bda2ab9 100644 --- a/evennia/accounts/manager.py +++ b/evennia/accounts/manager.py @@ -35,7 +35,6 @@ class AccountDBManager(TypedObjectManager, UserManager): get_account_from_uid get_account_from_name account_search (equivalent to evennia.search_account) - #swap_character """ diff --git a/evennia/game_template/typeclasses/accounts.py b/evennia/game_template/typeclasses/accounts.py index bbab3d4f22..99d861bf0b 100644 --- a/evennia/game_template/typeclasses/accounts.py +++ b/evennia/game_template/typeclasses/accounts.py @@ -65,7 +65,6 @@ class Account(DefaultAccount): * Helper methods msg(text=None, **kwargs) - swap_character(new_character, delete_old_character=False) execute_cmd(raw_string, session=None) search(ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, account=False) is_typeclass(typeclass, exact=False) diff --git a/evennia/prototypes/menus.py b/evennia/prototypes/menus.py index 37911d7010..8e88cad13c 100644 --- a/evennia/prototypes/menus.py +++ b/evennia/prototypes/menus.py @@ -57,6 +57,18 @@ def _get_flat_menu_prototype(caller, refresh=False, validate=False): return flat_prototype +def _get_unchanged_inherited(caller, protname): + """Return prototype values inherited from parent(s), which are not replaced in child""" + protototype = _get_menu_prototype(caller) + if protname in prototype: + return protname[protname], False + else: + flattened = _get_flat_menu_prototype(caller) + if protname in flattened: + return protname[protname], True + return None, False + + def _set_menu_prototype(caller, prototype): """Set the prototype with existing one""" caller.ndb._menutree.olc_prototype = prototype @@ -515,11 +527,11 @@ def node_index(caller): |c --- Prototype wizard --- |n A |cprototype|n is a 'template' for |wspawning|n an in-game entity. A field of the prototype - can be hard-coded or scripted using |w$protfuncs|n - for example to randomize the value - every time the prototype is used to spawn a new entity. + can either be hard-coded, left empty or scripted using |w$protfuncs|n - for example to + randomize the value every time a new entity is spawned. The fields whose names start with + 'Prototype-' are not fields on the object itself but are used for prototype-inheritance, or + when saving and loading. - The prototype fields whose names start with 'Prototype-' are not fields on the object itself - but are used in the template and when saving it for you (and maybe others) to use later. Select prototype field to edit. If you are unsure, start from [|w1|n]. Enter [|wh|n]elp at any menu node for more info. @@ -544,8 +556,8 @@ def node_index(caller): |c- $protfuncs |n Prototype-functions (protfuncs) allow for limited scripting within a prototype. These are - entered as a string $funcname(arg, arg, ...) and are evaluated |wat the time of spawning|n only. - They can also be nested for combined effects. + entered as a string $funcname(arg, arg, ...) and are evaluated |wat the time of spawning|n + only. They can also be nested for combined effects. {pfuncs} """.format(pfuncs=_format_protfuncs()) @@ -951,7 +963,10 @@ def node_aliases(caller): case sensitive. {actions} - """.format(actions=_format_list_actions("remove", prefix="|w|W to add new alias. Other action: ")) + {current} + """.format(actions=_format_list_actions("remove", + prefix="|w|W to add new alias. Other action: "), + current) helptext = """ Aliases are fixed alternative identifiers and are stored with the new object. diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 3d07a82e9a..60d5c160d6 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -7,6 +7,7 @@ be of use when designing your own game. """ from __future__ import division, print_function +import itertools from builtins import object, range from future.utils import viewkeys, raise_ @@ -33,6 +34,7 @@ _MULTIMATCH_TEMPLATE = settings.SEARCH_MULTIMATCH_TEMPLATE _EVENNIA_DIR = settings.EVENNIA_DIR _GAME_DIR = settings.GAME_DIR + try: import cPickle as pickle except ImportError: @@ -210,18 +212,27 @@ def justify(text, width=None, align="f", indent=0): gap = " " # minimum gap between words if line_rest > 0: if align == 'l': - line[-1] += " " * line_rest + if line[-1] == "\n\n": + line[-1] = " " * (line_rest-1) + "\n" + " " * width + "\n" + " " * width + else: + line[-1] += " " * line_rest elif align == 'r': line[0] = " " * line_rest + line[0] elif align == 'c': pad = " " * (line_rest // 2) line[0] = pad + line[0] - line[-1] = line[-1] + pad + " " * (line_rest % 2) + if line[-1] == "\n\n": + line[-1] = line[-1] + pad + " " * (line_rest % 2) + else: + line[-1] = pad + " " * (line_rest % 2 - 1) + \ + "\n" + " " * width + "\n" + " " * width else: # align 'f' gap += " " * (line_rest // max(1, ngaps)) rest_gap = line_rest % max(1, ngaps) for i in range(rest_gap): line[i] += " " + elif not any(line): + return [" " * width] return gap.join(line) # split into paragraphs and words @@ -262,6 +273,62 @@ def justify(text, width=None, align="f", indent=0): return "\n".join([indentstring + line for line in lines]) +def columnize(string, columns=2, spacing=4, align='l', width=None): + """ + Break a string into a number of columns, using as little + vertical space as possible. + + Args: + string (str): The string to columnize. + columns (int, optional): The number of columns to use. + spacing (int, optional): How much space to have between columns. + width (int, optional): The max width of the columns. + Defaults to client's default width. + + Returns: + columns (str): Text divided into columns. + + Raises: + RuntimeError: If given invalid values. + + """ + columns = max(1, columns) + spacing = max(1, spacing) + width = width if width else settings.CLIENT_DEFAULT_WIDTH + + w_spaces = (columns - 1) * spacing + w_txt = max(1, width - w_spaces) + + if w_spaces + columns > width: # require at least 1 char per column + raise RuntimeError("Width too small to fit columns") + + colwidth = int(w_txt / (1.0 * columns)) + + # first make a single column which we then split + onecol = justify(string, width=colwidth, align=align) + onecol = onecol.split("\n") + + nrows, dangling = divmod(len(onecol), columns) + nrows = [nrows + 1 if i < dangling else nrows for i in range(columns)] + + height = max(nrows) + cols = [] + istart = 0 + for irows in nrows: + cols.append(onecol[istart:istart+irows]) + istart = istart + irows + for col in cols: + if len(col) < height: + col.append(" " * colwidth) + + sep = " " * spacing + rows = [] + for irow in range(height): + rows.append(sep.join(col[irow] for col in cols)) + + return "\n".join(rows) + + def list_to_string(inlist, endsep="and", addquote=False): """ This pretty-formats a list as string output, adding an optional @@ -1548,6 +1615,7 @@ def format_table(table, extra_space=1): Examples: ```python + ftable = format_table([[...], [...], ...]) for ir, row in enumarate(ftable): if ir == 0: # make first row white