mirror of
https://github.com/evennia/evennia.git
synced 2026-03-31 04:57:16 +02:00
Start with final load/save/spawn nodes of menu
This commit is contained in:
parent
952a5a1ee3
commit
a0d34c7223
3 changed files with 179 additions and 79 deletions
|
|
@ -4,6 +4,7 @@ OLC Prototype menu nodes
|
|||
|
||||
"""
|
||||
|
||||
import json
|
||||
from ast import literal_eval
|
||||
from django.conf import settings
|
||||
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
|
||||
|
||||
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):
|
||||
out.append(" Simulating parsing ...")
|
||||
out.append(" Simulating prototype-func parsing ...")
|
||||
err, parsed_value = protlib.protfunc_parser(value, testing=True)
|
||||
if 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(
|
||||
type(parsed_value), parsed_value))
|
||||
else:
|
||||
out.append(" |gNo change.")
|
||||
out.append(" |gNo change when parsed.")
|
||||
|
||||
caller.msg("\n".join(out))
|
||||
|
||||
|
|
@ -185,23 +192,24 @@ def _path_cropper(pythonpath):
|
|||
def node_index(caller):
|
||||
prototype = _get_menu_prototype(caller)
|
||||
|
||||
text = ("|c --- Prototype wizard --- |n\n\n"
|
||||
"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 "
|
||||
"required.\n\n'|wprototype-'-properties|n are not used in the prototype itself but are used "
|
||||
"to organize and list prototypes. The 'prototype-key' uniquely identifies the prototype "
|
||||
"and allows you to edit an existing prototype or save a new one for use by you or "
|
||||
"others later.\n\n(make choice; q to abort. If unsure, start from 1.)")
|
||||
text = (
|
||||
"|c --- Prototype wizard --- |n\n\n"
|
||||
"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 "
|
||||
"required.\n\n'|wprototype-'-properties|n are not used in the prototype itself but are used "
|
||||
"to organize and list prototypes. The 'prototype-key' uniquely identifies the prototype "
|
||||
"and allows you to edit an existing prototype or save a new one for use by you or "
|
||||
"others later.\n\n(make choice; q to abort. If unsure, start from 1.)")
|
||||
|
||||
options = []
|
||||
options.append(
|
||||
{"desc": "|WPrototype-Key|n|n{}".format(_format_option_value("Key", True, prototype, None)),
|
||||
"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'):
|
||||
required = False
|
||||
cropper = None
|
||||
if key in ("Prototype", "Typeclass"):
|
||||
if key in ("Prototype-parent", "Typeclass"):
|
||||
required = "prototype" not in prototype and "typeclass" not in prototype
|
||||
if key == 'Typeclass':
|
||||
cropper = _path_cropper
|
||||
|
|
@ -215,6 +223,12 @@ def node_index(caller):
|
|||
{"desc": "|WPrototype-{}|n|n{}".format(
|
||||
key, _format_option_value(key, required, prototype, None)),
|
||||
"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
|
||||
|
||||
|
|
@ -429,54 +443,82 @@ def _caller_attrs(caller):
|
|||
return attrs
|
||||
|
||||
|
||||
def _attrparse(caller, attr_string):
|
||||
"attr is entering on the form 'attr = value'"
|
||||
def _display_attribute(attr_tuple):
|
||||
"""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:
|
||||
attrname, value = (part.strip() for part in attr_string.split('=', 1))
|
||||
attrname = attrname.lower()
|
||||
if attrname:
|
||||
try:
|
||||
value = literal_eval(value)
|
||||
except SyntaxError:
|
||||
caller.msg(_MENU_ATTR_LITERAL_EVAL_ERROR)
|
||||
else:
|
||||
return attrname, value
|
||||
else:
|
||||
return None, None
|
||||
nameparts = attrname.split(";", 2)
|
||||
nparts = len(nameparts)
|
||||
if nparts == 2:
|
||||
attrname, category = nameparts
|
||||
elif nparts > 2:
|
||||
attrname, category, locks = nameparts
|
||||
attr_tuple = (attrname, category, locks)
|
||||
|
||||
|
||||
def _add_attr(caller, attr_string, **kwargs):
|
||||
attrname, value = _attrparse(caller, attr_string)
|
||||
if attrname:
|
||||
prot = _get_menu_prototype(caller)
|
||||
prot['attrs'][attrname] = value
|
||||
_set_prototype_value(caller, "prototype", prot)
|
||||
text = "Added"
|
||||
attrs = prot.get('attrs', [])
|
||||
|
||||
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:
|
||||
text = "Added " + _display_attribute(attr_tuple)
|
||||
else:
|
||||
text = "Attribute must be given as 'attrname = <value>' where <value> uses valid Python."
|
||||
text = "Attribute must be given as 'attrname[;category;locks] = <value>'."
|
||||
|
||||
options = {"key": "_default",
|
||||
"goto": lambda caller: None}
|
||||
return text, options
|
||||
|
||||
|
||||
def _edit_attr(caller, attrname, new_value, **kwargs):
|
||||
attrname, value = _attrparse("{}={}".format(caller, attrname, new_value))
|
||||
if attrname:
|
||||
prot = _get_menu_prototype(caller)
|
||||
prot['attrs'][attrname] = value
|
||||
text = "Edited Attribute {} = {}".format(attrname, value)
|
||||
else:
|
||||
text = "Attribute value must be valid Python."
|
||||
options = {"key": "_default",
|
||||
"goto": lambda caller: None}
|
||||
return text, options
|
||||
|
||||
attr_string = "{}={}".format(attrname, new_value)
|
||||
|
||||
return _add_attr(caller, attr_string, edit=True)
|
||||
|
||||
|
||||
def _examine_attr(caller, selection):
|
||||
prot = _get_menu_prototype(caller)
|
||||
value = prot['attrs'][selection]
|
||||
return "Attribute {} = {}".format(selection, value)
|
||||
attr_tuple = prot['attrs'][selection]
|
||||
return _display_attribute(attr_tuple)
|
||||
|
||||
|
||||
@list_node(_caller_attrs)
|
||||
|
|
@ -484,8 +526,12 @@ def node_attrs(caller):
|
|||
prot = _get_menu_prototype(caller)
|
||||
attrs = prot.get("attrs")
|
||||
|
||||
text = ["Set the prototype's |yAttributes|n. Separate multiple attrs with commas. "
|
||||
"Will retain case sensitivity."]
|
||||
text = ["Set the prototype's |yAttributes|n. Enter attributes on one of these forms:\n"
|
||||
" 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:
|
||||
text.append("Current attrs are '|y{attrs}|n'.".format(attrs=attrs))
|
||||
else:
|
||||
|
|
@ -506,46 +552,78 @@ def _caller_tags(caller):
|
|||
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):
|
||||
"""
|
||||
Add tags to the system, parsing this syntax:
|
||||
tagname
|
||||
tagname;category
|
||||
tagname;category;data
|
||||
|
||||
"""
|
||||
|
||||
tag = tag.strip().lower()
|
||||
prototype = _get_menu_prototype(caller)
|
||||
tags = prototype.get('tags', [])
|
||||
if tags:
|
||||
if tag not in tags:
|
||||
tags.append(tag)
|
||||
category = None
|
||||
data = ""
|
||||
|
||||
tagtuple = tag.split(";", 2)
|
||||
ntuple = len(tagtuple)
|
||||
|
||||
if ntuple == 2:
|
||||
tag, category = tagtuple
|
||||
elif ntuple > 2:
|
||||
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 'edit' in kwargs:
|
||||
text = "Edited " + _display_tag(tag_tuple)
|
||||
else:
|
||||
text = "Added " + _display_tag(tag_tuple)
|
||||
else:
|
||||
tags = [tag]
|
||||
prototype['tags'] = tags
|
||||
_set_prototype_value(caller, "prototype", prototype)
|
||||
text = kwargs.get("text")
|
||||
if not text:
|
||||
text = "Added tag {}. (return to continue)".format(tag)
|
||||
text = "Tag must be given as 'tag[;category;data]."
|
||||
|
||||
options = {"key": "_default",
|
||||
"goto": lambda caller: None}
|
||||
return text, options
|
||||
|
||||
|
||||
def _edit_tag(caller, old_tag, new_tag, **kwargs):
|
||||
prototype = _get_menu_prototype(caller)
|
||||
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
|
||||
return _add_tag(caller, new_tag, edit=old_tag)
|
||||
|
||||
|
||||
@list_node(_caller_tags)
|
||||
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")
|
||||
return text, options
|
||||
|
||||
|
|
@ -650,7 +728,7 @@ def node_destination(caller):
|
|||
def node_prototype_desc(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)
|
||||
|
||||
if desc:
|
||||
|
|
@ -670,7 +748,7 @@ def node_prototype_desc(caller):
|
|||
|
||||
def node_prototype_tags(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."]
|
||||
tags = prototype.get('prototype_tags', [])
|
||||
|
||||
|
|
@ -691,15 +769,15 @@ def node_prototype_tags(caller):
|
|||
|
||||
def node_prototype_locks(caller):
|
||||
prototype = _get_menu_prototype(caller)
|
||||
text = ["Set |wMeta-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"
|
||||
"(If you are unsure, leave as default.)"]
|
||||
text = ["Set |wPrototype-Locks|n on the prototype. There are two valid lock types: "
|
||||
"'edit' (who can edit the prototype) and 'spawn' (who can spawn new objects with this "
|
||||
"prototype)\n(If you are unsure, leave as default.)"]
|
||||
locks = prototype.get('prototype_locks', '')
|
||||
if locks:
|
||||
text.append("Current lock is |w'{lockstring}'|n".format(lockstring=locks))
|
||||
else:
|
||||
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)
|
||||
options = _wizard_options("prototype_locks", "prototype_tags", "index")
|
||||
options.append({"key": "_default",
|
||||
|
|
@ -710,6 +788,21 @@ def node_prototype_locks(caller):
|
|||
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):
|
||||
"""
|
||||
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_tags": node_prototype_tags,
|
||||
"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)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@ from evennia.utils.evtable import EvTable
|
|||
|
||||
_MODULE_PROTOTYPE_MODULES = {}
|
||||
_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_META_CATEGORY = "db_prototype"
|
||||
_PROT_FUNCS = {}
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ Possible keywords are:
|
|||
supported are 'edit' and 'use'.
|
||||
prototype_tags(list, optional): List of tags or tuples (tag, category) used to group prototype
|
||||
in listings
|
||||
|
||||
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.
|
||||
prototype: Deprecated. Same meaning as 'parent'.
|
||||
|
||||
typeclass (str or callable, optional): if not set, will use typeclass of parent prototype or use
|
||||
`settings.BASE_OBJECT_TYPECLASS`
|
||||
key (str or callable, optional): the name of the spawned object. If not given this will set to a
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue