Start with final load/save/spawn nodes of menu

This commit is contained in:
Griatch 2018-06-27 00:13:19 +02:00
parent 194eb8e42f
commit 01af170eae
3 changed files with 179 additions and 79 deletions

View file

@ -4,6 +4,7 @@ OLC Prototype menu nodes
""" """
import json
from ast import literal_eval from ast import literal_eval
from django.conf import settings from django.conf import settings
from evennia.utils.evmenu import EvMenu, list_node from evennia.utils.evmenu import EvMenu, list_node
@ -132,10 +133,16 @@ def _set_property(caller, raw_string, **kwargs):
caller.ndb._menutree.olc_prototype = prototype caller.ndb._menutree.olc_prototype = prototype
out = [" Set {prop} to {value} ({typ}).".format(prop=prop, value=value, typ=type(value))] try:
# TODO simple way to get rid of the u'' markers in list reprs, remove this when on py3.
repr_value = json.dumps(value)
except Exception:
repr_value = value
out = [" Set {prop} to {value} ({typ}).".format(prop=prop, value=repr_value, typ=type(value))]
if kwargs.get("test_parse", True): if kwargs.get("test_parse", True):
out.append(" Simulating parsing ...") out.append(" Simulating prototype-func parsing ...")
err, parsed_value = protlib.protfunc_parser(value, testing=True) err, parsed_value = protlib.protfunc_parser(value, testing=True)
if err: if err:
out.append(" |yPython `literal_eval` warning: {}|n".format(err)) out.append(" |yPython `literal_eval` warning: {}|n".format(err))
@ -143,7 +150,7 @@ def _set_property(caller, raw_string, **kwargs):
out.append(" |g(Example-)value when parsed ({}):|n {}".format( out.append(" |g(Example-)value when parsed ({}):|n {}".format(
type(parsed_value), parsed_value)) type(parsed_value), parsed_value))
else: else:
out.append(" |gNo change.") out.append(" |gNo change when parsed.")
caller.msg("\n".join(out)) caller.msg("\n".join(out))
@ -185,7 +192,8 @@ def _path_cropper(pythonpath):
def node_index(caller): def node_index(caller):
prototype = _get_menu_prototype(caller) prototype = _get_menu_prototype(caller)
text = ("|c --- Prototype wizard --- |n\n\n" text = (
"|c --- Prototype wizard --- |n\n\n"
"Define the |yproperties|n of the prototype. All prototype values can be " "Define the |yproperties|n of the prototype. All prototype values can be "
"over-ridden at the time of spawning an instance of the prototype, but some are " "over-ridden at the time of spawning an instance of the prototype, but some are "
"required.\n\n'|wprototype-'-properties|n are not used in the prototype itself but are used " "required.\n\n'|wprototype-'-properties|n are not used in the prototype itself but are used "
@ -197,11 +205,11 @@ def node_index(caller):
options.append( options.append(
{"desc": "|WPrototype-Key|n|n{}".format(_format_option_value("Key", True, prototype, None)), {"desc": "|WPrototype-Key|n|n{}".format(_format_option_value("Key", True, prototype, None)),
"goto": "node_prototype_key"}) "goto": "node_prototype_key"})
for key in ('Prototype', 'Typeclass', 'Key', 'Aliases', 'Attrs', 'Tags', 'Locks', for key in ('Typeclass', 'Prototype-parent', 'Key', 'Aliases', 'Attrs', 'Tags', 'Locks',
'Permissions', 'Location', 'Home', 'Destination'): 'Permissions', 'Location', 'Home', 'Destination'):
required = False required = False
cropper = None cropper = None
if key in ("Prototype", "Typeclass"): if key in ("Prototype-parent", "Typeclass"):
required = "prototype" not in prototype and "typeclass" not in prototype required = "prototype" not in prototype and "typeclass" not in prototype
if key == 'Typeclass': if key == 'Typeclass':
cropper = _path_cropper cropper = _path_cropper
@ -215,6 +223,12 @@ def node_index(caller):
{"desc": "|WPrototype-{}|n|n{}".format( {"desc": "|WPrototype-{}|n|n{}".format(
key, _format_option_value(key, required, prototype, None)), key, _format_option_value(key, required, prototype, None)),
"goto": "node_prototype_{}".format(key.lower())}) "goto": "node_prototype_{}".format(key.lower())})
for key in ("Load", "Save", "Spawn"):
options.append(
{"key": ("|w{}|W{}".format(key[0], key[1:]), key[0]),
"desc": "|W{}|n".format(
key, _format_option_value(key, required, prototype, None)),
"goto": "node_prototype_{}".format(key.lower())})
return text, options return text, options
@ -429,54 +443,82 @@ def _caller_attrs(caller):
return attrs return attrs
def _attrparse(caller, attr_string): def _display_attribute(attr_tuple):
"attr is entering on the form 'attr = value'" """Pretty-print attribute tuple"""
attrkey, value, category, locks, default_access = attr_tuple
value = protlib.protfunc_parser(value)
typ = type(value)
out = ("Attribute key: '{attrkey}' (category: {category}, "
"locks: {locks})\n"
"Value (parsed to {typ}): {value}").format(
attrkey=attrkey,
category=category, locks=locks,
typ=typ, value=value)
return out
def _add_attr(caller, attr_string, **kwargs):
"""
Add new attrubute, parsing input.
attr is entered on these forms
attr = value
attr;category = value
attr;category;lockstring = value
"""
attrname = ''
category = None
locks = ''
if '=' in attr_string: if '=' in attr_string:
attrname, value = (part.strip() for part in attr_string.split('=', 1)) attrname, value = (part.strip() for part in attr_string.split('=', 1))
attrname = attrname.lower() attrname = attrname.lower()
if attrname: nameparts = attrname.split(";", 2)
try: nparts = len(nameparts)
value = literal_eval(value) if nparts == 2:
except SyntaxError: attrname, category = nameparts
caller.msg(_MENU_ATTR_LITERAL_EVAL_ERROR) elif nparts > 2:
else: attrname, category, locks = nameparts
return attrname, value attr_tuple = (attrname, category, locks)
else:
return None, None
def _add_attr(caller, attr_string, **kwargs):
attrname, value = _attrparse(caller, attr_string)
if attrname: if attrname:
prot = _get_menu_prototype(caller) prot = _get_menu_prototype(caller)
prot['attrs'][attrname] = value attrs = prot.get('attrs', [])
_set_prototype_value(caller, "prototype", prot)
text = "Added" try:
# replace existing attribute with the same name in the prototype
ind = [tup[0] for tup in attrs].index(attrname)
attrs[ind] = attr_tuple
except IndexError:
attrs.append(attr_tuple)
_set_prototype_value(caller, "attrs", attrs)
text = kwargs.get('text')
if not text:
if 'edit' in kwargs:
text = "Edited " + _display_attribute(attr_tuple)
else: else:
text = "Attribute must be given as 'attrname = <value>' where <value> uses valid Python." text = "Added " + _display_attribute(attr_tuple)
else:
text = "Attribute must be given as 'attrname[;category;locks] = <value>'."
options = {"key": "_default", options = {"key": "_default",
"goto": lambda caller: None} "goto": lambda caller: None}
return text, options return text, options
def _edit_attr(caller, attrname, new_value, **kwargs): def _edit_attr(caller, attrname, new_value, **kwargs):
attrname, value = _attrparse("{}={}".format(caller, attrname, new_value))
if attrname: attr_string = "{}={}".format(attrname, new_value)
prot = _get_menu_prototype(caller)
prot['attrs'][attrname] = value return _add_attr(caller, attr_string, edit=True)
text = "Edited Attribute {} = {}".format(attrname, value)
else:
text = "Attribute value must be valid Python."
options = {"key": "_default",
"goto": lambda caller: None}
return text, options
def _examine_attr(caller, selection): def _examine_attr(caller, selection):
prot = _get_menu_prototype(caller) prot = _get_menu_prototype(caller)
value = prot['attrs'][selection] attr_tuple = prot['attrs'][selection]
return "Attribute {} = {}".format(selection, value) return _display_attribute(attr_tuple)
@list_node(_caller_attrs) @list_node(_caller_attrs)
@ -484,8 +526,12 @@ def node_attrs(caller):
prot = _get_menu_prototype(caller) prot = _get_menu_prototype(caller)
attrs = prot.get("attrs") attrs = prot.get("attrs")
text = ["Set the prototype's |yAttributes|n. Separate multiple attrs with commas. " text = ["Set the prototype's |yAttributes|n. Enter attributes on one of these forms:\n"
"Will retain case sensitivity."] " attrname=value\n attrname;category=value\n attrname;category;lockstring=value\n"
"To give an attribute without a category but with a lockstring, leave that spot empty "
"(attrname;;lockstring=value)."
"Separate multiple attrs with commas. Use quotes to escape inputs with commas and "
"semi-colon."]
if attrs: if attrs:
text.append("Current attrs are '|y{attrs}|n'.".format(attrs=attrs)) text.append("Current attrs are '|y{attrs}|n'.".format(attrs=attrs))
else: else:
@ -506,46 +552,78 @@ def _caller_tags(caller):
return tags return tags
def _display_tag(tag_tuple):
"""Pretty-print attribute tuple"""
tagkey, category, data = tag_tuple
out = ("Tag: '{tagkey}' (category: {category}{})".format(
tagkey=tagkey, category=category, data=", data: {}".format(data) if data else ""))
return out
def _add_tag(caller, tag, **kwargs): def _add_tag(caller, tag, **kwargs):
"""
Add tags to the system, parsing this syntax:
tagname
tagname;category
tagname;category;data
"""
tag = tag.strip().lower() tag = tag.strip().lower()
prototype = _get_menu_prototype(caller) category = None
tags = prototype.get('tags', []) data = ""
if tags:
if tag not in tags: tagtuple = tag.split(";", 2)
tags.append(tag) ntuple = len(tagtuple)
else:
tags = [tag] if ntuple == 2:
prototype['tags'] = tags tag, category = tagtuple
_set_prototype_value(caller, "prototype", prototype) elif ntuple > 2:
text = kwargs.get("text") tag, category, data = tagtuple
tag_tuple = (tag, category, data)
if tag:
prot = _get_menu_prototype(caller)
tags = prot.get('tags', [])
old_tag = kwargs.get("edit", None)
if old_tag:
# editing a tag means removing the old and replacing with new
try:
ind = [tup[0] for tup in tags].index(old_tag)
del tags[ind]
except IndexError:
pass
tags.append(tag_tuple)
_set_prototype_value(caller, "tags", tags)
text = kwargs.get('text')
if not text: if not text:
text = "Added tag {}. (return to continue)".format(tag) if 'edit' in kwargs:
text = "Edited " + _display_tag(tag_tuple)
else:
text = "Added " + _display_tag(tag_tuple)
else:
text = "Tag must be given as 'tag[;category;data]."
options = {"key": "_default", options = {"key": "_default",
"goto": lambda caller: None} "goto": lambda caller: None}
return text, options return text, options
def _edit_tag(caller, old_tag, new_tag, **kwargs): def _edit_tag(caller, old_tag, new_tag, **kwargs):
prototype = _get_menu_prototype(caller) return _add_tag(caller, new_tag, edit=old_tag)
tags = prototype.get('tags', [])
old_tag = old_tag.strip().lower()
new_tag = new_tag.strip().lower()
tags[tags.index(old_tag)] = new_tag
prototype['tags'] = tags
_set_prototype_value(caller, 'prototype', prototype)
text = kwargs.get('text')
if not text:
text = "Changed tag {} to {}.".format(old_tag, new_tag)
options = {"key": "_default",
"goto": lambda caller: None}
return text, options
@list_node(_caller_tags) @list_node(_caller_tags)
def node_tags(caller): def node_tags(caller):
text = "Set the prototype's |yTags|n." text = ("Set the prototype's |yTags|n. Enter tags on one of the following forms:\n"
" tag\n tag;category\n tag;category;data\n"
"Note that 'data' is not commonly used.")
options = _wizard_options("tags", "attrs", "locks") options = _wizard_options("tags", "attrs", "locks")
return text, options return text, options
@ -650,7 +728,7 @@ def node_destination(caller):
def node_prototype_desc(caller): def node_prototype_desc(caller):
prototype = _get_menu_prototype(caller) prototype = _get_menu_prototype(caller)
text = ["The |wMeta-Description|n briefly describes the prototype for viewing in listings."] text = ["The |wPrototype-Description|n briefly describes the prototype for viewing in listings."]
desc = prototype.get("prototype_desc", None) desc = prototype.get("prototype_desc", None)
if desc: if desc:
@ -670,7 +748,7 @@ def node_prototype_desc(caller):
def node_prototype_tags(caller): def node_prototype_tags(caller):
prototype = _get_menu_prototype(caller) prototype = _get_menu_prototype(caller)
text = ["|wMeta-Tags|n can be used to classify and find prototypes. Tags are case-insensitive. " text = ["|wPrototype-Tags|n can be used to classify and find prototypes. Tags are case-insensitive. "
"Separate multiple by tags by commas."] "Separate multiple by tags by commas."]
tags = prototype.get('prototype_tags', []) tags = prototype.get('prototype_tags', [])
@ -691,15 +769,15 @@ def node_prototype_tags(caller):
def node_prototype_locks(caller): def node_prototype_locks(caller):
prototype = _get_menu_prototype(caller) prototype = _get_menu_prototype(caller)
text = ["Set |wMeta-Locks|n on the prototype. There are two valid lock types: " text = ["Set |wPrototype-Locks|n on the prototype. There are two valid lock types: "
"'edit' (who can edit the prototype) and 'use' (who can apply the prototype)\n" "'edit' (who can edit the prototype) and 'spawn' (who can spawn new objects with this "
"(If you are unsure, leave as default.)"] "prototype)\n(If you are unsure, leave as default.)"]
locks = prototype.get('prototype_locks', '') locks = prototype.get('prototype_locks', '')
if locks: if locks:
text.append("Current lock is |w'{lockstring}'|n".format(lockstring=locks)) text.append("Current lock is |w'{lockstring}'|n".format(lockstring=locks))
else: else:
text.append("Lock unset - if not changed the default lockstring will be set as\n" text.append("Lock unset - if not changed the default lockstring will be set as\n"
" |w'use:all(); edit:id({dbref}) or perm(Admin)'|n".format(dbref=caller.id)) " |w'spawn:all(); edit:id({dbref}) or perm(Admin)'|n".format(dbref=caller.id))
text = "\n\n".join(text) text = "\n\n".join(text)
options = _wizard_options("prototype_locks", "prototype_tags", "index") options = _wizard_options("prototype_locks", "prototype_tags", "index")
options.append({"key": "_default", options.append({"key": "_default",
@ -710,6 +788,21 @@ def node_prototype_locks(caller):
return text, options return text, options
def node_prototype_load(caller):
# load prototype from storage
pass
def node_prototype_save(caller):
# save current prototype to disk
pass
def node_prototype_spawn(caller):
# spawn an instance of this prototype
pass
class OLCMenu(EvMenu): class OLCMenu(EvMenu):
""" """
A custom EvMenu with a different formatting for the options. A custom EvMenu with a different formatting for the options.
@ -766,5 +859,8 @@ def start_olc(caller, session=None, prototype=None):
"node_prototype_desc": node_prototype_desc, "node_prototype_desc": node_prototype_desc,
"node_prototype_tags": node_prototype_tags, "node_prototype_tags": node_prototype_tags,
"node_prototype_locks": node_prototype_locks, "node_prototype_locks": node_prototype_locks,
"node_prototype_load": node_prototype_load,
"node_prototype_save": node_prototype_save,
"node_prototype_spawn": node_prototype_spawn
} }
OLCMenu(caller, menudata, startnode='node_index', session=session, olc_prototype=prototype) OLCMenu(caller, menudata, startnode='node_index', session=session, olc_prototype=prototype)

View file

@ -22,7 +22,11 @@ from evennia.utils.evtable import EvTable
_MODULE_PROTOTYPE_MODULES = {} _MODULE_PROTOTYPE_MODULES = {}
_MODULE_PROTOTYPES = {} _MODULE_PROTOTYPES = {}
_PROTOTYPE_META_NAMES = ("prototype_key", "prototype_desc", "prototype_tags", "prototype_locks") _PROTOTYPE_META_NAMES = (
"prototype_key", "prototype_desc", "prototype_tags", "prototype_locks", "prototype_parent")
_PROTOTYPE_RESERVED_KEYS = _PROTOTYPE_META_NAMES + (
"key", "aliases", "typeclass", "location", "home", "destination",
"permissions", "locks", "exec", "tags", "attrs")
_PROTOTYPE_TAG_CATEGORY = "from_prototype" _PROTOTYPE_TAG_CATEGORY = "from_prototype"
_PROTOTYPE_TAG_META_CATEGORY = "db_prototype" _PROTOTYPE_TAG_META_CATEGORY = "db_prototype"
_PROT_FUNCS = {} _PROT_FUNCS = {}

View file

@ -31,10 +31,10 @@ Possible keywords are:
supported are 'edit' and 'use'. supported are 'edit' and 'use'.
prototype_tags(list, optional): List of tags or tuples (tag, category) used to group prototype prototype_tags(list, optional): List of tags or tuples (tag, category) used to group prototype
in listings in listings
prototype_parent (str, tuple or callable, optional): name (prototype_key) of eventual parent prototype, or prototype_parent (str, tuple or callable, optional): name (prototype_key) of eventual parent prototype, or
a list of parents, for multiple left-to-right inheritance. a list of parents, for multiple left-to-right inheritance.
prototype: Deprecated. Same meaning as 'parent'. prototype: Deprecated. Same meaning as 'parent'.
typeclass (str or callable, optional): if not set, will use typeclass of parent prototype or use typeclass (str or callable, optional): if not set, will use typeclass of parent prototype or use
`settings.BASE_OBJECT_TYPECLASS` `settings.BASE_OBJECT_TYPECLASS`
key (str or callable, optional): the name of the spawned object. If not given this will set to a key (str or callable, optional): the name of the spawned object. If not given this will set to a