mirror of
https://github.com/evennia/evennia.git
synced 2026-03-18 13:56:30 +01:00
1363 lines
No EOL
126 KiB
HTML
1363 lines
No EOL
126 KiB
HTML
|
||
<!DOCTYPE html>
|
||
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
||
|
||
<title>EvMenu — Evennia 1.0-dev documentation</title>
|
||
<link rel="stylesheet" href="../_static/nature.css" type="text/css" />
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
||
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
|
||
<script src="../_static/jquery.js"></script>
|
||
<script src="../_static/underscore.js"></script>
|
||
<script src="../_static/doctools.js"></script>
|
||
<script src="../_static/language_data.js"></script>
|
||
<script async="async" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"processClass": "tex2jax_process|mathjax_process|math|output_area"}})</script>
|
||
<link rel="shortcut icon" href="../_static/favicon.ico"/>
|
||
<link rel="index" title="Index" href="../genindex.html" />
|
||
<link rel="search" title="Search" href="../search.html" />
|
||
</head><body>
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="General Index"
|
||
accesskey="I">index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia 1.0-dev</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">EvMenu</a></li>
|
||
</ul>
|
||
<div class="develop">develop branch</div>
|
||
</div>
|
||
|
||
<div class="document">
|
||
<div class="documentwrapper">
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<section class="tex2jax_ignore mathjax_ignore" id="evmenu">
|
||
<h1>EvMenu<a class="headerlink" href="#evmenu" title="Permalink to this headline">¶</a></h1>
|
||
<p>EvMenu is used for generate branching multi-choice menus. Each menu ‘node’ can
|
||
accepts specific options as input or free-form input. Depending what the player
|
||
chooses, they are forwarded to different nodes in the menu.</p>
|
||
<section id="introduction">
|
||
<h2>Introduction<a class="headerlink" href="#introduction" title="Permalink to this headline">¶</a></h2>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> utility class is located in <a class="reference internal" href="../api/evennia.utils.evmenu.html#evennia-utils-evmenu"><span class="std std-ref">evennia/utils/evmenu.py</span></a>.
|
||
It allows for easily adding interactive menus to the game; for example to implement Character
|
||
creation, building commands or similar. Below is an example of offering NPC conversation choices:</p>
|
||
<section id="examples">
|
||
<h3>Examples<a class="headerlink" href="#examples" title="Permalink to this headline">¶</a></h3>
|
||
<p>This section gives some examples of how menus work in-game. A menu is a state
|
||
(it’s actually a custom cmdset) where menu-specific commands are made available
|
||
to you. An EvMenu is usually started from inside a command, but could also
|
||
just be put in a file and run with <code class="docutils literal notranslate"><span class="pre">py</span></code>.</p>
|
||
<p>This is how the example menu will look in-game:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>Is your answer yes or no?
|
||
_________________________________________
|
||
[Y]es! - Answer yes.
|
||
[N]o! - Answer no.
|
||
[A]bort - Answer neither, and abort.
|
||
</pre></div>
|
||
</div>
|
||
<p>If you pick (for example) Y(es), you will see</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>You chose yes!
|
||
|
||
Thanks for your answer. Goodbye!
|
||
</pre></div>
|
||
</div>
|
||
<p>After which the menu will end (in this example at least - it could also continue
|
||
on to other questions and choices or even repeat the same node over and over!)</p>
|
||
<p>Here’s the full EvMenu code for this example:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">evmenu</span>
|
||
|
||
<span class="k">def</span> <span class="nf">_handle_answer</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_input</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="n">answer</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"answer"</span><span class="p">)</span>
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You chose </span><span class="si">{</span><span class="n">answer</span><span class="si">}</span><span class="s2">!"</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="s2">"end"</span> <span class="c1"># name of next node</span>
|
||
|
||
<span class="k">def</span> <span class="nf">node_question</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_input</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="s2">"Is your answer yes or no?"</span>
|
||
<span class="n">options</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"[Y]es!"</span><span class="p">,</span> <span class="s2">"yes"</span><span class="p">,</span> <span class="s2">"y"</span><span class="p">),</span>
|
||
<span class="s2">"desc"</span><span class="p">:</span> <span class="n">Answer</span> <span class="n">yes</span><span class="o">.</span><span class="s2">",</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="n">_handle_answer</span><span class="p">,</span> <span class="p">{</span><span class="s2">"answer"</span><span class="p">:</span> <span class="s2">"yes"</span><span class="p">}},</span>
|
||
<span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"[N]o!"</span><span class="p">,</span> <span class="s2">"no"</span><span class="p">,</span> <span class="s2">"n"</span><span class="p">),</span>
|
||
<span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Answer no."</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="n">_handle_answer</span><span class="p">,</span> <span class="p">{</span><span class="s2">"answer"</span><span class="p">:</span> <span class="s2">"no"</span><span class="p">}},</span>
|
||
<span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"[A]bort"</span><span class="p">,</span> <span class="s2">"abort"</span><span class="p">,</span> <span class="s2">"a"</span><span class="p">),</span>
|
||
<span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Answer neither, and abort."</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="s2">"end"</span><span class="p">}</span>
|
||
<span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
<span class="k">def</span> <span class="nf">node_end</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_input</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="n">text</span> <span class="s2">"Thanks for your answer. Goodbye!"</span>
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="kc">None</span> <span class="c1"># empty options ends the menu</span>
|
||
|
||
<span class="n">evmenu</span><span class="o">.</span><span class="n">EvMenu</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="p">{</span><span class="s2">"start"</span><span class="p">:</span> <span class="n">node_question</span><span class="p">,</span> <span class="s2">"end"</span><span class="p">:</span> <span class="n">node_end</span><span class="p">})</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>Note the call to <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> at the end; this immediately creates the menu for the
|
||
<code class="docutils literal notranslate"><span class="pre">caller</span></code>. It also assigns the two node-functions to menu node-names <code class="docutils literal notranslate"><span class="pre">start</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">end</span></code>, which is what the menu then uses to reference the nodes.</p>
|
||
<p>Each node of the menu is a function that returns the text and a list of dicts
|
||
describing the choices you can make on that node.</p>
|
||
<p>Each option details what it should show (key/desc) as well as which node to go
|
||
to (goto) next. The “goto” should be the name of the next node to go (if <code class="docutils literal notranslate"><span class="pre">None</span></code>,
|
||
the same node will be rerun again).</p>
|
||
<p>Above, the <code class="docutils literal notranslate"><span class="pre">Abort</span></code> option gives the “end” node name just as a string whereas the
|
||
yes/no options instead uses the callable <code class="docutils literal notranslate"><span class="pre">_handle_answer</span></code> but pass different
|
||
arguments to it. <code class="docutils literal notranslate"><span class="pre">_handle_answer</span></code> then returns the name of the next node (this
|
||
allows you to perform actions when making a choice before you move on to the
|
||
next node the menu). Note that <code class="docutils literal notranslate"><span class="pre">_handle_answer</span></code> is <em>not</em> a node in the menu,
|
||
it’s just a helper function.</p>
|
||
<p>When choosing ‘yes’ (or ‘no’) what happens here is that <code class="docutils literal notranslate"><span class="pre">_handle_answer</span></code> gets
|
||
called and echoes your choice before directing to the “end” node, which exits
|
||
the menu (since it doesn’t return any options).</p>
|
||
<p>You can also write menus using the <a class="reference internal" href="#evmenu-templating-language"><span class="std std-doc">EvMenu templating language</span></a>. This
|
||
allows you to use a text string to generate simpler menus with less boiler
|
||
plate. Let’s create exactly the same menu using the templating language:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">evmenu</span>
|
||
|
||
<span class="k">def</span> <span class="nf">_handle_answer</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_input</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="n">answer</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"answer"</span><span class="p">)</span>
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You chose </span><span class="si">{</span><span class="n">answer</span><span class="si">}</span><span class="s2">!"</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="s2">"end"</span> <span class="c1"># name of next node</span>
|
||
|
||
<span class="n">menu_template</span> <span class="o">=</span> <span class="s2">"""</span>
|
||
|
||
<span class="s2">## node start</span>
|
||
|
||
<span class="s2">Is your answer yes or no?</span>
|
||
|
||
<span class="s2">## options</span>
|
||
|
||
<span class="s2">[Y]es!;yes;y: Answer yes. -> handle_answer(answer=yes)</span>
|
||
<span class="s2">[N]o!;no;n: Answer no. -> handle_answer(answer=no)</span>
|
||
<span class="s2">[A]bort;abort;a: Answer neither, and abort. -> end</span>
|
||
|
||
<span class="s2">## node end</span>
|
||
|
||
<span class="s2">Thanks for your answer. Goodbye!</span>
|
||
|
||
<span class="s2">"""</span>
|
||
|
||
<span class="n">evmenu</span><span class="o">.</span><span class="n">template2menu</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">menu_template</span><span class="p">,</span> <span class="p">{</span><span class="s2">"handle_answer"</span><span class="p">:</span> <span class="n">_handle_answer</span><span class="p">})</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>As seen, the <code class="docutils literal notranslate"><span class="pre">_handle_answer</span></code> is the same, but the menu structure is
|
||
described in the <code class="docutils literal notranslate"><span class="pre">menu_template</span></code> string. The <code class="docutils literal notranslate"><span class="pre">template2menu</span></code> helper
|
||
uses the template-string and a mapping of callables (we must add
|
||
<code class="docutils literal notranslate"><span class="pre">_handle_answer</span></code> here) to build a full EvMenu for us.</p>
|
||
<p>Here’s another menu example, where we can choose how to interact with an NPC:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">The</span> <span class="n">guard</span> <span class="n">looks</span> <span class="n">at</span> <span class="n">you</span> <span class="n">suspiciously</span><span class="o">.</span>
|
||
<span class="s2">"No one is supposed to be in here ..."</span>
|
||
<span class="n">he</span> <span class="n">says</span><span class="p">,</span> <span class="n">a</span> <span class="n">hand</span> <span class="n">on</span> <span class="n">his</span> <span class="n">weapon</span><span class="o">.</span>
|
||
<span class="n">_______________________________________________</span>
|
||
<span class="mf">1.</span> <span class="n">Try</span> <span class="n">to</span> <span class="n">bribe</span> <span class="n">him</span> <span class="p">[</span><span class="n">Cha</span> <span class="o">+</span> <span class="mi">10</span> <span class="n">gold</span><span class="p">]</span>
|
||
<span class="mf">2.</span> <span class="n">Convince</span> <span class="n">him</span> <span class="n">you</span> <span class="n">work</span> <span class="n">here</span> <span class="p">[</span><span class="n">Int</span><span class="p">]</span>
|
||
<span class="mf">3.</span> <span class="n">Appeal</span> <span class="n">to</span> <span class="n">his</span> <span class="n">vanity</span> <span class="p">[</span><span class="n">Cha</span><span class="p">]</span>
|
||
<span class="mf">4.</span> <span class="n">Try</span> <span class="n">to</span> <span class="n">knock</span> <span class="n">him</span> <span class="n">out</span> <span class="p">[</span><span class="n">Luck</span> <span class="o">+</span> <span class="n">Dex</span><span class="p">]</span>
|
||
<span class="mf">5.</span> <span class="n">Try</span> <span class="n">to</span> <span class="n">run</span> <span class="n">away</span> <span class="p">[</span><span class="n">Dex</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||
<span class="k">def</span> <span class="nf">_skill_check</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="n">skills</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"skills"</span><span class="p">,</span> <span class="p">[])</span>
|
||
<span class="n">gold</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"gold"</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
|
||
|
||
<span class="c1"># perform skill check here, decide if check passed or not</span>
|
||
<span class="c1"># then decide which node-name to return based on</span>
|
||
<span class="c1"># the result ...</span>
|
||
|
||
<span class="k">return</span> <span class="n">next_node_name</span>
|
||
|
||
<span class="k">def</span> <span class="nf">node_guard</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwarg</span><span class="p">):</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="s1">'The guard looks at you suspiciously.</span><span class="se">\n</span><span class="s1">'</span>
|
||
<span class="s1">'"No one is supposed to be in here ..."</span><span class="se">\n</span><span class="s1">'</span>
|
||
<span class="s1">'he says, a hand on his weapon.'</span>
|
||
<span class="n">options</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="p">{</span><span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Try to bribe on [Cha + 10 gold]"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_skill_check</span><span class="p">,</span> <span class="p">{</span><span class="s2">"skills"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"Cha"</span><span class="p">],</span> <span class="s2">"gold"</span><span class="p">:</span> <span class="mi">10</span><span class="p">})},</span>
|
||
<span class="p">{</span><span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Convince him you work here [Int]."</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_skill_check</span><span class="p">,</span> <span class="p">{</span><span class="s2">"skills"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"Int"</span><span class="p">]})},</span>
|
||
<span class="p">{</span><span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Appeal to his vanity [Cha]"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_skill_check</span><span class="p">,</span> <span class="p">{</span><span class="s2">"skills"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"Cha"</span><span class="p">]})},</span>
|
||
<span class="p">{</span><span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Try to knock him out [Luck + Dex]"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_skill_check</span><span class="p">,</span> <span class="p">{</span><span class="s2">"skills"" ["</span><span class="n">Luck</span><span class="s2">", "</span><span class="n">Dex</span><span class="s2">"]})},</span>
|
||
<span class="p">{</span><span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Try to run away [Dex]"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_skill_check</span><span class="p">,</span> <span class="p">{</span><span class="s2">"skills"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"Dex"</span><span class="p">]})}</span>
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="c1"># EvMenu called below, with all the nodes ...</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that by skipping the <code class="docutils literal notranslate"><span class="pre">key</span></code> of the options, we instead get an
|
||
(auto-generated) list of numbered options to choose from.</p>
|
||
<p>Here the <code class="docutils literal notranslate"><span class="pre">_skill_check</span></code> helper will check (roll your stats, exactly what this
|
||
means depends on your game) to decide if your approach succeeded. It may then
|
||
choose to point you to nodes that continue the conversation or maybe dump you
|
||
into combat!</p>
|
||
</section>
|
||
</section>
|
||
<section id="launching-the-menu">
|
||
<h2>Launching the menu<a class="headerlink" href="#launching-the-menu" title="Permalink to this headline">¶</a></h2>
|
||
<p>Initializing the menu is done using a call to the <code class="docutils literal notranslate"><span class="pre">evennia.utils.evmenu.EvMenu</span></code> class. This is the
|
||
most common way to do so - from inside a <a class="reference internal" href="Commands.html"><span class="doc std std-doc">Command</span></a>:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in, for example gamedir/commands/command.py</span>
|
||
|
||
<span class="kn">from</span> <span class="nn">evennia.utils.evmenu</span> <span class="kn">import</span> <span class="n">EvMenu</span>
|
||
|
||
<span class="k">class</span> <span class="nc">CmdTestMenu</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"testcommand"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
|
||
<span class="n">EvMenu</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="s2">"world.mymenu"</span><span class="p">)</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>When running this command, the menu will start using the menu nodes loaded from
|
||
<code class="docutils literal notranslate"><span class="pre">mygame/world/mymenu.py</span></code>. See next section on how to define menu nodes.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> has the following optional callsign:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">EvMenu</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">menu_data</span><span class="p">,</span>
|
||
<span class="n">startnode</span><span class="o">=</span><span class="s2">"start"</span><span class="p">,</span>
|
||
<span class="n">cmdset_mergetype</span><span class="o">=</span><span class="s2">"Replace"</span><span class="p">,</span> <span class="n">cmdset_priority</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="n">auto_quit</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">auto_look</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">auto_help</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||
<span class="n">cmd_on_exit</span><span class="o">=</span><span class="s2">"look"</span><span class="p">,</span>
|
||
<span class="n">persistent</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||
<span class="n">startnode_input</span><span class="o">=</span><span class="s2">""</span><span class="p">,</span>
|
||
<span class="n">session</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||
<span class="n">debug</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||
<span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">caller</span></code> (Object or Account): is a reference to the object using the menu. This object will get a
|
||
new <a class="reference internal" href="Command-Sets.html"><span class="doc std std-doc">CmdSet</span></a> assigned to it, for handling the menu.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">menu_data</span></code> (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
|
||
<code class="docutils literal notranslate"><span class="pre">_</span></code> will be ignored by the loader. Alternatively, this can be a direct mapping
|
||
<code class="docutils literal notranslate"><span class="pre">{"nodename":function,</span> <span class="pre">...}</span></code>.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">startnode</span></code> (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.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">cmdset_mergetype</span></code> (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
|
||
them, so be careful as to what to name your menu entries in this case).</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">cmdset_priority</span></code> (int): The priority with which to merge in the menu cmdset. This allows for
|
||
advanced usage.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">auto_quit</span></code>, <code class="docutils literal notranslate"><span class="pre">auto_look</span></code>, <code class="docutils literal notranslate"><span class="pre">auto_help</span></code> (bool): If either of these are <code class="docutils literal notranslate"><span class="pre">True</span></code>, the menu
|
||
automatically makes a <code class="docutils literal notranslate"><span class="pre">quit</span></code>, <code class="docutils literal notranslate"><span class="pre">look</span></code> or <code class="docutils literal notranslate"><span class="pre">help</span></code> 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 <code class="docutils literal notranslate"><span class="pre">quit</span></code> is highly recommend - if <code class="docutils literal notranslate"><span class="pre">False</span></code>, the menu <em>must</em> 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 <code class="docutils literal notranslate"><span class="pre">persistent</span></code>)!</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">cmd_on_exit</span></code> (str): This command string will be executed right <em>after</em> 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 <code class="docutils literal notranslate"><span class="pre">None</span></code>, no command will be triggered
|
||
after
|
||
exiting the menu.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">persistent</span></code> (bool) - if <code class="docutils literal notranslate"><span class="pre">True</span></code>, 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!)</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">startnode_input</span></code> (str or (str, dict) tuple): Pass an input text or a input text + kwargs to the
|
||
start node as if it was entered on a fictional previous node. This can be very useful in order to
|
||
start a menu differently depending on the Command’s arguments in which it was initialized.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">session</span></code> (Session): Useful when calling the menu from an <a class="reference internal" href="Accounts.html"><span class="doc std std-doc">Account</span></a> in
|
||
<code class="docutils literal notranslate"><span class="pre">MULTISESSION_MODDE</span></code> higher than 2, to make sure only the right Session sees the menu output.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">debug</span></code> (bool): If set, the <code class="docutils literal notranslate"><span class="pre">menudebug</span></code> command will be made available in the menu. Use it to
|
||
list the current state of the menu and use <code class="docutils literal notranslate"><span class="pre">menudebug</span> <span class="pre"><variable></span></code> to inspect a specific state
|
||
variable from the list.</p></li>
|
||
<li><p>All other keyword arguments will be available as initial data for the nodes. They will be
|
||
available in all nodes as properties on <code class="docutils literal notranslate"><span class="pre">caller.ndb._menutree</span></code> (see below). These will also
|
||
survive a <code class="docutils literal notranslate"><span class="pre">@reload</span></code> if the menu is <code class="docutils literal notranslate"><span class="pre">persistent</span></code>.</p></li>
|
||
</ul>
|
||
<p>You don’t need to store the EvMenu instance anywhere - the very act of initializing it will store it
|
||
as <code class="docutils literal notranslate"><span class="pre">caller.ndb._menutree</span></code> on the <code class="docutils literal notranslate"><span class="pre">caller</span></code>. This object will be deleted automatically when the menu
|
||
is exited and you can also use it to store your own temporary variables for access throughout the
|
||
menu. Temporary variables you store on a persistent <code class="docutils literal notranslate"><span class="pre">_menutree</span></code> as it runs will
|
||
<em>not</em> survive a <code class="docutils literal notranslate"><span class="pre">@reload</span></code>, only those you set as part of the original <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> call.</p>
|
||
</section>
|
||
<section id="the-menu-nodes">
|
||
<h2>The Menu nodes<a class="headerlink" href="#the-menu-nodes" title="Permalink to this headline">¶</a></h2>
|
||
<p>The EvMenu nodes consist of functions on one of these forms.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">menunodename1</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
|
||
<span class="c1"># code</span>
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
<span class="k">def</span> <span class="nf">menunodename2</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">):</span>
|
||
<span class="c1"># code</span>
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
<span class="k">def</span> <span class="nf">menunodename3</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="c1"># code</span>
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<blockquote>
|
||
<div><p>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.</p>
|
||
</div></blockquote>
|
||
<section id="input-arguments-to-the-node">
|
||
<h3>Input arguments to the node<a class="headerlink" href="#input-arguments-to-the-node" title="Permalink to this headline">¶</a></h3>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">caller</span></code> (Object or Account): The object using the menu - usually a Character but could also be a
|
||
Session or Account depending on where the menu is used.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">raw_string</span></code> (str): If this is given, it will be set to the exact text the user entered on the
|
||
<em>previous</em> node (that is, the command entered to get to this node). On the starting-node of the
|
||
menu, this will be an empty string, unless <code class="docutils literal notranslate"><span class="pre">startnode_input</span></code> was set.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">kwargs</span></code> (dict): These extra keyword arguments are extra optional arguments passed to the node
|
||
when the user makes a choice on the <em>previous</em> node. This may include things like status flags
|
||
and details about which exact option was chosen (which can be impossible to determine from
|
||
<code class="docutils literal notranslate"><span class="pre">raw_string</span></code> alone). Just what is passed in <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> is up to you when you create the previous
|
||
node.</p></li>
|
||
</ul>
|
||
</section>
|
||
<section id="return-values-from-the-node">
|
||
<h3>Return values from the node<a class="headerlink" href="#return-values-from-the-node" title="Permalink to this headline">¶</a></h3>
|
||
<p>Each function must return two variables, <code class="docutils literal notranslate"><span class="pre">text</span></code> and <code class="docutils literal notranslate"><span class="pre">options</span></code>.</p>
|
||
<section id="text">
|
||
<h4>text<a class="headerlink" href="#text" title="Permalink to this headline">¶</a></h4>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">text</span></code> variable is a string or tuple. This text is what will be displayed when the user reaches
|
||
this node. If this is a tuple, then the first element of the tuple will be considered the displayed
|
||
text and the second the help-text to display when the user enters the <code class="docutils literal notranslate"><span class="pre">help</span></code> command on this node.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="n">text</span> <span class="o">=</span> <span class="p">(</span><span class="s2">"This is the text to display"</span><span class="p">,</span> <span class="s2">"This is the help text for this node"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Returning a <code class="docutils literal notranslate"><span class="pre">None</span></code> text is allowed and simply leads to a node with no text and only options. If the
|
||
help text is not given, the menu will give a generic error message when using <code class="docutils literal notranslate"><span class="pre">help</span></code>.</p>
|
||
</section>
|
||
<section id="options">
|
||
<h4>options<a class="headerlink" href="#options" title="Permalink to this headline">¶</a></h4>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">options</span></code> list describe all the choices available to the user when viewing this node. If
|
||
<code class="docutils literal notranslate"><span class="pre">options</span></code> is
|
||
returned as <code class="docutils literal notranslate"><span class="pre">None</span></code>, it means that this node is an <em>Exit node</em> - any text is displayed and then the
|
||
menu immediately exits, running the <code class="docutils literal notranslate"><span class="pre">exit_cmd</span></code> if given.</p>
|
||
<p>Otherwise, <code class="docutils literal notranslate"><span class="pre">options</span></code> 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:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">node_test</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
|
||
<span class="n">text</span> <span class="o">=</span> <span class="s2">"A goblin attacks you!"</span>
|
||
|
||
<span class="n">options</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"Attack"</span><span class="p">,</span> <span class="s2">"a"</span><span class="p">,</span> <span class="s2">"att"</span><span class="p">),</span>
|
||
<span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Strike the enemy with all your might"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="s2">"node_attack"</span><span class="p">},</span>
|
||
<span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"Defend"</span><span class="p">,</span> <span class="s2">"d"</span><span class="p">,</span> <span class="s2">"def"</span><span class="p">),</span>
|
||
<span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Hold back and defend yourself"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_defend</span><span class="p">,</span> <span class="p">{</span><span class="s2">"str"</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="s2">"enemyname"</span><span class="p">:</span> <span class="s2">"Goblin"</span><span class="p">})})</span>
|
||
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>This will produce a menu node looking like this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>A goblin attacks you!
|
||
________________________________
|
||
|
||
Attack: Strike the enemy with all your might
|
||
Defend: Hold back and defend yourself
|
||
|
||
</pre></div>
|
||
</div>
|
||
<section id="option-key-key">
|
||
<h5>option-key ‘key’<a class="headerlink" href="#option-key-key" title="Permalink to this headline">¶</a></h5>
|
||
<p>The option’s <code class="docutils literal notranslate"><span class="pre">key</span></code> 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
|
||
coloring to the choice. The first element of the aliasing tuple should then be the colored version,
|
||
followed by a version without color - since otherwise the user would have to enter the color codes
|
||
to select that choice.</p>
|
||
<p>Note that the <code class="docutils literal notranslate"><span class="pre">key</span></code> is <em>optional</em>. If no key is given, it will instead automatically be replaced
|
||
with a running number starting from <code class="docutils literal notranslate"><span class="pre">1</span></code>. If removing the <code class="docutils literal notranslate"><span class="pre">key</span></code> part of each option, the resulting
|
||
menu node would look like this instead:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>A goblin attacks you!
|
||
________________________________
|
||
|
||
1: Strike the enemy with all your might
|
||
2: Hold back and defend yourself
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>Whether you want to use a key or rely on numbers is mostly
|
||
a matter of style and the type of menu.</p>
|
||
<p>EvMenu accepts one important special <code class="docutils literal notranslate"><span class="pre">key</span></code> given only as <code class="docutils literal notranslate"><span class="pre">"_default"</span></code>. This key is used when a user
|
||
enters something that does not match any other fixed keys. It is particularly useful for getting
|
||
user input:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">node_readuser</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="s2">"Please enter your name"</span>
|
||
|
||
<span class="n">options</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"_default"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="s2">"node_parse_input"</span><span class="p">}</span>
|
||
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>A <code class="docutils literal notranslate"><span class="pre">"_default"</span></code> option does not show up in the menu, so the above will just be a node saying
|
||
<code class="docutils literal notranslate"><span class="pre">"Please</span> <span class="pre">enter</span> <span class="pre">your</span> <span class="pre">name"</span></code>. The name they entered will appear as <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> in the next node.</p>
|
||
</section>
|
||
</section>
|
||
<section id="option-key-desc">
|
||
<h4>option-key ‘desc’<a class="headerlink" href="#option-key-desc" title="Permalink to this headline">¶</a></h4>
|
||
<p>This simply contains the description as to what happens when selecting the menu option. For
|
||
<code class="docutils literal notranslate"><span class="pre">"_default"</span></code> options or if the <code class="docutils literal notranslate"><span class="pre">key</span></code> is already long or descriptive, it is not strictly needed. But
|
||
usually it’s better to keep the <code class="docutils literal notranslate"><span class="pre">key</span></code> short and put more detail in <code class="docutils literal notranslate"><span class="pre">desc</span></code>.</p>
|
||
</section>
|
||
<section id="option-key-goto">
|
||
<h4>option-key ‘goto’<a class="headerlink" href="#option-key-goto" title="Permalink to this headline">¶</a></h4>
|
||
<p>This is the operational part of the option and fires only when the user chooses said option. Here
|
||
are three ways to write it</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||
<span class="k">def</span> <span class="nf">_action_two</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="c1"># do things ...</span>
|
||
<span class="k">return</span> <span class="s2">"calculated_node_to_go_to"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">_action_three</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="c1"># do things ...</span>
|
||
<span class="k">return</span> <span class="s2">"node_four"</span><span class="p">,</span> <span class="p">{</span><span class="s2">"mode"</span><span class="p">:</span> <span class="mi">4</span><span class="p">}</span>
|
||
|
||
<span class="k">def</span> <span class="nf">node_select</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
|
||
<span class="n">text</span> <span class="o">=</span> <span class="p">(</span><span class="s2">"select one"</span><span class="p">,</span>
|
||
<span class="s2">"help - they all do different things ..."</span><span class="p">)</span>
|
||
|
||
<span class="n">options</span> <span class="o">=</span> <span class="p">({</span><span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Option one"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="s2">"node_one"</span><span class="p">},</span>
|
||
<span class="p">{</span><span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Option two"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="n">_action_two</span><span class="p">},</span>
|
||
<span class="p">{</span><span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Option three"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_action_three</span><span class="p">,</span> <span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">"key2"</span><span class="p">:</span> <span class="mi">2</span><span class="p">})}</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>As seen above, <code class="docutils literal notranslate"><span class="pre">goto</span></code> could just be pointing to a single <code class="docutils literal notranslate"><span class="pre">nodename</span></code> string - the name of the node to
|
||
go to. When given like this, EvMenu will look for a node named like this and call its associated
|
||
function as</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="n">nodename</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Here, <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> is always the input the user entered to make that choice and <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> are the
|
||
same as those <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> that already entered the <em>current</em> node (they are passed on).</p>
|
||
<p>Alternatively the <code class="docutils literal notranslate"><span class="pre">goto</span></code> could point to a “goto-callable”. Such callables are usually defined in the
|
||
same
|
||
module as the menu nodes and given names starting with <code class="docutils literal notranslate"><span class="pre">_</span></code> (to avoid being parsed as nodes
|
||
themselves). These callables will be called the same as a node function - <code class="docutils literal notranslate"><span class="pre">callable(caller,</span> <span class="pre">raw_string,</span> <span class="pre">**kwargs)</span></code>, where <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> is what the user entered on this node and <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> is
|
||
forwarded from the node’s own input.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">goto</span></code> option key could also point to a tuple <code class="docutils literal notranslate"><span class="pre">(callable,</span> <span class="pre">kwargs)</span></code> - this allows for customizing
|
||
the kwargs passed into the goto-callable, for example you could use the same callable but change the
|
||
kwargs passed into it depending on which option was actually chosen.</p>
|
||
<p>The “goto callable” must either return a string <code class="docutils literal notranslate"><span class="pre">"nodename"</span></code> or a tuple <code class="docutils literal notranslate"><span class="pre">("nodename",</span> <span class="pre">mykwargs)</span></code>.
|
||
This will lead to the next node being called as either <code class="docutils literal notranslate"><span class="pre">nodename(caller,</span> <span class="pre">raw_string,</span> <span class="pre">**kwargs)</span></code> or
|
||
<code class="docutils literal notranslate"><span class="pre">nodename(caller,</span> <span class="pre">raw_string,</span> <span class="pre">**mykwargs)</span></code> - so this allows changing (or replacing) the options
|
||
going
|
||
into the next node depending on what option was chosen.</p>
|
||
<p>There is one important case - if the goto-callable returns <code class="docutils literal notranslate"><span class="pre">None</span></code> for a <code class="docutils literal notranslate"><span class="pre">nodename</span></code>, <em>the current
|
||
node will run again</em>, possibly with different kwargs. This makes it very easy to re-use a node over
|
||
and over, for example allowing different options to update some text form being passed and
|
||
manipulated for every iteration.</p>
|
||
<blockquote>
|
||
<div><p>The EvMenu also supports the <code class="docutils literal notranslate"><span class="pre">exec</span></code> option key. This allows for running a callable <em>before</em> the
|
||
goto-callable. This functionality comes from a time before goto could be a callable and is
|
||
<em>deprecated</em> as of Evennia 0.8. Use <code class="docutils literal notranslate"><span class="pre">goto</span></code> for all functionality where you’d before use <code class="docutils literal notranslate"><span class="pre">exec</span></code>.</p>
|
||
</div></blockquote>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
<section id="temporary-storage">
|
||
<h2>Temporary storage<a class="headerlink" href="#temporary-storage" title="Permalink to this headline">¶</a></h2>
|
||
<p>When the menu starts, the EvMenu instance is stored on the caller as <code class="docutils literal notranslate"><span class="pre">caller.ndb._menutree</span></code>. Through
|
||
this object you can in principle reach the menu’s internal state if you know what you are doing.
|
||
This is also a good place to store temporary, more global variables that may be cumbersome to keep
|
||
passing from node to node via the <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>. The <code class="docutils literal notranslate"><span class="pre">_menutree</span></code> will be deleted automatically when the
|
||
menu closes, meaning you don’t need to worry about cleaning anything up.</p>
|
||
<p>If you want <em>permanent</em> state storage, it’s instead better to use an Attribute on <code class="docutils literal notranslate"><span class="pre">caller</span></code>. Remember
|
||
that this will remain after the menu closes though, so you need to handle any needed cleanup
|
||
yourself.</p>
|
||
</section>
|
||
<section id="customizing-menu-formatting">
|
||
<h2>Customizing Menu formatting<a class="headerlink" href="#customizing-menu-formatting" title="Permalink to this headline">¶</a></h2>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> display of nodes, options etc are controlled by a series of formatting methods on the
|
||
<code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> class. To customize these, simply create a new child class of <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> and override as
|
||
needed. Here is an example:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia.utils.evmenu</span> <span class="kn">import</span> <span class="n">EvMenu</span>
|
||
|
||
<span class="k">class</span> <span class="nc">MyEvMenu</span><span class="p">(</span><span class="n">EvMenu</span><span class="p">):</span>
|
||
|
||
<span class="k">def</span> <span class="nf">nodetext_formatter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nodetext</span><span class="p">):</span>
|
||
<span class="sd">"""</span>
|
||
<span class="sd"> Format the node text itself.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> nodetext (str): The full node text (the text describing the node).</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> nodetext (str): The formatted node text.</span>
|
||
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="k">def</span> <span class="nf">helptext_formatter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">helptext</span><span class="p">):</span>
|
||
<span class="sd">"""</span>
|
||
<span class="sd"> Format the node's help text</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> helptext (str): The unformatted help text for the node.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> helptext (str): The formatted help text.</span>
|
||
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="k">def</span> <span class="nf">options_formatter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">optionlist</span><span class="p">):</span>
|
||
<span class="sd">"""</span>
|
||
<span class="sd"> Formats the option block.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> optionlist (list): List of (key, description) tuples for every</span>
|
||
<span class="sd"> option related to this node.</span>
|
||
<span class="sd"> caller (Object, Account or None, optional): The caller of the node.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> options (str): The formatted option display.</span>
|
||
|
||
<span class="sd"> """</span>
|
||
|
||
<span class="k">def</span> <span class="nf">node_formatter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">nodetext</span><span class="p">,</span> <span class="n">optionstext</span><span class="p">):</span>
|
||
<span class="sd">"""</span>
|
||
<span class="sd"> Formats the entirety of the node.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> nodetext (str): The node text as returned by `self.nodetext_formatter`.</span>
|
||
<span class="sd"> optionstext (str): The options display as returned by `self.options_formatter`.</span>
|
||
<span class="sd"> caller (Object, Account or None, optional): The caller of the node.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> node (str): The formatted node to display.</span>
|
||
|
||
<span class="sd"> """</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>See <code class="docutils literal notranslate"><span class="pre">evennia/utils/evmenu.py</span></code> for the details of their default implementations.</p>
|
||
</section>
|
||
<section id="evmenu-templating-language">
|
||
<h2>EvMenu templating language<a class="headerlink" href="#evmenu-templating-language" title="Permalink to this headline">¶</a></h2>
|
||
<p>In <a class="reference external" href="http://evmenu.py">evmenu.py</a> are two helper functions <code class="docutils literal notranslate"><span class="pre">parse_menu_template</span></code> and <code class="docutils literal notranslate"><span class="pre">template2menu</span></code>
|
||
that is used to parse a <em>menu template</em> string into an EvMenu:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evmenu.template2menu(caller, menu_template, goto_callables)
|
||
</pre></div>
|
||
</div>
|
||
<p>One can also do it in two steps, by generate a menutree and using that to call
|
||
EvMenu normally:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>menutree = evmenu.parse_menu_template(caller, menu_template, goto_callables)
|
||
EvMenu(caller, menutree)
|
||
</pre></div>
|
||
</div>
|
||
<p>With this latter solution, one could mix and match normally created menu nodes
|
||
with those generated by the template engine.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">goto_callables</span></code> is a mapping <code class="docutils literal notranslate"><span class="pre">{"funcname":</span> <span class="pre">callable,</span> <span class="pre">...}</span></code>, where each
|
||
callable must be a module-global function on the form
|
||
<code class="docutils literal notranslate"><span class="pre">funcname(caller,</span> <span class="pre">raw_string,</span> <span class="pre">**kwargs)</span></code> (like any goto-callable). The
|
||
<code class="docutils literal notranslate"><span class="pre">menu_template</span></code> is a multi-line string on the following form:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">menu_template</span> <span class="o">=</span> <span class="s2">"""</span>
|
||
|
||
<span class="s2">## node node1</span>
|
||
|
||
<span class="s2">Text for node</span>
|
||
|
||
<span class="s2">## options</span>
|
||
|
||
<span class="s2">key1: desc1 -> node2</span>
|
||
<span class="s2">key2: desc2 -> node3</span>
|
||
<span class="s2">key3: desc3 -> node4</span>
|
||
<span class="s2">"""</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Each menu node is defined by a <code class="docutils literal notranslate"><span class="pre">##</span> <span class="pre">node</span> <span class="pre"><name></span></code> containing the text of the node,
|
||
followed by <code class="docutils literal notranslate"><span class="pre">##</span> <span class="pre">options</span></code> Also <code class="docutils literal notranslate"><span class="pre">##</span> <span class="pre">NODE</span></code> and <code class="docutils literal notranslate"><span class="pre">##</span> <span class="pre">OPTIONS</span></code> work. No python code
|
||
logics is allowed in the template, this code is not evaluated but parsed. More
|
||
advanced dynamic usage requires a full node-function.</p>
|
||
<p>Except for defining the node/options, <code class="docutils literal notranslate"><span class="pre">#</span></code> act as comments - everything following
|
||
will be ignored by the template parser.</p>
|
||
<section id="template-options">
|
||
<h3>Template Options<a class="headerlink" href="#template-options" title="Permalink to this headline">¶</a></h3>
|
||
<p>The option syntax is</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span><key>: [desc ->] nodename or function-call
|
||
</pre></div>
|
||
</div>
|
||
<p>The ‘desc’ part is optional, and if that is not given, the <code class="docutils literal notranslate"><span class="pre">-></span></code> can be skipped
|
||
too:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>key: nodename
|
||
</pre></div>
|
||
</div>
|
||
<p>The key can both be strings and numbers. Separate the aliases with <code class="docutils literal notranslate"><span class="pre">;</span></code>.</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>key: node1
|
||
1: node2
|
||
key;k: node3
|
||
foobar;foo;bar;f;b: node4
|
||
</pre></div>
|
||
</div>
|
||
<p>Starting the key with the special letter <code class="docutils literal notranslate"><span class="pre">></span></code> indicates that what follows is a
|
||
glob/regex matcher.</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>>: node1 - matches empty input
|
||
> foo*: node1 - everything starting with foo
|
||
> *foo: node3 - everything ending with foo
|
||
> [0-9]+?: node4 - regex (all numbers)
|
||
> *: node5 - catches everything else (put as last option)
|
||
</pre></div>
|
||
</div>
|
||
<p>Here’s how to call a goto-function from an option:</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>key: desc -> myfunc(foo=bar)
|
||
</pre></div>
|
||
</div>
|
||
<p>For this to work <code class="docutils literal notranslate"><span class="pre">template2menu</span></code> or <code class="docutils literal notranslate"><span class="pre">parse_menu_template</span></code> must be given a dict
|
||
that includes <code class="docutils literal notranslate"><span class="pre">{"myfunc":</span> <span class="pre">_actual_myfunc_callable}</span></code>. All callables to be
|
||
available in the template must be mapped this way. Goto callables act like
|
||
normal EvMenu goto-callables and should have a callsign of
|
||
<code class="docutils literal notranslate"><span class="pre">_actual_myfunc_callable(caller,</span> <span class="pre">raw_string,</span> <span class="pre">**kwargs)</span></code> and return the next node
|
||
(passing dynamic kwargs into the next node does not work with the template</p>
|
||
<ul class="simple">
|
||
<li><p>use the full EvMenu if you want advanced dynamic data passing).</p></li>
|
||
</ul>
|
||
<p>Only no or named keywords are allowed in these callables. So</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>myfunc() # OK
|
||
myfunc(foo=bar) # OK
|
||
myfunc(foo) # error!
|
||
</pre></div>
|
||
</div>
|
||
<p>This is because these properties are passed as <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> into the goto callable.</p>
|
||
</section>
|
||
<section id="templating-example">
|
||
<h3>Templating example<a class="headerlink" href="#templating-example" title="Permalink to this headline">¶</a></h3>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">random</span>
|
||
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">evmenu</span>
|
||
|
||
<span class="k">def</span> <span class="nf">_gamble</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You roll the dice ..."</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">random</span><span class="p">()</span> <span class="o"><</span> <span class="mf">0.5</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="s2">"loose"</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="s2">"win"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">_try_again</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="kc">None</span> <span class="c1"># reruns the same node</span>
|
||
|
||
<span class="n">template_string</span> <span class="o">=</span> <span class="s2">"""</span>
|
||
|
||
<span class="s2">## node start</span>
|
||
|
||
<span class="s2">Death patiently holds out a set of bone dice to you.</span>
|
||
|
||
<span class="s2">"ROLL"</span>
|
||
|
||
<span class="s2">he says.</span>
|
||
|
||
<span class="s2">## options</span>
|
||
|
||
<span class="s2">1. Roll the dice -> gamble()</span>
|
||
<span class="s2">2. Try to talk yourself out of rolling -> ask_again()</span>
|
||
|
||
<span class="s2">## node win</span>
|
||
|
||
<span class="s2">The dice clatter over the stones.</span>
|
||
|
||
<span class="s2">"LOOKS LIKE YOU WIN THIS TIME"</span>
|
||
|
||
<span class="s2">says Death.</span>
|
||
|
||
<span class="s2"># (this ends the menu since there are no options)</span>
|
||
|
||
<span class="s2">## node loose</span>
|
||
|
||
<span class="s2">The dice clatter over the stones.</span>
|
||
|
||
<span class="s2">"YOUR LUCK RAN OUT"</span>
|
||
|
||
<span class="s2">says Death.</span>
|
||
|
||
<span class="s2">"YOU ARE COMING WITH ME."</span>
|
||
|
||
<span class="s2"># (this ends the menu, but what happens next - who knows!)</span>
|
||
|
||
<span class="s2">"""</span>
|
||
|
||
<span class="n">goto_callables</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"gamble"</span><span class="p">:</span> <span class="n">_gamble</span><span class="p">,</span> <span class="s2">"ask_again"</span><span class="p">:</span> <span class="n">_ask_again</span><span class="p">}</span>
|
||
<span class="n">evmenu</span><span class="o">.</span><span class="n">template2menu</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">template_string</span><span class="p">,</span> <span class="n">goto_callables</span><span class="p">)</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="id1">
|
||
<h2>Examples:<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h2>
|
||
<ul class="simple">
|
||
<li><p><strong><a class="reference internal" href="#example-simple-branching-menu"><span class="std std-doc">Simple branching menu</span></a></strong> - choose from options</p></li>
|
||
<li><p><strong><a class="reference internal" href="#example-dynamic-goto"><span class="std std-doc">Dynamic goto</span></a></strong> - jumping to different nodes based on response</p></li>
|
||
<li><p><strong><a class="reference internal" href="#example-set-caller-properties"><span class="std std-doc">Set caller properties</span></a></strong> - a menu that changes things</p></li>
|
||
<li><p><strong><a class="reference internal" href="#example-get-arbitrary-input"><span class="std std-doc">Getting arbitrary input</span></a></strong> - entering text</p></li>
|
||
<li><p><strong><a class="reference internal" href="#example-storing-data-between-nodes"><span class="std std-doc">Storing data between nodes</span></a></strong> - keeping states and
|
||
information while in the menu</p></li>
|
||
<li><p><strong><a class="reference internal" href="#example-repeating-the-same-node"><span class="std std-doc">Repeating the same node</span></a></strong> - validating within the node
|
||
before moving to the next</p></li>
|
||
<li><p><strong><a class="reference internal" href="#example-yes-no-prompt"><span class="std std-doc">Yes/No prompt</span></a></strong> - entering text with limited possible responses
|
||
(this is <em>not</em> using EvMenu but the conceptually similar yet technically unrelated <code class="docutils literal notranslate"><span class="pre">get_input</span></code>
|
||
helper function accessed as <code class="docutils literal notranslate"><span class="pre">evennia.utils.evmenu.get_input</span></code>).</p></li>
|
||
</ul>
|
||
<section id="example-simple-branching-menu">
|
||
<h3>Example: Simple branching menu<a class="headerlink" href="#example-simple-branching-menu" title="Permalink to this headline">¶</a></h3>
|
||
<p>Below is an example of a simple branching menu node leading to different other nodes depending on
|
||
choice:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/world/mychargen.py</span>
|
||
|
||
<span class="k">def</span> <span class="nf">define_character</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
|
||
<span class="n">text</span> <span class="o">=</span> \
|
||
<span class="sd">"""</span>
|
||
<span class="sd"> What aspect of your character do you want</span>
|
||
<span class="sd"> to change next?</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">options</span> <span class="o">=</span> <span class="p">({</span><span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Change the name"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="s2">"set_name"</span><span class="p">},</span>
|
||
<span class="p">{</span><span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Change the description"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="s2">"set_description"</span><span class="p">})</span>
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
<span class="n">EvMenu</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="s2">"world.mychargen"</span><span class="p">,</span> <span class="n">startnode</span><span class="o">=</span><span class="s2">"define_character"</span><span class="p">)</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>This will result in the following node display:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>What aspect of your character do you want
|
||
to change next?
|
||
_________________________
|
||
1: Change the name
|
||
2: Change the description
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that since we didn’t specify the “name” key, EvMenu will let the user enter numbers instead. In
|
||
the following examples we will not include the <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> call but just show nodes running inside the
|
||
menu. Also, since <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> also takes a dictionary to describe the menu, we could have called it
|
||
like this instead in the example:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">EvMenu</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="p">{</span><span class="s2">"define_character"</span><span class="p">:</span> <span class="n">define_character</span><span class="p">},</span> <span class="n">startnode</span><span class="o">=</span><span class="s2">"define_character"</span><span class="p">)</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="example-dynamic-goto">
|
||
<h3>Example: Dynamic goto<a class="headerlink" href="#example-dynamic-goto" title="Permalink to this headline">¶</a></h3>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||
<span class="k">def</span> <span class="nf">_is_in_mage_guild</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">caller</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'mage'</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"guild_member"</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="s2">"mage_guild_welcome"</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="s2">"mage_guild_blocked"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">enter_guild</span><span class="p">:</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="s1">'You say to the mage guard:'</span>
|
||
<span class="n">options</span> <span class="p">({</span><span class="s1">'desc'</span><span class="p">:</span> <span class="s1">'I need to get in there.'</span><span class="p">,</span>
|
||
<span class="s1">'goto'</span><span class="p">:</span> <span class="n">_is_in_mage_guild</span><span class="p">},</span>
|
||
<span class="p">{</span><span class="s1">'desc'</span><span class="p">:</span> <span class="s1">'Never mind'</span><span class="p">,</span>
|
||
<span class="s1">'goto'</span><span class="p">:</span> <span class="s1">'end_conversation'</span><span class="p">})</span>
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This simple callable goto will analyse what happens depending on who the <code class="docutils literal notranslate"><span class="pre">caller</span></code> is. The
|
||
<code class="docutils literal notranslate"><span class="pre">enter_guild</span></code> node will give you a choice of what to say to the guard. If you try to enter, you will
|
||
end up in different nodes depending on (in this example) if you have the right <a class="reference internal" href="Tags.html"><span class="doc std std-doc">Tag</span></a> set on
|
||
yourself or not. Note that since we don’t include any ‘key’s in the option dictionary, you will just
|
||
get to pick between numbers.</p>
|
||
</section>
|
||
<section id="example-set-caller-properties">
|
||
<h3>Example: Set caller properties<a class="headerlink" href="#example-set-caller-properties" title="Permalink to this headline">¶</a></h3>
|
||
<p>Here is an example of passing arguments into the <code class="docutils literal notranslate"><span class="pre">goto</span></code> callable and use that to influence
|
||
which node it should go to next:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||
<span class="k">def</span> <span class="nf">_set_attribute</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="s2">"Get which attribute to modify and set it"</span>
|
||
|
||
<span class="n">attrname</span><span class="p">,</span> <span class="n">value</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"attr"</span><span class="p">,</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">))</span>
|
||
<span class="n">next_node</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"next_node"</span><span class="p">)</span>
|
||
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">attrname</span><span class="p">,</span> <span class="n">attrvalue</span><span class="p">)</span>
|
||
|
||
<span class="k">return</span> <span class="n">next_node</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">node_background</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
|
||
<span class="n">text</span> <span class="o">=</span> \
|
||
<span class="sa">f</span><span class="s2">"""</span>
|
||
<span class="s2"> </span><span class="si">{</span><span class="n">caller</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> experienced a traumatic event</span>
|
||
<span class="s2"> in their childhood. What was it?</span>
|
||
<span class="s2"> """</span>
|
||
|
||
<span class="n">options</span> <span class="o">=</span> <span class="p">({</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"death"</span><span class="p">,</span>
|
||
<span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"A violent death in the family"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_set_attribute</span><span class="p">,</span> <span class="p">{</span><span class="s2">"attr"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"experienced_violence"</span><span class="p">,</span> <span class="kc">True</span><span class="p">),</span>
|
||
<span class="s2">"next_node"</span><span class="p">:</span> <span class="s2">"node_violent_background"</span><span class="p">})},</span>
|
||
<span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"betrayal"</span><span class="p">,</span>
|
||
<span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"The betrayal of a trusted grown-up"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_set_attribute</span><span class="p">,</span> <span class="p">{</span><span class="s2">"attr"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"experienced_betrayal"</span><span class="p">,</span> <span class="kc">True</span><span class="p">),</span>
|
||
<span class="s2">"next_node"</span><span class="p">:</span> <span class="s2">"node_betrayal_background"</span><span class="p">})})</span>
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This will give the following output:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>Kovash the magnificent experienced a traumatic event
|
||
in their childhood. What was it?
|
||
____________________________________________________
|
||
death: A violent death in the family
|
||
betrayal: The betrayal of a trusted grown-up
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>Note above how we use the <code class="docutils literal notranslate"><span class="pre">_set_attribute</span></code> helper function to set the attribute depending on the
|
||
User’s choice. In thie case the helper function doesn’t know anything about what node called it - we
|
||
even tell it which nodename it should return, so the choices leads to different paths in the menu.
|
||
We could also imagine the helper function analyzing what other choices</p>
|
||
</section>
|
||
<section id="example-get-arbitrary-input">
|
||
<h3>Example: Get arbitrary input<a class="headerlink" href="#example-get-arbitrary-input" title="Permalink to this headline">¶</a></h3>
|
||
<p>An example of the menu asking the user for input - any input.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||
<span class="k">def</span> <span class="nf">_set_name</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
|
||
<span class="n">inp</span> <span class="o">=</span> <span class="n">raw_string</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||
|
||
<span class="n">prev_entry</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"prev_entry"</span><span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">inp</span><span class="p">:</span>
|
||
<span class="c1"># a blank input either means OK or Abort</span>
|
||
<span class="k">if</span> <span class="n">prev_entry</span><span class="p">:</span>
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">prev_entry</span>
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Set name to </span><span class="si">{</span><span class="n">prev_entry</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="s2">"node_background"</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Aborted."</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="s2">"node_exit"</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="c1"># re-run old node, but pass in the name given</span>
|
||
<span class="k">return</span> <span class="kc">None</span><span class="p">,</span> <span class="p">{</span><span class="s2">"prev_entry"</span><span class="p">:</span> <span class="n">inp</span><span class="p">}</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">enter_name</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
|
||
<span class="c1"># check if we already entered a name before</span>
|
||
<span class="n">prev_entry</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"prev_entry"</span><span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="n">prev_entry</span><span class="p">:</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="s2">"Current name: </span><span class="si">{}</span><span class="s2">.</span><span class="se">\n</span><span class="s2">Enter another name or <return> to accept."</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="s2">"Enter your character's name or <return> to abort."</span>
|
||
|
||
<span class="n">options</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"_default"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_set_name</span><span class="p">,</span> <span class="p">{</span><span class="s2">"prev_entry"</span><span class="p">:</span> <span class="n">prev_entry</span><span class="p">})}</span>
|
||
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>This will display as</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Enter</span> <span class="n">your</span> <span class="n">character</span><span class="s1">'s name or <return> to abort.</span>
|
||
|
||
<span class="o">></span> <span class="n">Gandalf</span>
|
||
|
||
<span class="n">Current</span> <span class="n">name</span><span class="p">:</span> <span class="n">Gandalf</span>
|
||
<span class="n">Enter</span> <span class="n">another</span> <span class="n">name</span> <span class="ow">or</span> <span class="o"><</span><span class="k">return</span><span class="o">></span> <span class="n">to</span> <span class="n">accept</span><span class="o">.</span>
|
||
|
||
<span class="o">></span>
|
||
|
||
<span class="n">Set</span> <span class="n">name</span> <span class="n">to</span> <span class="n">Gandalf</span><span class="o">.</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>Here we re-use the same node twice for reading the input data from the user. Whatever we enter will
|
||
be caught by the <code class="docutils literal notranslate"><span class="pre">_default</span></code> option and passed into the helper function. We also pass along whatever
|
||
name we have entered before. This allows us to react correctly on an “empty” input - continue to the
|
||
node named <code class="docutils literal notranslate"><span class="pre">"node_background"</span></code> if we accept the input or go to an exit node if we presses Return
|
||
without entering anything. By returning <code class="docutils literal notranslate"><span class="pre">None</span></code> from the helper function we automatically re-run the
|
||
previous node, but updating its ingoing kwargs to tell it to display a different text.</p>
|
||
</section>
|
||
<section id="example-storing-data-between-nodes">
|
||
<h3>Example: Storing data between nodes<a class="headerlink" href="#example-storing-data-between-nodes" title="Permalink to this headline">¶</a></h3>
|
||
<p>A convenient way to store data is to store it on the <code class="docutils literal notranslate"><span class="pre">caller.ndb._menutree</span></code> which you can reach from
|
||
every node. The advantage of doing this is that the <code class="docutils literal notranslate"><span class="pre">_menutree</span></code> NAttribute will be deleted
|
||
automatically when you exit the menu.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||
<span class="k">def</span> <span class="nf">_set_name</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">_menutree</span><span class="o">.</span><span class="n">charactersheet</span> <span class="o">=</span> <span class="p">{}</span>
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">_menutree</span><span class="o">.</span><span class="n">charactersheet</span><span class="p">[</span><span class="s1">'name'</span><span class="p">]</span> <span class="o">=</span> <span class="n">raw_string</span>
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You set your name to </span><span class="si">{</span><span class="n">raw_string</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="s2">"background"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">node_set_name</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="s1">'Enter your name:'</span>
|
||
<span class="n">options</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'key'</span><span class="p">:</span> <span class="s1">'_default'</span><span class="p">,</span>
|
||
<span class="s1">'goto'</span><span class="p">:</span> <span class="n">_set_name</span><span class="p">}</span>
|
||
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
<span class="o">...</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">node_view_sheet</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"Character sheet:</span><span class="se">\n</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">_menutree</span><span class="o">.</span><span class="n">charactersheet</span><span class="si">}</span><span class="s2">"</span>
|
||
|
||
<span class="n">options</span> <span class="o">=</span> <span class="p">({</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"Accept"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="s2">"finish_chargen"</span><span class="p">},</span>
|
||
<span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"Decline"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="s2">"start_over"</span><span class="p">})</span>
|
||
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>Instead of passing the character sheet along from node to node through the <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> we instead
|
||
set it up temporarily on <code class="docutils literal notranslate"><span class="pre">caller.ndb._menutree.charactersheet</span></code>. This makes it easy to reach from
|
||
all nodes. At the end we look at it and, if we accept the character the menu will likely save the
|
||
result to permanent storage and exit.</p>
|
||
<blockquote>
|
||
<div><p>One point to remember though is that storage on <code class="docutils literal notranslate"><span class="pre">caller.ndb._menutree</span></code> is not persistent across
|
||
<code class="docutils literal notranslate"><span class="pre">@reloads</span></code>. If you are using a persistent menu (using <code class="docutils literal notranslate"><span class="pre">EvMenu(...,</span> <span class="pre">persistent=True)</span></code> you should
|
||
use
|
||
<code class="docutils literal notranslate"><span class="pre">caller.db</span></code> to store in-menu data like this as well. You must then yourself make sure to clean it
|
||
when the user exits the menu.</p>
|
||
</div></blockquote>
|
||
</section>
|
||
<section id="example-repeating-the-same-node">
|
||
<h3>Example: Repeating the same node<a class="headerlink" href="#example-repeating-the-same-node" title="Permalink to this headline">¶</a></h3>
|
||
<p>Sometimes you want to make a chain of menu nodes one after another, but you don’t want the user to
|
||
be able to continue to the next node until you have verified that what they input in the previous
|
||
node is ok. A common example is a login menu:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||
<span class="k">def</span> <span class="nf">_check_username</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="c1"># we assume lookup_username() exists</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">lookup_username</span><span class="p">(</span><span class="n">raw_string</span><span class="p">):</span>
|
||
<span class="c1"># re-run current node by returning `None`</span>
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"|rUsername not found. Try again."</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="kc">None</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="c1"># username ok - continue to next node</span>
|
||
<span class="k">return</span> <span class="s2">"node_password"</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">node_username</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="s2">"Please enter your user name."</span>
|
||
<span class="n">options</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"_default"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="n">_check_username</span><span class="p">}</span>
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">_check_password</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
|
||
<span class="n">nattempts</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"nattempts"</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">nattempts</span> <span class="o">></span> <span class="mi">3</span><span class="p">:</span>
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Too many failed attempts. Logging out"</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="s2">"node_abort"</span>
|
||
<span class="k">elif</span> <span class="ow">not</span> <span class="n">validate_password</span><span class="p">(</span><span class="n">raw_string</span><span class="p">):</span>
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Password error. Try again."</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="kc">None</span><span class="p">,</span> <span class="p">{</span><span class="s2">"nattempts"</span><span class="p">,</span> <span class="n">nattempts</span> <span class="o">+</span> <span class="mi">1</span><span class="p">}</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="c1"># password accepted</span>
|
||
<span class="k">return</span> <span class="s2">"node_login"</span>
|
||
|
||
<span class="k">def</span> <span class="nf">node_password</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="s2">"Enter your password."</span>
|
||
<span class="n">options</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"_default"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="n">_check_password</span><span class="p">}</span>
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>This will display something like</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">---------------------------</span>
|
||
<span class="n">Please</span> <span class="n">enter</span> <span class="n">your</span> <span class="n">username</span><span class="o">.</span>
|
||
<span class="o">---------------------------</span>
|
||
|
||
<span class="o">></span> <span class="n">Fo</span>
|
||
|
||
<span class="o">------------------------------</span>
|
||
<span class="n">Username</span> <span class="ow">not</span> <span class="n">found</span><span class="o">.</span> <span class="n">Try</span> <span class="n">again</span><span class="o">.</span>
|
||
<span class="n">______________________________</span>
|
||
<span class="n">abort</span><span class="p">:</span> <span class="p">(</span><span class="n">back</span> <span class="n">to</span> <span class="n">start</span><span class="p">)</span>
|
||
<span class="o">------------------------------</span>
|
||
|
||
<span class="o">></span> <span class="n">Foo</span>
|
||
|
||
<span class="o">---------------------------</span>
|
||
<span class="n">Please</span> <span class="n">enter</span> <span class="n">your</span> <span class="n">password</span><span class="o">.</span>
|
||
<span class="o">---------------------------</span>
|
||
|
||
<span class="o">></span> <span class="n">Bar</span>
|
||
|
||
<span class="o">--------------------------</span>
|
||
<span class="n">Password</span> <span class="n">error</span><span class="o">.</span> <span class="n">Try</span> <span class="n">again</span><span class="o">.</span>
|
||
<span class="o">--------------------------</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>And so on.</p>
|
||
<p>Here the goto-callables will return to the previous node if there is an error. In the case of
|
||
password attempts, this will tick up the <code class="docutils literal notranslate"><span class="pre">nattempts</span></code> argument that will get passed on from iteration
|
||
to iteration until too many attempts have been made.</p>
|
||
</section>
|
||
<section id="defining-nodes-in-a-dictionary">
|
||
<h3>Defining nodes in a dictionary<a class="headerlink" href="#defining-nodes-in-a-dictionary" title="Permalink to this headline">¶</a></h3>
|
||
<p>You can also define your nodes directly in a dictionary to feed into the <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> creator.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">mynode</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
|
||
<span class="c1"># a normal menu node function</span>
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
<span class="n">menu_data</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"node1"</span><span class="p">:</span> <span class="n">mynode</span><span class="p">,</span>
|
||
<span class="s2">"node2"</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">caller</span><span class="p">:</span> <span class="p">(</span>
|
||
<span class="s2">"This is the node text"</span><span class="p">,</span>
|
||
<span class="p">({</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"lambda node 1"</span><span class="p">,</span>
|
||
<span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"go to node 1 (mynode)"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="s2">"node1"</span><span class="p">},</span>
|
||
<span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"lambda node 2"</span><span class="p">,</span>
|
||
<span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"go to thirdnode"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="s2">"node3"</span><span class="p">})),</span>
|
||
<span class="s2">"node3"</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">:</span> <span class="p">(</span>
|
||
<span class="c1"># ... etc ) }</span>
|
||
|
||
<span class="c1"># start menu, assuming 'caller' is available from earlier</span>
|
||
<span class="n">EvMenu</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">menu_data</span><span class="p">,</span> <span class="n">startnode</span><span class="o">=</span><span class="s2">"node1"</span><span class="p">)</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>The keys of the dictionary become the node identifiers. You can use any callable on the right form
|
||
to describe each node. If you use Python <code class="docutils literal notranslate"><span class="pre">lambda</span></code> expressions you can make nodes really on the fly.
|
||
If you do, the lambda expression must accept one or two arguments and always return a tuple with two
|
||
elements (the text of the node and its options), same as any menu node function.</p>
|
||
<p>Creating menus like this is one way to present a menu that changes with the circumstances - you
|
||
could for example remove or add nodes before launching the menu depending on some criteria. The
|
||
drawback is that a <code class="docutils literal notranslate"><span class="pre">lambda</span></code> expression <a class="reference external" href="https://docs.python.org/2/tutorial/controlflow.html#lambda-expressions">is much more
|
||
limited</a> than a full
|
||
function - for example you can’t use other Python keywords like <code class="docutils literal notranslate"><span class="pre">if</span></code> inside the body of the
|
||
<code class="docutils literal notranslate"><span class="pre">lambda</span></code>.</p>
|
||
<p>Unless you are dealing with a relatively simple dynamic menu, defining menus with lambda’s is
|
||
probably more work than it’s worth: You can create dynamic menus by instead making each node
|
||
function more clever. See the <a class="reference internal" href="../Howto/NPC-shop-Tutorial.html"><span class="doc std std-doc">NPC shop tutorial</span></a> for an example of this.</p>
|
||
</section>
|
||
</section>
|
||
<section id="ask-for-simple-input">
|
||
<h2>Ask for simple input<a class="headerlink" href="#ask-for-simple-input" title="Permalink to this headline">¶</a></h2>
|
||
<p>This describes two ways for asking for simple questions from the user. Using Python’s <code class="docutils literal notranslate"><span class="pre">input</span></code>
|
||
will <em>not</em> work in Evennia. <code class="docutils literal notranslate"><span class="pre">input</span></code> will <em>block</em> the entire server for <em>everyone</em> until that one
|
||
player has entered their text, which is not what you want.</p>
|
||
<section id="the-yield-way">
|
||
<h3>The <code class="docutils literal notranslate"><span class="pre">yield</span></code> way<a class="headerlink" href="#the-yield-way" title="Permalink to this headline">¶</a></h3>
|
||
<p>In the <code class="docutils literal notranslate"><span class="pre">func</span></code> method of your Commands (only) you can use Python’s built-in <code class="docutils literal notranslate"><span class="pre">yield</span></code> command to
|
||
request input in a similar way to <code class="docutils literal notranslate"><span class="pre">input</span></code>. It looks like this:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">result</span> <span class="o">=</span> <span class="k">yield</span><span class="p">(</span><span class="s2">"Please enter your answer:"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This will send “Please enter your answer” to the Command’s <code class="docutils literal notranslate"><span class="pre">self.caller</span></code> and then pause at that
|
||
point. All other players at the server will be unaffected. Once caller enteres a reply, the code
|
||
execution will continue and you can do stuff with the <code class="docutils literal notranslate"><span class="pre">result</span></code>. Here is an example:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span>
|
||
<span class="k">class</span> <span class="nc">CmdTestInput</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"test"</span>
|
||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="n">result</span> <span class="o">=</span> <span class="k">yield</span><span class="p">(</span><span class="s2">"Please enter something:"</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You entered </span><span class="si">{</span><span class="n">result</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||
<span class="n">result2</span> <span class="o">=</span> <span class="k">yield</span><span class="p">(</span><span class="s2">"Now enter something else:"</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You now entered </span><span class="si">{</span><span class="n">result2</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Using <code class="docutils literal notranslate"><span class="pre">yield</span></code> is simple and intuitive, but it will only access input from <code class="docutils literal notranslate"><span class="pre">self.caller</span></code> and you
|
||
cannot abort or time out the pause until the player has responded. Under the hood, it is actually
|
||
just a wrapper calling <code class="docutils literal notranslate"><span class="pre">get_input</span></code> described in the following section.</p>
|
||
<blockquote>
|
||
<div><p>Important Note: In Python you <em>cannot mix <code class="docutils literal notranslate"><span class="pre">yield</span></code> and <code class="docutils literal notranslate"><span class="pre">return</span> <span class="pre"><value></span></code> in the same method</em>. It has
|
||
to do with <code class="docutils literal notranslate"><span class="pre">yield</span></code> turning the method into a
|
||
<a class="reference external" href="https://www.learnpython.org/en/Generators">generator</a>. A <code class="docutils literal notranslate"><span class="pre">return</span></code> without an argument works, you
|
||
can just not do <code class="docutils literal notranslate"><span class="pre">return</span> <span class="pre"><value></span></code>. This is usually not something you need to do in <code class="docutils literal notranslate"><span class="pre">func()</span></code> anyway,
|
||
but worth keeping in mind.</p>
|
||
</div></blockquote>
|
||
</section>
|
||
<section id="the-get-input-way">
|
||
<h3>The <code class="docutils literal notranslate"><span class="pre">get_input</span></code> way<a class="headerlink" href="#the-get-input-way" title="Permalink to this headline">¶</a></h3>
|
||
<p>The evmenu module offers a helper function named <code class="docutils literal notranslate"><span class="pre">get_input</span></code>. This is wrapped by the <code class="docutils literal notranslate"><span class="pre">yield</span></code>
|
||
statement which is often easier and more intuitive to use. But <code class="docutils literal notranslate"><span class="pre">get_input</span></code> offers more flexibility
|
||
and power if you need it. While in the same module as <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code>, <code class="docutils literal notranslate"><span class="pre">get_input</span></code> is technically unrelated
|
||
to it. The <code class="docutils literal notranslate"><span class="pre">get_input</span></code> allows you to ask and receive simple one-line input from the user without
|
||
launching the full power of a menu to do so. To use, call <code class="docutils literal notranslate"><span class="pre">get_input</span></code> like this:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">get_input</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">prompt</span><span class="p">,</span> <span class="n">callback</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Here <code class="docutils literal notranslate"><span class="pre">caller</span></code> is the entity that should receive the prompt for input given as <code class="docutils literal notranslate"><span class="pre">prompt</span></code>. The
|
||
<code class="docutils literal notranslate"><span class="pre">callback</span></code> is a callable <code class="docutils literal notranslate"><span class="pre">function(caller,</span> <span class="pre">prompt,</span> <span class="pre">user_input)</span></code> that you define to handle the answer
|
||
from the user. When run, the caller will see <code class="docutils literal notranslate"><span class="pre">prompt</span></code> appear on their screens and <em>any</em> text they
|
||
enter will be sent into the callback for whatever processing you want.</p>
|
||
<p>Below is a fully explained callback and example call:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span>
|
||
<span class="kn">from</span> <span class="nn">evennia.utils.evmenu</span> <span class="kn">import</span> <span class="n">get_input</span>
|
||
|
||
<span class="k">def</span> <span class="nf">callback</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">prompt</span><span class="p">,</span> <span class="n">user_input</span><span class="p">):</span>
|
||
<span class="sd">"""</span>
|
||
<span class="sd"> This is a callback you define yourself.</span>
|
||
|
||
<span class="sd"> Args:</span>
|
||
<span class="sd"> caller (Account or Object): The one being asked</span>
|
||
<span class="sd"> for input</span>
|
||
<span class="sd"> prompt (str): A copy of the current prompt</span>
|
||
<span class="sd"> user_input (str): The input from the account.</span>
|
||
|
||
<span class="sd"> Returns:</span>
|
||
<span class="sd"> repeat (bool): If not set or False, exit the</span>
|
||
<span class="sd"> input prompt and clean up. If returning anything</span>
|
||
<span class="sd"> True, stay in the prompt, which means this callback</span>
|
||
<span class="sd"> will be called again with the next user input.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"When asked '</span><span class="si">{</span><span class="n">prompt</span><span class="si">}</span><span class="s2">', you answered '</span><span class="si">{</span><span class="n">user_input</span><span class="si">}</span><span class="s2">'."</span><span class="p">)</span>
|
||
|
||
<span class="n">get_input</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="s2">"Write something! "</span><span class="p">,</span> <span class="n">callback</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This will show as</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>Write something!
|
||
> Hello
|
||
When asked 'Write something!', you answered 'Hello'.
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>Normally, the <code class="docutils literal notranslate"><span class="pre">get_input</span></code> function quits after any input, but as seen in the example docs, you could
|
||
return True from the callback to repeat the prompt until you pass whatever check you want.</p>
|
||
<blockquote>
|
||
<div><p>Note: You <em>cannot</em> link consecutive questions by putting a new <code class="docutils literal notranslate"><span class="pre">get_input</span></code> call inside the
|
||
callback If you want that you should use an EvMenu instead (see the <a class="reference internal" href="#example-repeating-the-same-node"><span class="std std-doc">Repeating the same
|
||
node</span></a> example above). Otherwise you can either peek at the
|
||
implementation of <code class="docutils literal notranslate"><span class="pre">get_input</span></code> and implement your own mechanism (it’s just using cmdset nesting) or
|
||
you can look at <a class="reference external" href="https://groups.google.com/forum/#%21category-topic/evennia/evennia-questions/16pi0SfMO5U">this extension suggested on the mailing
|
||
list</a>.</p>
|
||
</div></blockquote>
|
||
<section id="example-yes-no-prompt">
|
||
<h4>Example: Yes/No prompt<a class="headerlink" href="#example-yes-no-prompt" title="Permalink to this headline">¶</a></h4>
|
||
<p>Below is an example of a Yes/No prompt using the <code class="docutils literal notranslate"><span class="pre">get_input</span></code> function:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">yesno</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">prompt</span><span class="p">,</span> <span class="n">result</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">result</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">"y"</span><span class="p">,</span> <span class="s2">"yes"</span><span class="p">,</span> <span class="s2">"n"</span><span class="p">,</span> <span class="s2">"no"</span><span class="p">):</span>
|
||
<span class="c1"># do stuff to handle the yes/no answer</span>
|
||
<span class="c1"># ...</span>
|
||
<span class="c1"># if we return None/False the prompt state</span>
|
||
<span class="c1"># will quit after this</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="c1"># the answer is not on the right yes/no form</span>
|
||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Please answer Yes or No. </span><span class="se">\n</span><span class="si">{prompt}</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="o">@</span> <span class="c1"># returning True will make sure the prompt state is not exited</span>
|
||
<span class="k">return</span> <span class="kc">True</span>
|
||
|
||
<span class="c1"># ask the question</span>
|
||
<span class="n">get_input</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="s2">"Is Evennia great (Yes/No)?"</span><span class="p">,</span> <span class="n">yesno</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
<section id="the-list-node-decorator">
|
||
<h2>The <code class="docutils literal notranslate"><span class="pre">@list_node</span></code> decorator<a class="headerlink" href="#the-list-node-decorator" title="Permalink to this headline">¶</a></h2>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">evennia.utils.evmenu.list_node</span></code> is an advanced decorator for use with <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> node functions.
|
||
It is used to quickly create menus for manipulating large numbers of items.</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">text</span> <span class="n">here</span>
|
||
<span class="n">______________________________________________</span>
|
||
|
||
<span class="mf">1.</span> <span class="n">option1</span> <span class="mf">7.</span> <span class="n">option7</span> <span class="mf">13.</span> <span class="n">option13</span>
|
||
<span class="mf">2.</span> <span class="n">option2</span> <span class="mf">8.</span> <span class="n">option8</span> <span class="mf">14.</span> <span class="n">option14</span>
|
||
<span class="mf">3.</span> <span class="n">option3</span> <span class="mf">9.</span> <span class="n">option9</span> <span class="p">[</span><span class="n">p</span><span class="p">]</span><span class="n">revius</span> <span class="n">page</span>
|
||
<span class="mf">4.</span> <span class="n">option4</span> <span class="mf">10.</span> <span class="n">option10</span> <span class="n">page</span> <span class="mi">2</span>
|
||
<span class="mf">5.</span> <span class="n">option5</span> <span class="mf">11.</span> <span class="n">option11</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span><span class="n">ext</span> <span class="n">page</span>
|
||
<span class="mf">6.</span> <span class="n">option6</span> <span class="mf">12.</span> <span class="n">option12</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>The menu will automatically create an multi-page option listing that one can flip through. One can
|
||
inpect each entry and then select them with prev/next. This is how it is used:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia.utils.evmenu</span> <span class="kn">import</span> <span class="n">list_node</span>
|
||
|
||
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">_options</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">[</span><span class="s1">'option1'</span><span class="p">,</span> <span class="s1">'option2'</span><span class="p">,</span> <span class="o">...</span> <span class="s1">'option100'</span><span class="p">]</span>
|
||
|
||
<span class="n">_select</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">menuchoice</span><span class="p">,</span> <span class="n">available_choices</span><span class="p">):</span>
|
||
<span class="c1"># analyze choice</span>
|
||
<span class="k">return</span> <span class="s2">"next_node"</span>
|
||
|
||
<span class="nd">@list_node</span><span class="p">(</span><span class="n">options</span><span class="p">,</span> <span class="n">select</span><span class="o">=</span><span class="n">_select</span><span class="p">,</span> <span class="n">pagesize</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
|
||
<span class="k">def</span> <span class="nf">node_mylist</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">options</span></code> argument to <code class="docutils literal notranslate"><span class="pre">list_node</span></code> is either a list, a generator or a callable returning a list
|
||
of strings for each option that should be displayed in the node.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">select</span></code> is a callable in the example above but could also be the name of a menu node. If a
|
||
callable, the <code class="docutils literal notranslate"><span class="pre">menuchoice</span></code> argument holds the selection done and <code class="docutils literal notranslate"><span class="pre">available_choices</span></code> holds all the
|
||
options available. The callable should return the menu to go to depending on the selection (or
|
||
<code class="docutils literal notranslate"><span class="pre">None</span></code> to rerun the same node). If the name of a menu node, the selection will be passed as
|
||
<code class="docutils literal notranslate"><span class="pre">selection</span></code> kwarg to that node.</p>
|
||
<p>The decorated node itself should return <code class="docutils literal notranslate"><span class="pre">text</span></code> to display in the node. It must return at least an
|
||
empty dictionary for its options. It returning options, those will supplement the options
|
||
auto-created by the <code class="docutils literal notranslate"><span class="pre">list_node</span></code> decorator.</p>
|
||
</section>
|
||
<section id="assorted-notes">
|
||
<h2>Assorted notes<a class="headerlink" href="#assorted-notes" title="Permalink to this headline">¶</a></h2>
|
||
<p>The EvMenu is implemented using <a class="reference internal" href="Commands.html"><span class="doc std std-doc">Commands</span></a>. When you start a new EvMenu, the user of the
|
||
menu will be assigned a <a class="reference internal" href="Command-Sets.html"><span class="doc std std-doc">CmdSet</span></a> with the commands they need to navigate the menu.
|
||
This means that if you were to, from inside the menu, assign a new command set to the caller, <em>you
|
||
may override the Menu Cmdset and kill the menu</em>. If you want to assign cmdsets to the caller as part
|
||
of the menu, you should store the cmdset on <code class="docutils literal notranslate"><span class="pre">caller.ndb._menutree</span></code> and wait to actually assign it
|
||
until the exit node.</p>
|
||
</section>
|
||
</section>
|
||
|
||
|
||
<div class="clearer"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||
<div class="sphinxsidebarwrapper">
|
||
<p class="logo"><a href="../index.html">
|
||
<img class="logo" src="../_static/evennia_logo.png" alt="Logo"/>
|
||
</a></p>
|
||
<div id="searchbox" style="display: none" role="search">
|
||
<h3 id="searchlabel">Quick search</h3>
|
||
<div class="searchformwrapper">
|
||
<form class="search" action="../search.html" method="get">
|
||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||
<input type="submit" value="Go" />
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<script>$('#searchbox').show(0);</script>
|
||
<p><h3><a href="../index.html">Table of Contents</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">EvMenu</a><ul>
|
||
<li><a class="reference internal" href="#introduction">Introduction</a><ul>
|
||
<li><a class="reference internal" href="#examples">Examples</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#launching-the-menu">Launching the menu</a></li>
|
||
<li><a class="reference internal" href="#the-menu-nodes">The Menu nodes</a><ul>
|
||
<li><a class="reference internal" href="#input-arguments-to-the-node">Input arguments to the node</a></li>
|
||
<li><a class="reference internal" href="#return-values-from-the-node">Return values from the node</a><ul>
|
||
<li><a class="reference internal" href="#text">text</a></li>
|
||
<li><a class="reference internal" href="#options">options</a><ul>
|
||
<li><a class="reference internal" href="#option-key-key">option-key ‘key’</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#option-key-desc">option-key ‘desc’</a></li>
|
||
<li><a class="reference internal" href="#option-key-goto">option-key ‘goto’</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#temporary-storage">Temporary storage</a></li>
|
||
<li><a class="reference internal" href="#customizing-menu-formatting">Customizing Menu formatting</a></li>
|
||
<li><a class="reference internal" href="#evmenu-templating-language">EvMenu templating language</a><ul>
|
||
<li><a class="reference internal" href="#template-options">Template Options</a></li>
|
||
<li><a class="reference internal" href="#templating-example">Templating example</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#id1">Examples:</a><ul>
|
||
<li><a class="reference internal" href="#example-simple-branching-menu">Example: Simple branching menu</a></li>
|
||
<li><a class="reference internal" href="#example-dynamic-goto">Example: Dynamic goto</a></li>
|
||
<li><a class="reference internal" href="#example-set-caller-properties">Example: Set caller properties</a></li>
|
||
<li><a class="reference internal" href="#example-get-arbitrary-input">Example: Get arbitrary input</a></li>
|
||
<li><a class="reference internal" href="#example-storing-data-between-nodes">Example: Storing data between nodes</a></li>
|
||
<li><a class="reference internal" href="#example-repeating-the-same-node">Example: Repeating the same node</a></li>
|
||
<li><a class="reference internal" href="#defining-nodes-in-a-dictionary">Defining nodes in a dictionary</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#ask-for-simple-input">Ask for simple input</a><ul>
|
||
<li><a class="reference internal" href="#the-yield-way">The <code class="docutils literal notranslate"><span class="pre">yield</span></code> way</a></li>
|
||
<li><a class="reference internal" href="#the-get-input-way">The <code class="docutils literal notranslate"><span class="pre">get_input</span></code> way</a><ul>
|
||
<li><a class="reference internal" href="#example-yes-no-prompt">Example: Yes/No prompt</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#the-list-node-decorator">The <code class="docutils literal notranslate"><span class="pre">@list_node</span></code> decorator</a></li>
|
||
<li><a class="reference internal" href="#assorted-notes">Assorted notes</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<div role="note" aria-label="source link">
|
||
<!--h3>This Page</h3-->
|
||
<ul class="this-page-menu">
|
||
<li><a href="../_sources/Components/EvMenu.md.txt"
|
||
rel="nofollow">Show Page Source</a></li>
|
||
</ul>
|
||
</div><h3>Links</h3>
|
||
<ul>
|
||
<li><a href="https://www.evennia.com">Home page</a> </li>
|
||
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
|
||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||
<li>
|
||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||
</li>
|
||
</ul>
|
||
<h3>Versions</h3>
|
||
<ul>
|
||
<li><a href="EvMenu.html">1.0-dev (develop branch)</a></li>
|
||
</ul>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="clearer"></div>
|
||
</div>
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="General Index"
|
||
>index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia 1.0-dev</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">EvMenu</a></li>
|
||
</ul>
|
||
<div class="develop">develop branch</div>
|
||
</div>
|
||
<div class="footer" role="contentinfo">
|
||
© Copyright 2020, The Evennia developer community.
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||
</div>
|
||
</body>
|
||
</html> |