mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Update the building menu, following Griatch's feedback
This commit is contained in:
parent
415322fe1a
commit
fa31367a76
2 changed files with 164 additions and 24 deletions
|
|
@ -3,11 +3,39 @@ Module containing the building menu system.
|
|||
|
||||
Evennia contributor: vincent-lg 2018
|
||||
|
||||
Building menus are similar to `EvMenu`, except that they have been
|
||||
specifically designed to edit information as a builder. Creating a
|
||||
building menu in a command allows builders quick-editing of a
|
||||
given object, like a room. Here is an example of output you could
|
||||
obtain when editing the room:
|
||||
Building menus are in-game menus, not unlike `EvMenu` though using a
|
||||
different approach. Building menus have been specifically designed to edit
|
||||
information as a builder. Creating a building menu in a command allows
|
||||
builders quick-editing of a given object, like a room. If you follow the
|
||||
steps below to add the contrib, you will have access to an `@edit` command
|
||||
that will edit any default object offering to change its key and description.
|
||||
|
||||
1. Import the `GenericBuildingCmd` class from this contrib in your `mygame/commands/default_cmdset.py` file:
|
||||
|
||||
```python
|
||||
from evennia.contrib.building_menu import GenericBuildingCmd
|
||||
```
|
||||
|
||||
2. Below, add the command in the `CharacterCmdSet`:
|
||||
|
||||
```python
|
||||
# ... These lines should exist in the file
|
||||
class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||
key = "DefaultCharacter"
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
super(CharacterCmdSet, self).at_cmdset_creation()
|
||||
# ... add the line below
|
||||
self.add(GenericBuildingCmd())
|
||||
```
|
||||
|
||||
The `@edit` command will allow you to edit any object. You will need to
|
||||
specify the object name or ID as an argument. For instance: `@edit here`
|
||||
will edit the current room. However, building menus can perform much more
|
||||
than this very simple example, read on for more details.
|
||||
|
||||
Building menus can be set to edit about anything. Here is an example of
|
||||
output you could obtain when editing the room:
|
||||
|
||||
```
|
||||
Editing the room: Limbo(#2)
|
||||
|
|
@ -51,12 +79,24 @@ and enter t, she will be in the title choice. She can change the title
|
|||
(it will write in the room's `key` attribute) and then go back to the
|
||||
main menu using `@`.
|
||||
|
||||
`add_choice` has a lot of arguments and offer a great deal of
|
||||
`add_choice` has a lot of arguments and offers a great deal of
|
||||
flexibility. The most useful ones is probably the usage of callbacks,
|
||||
as you can set almost any argument in `add_choice` to be a callback, a
|
||||
function that you have defined above in your module. This function will be
|
||||
called when the menu element is triggered.
|
||||
|
||||
Notice that in order to edit a description, the best method to call isn't
|
||||
`add_choice`, but `add_choice_edit`. This is a convenient shortcut
|
||||
which is available to quickly open an `EvEditor` when entering this choice
|
||||
and going back to the menu when the editor closes.
|
||||
|
||||
```
|
||||
class RoomBuildingMenu(BuildingMenu):
|
||||
def init(self, room):
|
||||
self.add_choice("title", "t", attr="key")
|
||||
self.add_choice_edit("description", key="d", attr="db.desc")
|
||||
```
|
||||
|
||||
When you wish to create a building menu, you just need to import your
|
||||
class, create it specifying your intended caller and object to edit,
|
||||
then call `open`:
|
||||
|
|
@ -66,6 +106,8 @@ from <wherever> import RoomBuildingMenu
|
|||
|
||||
class CmdEdit(Command):
|
||||
|
||||
key = "redit"
|
||||
|
||||
def func(self):
|
||||
menu = RoomBuildingMenu(self.caller, self.caller.location)
|
||||
menu.open()
|
||||
|
|
@ -114,7 +156,7 @@ def _menu_savefunc(caller, buf):
|
|||
return True
|
||||
|
||||
def _menu_quitfunc(caller):
|
||||
caller.cmdset.add(BuildingMenuCmdSet, permanent=calelr.ndb._building_menu and caller.ndb._building_menu.persistent or False)
|
||||
caller.cmdset.add(BuildingMenuCmdSet, permanent=caller.ndb._building_menu and caller.ndb._building_menu.persistent or False)
|
||||
if caller.ndb._building_menu:
|
||||
caller.ndb._building_menu.move(back=True)
|
||||
|
||||
|
|
@ -129,7 +171,7 @@ def _call_or_get(value, menu=None, choice=None, string=None, obj=None, caller=No
|
|||
menu (BuildingMenu, optional): the building menu to pass to value
|
||||
if it is a callable.
|
||||
choice (Choice, optional): the choice to pass to value if a callable.
|
||||
string (str, optional): the raw string to pass to value if a callback. if a callable.
|
||||
string (str, optional): the raw string to pass to value if a callback.
|
||||
obj (Object): the object to pass to value if a callable.
|
||||
caller (Account or Object, optional): the caller to pass to value
|
||||
if a callable.
|
||||
|
|
@ -202,7 +244,10 @@ def menu_setattr(menu, choice, obj, string):
|
|||
"""
|
||||
attr = getattr(choice, "attr", None) if choice else None
|
||||
if choice is None or string is None or attr is None or menu is None:
|
||||
log_err("The `menu_setattr` function was called to set the attribute {} of object {} to {}, but the choice {} of menu {} or another information is missing.".format(attr, obj, repr(string), choice, menu))
|
||||
log_err(dedent("""
|
||||
The `menu_setattr` function was called to set the attribute {} of object {} to {},
|
||||
but the choice {} of menu {} or another information is missing.
|
||||
""".format(attr, obj, repr(string), choice, menu)).strip("\n")).strip()
|
||||
return
|
||||
|
||||
for part in attr.split(".")[:-1]:
|
||||
|
|
@ -219,6 +264,11 @@ def menu_quit(caller, menu):
|
|||
caller (Account or Object): the caller.
|
||||
menu (BuildingMenu): the building menu to close.
|
||||
|
||||
Note:
|
||||
This callback is used by default when using the
|
||||
`BuildingMenu.add_choice_quit` method. This method is called
|
||||
automatically if the menu has no parent.
|
||||
|
||||
"""
|
||||
if caller is None or menu is None:
|
||||
log_err("The function `menu_quit` was called with missing arguments: caller={}, menu={}".format(caller, menu))
|
||||
|
|
@ -231,7 +281,7 @@ def menu_quit(caller, menu):
|
|||
|
||||
def menu_edit(caller, choice, obj):
|
||||
"""
|
||||
Open the EvEditor to edit a specified field.
|
||||
Open the EvEditor to edit a specified attribute.
|
||||
|
||||
Args:
|
||||
caller (Account or Object): the caller.
|
||||
|
|
@ -437,13 +487,13 @@ class BuildingMenu(object):
|
|||
"""
|
||||
Class allowing to create and set building menus to edit specific objects.
|
||||
|
||||
A building menu is a kind of `EvMenu` designed to edit objects by
|
||||
builders, although it can be used for players in some contexts. You
|
||||
could, for instance, create a building menu to edit a room with a
|
||||
A building menu is somewhat similar to `EvMenu`, but designed to edit
|
||||
objects by builders, although it can be used for players in some contexts.
|
||||
You could, for instance, create a building menu to edit a room with a
|
||||
sub-menu for the room's key, another for the room's description,
|
||||
another for the room's exits, and so on.
|
||||
|
||||
To add choices (sub-menus), you should call `add_choice` (see the
|
||||
To add choices (simple sub-menus), you should call `add_choice` (see the
|
||||
full documentation of this method). With most arguments, you can
|
||||
specify either a plain string or a callback. This callback will be
|
||||
called when the operation is to be performed.
|
||||
|
|
@ -492,9 +542,13 @@ class BuildingMenu(object):
|
|||
self.persistent = persistent
|
||||
self.choices = []
|
||||
self.cmds = {}
|
||||
self.can_quit = False
|
||||
|
||||
if obj:
|
||||
self.init(obj)
|
||||
if not parents and not self.can_quit:
|
||||
# Automatically add the menu to quit
|
||||
self.add_choice_quit(key=None)
|
||||
self._add_keys_choice()
|
||||
|
||||
@property
|
||||
|
|
@ -686,16 +740,26 @@ class BuildingMenu(object):
|
|||
key = key.lower()
|
||||
aliases = aliases or []
|
||||
aliases = [a.lower() for a in aliases]
|
||||
if on_enter is None and on_nomatch is None:
|
||||
if attr is None:
|
||||
raise ValueError("The choice {} has neither attr nor callback, specify one of these as arguments".format(title))
|
||||
|
||||
if attr and on_nomatch is None:
|
||||
on_nomatch = menu_setattr
|
||||
|
||||
if key and key in self.cmds:
|
||||
raise ValueError("A conflict exists between {} and {}, both use key or alias {}".format(self.cmds[key], title, repr(key)))
|
||||
|
||||
if attr:
|
||||
if glance is None:
|
||||
glance = "{obj." + attr + "}"
|
||||
if text is None:
|
||||
text = """
|
||||
-------------------------------------------------------------------------------
|
||||
{attr} for {{obj}}(#{{obj.id}})
|
||||
|
||||
You can change this value simply by entering it.
|
||||
Use |y{back}|n to go back to the main menu.
|
||||
|
||||
Current value: |c{{{obj_attr}}}|n
|
||||
""".format(attr=attr, obj_attr="obj." + attr, back="|n or |y".join(self.keys_go_back))
|
||||
|
||||
choice = Choice(title, key=key, aliases=aliases, attr=attr, text=text, glance=glance, on_enter=on_enter, on_nomatch=on_nomatch, on_leave=on_leave,
|
||||
menu=self, caller=self.caller, obj=self.obj)
|
||||
self.choices.append(choice)
|
||||
|
|
@ -731,7 +795,7 @@ class BuildingMenu(object):
|
|||
|
||||
"""
|
||||
on_enter = on_enter or menu_edit
|
||||
return self.add_choice(title, key=key, aliases=aliases, attr=attr, glance=glance, on_enter=on_enter)
|
||||
return self.add_choice(title, key=key, aliases=aliases, attr=attr, glance=glance, on_enter=on_enter, text="")
|
||||
|
||||
def add_choice_quit(self, title="quit the menu", key="q", aliases=None, on_enter=None):
|
||||
"""
|
||||
|
|
@ -753,6 +817,7 @@ class BuildingMenu(object):
|
|||
|
||||
"""
|
||||
on_enter = on_enter or menu_quit
|
||||
self.can_quit = True
|
||||
return self.add_choice(title, key=key, aliases=aliases, on_enter=on_enter)
|
||||
|
||||
def open(self):
|
||||
|
|
@ -767,6 +832,11 @@ class BuildingMenu(object):
|
|||
"""
|
||||
caller = self.caller
|
||||
self._save()
|
||||
|
||||
# Remove the same-key cmdset if exists
|
||||
if caller.cmdset.has(BuildingMenuCmdSet):
|
||||
caller.cmdset.remove(BuildingMenuCmdSet)
|
||||
|
||||
self.caller.cmdset.add(BuildingMenuCmdSet, permanent=self.persistent)
|
||||
self.display()
|
||||
|
||||
|
|
@ -923,7 +993,11 @@ class BuildingMenu(object):
|
|||
|
||||
if choice.glance:
|
||||
glance = _call_or_get(choice.glance, menu=self, choice=choice, caller=self.caller, string="", obj=self.obj)
|
||||
glance = glance.format(obj=self.obj, caller=self.caller)
|
||||
try:
|
||||
glance = glance.format(obj=self.obj, caller=self.caller)
|
||||
except:
|
||||
import pdb;pdb.set_trace()
|
||||
|
||||
ret += ": " + glance
|
||||
|
||||
return ret
|
||||
|
|
@ -978,3 +1052,70 @@ class BuildingMenu(object):
|
|||
return
|
||||
|
||||
return building_menu
|
||||
|
||||
|
||||
# Generic building menu and command
|
||||
class GenericBuildingMenu(BuildingMenu):
|
||||
|
||||
"""A generic building menu, allowing to edit any object.
|
||||
|
||||
This is more a demonstration menu. By default, it allows to edit the
|
||||
object key and description. Nevertheless, it will be useful to demonstrate
|
||||
how building menus are meant to be used.
|
||||
|
||||
"""
|
||||
|
||||
def init(self, obj):
|
||||
"""Build the meny, adding the 'key' and 'description' choices.
|
||||
|
||||
Args:
|
||||
obj (Object): any object to be edited, like a character or room.
|
||||
|
||||
Note:
|
||||
The 'quit' choice will be automatically added, though you can
|
||||
call `add_choice_quit` to add this choice with different options.
|
||||
|
||||
"""
|
||||
self.add_choice("key", key="k", attr="key", glance="{obj.key}", text="""
|
||||
-------------------------------------------------------------------------------
|
||||
Editing the key of {{obj.key}}(#{{obj.id}})
|
||||
|
||||
You can change the simply by entering it.
|
||||
Use |y{back}|n to go back to the main menu.
|
||||
|
||||
Current key: |c{{obj.key}}|n
|
||||
""".format(back="|n or |y".join(self.keys_go_back)))
|
||||
self.add_choice_edit("description", key="d", attr="db.desc")
|
||||
|
||||
|
||||
class GenericBuildingCmd(Command):
|
||||
|
||||
"""
|
||||
Generic building command.
|
||||
|
||||
Syntax:
|
||||
@edit [object]
|
||||
|
||||
Open a building menu to edit the specified object. This menu allows to
|
||||
change the object's key and description.
|
||||
|
||||
Examples:
|
||||
@edit here
|
||||
@edit self
|
||||
@edit #142
|
||||
|
||||
"""
|
||||
|
||||
key = "@edit"
|
||||
|
||||
def func(self):
|
||||
if not self.args.strip():
|
||||
self.msg("You should provide an argument to this function: the object to edit.")
|
||||
return
|
||||
|
||||
obj = self.caller.search(self.args.strip(), global_search=True)
|
||||
if not obj:
|
||||
return
|
||||
|
||||
menu = GenericBuildingMenu(self.caller, obj)
|
||||
menu.open()
|
||||
|
|
|
|||
|
|
@ -1712,7 +1712,6 @@ class TestBuildingMenu(CommandTest):
|
|||
super(TestBuildingMenu, self).setUp()
|
||||
self.menu = BuildingMenu(caller=self.char1, obj=self.room1, title="test")
|
||||
self.menu.add_choice("title", key="t", attr="key")
|
||||
self.menu.add_choice_quit()
|
||||
|
||||
def test_quit(self):
|
||||
"""Try to quit the building menu."""
|
||||
|
|
@ -1774,9 +1773,9 @@ class TestBuildingMenu(CommandTest):
|
|||
def on_nomatch_t2(caller, menu):
|
||||
menu.move("t3") # this time the key matters
|
||||
|
||||
t1 = self.menu.add_choice("what", key="t1", attr="t1", on_nomatch=on_nomatch_t1)
|
||||
t2 = self.menu.add_choice("and", key="t1.*", attr="t2", on_nomatch=on_nomatch_t2)
|
||||
t3 = self.menu.add_choice("why", key="t1.*.t3", attr="t3")
|
||||
t1 = self.menu.add_choice("what", key="t1", on_nomatch=on_nomatch_t1)
|
||||
t2 = self.menu.add_choice("and", key="t1.*", on_nomatch=on_nomatch_t2)
|
||||
t3 = self.menu.add_choice("why", key="t1.*.t3")
|
||||
self.menu.open()
|
||||
|
||||
# Move into t1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue