Fixed all links

This commit is contained in:
Griatch 2020-10-11 19:31:05 +02:00
parent d4f1733bc7
commit 26f8ba3f71
175 changed files with 11972 additions and 4443 deletions

View file

@ -25,9 +25,26 @@ This is an example of a menu *node*. Think of a node as a point where the menu s
and waits for user to give some input. By jumping to different nodes depending on the input, a menu
is constructed.
To create the menu, EvMenu uses normal Python functions, one per node. It will load all those
## Ways to create the menu
### node functions
The native way to define an EvMenu is to define Python functions, one per node. It will load all
those
functions/nodes either from a module or by being passed a dictionary mapping the node's names to
said functions, like `{"nodename": <function>, ...}`
said functions, like `{"nodename": <function>, ...}`. Since you are dealing with raw code, this is
by
far the most powerful way - for example you could have dynamic nodes that change content depending
on game context, time and what you picked before.
### menu templating
For a simpler menu you often don't need the full flexibility you get from defining each node as a
Python function. For that, there is the _EvMenu templating_ language. This allows you to define the
menu
in a more human-readable string with a simple format. This is then parsed to produce the
`{"nodename": <function>, ...}` mapping for you, for the EvMenu to use normally. The templating
language is described in the [Menu templating section](./EvMenu#Evmenu-templating-language).
## Launching the menu
@ -46,12 +63,38 @@ class CmdTestMenu(Command):
def func(self):
EvMenu(caller, "world.mymenu")
EvMenu(caller, "world.mymenu")
```
When running this command, the menu will start using the menu nodes loaded from
`mygame/world/mymenu.py`. See next section on how to define menu nodes.
`mygame/world/mymenu.py` and use this to build the menu-tree - each function name becomes
the name of a node in the tree. See next section on how to define menu nodes.
Alternatively, you could pass the menu-tree to EvMenu directly:
```python
menutree = {"start": nodestartfunc,
"node1": nodefunc1,
"node2": nodefunc2,, ...}
EvMenu(caller, menutree)
```
This menutree can also be generated from an *EvMenu template*
```python
from evennia.utils.evmenu import parse_menu_template
menutree = parse_menu_template(caller, template_string, goto_callables)
EvMenu(caller, menutree)
```
The `template_string` and `goto_callables` are described in [Template language
section](EvMenu#Evmenu-templating-language).
## The EvMenu class
The `EvMenu` has the following optional callsign:
@ -73,26 +116,34 @@ EvMenu(caller, menu_data,
new [CmdSet](./Command-Sets) assigned to it, for handling the menu.
- `menu_data` (str, module or dict): is a module or python path to a module where the global-level
functions will each be considered to be a menu node. Their names in the module will be the names
by which they are referred to in the module. Importantly, function names starting with an underscore
`_` will be ignored by the loader. Alternatively, this can be a direct mapping `{"nodename":function, ...}`.
by which they are referred to in the module. Importantly, function names starting with an
underscore
`_` will be ignored by the loader. Alternatively, this can be a direct mapping
`{"nodename":function, ...}`.
- `startnode` (str): is the name of the menu-node to start the menu at. Changing this means that
you can jump into a menu tree at different positions depending on circumstance and thus possibly
re-use menu entries.
- `cmdset_mergetype` (str): This is usually one of "Replace" or "Union" (see [CmdSets](./Command-Sets).
- `cmdset_mergetype` (str): This is usually one of "Replace" or "Union" (see [CmdSets](Command-
Sets).
The first means that the menu is exclusive - the user has no access to any other commands while
in the menu. The Union mergetype means the menu co-exists with previous commands (and may overload
in the menu. The Union mergetype means the menu co-exists with previous commands (and may
overload
them, so be careful as to what to name your menu entries in this case).
- `cmdset_priority` (int): The priority with which to merge in the menu cmdset. This allows for
advanced usage.
- `auto_quit`, `auto_look`, `auto_help` (bool): If either of these are `True`, the menu
automatically makes a `quit`, `look` or `help` command available to the user. The main reason why
you'd want to turn this off is if you want to use the aliases "q", "l" or "h" for something in your
menu. Nevertheless, at least `quit` is highly recommend - if `False`, the menu *must* itself supply
an "exit node" (a node without any options), or the user will be stuck in the menu until the server
you'd want to turn this off is if you want to use the aliases "q", "l" or "h" for something in
your
menu. Nevertheless, at least `quit` is highly recommend - if `False`, the menu *must* itself
supply
an "exit node" (a node without any options), or the user will be stuck in the menu until the
server
reloads (or eternally if the menu is `persistent`)!
- `cmd_on_exit` (str): This command string will be executed right *after* the menu has closed down.
From experience, it's useful to trigger a "look" command to make sure the user is aware of the
change of state; but any command can be used. If set to `None`, no command will be triggered after
change of state; but any command can be used. If set to `None`, no command will be triggered
after
exiting the menu.
- `persistent` (bool) - if `True`, the menu will survive a reload (so the user will not be kicked
out by the reload - make sure they can exit on their own!)
@ -134,7 +185,8 @@ def menunodename3(caller, raw_string, **kwargs):
```
> While all of the above forms are okay, it's recommended to stick to the third and last form since it
> While all of the above forms are okay, it's recommended to stick to the third and last form since
it
> gives the most flexibility. The previous forms are mainly there for backwards compatibility with
> existing menus from a time when EvMenu was less able.
@ -173,11 +225,13 @@ help text is not given, the menu will give a generic error message when using `h
#### options
The `options` list describe all the choices available to the user when viewing this node. If `options` is
The `options` list describe all the choices available to the user when viewing this node. If
`options` is
returned as `None`, it means that this node is an *Exit node* - any text is displayed and then the
menu immediately exits, running the `exit_cmd` if given.
Otherwise, `options` should be a list (or tuple) of dictionaries, one for each option. If only one option is
Otherwise, `options` should be a list (or tuple) of dictionaries, one for each option. If only one
option is
available, a single dictionary can also be returned. This is how it could look:
@ -187,10 +241,10 @@ def node_test(caller, raw_string, **kwargs):
text = "A goblin attacks you!"
options = (
{"key": ("Attack", "a", "att"),
{"key": ("Attack", "a", "att"),
"desc": "Strike the enemy with all your might",
"goto": "node_attack"},
{"key": ("Defend", "d", "def"),
{"key": ("Defend", "d", "def"),
"desc": "Hold back and defend yourself",
"goto": (_defend, {"str": 10, "enemyname": "Goblin"})})
@ -212,7 +266,8 @@ Defend: Hold back and defend yourself
##### option-key 'key'
The option's `key` is what the user should enter in order to choose that option. If given as a tuple, the
The option's `key` is what the user should enter in order to choose that option. If given as a
tuple, the
first string of that tuple will be what is shown on-screen while the rest are aliases for picking
that option. In the above example, the user could enter "Attack" (or "attack", it's not
case-sensitive), "a" or "att" in order to attack the goblin. Aliasing is useful for adding custom
@ -284,11 +339,11 @@ def node_select(caller, raw_string, **kwargs):
"help - they all do different things ...")
options = ({"desc": "Option one",
"goto": "node_one"},
{"desc": "Option two",
"goto": _action_two},
{"desc": "Option three",
"goto": (_action_three, {"key": 1, "key2": 2})}
"goto": "node_one"},
{"desc": "Option two",
"goto": _action_two},
{"desc": "Option three",
"goto": (_action_three, {"key": 1, "key2": 2})}
)
return text, options
@ -306,7 +361,8 @@ function as
Here, `raw_string` is always the input the user entered to make that choice and `kwargs` are the
same as those `kwargs` that already entered the *current* node (they are passed on).
Alternatively the `goto` could point to a "goto-callable". Such callables are usually defined in the same
Alternatively the `goto` could point to a "goto-callable". Such callables are usually defined in the
same
module as the menu nodes and given names starting with `_` (to avoid being parsed as nodes
themselves). These callables will be called the same as a node function - `callable(caller,
raw_string, **kwargs)`, where `raw_string` is what the user entered on this node and `**kwargs` is
@ -318,7 +374,8 @@ kwargs passed into it depending on which option was actually chosen.
The "goto callable" must either return a string `"nodename"` or a tuple `("nodename", mykwargs)`.
This will lead to the next node being called as either `nodename(caller, raw_string, **kwargs)` or
`nodename(caller, raw_string, **mykwargs)` - so this allows changing (or replacing) the options going
`nodename(caller, raw_string, **mykwargs)` - so this allows changing (or replacing) the options
going
into the next node depending on what option was chosen.
There is one important case - if the goto-callable returns `None` for a `nodename`, *the current
@ -412,21 +469,293 @@ class MyEvMenu(EvMenu):
See `evennia/utils/evmenu.py` for the details of their default implementations.
## Evmenu templating language
The EvMenu is very powerful and flexible. But often your menu is simple enough to
not require the full power of EvMenu. For this you can use the Evmenu templating language.
This is how the templating is used:
```python
from evennia.utils.evmenu import parse_menu_template, EvMenu
template_string = "(will be described below)"
# this could be empty if you don't need to access any callables
# in your template
goto_callables = {"mycallable1": function, ...}
# generate the menutree
menutree = parse_menu_template(caller, template_string, goto_callables)
# a normal EvMenu call
EvMenu(caller, menutree, ...)
```
... So the `parse_menu_template` is just another way to generate the `menutree` dict needed by
EvMenu - after this EvMenu works normally.
The good thing with this two-step procedude is that you can mix- and match - if you wanted
you could insert a normal, fully flexible function-based node-function in the `menutree` before
passing
the whole thing into `EvMenu` and get the best of both worlds. It also makes it
easy to substitute base EvMenu with a child class that changes the menu display.
... But if you really don't need any such customization, you can also apply the template in one step
using
the `template2menu` helper:
```python
from evennia.utils.evmenu import template2menu
template_string = "(will be described below)"
goto_callables = {"mycallable1": function, ...}
template2menu(caller, template_string, goto_callables, startnode="start", ...)
```
In addition to the template-related arguments, `template2menu` takes all the same `**kwargs`
as `EvMenu` and will parse the template and start the menu for you in one go.
### The templating string
The template is a normal string with a very simple format. Each node begins
with a marker `## Node <name of node>`, follwowed by a `## Options` separator (the `Node` and
`Options` are
case-insensitive).
```python
template_string = """
## NODE start
<text for the node>
## OPTIONS
# this is a comment. Only line-comments are allowed.
key;alias;alias: description -> goto_str_or_callable
key;alias;alias: goto_str_or_callable
>pattern: goto_str_or_callable
"""
```
- The text after `## NODE` defines the name of the node. This must be unique within the
menu because this is what you use for `goto` statements. The name could have spaces.
- The area between `## NODE` and `## OPTIONS` contains the text of the node. It can have
normal formatting and will retain intentation.
- The `## OPTIONS` section, until the next `## NODE` or the end of the string,
holds the options, one per line.
- Option-indenting is ignored but can be useful for readability.
- The options-section can also have line-comments, marked by starting the line with `#`.
- A node without a following `## OPTIONS` section indicates an end node, and reaching
it will print the text and immediately exit the menu (same as for regular EvMenu).
### Templating options format
The normal, full syntax is:
key;alias;alias: description -> goto_str_or_callable
An example would be
next;n: Go to node Two -> node2
In the menu, this will become an option
next: Go to node Two
where you can enter `next` or `n` to go to the menu node named `node2`.
To skip the description, just add the goto without the `->`:
next;n: node2
This will create a menu option without any description:
next
A special key is `>`. This acts as a _pattern matcher_. Between `>` and the `:` one
can fit an optional _pattern_. This
pattern will first be parsed with [glob-style
parsing](https://docs.python.org/2/library/fnmatch.html) and then
with [regex](https://docs.python.org/3/library/re.html#module-re), and only if
the player's input matches either will the option be chosen. An input-matching
option cannot have a description.
```
# this matches the empty string (just pressing return)
>: node2
# this matches input starting with 'test' (regex match)
> ^test.+?: testnode
# this matches any number input (regex match)
> [0-9]+?: countnode
# this matches everything not covered by previous options
# (glob-matching, space is stripped without quotes)
> *: node3
```
You can have multiple pattern-matchers for a node but remember that options are
checked in the order they are listed. So make sure to put your pattern-matchers
in decending order of generality; if you have a 'catch-all' pattern,
it should be put last or those behind it will never be tried.
```
next;n: node2
back;b: node1
>: node2
```
The above would give you the option to write next/back but you can also just press return to move on
to the next node.
### Templating goto-callables
Instead of giving the name of a node to go to, you can also give the name
of a _goto_callable_, which in turn returns the name of the node to go to. You
tell the template it's a callable by simply adding `()` at the end.
next: Go to node 2 -> goto_node2()
You can also add keyword arguments:
back: myfunction(from=foo)
> Note: ONLY keyword-arguments are supported! Trying to pass a positional
> argument will lead to an error.
The contents of the kwargs-values will be evaluated by `literal_eval` so
you don't need to add quotes to strings _unless they have spaces in them_. Numbers
will be converted correctly, but more complex input structures (like lists or dicts) will
_not_ - if you want more complex input you should use a full function-based EvMenu
node instead.
The goto-callable is defined just like any Evmenu goto-func. You must always
use the full form (including `**kwargs`):
```python
def mygotocallable(caller, raw_string, **kwargs):
# ...
return "nodename_to_goto"
```
Return `None` to re-run the current node. Any keyword arguments you specify in
your template will be passed to your goto-callable in `**kwargs`. Unlike in
regular EvMenu nodes you _can't_ return kwargs to pass it between nodes and other dynamic
tricks.
All goto-callables you use in your menu-template must be added to the
`goto_callable` mapping that you pass to `parse_menu_template` or
`template2menu`.
### Templating example to show all possible options:
```python
template_string = """
## NODE start
This is the text of the start node.
Both ## NODE, ## node or ## Node works. The node-name can have
spaces.
The text area can have multiple lines, line breaks etc.
## OPTIONS
# here starts the option-defition
# comments are only allowed from beginning of line.
# Indenting is not necessary, but good for readability
1: Option number 1 -> node1
2: Option number 2 -> node2
next: This steps next -> go_back()
# the -> can be ignored if there is no desc
back: go_back(from_node=start)
abort: abort
# ----------------------------------- this is ignored
## NODE node1
Text for Node1. Enter a message!
<return> to go back.
## options
# Starting the option-line with >
# allows to perform different actions depending on
# what is inserted.
# this catches everything starting with foo
> foo*: handle_foo_message()
# regex are also allowed (this catches number inputs)
> [0-9]+?: handle_numbers()
# this catches the empty return
>: start
# this catches everything else
> *: handle_message(from_node=node1)
# -----------------------------------------
## NODE node2
Text for Node2. Just go back.
## options
>: start
# node abort
This exits the menu since there is no `## options` section.
"""
# we assume the callables are defined earlier
goto_callables = {"go_back": go_back_func,
"handle_foo_message": handle_message,
"handle_numbers": my_number_handler,
"handle_message": handle_message2}
# boom - a menu
template2menu(caller, template_string, goto_callables)
```
## Examples:
- **[Simple branching menu](./EvMenu#example-simple-branching-menu)** - choose from options
- **[Dynamic goto](./EvMenu#example-dynamic-goto)** - jumping to different nodes based on response
- **[Set caller properties](./EvMenu#example-set-caller-properties)** - a menu that changes things
- **[Getting arbitrary input](./EvMenu#example-get-arbitrary-input)** - entering text
- **[Storing data between nodes](./EvMenu#example-storing-data-between-nodes)** - keeping states and information while in the menu
- **[Repeating the same node](./EvMenu#example-repeating-the-same-node)** - validating within the node before moving to the next
- **[Full Menu](./EvMenu#example-full-menu):** a complete example
- **[Yes/No prompt](./EvMenu#example-yesno-prompt)** - entering text with limited possible responses (this is *not* using EvMenu but the conceptually similar yet technically unrelated `get_input` helper function accessed as `evennia.utils.evmenu.get_input`).
- **[Storing data between nodes](./EvMenu#example-storing-data-between-nodes)** - keeping states and
information while in the menu
- **[Repeating the same node](./EvMenu#example-repeating-the-same-node)** - validating within the node
before moving to the next
- **[Yes/No prompt](./EvMenu#example-yesno-prompt)** - entering text with limited possible responses
(this is *not* using EvMenu but the conceptually similar yet technically unrelated `get_input`
helper function accessed as `evennia.utils.evmenu.get_input`).
### Example: Simple branching menu
Below is an example of a simple branching menu node leading to different other nodes depending on choice:
Below is an example of a simple branching menu node leading to different other nodes depending on
choice:
```python
# in mygame/world/mychargen.py
@ -520,11 +849,11 @@ def node_background(caller):
options = ({"key": "death",
"desc": "A violent death in the family",
"goto": (_set_attribute, {"attr": ("experienced_violence", True),
"next_node": "node_violent_background"})},
"next_node": "node_violent_background"})},
{"key": "betrayal",
"desc": "The betrayal of a trusted grown-up",
"goto": (_set_attribute, {"attr": ("experienced_betrayal", True),
"next_node": "node_betrayal_background"})})
"next_node": "node_betrayal_background"})})
return text, options
```
@ -564,8 +893,8 @@ def _set_name(caller, raw_string, **kwargs):
caller.msg("Set name to {}.".format(prev_entry))
return "node_background"
else:
caller.msg("Aborted.")
return "node_exit"
caller.msg("Aborted.")
return "node_exit"
else:
# re-run old node, but pass in the name given
return None, {"prev_entry": inp}
@ -577,9 +906,9 @@ def enter_name(caller, raw_string, **kwargs):
prev_entry = kwargs.get("prev_entry")
if prev_entry:
text = "Current name: {}.\nEnter another name or <return> to accept."
text = "Current name: {}.\nEnter another name or <return> to accept."
else:
text = "Enter your character's name or <return> to abort."
text = "Enter your character's name or <return> to abort."
options = {"key": "_default",
"goto": (_set_name, {"prev_entry": prev_entry})}
@ -643,7 +972,7 @@ def node_view_sheet(caller):
options = ({"key": "Accept",
"goto": "finish_chargen"},
{"key": "Decline",
{"key": "Decline",
"goto": "start_over"})
return text, options
@ -656,7 +985,8 @@ all nodes. At the end we look at it and, if we accept the character the menu wil
result to permanent storage and exit.
> One point to remember though is that storage on `caller.ndb._menutree` is not persistent across
> `@reloads`. If you are using a persistent menu (using `EvMenu(..., persistent=True)` you should use
> `@reloads`. If you are using a persistent menu (using `EvMenu(..., persistent=True)` you should
use
> `caller.db` to store in-menu data like this as well. You must then yourself make sure to clean it
> when the user exits the menu.
@ -673,12 +1003,12 @@ node is ok. A common example is a login menu:
def _check_username(caller, raw_string, **kwargs):
# we assume lookup_username() exists
if not lookup_username(raw_string):
# re-run current node by returning `None`
caller.msg("|rUsername not found. Try again.")
return None
# re-run current node by returning `None`
caller.msg("|rUsername not found. Try again.")
return None
else:
# username ok - continue to next node
return "node_password"
# username ok - continue to next node
return "node_password"
def node_username(caller):
@ -692,19 +1022,19 @@ def _check_password(caller, raw_string, **kwargs):
nattempts = kwargs.get("nattempts", 0)
if nattempts > 3:
caller.msg("Too many failed attempts. Logging out")
return "node_abort"
caller.msg("Too many failed attempts. Logging out")
return "node_abort"
elif not validate_password(raw_string):
caller.msg("Password error. Try again.")
return None, {"nattempts", nattempts + 1}
return None, {"nattempts", nattempts + 1}
else:
# password accepted
return "node_login"
# password accepted
return "node_login"
def node_password(caller, raw_string, **kwargs):
text = "Enter your password."
options = {"key": "_default",
"goto": _check_password}
"goto": _check_password}
return text, options
```
@ -885,7 +1215,7 @@ Normally, the `get_input` function quits after any input, but as seen in the exa
return True from the callback to repeat the prompt until you pass whatever check you want.
> Note: You *cannot* link consecutive questions by putting a new `get_input` call inside the
> callback If you want that you should use an EvMenu instead (see the [Repeating the same
> callback. If you want that you should use an EvMenu instead (see the [Repeating the same
> node](EvMenu#example-repeating-the-same-node) example above). Otherwise you can either peek at the
> implementation of `get_input` and implement your own mechanism (it's just using cmdset nesting) or
> you can look at [this extension suggested on the mailing
@ -947,14 +1277,15 @@ _options(caller):
_select(caller, menuchoice, available_choices):
# analyze choice
return "next_node"
return node_matching_the_choice
@list_node(options, select=_select, pagesize=10)
@list_node(_options, select=_select, pagesize=10)
def node_mylist(caller, raw_string, **kwargs):
...
return text, options
# the decorator auto-creates the options; any options
# returned here would be appended to the auto-options
return node_text, {}
```
The `options` argument to `list_node` is either a list, a generator or a callable returning a list