Refactor spawner menu
This commit is contained in:
parent
ad4b58a6cf
commit
5ed765d664
2 changed files with 171 additions and 91 deletions
|
|
@ -43,13 +43,18 @@ command definition too) with function definitions:
|
||||||
def node_with_other_name(caller, input_string):
|
def node_with_other_name(caller, input_string):
|
||||||
# code
|
# code
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
def another_node(caller, input_string, **kwargs):
|
||||||
|
# code
|
||||||
|
return text, options
|
||||||
```
|
```
|
||||||
|
|
||||||
Where caller is the object using the menu and input_string is the
|
Where caller is the object using the menu and input_string is the
|
||||||
command entered by the user on the *previous* node (the command
|
command entered by the user on the *previous* node (the command
|
||||||
entered to get to this node). The node function code will only be
|
entered to get to this node). The node function code will only be
|
||||||
executed once per node-visit and the system will accept nodes with
|
executed once per node-visit and the system will accept nodes with
|
||||||
both one or two arguments interchangeably.
|
both one or two arguments interchangeably. It also accepts nodes
|
||||||
|
that takes **kwargs.
|
||||||
|
|
||||||
The menu tree itself is available on the caller as
|
The menu tree itself is available on the caller as
|
||||||
`caller.ndb._menutree`. This makes it a convenient place to store
|
`caller.ndb._menutree`. This makes it a convenient place to store
|
||||||
|
|
@ -82,12 +87,14 @@ menu is immediately exited and the default "look" command is called.
|
||||||
the callable. Those kwargs will also be passed into the next node if possible.
|
the callable. Those kwargs will also be passed into the next node if possible.
|
||||||
Such a callable should return either a str or a (str, dict), where the
|
Such a callable should return either a str or a (str, dict), where the
|
||||||
string is the name of the next node to go to and the dict is the new,
|
string is the name of the next node to go to and the dict is the new,
|
||||||
(possibly modified) kwarg to pass into the next node.
|
(possibly modified) kwarg to pass into the next node. If the callable returns
|
||||||
|
None or the empty string, the current node will be revisited.
|
||||||
- `exec` (str, callable or tuple, optional): This takes the same input as `goto` above
|
- `exec` (str, callable or tuple, optional): This takes the same input as `goto` above
|
||||||
and runs before it. If given a node name, the node will be executed but will not
|
and runs before it. If given a node name, the node will be executed but will not
|
||||||
be considered the next node. If node/callback returns str or (str, dict), these will
|
be considered the next node. If node/callback returns str or (str, dict), these will
|
||||||
replace the `goto` step (`goto` callbacks will not fire), with the string being the
|
replace the `goto` step (`goto` callbacks will not fire), with the string being the
|
||||||
next node name and the optional dict acting as the kwargs-input for the next node.
|
next node name and the optional dict acting as the kwargs-input for the next node.
|
||||||
|
If an exec callable returns the empty string (only), the current node is re-run.
|
||||||
|
|
||||||
If key is not given, the option will automatically be identified by
|
If key is not given, the option will automatically be identified by
|
||||||
its number 1..N.
|
its number 1..N.
|
||||||
|
|
@ -669,6 +676,9 @@ class EvMenu(object):
|
||||||
|
|
||||||
if isinstance(ret, basestring):
|
if isinstance(ret, basestring):
|
||||||
# only return a value if a string (a goto target), ignore all other returns
|
# only return a value if a string (a goto target), ignore all other returns
|
||||||
|
if not ret:
|
||||||
|
# an empty string - rerun the same node
|
||||||
|
return self.nodename
|
||||||
return ret, kwargs
|
return ret, kwargs
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
@ -718,6 +728,9 @@ class EvMenu(object):
|
||||||
raise EvMenuError(
|
raise EvMenuError(
|
||||||
"{}: goto callable must return str or (str, dict)".format(inp_nodename))
|
"{}: goto callable must return str or (str, dict)".format(inp_nodename))
|
||||||
nodename, kwargs = nodename[:2]
|
nodename, kwargs = nodename[:2]
|
||||||
|
if not nodename:
|
||||||
|
# no nodename return. Re-run current node
|
||||||
|
nodename = self.nodename
|
||||||
try:
|
try:
|
||||||
# execute the found node, make use of the returns.
|
# execute the found node, make use of the returns.
|
||||||
nodetext, options = self._execute_node(nodename, raw_string, **kwargs)
|
nodetext, options = self._execute_node(nodename, raw_string, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -671,6 +671,10 @@ def _get_menu_metaprot(caller):
|
||||||
return metaproto
|
return metaproto
|
||||||
|
|
||||||
|
|
||||||
|
def _is_new_prototype(caller):
|
||||||
|
return hasattr(caller.ndb._menutree, "olc_new")
|
||||||
|
|
||||||
|
|
||||||
def _set_menu_metaprot(caller, field, value):
|
def _set_menu_metaprot(caller, field, value):
|
||||||
metaprot = _get_menu_metaprot(caller)
|
metaprot = _get_menu_metaprot(caller)
|
||||||
kwargs = dict(metaprot.__dict__)
|
kwargs = dict(metaprot.__dict__)
|
||||||
|
|
@ -678,30 +682,121 @@ def _set_menu_metaprot(caller, field, value):
|
||||||
caller.ndb._menutree.olc_metaprot = build_metaproto(**kwargs)
|
caller.ndb._menutree.olc_metaprot = build_metaproto(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
def node_meta_index(caller):
|
def _format_property(key, required=False, metaprot=None, prototype=None):
|
||||||
metaprot = _get_menu_metaprot(caller)
|
key = key.lower()
|
||||||
key = "|g{}|n".format(
|
if metaprot is not None:
|
||||||
crop(metaprot.key, _MENU_CROP_WIDTH)) if metaprot.key else "|rundefined, required|n"
|
prop = getattr(metaprot, key) or ''
|
||||||
desc = "|g{}|n".format(
|
elif prototype is not None:
|
||||||
crop(metaprot.desc, _MENU_CROP_WIDTH)) if metaprot.desc else "''"
|
prop = prototype.get(key, '')
|
||||||
tags = "|g{}|n".format(
|
|
||||||
crop(", ".join(metaprot.tags), _MENU_CROP_WIDTH)) if metaprot.tags else []
|
out = prop
|
||||||
locks = "|g{}|n".format(
|
if callable(prop):
|
||||||
crop(", ".join(metaprot.locks), _MENU_CROP_WIDTH)) if metaprot.tags else []
|
if hasattr(prop, '__name__'):
|
||||||
prot = "|gdefined|n" if metaprot.prototype else "|rundefined, required|n"
|
out = "<{}>".format(prop.__name__)
|
||||||
|
else:
|
||||||
|
out = repr(prop)
|
||||||
|
if is_iter(prop):
|
||||||
|
out = ", ".join(str(pr) for pr in prop)
|
||||||
|
if not out and required:
|
||||||
|
out = "|rrequired"
|
||||||
|
return " ({}|n)".format(crop(out, _MENU_CROP_WIDTH))
|
||||||
|
|
||||||
|
|
||||||
|
def _set_property(caller, raw_string, **kwargs):
|
||||||
|
"""
|
||||||
|
Update a property. To be called by the 'goto' option variable.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
caller (Object, Account): The user of the wizard.
|
||||||
|
raw_string (str): Input from user on given node - the new value to set.
|
||||||
|
Kwargs:
|
||||||
|
prop (str): Property name to edit with `raw_string`.
|
||||||
|
processor (callable): Converts `raw_string` to a form suitable for saving.
|
||||||
|
next_node (str): Where to redirect to after this has run.
|
||||||
|
Returns:
|
||||||
|
next_node (str): Next node to go to.
|
||||||
|
|
||||||
|
"""
|
||||||
|
prop = kwargs.get("prop", "meta_key")
|
||||||
|
processor = kwargs.get("processor", None)
|
||||||
|
next_node = kwargs.get("next_node", "node_index")
|
||||||
|
|
||||||
|
propname_low = prop.strip().lower()
|
||||||
|
meta = propname_low.startswith("meta_")
|
||||||
|
if meta:
|
||||||
|
propname_low = propname_low[5:]
|
||||||
|
raw_string = raw_string.strip()
|
||||||
|
|
||||||
|
if callable(processor):
|
||||||
|
try:
|
||||||
|
value = processor(raw_string)
|
||||||
|
except Exception as err:
|
||||||
|
caller.msg("Could not set {prop} to {value} ({err})".format(
|
||||||
|
prop=prop.replace("_", "-").capitalize(), value=raw_string, err=str(err)))
|
||||||
|
# this means we'll re-run the current node.
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
value = raw_string
|
||||||
|
|
||||||
|
if meta:
|
||||||
|
_set_menu_metaprot(caller, propname_low, value)
|
||||||
|
else:
|
||||||
|
metaprot = _get_menu_metaprot(caller)
|
||||||
|
prototype = metaprot.prototype
|
||||||
|
prototype[propname_low] = value
|
||||||
|
_set_menu_metaprot(caller, "prototype", prototype)
|
||||||
|
|
||||||
|
caller.msg("Set {prop} to {value}.".format(
|
||||||
|
prop=prop.replace("_", "-").capitalize(), value=str(value)))
|
||||||
|
|
||||||
|
return next_node
|
||||||
|
|
||||||
|
|
||||||
|
def _wizard_options(prev_node, next_node):
|
||||||
|
options = [{"desc": "forward ({})".format(next_node.replace("_", "-")),
|
||||||
|
"goto": "node_{}".format(next_node)},
|
||||||
|
{"desc": "back ({})".format(prev_node.replace("_", "-")),
|
||||||
|
"goto": "node_{}".format(prev_node)}]
|
||||||
|
if "index" not in (prev_node, next_node):
|
||||||
|
options.append({"desc": "index",
|
||||||
|
"goto": "node_index"})
|
||||||
|
return options
|
||||||
|
|
||||||
|
|
||||||
|
def node_index(caller):
|
||||||
|
metaprot = _get_menu_metaprot(caller)
|
||||||
|
prototype = metaprot.prototype
|
||||||
|
|
||||||
|
text = ("|c --- Prototype wizard --- |n\n\n"
|
||||||
|
"Define properties 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"
|
||||||
|
"'Meta'-properties are not used in the prototype itself but are used to organize and "
|
||||||
|
"list prototypes. The 'Meta-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 = []
|
||||||
|
# The meta-key goes first
|
||||||
|
options.append(
|
||||||
|
{"desc": "|WMeta-Key|n|n{}".format(_format_property("Key", True, metaprot, None)),
|
||||||
|
"goto": "node_meta_key"})
|
||||||
|
for key in ('Prototype', 'Typeclass', 'Key', 'Aliases', 'Home', 'Destination',
|
||||||
|
'Permissions', 'Locks', 'Location', 'Tags', 'Attrs'):
|
||||||
|
req = False
|
||||||
|
if key in ("Prototype", "Typeclass"):
|
||||||
|
req = "prototype" not in prototype and "typeclass" not in prototype
|
||||||
|
options.append(
|
||||||
|
{"desc": "|w{}|n{}".format(key, _format_property(key, req, None, prototype)),
|
||||||
|
"goto": "node_{}".format(key.lower())})
|
||||||
|
for key in ('Desc', 'Tags', 'Locks'):
|
||||||
|
options.append(
|
||||||
|
{"desc": "|WMeta-{}|n|n{}".format(key, _format_property(key, req, metaprot, None)),
|
||||||
|
"goto": "node_meta_{}".format(key.lower())})
|
||||||
|
|
||||||
text = ("|c --- Prototype wizard --- |n\n"
|
|
||||||
"(make choice; q to abort, h for help)")
|
|
||||||
options = (
|
|
||||||
{"desc": "Key ({})".format(key), "goto": "node_meta_key"},
|
|
||||||
{"desc": "Description ({})".format(desc), "goto": "node_meta_desc"},
|
|
||||||
{"desc": "Tags ({})".format(tags), "goto": "node_meta_tags"},
|
|
||||||
{"desc": "Locks ({})".format(locks), "goto": "node_meta_locks"},
|
|
||||||
{"desc": "Prototype[menu] ({})".format(prot), "goto": "node_prototype_index"})
|
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
def _node_check_key(caller, key):
|
def _check_meta_key(caller, key):
|
||||||
old_metaprot = search_prototype(key)
|
old_metaprot = search_prototype(key)
|
||||||
olc_new = caller.ndb._menutree.olc_new
|
olc_new = caller.ndb._menutree.olc_new
|
||||||
key = key.strip().lower()
|
key = key.strip().lower()
|
||||||
|
|
@ -719,15 +814,12 @@ def _node_check_key(caller, key):
|
||||||
caller.msg("Prototype already exists. Reloading.")
|
caller.msg("Prototype already exists. Reloading.")
|
||||||
return "node_meta_index"
|
return "node_meta_index"
|
||||||
|
|
||||||
# continue on
|
return _set_property(caller, key, prop='meta_key', next_node="node_meta_desc")
|
||||||
_set_menu_metaprot(caller, 'key', key)
|
|
||||||
caller.msg("Key '{key}' was set.".format(key=key))
|
|
||||||
return "node_meta_desc"
|
|
||||||
|
|
||||||
|
|
||||||
def node_meta_key(caller):
|
def node_meta_key(caller):
|
||||||
metaprot = _get_menu_metaprot(caller)
|
metaprot = _get_menu_metaprot(caller)
|
||||||
text = ["The prototype name, or |ckey|n, uniquely identifies the prototype. "
|
text = ["The prototype name, or |cmeta-key|n, uniquely identifies the prototype. "
|
||||||
"It is used to find and use the prototype to spawn new entities. "
|
"It is used to find and use the prototype to spawn new entities. "
|
||||||
"It is not case sensitive."]
|
"It is not case sensitive."]
|
||||||
old_key = metaprot.key
|
old_key = metaprot.key
|
||||||
|
|
@ -735,25 +827,14 @@ def node_meta_key(caller):
|
||||||
text.append("Current key is '|y{key}|n'".format(key=old_key))
|
text.append("Current key is '|y{key}|n'".format(key=old_key))
|
||||||
else:
|
else:
|
||||||
text.append("The key is currently unset.")
|
text.append("The key is currently unset.")
|
||||||
text.append("Enter text or make a choice (q for quit, h for help)")
|
text.append("Enter text or make a choice (q for quit)")
|
||||||
text = "\n".join(text)
|
text = "\n\n".join(text)
|
||||||
options = ({"desc": "forward (desc)",
|
options = _wizard_options("index", "meta_desc")
|
||||||
"goto": "node_meta_desc"},
|
options.append({"key": "_default",
|
||||||
{"desc": "back (index)",
|
"goto": _check_meta_key})
|
||||||
"goto": "node_meta_index"},
|
|
||||||
{"key": "_default",
|
|
||||||
"desc": "enter a key",
|
|
||||||
"goto": _node_check_key})
|
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
def _node_check_desc(caller, desc):
|
|
||||||
desc = desc.strip()
|
|
||||||
_set_menu_metaprot(caller, 'desc', desc)
|
|
||||||
caller.msg("Description was set to '{desc}'.".format(desc=desc))
|
|
||||||
return "node_meta_tags"
|
|
||||||
|
|
||||||
|
|
||||||
def node_meta_desc(caller):
|
def node_meta_desc(caller):
|
||||||
|
|
||||||
metaprot = _get_menu_metaprot(caller)
|
metaprot = _get_menu_metaprot(caller)
|
||||||
|
|
@ -764,25 +845,14 @@ def node_meta_desc(caller):
|
||||||
text.append("The current meta desc is:\n\"|y{desc}|n\"".format(desc=desc))
|
text.append("The current meta desc is:\n\"|y{desc}|n\"".format(desc=desc))
|
||||||
else:
|
else:
|
||||||
text.append("Description is currently unset.")
|
text.append("Description is currently unset.")
|
||||||
text = "\n".join(text)
|
text = "\n\n".join(text)
|
||||||
options = ({"desc": "forward (tags)",
|
options = _wizard_options("meta_key", "meta_tags")
|
||||||
"goto": "node_meta_tags"},
|
options.append({"key": "_default",
|
||||||
{"desc": "back (key)",
|
"goto": (_set_property,
|
||||||
"goto": "node_meta_key"},
|
dict(prop='meta_desc', next_node="node_meta_tags"))})
|
||||||
{"key": "_default",
|
|
||||||
"desc": "enter a description",
|
|
||||||
"goto": _node_check_desc})
|
|
||||||
|
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
def _node_check_tags(caller, tags):
|
|
||||||
tags = [part.strip().lower() for part in tags.split(",")]
|
|
||||||
_set_menu_metaprot(caller, 'tags', tags)
|
|
||||||
caller.msg("Tags {tags} were set".format(tags=tags))
|
|
||||||
return "node_meta_locks"
|
|
||||||
|
|
||||||
|
|
||||||
def node_meta_tags(caller):
|
def node_meta_tags(caller):
|
||||||
metaprot = _get_menu_metaprot(caller)
|
metaprot = _get_menu_metaprot(caller)
|
||||||
text = ["|cTags|n can be used to classify and find prototypes. Tags are case-insensitive. "
|
text = ["|cTags|n can be used to classify and find prototypes. Tags are case-insensitive. "
|
||||||
|
|
@ -793,24 +863,16 @@ def node_meta_tags(caller):
|
||||||
text.append("The current tags are:\n|y{tags}|n".format(tags=tags))
|
text.append("The current tags are:\n|y{tags}|n".format(tags=tags))
|
||||||
else:
|
else:
|
||||||
text.append("No tags are currently set.")
|
text.append("No tags are currently set.")
|
||||||
text = "\n".join(text)
|
text = "\n\n".join(text)
|
||||||
options = ({"desc": "forward (locks)",
|
options = _wizard_options("meta_desc", "meta_locks")
|
||||||
"goto": "node_meta_locks"},
|
options.append({"key": "_default",
|
||||||
{"desc": "back (desc)",
|
"goto": (_set_property,
|
||||||
"goto": "node_meta_desc"},
|
dict(prop="meta_tags",
|
||||||
{"key": "_default",
|
processor=lambda s: [str(part.strip()) for part in s.split(",")],
|
||||||
"desc": "enter tags separated by commas",
|
next_node="node_meta_locks"))})
|
||||||
"goto": _node_check_tags})
|
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
def _node_check_locks(caller, lockstring):
|
|
||||||
# TODO - have a way to validate lock string here
|
|
||||||
_set_menu_metaprot(caller, 'locks', lockstring)
|
|
||||||
caller.msg("Set lockstring '{lockstring}'.".format(lockstring=lockstring))
|
|
||||||
return "node_prototype_index"
|
|
||||||
|
|
||||||
|
|
||||||
def node_meta_locks(caller):
|
def node_meta_locks(caller):
|
||||||
metaprot = _get_menu_metaprot(caller)
|
metaprot = _get_menu_metaprot(caller)
|
||||||
text = ["Set |ylocks|n on the prototype. There are two valid lock types: "
|
text = ["Set |ylocks|n on the prototype. There are two valid lock types: "
|
||||||
|
|
@ -822,24 +884,30 @@ def node_meta_locks(caller):
|
||||||
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"
|
||||||
" |y'use:all(); edit:id({dbref}) or perm(Admin)'|n".format(dbref=caller.id))
|
" |y'use:all(); edit:id({dbref}) or perm(Admin)'|n".format(dbref=caller.id))
|
||||||
text = "\n".join(text)
|
text = "\n\n".join(text)
|
||||||
options = ({"desc": "forward (prototype)",
|
options = _wizard_options("meta_tags", "prototype")
|
||||||
"goto": "node_prototype_index"},
|
options.append({"key": "_default",
|
||||||
{"desc": "back (tags)",
|
"desc": "enter lockstring",
|
||||||
"goto": "node_meta_tags"},
|
"goto": (_set_property,
|
||||||
{"key": "_default",
|
dict(prop="meta_locks",
|
||||||
"desc": "enter lockstring",
|
next_node="node_key"))})
|
||||||
"goto": _node_check_locks})
|
|
||||||
|
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
def node_prototype_index(caller):
|
def node_key(caller):
|
||||||
metaprot = _get_menu_metaprot(caller)
|
metaprot = _get_menu_metaprot(caller)
|
||||||
text = [" |c--- Prototype menu --- |n"
|
prot = metaprot.prototype
|
||||||
]
|
key = prot.get("key")
|
||||||
|
|
||||||
pass
|
text = ["Set the prototype's |ykey|n."]
|
||||||
|
if key:
|
||||||
|
text.append("Current key value is '|y{}|n'.")
|
||||||
|
else:
|
||||||
|
text.append("Key is currently unset.")
|
||||||
|
text = "\n\n".join(text)
|
||||||
|
options = _wizard_options("meta_locks",
|
||||||
|
|
||||||
|
return "\n".join(text), options
|
||||||
|
|
||||||
|
|
||||||
def start_olc(caller, session=None, metaproto=None):
|
def start_olc(caller, session=None, metaproto=None):
|
||||||
|
|
@ -853,14 +921,13 @@ def start_olc(caller, session=None, metaproto=None):
|
||||||
prototype rather than creating a new one.
|
prototype rather than creating a new one.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
menudata = {"node_index": node_index,
|
||||||
menudata = {"node_meta_index": node_meta_index,
|
|
||||||
"node_meta_key": node_meta_key,
|
"node_meta_key": node_meta_key,
|
||||||
"node_meta_desc": node_meta_desc,
|
"node_meta_desc": node_meta_desc,
|
||||||
"node_meta_tags": node_meta_tags,
|
"node_meta_tags": node_meta_tags,
|
||||||
"node_meta_locks": node_meta_locks,
|
"node_meta_locks": node_meta_locks,
|
||||||
"node_prototype_index": node_prototype_index}
|
"node_key": node_key}
|
||||||
EvMenu(caller, menudata, startnode='node_meta_index', session=session, olc_metaproto=metaproto)
|
EvMenu(caller, menudata, startnode='node_index', session=session, olc_metaproto=metaproto)
|
||||||
|
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue