Refactor spawn, update remaining in olc

This commit is contained in:
Griatch 2018-07-29 16:53:54 +02:00
parent 16640fa923
commit 6126391406
3 changed files with 135 additions and 65 deletions

View file

@ -1874,10 +1874,25 @@ def node_update_objects(caller, **kwargs):
diff, obj_prototype = spawner.prototype_diff_from_object(prototype, obj) diff, obj_prototype = spawner.prototype_diff_from_object(prototype, obj)
text = ["Suggested changes to {} objects. ".format(len(update_objects)), text = ["Suggested changes to {} objects. ".format(len(update_objects)),
"Showing random example obj to change: {name} (#{dbref}))\n".format(obj.key, obj.dbref)] "Showing random example obj to change: {name} ({dbref}))\n".format(
options = [] name=obj.key, dbref=obj.dbref)]
helptext = """
Be careful with this operation! The upgrade mechanism will try to automatically estimate
what changes need to be applied. But the estimate is |wonly based on the analysis of one
randomly selected object|n among all objects spawned by this prototype. If that object
happens to be unusual in some way the estimate will be off and may lead to unexpected
results for other objects. Always test your objects carefully after an upgrade and
consider being conservative (switch to KEEP) or even do the update manually if you are
unsure that the results will be acceptable. """
options = _wizard_options("update_objects", back_node[5:], None)
io = 0 io = 0
for (key, inst) in sorted(((key, val) for key, val in diff.items()), key=lambda tup: tup[0]): for (key, inst) in sorted(((key, val) for key, val in diff.items()), key=lambda tup: tup[0]):
if key in protlib._PROTOTYPE_META_NAMES:
continue
line = "{iopt} |w{key}|n: {old}{sep}{new} {change}" line = "{iopt} |w{key}|n: {old}{sep}{new} {change}"
old_val = utils.crop(str(obj_prototype[key]), width=20) old_val = utils.crop(str(obj_prototype[key]), width=20)
@ -1907,14 +1922,7 @@ def node_update_objects(caller, **kwargs):
{"key": "|wb|rack ({})".format(back_node[5:], 'b'), {"key": "|wb|rack ({})".format(back_node[5:], 'b'),
"goto": back_node}]) "goto": back_node}])
helptext = """ text = "\n".join(text)
Be careful with this operation! The upgrade mechanism will try to automatically estimate
what changes need to be applied. But the estimate is |wonly based on the analysis of one
randomly selected object|n among all objects spawned by this prototype. If that object
happens to be unusual in some way the estimate will be off and may lead to unexpected
results for other objects. Always test your objects carefully after an upgrade and
consider being conservative (switch to KEEP) or even do the update manually if you are
unsure that the results will be acceptable. """
text = (text, helptext) text = (text, helptext)
@ -1928,7 +1936,8 @@ def node_prototype_save(caller, **kwargs):
"""Save prototype to disk """ """Save prototype to disk """
# these are only set if we selected 'yes' to save on a previous pass # these are only set if we selected 'yes' to save on a previous pass
prototype = kwargs.get("prototype", None) prototype = kwargs.get("prototype", None)
accept_save = kwargs.get("accept_save", False) # set to True/False if answered, None if first pass
accept_save = kwargs.get("accept_save", None)
if accept_save and prototype: if accept_save and prototype:
# we already validated and accepted the save, so this node acts as a goto callback and # we already validated and accepted the save, so this node acts as a goto callback and
@ -1939,22 +1948,38 @@ def node_prototype_save(caller, **kwargs):
spawned_objects = protlib.search_objects_with_prototype(prototype_key) spawned_objects = protlib.search_objects_with_prototype(prototype_key)
nspawned = spawned_objects.count() nspawned = spawned_objects.count()
text = ["|gPrototype saved.|n"]
if nspawned: if nspawned:
text = ("Do you want to update {} object(s) " text.append("\nDo you want to update {} object(s) "
"already using this prototype?".format(nspawned)) "already using this prototype?".format(nspawned))
options = ( options = (
{"key": ("|wY|Wes|n", "yes", "y"), {"key": ("|wY|Wes|n", "yes", "y"),
"desc": "Go to updating screen",
"goto": ("node_update_objects", "goto": ("node_update_objects",
{"accept_update": True, "objects": spawned_objects, {"accept_update": True, "objects": spawned_objects,
"prototype": prototype, "back_node": "node_prototype_save"})}, "prototype": prototype, "back_node": "node_prototype_save"})},
{"key": ("[|wN|Wo|n]", "n"), {"key": ("[|wN|Wo|n]", "n"),
"goto": "node_spawn"}, "desc": "Return to index",
"goto": "node_index"},
{"key": "_default", {"key": "_default",
"goto": "node_spawn"}) "goto": "node_index"})
else: else:
text = "|gPrototype saved.|n" text.append("(press Return to continue)")
options = {"key": "_default", options = {"key": "_default",
"goto": "node_spawn"} "goto": "node_index"}
text = "\n".join(text)
helptext = """
Updating objects means that the spawner will find all objects previously created by this
prototype. You will be presented with a list of the changes the system will try to apply to
each of these objects and you can choose to customize that change if needed. If you have
done a lot of manual changes to your objects after spawning, you might want to update those
objects manually instead.
"""
text = (text, helptext)
return text, options return text, options
@ -1967,27 +1992,19 @@ def node_prototype_save(caller, **kwargs):
if error: if error:
# abort save # abort save
text.append( text.append(
"Validation errors were found. They need to be corrected before this prototype " "\n|yValidation errors were found. They need to be corrected before this prototype "
"can be saved (or used to spawn).") "can be saved (or used to spawn).|n")
options = _wizard_options("prototype_save", "prototype_locks", "index") options = _wizard_options("prototype_save", "index", None)
return "\n".join(text), options return "\n".join(text), options
prototype_key = prototype['prototype_key'] prototype_key = prototype['prototype_key']
if protlib.search_prototype(prototype_key): if protlib.search_prototype(prototype_key):
text.append("Do you want to save/overwrite the existing prototype '{name}'?".format( text.append("\nDo you want to save/overwrite the existing prototype '{name}'?".format(
name=prototype_key)) name=prototype_key))
else: else:
text.append("Do you want to save the prototype as '{name}'?".format(prototype_key)) text.append("\nDo you want to save the prototype as '{name}'?".format(name=prototype_key))
options = ( text = "\n".join(text)
{"key": ("[|wY|Wes|n]", "yes", "y"),
"goto": ("node_prototype_save",
{"accept": True, "prototype": prototype})},
{"key": ("|wN|Wo|n", "n"),
"goto": "node_spawn"},
{"key": "_default",
"goto": ("node_prototype_save",
{"accept": True, "prototype": prototype})})
helptext = """ helptext = """
Saving the prototype makes it available for use later. It can also be used to inherit from, Saving the prototype makes it available for use later. It can also be used to inherit from,
@ -1999,6 +2016,18 @@ def node_prototype_save(caller, **kwargs):
text = (text, helptext) text = (text, helptext)
options = (
{"key": ("[|wY|Wes|n]", "yes", "y"),
"desc": "Save prototype",
"goto": ("node_prototype_save",
{"accept_save": True, "prototype": prototype})},
{"key": ("|wN|Wo|n", "n"),
"desc": "Abort and return to Index",
"goto": "node_index"},
{"key": "_default",
"goto": ("node_prototype_save",
{"accept_save": True, "prototype": prototype})})
return text, options return text, options
@ -2015,26 +2044,42 @@ def _spawn(caller, **kwargs):
obj = spawner.spawn(prototype) obj = spawner.spawn(prototype)
if obj: if obj:
obj = obj[0] obj = obj[0]
caller.msg("|gNew instance|n {key} ({dbref}) |gspawned.|n".format( text = "|gNew instance|n {key} ({dbref}) |gspawned.|n".format(
key=obj.key, dbref=obj.dbref)) key=obj.key, dbref=obj.dbref)
else: else:
caller.msg("|rError: Spawner did not return a new instance.|n") text = "|rError: Spawner did not return a new instance.|n"
return obj return "node_examine_entity", {"text": text, "back": "prototype_spawn"}
def node_prototype_spawn(caller, **kwargs): def node_prototype_spawn(caller, **kwargs):
"""Submenu for spawning the prototype""" """Submenu for spawning the prototype"""
prototype = _get_menu_prototype(caller) prototype = _get_menu_prototype(caller)
error, text = _validate_prototype(prototype)
already_validated = kwargs.get("already_validated", False)
if already_validated:
error, text = None, []
else:
error, text = _validate_prototype(prototype)
text = [text] text = [text]
if error: if error:
text.append("|rPrototype validation failed. Correct the errors before spawning.|n") text.append("\n|rPrototype validation failed. Correct the errors before spawning.|n")
options = _wizard_options("prototype_spawn", "prototype_locks", "index") options = _wizard_options("prototype_spawn", "index", None)
return "\n".join(text), options return "\n".join(text), options
text = "\n".join(text)
helptext = """
Spawning is the act of instantiating a prototype into an actual object. As a new object is
spawned, every $protfunc in the prototype is called anew. Since this is a common thing to
do, you may also temporarily change the |clocation|n of this prototype to bypass whatever
value is set in the prototype.
"""
text = (text, helptext)
# show spawn submenu options # show spawn submenu options
options = [] options = []
prototype_key = prototype['prototype_key'] prototype_key = prototype['prototype_key']
@ -2064,18 +2109,10 @@ def node_prototype_spawn(caller, **kwargs):
options.append( options.append(
{"desc": "Update {num} existing objects with this prototype".format(num=nspawned), {"desc": "Update {num} existing objects with this prototype".format(num=nspawned),
"goto": ("node_update_objects", "goto": ("node_update_objects",
dict(prototype=prototype, opjects=spawned_objects, {"objects": list(spawned_objects),
back_node="node_prototype_spawn"))}) "prototype": prototype,
options.extend(_wizard_options("prototype_spawn", "prototype_save", "index")) "back_node": "node_prototype_spawn"})})
options.extend(_wizard_options("prototype_spawn", "index", None))
helptext = """
Spawning is the act of instantiating a prototype into an actual object. As a new object is
spawned, every $protfunc in the prototype is called anew. Since this is a common thing to
do, you may also temporarily change the |clocation|n of this prototype to bypass whatever
value is set in the prototype.
"""
text = (text, helptext)
return text, options return text, options
@ -2088,30 +2125,56 @@ def _prototype_load_select(caller, prototype_key):
if matches: if matches:
prototype = matches[0] prototype = matches[0]
_set_menu_prototype(caller, prototype) _set_menu_prototype(caller, prototype)
caller.msg("|gLoaded prototype '{}'.".format(prototype_key)) return "node_examine_entity", \
return "node_index" {"text": "|gLoaded prototype {}.|n".format(prototype['prototype_key']),
"back": "index"}
else: else:
caller.msg("|rFailed to load prototype '{}'.".format(prototype_key)) caller.msg("|rFailed to load prototype '{}'.".format(prototype_key))
return None return None
def _prototype_load_actions(caller, raw_inp, **kwargs):
"""Parse the default Convert prototype to a string representation for closer inspection"""
choices = kwargs.get("available_choices", [])
prototype, action = _default_parse(
raw_inp, choices, ("examine", "e", "l"))
if prototype:
# a selection of parent was made
prototype = protlib.search_prototype(key=prototype)[0]
# which action to apply on the selection
if action == 'examine':
# examine the prototype
txt = protlib.prototype_to_str(prototype)
kwargs['text'] = txt
kwargs['back'] = 'prototype_load'
return "node_examine_entity", kwargs
return 'node_prototype_load'
@list_node(_all_prototype_parents, _prototype_load_select) @list_node(_all_prototype_parents, _prototype_load_select)
def node_prototype_load(caller, **kwargs): def node_prototype_load(caller, **kwargs):
"""Load prototype""" """Load prototype"""
text = """ text = """
Select a prototype to load. This will replace any prototype currently being edited! Select a prototype to load. This will replace any prototype currently being edited!
"""
{actions}
""".format(actions=_format_list_actions("examine"))
helptext = """ helptext = """
Loading a prototype will load it and return you to the main index. It can be a good idea to Loading a prototype will load it and return you to the main index. It can be a good idea
examine the prototype before loading it. to examine the prototype before loading it.
""" """
text = (text, helptext) text = (text, helptext)
options = _wizard_options("prototype_load", "prototype_save", "index") options = _wizard_options("prototype_load", "index", None)
options.append({"key": "_default", options.append({"key": "_default",
"goto": _prototype_parent_actions}) "goto": _prototype_load_actions})
return text, options return text, options

View file

@ -553,7 +553,9 @@ def spawn(*prototypes, **kwargs):
alias_string = init_spawn_value(val, make_iter) alias_string = init_spawn_value(val, make_iter)
val = prot.pop("tags", []) val = prot.pop("tags", [])
tags = init_spawn_value(val, make_iter) tags = []
for (tag, category, data) in tags:
tags.append((init_spawn_value(val, str), category, data))
prototype_key = prototype.get('prototype_key', None) prototype_key = prototype.get('prototype_key', None)
if prototype_key: if prototype_key:
@ -567,9 +569,11 @@ def spawn(*prototypes, **kwargs):
nattributes = dict((key.split("_", 1)[1], init_spawn_value(val, value_to_obj)) nattributes = dict((key.split("_", 1)[1], init_spawn_value(val, value_to_obj))
for key, val in prot.items() if key.startswith("ndb_")) for key, val in prot.items() if key.startswith("ndb_"))
# the rest are attributes # the rest are attribute tuples (attrname, value, category, locks)
val = prot.pop("attrs", []) val = make_iter(prot.pop("attrs", []))
attributes = init_spawn_value(val, list) attributes = []
for (attrname, value, category, locks) in val:
attributes.append((attrname, init_spawn_value(val), category, locks))
simple_attributes = [] simple_attributes = []
for key, value in ((key, value) for key, value in prot.items() for key, value in ((key, value) for key, value in prot.items()

View file

@ -938,7 +938,7 @@ class EvMenu(object):
for key, desc in optionlist: for key, desc in optionlist:
if not (key or desc): if not (key or desc):
continue continue
desc_string = ": %s" % (desc if desc else "") desc_string = ": %s" % desc if desc else ""
table_width_max = max(table_width_max, table_width_max = max(table_width_max,
max(m_len(p) for p in key.split("\n")) + max(m_len(p) for p in key.split("\n")) +
max(m_len(p) for p in desc_string.split("\n")) + colsep) max(m_len(p) for p in desc_string.split("\n")) + colsep)
@ -1140,9 +1140,12 @@ def list_node(option_generator, select=None, pagesize=10):
decorated_options = make_iter(decorated_options) decorated_options = make_iter(decorated_options)
extra_options = [] extra_options = []
if isinstance(decorated_options, dict):
decorated_options = [decorated_options]
for eopt in decorated_options: for eopt in decorated_options:
cback = ("goto" in eopt and "goto") or ("exec" in eopt and "exec") or None cback = ("goto" in eopt and "goto") or ("exec" in eopt and "exec") or None
if cback: if cback:
print("eopt, cback: {} {}".format(eopt, cback))
signature = eopt[cback] signature = eopt[cback]
if callable(signature): if callable(signature):
# callable with no kwargs defined # callable with no kwargs defined