Add legacy 2.x docs

This commit is contained in:
Griatch 2023-12-20 18:06:19 +01:00
parent df0a1a4f59
commit 07cfac0bfa
2288 changed files with 531353 additions and 0 deletions

View file

@ -0,0 +1,151 @@
<!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>12. NPC and monster AI &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="13. Dynamically generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
<link rel="prev" title="11. Turnbased Combat" href="Beginner-Tutorial-Combat-Turnbased.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Combat-Turnbased.html" title="11. Turnbased Combat"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">12. </span>NPC and monster AI</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Combat-Turnbased.html"
title="previous chapter"><span class="section-number">11. </span>Turnbased Combat</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Dungeon.html"
title="next chapter"><span class="section-number">13. </span>Dynamically generated Dungeon</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-AI.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="npc-and-monster-ai">
<h1><span class="section-number">12. </span>NPC and monster AI<a class="headerlink" href="#npc-and-monster-ai" title="Permalink to this headline"></a></h1>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>This part of the Beginner tutorial is still being developed.</p>
</div>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Combat-Turnbased.html" title="11. Turnbased Combat"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">12. </span>NPC and monster AI</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,566 @@
<!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>3. Player Characters &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="4. In-game Objects and items" href="Beginner-Tutorial-Objects.html" />
<link rel="prev" title="2. Rules and dice rolling" href="Beginner-Tutorial-Rules.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Objects.html" title="4. In-game Objects and items"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rules.html" title="2. Rules and dice rolling"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">3. </span>Player Characters</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">3. Player Characters</a><ul>
<li><a class="reference internal" href="#inheritance-structure">3.1. Inheritance structure</a></li>
<li><a class="reference internal" href="#living-mixin-class">3.2. Living mixin class</a></li>
<li><a class="reference internal" href="#character-class">3.3. Character class</a><ul>
<li><a class="reference internal" href="#funcparser-inlines">3.3.1. Funcparser inlines</a></li>
<li><a class="reference internal" href="#backtracking">3.3.2. Backtracking</a></li>
</ul>
</li>
<li><a class="reference internal" href="#connecting-the-character-with-evennia">3.4. Connecting the Character with Evennia</a></li>
<li><a class="reference internal" href="#unit-testing">3.5. Unit Testing</a></li>
<li><a class="reference internal" href="#about-races-and-classes">3.6. About races and classes</a></li>
<li><a class="reference internal" href="#summary">3.7. Summary</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Rules.html"
title="previous chapter"><span class="section-number">2. </span>Rules and dice rolling</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Objects.html"
title="next chapter"><span class="section-number">4. </span>In-game Objects and items</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Characters.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="player-characters">
<h1><span class="section-number">3. </span>Player Characters<a class="headerlink" href="#player-characters" title="Permalink to this headline"></a></h1>
<p>In the <a class="reference internal" href="Beginner-Tutorial-Rules.html"><span class="doc std std-doc">previous lesson about rules and dice rolling</span></a> we made some
assumptions about the “Player Character” entity:</p>
<ul class="simple">
<li><p>It should store Abilities on itself as <code class="docutils literal notranslate"><span class="pre">character.strength</span></code>, <code class="docutils literal notranslate"><span class="pre">character.constitution</span></code> etc.</p></li>
<li><p>It should have a <code class="docutils literal notranslate"><span class="pre">.heal(amount)</span></code> method.</p></li>
</ul>
<p>So we have some guidelines of how it should look! A Character is a database entity with values that
should be able to be changed over time. It makes sense to base it off Evennias
<a class="reference internal" href="../../../Components/Typeclasses.html"><span class="doc std std-doc">DefaultCharacter Typeclass</span></a>. The Character class is like a character sheet in a tabletop
RPG, it will hold everything relevant to that PC.</p>
<section id="inheritance-structure">
<h2><span class="section-number">3.1. </span>Inheritance structure<a class="headerlink" href="#inheritance-structure" title="Permalink to this headline"></a></h2>
<p>Player Characters (PCs) are not the only “living” things in our world. We also have <em>NPCs</em>
(like shopkeepers and other friendlies) as well as <em>monsters</em> (mobs) that can attack us.</p>
<p>In code, there are a few ways we could structure this. If NPCs/monsters were just special cases of PCs,
we could use a class inheritance like this:</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">DefaultCharacter</span>
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
<span class="c1"># stuff </span>
<span class="k">class</span> <span class="nc">EvAdventureNPC</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">):</span>
<span class="c1"># more stuff </span>
<span class="k">class</span> <span class="nc">EvAdventureMob</span><span class="p">(</span><span class="n">EvAdventureNPC</span><span class="p">):</span>
<span class="c1"># more stuff </span>
</pre></div>
</div>
<p>All code we put on the <code class="docutils literal notranslate"><span class="pre">Character</span></code> class would now be inherited to <code class="docutils literal notranslate"><span class="pre">NPC</span></code> and <code class="docutils literal notranslate"><span class="pre">Mob</span></code> automatically.</p>
<p>However, in <em>Knave</em>, NPCs and particularly monsters are <em>not</em> using the same rules as PCs - they are
simplified to use a Hit-Die (HD) concept. So while still character-like, NPCs should be separate from
PCs like this:</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">DefaultCharacter</span>
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
<span class="c1"># stuff </span>
<span class="k">class</span> <span class="nc">EvAdventureNPC</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
<span class="c1"># separate stuff </span>
<span class="k">class</span> <span class="nc">EvAdventureMob</span><span class="p">(</span><span class="n">EvadventureNPC</span><span class="p">):</span>
<span class="c1"># more separate stuff</span>
</pre></div>
</div>
<p>Nevertheless, there are some things that <em>should</em> be common for all living things:</p>
<ul class="simple">
<li><p>All can take damage.</p></li>
<li><p>All can die.</p></li>
<li><p>All can heal</p></li>
<li><p>All can hold and lose coins</p></li>
<li><p>All can loot their fallen foes.</p></li>
<li><p>All can get looted when defeated.</p></li>
</ul>
<p>We dont want to code this separately for every class but we no longer have a common parent
class to put it on. So instead well use the concept of a <em>mixin</em> class:</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">DefaultCharacter</span>
<span class="k">class</span> <span class="nc">LivingMixin</span><span class="p">:</span>
<span class="c1"># stuff common for all living things</span>
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
<span class="c1"># stuff </span>
<span class="k">class</span> <span class="nc">EvAdventureNPC</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
<span class="c1"># stuff </span>
<span class="k">class</span> <span class="nc">EvAdventureMob</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">EvadventureNPC</span><span class="p">):</span>
<span class="c1"># more stuff</span>
</pre></div>
</div>
<aside class="sidebar">
<p>In <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.characters.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/characters.py</span></a>
is an example of a character class structure.</p>
</aside>
<p>Above, the <code class="docutils literal notranslate"><span class="pre">LivingMixin</span></code> class cannot work on its own - it just patches the other classes with some
extra functionality all living things should be able to do. This is an example of
<em>multiple inheritance</em>. Its useful to know about, but one should not over-do multiple inheritance
since it can also get confusing to follow the code.</p>
</section>
<section id="living-mixin-class">
<h2><span class="section-number">3.2. </span>Living mixin class<a class="headerlink" href="#living-mixin-class" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/characters.py</span></code></p>
</div></blockquote>
<p>Lets get some useful common methods all living things should have in our game.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/characters.py </span>
<span class="kn">from</span> <span class="nn">.rules</span> <span class="kn">import</span> <span class="n">dice</span>
<span class="k">class</span> <span class="nc">LivingMixin</span><span class="p">:</span>
<span class="c1"># makes it easy for mobs to know to attack PCs</span>
<span class="n">is_pc</span> <span class="o">=</span> <span class="kc">False</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">hurt_level</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> String describing how hurt this character is.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">percent</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">min</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">100</span> <span class="o">*</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">/</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span><span class="p">)))</span>
<span class="k">if</span> <span class="mi">95</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">100</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|gPerfect|n&quot;</span>
<span class="k">elif</span> <span class="mi">80</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">95</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|gScraped|n&quot;</span>
<span class="k">elif</span> <span class="mi">60</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">80</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|GBruised|n&quot;</span>
<span class="k">elif</span> <span class="mi">45</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">60</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|yHurt|n&quot;</span>
<span class="k">elif</span> <span class="mi">30</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">45</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|yWounded|n&quot;</span>
<span class="k">elif</span> <span class="mi">15</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">30</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|rBadly wounded|n&quot;</span>
<span class="k">elif</span> <span class="mi">1</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">15</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|rBarely hanging on|n&quot;</span>
<span class="k">elif</span> <span class="n">percent</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|RCollapsed!|n&quot;</span>
<span class="k">def</span> <span class="nf">heal</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">hp</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Heal hp amount of health, not allowing to exceed our max hp</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">damage</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span> <span class="o">-</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp</span>
<span class="n">healed</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">damage</span><span class="p">,</span> <span class="n">hp</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">+=</span> <span class="n">healed</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;You heal for </span><span class="si">{</span><span class="n">healed</span><span class="si">}</span><span class="s2"> HP.&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">at_pay</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">amount</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;When paying coins, make sure to never detract more than we have&quot;&quot;&quot;</span>
<span class="n">amount</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">amount</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">coins</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">coins</span> <span class="o">-=</span> <span class="n">amount</span>
<span class="k">return</span> <span class="n">amount</span>
<span class="k">def</span> <span class="nf">at_attacked</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attacker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called when being attacked and combat starts.&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">at_damage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">damage</span><span class="p">,</span> <span class="n">attacker</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called when attacked and taking damage.&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">-=</span> <span class="n">damage</span>
<span class="k">def</span> <span class="nf">at_defeat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called when defeated. By default this means death.&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">at_death</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">at_death</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called when this thing dies.&quot;&quot;&quot;</span>
<span class="c1"># this will mean different things for different living things</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">at_do_loot</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looted</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called when looting another entity&quot;&quot;&quot;</span>
<span class="n">looted</span><span class="o">.</span><span class="n">at_looted</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">at_looted</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looter</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called when looted by another entity&quot;&quot;&quot;</span>
<span class="c1"># default to stealing some coins </span>
<span class="n">max_steal</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d10&quot;</span><span class="p">)</span>
<span class="n">stolen</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">at_pay</span><span class="p">(</span><span class="n">max_steal</span><span class="p">)</span>
<span class="n">looter</span><span class="o">.</span><span class="n">coins</span> <span class="o">+=</span> <span class="n">stolen</span>
</pre></div>
</div>
<p>Most of these are empty since they will behave differently for characters and npcs. But having them in the mixin means we can expect these methods to be available for all living things.</p>
<p>Once we create more of our game, we will need to remember to actually call these hook methods so they serve a purpose. For example, once we implement combat, we must remember to call <code class="docutils literal notranslate"><span class="pre">at_attacked</span></code> as well as the other methods involving taking damage, getting defeated or dying.</p>
</section>
<section id="character-class">
<h2><span class="section-number">3.3. </span>Character class<a class="headerlink" href="#character-class" title="Permalink to this headline"></a></h2>
<p>We will now start making the basic Character class, based on what we need from <em>Knave</em>.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/characters.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span><span class="p">,</span> <span class="n">AttributeProperty</span>
<span class="kn">from</span> <span class="nn">.rules</span> <span class="kn">import</span> <span class="n">dice</span>
<span class="k">class</span> <span class="nc">LivingMixin</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> A character to use for EvAdventure. </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">is_pc</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">strength</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">dexterity</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">constitution</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">intelligence</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">wisdom</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">charisma</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">hp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
<span class="n">hp_max</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
<span class="n">level</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">xp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">coins</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">at_defeat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Characters roll on the death table&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">allow_death</span><span class="p">:</span>
<span class="c1"># this allow rooms to have non-lethal battles</span>
<span class="n">dice</span><span class="o">.</span><span class="n">roll_death</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="s2">&quot;$You() $conj(collapse) in a heap, alive but beaten.&quot;</span><span class="p">,</span>
<span class="n">from_obj</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">heal</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">at_death</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;We rolled &#39;dead&#39; on the death table.&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="s2">&quot;$You() collapse in a heap, embraced by death.&quot;</span><span class="p">,</span>
<span class="n">from_obj</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
<span class="c1"># TODO - go back into chargen to make a new character! </span>
</pre></div>
</div>
<p>We make an assumption about our rooms here - that they have a property <code class="docutils literal notranslate"><span class="pre">.allow_death</span></code>. We need to make a note to actually add such a property to rooms later!</p>
<p>In our <code class="docutils literal notranslate"><span class="pre">Character</span></code> class we implement all attributes we want to simulate from the <em>Knave</em> ruleset.
The <code class="docutils literal notranslate"><span class="pre">AttributeProperty</span></code> is one way to add an Attribute in a field-like way; these will be accessible on every character in several ways:</p>
<ul class="simple">
<li><p>As <code class="docutils literal notranslate"><span class="pre">character.strength</span></code></p></li>
<li><p>As <code class="docutils literal notranslate"><span class="pre">character.db.strength</span></code></p></li>
<li><p>As <code class="docutils literal notranslate"><span class="pre">character.attributes.get(&quot;strength&quot;)</span></code></p></li>
</ul>
<p>See <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attributes</span></a> for seeing how Attributes work.</p>
<p>Unlike in base <em>Knave</em>, we store <code class="docutils literal notranslate"><span class="pre">coins</span></code> as a separate Attribute rather than as items in the inventory, this makes it easier to handle barter and trading later.</p>
<p>We implement the Player Character versions of <code class="docutils literal notranslate"><span class="pre">at_defeat</span></code> and <code class="docutils literal notranslate"><span class="pre">at_death</span></code>. We also make use of <code class="docutils literal notranslate"><span class="pre">.heal()</span></code> from the <code class="docutils literal notranslate"><span class="pre">LivingMixin</span></code> class.</p>
<section id="funcparser-inlines">
<h3><span class="section-number">3.3.1. </span>Funcparser inlines<a class="headerlink" href="#funcparser-inlines" title="Permalink to this headline"></a></h3>
<p>This piece of code is worth some more explanation:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="s2">&quot;$You() $conj(collapse) in a heap, alive but beaten.&quot;</span><span class="p">,</span>
<span class="n">from_obj</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
</pre></div>
</div>
<p>Remember that <code class="docutils literal notranslate"><span class="pre">self</span></code> is the Character instance here. So <code class="docutils literal notranslate"><span class="pre">self.location.msg_contents</span></code> means “send a message to everything inside my current location”. In other words, send a message to everyone in the same place as the character.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">$You()</span> <span class="pre">$conj(collapse)</span></code> are <a class="reference internal" href="../../../Components/FuncParser.html"><span class="doc std std-doc">FuncParser inlines</span></a>. These are functions that
execute in the string. The resulting string may look different for different audiences. The <code class="docutils literal notranslate"><span class="pre">$You()</span></code> inline function will use <code class="docutils literal notranslate"><span class="pre">from_obj</span></code> to figure out who you are and either show your name or You. The <code class="docutils literal notranslate"><span class="pre">$conj()</span></code> (verb conjugator) will tweak the (English) verb to match.</p>
<ul class="simple">
<li><p>You will see: <code class="docutils literal notranslate"><span class="pre">&quot;You</span> <span class="pre">collapse</span> <span class="pre">in</span> <span class="pre">a</span> <span class="pre">heap,</span> <span class="pre">alive</span> <span class="pre">but</span> <span class="pre">beaten.&quot;</span></code></p></li>
<li><p>Others in the room will see: <code class="docutils literal notranslate"><span class="pre">&quot;Thomas</span> <span class="pre">collapses</span> <span class="pre">in</span> <span class="pre">a</span> <span class="pre">heap,</span> <span class="pre">alive</span> <span class="pre">but</span> <span class="pre">beaten.&quot;</span></code></p></li>
</ul>
<p>Note how <code class="docutils literal notranslate"><span class="pre">$conj()</span></code> chose <code class="docutils literal notranslate"><span class="pre">collapse/collapses</span></code> to make the sentences grammatically correct.</p>
</section>
<section id="backtracking">
<h3><span class="section-number">3.3.2. </span>Backtracking<a class="headerlink" href="#backtracking" title="Permalink to this headline"></a></h3>
<p>We make our first use of the <code class="docutils literal notranslate"><span class="pre">rules.dice</span></code> roller to roll on the death table! As you may recall, in the previous lesson, we didnt know just what to do when rolling dead on this table. Now we know - we should be calling <code class="docutils literal notranslate"><span class="pre">at_death</span></code> on the character. So lets add that where we had TODOs before:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/rules.py </span>
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">roll_death</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">character</span><span class="p">):</span>
<span class="n">ability_name</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d8&quot;</span><span class="p">,</span> <span class="n">death_table</span><span class="p">)</span>
<span class="k">if</span> <span class="n">ability_name</span> <span class="o">==</span> <span class="s2">&quot;dead&quot;</span><span class="p">:</span>
<span class="c1"># kill the character!</span>
<span class="n">character</span><span class="o">.</span><span class="n">at_death</span><span class="p">()</span> <span class="c1"># &lt;------ TODO no more</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="k">if</span> <span class="n">current_ability</span> <span class="o">&lt;</span> <span class="o">-</span><span class="mi">10</span><span class="p">:</span>
<span class="c1"># kill the character!</span>
<span class="n">character</span><span class="o">.</span><span class="n">at_death</span><span class="p">()</span> <span class="c1"># &lt;------- TODO no more</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># ... </span>
</pre></div>
</div>
</section>
</section>
<section id="connecting-the-character-with-evennia">
<h2><span class="section-number">3.4. </span>Connecting the Character with Evennia<a class="headerlink" href="#connecting-the-character-with-evennia" title="Permalink to this headline"></a></h2>
<p>You can easily make yourself an <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> in-game by using the
<code class="docutils literal notranslate"><span class="pre">type</span></code> command:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>type self = evadventure.characters.EvAdventureCharacter
</pre></div>
</div>
<p>You can now do <code class="docutils literal notranslate"><span class="pre">examine</span> <span class="pre">self</span></code> to check your type updated.</p>
<p>If you want <em>all</em> new Characters to be of this type you need to tell Evennia about it. Evennia
uses a global setting <code class="docutils literal notranslate"><span class="pre">BASE_CHARACTER_TYPECLASS</span></code> to know which typeclass to use when creating
Characters (when logging in, for example). This defaults to <code class="docutils literal notranslate"><span class="pre">typeclasses.characters.Character</span></code> (that is,
the <code class="docutils literal notranslate"><span class="pre">Character</span></code> class in <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/characters.py</span></code>).</p>
<p>There are thus two ways to weave your new Character class into Evennia:</p>
<ol class="simple">
<li><p>Change <code class="docutils literal notranslate"><span class="pre">mygame/server/conf/settings.py</span></code> and add <code class="docutils literal notranslate"><span class="pre">BASE_CHARACTER_TYPECLASS</span> <span class="pre">=</span> <span class="pre">&quot;evadventure.characters.EvAdventureCharacter&quot;</span></code>.</p></li>
<li><p>Or, change <code class="docutils literal notranslate"><span class="pre">typeclasses.characters.Character</span></code> to inherit from <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code>.</p></li>
</ol>
<p>You must always reload the server for changes like this to take effect.</p>
<div class="admonition important">
<p class="admonition-title">Important</p>
<p>In this tutorial we are making all changes in a folder <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/</span></code>. This means we can isolate
our code but means we need to do some extra steps to tie the character (and other objects) into Evennia.
For your own game it would be just fine to start editing <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/characters.py</span></code> directly
instead.</p>
</div>
</section>
<section id="unit-testing">
<h2><span class="section-number">3.5. </span>Unit Testing<a class="headerlink" href="#unit-testing" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/tests/test_characters.py</span></code></p>
</div></blockquote>
<p>For testing, we just need to create a new EvAdventure character and check
that calling the methods on it doesnt error out.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/tests/test_characters.py </span>
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">create</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">BaseEvenniaTest</span>
<span class="kn">from</span> <span class="nn">..characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
<span class="k">class</span> <span class="nc">TestCharacters</span><span class="p">(</span><span class="n">BaseEvenniaTest</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">character</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;testchar&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_heal</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="mi">0</span>
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">hp_max</span> <span class="o">=</span> <span class="mi">8</span>
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">heal</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">hp</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="c1"># make sure we can&#39;t heal more than max</span>
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">heal</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">hp</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_at_pay</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">coins</span> <span class="o">=</span> <span class="mi">100</span>
<span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">at_pay</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="mi">60</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">coins</span><span class="p">,</span> <span class="mi">40</span><span class="p">)</span>
<span class="c1"># can&#39;t get more coins than we have </span>
<span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">at_pay</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="mi">40</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">coins</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="c1"># tests for other methods ... </span>
</pre></div>
</div>
<p>If you followed the previous lessons, these tests should look familiar. Consider adding
tests for other methods as practice. Refer to previous lessons for details.</p>
<p>For running the tests you do:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> evennia test --settings settings.py .evadventure.tests.test_character
</pre></div>
</div>
</section>
<section id="about-races-and-classes">
<h2><span class="section-number">3.6. </span>About races and classes<a class="headerlink" href="#about-races-and-classes" title="Permalink to this headline"></a></h2>
<p><em>Knave</em> doesnt have any D&amp;D-style <em>classes</em> (like Thief, Fighter etc). It also does not bother with
<em>races</em> (like dwarves, elves etc). This makes the tutorial shorter, but you may ask yourself how youd
add these functions.</p>
<p>In the framework we have sketched out for <em>Knave</em>, it would be simple - youd add your race/class as
an Attribute on your Character:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/characters.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span><span class="p">,</span> <span class="n">AttributeProperty</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="n">charclass</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="s2">&quot;Fighter&quot;</span><span class="p">)</span>
<span class="n">charrace</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="s2">&quot;Human&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>We use <code class="docutils literal notranslate"><span class="pre">charclass</span></code> rather than <code class="docutils literal notranslate"><span class="pre">class</span></code> here, because <code class="docutils literal notranslate"><span class="pre">class</span></code> is a reserved Python keyword. Naming
<code class="docutils literal notranslate"><span class="pre">race</span></code> as <code class="docutils literal notranslate"><span class="pre">charrace</span></code> thus matches in style.</p>
<p>Wed then need to expand our <a class="reference internal" href="Beginner-Tutorial-Rules.html"><span class="doc std std-doc">rules module</span></a> (and later
<a class="reference internal" href="Beginner-Tutorial-Chargen.html"><span class="doc std std-doc">character generation</span></a> to check and include what these classes mean.</p>
</section>
<section id="summary">
<h2><span class="section-number">3.7. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline"></a></h2>
<p>With the <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> class in place, we have a better understanding of how our PCs will look
like under <em>Knave</em>.</p>
<p>For now, we only have bits and pieces and havent been testing this code in-game. But if you want
you can swap yourself into <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> right now. Log into your game and run
the command</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>type self = evadventure.characters.EvAdventureCharacter
</pre></div>
</div>
<p>If all went well, <code class="docutils literal notranslate"><span class="pre">ex</span> <span class="pre">self</span></code> will now show your typeclass as being <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code>.
Check out your strength with</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>py self.strength = 3
</pre></div>
</div>
<div class="admonition important">
<p class="admonition-title">Important</p>
<p>When doing <code class="docutils literal notranslate"><span class="pre">ex</span> <span class="pre">self</span></code> you will <em>not</em> see all your Abilities listed yet. Thats because
Attributes added with <code class="docutils literal notranslate"><span class="pre">AttributeProperty</span></code> are not available until they have been accessed at
least once. So once you set (or look at) <code class="docutils literal notranslate"><span class="pre">.strength</span></code> above, <code class="docutils literal notranslate"><span class="pre">strength</span></code> will show in <code class="docutils literal notranslate"><span class="pre">examine</span></code> from
then on.</p>
</div>
</section>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-Objects.html" title="4. In-game Objects and items"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rules.html" title="2. Rules and dice rolling"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">3. </span>Player Characters</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,778 @@
<!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>6. Character Generation &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="7. In-game Rooms" href="Beginner-Tutorial-Rooms.html" />
<link rel="prev" title="5. Handling Equipment" href="Beginner-Tutorial-Equipment.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rooms.html" title="7. In-game Rooms"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Equipment.html" title="5. Handling Equipment"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">6. </span>Character Generation</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">6. Character Generation</a><ul>
<li><a class="reference internal" href="#how-it-will-work">6.1. How it will work</a></li>
<li><a class="reference internal" href="#random-tables">6.2. Random tables</a></li>
<li><a class="reference internal" href="#storing-state-of-the-menu">6.3. Storing state of the menu</a><ul>
<li><a class="reference internal" href="#showing-the-sheet">6.3.1. Showing the sheet</a></li>
<li><a class="reference internal" href="#apply-character">6.3.2. Apply character</a></li>
</ul>
</li>
<li><a class="reference internal" href="#initializing-evmenu">6.4. Initializing EvMenu</a></li>
<li><a class="reference internal" href="#main-node-choosing-what-to-do">6.5. Main Node: Choosing what to do</a></li>
<li><a class="reference internal" href="#node-changing-your-name">6.6. Node: Changing your name</a></li>
<li><a class="reference internal" href="#node-swapping-abilities-around">6.7. Node: Swapping Abilities around</a></li>
<li><a class="reference internal" href="#node-creating-the-character">6.8. Node: Creating the Character</a></li>
<li><a class="reference internal" href="#tying-the-nodes-together">6.9. Tying the nodes together</a></li>
<li><a class="reference internal" href="#conclusions">6.10. Conclusions</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Equipment.html"
title="previous chapter"><span class="section-number">5. </span>Handling Equipment</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Rooms.html"
title="next chapter"><span class="section-number">7. </span>In-game Rooms</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Chargen.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="character-generation">
<h1><span class="section-number">6. </span>Character Generation<a class="headerlink" href="#character-generation" title="Permalink to this headline"></a></h1>
<p>In previous lessons we have established how a character looks. Now we need to give the player a
chance to create one.</p>
<section id="how-it-will-work">
<h2><span class="section-number">6.1. </span>How it will work<a class="headerlink" href="#how-it-will-work" title="Permalink to this headline"></a></h2>
<p>A fresh Evennia install will automatically create a new Character with the same name as your
Account when you log in. This is quick and simple and mimics older MUD styles. You could picture
doing this, and then customizing the Character in-place.</p>
<p>We will be a little more sophisticated though. We want the user to be able to create a character
using a menu when they log in.</p>
<p>We do this by editing <code class="docutils literal notranslate"><span class="pre">mygame/server/conf/settings.py</span></code> and adding the line</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>AUTO_CREATE_CHARACTER_WITH_ACCOUNT = False
</pre></div>
</div>
<p>When doing this, connecting with the game with a new account will land you in “OOC” mode. The ooc-version of <code class="docutils literal notranslate"><span class="pre">look</span></code> (sitting in the Account cmdset) will show a list of available characters if you have any. You can also enter <code class="docutils literal notranslate"><span class="pre">charcreate</span></code> to make a new character. The <code class="docutils literal notranslate"><span class="pre">charcreate</span></code> is a simple command coming with Evennia that just lets you make a new character with a given name and description. We will later modify that to kick off our chargen. For now well just keep in mind thats how well start off the menu.</p>
<p>In <em>Knave</em>, most of the character-generation is random. This means this tutorial can be pretty
compact while still showing the basic idea. What we will create is a menu looking like this:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Silas</span>
<span class="n">STR</span> <span class="o">+</span><span class="mi">1</span>
<span class="n">DEX</span> <span class="o">+</span><span class="mi">2</span>
<span class="n">CON</span> <span class="o">+</span><span class="mi">1</span>
<span class="n">INT</span> <span class="o">+</span><span class="mi">3</span>
<span class="n">WIS</span> <span class="o">+</span><span class="mi">1</span>
<span class="n">CHA</span> <span class="o">+</span><span class="mi">2</span>
<span class="n">You</span> <span class="n">are</span> <span class="n">lanky</span> <span class="k">with</span> <span class="n">a</span> <span class="n">sunken</span> <span class="n">face</span> <span class="ow">and</span> <span class="n">filthy</span> <span class="n">hair</span><span class="p">,</span> <span class="n">breathy</span> <span class="n">speech</span><span class="p">,</span> <span class="ow">and</span> <span class="n">foreign</span> <span class="n">clothing</span><span class="o">.</span>
<span class="n">You</span> <span class="n">were</span> <span class="n">a</span> <span class="n">herbalist</span><span class="p">,</span> <span class="n">but</span> <span class="n">you</span> <span class="n">were</span> <span class="n">pursued</span> <span class="ow">and</span> <span class="n">ended</span> <span class="n">up</span> <span class="n">a</span> <span class="n">knave</span><span class="o">.</span> <span class="n">You</span> <span class="n">are</span> <span class="n">honest</span> <span class="n">but</span> <span class="n">also</span>
<span class="n">suspicious</span><span class="o">.</span> <span class="n">You</span> <span class="n">are</span> <span class="n">of</span> <span class="n">the</span> <span class="n">neutral</span> <span class="n">alignment</span><span class="o">.</span>
<span class="n">Your</span> <span class="n">belongings</span><span class="p">:</span>
<span class="n">Brigandine</span> <span class="n">armor</span><span class="p">,</span> <span class="n">ration</span><span class="p">,</span> <span class="n">ration</span><span class="p">,</span> <span class="n">sword</span><span class="p">,</span> <span class="n">torch</span><span class="p">,</span> <span class="n">torch</span><span class="p">,</span> <span class="n">torch</span><span class="p">,</span> <span class="n">torch</span><span class="p">,</span> <span class="n">torch</span><span class="p">,</span>
<span class="n">tinderbox</span><span class="p">,</span> <span class="n">chisel</span><span class="p">,</span> <span class="n">whistle</span>
<span class="o">----------------------------------------------------------------------------------------</span>
<span class="mf">1.</span> <span class="n">Change</span> <span class="n">your</span> <span class="n">name</span>
<span class="mf">2.</span> <span class="n">Swap</span> <span class="n">two</span> <span class="n">of</span> <span class="n">your</span> <span class="n">ability</span> <span class="n">scores</span> <span class="p">(</span><span class="n">once</span><span class="p">)</span>
<span class="mf">3.</span> <span class="n">Accept</span> <span class="ow">and</span> <span class="n">create</span> <span class="n">character</span>
</pre></div>
</div>
<p>If you select 1, you get a new menu node:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Your</span> <span class="n">current</span> <span class="n">name</span> <span class="ow">is</span> <span class="n">Silas</span><span class="o">.</span> <span class="n">Enter</span> <span class="n">a</span> <span class="n">new</span> <span class="n">name</span> <span class="ow">or</span> <span class="n">leave</span> <span class="n">empty</span> <span class="n">to</span> <span class="n">abort</span><span class="o">.</span>
<span class="o">-----------------------------------------------------------------------------------------</span>
</pre></div>
</div>
<p>You can now enter a new name. When pressing return youll get back to the first menu node
showing your character, now with the new name.</p>
<p>If you select 2, you go to another menu node:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>Your current abilities:
STR +1
DEX +2
CON +1
INT +3
WIS +1
CHA +2
You can swap the values of two abilities around.
You can only do this once, so choose carefully!
To swap the values of e.g. STR and INT, write &#39;STR INT&#39;. Empty to abort.
------------------------------------------------------------------------------------------
</pre></div>
</div>
<p>If you enter <code class="docutils literal notranslate"><span class="pre">WIS</span> <span class="pre">CHA</span></code> here, WIS will become <code class="docutils literal notranslate"><span class="pre">+2</span></code> and <code class="docutils literal notranslate"><span class="pre">CHA</span></code> <code class="docutils literal notranslate"><span class="pre">+1</span></code>. You will then again go back
to the main node to see your new character, but this time the option to swap will no longer be
available (you can only do it once).</p>
<p>If you finally select the <code class="docutils literal notranslate"><span class="pre">Accept</span> <span class="pre">and</span> <span class="pre">create</span> <span class="pre">character</span></code> option, the character will be created
and youll leave the menu;</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Character was created!
</pre></div>
</div>
</section>
<section id="random-tables">
<h2><span class="section-number">6.2. </span>Random tables<a class="headerlink" href="#random-tables" title="Permalink to this headline"></a></h2>
<aside class="sidebar">
<p>Full Knave random tables are found in
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.random_tables.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/random_tables.py</span></a>.</p>
</aside>
<blockquote>
<div><p>Make a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/random_tables.py</span></code>.</p>
</div></blockquote>
<p>Since most of <em>Knave</em>s character generation is random we will need to roll on random tables
from the <em>Knave</em> rulebook. While we added the ability to roll on a random table back in the
<a class="reference internal" href="Beginner-Tutorial-Rules.html"><span class="doc std std-doc">Rules Tutorial</span></a>, we havent added the relevant tables yet.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/random_tables.py </span>
<span class="n">chargen_tables</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;physique&quot;</span><span class="p">:</span> <span class="p">[</span>
<span class="s2">&quot;athletic&quot;</span><span class="p">,</span> <span class="s2">&quot;brawny&quot;</span><span class="p">,</span> <span class="s2">&quot;corpulent&quot;</span><span class="p">,</span> <span class="s2">&quot;delicate&quot;</span><span class="p">,</span> <span class="s2">&quot;gaunt&quot;</span><span class="p">,</span> <span class="s2">&quot;hulking&quot;</span><span class="p">,</span> <span class="s2">&quot;lanky&quot;</span><span class="p">,</span>
<span class="s2">&quot;ripped&quot;</span><span class="p">,</span> <span class="s2">&quot;rugged&quot;</span><span class="p">,</span> <span class="s2">&quot;scrawny&quot;</span><span class="p">,</span> <span class="s2">&quot;short&quot;</span><span class="p">,</span> <span class="s2">&quot;sinewy&quot;</span><span class="p">,</span> <span class="s2">&quot;slender&quot;</span><span class="p">,</span> <span class="s2">&quot;flabby&quot;</span><span class="p">,</span>
<span class="s2">&quot;statuesque&quot;</span><span class="p">,</span> <span class="s2">&quot;stout&quot;</span><span class="p">,</span> <span class="s2">&quot;tiny&quot;</span><span class="p">,</span> <span class="s2">&quot;towering&quot;</span><span class="p">,</span> <span class="s2">&quot;willowy&quot;</span><span class="p">,</span> <span class="s2">&quot;wiry&quot;</span><span class="p">,</span>
<span class="p">],</span>
<span class="s2">&quot;face&quot;</span><span class="p">:</span> <span class="p">[</span>
<span class="s2">&quot;bloated&quot;</span><span class="p">,</span> <span class="s2">&quot;blunt&quot;</span><span class="p">,</span> <span class="s2">&quot;bony&quot;</span><span class="p">,</span> <span class="c1"># ... </span>
<span class="p">],</span> <span class="c1"># ... </span>
<span class="p">}</span>
</pre></div>
</div>
<p>The tables are just copied from the <em>Knave</em> rules. We group the aspects in a dict
<code class="docutils literal notranslate"><span class="pre">character_generation</span></code> to separate chargen-only tables from other random tables well also
keep in here.</p>
</section>
<section id="storing-state-of-the-menu">
<h2><span class="section-number">6.3. </span>Storing state of the menu<a class="headerlink" href="#storing-state-of-the-menu" title="Permalink to this headline"></a></h2>
<aside class="sidebar">
<p>There is a full implementation of the chargen in
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.chargen.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/chargen.py</span></a>.</p>
</aside>
<blockquote>
<div><p>create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/chargen.py</span></code>.</p>
</div></blockquote>
<p>During character generation we will need an entity to store/retain the changes, like a
temporary character sheet.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py </span>
<span class="kn">from</span> <span class="nn">.random_tables</span> <span class="kn">import</span> <span class="n">chargen_tables</span>
<span class="kn">from</span> <span class="nn">.rules</span> <span class="kn">import</span> <span class="n">dice</span>
<span class="k">class</span> <span class="nc">TemporaryCharacterSheet</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">_random_ability</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">min</span><span class="p">(</span><span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d6&quot;</span><span class="p">),</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d6&quot;</span><span class="p">),</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d6&quot;</span><span class="p">))</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">ability_changes</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># how many times we tried swap abilities</span>
<span class="c1"># name will likely be modified later</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d282&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">])</span>
<span class="c1"># base attribute values</span>
<span class="bp">self</span><span class="o">.</span><span class="n">strength</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_random_ability</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">dexterity</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_random_ability</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">constitution</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_random_ability</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">intelligence</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_random_ability</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">wisdom</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_random_ability</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">charisma</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_random_ability</span><span class="p">()</span>
<span class="c1"># physical attributes (only for rp purposes)</span>
<span class="n">physique</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;physique&quot;</span><span class="p">])</span>
<span class="n">face</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;face&quot;</span><span class="p">])</span>
<span class="n">skin</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;skin&quot;</span><span class="p">])</span>
<span class="n">hair</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;hair&quot;</span><span class="p">])</span>
<span class="n">clothing</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;clothing&quot;</span><span class="p">])</span>
<span class="n">speech</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;speech&quot;</span><span class="p">])</span>
<span class="n">virtue</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;virtue&quot;</span><span class="p">])</span>
<span class="n">vice</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;vice&quot;</span><span class="p">])</span>
<span class="n">background</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;background&quot;</span><span class="p">])</span>
<span class="n">misfortune</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;misfortune&quot;</span><span class="p">])</span>
<span class="n">alignment</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;alignment&quot;</span><span class="p">])</span>
<span class="bp">self</span><span class="o">.</span><span class="n">desc</span> <span class="o">=</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;You are </span><span class="si">{</span><span class="n">physique</span><span class="si">}</span><span class="s2"> with a </span><span class="si">{</span><span class="n">face</span><span class="si">}</span><span class="s2"> face, </span><span class="si">{</span><span class="n">skin</span><span class="si">}</span><span class="s2"> skin, </span><span class="si">{</span><span class="n">hair</span><span class="si">}</span><span class="s2"> hair, </span><span class="si">{</span><span class="n">speech</span><span class="si">}</span><span class="s2"> speech,&quot;</span>
<span class="sa">f</span><span class="s2">&quot; and </span><span class="si">{</span><span class="n">clothing</span><span class="si">}</span><span class="s2"> clothing. You were a </span><span class="si">{</span><span class="n">background</span><span class="o">.</span><span class="n">title</span><span class="p">()</span><span class="si">}</span><span class="s2">, but you were&quot;</span>
<span class="sa">f</span><span class="s2">&quot; </span><span class="si">{</span><span class="n">misfortune</span><span class="si">}</span><span class="s2"> and ended up a knave. You are </span><span class="si">{</span><span class="n">virtue</span><span class="si">}</span><span class="s2"> but also </span><span class="si">{</span><span class="n">vice</span><span class="si">}</span><span class="s2">. You are of the&quot;</span>
<span class="sa">f</span><span class="s2">&quot; </span><span class="si">{</span><span class="n">alignment</span><span class="si">}</span><span class="s2"> alignment.&quot;</span>
<span class="p">)</span>
<span class="c1"># </span>
<span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d8&quot;</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span>
<span class="bp">self</span><span class="o">.</span><span class="n">xp</span> <span class="o">=</span> <span class="mi">0</span>
<span class="bp">self</span><span class="o">.</span><span class="n">level</span> <span class="o">=</span> <span class="mi">1</span>
<span class="c1"># random equipment</span>
<span class="bp">self</span><span class="o">.</span><span class="n">armor</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;armor&quot;</span><span class="p">])</span>
<span class="n">_helmet_and_shield</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;helmets and shields&quot;</span><span class="p">])</span>
<span class="bp">self</span><span class="o">.</span><span class="n">helmet</span> <span class="o">=</span> <span class="s2">&quot;helmet&quot;</span> <span class="k">if</span> <span class="s2">&quot;helmet&quot;</span> <span class="ow">in</span> <span class="n">_helmet_and_shield</span> <span class="k">else</span> <span class="s2">&quot;none&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">shield</span> <span class="o">=</span> <span class="s2">&quot;shield&quot;</span> <span class="k">if</span> <span class="s2">&quot;shield&quot;</span> <span class="ow">in</span> <span class="n">_helmet_and_shield</span> <span class="k">else</span> <span class="s2">&quot;none&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">weapon</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;starting weapon&quot;</span><span class="p">])</span>
<span class="bp">self</span><span class="o">.</span><span class="n">backpack</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">&quot;ration&quot;</span><span class="p">,</span>
<span class="s2">&quot;ration&quot;</span><span class="p">,</span>
<span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;dungeoning gear&quot;</span><span class="p">]),</span>
<span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;dungeoning gear&quot;</span><span class="p">]),</span>
<span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;general gear 1&quot;</span><span class="p">]),</span>
<span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">&quot;general gear 2&quot;</span><span class="p">]),</span>
<span class="p">]</span>
</pre></div>
</div>
<p>Here we have followed the <em>Knave</em> rulebook to randomize abilities, description and equipment. The <code class="docutils literal notranslate"><span class="pre">dice.roll()</span></code> and <code class="docutils literal notranslate"><span class="pre">dice.roll_random_table</span></code> methods now become very useful! Everything here should be easy to follow.</p>
<p>The main difference from baseline <em>Knave</em> is that we make a table of “starting weapon” (in Knave you can pick whatever you like).</p>
<p>We also initialize <code class="docutils literal notranslate"><span class="pre">.ability_changes</span> <span class="pre">=</span> <span class="pre">0</span></code>. Knave only allows us to swap the values of two
Abilities <em>once</em>. We will use this to know if it has been done or not.</p>
<section id="showing-the-sheet">
<h3><span class="section-number">6.3.1. </span>Showing the sheet<a class="headerlink" href="#showing-the-sheet" title="Permalink to this headline"></a></h3>
<p>Now that we have our temporary character sheet, we should make it easy to visualize it.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py </span>
<span class="n">_TEMP_SHEET</span> <span class="o">=</span> <span class="s2">&quot;&quot;&quot;</span>
<span class="si">{name}</span>
<span class="s2">STR +</span><span class="si">{strength}</span>
<span class="s2">DEX +</span><span class="si">{dexterity}</span>
<span class="s2">CON +</span><span class="si">{constitution}</span>
<span class="s2">INT +</span><span class="si">{intelligence}</span>
<span class="s2">WIS +</span><span class="si">{wisdom}</span>
<span class="s2">CHA +</span><span class="si">{charisma}</span>
<span class="si">{description}</span>
<span class="s2"> </span>
<span class="s2">Your belongings:</span>
<span class="si">{equipment}</span>
<span class="s2">&quot;&quot;&quot;</span>
<span class="k">class</span> <span class="nc">TemporaryCharacterSheet</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">show_sheet</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">equipment</span> <span class="o">=</span> <span class="p">(</span>
<span class="nb">str</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
<span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">armor</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">shield</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">weapon</span><span class="p">]</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">backpack</span>
<span class="k">if</span> <span class="n">item</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">_TEMP_SHEET</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
<span class="n">name</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="n">strength</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">strength</span><span class="p">,</span>
<span class="n">dexterity</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">dexterity</span><span class="p">,</span>
<span class="n">constitution</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">constitution</span><span class="p">,</span>
<span class="n">intelligence</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">intelligence</span><span class="p">,</span>
<span class="n">wisdom</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">wisdom</span><span class="p">,</span>
<span class="n">charisma</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">charisma</span><span class="p">,</span>
<span class="n">description</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">desc</span><span class="p">,</span>
<span class="n">equipment</span><span class="o">=</span><span class="s2">&quot;, &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">equipment</span><span class="p">),</span>
<span class="p">)</span>
</pre></div>
</div>
<p>The new <code class="docutils literal notranslate"><span class="pre">show_sheet</span></code> method collect the data from the temporary sheet and return it in a pretty form. Making a template string like <code class="docutils literal notranslate"><span class="pre">_TEMP_SHEET</span></code> makes it easier to change things later if you want to change how things look.</p>
</section>
<section id="apply-character">
<h3><span class="section-number">6.3.2. </span>Apply character<a class="headerlink" href="#apply-character" title="Permalink to this headline"></a></h3>
<p>Once we are happy with our character, we need to actually create it with the stats we chose.
This is a bit more involved.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py </span>
<span class="c1"># ... </span>
<span class="kn">from</span> <span class="nn">.characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_object</span>
<span class="kn">from</span> <span class="nn">evennia.prototypes.spawner</span> <span class="kn">import</span> <span class="n">spawn</span>
<span class="k">class</span> <span class="nc">TemporaryCharacterSheet</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">apply</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># create character object with given abilities</span>
<span class="n">new_character</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span>
<span class="n">EvAdventureCharacter</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="n">attrs</span><span class="o">=</span><span class="p">(</span>
<span class="p">(</span><span class="s2">&quot;strength&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">strength</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;dexterity&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">dexterity</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;constitution&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">constitution</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;intelligence&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">intelligence</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;wisdom&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">wisdom</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;charisma&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">wisdom</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;hp&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;hp_max&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;desc&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">desc</span><span class="p">),</span>
<span class="p">),</span>
<span class="p">)</span>
<span class="c1"># spawn equipment (will require prototypes created before it works)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">weapon</span><span class="p">:</span>
<span class="n">weapon</span> <span class="o">=</span> <span class="n">spawn</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">weapon</span><span class="p">)</span>
<span class="n">new_character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="n">weapon</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">shield</span><span class="p">:</span>
<span class="n">shield</span> <span class="o">=</span> <span class="n">spawn</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">shield</span><span class="p">)</span>
<span class="n">new_character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="n">shield</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">armor</span><span class="p">:</span>
<span class="n">armor</span> <span class="o">=</span> <span class="n">spawn</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">armor</span><span class="p">)</span>
<span class="n">new_character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="n">armor</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">:</span>
<span class="n">helmet</span> <span class="o">=</span> <span class="n">spawn</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">)</span>
<span class="n">new_character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="n">helmet</span><span class="p">)</span>
<span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">backpack</span><span class="p">:</span>
<span class="n">item</span> <span class="o">=</span> <span class="n">spawn</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
<span class="n">new_character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">store</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
<span class="k">return</span> <span class="n">new_character</span>
</pre></div>
</div>
<p>We use <code class="docutils literal notranslate"><span class="pre">create_object</span></code> to create a new <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code>. We feed it with all relevant data from the temporary character sheet. This is when these become an actual character.</p>
<aside class="sidebar">
<p>A prototype is basically a <code class="docutils literal notranslate"><span class="pre">dict</span></code> describing how the object should be created. Since
its just a piece of code, it can stored in a Python module and used to quickly <em>spawn</em> (create)
things from those prototypes.</p>
</aside>
<p>Each piece of equipment is an object in in its own right. We will here assume that all game
items are defined as <a class="reference internal" href="../../../Components/Prototypes.html"><span class="doc std std-doc">Prototypes</span></a> keyed to its name, such as “sword”, “brigandine
armor” etc.</p>
<p>We havent actually created those prototypes yet, so for now well need to assume they are there. Once a piece of equipment has been spawned, we make sure to move it into the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> we created in the <a class="reference internal" href="Beginner-Tutorial-Equipment.html"><span class="doc std std-doc">Equipment lesson</span></a>.</p>
</section>
</section>
<section id="initializing-evmenu">
<h2><span class="section-number">6.4. </span>Initializing EvMenu<a class="headerlink" href="#initializing-evmenu" title="Permalink to this headline"></a></h2>
<p>Evennia comes with a full menu-generation system based on <a class="reference internal" href="../../../Components/Command-Sets.html"><span class="doc std std-doc">Command sets</span></a>, called
<a class="reference internal" href="../../../Components/EvMenu.html"><span class="doc std std-doc">EvMenu</span></a>.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">EvMenu</span>
<span class="c1"># ...</span>
<span class="c1"># chargen menu </span>
<span class="c1"># this goes to the bottom of the module</span>
<span class="k">def</span> <span class="nf">start_chargen</span><span class="p">(</span><span class="n">caller</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="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> This is a start point for spinning up the chargen from a command later.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">menutree</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># TODO!</span>
<span class="c1"># this generates all random components of the character</span>
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">TemporaryCharacterSheet</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="n">menutree</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span> <span class="n">tmp_character</span><span class="o">=</span><span class="n">tmp_character</span><span class="p">)</span>
</pre></div>
</div>
<p>This first function is what we will call from elsewhere (for example from a custom <code class="docutils literal notranslate"><span class="pre">charcreate</span></code>
command) to kick the menu into gear.</p>
<p>It takes the <code class="docutils literal notranslate"><span class="pre">caller</span></code> (the one to want to start the menu) and a <code class="docutils literal notranslate"><span class="pre">session</span></code> argument. The latter will help track just which client-connection we are using (depending on Evennia settings, you could be connecting with multiple clients).</p>
<p>We create a <code class="docutils literal notranslate"><span class="pre">TemporaryCharacterSheet</span></code> and call <code class="docutils literal notranslate"><span class="pre">.generate()</span></code> to make a random character. We then feed all this into <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code>.</p>
<p>The moment this happens, the user will be in the menu, there are no further steps needed.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">menutree</span></code> is what well create next. It describes which menu nodes are available to jump
between.</p>
</section>
<section id="main-node-choosing-what-to-do">
<h2><span class="section-number">6.5. </span>Main Node: Choosing what to do<a class="headerlink" href="#main-node-choosing-what-to-do" title="Permalink to this headline"></a></h2>
<p>This is the first menu node. It will act as a central hub, from which one can choose different
actions.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py </span>
<span class="c1"># ...</span>
<span class="c1"># at the end of the module, but before the `start_chargen` function</span>
<span class="k">def</span> <span class="nf">node_chargen</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">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">&quot;tmp_character&quot;</span><span class="p">]</span>
<span class="n">text</span> <span class="o">=</span> <span class="n">tmp_character</span><span class="o">.</span><span class="n">show_sheet</span><span class="p">()</span>
<span class="n">options</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span>
<span class="s2">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Change your name&quot;</span><span class="p">,</span>
<span class="s2">&quot;goto&quot;</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;node_change_name&quot;</span><span class="p">,</span> <span class="n">kwargs</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">]</span>
<span class="k">if</span> <span class="n">tmp_character</span><span class="o">.</span><span class="n">ability_changes</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">options</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
<span class="p">{</span>
<span class="s2">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Swap two of your ability scores (once)&quot;</span><span class="p">,</span>
<span class="s2">&quot;goto&quot;</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;node_swap_abilities&quot;</span><span class="p">,</span> <span class="n">kwargs</span><span class="p">),</span>
<span class="p">}</span>
<span class="p">)</span>
<span class="n">options</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
<span class="p">{</span>
<span class="s2">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Accept and create character&quot;</span><span class="p">,</span>
<span class="s2">&quot;goto&quot;</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;node_apply_character&quot;</span><span class="p">,</span> <span class="n">kwargs</span><span class="p">)</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="c1"># ...</span>
</pre></div>
</div>
<p>A lot to unpack here! In Evennia, its convention to name your node-functions <code class="docutils literal notranslate"><span class="pre">node_*</span></code>. While
not required, it helps you track what is a node and not.</p>
<p>Every menu-node, should accept <code class="docutils literal notranslate"><span class="pre">caller,</span> <span class="pre">raw_string,</span> <span class="pre">**kwargs</span></code> as arguments. Here <code class="docutils literal notranslate"><span class="pre">caller</span></code> is the <code class="docutils literal notranslate"><span class="pre">caller</span></code> you passed into the <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> call. <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> is the input given by the user in order to <em>get to this node</em>, so currently empty. The <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> are all extra keyword arguments passed into <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code>. They can also be passed between nodes. In this case, we passed the keyword <code class="docutils literal notranslate"><span class="pre">tmp_character</span></code> to <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code>. We now have the temporary character sheet available in the node!</p>
<p>An <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> node must always return two things - <code class="docutils literal notranslate"><span class="pre">text</span></code> and <code class="docutils literal notranslate"><span class="pre">options</span></code>. The <code class="docutils literal notranslate"><span class="pre">text</span></code> is what will
show to the user when looking at this node. The <code class="docutils literal notranslate"><span class="pre">options</span></code> are, well, what options should be
presented to move on from here to some other place.</p>
<p>For the text, we simply get a pretty-print of the temporary character sheet. A single option is
defined as a <code class="docutils literal notranslate"><span class="pre">dict</span></code> like this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;name&quot;</span><span class="o">.</span> <span class="s2">&quot;alias1&quot;</span><span class="p">,</span> <span class="s2">&quot;alias2&quot;</span><span class="p">,</span> <span class="o">...</span><span class="p">),</span> <span class="c1"># if skipped, auto-show a number</span>
<span class="s2">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;text to describe what happens when selecting option&quot;</span><span class="p">,</span><span class="o">.</span>
<span class="s2">&quot;goto&quot;</span><span class="p">:</span> <span class="p">(</span><span class="s2">&quot;name of node or a callable&quot;</span><span class="p">,</span> <span class="n">kwargs_to_pass_into_next_node_or_callable</span><span class="p">)</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Multiple option-dicts are returned in a list or tuple. The <code class="docutils literal notranslate"><span class="pre">goto</span></code> option-key is important to
understand. The job of this is to either point directly to another node (by giving its name), or
by pointing to a Python callable (like a function) <em>that then returns that name</em>. You can also
pass kwargs (as a dict). This will be made available as <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> in the callable or next node.</p>
<p>While an option can have a <code class="docutils literal notranslate"><span class="pre">key</span></code>, you can also skip it to just get a running number.</p>
<p>In our <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code> node, we point to three nodes by name: <code class="docutils literal notranslate"><span class="pre">node_change_name</span></code>,
<code class="docutils literal notranslate"><span class="pre">node_swap_abilities</span></code>, and <code class="docutils literal notranslate"><span class="pre">node_apply_character</span></code>. We also make sure to pass along <code class="docutils literal notranslate"><span class="pre">kwargs</span></code>
to each node, since that contains our temporary character sheet.</p>
<p>The middle of these options only appear if we havent already switched two abilities around - to know this, we check the <code class="docutils literal notranslate"><span class="pre">.ability_changes</span></code> property to make sure its still 0.</p>
</section>
<section id="node-changing-your-name">
<h2><span class="section-number">6.6. </span>Node: Changing your name<a class="headerlink" href="#node-changing-your-name" title="Permalink to this headline"></a></h2>
<p>This is where you end up if you opted to change your name in <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code>.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py</span>
<span class="c1"># ...</span>
<span class="c1"># after previous node </span>
<span class="k">def</span> <span class="nf">_update_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="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Used by node_change_name below to check what user </span>
<span class="sd"> entered and update the name if appropriate.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="n">raw_string</span><span class="p">:</span>
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">&quot;tmp_character&quot;</span><span class="p">]</span>
<span class="n">tmp_character</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">raw_string</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="o">.</span><span class="n">capitalize</span><span class="p">()</span>
<span class="k">return</span> <span class="s2">&quot;node_chargen&quot;</span><span class="p">,</span> <span class="n">kwargs</span>
<span class="k">def</span> <span class="nf">node_change_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="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Change the random name of the character.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">&quot;tmp_character&quot;</span><span class="p">]</span>
<span class="n">text</span> <span class="o">=</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;Your current name is |w</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">|n. &quot;</span>
<span class="s2">&quot;Enter a new name or leave empty to abort.&quot;</span>
<span class="p">)</span>
<span class="n">options</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;_default&quot;</span><span class="p">,</span>
<span class="s2">&quot;goto&quot;</span><span class="p">:</span> <span class="p">(</span><span class="n">_update_name</span><span class="p">,</span> <span class="n">kwargs</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>There are two functions here - the menu node itself (<code class="docutils literal notranslate"><span class="pre">node_change_name</span></code>) and a
helper <em>goto_function</em> (<code class="docutils literal notranslate"><span class="pre">_update_name</span></code>) to handle the users input.</p>
<p>For the (single) option, we use a special <code class="docutils literal notranslate"><span class="pre">key</span></code> named <code class="docutils literal notranslate"><span class="pre">_default</span></code>. This makes this option
a catch-all: If the user enters something that does not match any other option, this is
the option that will be used. Since we have no other options here, we will always use this option no matter what the user enters.</p>
<p>Also note that the <code class="docutils literal notranslate"><span class="pre">goto</span></code> part of the option points to the <code class="docutils literal notranslate"><span class="pre">_update_name</span></code> callable rather than to
the name of a node. Its important we keep passing <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> along to it!</p>
<p>When a user writes anything at this node, the <code class="docutils literal notranslate"><span class="pre">_update_name</span></code> callable will be called. This has
the same arguments as a node, but it is <em>not</em> a node - we will only use it to <em>figure out</em> which
node to go to next.</p>
<p>In <code class="docutils literal notranslate"><span class="pre">_update_name</span></code> we now have a use for the <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> argument - this is what was written by the user on the previous node, remember? This is now either an empty string (meaning to ignore it) or the new name of the character.</p>
<p>A goto-function like <code class="docutils literal notranslate"><span class="pre">_update_name</span></code> must return the name of the next node to use. It can also
optionally return the <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> to pass into that node - we want to always do this, so we dont
loose our temporary character sheet. Here we will always go back to the <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code>.</p>
<blockquote>
<div><p>Hint: If returning <code class="docutils literal notranslate"><span class="pre">None</span></code> from a goto-callable, you will always return to the last node you
were at.</p>
</div></blockquote>
</section>
<section id="node-swapping-abilities-around">
<h2><span class="section-number">6.7. </span>Node: Swapping Abilities around<a class="headerlink" href="#node-swapping-abilities-around" title="Permalink to this headline"></a></h2>
<p>You get here by selecting the second option from the <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code> node.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py </span>
<span class="c1"># ...</span>
<span class="c1"># after previous node </span>
<span class="n">_ABILITIES</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;STR&quot;</span><span class="p">:</span> <span class="s2">&quot;strength&quot;</span><span class="p">,</span>
<span class="s2">&quot;DEX&quot;</span><span class="p">:</span> <span class="s2">&quot;dexterity&quot;</span><span class="p">,</span>
<span class="s2">&quot;CON&quot;</span><span class="p">:</span> <span class="s2">&quot;constitution&quot;</span><span class="p">,</span>
<span class="s2">&quot;INT&quot;</span><span class="p">:</span> <span class="s2">&quot;intelligence&quot;</span><span class="p">,</span>
<span class="s2">&quot;WIS&quot;</span><span class="p">:</span> <span class="s2">&quot;wisdom&quot;</span><span class="p">,</span>
<span class="s2">&quot;CHA&quot;</span><span class="p">:</span> <span class="s2">&quot;charisma&quot;</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">def</span> <span class="nf">_swap_abilities</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="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Used by node_swap_abilities to parse the user&#39;s input and swap ability</span>
<span class="sd"> values.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="n">raw_string</span><span class="p">:</span>
<span class="n">abi1</span><span class="p">,</span> <span class="o">*</span><span class="n">abi2</span> <span class="o">=</span> <span class="n">raw_string</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">abi2</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">&quot;That doesn&#39;t look right.&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">None</span><span class="p">,</span> <span class="n">kwargs</span>
<span class="n">abi2</span> <span class="o">=</span> <span class="n">abi2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">abi1</span><span class="p">,</span> <span class="n">abi2</span> <span class="o">=</span> <span class="n">abi1</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">(),</span> <span class="n">abi2</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="k">if</span> <span class="n">abi1</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">_ABILITIES</span> <span class="ow">or</span> <span class="n">abi2</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">_ABILITIES</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">&quot;Not a familiar set of abilites.&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">None</span><span class="p">,</span> <span class="n">kwargs</span>
<span class="c1"># looks okay = swap values. We need to convert STR to strength etc</span>
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">&quot;tmp_character&quot;</span><span class="p">]</span>
<span class="n">abi1</span> <span class="o">=</span> <span class="n">_ABILITIES</span><span class="p">[</span><span class="n">abi1</span><span class="p">]</span>
<span class="n">abi2</span> <span class="o">=</span> <span class="n">_ABILITIES</span><span class="p">[</span><span class="n">abi2</span><span class="p">]</span>
<span class="n">abival1</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">tmp_character</span><span class="p">,</span> <span class="n">abi1</span><span class="p">)</span>
<span class="n">abival2</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">tmp_character</span><span class="p">,</span> <span class="n">abi2</span><span class="p">)</span>
<span class="nb">setattr</span><span class="p">(</span><span class="n">tmp_character</span><span class="p">,</span> <span class="n">abi1</span><span class="p">,</span> <span class="n">abival2</span><span class="p">)</span>
<span class="nb">setattr</span><span class="p">(</span><span class="n">tmp_character</span><span class="p">,</span> <span class="n">abi2</span><span class="p">,</span> <span class="n">abival1</span><span class="p">)</span>
<span class="n">tmp_character</span><span class="o">.</span><span class="n">ability_changes</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="s2">&quot;node_chargen&quot;</span><span class="p">,</span> <span class="n">kwargs</span>
<span class="k">def</span> <span class="nf">node_swap_abilities</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="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> One is allowed to swap the values of two abilities around, once.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">&quot;tmp_character&quot;</span><span class="p">]</span>
<span class="n">text</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2">Your current abilities:</span>
<span class="s2">STR +</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">strength</span><span class="si">}</span>
<span class="s2">DEX +</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">dexterity</span><span class="si">}</span>
<span class="s2">CON +</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">constitution</span><span class="si">}</span>
<span class="s2">INT +</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">intelligence</span><span class="si">}</span>
<span class="s2">WIS +</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">wisdom</span><span class="si">}</span>
<span class="s2">CHA +</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">charisma</span><span class="si">}</span>
<span class="s2">You can swap the values of two abilities around.</span>
<span class="s2">You can only do this once, so choose carefully!</span>
<span class="s2">To swap the values of e.g. STR and INT, write |wSTR INT|n. Empty to abort.</span>
<span class="s2">&quot;&quot;&quot;</span>
<span class="n">options</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;_default&quot;</span><span class="p">,</span> <span class="s2">&quot;goto&quot;</span><span class="p">:</span> <span class="p">(</span><span class="n">_swap_abilities</span><span class="p">,</span> <span class="n">kwargs</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 is more code, but the logic is the same - we have a node (<code class="docutils literal notranslate"><span class="pre">node_swap_abilities</span></code>) and
and a goto-callable helper (<code class="docutils literal notranslate"><span class="pre">_swap_abilities</span></code>). We catch everything the user writes on the
node (such as <code class="docutils literal notranslate"><span class="pre">WIS</span> <span class="pre">CON</span></code>) and feed it into the helper.</p>
<p>In <code class="docutils literal notranslate"><span class="pre">_swap_abilities</span></code>, we need to analyze the <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> from the user to see what they
want to do.</p>
<p>Most code in the helper is validating the user didnt enter nonsense. If they did,
we use <code class="docutils literal notranslate"><span class="pre">caller.msg()</span></code> to tell them and then return <code class="docutils literal notranslate"><span class="pre">None,</span> <span class="pre">kwargs</span></code>, which re-runs the same node (the name-selection) all over again.</p>
<p>Since we want users to be able to write “CON” instead of the longer “constitution”, we need a mapping <code class="docutils literal notranslate"><span class="pre">_ABILITIES</span></code> to easily convert between the two (its stored as <code class="docutils literal notranslate"><span class="pre">consitution</span></code> on the temporary character sheet). Once we know which abilities they want to swap, we do so and tick up the <code class="docutils literal notranslate"><span class="pre">.ability_changes</span></code> counter. This means this option will no longer be available from the main node.</p>
<p>Finally, we return to <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code> again.</p>
</section>
<section id="node-creating-the-character">
<h2><span class="section-number">6.8. </span>Node: Creating the Character<a class="headerlink" href="#node-creating-the-character" title="Permalink to this headline"></a></h2>
<p>We get here from the main node by opting to finish chargen.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">node_apply_character</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="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> End chargen and create the character. We will also puppet it.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">&quot;tmp_character&quot;</span><span class="p">]</span>
<span class="n">new_character</span> <span class="o">=</span> <span class="n">tmp_character</span><span class="o">.</span><span class="n">apply</span><span class="p">(</span><span class="n">caller</span><span class="p">)</span>
<span class="n">caller</span><span class="o">.</span><span class="n">account</span><span class="o">.</span><span class="n">add_character</span><span class="p">(</span><span class="n">new_character</span><span class="p">)</span>
<span class="n">text</span> <span class="o">=</span> <span class="s2">&quot;Character created!&quot;</span>
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="kc">None</span>
</pre></div>
</div>
<p>When entering the node, we will take the Temporary character sheet and use its <code class="docutils literal notranslate"><span class="pre">.appy</span></code> method to create a new Character with all equipment.</p>
<p>This is what is called an <em>end node</em>, because it returns <code class="docutils literal notranslate"><span class="pre">None</span></code> instead of options. After this, the menu will exit. We will be back to the default character selection screen. The characters found on that screen are the ones listed in the <code class="docutils literal notranslate"><span class="pre">_playable_characters</span></code> Attribute, so we need to also the new character to it.</p>
</section>
<section id="tying-the-nodes-together">
<h2><span class="section-number">6.9. </span>Tying the nodes together<a class="headerlink" href="#tying-the-nodes-together" title="Permalink to this headline"></a></h2>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">start_chargen</span><span class="p">(</span><span class="n">caller</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="sd">&quot;&quot;&quot;</span>
<span class="sd">This is a start point for spinning up the chargen from a command later.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">menutree</span> <span class="o">=</span> <span class="p">{</span> <span class="c1"># &lt;----- can now add this!</span>
<span class="s2">&quot;node_chargen&quot;</span><span class="p">:</span> <span class="n">node_chargen</span><span class="p">,</span>
<span class="s2">&quot;node_change_name&quot;</span><span class="p">:</span> <span class="n">node_change_name</span><span class="p">,</span>
<span class="s2">&quot;node_swap_abilities&quot;</span><span class="p">:</span> <span class="n">node_swap_abilities</span><span class="p">,</span>
<span class="s2">&quot;node_apply_character&quot;</span><span class="p">:</span> <span class="n">node_apply_character</span>
<span class="p">}</span>
<span class="c1"># this generates all random components of the character</span>
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">TemporaryCharacterSheet</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="n">menutree</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span>
<span class="n">startnode</span><span class="o">=</span><span class="s2">&quot;node_chargen&quot;</span><span class="p">,</span> <span class="c1"># &lt;----- </span>
<span class="n">tmp_character</span><span class="o">=</span><span class="n">tmp_character</span><span class="p">)</span>
</pre></div>
</div>
<p>Now that we have all the nodes, we add them to the <code class="docutils literal notranslate"><span class="pre">menutree</span></code> we left empty before. We only add the nodes, <em>not</em> the goto-helpers! The keys we set in the <code class="docutils literal notranslate"><span class="pre">menutree</span></code> dictionary are the names we should use to point to nodes from inside the menu (and we did).</p>
<p>We also add a keyword argument <code class="docutils literal notranslate"><span class="pre">startnode</span></code> pointing to the <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code> node. This tells EvMenu to first jump into that node when the menu is starting up.</p>
</section>
<section id="conclusions">
<h2><span class="section-number">6.10. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline"></a></h2>
<p>This lesson taught us how to use <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> to make an interactive character generator. In an RPG more complex than <em>Knave</em>, the menu would be bigger and more intricate, but the same principles apply.</p>
<p>Together with the previous lessons we have now fished most of the basics around player
characters - how they store their stats, handle their equipment and how to create them.</p>
<p>In the next lesson well address how EvAdventure <em>Rooms</em> work.</p>
</section>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-Rooms.html" title="7. In-game Rooms"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Equipment.html" title="5. Handling Equipment"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">6. </span>Character Generation</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,983 @@
<!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>9. Combat base framework &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="10. Twitch Combat" href="Beginner-Tutorial-Combat-Twitch.html" />
<link rel="prev" title="8. Non-Player-Characters" href="Beginner-Tutorial-NPCs.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Combat-Twitch.html" title="10. Twitch Combat"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">9. </span>Combat base framework</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">9. Combat base framework</a><ul>
<li><a class="reference internal" href="#combathandler">9.1. CombatHandler</a><ul>
<li><a class="reference internal" href="#combathandler-get-or-create-combathandler">9.1.1. CombatHandler.get_or_create_combathandler</a></li>
<li><a class="reference internal" href="#combathandler-msg">9.1.2. CombatHandler.msg</a></li>
<li><a class="reference internal" href="#combathandler-get-combat-summary">9.1.3. Combathandler.get_combat_summary</a></li>
</ul>
</li>
<li><a class="reference internal" href="#actions">9.2. Actions</a></li>
<li><a class="reference internal" href="#action-dicts">9.3. Action dicts</a></li>
<li><a class="reference internal" href="#action-classes">9.4. Action classes</a><ul>
<li><a class="reference internal" href="#hold-action">9.4.1. Hold Action</a></li>
<li><a class="reference internal" href="#attack-action">9.4.2. Attack Action</a></li>
<li><a class="reference internal" href="#stunt-action">9.4.3. Stunt Action</a></li>
<li><a class="reference internal" href="#use-item-action">9.4.4. Use Item Action</a></li>
<li><a class="reference internal" href="#wield-action">9.4.5. Wield Action</a></li>
</ul>
</li>
<li><a class="reference internal" href="#testing">9.5. Testing</a></li>
<li><a class="reference internal" href="#conclusions">9.6. Conclusions</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-NPCs.html"
title="previous chapter"><span class="section-number">8. </span>Non-Player-Characters</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Combat-Twitch.html"
title="next chapter"><span class="section-number">10. </span>Twitch Combat</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Base.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="combat-base-framework">
<h1><span class="section-number">9. </span>Combat base framework<a class="headerlink" href="#combat-base-framework" title="Permalink to this headline"></a></h1>
<p>Combat is core to many games. Exactly how it works is very game-dependent. In this lesson we will build a framework to implement two common flavors:</p>
<ul class="simple">
<li><p>“Twitch-based” combat (<a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html"><span class="doc std std-doc">specific lesson here</span></a>) means that you perform a combat action by entering a command, and after some delay (which may depend on your skills etc), the action happens. Its called twitch because actions often happen fast enough that changing your strategy may involve some element of quick thinking and a twitchy trigger finger.</p></li>
<li><p>“Turn-based” combat (<a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html"><span class="doc std std-doc">specific lesson here</span></a>) means that players input actions in clear turns. Timeout for entering/queuing your actions is often much longer than twitch-based style. Once everyone made their choice (or the timeout is reached), everyones action happens all at once, after which the next turn starts. This style of combat requires less player reflexes.</p></li>
</ul>
<p>We will design a base combat system that supports both styles.</p>
<ul class="simple">
<li><p>We need a <code class="docutils literal notranslate"><span class="pre">CombatHandler</span></code> to track the progress of combat. This will be a <a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Script</span></a>. Exactly how this works (and where it is stored) will be a bit different between Twitch- and Turnbased combat. We will create its common framework in this lesson.</p></li>
<li><p>Combat are divided into <em>actions</em>. We want to be able to easily extend our combat with more possible actions. An action needs Python code to show what actually happens when the action is performed. We will define such code in <code class="docutils literal notranslate"><span class="pre">Action</span></code> classes.</p></li>
<li><p>We also need a way to describe a <em>specific instance</em> of a given action. That is, when we do an “attack” action, we need at the minimum to know who is being attacked. For this will we use Python <code class="docutils literal notranslate"><span class="pre">dicts</span></code> that we will refer to as <code class="docutils literal notranslate"><span class="pre">action_dicts</span></code>.</p></li>
</ul>
<section id="combathandler">
<h2><span class="section-number">9.1. </span>CombatHandler<a class="headerlink" href="#combathandler" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/combat_base.py</span></code></p>
</div></blockquote>
<aside class="sidebar">
<p>In <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia-contrib-tutorials-evadventure-combat-base"><span class="std std-ref">evennia/contrib/tutorials/evadventure/combat_base.py</span></a> youll find a complete implementation of the base combat module.</p>
</aside>
<p>Our “Combat Handler” will handle the administration around combat. It needs to be <em>persistent</em> (even is we reload the server your combat should keep going).</p>
<p>Creating the CombatHandler is a little of a catch-22 - how it works depends on how Actions and Action-dicts look. But without having the CombatHandler, its hard to know how to design Actions and Action-dicts. So well start with its general structure and fill out the details later in this lesson.</p>
<p>Below, methods with <code class="docutils literal notranslate"><span class="pre">pass</span></code> will be filled out this lesson while those raising <code class="docutils literal notranslate"><span class="pre">NotImplementedError</span></code> will be different for Twitch/Turnbased combat and will be implemented in their respective lessons following this one.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultScript</span>
<span class="k">class</span> <span class="nc">CombatFailure</span><span class="p">(</span><span class="ne">RuntimeError</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;If some error happens in combat&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">EvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">DefaultSCript</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> This should be created when combat starts. It &#39;ticks&#39; the combat </span>
<span class="sd"> and tracks all sides of it.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># common for all types of combat</span>
<span class="n">action_classes</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># to fill in later </span>
<span class="n">fallback_action_dict</span> <span class="o">=</span> <span class="p">{}</span>
<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">get_or_create_combathandler</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; Get or create combathandler on `obj`.&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">msg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">combatant</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Send a message to all combatants.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">pass</span> <span class="c1"># TODO</span>
<span class="k">def</span> <span class="nf">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Get a nicely formatted &#39;battle report&#39; of combat, from the </span>
<span class="sd"> perspective of the combatant.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">pass</span> <span class="c1"># TODO</span>
<span class="c1"># implemented differently by Twitch- and Turnbased combat</span>
<span class="k">def</span> <span class="nf">get_sides</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Get who&#39;s still alive on the two sides of combat, as a </span>
<span class="sd"> tuple `([allies], [enemies])` from the perspective of `combatant` </span>
<span class="sd"> (who is _not_ included in the `allies` list.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">give_advantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Give advantage to recipient against target.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">give_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Give disadvantage to recipient against target. </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">has_advantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Does combatant have advantage against target?</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">has_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Does combatant have disadvantage against target?</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">queue_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Queue an action for the combatant by providing </span>
<span class="sd"> action dict.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">execute_next_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Perform a combatant&#39;s next action.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">start_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Start combat.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">check_stop_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Check if the combat is over and if it should be stopped.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">stop_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Stop combat and do cleanup.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
</pre></div>
</div>
<p>The Combat Handler is a <a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Script</span></a>. Scripts are typeclassed entities, which means that they are persistently stored in the database. Scripts can optionally be stored “on” other objects (such as on Characters or Rooms) or be global without any such connection. While Scripts has an optional timer component, it is not active by default and Scripts are commonly used just as plain storage. Since Scripts dont have an in-game existence, they are great for storing data on systems of all kinds, including our combat.</p>
<p>Lets implement the generic methods we need.</p>
<section id="combathandler-get-or-create-combathandler">
<h3><span class="section-number">9.1.1. </span>CombatHandler.get_or_create_combathandler<a class="headerlink" href="#combathandler-get-or-create-combathandler" title="Permalink to this headline"></a></h3>
<p>A helper method for quickly getting the combathandler for an ongoing combat and combatant.</p>
<p>We expect to create the script “on” an object (which one we dont know yet, but we expect it to be a typeclassed entity).</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_script</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">DefaultScript</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">get_or_create_combathandler</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Get or create a combathandler on `obj`.</span>
<span class="sd"> </span>
<span class="sd"> Args:</span>
<span class="sd"> obj (any): The Typeclassed entity to store this Script on. </span>
<span class="sd"> Keyword Args:</span>
<span class="sd"> combathandler_key (str): Identifier for script. &#39;combathandler&#39; by</span>
<span class="sd"> default.</span>
<span class="sd"> **kwargs: Extra arguments to the Script, if it is created.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">obj</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">CombatFailure</span><span class="p">(</span><span class="s2">&quot;Cannot start combat without a place to do it!&quot;</span><span class="p">)</span>
<span class="n">combathandler_key</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">,</span> <span class="s2">&quot;combathandler&quot;</span><span class="p">)</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combathandler</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">combathandler</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">combathandler</span><span class="o">.</span><span class="n">id</span><span class="p">:</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">scripts</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">combathandler_key</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">combathandler</span><span class="p">:</span>
<span class="c1"># have to create from scratch</span>
<span class="n">persistent</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">&quot;persistent&quot;</span><span class="p">,</span> <span class="kc">True</span><span class="p">)</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="n">create_script</span><span class="p">(</span>
<span class="bp">cls</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="n">combathandler_key</span><span class="p">,</span>
<span class="n">obj</span><span class="o">=</span><span class="n">obj</span><span class="p">,</span>
<span class="n">persistent</span><span class="o">=</span><span class="n">persistent</span><span class="p">,</span>
<span class="o">**</span><span class="n">kwargs</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">obj</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combathandler</span>
<span class="k">return</span> <span class="n">combathandler</span>
<span class="c1"># ... </span>
</pre></div>
</div>
<p>This helper method uses <code class="docutils literal notranslate"><span class="pre">obj.scripts.get()</span></code> to find if the combat script already exists on the provided <code class="docutils literal notranslate"><span class="pre">obj</span></code>. If not, it will create it using Evennias <a class="reference internal" href="../../../api/evennia.utils.create.html#evennia.utils.create.create_script" title="evennia.utils.create.create_script"><span class="xref myst py py-func">create_script</span></a> function. For some extra speed we cache the handler as <code class="docutils literal notranslate"><span class="pre">obj.ndb.combathandler</span></code> The <code class="docutils literal notranslate"><span class="pre">.ndb.</span></code> (non-db) means that handler is cached only in memory.</p>
<aside class="sidebar">
<p class="sidebar-title">Checking .id (or .pk)</p>
<p>When getting it from cache, we make sure to also check if the combathandler we got has a database <code class="docutils literal notranslate"><span class="pre">.id</span></code> that is not <code class="docutils literal notranslate"><span class="pre">None</span></code> (we could also check <code class="docutils literal notranslate"><span class="pre">.pk</span></code>, stands for “primary key”) . If its <code class="docutils literal notranslate"><span class="pre">None</span></code>, this means the database entity was deleted and we just got its cached python representation from memory - we need to recreate it.</p>
</aside>
<p><code class="docutils literal notranslate"><span class="pre">get_or_create_combathandler</span></code> is decorated to be a <a class="reference external" href="https://docs.python.org/3/library/functions.html#classmethod">classmethod</a>, meaning it should be used on the handler class directly (rather than on an <em>instance</em> of said class). This makes sense because this method actually should return the new instance.</p>
<p>As a class method well need to call this directly on the class, like this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">EvAdventureCombatBaseHandler</span><span class="o">.</span><span class="n">get_or_create_combathandler</span><span class="p">(</span><span class="n">combatant</span><span class="p">)</span>
</pre></div>
</div>
<p>The result will be a new handler <em>or</em> one that was already defined.</p>
</section>
<section id="combathandler-msg">
<h3><span class="section-number">9.1.2. </span>CombatHandler.msg<a class="headerlink" href="#combathandler-msg" title="Permalink to this headline"></a></h3>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">DefaultScript</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">msg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">combatant</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Central place for sending messages to combatants. This allows</span>
<span class="sd"> for adding any combat-specific text-decoration in one place.</span>
<span class="sd"> Args:</span>
<span class="sd"> message (str): The message to send.</span>
<span class="sd"> combatant (Object): The &#39;You&#39; in the message, if any.</span>
<span class="sd"> broadcast (bool): If `False`, `combatant` must be included and</span>
<span class="sd"> will be the only one to see the message. If `True`, send to</span>
<span class="sd"> everyone in the location.</span>
<span class="sd"> location (Object, optional): If given, use this as the location to</span>
<span class="sd"> send broadcast messages to. If not, use `self.obj` as that</span>
<span class="sd"> location.</span>
<span class="sd"> Notes:</span>
<span class="sd"> If `combatant` is given, use `$You/you()` markup to create</span>
<span class="sd"> a message that looks different depending on who sees it. Use</span>
<span class="sd"> `$You(combatant_key)` to refer to other combatants.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">location</span><span class="p">:</span>
<span class="n">location</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span>
<span class="n">location_objs</span> <span class="o">=</span> <span class="n">location</span><span class="o">.</span><span class="n">contents</span>
<span class="n">exclude</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">broadcast</span> <span class="ow">and</span> <span class="n">combatant</span><span class="p">:</span>
<span class="n">exclude</span> <span class="o">=</span> <span class="p">[</span><span class="n">obj</span> <span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">location_objs</span> <span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">combatant</span><span class="p">]</span>
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="n">message</span><span class="p">,</span>
<span class="n">exclude</span><span class="o">=</span><span class="n">exclude</span><span class="p">,</span>
<span class="n">from_obj</span><span class="o">=</span><span class="n">combatant</span><span class="p">,</span>
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">locobj</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">locobj</span> <span class="k">for</span> <span class="n">locobj</span> <span class="ow">in</span> <span class="n">location_objs</span><span class="p">},</span>
<span class="p">)</span>
<span class="c1"># ... </span>
</pre></div>
</div>
<aside class="sidebar">
<p>The <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> property of a Script is the entity on which the Script sits. If set on a Character, <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> will be that Character. If on a room, itd be that room. For a global script, <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> is <code class="docutils literal notranslate"><span class="pre">None</span></code>.</p>
</aside>
<p>We saw the <code class="docutils literal notranslate"><span class="pre">location.msg_contents()</span></code> method before in the <a class="reference internal" href="Beginner-Tutorial-Objects.html#weapons"><span class="std std-doc">Weapon class of the Objects lesson</span></a>. Its purpose is to take a string on the form <code class="docutils literal notranslate"><span class="pre">&quot;$You()</span> <span class="pre">do</span> <span class="pre">stuff</span> <span class="pre">against</span> <span class="pre">$you(key)&quot;</span></code> and make sure all sides see a string suitable just to them. Our <code class="docutils literal notranslate"><span class="pre">msg()</span></code> method will by default broadcast the message to everyone in the room.</p>
<div style="clear: right;"></div>
<p>Youd use it like this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(throw) </span><span class="si">{</span><span class="n">item</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> at $you(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">).&quot;</span><span class="p">,</span>
<span class="n">combatant</span><span class="o">=</span><span class="n">combatant</span><span class="p">,</span>
<span class="n">location</span><span class="o">=</span><span class="n">combatant</span><span class="o">.</span><span class="n">location</span>
<span class="p">)</span>
</pre></div>
</div>
<p>If combatant is <code class="docutils literal notranslate"><span class="pre">Trickster</span></code>, <code class="docutils literal notranslate"><span class="pre">item.key</span></code> is “a colorful ball” and <code class="docutils literal notranslate"><span class="pre">target.key</span></code> is “Goblin”, then</p>
<p>The combatant would see:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You throw a colorful ball at Goblin.
</pre></div>
</div>
<p>The Goblin sees</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Trickster throws a colorful ball at you.
</pre></div>
</div>
<p>Everyone else in the room sees</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Trickster throws a colorful ball at Goblin.
</pre></div>
</div>
</section>
<section id="combathandler-get-combat-summary">
<h3><span class="section-number">9.1.3. </span>Combathandler.get_combat_summary<a class="headerlink" href="#combathandler-get-combat-summary" title="Permalink to this headline"></a></h3>
<p>We want to be able to show a nice summary of the current combat:</p>
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span><span class="w"> </span>Goblin<span class="w"> </span>shaman<span class="w"> </span><span class="o">(</span>Perfect<span class="o">)</span>
<span class="w"> </span>Gregor<span class="w"> </span><span class="o">(</span>Hurt<span class="o">)</span><span class="w"> </span>Goblin<span class="w"> </span>brawler<span class="o">(</span>Hurt<span class="o">)</span>
<span class="w"> </span>Bob<span class="w"> </span><span class="o">(</span>Perfect<span class="o">)</span><span class="w"> </span>vs<span class="w"> </span>Goblin<span class="w"> </span>grunt<span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="o">(</span>Hurt<span class="o">)</span>
<span class="w"> </span>Goblin<span class="w"> </span>grunt<span class="w"> </span><span class="m">2</span><span class="w"> </span><span class="o">(</span>Perfect<span class="o">)</span>
<span class="w"> </span>Goblin<span class="w"> </span>grunt<span class="w"> </span><span class="m">3</span><span class="w"> </span><span class="o">(</span>Wounded<span class="o">)</span>
</pre></div>
</div>
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span>
<span class="normal">12</span>
<span class="normal">13</span>
<span class="normal">14</span>
<span class="normal">15</span>
<span class="normal">16</span>
<span class="normal">17</span>
<span class="normal">18</span>
<span class="normal">19</span>
<span class="normal">20</span>
<span class="normal">21</span>
<span class="normal">22</span>
<span class="normal">23</span>
<span class="normal">24</span>
<span class="normal">25</span>
<span class="normal">26</span>
<span class="normal">27</span>
<span class="normal">28</span>
<span class="normal">29</span>
<span class="normal">30</span>
<span class="normal">31</span>
<span class="normal">32</span>
<span class="normal">33</span>
<span class="normal">34</span>
<span class="normal">35</span>
<span class="normal">36</span>
<span class="normal">37</span>
<span class="normal">38</span>
<span class="normal">39</span>
<span class="normal">40</span>
<span class="normal">41</span>
<span class="normal">42</span>
<span class="normal">43</span>
<span class="normal">44</span>
<span class="normal">45</span>
<span class="normal">46</span>
<span class="normal">47</span>
<span class="normal">48</span>
<span class="normal">49</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/combat_base.py</span>
<span class="c1"># ...</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">EvTable</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">DefaultScript</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
<span class="hll"> <span class="n">allies</span><span class="p">,</span> <span class="n">enemies</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_sides</span><span class="p">(</span><span class="n">combatant</span><span class="p">)</span>
</span> <span class="n">nallies</span><span class="p">,</span> <span class="n">nenemies</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">allies</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">enemies</span><span class="p">)</span>
<span class="hll">
</span> <span class="c1"># prepare colors and hurt-levels</span>
<span class="n">allies</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">ally</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">ally</span><span class="o">.</span><span class="n">hurt_level</span><span class="si">}</span><span class="s2">)&quot;</span> <span class="k">for</span> <span class="n">ally</span> <span class="ow">in</span> <span class="n">allies</span><span class="p">]</span>
<span class="n">enemies</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">enemy</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">enemy</span><span class="o">.</span><span class="n">hurt_level</span><span class="si">}</span><span class="s2">)&quot;</span> <span class="k">for</span> <span class="n">enemy</span> <span class="ow">in</span> <span class="n">enemies</span><span class="p">]</span>
<span class="hll">
</span><span class="hll"> <span class="c1"># the center column with the &#39;vs&#39;</span>
</span> <span class="n">vs_column</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;&quot;</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="n">nallies</span><span class="p">,</span> <span class="n">nenemies</span><span class="p">))]</span>
<span class="n">vs_column</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">vs_column</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;|wvs|n&quot;</span>
<span class="c1"># the two allies / enemies columns should be centered vertically</span>
<span class="n">diff</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="n">nallies</span> <span class="o">-</span> <span class="n">nenemies</span><span class="p">)</span>
<span class="hll"> <span class="n">top_empty</span> <span class="o">=</span> <span class="n">diff</span> <span class="o">//</span> <span class="mi">2</span>
</span> <span class="n">bot_empty</span> <span class="o">=</span> <span class="n">diff</span> <span class="o">-</span> <span class="n">top_empty</span>
<span class="n">topfill</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;&quot;</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">top_empty</span><span class="p">)]</span>
<span class="n">botfill</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;&quot;</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">bot_empty</span><span class="p">)]</span>
<span class="k">if</span> <span class="n">nallies</span> <span class="o">&gt;=</span> <span class="n">nenemies</span><span class="p">:</span>
<span class="n">enemies</span> <span class="o">=</span> <span class="n">topfill</span> <span class="o">+</span> <span class="n">enemies</span> <span class="o">+</span> <span class="n">botfill</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">allies</span> <span class="o">=</span> <span class="n">topfill</span> <span class="o">+</span> <span class="n">allies</span> <span class="o">+</span> <span class="n">botfill</span>
<span class="c1"># make a table with three columns</span>
<span class="k">return</span> <span class="n">evtable</span><span class="o">.</span><span class="n">EvTable</span><span class="p">(</span>
<span class="n">table</span><span class="o">=</span><span class="p">[</span>
<span class="hll"> <span class="n">evtable</span><span class="o">.</span><span class="n">EvColumn</span><span class="p">(</span><span class="o">*</span><span class="n">allies</span><span class="p">,</span> <span class="n">align</span><span class="o">=</span><span class="s2">&quot;l&quot;</span><span class="p">),</span>
</span> <span class="n">evtable</span><span class="o">.</span><span class="n">EvColumn</span><span class="p">(</span><span class="o">*</span><span class="n">vs_column</span><span class="p">,</span> <span class="n">align</span><span class="o">=</span><span class="s2">&quot;c&quot;</span><span class="p">),</span>
<span class="n">evtable</span><span class="o">.</span><span class="n">EvColumn</span><span class="p">(</span><span class="o">*</span><span class="n">enemies</span><span class="p">,</span> <span class="n">align</span><span class="o">=</span><span class="s2">&quot;r&quot;</span><span class="p">),</span>
<span class="p">],</span>
<span class="n">border</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">maxwidth</span><span class="o">=</span><span class="mi">78</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># ... </span>
</pre></div></td></tr></table></div>
</div>
<p>This may look complex, but the complexity is only in figuring out how to organize three columns, especially how to to adjust to the two sides on each side of the <code class="docutils literal notranslate"><span class="pre">vs</span></code> are roughly vertically aligned.</p>
<ul class="simple">
<li><p><strong>Line 15</strong> : We make use of the <code class="docutils literal notranslate"><span class="pre">self.get_sides(combatant)</span></code> method which we havent actually implemented yet. This is because turn-based and twitch-based combat will need different ways to find out what the sides are. The <code class="docutils literal notranslate"><span class="pre">allies</span></code> and <code class="docutils literal notranslate"><span class="pre">enemies</span></code> are lists.</p></li>
<li><p><strong>Line 17</strong>: The <code class="docutils literal notranslate"><span class="pre">combatant</span></code> is not a part of the <code class="docutils literal notranslate"><span class="pre">allies</span></code> list (this is how we defined <code class="docutils literal notranslate"><span class="pre">get_sides</span></code> to work), so we insert it at the top of the list (so they show first on the left-hand side).</p></li>
<li><p><strong>Lines 21, 22</strong>: We make use of the <code class="docutils literal notranslate"><span class="pre">.hurt_level</span></code> values of all living things (see the <a class="reference internal" href="Beginner-Tutorial-Characters.html"><span class="doc std std-doc">LivingMixin of the Character lesson</span></a>).</p></li>
<li><p><strong>Lines 28-39</strong>: We determine how to vertically center the two sides by adding empty lines above and below the content.</p></li>
<li><p><strong>Line 41</strong>: The <a class="reference internal" href="../../../Components/EvTable.html"><span class="doc std std-doc">Evtable</span></a> is an Evennia utility for making, well, text tables. Once we are happy with the columns, we feed them to the table and let Evennia do the rest. Its worth to explore <code class="docutils literal notranslate"><span class="pre">EvTable</span></code> since it can help you create all sorts of nice layouts.</p></li>
</ul>
</section>
</section>
<section id="actions">
<h2><span class="section-number">9.2. </span>Actions<a class="headerlink" href="#actions" title="Permalink to this headline"></a></h2>
<p>In EvAdventure we will only support a few common combat actions, mapping to the equivalent rolls and checks used in <em>Knave</em>. We will design our combat framework so that its easy to expand with other actions later.</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">hold</span></code> - The simplest action. You just lean back and do nothing.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">attack</span></code> - You attack a given <code class="docutils literal notranslate"><span class="pre">target</span></code> using your currently equipped weapon. This will become a roll of STR or WIS against the targets ARMOR.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">stunt</span></code> - You make a stunt, which in roleplaying terms would mean you tripping your opponent, taunting or otherwise trying to gain the upper hand without hurting them. You can do this to give yourself (or an ally) <em>advantage</em> against a <code class="docutils literal notranslate"><span class="pre">target</span></code> on the next action. You can also give a <code class="docutils literal notranslate"><span class="pre">target</span></code> <em>disadvantage</em> against you or an ally for their next action.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">use</span> <span class="pre">item</span></code> - You make use of a <code class="docutils literal notranslate"><span class="pre">Consumable</span></code> in your inventory. When used on yourself, itd normally be something like a healing potion. If used on an enemy it could be a firebomb or a bottle of acid.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">wield</span></code> - You wield an item. Depending on what is being wielded, it will be wielded in different ways: A helmet will be placed on the head, a piece of armor on the chest. A sword will be wielded in one hand, a shield in another. A two-handed axe will use up two hands. Doing so will move whatever was there previously to the backpack.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">flee</span></code> - You run away/disengage. This action is only applicable in turn-based combat (in twitch-based combat you just move to another room to flee). We will thus wait to define this action until the <a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html"><span class="doc std std-doc">Turnbased combat lesson</span></a>.</p></li>
</ul>
</section>
<section id="action-dicts">
<h2><span class="section-number">9.3. </span>Action dicts<a class="headerlink" href="#action-dicts" title="Permalink to this headline"></a></h2>
<p>To pass around the details of an attack (the second point above), we will use a <code class="docutils literal notranslate"><span class="pre">dict</span></code>. A <code class="docutils literal notranslate"><span class="pre">dict</span></code> is simple and also easy to save in an <code class="docutils literal notranslate"><span class="pre">Attribute</span></code>. Well call this the <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> and heres what we need for each action.</p>
<blockquote>
<div><p>You dont need to type these out anywhere, its listed here for reference. We will use these dicts when calling <code class="docutils literal notranslate"><span class="pre">combathandler.queue_action(combatant,</span> <span class="pre">action_dict)</span></code>.</p>
</div></blockquote>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">hold_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span>
<span class="p">}</span>
<span class="n">attack_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;attack&quot;</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Character</span><span class="o">/</span><span class="n">NPC</span><span class="o">&gt;</span>
<span class="p">}</span>
<span class="n">stunt_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;stunt&quot;</span><span class="p">,</span>
<span class="s2">&quot;recipient&quot;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Character</span><span class="o">/</span><span class="n">NPC</span><span class="o">&gt;</span><span class="p">,</span> <span class="c1"># who gains advantage/disadvantage</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Character</span><span class="o">/</span><span class="n">NPC</span><span class="o">&gt;</span><span class="p">,</span> <span class="c1"># who the recipient gainst adv/dis against</span>
<span class="s2">&quot;advantage&quot;</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span> <span class="c1"># grant advantage or disadvantage?</span>
<span class="s2">&quot;stunt_type&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="p">,</span> <span class="c1"># Ability to use for the challenge</span>
<span class="s2">&quot;defense_type&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="p">,</span> <span class="c1"># what Ability for recipient to defend with if we</span>
<span class="c1"># are trying to give disadvantage </span>
<span class="p">}</span>
<span class="n">use_item_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;use&quot;</span><span class="p">,</span>
<span class="s2">&quot;item&quot;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Object</span><span class="o">&gt;</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Character</span><span class="o">/</span><span class="n">NPC</span><span class="o">/</span><span class="kc">None</span><span class="o">&gt;</span> <span class="c1"># if using item against someone else </span>
<span class="p">}</span>
<span class="n">wield_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;wield&quot;</span><span class="p">,</span>
<span class="s2">&quot;item&quot;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Object</span><span class="o">&gt;</span>
<span class="p">}</span>
<span class="c1"># used only for the turnbased combat, so its Action will be defined there</span>
<span class="n">flee_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;flee&quot;</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Apart from the <code class="docutils literal notranslate"><span class="pre">stunt</span></code> action, these dicts are all pretty simple. The <code class="docutils literal notranslate"><span class="pre">key</span></code> identifes the action to perform and the other fields identifies the minimum things you need to know in order to resolve each action.</p>
<p>We have not yet written the code to set these dicts, but we will assume that we know who is performing each of these actions. So if <code class="docutils literal notranslate"><span class="pre">Beowulf</span></code> attacks <code class="docutils literal notranslate"><span class="pre">Grendel</span></code>, Beowulf is not himself included in the attack dict:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">attack_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;attack&quot;</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="n">Grendel</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Lets explain the longest action dict, the <code class="docutils literal notranslate"><span class="pre">Stunt</span></code> action dict in more detail as well. In this example, The <code class="docutils literal notranslate"><span class="pre">Trickster</span></code> is performing a <em>Stunt</em> in order to help his friend <code class="docutils literal notranslate"><span class="pre">Paladin</span></code> to gain an INT- <em>advantage</em> against the <code class="docutils literal notranslate"><span class="pre">Goblin</span></code> (maybe the paladin is preparing to cast a spell of something). Since <code class="docutils literal notranslate"><span class="pre">Trickster</span></code> is doing the action, hes not showing up in the dict:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">stunt_action_dict</span> <span class="o">-</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;stunt&quot;</span><span class="p">,</span>
<span class="s2">&quot;recipient&quot;</span><span class="p">:</span> <span class="n">Paladin</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="n">Goblin</span><span class="p">,</span>
<span class="s2">&quot;advantage&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;stunt_type&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">INT</span><span class="p">,</span>
<span class="s2">&quot;defense_type&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">INT</span><span class="p">,</span>
<span class="p">}</span>
</pre></div>
</div>
<aside class="sidebar">
<p>In EvAdventure, well always set <code class="docutils literal notranslate"><span class="pre">stunt_type</span> <span class="pre">==</span> <span class="pre">defense_type</span></code> for simplicity. But you could also consider mixing things up so you could use DEX to confuse someone and give them INT disadvantage, for example.</p>
</aside>
<p>This should result in an INT vs INT based check between the <code class="docutils literal notranslate"><span class="pre">Trickster</span></code> and the <code class="docutils literal notranslate"><span class="pre">Goblin</span></code> (maybe the trickster is trying to confuse the goblin with some clever word play). If the <code class="docutils literal notranslate"><span class="pre">Trickster</span></code> wins, the <code class="docutils literal notranslate"><span class="pre">Paladin</span></code> gains advantage against the Goblin on the <code class="docutils literal notranslate"><span class="pre">Paladin</span></code>s next action .</p>
</section>
<section id="action-classes">
<h2><span class="section-number">9.4. </span>Action classes<a class="headerlink" href="#action-classes" title="Permalink to this headline"></a></h2>
<p>Once our <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> identifies the particular action we should use, we need something that reads those keys/values and actually <em>performs</em> the action.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="k">class</span> <span class="nc">CombatAction</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combathandler</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combathandler</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="o">=</span> <span class="n">combatant</span>
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">action_dict</span><span class="o">.</span><span class="n">items</span><span class="p">();</span>
<span class="k">if</span> <span class="n">key</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;_&quot;</span><span class="p">):</span>
<span class="nb">setattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>
</pre></div>
</div>
<p>We will create a new instance of this class <em>every time an action is happening</em>. So we store some key things every action will need - we will need a reference to the common <code class="docutils literal notranslate"><span class="pre">combathandler</span></code> (which we will design in the next section), and to the <code class="docutils literal notranslate"><span class="pre">combatant</span></code> (the one performing this action). The <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> is a dict matching the action we want to perform.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">setattr</span></code> Python standard function assigns the keys/values of the <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> to be properties “on” this action. This is very convenient to use in other methods. So for the <code class="docutils literal notranslate"><span class="pre">stunt</span></code> action, other methods could just access <code class="docutils literal notranslate"><span class="pre">self.key</span></code>, <code class="docutils literal notranslate"><span class="pre">self.recipient</span></code>, <code class="docutils literal notranslate"><span class="pre">self.target</span></code> and so on directly.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="k">class</span> <span class="nc">CombatAction</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">msg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
<span class="s2">&quot;Send message to others in combat&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">combatant</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="n">broadcast</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">can_use</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Return False if combatant can&#39;s use this action right now&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="kc">True</span>
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Does the actional action&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">post_execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called after `execute`&quot;&quot;&quot;</span>
<span class="k">pass</span>
</pre></div>
</div>
<p>Its <em>very</em> common to want to send messages to everyone in combat - you need to tell people they are getting attacked, if they get hurt and so on. So having a <code class="docutils literal notranslate"><span class="pre">msg</span></code> helper method on the action is convenient. We offload all the complexity to the combathandler.msg() method.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">can_use</span></code>, <code class="docutils literal notranslate"><span class="pre">execute</span></code> and <code class="docutils literal notranslate"><span class="pre">post_execute</span></code> should all be called in a chain and we should make sure the <code class="docutils literal notranslate"><span class="pre">combathandler</span></code> calls them like this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">action</span><span class="o">.</span><span class="n">can_use</span><span class="p">():</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
<span class="n">action</span><span class="o">.</span><span class="n">post_execute</span><span class="p">()</span>
</pre></div>
</div>
<section id="hold-action">
<h3><span class="section-number">9.4.1. </span>Hold Action<a class="headerlink" href="#hold-action" title="Permalink to this headline"></a></h3>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">CombatActionHold</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Action that does nothing </span>
<span class="sd"> </span>
<span class="sd"> action_dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;hold&quot;</span>
<span class="sd"> }</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>
</div>
<p>Holding does nothing but its cleaner to nevertheless have a separate class for it. We use the docstring to specify how its action-dict should look.</p>
</section>
<section id="attack-action">
<h3><span class="section-number">9.4.2. </span>Attack Action<a class="headerlink" href="#attack-action" title="Permalink to this headline"></a></h3>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">CombatActionAttack</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> A regular attack, using a wielded weapon.</span>
<span class="sd"> </span>
<span class="sd"> action-dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;attack&quot;,</span>
<span class="sd"> &quot;target&quot;: Character/Object</span>
<span class="sd"> }</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">attacker</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="n">weapon</span> <span class="o">=</span> <span class="n">attacker</span><span class="o">.</span><span class="n">weapon</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span>
<span class="k">if</span> <span class="n">weapon</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="n">weapon</span><span class="o">.</span><span class="n">use</span><span class="p">(</span>
<span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="n">advantage</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
<span class="p">)</span>
<span class="n">weapon</span><span class="o">.</span><span class="n">at_post_use</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
</pre></div>
</div>
<p>Refer to how we <a class="reference internal" href="Beginner-Tutorial-Objects.html#weapons"><span class="std std-doc">designed Evadventure weapons</span></a> to understand what happens here - most of the work is performed by the weapon class - we just plug in the relevant arguments.</p>
</section>
<section id="stunt-action">
<h3><span class="section-number">9.4.3. </span>Stunt Action<a class="headerlink" href="#stunt-action" title="Permalink to this headline"></a></h3>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">CombatActionStunt</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Perform a stunt the grants a beneficiary (can be self) advantage on their next action against a </span>
<span class="sd"> target. Whenever performing a stunt that would affect another negatively (giving them</span>
<span class="sd"> disadvantage against an ally, or granting an advantage against them, we need to make a check</span>
<span class="sd"> first. We don&#39;t do a check if giving an advantage to an ally or ourselves.</span>
<span class="sd"> action_dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;stunt&quot;,</span>
<span class="sd"> &quot;recipient&quot;: Character/NPC,</span>
<span class="sd"> &quot;target&quot;: Character/NPC,</span>
<span class="sd"> &quot;advantage&quot;: bool, # if False, it&#39;s a disadvantage</span>
<span class="sd"> &quot;stunt_type&quot;: Ability, # what ability (like STR, DEX etc) to use to perform this stunt. </span>
<span class="sd"> &quot;defense_type&quot;: Ability, # what ability to use to defend against (negative) effects of</span>
<span class="sd"> this stunt.</span>
<span class="sd"> }</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span>
<span class="n">attacker</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="n">recipient</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">recipient</span> <span class="c1"># the one to receive the effect of the stunt</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span> <span class="c1"># the affected by the stunt (can be the same as recipient/combatant)</span>
<span class="n">txt</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
<span class="k">if</span> <span class="n">recipient</span> <span class="o">==</span> <span class="n">target</span><span class="p">:</span>
<span class="c1"># grant another entity dis/advantage against themselves</span>
<span class="n">defender</span> <span class="o">=</span> <span class="n">recipient</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># recipient not same as target; who will defend depends on disadvantage or advantage</span>
<span class="c1"># to give.</span>
<span class="n">defender</span> <span class="o">=</span> <span class="n">target</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">advantage</span> <span class="k">else</span> <span class="n">recipient</span>
<span class="c1"># trying to give advantage to recipient against target. Target defends against caller</span>
<span class="n">is_success</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">txt</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">dice</span><span class="o">.</span><span class="n">opposed_saving_throw</span><span class="p">(</span>
<span class="n">attacker</span><span class="p">,</span>
<span class="n">defender</span><span class="p">,</span>
<span class="n">attack_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">stunt_type</span><span class="p">,</span>
<span class="n">defense_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">defense_type</span><span class="p">,</span>
<span class="n">advantage</span><span class="o">=</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">defender</span><span class="p">),</span>
<span class="n">disadvantage</span><span class="o">=</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_disadvantage</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">defender</span><span class="p">),</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;$You() $conj(attempt) stunt on $You(</span><span class="si">{</span><span class="n">defender</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">). </span><span class="si">{</span><span class="n">txt</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="c1"># deal with results</span>
<span class="k">if</span> <span class="n">is_success</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">advantage</span><span class="p">:</span>
<span class="n">combathandler</span><span class="o">.</span><span class="n">give_advantage</span><span class="p">(</span><span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">combathandler</span><span class="o">.</span><span class="n">give_disadvantage</span><span class="p">(</span><span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
<span class="k">if</span> <span class="n">recipient</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(gain) </span><span class="si">{</span><span class="s1">&#39;advantage&#39;</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="bp">self</span><span class="o">.</span><span class="n">advantage</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="s1">&#39;disadvantage&#39;</span><span class="si">}</span><span class="s2"> &quot;</span>
<span class="sa">f</span><span class="s2">&quot;against $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">)!&quot;</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(cause) $You(</span><span class="si">{</span><span class="n">recipient</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) &quot;</span>
<span class="sa">f</span><span class="s2">&quot;to gain </span><span class="si">{</span><span class="s1">&#39;advantage&#39;</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="bp">self</span><span class="o">.</span><span class="n">advantage</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="s1">&#39;disadvantage&#39;</span><span class="si">}</span><span class="s2"> &quot;</span>
<span class="sa">f</span><span class="s2">&quot;against $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">)!&quot;</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="s2">&quot;|yHaving succeeded, you hold back to plan your next move.|n [hold]&quot;</span><span class="p">,</span>
<span class="n">broadcast</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;$You(</span><span class="si">{</span><span class="n">defender</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) $conj(resist)! $You() $conj(fail) the stunt.&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>The main action here is the call to the <code class="docutils literal notranslate"><span class="pre">rules.dice.opposed_saving_throw</span></code> to determine if the stunt succeeds. After that, most lines is about figuring out who should be given advantage/disadvantage and to communicate the result to the affected parties.</p>
<p>Note that we make heavy use of the helper methods on the <code class="docutils literal notranslate"><span class="pre">combathandler</span></code> here, even those that are not yet implemented. As long as we pass the <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> into the <code class="docutils literal notranslate"><span class="pre">combathandler</span></code>, the action doesnt actually care what happens next.</p>
<p>After we have performed a successful stunt, we queue the <code class="docutils literal notranslate"><span class="pre">combathandler.fallback_action_dict</span></code>. This is because stunts are meant to be one-off things are if we are repeating actions, it would not make sense to repeat the stunt over and over.</p>
</section>
<section id="use-item-action">
<h3><span class="section-number">9.4.4. </span>Use Item Action<a class="headerlink" href="#use-item-action" title="Permalink to this headline"></a></h3>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">CombatActionUseItem</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Use an item in combat. This is meant for one-off or limited-use items (so things like scrolls and potions, not swords and shields). If this is some sort of weapon or spell rune, we refer to the item to determine what to use for attack/defense rolls.</span>
<span class="sd"> action_dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;use&quot;,</span>
<span class="sd"> &quot;item&quot;: Object</span>
<span class="sd"> &quot;target&quot;: Character/NPC/Object/None</span>
<span class="sd"> }</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">item</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">item</span>
<span class="n">user</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span>
<span class="k">if</span> <span class="n">item</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="n">item</span><span class="o">.</span><span class="n">use</span><span class="p">(</span>
<span class="n">user</span><span class="p">,</span>
<span class="n">target</span><span class="p">,</span>
<span class="n">advantage</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">),</span>
<span class="n">disadvantage</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_disadvantage</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">),</span>
<span class="p">)</span>
<span class="n">item</span><span class="o">.</span><span class="n">at_post_use</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
</pre></div>
</div>
<p>See the <a class="reference internal" href="Beginner-Tutorial-Objects.html"><span class="doc std std-doc">Consumable items in the Object lesson</span></a> to see how consumables work. Like with weapons, we offload all the logic to the item we use.</p>
</section>
<section id="wield-action">
<h3><span class="section-number">9.4.5. </span>Wield Action<a class="headerlink" href="#wield-action" title="Permalink to this headline"></a></h3>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">CombatActionWield</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Wield a new weapon (or spell) from your inventory. This will </span>
<span class="sd"> swap out the one you are currently wielding, if any.</span>
<span class="sd"> action_dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;wield&quot;,</span>
<span class="sd"> &quot;item&quot;: Object</span>
<span class="sd"> }</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">item</span><span class="p">)</span>
</pre></div>
</div>
<p>We rely on the <a class="reference internal" href="Beginner-Tutorial-Equipment.html"><span class="doc std std-doc">Equipment handler</span></a> we created to handle the swapping of items for us. Since it doesnt make sense to keep swapping over and over, we queue the fallback action after this one.</p>
</section>
</section>
<section id="testing">
<h2><span class="section-number">9.5. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a module <code class="docutils literal notranslate"><span class="pre">evadventure/tests/test_combat.py</span></code>.</p>
</div></blockquote>
<aside class="sidebar">
<p>See <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia-contrib-tutorials-evadventure-tests-test-combat"><span class="std std-ref">evennia/contrib/tutorials/evadventure/tests/test_combat.py</span></a> for ready-made combat unit tests.</p>
</aside>
<p>Unit testing the combat base classes can seem impossible because we have not yet implemented most of it. We can however get very far by the use of <a class="reference external" href="https://docs.python.org/3/library/unittest.mock.html">Mocks</a>. The idea of a Mock is that you <em>replace</em> a piece of code with a dummy object (a mock) that can be called to return some specific value.</p>
<p>For example, consider this following test of the <code class="docutils literal notranslate"><span class="pre">CombatHandler.get_combat_summary</span></code>. We cant just call this because it internally calls <code class="docutils literal notranslate"><span class="pre">.get_sides</span></code> which would raise a <code class="docutils literal notranslate"><span class="pre">NotImplementedError</span></code>.</p>
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span>
<span class="normal">12</span>
<span class="normal">13</span>
<span class="normal">14</span>
<span class="normal">15</span>
<span class="normal">16</span>
<span class="normal">17</span>
<span class="normal">18</span>
<span class="normal">19</span>
<span class="normal">20</span>
<span class="normal">21</span>
<span class="normal">22</span>
<span class="normal">23</span>
<span class="normal">24</span>
<span class="normal">25</span>
<span class="normal">26</span>
<span class="normal">27</span>
<span class="normal">28</span>
<span class="normal">29</span>
<span class="normal">30</span>
<span class="normal">31</span>
<span class="normal">32</span>
<span class="normal">33</span>
<span class="normal">34</span>
<span class="normal">35</span>
<span class="normal">36</span>
<span class="normal">37</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/tests/test_combat.py </span>
<span class="kn">from</span> <span class="nn">unittest.mock</span> <span class="kn">import</span> <span class="n">Mock</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTestCase</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_object</span>
<span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">combat_base</span>
<span class="kn">from</span> <span class="nn">..rooms</span> <span class="kn">import</span> <span class="n">EvAdventureRoom</span>
<span class="kn">from</span> <span class="nn">..characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
<span class="k">class</span> <span class="nc">TestEvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">EvenniaTestCase</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">location</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;testroom&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;testchar&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureMob</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;testmonster&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># do the test from perspective of combatant</span>
<span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_sides</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="p">([],</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">]))</span>
</span> <span class="n">result</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span>
<span class="n">result</span><span class="p">,</span>
<span class="s2">&quot; testchar (Perfect) vs testmonster (Perfect)&quot;</span>
<span class="p">)</span>
<span class="c1"># test from the perspective of the monster </span>
<span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_sides</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="p">([],</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">]))</span>
</span> <span class="n">result</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span>
<span class="n">result</span><span class="p">,</span>
<span class="s2">&quot; testmonster (Perfect) vs testchar (Perfect)&quot;</span>
<span class="p">)</span>
</pre></div></td></tr></table></div>
</div>
<p>The interesting places are where we apply the mocks:</p>
<ul class="simple">
<li><p><strong>Line 25</strong> and <strong>Line 32</strong>: While <code class="docutils literal notranslate"><span class="pre">get_sides</span></code> is not implemented yet, we know what it is <em>supposed</em> to return - a tuple of lists. So for the sake of the test, we <em>replace</em> the <code class="docutils literal notranslate"><span class="pre">get_sides</span></code> method with a mock that when called will return something useful.</p></li>
</ul>
<p>With this kind of approach its possible to fully test a system also when its not complete yet.</p>
</section>
<section id="conclusions">
<h2><span class="section-number">9.6. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline"></a></h2>
<p>We have the core functionality we need for our combat system! In the following two lessons we will make use of these building blocks to create two styles of combat.</p>
</section>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-Combat-Twitch.html" title="10. Twitch Combat"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">9. </span>Combat base framework</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,151 @@
<!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>16. In-game Commands &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="Part 4: Using What We Created" href="../Part4/Beginner-Tutorial-Part4-Overview.html" />
<link rel="prev" title="15. In-game Shops" href="Beginner-Tutorial-Shops.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="../Part4/Beginner-Tutorial-Part4-Overview.html" title="Part 4: Using What We Created"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">16. </span>In-game Commands</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Shops.html"
title="previous chapter"><span class="section-number">15. </span>In-game Shops</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="../Part4/Beginner-Tutorial-Part4-Overview.html"
title="next chapter">Part 4: Using What We Created</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Commands.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="in-game-commands">
<h1><span class="section-number">16. </span>In-game Commands<a class="headerlink" href="#in-game-commands" title="Permalink to this headline"></a></h1>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>This part of the Beginner tutorial is still being developed.</p>
</div>
</section>
</div>
</div>
</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="right" >
<a href="../Part4/Beginner-Tutorial-Part4-Overview.html" title="Part 4: Using What We Created"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">16. </span>In-game Commands</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,151 @@
<!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>13. Dynamically generated Dungeon &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="14. Game Quests" href="Beginner-Tutorial-Quests.html" />
<link rel="prev" title="12. NPC and monster AI" href="Beginner-Tutorial-AI.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Quests.html" title="14. Game Quests"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-AI.html" title="12. NPC and monster AI"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">13. </span>Dynamically generated Dungeon</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-AI.html"
title="previous chapter"><span class="section-number">12. </span>NPC and monster AI</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Quests.html"
title="next chapter"><span class="section-number">14. </span>Game Quests</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Dungeon.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="dynamically-generated-dungeon">
<h1><span class="section-number">13. </span>Dynamically generated Dungeon<a class="headerlink" href="#dynamically-generated-dungeon" title="Permalink to this headline"></a></h1>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>This part of the Beginner tutorial is still being developed.</p>
</div>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-Quests.html" title="14. Game Quests"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-AI.html" title="12. NPC and monster AI"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">13. </span>Dynamically generated Dungeon</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,733 @@
<!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>5. Handling Equipment &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="6. Character Generation" href="Beginner-Tutorial-Chargen.html" />
<link rel="prev" title="4. In-game Objects and items" href="Beginner-Tutorial-Objects.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Chargen.html" title="6. Character Generation"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Objects.html" title="4. In-game Objects and items"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">5. </span>Handling Equipment</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">5. Handling Equipment</a><ul>
<li><a class="reference internal" href="#equipmenthandler-that-saves">5.1. EquipmentHandler that saves</a></li>
<li><a class="reference internal" href="#connecting-the-equipmenthandler">5.2. Connecting the EquipmentHandler</a></li>
<li><a class="reference internal" href="#expanding-the-equipmenthandler">5.3. Expanding the Equipmenthandler</a></li>
<li><a class="reference internal" href="#validate-slot-usage">5.4. <code class="docutils literal notranslate"><span class="pre">.validate_slot_usage</span></code></a><ul>
<li><a class="reference internal" href="#max-slots">5.4.1. <code class="docutils literal notranslate"><span class="pre">.max_slots</span></code></a></li>
<li><a class="reference internal" href="#count-slots">5.4.2. <code class="docutils literal notranslate"><span class="pre">.count_slots</span></code></a></li>
<li><a class="reference internal" href="#validating-slots">5.4.3. Validating slots</a></li>
</ul>
</li>
<li><a class="reference internal" href="#add-and-remove">5.5. <code class="docutils literal notranslate"><span class="pre">.add</span></code> and <code class="docutils literal notranslate"><span class="pre">.remove</span></code></a></li>
<li><a class="reference internal" href="#moving-things-around">5.6. Moving things around</a></li>
<li><a class="reference internal" href="#get-everything">5.7. Get everything</a></li>
<li><a class="reference internal" href="#weapon-and-armor">5.8. Weapon and armor</a><ul>
<li><a class="reference internal" href="#fixing-the-character-class">5.8.1. Fixing the Character class</a></li>
</ul>
</li>
<li><a class="reference internal" href="#extra-credits">5.9. Extra credits</a></li>
<li><a class="reference internal" href="#unit-testing">5.10. Unit Testing</a></li>
<li><a class="reference internal" href="#summary">5.11. Summary</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Objects.html"
title="previous chapter"><span class="section-number">4. </span>In-game Objects and items</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Chargen.html"
title="next chapter"><span class="section-number">6. </span>Character Generation</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Equipment.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="handling-equipment">
<h1><span class="section-number">5. </span>Handling Equipment<a class="headerlink" href="#handling-equipment" title="Permalink to this headline"></a></h1>
<p>In <em>Knave</em>, you have a certain number of inventory “slots”. The amount of slots is given by <code class="docutils literal notranslate"><span class="pre">CON</span> <span class="pre">+</span> <span class="pre">10</span></code>. All items (except coins) have a <code class="docutils literal notranslate"><span class="pre">size</span></code>, indicating how many slots it uses. You cant carry more items than you have slot-space for. Also items wielded or worn count towards the slots.</p>
<p>We still need to track what the character is using however: What weapon they have readied affects the damage they can do. The shield, helmet and armor they use affects their defense.</p>
<p>We have already set up the possible wear/wield locations when we defined our Objects
<a class="reference internal" href="Beginner-Tutorial-Objects.html"><span class="doc std std-doc">in the previous lesson</span></a>. This is what we have in <code class="docutils literal notranslate"><span class="pre">enums.py</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/enums.py</span>
<span class="c1"># ...</span>
<span class="k">class</span> <span class="nc">WieldLocation</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<span class="n">BACKPACK</span> <span class="o">=</span> <span class="s2">&quot;backpack&quot;</span>
<span class="n">WEAPON_HAND</span> <span class="o">=</span> <span class="s2">&quot;weapon_hand&quot;</span>
<span class="n">SHIELD_HAND</span> <span class="o">=</span> <span class="s2">&quot;shield_hand&quot;</span>
<span class="n">TWO_HANDS</span> <span class="o">=</span> <span class="s2">&quot;two_handed_weapons&quot;</span>
<span class="n">BODY</span> <span class="o">=</span> <span class="s2">&quot;body&quot;</span> <span class="c1"># armor</span>
<span class="n">HEAD</span> <span class="o">=</span> <span class="s2">&quot;head&quot;</span> <span class="c1"># helmets</span>
</pre></div>
</div>
<p>Basically, all the weapon/armor locations are exclusive - you can only have one item in each (or none). The BACKPACK is special - it contains any number of items (up to the maximum slot usage).</p>
<section id="equipmenthandler-that-saves">
<h2><span class="section-number">5.1. </span>EquipmentHandler that saves<a class="headerlink" href="#equipmenthandler-that-saves" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/equipment.py</span></code>.</p>
</div></blockquote>
<aside class="sidebar">
<p>If you want to understand more about behind how Evennia uses handlers, there is a
<a class="reference internal" href="../../Tutorial-Persistent-Handler.html"><span class="doc std std-doc">dedicated tutorial</span></a> talking about the principle.</p>
</aside>
<p>In default Evennia, everything you pick up will end up “inside” your character object (that is, have you as its <code class="docutils literal notranslate"><span class="pre">.location</span></code>). This is called your <em>inventory</em> and has no limit. We will keep moving items into us when we pick them up, but we will add more functionality using an <em>Equipment handler</em>.</p>
<p>A handler is (for our purposes) an object that sits “on” another entity, containing functionality for doing one specific thing (managing equipment, in our case).</p>
<p>This is the start of our handler:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/equipment.py </span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span>
<span class="k">class</span> <span class="nc">EquipmentHandler</span><span class="p">:</span>
<span class="n">save_attribute</span> <span class="o">=</span> <span class="s2">&quot;inventory_slots&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
<span class="c1"># here obj is the character we store the handler on </span>
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="n">obj</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_load</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">_load</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Load our data from an Attribute on `self.obj`&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">get</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">save_attribute</span><span class="p">,</span>
<span class="n">category</span><span class="o">=</span><span class="s2">&quot;inventory&quot;</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="p">{</span>
<span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">WieldLocation</span><span class="o">.</span><span class="n">BODY</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">WieldLocation</span><span class="o">.</span><span class="n">HEAD</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">:</span> <span class="p">[]</span>
<span class="p">}</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">_save</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Save our data back to the same Attribute&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">obj</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="bp">self</span><span class="o">.</span><span class="n">save_attribute</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">&quot;inventory&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>This is a compact and functional little handler. Before analyzing how it works, this is how
we will add it to the Character:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/characters.py</span>
<span class="c1"># ... </span>
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">lazy_property</span>
<span class="kn">from</span> <span class="nn">.equipment</span> <span class="kn">import</span> <span class="n">EquipmentHandler</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="nd">@lazy_property</span>
<span class="k">def</span> <span class="nf">equipment</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">EquipmentHandler</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
</pre></div>
</div>
<p>After reloading the server, the equipment-handler will now be accessible on character-instances as</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>character.equipment
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">&#64;lazy_property</span></code> works such that it will not load the handler until someone actually tries to fetch it with <code class="docutils literal notranslate"><span class="pre">character.equipment</span></code>. When that happens, we start up the handler and feed it <code class="docutils literal notranslate"><span class="pre">self</span></code> (the <code class="docutils literal notranslate"><span class="pre">Character</span></code> instance itself). This is what enters <code class="docutils literal notranslate"><span class="pre">__init__</span></code> as <code class="docutils literal notranslate"><span class="pre">.obj</span></code> in the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> code above.</p>
<p>So we now have a handler on the character, and the handler has a back-reference to the character it sits on.</p>
<p>Since the handler itself is just a regular Python object, we need to use the <code class="docutils literal notranslate"><span class="pre">Character</span></code> to store
our data - our <em>Knave</em> “slots”. We must save them to the database, because we want the server to remember them even after reloading.</p>
<p>Using <code class="docutils literal notranslate"><span class="pre">self.obj.attributes.add()</span></code> and <code class="docutils literal notranslate"><span class="pre">.get()</span></code> we save the data to the Character in a specially named <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attribute</span></a>. Since we use a <code class="docutils literal notranslate"><span class="pre">category</span></code>, we are unlikely to collide with
other Attributes.</p>
<p>Our storage structure is a <code class="docutils literal notranslate"><span class="pre">dict</span></code> with keys after our available <code class="docutils literal notranslate"><span class="pre">WieldLocation</span></code> enums. Each can only have one item except <code class="docutils literal notranslate"><span class="pre">WieldLocation.BACKPACK</span></code>, which is a list.</p>
</section>
<section id="connecting-the-equipmenthandler">
<h2><span class="section-number">5.2. </span>Connecting the EquipmentHandler<a class="headerlink" href="#connecting-the-equipmenthandler" title="Permalink to this headline"></a></h2>
<p>Whenever an object leaves from one location to the next, Evennia will call a set of <em>hooks</em> (methods) on the object that moves, on the source-location and on its destination. This is the same for all moving things - whether its a character moving between rooms or an item being dropping from your hand to the ground.</p>
<p>We need to tie our new <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> into this system. By reading the doc page on <a class="reference internal" href="../../../Components/Objects.html"><span class="doc std std-doc">Objects</span></a>, or looking at the <a class="reference internal" href="../../../api/evennia.objects.objects.html#evennia.objects.objects.DefaultObject.move_to" title="evennia.objects.objects.DefaultObject.move_to"><span class="xref myst py py-meth">DefaultObject.move_to</span></a> docstring, well find out what hooks Evennia will call. Here <code class="docutils literal notranslate"><span class="pre">self</span></code> is the object being moved from <code class="docutils literal notranslate"><span class="pre">source_location</span></code> to <code class="docutils literal notranslate"><span class="pre">destination</span></code>:</p>
<ol class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">self.at_pre_move(destination)</span></code> (abort if return False)</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source_location.at_pre_object_leave(self,</span> <span class="pre">destination)</span></code> (abort if return False)</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">destination.at_pre_object_receive(self,</span> <span class="pre">source_location)</span></code> (abort if return False)</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source_location.at_object_leave(self,</span> <span class="pre">destination)</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">self.announce_move_from(destination)</span></code></p></li>
<li><p>(move happens here)</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">self.announce_move_to(source_location)</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">destination.at_object_receive(self,</span> <span class="pre">source_location)</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">self.at_post_move(source_location)</span></code></p></li>
</ol>
<p>All of these hooks can be overridden to customize movement behavior. In this case we are interested in controlling how items enter and leave our character - being inside the character is the same as them carrying it. We have three good hook-candidates to use for this.</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">.at_pre_object_receive</span></code> - used to check if you can actually pick something up, or if your equipment-store is full.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.at_object_receive</span></code> - used to add the item to the equipmenthandler</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.at_object_leave</span></code> - used to remove the item from the equipmenthandler</p></li>
</ul>
<p>You could also picture using <code class="docutils literal notranslate"><span class="pre">.at_pre_object_leave</span></code> to restrict dropping (cursed?) items, but
we will skip that for this tutorial.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/character.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">at_pre_object_receive</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">moved_object</span><span class="p">,</span> <span class="n">source_location</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called by Evennia before object arrives &#39;in&#39; this character (that is,</span>
<span class="sd"> if they pick up something). If it returns False, move is aborted.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">validate_slot_usage</span><span class="p">(</span><span class="n">moved_object</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">at_object_receive</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">moved_object</span><span class="p">,</span> <span class="n">source_location</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Called by Evennia when an object arrives &#39;in&#39; the character.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">moved_object</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">at_object_leave</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">moved_object</span><span class="p">,</span> <span class="n">destination</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Called by Evennia when object leaves the Character. </span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">moved_object</span><span class="p">)</span>
</pre></div>
</div>
<p>Above we have assumed the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> (<code class="docutils literal notranslate"><span class="pre">.equipment</span></code>) has methods <code class="docutils literal notranslate"><span class="pre">.validate_slot_usage</span></code>, <code class="docutils literal notranslate"><span class="pre">.add</span></code> and <code class="docutils literal notranslate"><span class="pre">.remove</span></code>. But we havent actually added them yet - we just put some reasonable names! Before we can use this, we need to go actually adding those methods.</p>
<p>When you do things like <code class="docutils literal notranslate"><span class="pre">create/drop</span> <span class="pre">monster:NPC</span></code>, the npc will briefly be in your inventory before being dropped on the ground. Since an NPC is not a valid thing to equip, the EquipmentHandler will complain with an <code class="docutils literal notranslate"><span class="pre">EquipmentError</span></code> (we define this see below). So we need to</p>
</section>
<section id="expanding-the-equipmenthandler">
<h2><span class="section-number">5.3. </span>Expanding the Equipmenthandler<a class="headerlink" href="#expanding-the-equipmenthandler" title="Permalink to this headline"></a></h2>
</section>
<section id="validate-slot-usage">
<h2><span class="section-number">5.4. </span><code class="docutils literal notranslate"><span class="pre">.validate_slot_usage</span></code><a class="headerlink" href="#validate-slot-usage" title="Permalink to this headline"></a></h2>
<p>Lets start with implementing the first method we came up with above, <code class="docutils literal notranslate"><span class="pre">validate_slot_usage</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/equipment.py </span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">Ability</span>
<span class="k">class</span> <span class="nc">EquipmentError</span><span class="p">(</span><span class="ne">TypeError</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;All types of equipment-errors&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">EquipmentHandler</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">max_slots</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Max amount of slots, based on CON defense (CON + 10)&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">,</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CON</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="mi">10</span>
<span class="k">def</span> <span class="nf">count_slots</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Count current slot usage&quot;&quot;&quot;</span>
<span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span>
<span class="n">wield_usage</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="n">slotobj</span><span class="p">,</span> <span class="s2">&quot;size&quot;</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="ow">or</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">slot</span><span class="p">,</span> <span class="n">slotobj</span> <span class="ow">in</span> <span class="n">slots</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
<span class="k">if</span> <span class="n">slot</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span>
<span class="p">)</span>
<span class="n">backpack_usage</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="n">slotobj</span><span class="p">,</span> <span class="s2">&quot;size&quot;</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="ow">or</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">slotobj</span> <span class="ow">in</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">wield_usage</span> <span class="o">+</span> <span class="n">backpack_usage</span>
<span class="k">def</span> <span class="nf">validate_slot_usage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Check if obj can fit in equipment, based on its size.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">inherits_from</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">EvAdventureObject</span><span class="p">):</span>
<span class="c1"># in case we mix with non-evadventure objects</span>
<span class="k">raise</span> <span class="n">EquipmentError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">obj</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is not something that can be equipped.&quot;</span><span class="p">)</span>
<span class="n">size</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">size</span>
<span class="n">max_slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">max_slots</span>
<span class="n">current_slot_usage</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">count_slots</span><span class="p">()</span>
<span class="k">return</span> <span class="n">current_slot_usage</span> <span class="o">+</span> <span class="n">size</span> <span class="o">&lt;=</span> <span class="n">max_slots</span>
</pre></div>
</div>
<aside class="sidebar">
<p>The <code class="docutils literal notranslate"><span class="pre">&#64;property</span></code> decorator turns a method into a property so you dont need to call it.
That is, you can access <code class="docutils literal notranslate"><span class="pre">.max_slots</span></code> instead of <code class="docutils literal notranslate"><span class="pre">.max_slots()</span></code>. In this case, its just a
little less to type.</p>
</aside>
<p>We add two helpers - the <code class="docutils literal notranslate"><span class="pre">max_slots</span></code> <em>property</em> and <code class="docutils literal notranslate"><span class="pre">count_slots</span></code>, a method that calculate the current slots being in use. Lets figure out how they work.</p>
<section id="max-slots">
<h3><span class="section-number">5.4.1. </span><code class="docutils literal notranslate"><span class="pre">.max_slots</span></code><a class="headerlink" href="#max-slots" title="Permalink to this headline"></a></h3>
<p>For <code class="docutils literal notranslate"><span class="pre">max_slots</span></code>, remember that <code class="docutils literal notranslate"><span class="pre">.obj</span></code> on the handler is a back-reference to the <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> we put this handler on. <code class="docutils literal notranslate"><span class="pre">getattr</span></code> is a Python method for retrieving a named property on an object. The <code class="docutils literal notranslate"><span class="pre">Enum</span></code> <code class="docutils literal notranslate"><span class="pre">Ability.CON.value</span></code> is the string <code class="docutils literal notranslate"><span class="pre">Constitution</span></code> (check out the <a class="reference internal" href="Beginner-Tutorial-Utilities.html"><span class="doc std std-doc">first Utility and Enums tutorial</span></a> if you dont recall).</p>
<p>So to be clear,</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">,</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CON</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="o">+</span> <span class="mi">10</span>
</pre></div>
</div>
<p>is the same as writing</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nb">getattr</span><span class="p">(</span><span class="n">your_character</span><span class="p">,</span> <span class="s2">&quot;Constitution&quot;</span><span class="p">)</span> <span class="o">+</span> <span class="mi">10</span>
</pre></div>
</div>
<p>which is the same as doing something like this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">your_character</span><span class="o">.</span><span class="n">Constitution</span> <span class="o">+</span> <span class="mi">10</span>
</pre></div>
</div>
<p>In our code we write <code class="docutils literal notranslate"><span class="pre">getattr(self.obj,</span> <span class="pre">Ability.CON.value,</span> <span class="pre">1)</span></code> - that extra <code class="docutils literal notranslate"><span class="pre">1</span></code> means that if there should happen to <em>not</em> be a property “Constitution” on <code class="docutils literal notranslate"><span class="pre">self.obj</span></code>, we should not error out but just return 1.</p>
</section>
<section id="count-slots">
<h3><span class="section-number">5.4.2. </span><code class="docutils literal notranslate"><span class="pre">.count_slots</span></code><a class="headerlink" href="#count-slots" title="Permalink to this headline"></a></h3>
<p>In this helper we use two Python tools - the <code class="docutils literal notranslate"><span class="pre">sum()</span></code> function and a <a class="reference external" href="https://www.w3schools.com/python/python_lists_comprehension.asp">list comprehension</a>. The former simply adds the values of any iterable together. The latter is a more efficient way to create a list:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>new_list = [item for item in some_iterable if condition]
all_above_5 = [num for num in range(10) if num &gt; 5] # [6, 7, 8, 9]
all_below_5 = [num for num in range(10) if num &lt; 5] # [0, 1, 2, 3, 4]
</pre></div>
</div>
<p>To make it easier to understand, try reading the last line above as “for every number in the range 0-9, pick all with a value below 5 and make a list of them”. You can also embed such comprehensions directly in a function call like <code class="docutils literal notranslate"><span class="pre">sum()</span></code> without using <code class="docutils literal notranslate"><span class="pre">[]</span></code> around it.</p>
<p>In <code class="docutils literal notranslate"><span class="pre">count_slots</span></code> we have this code:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">wield_usage</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="n">slotobj</span><span class="p">,</span> <span class="s2">&quot;size&quot;</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="k">for</span> <span class="n">slot</span><span class="p">,</span> <span class="n">slotobj</span> <span class="ow">in</span> <span class="n">slots</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
<span class="k">if</span> <span class="n">slot</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span>
<span class="p">)</span>
</pre></div>
</div>
<p>We should be able to follow all except <code class="docutils literal notranslate"><span class="pre">slots.items()</span></code>. Since <code class="docutils literal notranslate"><span class="pre">slots</span></code> is a <code class="docutils literal notranslate"><span class="pre">dict</span></code>, we can use <code class="docutils literal notranslate"><span class="pre">.items()</span></code> to get a sequence of <code class="docutils literal notranslate"><span class="pre">(key,</span> <span class="pre">value)</span></code> pairs. We store these in <code class="docutils literal notranslate"><span class="pre">slot</span></code> and <code class="docutils literal notranslate"><span class="pre">slotobj</span></code>. So the above can be understood as “for every <code class="docutils literal notranslate"><span class="pre">slot</span></code> and <code class="docutils literal notranslate"><span class="pre">slotobj</span></code>-pair in <code class="docutils literal notranslate"><span class="pre">slots</span></code>, check which slot location it is. If it is <em>not</em> in the backpack, get its size and add it to the list. Sum over all these
sizes”.</p>
<p>A less compact but maybe more readonable way to write this would be:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">backpack_item_sizes</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">slot</span><span class="p">,</span> <span class="n">slotobj</span> <span class="ow">in</span> <span class="n">slots</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="n">slot</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">:</span>
<span class="n">size</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">slotobj</span><span class="p">,</span> <span class="s2">&quot;size&quot;</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">backpack_item_sizes</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">size</span><span class="p">)</span>
<span class="n">wield_usage</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">backpack_item_sizes</span><span class="p">)</span>
</pre></div>
</div>
<p>The same is done for the items actually in the BACKPACK slot. The total sizes are added
together.</p>
</section>
<section id="validating-slots">
<h3><span class="section-number">5.4.3. </span>Validating slots<a class="headerlink" href="#validating-slots" title="Permalink to this headline"></a></h3>
<p>With these helpers in place, <code class="docutils literal notranslate"><span class="pre">validate_slot_usage</span></code> now becomes simple. We use <code class="docutils literal notranslate"><span class="pre">max_slots</span></code> to see how much we can carry. We then get how many slots we are already using (with <code class="docutils literal notranslate"><span class="pre">count_slots</span></code>) and see if our new <code class="docutils literal notranslate"><span class="pre">obj</span></code>s size would be too much for us.</p>
</section>
</section>
<section id="add-and-remove">
<h2><span class="section-number">5.5. </span><code class="docutils literal notranslate"><span class="pre">.add</span></code> and <code class="docutils literal notranslate"><span class="pre">.remove</span></code><a class="headerlink" href="#add-and-remove" title="Permalink to this headline"></a></h2>
<p>We will make it so <code class="docutils literal notranslate"><span class="pre">.add</span></code> puts something in the <code class="docutils literal notranslate"><span class="pre">BACKPACK</span></code> location and <code class="docutils literal notranslate"><span class="pre">remove</span></code> drops it, wherever it is (even if it was in your hands).</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/equipment.py </span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">Ability</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EquipmentHandler</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Put something in the backpack.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validate_slot_usage</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_save</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">remove</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj_or_slot</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Remove specific object or objects from a slot.</span>
<span class="sd"> Returns a list of 0, 1 or more objects removed from inventory.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span>
<span class="n">ret</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">obj_or_slot</span><span class="p">,</span> <span class="n">WieldLocation</span><span class="p">):</span>
<span class="c1"># a slot; if this fails, obj_or_slot must be obj</span>
<span class="k">if</span> <span class="n">obj_or_slot</span> <span class="ow">is</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">:</span>
<span class="c1"># empty entire backpack</span>
<span class="n">ret</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">obj_or_slot</span><span class="p">])</span>
<span class="n">slots</span><span class="p">[</span><span class="n">obj_or_slot</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">ret</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">obj_or_slot</span><span class="p">])</span>
<span class="n">slots</span><span class="p">[</span><span class="n">obj_or_slot</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">elif</span> <span class="n">obj_or_slot</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span><span class="o">.</span><span class="n">values</span><span class="p">():</span>
<span class="c1"># obj in use/wear slot</span>
<span class="k">for</span> <span class="n">slot</span><span class="p">,</span> <span class="n">objslot</span> <span class="ow">in</span> <span class="n">slots</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="n">objslot</span> <span class="ow">is</span> <span class="n">obj_or_slot</span><span class="p">:</span>
<span class="n">slots</span><span class="p">[</span><span class="n">slot</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">ret</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">objslot</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">obj_or_slot</span> <span class="ow">in</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]:</span> <span class="c1"># obj in backpack slot</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">obj_or_slot</span><span class="p">)</span>
<span class="n">ret</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">obj_or_slot</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">if</span> <span class="n">ret</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_save</span><span class="p">()</span>
<span class="k">return</span> <span class="n">ret</span>
</pre></div>
</div>
<p>In <code class="docutils literal notranslate"><span class="pre">.add</span></code>, we make use of <code class="docutils literal notranslate"><span class="pre">validate_slot_usage</span></code> to
double-check we can actually fit the thing, then we add the item to the backpack.</p>
<p>In <code class="docutils literal notranslate"><span class="pre">.remove</span></code>, we allow emptying both by <code class="docutils literal notranslate"><span class="pre">WieldLocation</span></code> or by explicitly saying which object to remove. Note that the first <code class="docutils literal notranslate"><span class="pre">if</span></code> statement checks if <code class="docutils literal notranslate"><span class="pre">obj_or_slot</span></code> is a slot. So if that fails then code in the other <code class="docutils literal notranslate"><span class="pre">elif</span></code> can safely assume that it must instead be an object!</p>
<p>Any removed objects are returned. If we gave <code class="docutils literal notranslate"><span class="pre">BACKPACK</span></code> as the slot, we empty the backpack and return all items inside it.</p>
<p>Whenever we change the equipment loadout we must make sure to <code class="docutils literal notranslate"><span class="pre">._save()</span></code> the result, or it will be lost after a server reload.</p>
</section>
<section id="moving-things-around">
<h2><span class="section-number">5.6. </span>Moving things around<a class="headerlink" href="#moving-things-around" title="Permalink to this headline"></a></h2>
<p>With the help of <code class="docutils literal notranslate"><span class="pre">.remove()</span></code> and <code class="docutils literal notranslate"><span class="pre">.add()</span></code> we can get things in and out of the <code class="docutils literal notranslate"><span class="pre">BACKPACK</span></code> equipment location. We also need to grab stuff from the backpack and wield or wear it. We add a <code class="docutils literal notranslate"><span class="pre">.move</span></code> method on the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> to do this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/equipment.py </span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">Ability</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EquipmentHandler</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Move object from backpack to its intended `inventory_use_slot`.&quot;&quot;&quot;</span>
<span class="c1"># make sure to remove from equipment/backpack first, to avoid double-adding</span>
<span class="bp">self</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">validate_slot_usage</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
<span class="k">return</span>
<span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span>
<span class="n">use_slot</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="s2">&quot;inventory_use_slot&quot;</span><span class="p">,</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">)</span>
<span class="n">to_backpack</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">if</span> <span class="n">use_slot</span> <span class="ow">is</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">:</span>
<span class="c1"># two-handed weapons can&#39;t co-exist with weapon/shield-hand used items</span>
<span class="n">to_backpack</span> <span class="o">=</span> <span class="p">[</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">],</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">]]</span>
<span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">]</span> <span class="o">=</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">slots</span><span class="p">[</span><span class="n">use_slot</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span>
<span class="k">elif</span> <span class="n">use_slot</span> <span class="ow">in</span> <span class="p">(</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">,</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">):</span>
<span class="c1"># can&#39;t keep a two-handed weapon if adding a one-handed weapon or shield</span>
<span class="n">to_backpack</span> <span class="o">=</span> <span class="p">[</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">]]</span>
<span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">slots</span><span class="p">[</span><span class="n">use_slot</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span>
<span class="k">elif</span> <span class="n">use_slot</span> <span class="ow">is</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">:</span>
<span class="c1"># it belongs in backpack, so goes back to it</span>
<span class="n">to_backpack</span> <span class="o">=</span> <span class="p">[</span><span class="n">obj</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># for others (body, head), just replace whatever&#39;s there</span>
<span class="n">replaced</span> <span class="o">=</span> <span class="p">[</span><span class="n">obj</span><span class="p">]</span>
<span class="n">slots</span><span class="p">[</span><span class="n">use_slot</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span>
<span class="k">for</span> <span class="n">to_backpack_obj</span> <span class="ow">in</span> <span class="n">to_backpack</span><span class="p">:</span>
<span class="c1"># put stuff in backpack</span>
<span class="n">slots</span><span class="p">[</span><span class="n">use_slot</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">to_backpack_obj</span><span class="p">)</span>
<span class="c1"># store new state</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_save</span><span class="p">()</span>
</pre></div>
</div>
<p>Here we remember that every <code class="docutils literal notranslate"><span class="pre">EvAdventureObject</span></code> has an <code class="docutils literal notranslate"><span class="pre">inventory_use_slot</span></code> property that tells us where it goes. So we just need to move the object to that slot, replacing whatever is in that place from before. Anything we replace goes back to the backpack.</p>
</section>
<section id="get-everything">
<h2><span class="section-number">5.7. </span>Get everything<a class="headerlink" href="#get-everything" title="Permalink to this headline"></a></h2>
<p>In order to visualize our inventory, we need some method to get everything we are carrying.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/equipment.py </span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">Ability</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EquipmentHandler</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">all</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Get all objects in inventory, regardless of location.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span>
<span class="n">lst</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">],</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">),</span>
<span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">],</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">),</span>
<span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">],</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">),</span>
<span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BODY</span><span class="p">],</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BODY</span><span class="p">),</span>
<span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">HEAD</span><span class="p">],</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">HEAD</span><span class="p">),</span>
<span class="p">]</span> <span class="o">+</span> <span class="p">[(</span><span class="n">item</span><span class="p">,</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">)</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]]</span>
<span class="k">return</span> <span class="n">lst</span>
</pre></div>
</div>
<p>Here we get all the equipment locations and add their contents together into a list of tuples
<code class="docutils literal notranslate"><span class="pre">[(item,</span> <span class="pre">WieldLocation),</span> <span class="pre">...]</span></code>. This is convenient for display.</p>
</section>
<section id="weapon-and-armor">
<h2><span class="section-number">5.8. </span>Weapon and armor<a class="headerlink" href="#weapon-and-armor" title="Permalink to this headline"></a></h2>
<p>Its convenient to have the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> easily tell you what weapon is currently wielded and what <em>armor</em> level all worn equipment provides. Otherwise youd need to figure out what item is in which wield-slot and to add up armor slots manually every time you need to know.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/equipment.py </span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">Ability</span>
<span class="kn">from</span> <span class="nn">.objects</span> <span class="kn">import</span> <span class="n">get_bare_hand</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EquipmentHandler</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">armor</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span>
<span class="k">return</span> <span class="nb">sum</span><span class="p">(</span>
<span class="p">(</span>
<span class="c1"># armor is listed using its defense, so we remove 10 from it</span>
<span class="c1"># (11 is base no-armor value in Knave)</span>
<span class="nb">getattr</span><span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BODY</span><span class="p">],</span> <span class="s2">&quot;armor&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
<span class="c1"># shields and helmets are listed by their bonus to armor</span>
<span class="nb">getattr</span><span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">],</span> <span class="s2">&quot;armor&quot;</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="nb">getattr</span><span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">HEAD</span><span class="p">],</span> <span class="s2">&quot;armor&quot;</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="p">)</span>
<span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">weapon</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># first checks two-handed wield, then one-handed; the two</span>
<span class="c1"># should never appear simultaneously anyhow (checked in `move` method).</span>
<span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span>
<span class="n">weapon</span> <span class="o">=</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">weapon</span><span class="p">:</span>
<span class="n">weapon</span> <span class="o">=</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">]</span>
<span class="c1"># if we still don&#39;t have a weapon, we return None here</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">weapon</span><span class="p">:</span>
<span class="o">~</span> <span class="n">weapon</span> <span class="o">=</span> <span class="n">get_bare_hands</span><span class="p">()</span>
<span class="k">return</span> <span class="n">weapon</span>
</pre></div>
</div>
<p>In the <code class="docutils literal notranslate"><span class="pre">.armor()</span></code> method we get the item (if any) out of each relevant wield-slot (body, shield, head), and grab their <code class="docutils literal notranslate"><span class="pre">armor</span></code> Attribute. We then <code class="docutils literal notranslate"><span class="pre">sum()</span></code> them all up.</p>
<p>In <code class="docutils literal notranslate"><span class="pre">.weapon()</span></code>, we simply check which of the possible weapon slots (weapon-hand or two-hands) have something in them. If not we fall back to the Bare Hands object we created in the <a class="reference internal" href="Beginner-Tutorial-Objects.html#your-bare-hands"><span class="std std-doc">Object tutorial lesson</span></a> earlier.</p>
<section id="fixing-the-character-class">
<h3><span class="section-number">5.8.1. </span>Fixing the Character class<a class="headerlink" href="#fixing-the-character-class" title="Permalink to this headline"></a></h3>
<p>So we have added our equipment handler which validate what we put in it. This will however lead to a problem when we create things like NPCs in game, e.g. with</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>create/drop monster:evadventure.npcs.EvAdventureNPC
</pre></div>
</div>
<p>The problem is that when the/ monster is created it will briefly appear in your inventory before being dropped, so this code will fire on you when you do that (assuming you are an <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code>):</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/characters.py</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">at_object_receive</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">moved_object</span><span class="p">,</span> <span class="n">source_location</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Called by Evennia when an object arrives &#39;in&#39; the character.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">moved_object</span><span class="p">)</span>
</pre></div>
</div>
<p>At this means that the equipmenthandler will check the NPC, and since its not a equippable thing, an <code class="docutils literal notranslate"><span class="pre">EquipmentError</span></code> will be raised, failing the creation. Since we want to be able to create npcs etc easily, we will handle this error with a <code class="docutils literal notranslate"><span class="pre">try...except</span></code> statement like so:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/characters.py</span>
<span class="c1"># ... </span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">logger</span>
<span class="kn">from</span> <span class="nn">.equipment</span> <span class="kn">import</span> <span class="n">EquipmentError</span>
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">at_object_receive</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">moved_object</span><span class="p">,</span> <span class="n">source_location</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Called by Evennia when an object arrives &#39;in&#39; the character.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">moved_object</span><span class="p">)</span>
<span class="k">except</span> <span class="n">EquipmentError</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">log_trace</span><span class="p">()</span>
</pre></div>
</div>
<p>Using Evennias <code class="docutils literal notranslate"><span class="pre">logger.log_trace()</span></code> we catch the error and direct it to the server log. This allows you to see if there are real errors here as well, but once things work and these errors are spammy, you can also just replace the <code class="docutils literal notranslate"><span class="pre">logger.log_trace()</span></code> line with a <code class="docutils literal notranslate"><span class="pre">pass</span></code> to hide these errors.</p>
</section>
</section>
<section id="extra-credits">
<h2><span class="section-number">5.9. </span>Extra credits<a class="headerlink" href="#extra-credits" title="Permalink to this headline"></a></h2>
<p>This covers the basic functionality of the equipment handler. There are other useful methods that
can be added:</p>
<ul class="simple">
<li><p>Given an item, figure out which equipment slot it is currently in</p></li>
<li><p>Make a string representing the current loadout</p></li>
<li><p>Get everything in the backpack (only)</p></li>
<li><p>Get all wieldable items (weapons, shields) from backpack</p></li>
<li><p>Get all usable items (items with a use-location of <code class="docutils literal notranslate"><span class="pre">BACKPACK</span></code>) from the backpack</p></li>
</ul>
<p>Experiment with adding those. A full example is found in
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.equipment.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/equipment.py</span></a>.</p>
</section>
<section id="unit-testing">
<h2><span class="section-number">5.10. </span>Unit Testing<a class="headerlink" href="#unit-testing" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/tests/test_equipment.py</span></code>.</p>
</div></blockquote>
<aside class="sidebar">
<p>See <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_equipment.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/tests/test_equipment.py</span></a>
for a finished testing example.</p>
</aside>
<p>To test the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code>, easiest is create an <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> (this should by now
have <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> available on itself as <code class="docutils literal notranslate"><span class="pre">.equipment</span></code>) and a few test objects; then test
passing these into the handlers methods.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/tests/test_equipment.py </span>
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">create</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">BaseEvenniaTest</span>
<span class="kn">from</span> <span class="nn">..objects</span> <span class="kn">import</span> <span class="n">EvAdventureObject</span><span class="p">,</span> <span class="n">EvAdventureHelmet</span><span class="p">,</span> <span class="n">EvAdventureWeapon</span>
<span class="kn">from</span> <span class="nn">..enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span>
<span class="kn">from</span> <span class="nn">..characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
<span class="k">class</span> <span class="nc">TestEquipment</span><span class="p">(</span><span class="n">BaseEvenniaTest</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">character</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s1">&#39;testchar&#39;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">helmet</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureHelmet</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;helmet&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">weapon</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureWeapon</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;weapon&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_add_remove</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">],</span>
<span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">]</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">],</span> <span class="p">[])</span>
<span class="c1"># ... </span>
</pre></div>
</div>
</section>
<section id="summary">
<h2><span class="section-number">5.11. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline"></a></h2>
<p><em>Handlers</em> are useful for grouping functionality together. Now that we spent our time making the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code>, we shouldnt need to worry about item-slots anymore - the handler handles all the details for us. As long as we call its methods, the details can be forgotten about.</p>
<p>We also learned to use <em>hooks</em> to tie <em>Knave</em>s custom equipment handling into Evennia.</p>
<p>With <code class="docutils literal notranslate"><span class="pre">Characters</span></code>, <code class="docutils literal notranslate"><span class="pre">Objects</span></code> and now <code class="docutils literal notranslate"><span class="pre">Equipment</span></code> in place, we should be able to move on to character generation - where players get to make their own character!</p>
</section>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-Chargen.html" title="6. Character Generation"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Objects.html" title="4. In-game Objects and items"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">5. </span>Handling Equipment</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,356 @@
<!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>8. Non-Player-Characters &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="9. Combat base framework" href="Beginner-Tutorial-Combat-Base.html" />
<link rel="prev" title="7. In-game Rooms" href="Beginner-Tutorial-Rooms.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Combat-Base.html" title="9. Combat base framework"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rooms.html" title="7. In-game Rooms"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">8. </span>Non-Player-Characters</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">8. Non-Player-Characters</a><ul>
<li><a class="reference internal" href="#the-npc-base-class">8.1. The NPC base class</a></li>
<li><a class="reference internal" href="#testing">8.2. Testing</a></li>
<li><a class="reference internal" href="#conclusions">8.3. Conclusions</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Rooms.html"
title="previous chapter"><span class="section-number">7. </span>In-game Rooms</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Combat-Base.html"
title="next chapter"><span class="section-number">9. </span>Combat base framework</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-NPCs.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="non-player-characters">
<h1><span class="section-number">8. </span>Non-Player-Characters<a class="headerlink" href="#non-player-characters" title="Permalink to this headline"></a></h1>
<aside class="sidebar">
<p class="sidebar-title">vNPCs</p>
<p>You should usually avoid creating hundreds of NPC objects to populate your busy town - in a text game so many NPCs will just spam the screen and annoy your players. Since this is a text game, you can usually get away with using <em>vNPcs</em> - virtual NPCs. vNPCs are only described in text - a room could be described as a bustling street, farmers can be described shouting to each other. Using room descriptions for this works well, but the tutorial lesson about <a class="reference internal" href="Beginner-Tutorial-Rooms.html"><span class="doc std std-doc">EvAdventure Rooms</span></a> has a section called <a class="reference internal" href="Beginner-Tutorial-Rooms.html#adding-life-to-a-room"><span class="std std-doc">adding life to a room</span></a> that can be used for making vNPCs appear to do things in the background.</p>
</aside>
<p><em>Non-Player-Characters</em>, or NPCs, is the common term for all active agents that are <em>not</em> controlled by players. NPCs could be anything from merchants and quest givers, to monsters and bosses. They could also be flavor - townsfolk doing their chores, farmers tending their fields - there to make the world feel “more alive”.</p>
<p>In this lesson we will create the base class of <em>EvAdventure</em> NPCs based on the <em>Knave</em> ruleset. According to the <em>Knave</em> rules, NPCs have some simplified stats compared to the <a class="reference internal" href="Beginner-Tutorial-Characters.html"><span class="doc std std-doc">PC characters</span></a> we designed earlier.</p>
<div style="clear: right;"></div>
<section id="the-npc-base-class">
<h2><span class="section-number">8.1. </span>The NPC base class<a class="headerlink" href="#the-npc-base-class" title="Permalink to this headline"></a></h2>
<aside class="sidebar">
<p>See <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia-contrib-tutorials-evadventure-npcs"><span class="std std-ref">evennia/contrib/tutorials/evadventure/npcs.py</span></a> for a ready-made example of an npc module.</p>
</aside>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/npcs.py</span></code>.</p>
</div></blockquote>
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span>
<span class="normal">12</span>
<span class="normal">13</span>
<span class="normal">14</span>
<span class="normal">15</span>
<span class="normal">16</span>
<span class="normal">17</span>
<span class="normal">18</span>
<span class="normal">19</span>
<span class="normal">20</span>
<span class="normal">21</span>
<span class="normal">22</span>
<span class="normal">23</span>
<span class="normal">24</span>
<span class="normal">25</span>
<span class="normal">26</span>
<span class="normal">27</span>
<span class="normal">28</span>
<span class="normal">29</span>
<span class="normal">30</span>
<span class="normal">31</span>
<span class="normal">32</span>
<span class="normal">33</span>
<span class="normal">34</span>
<span class="normal">35</span>
<span class="normal">36</span>
<span class="normal">37</span>
<span class="normal">38</span>
<span class="normal">39</span>
<span class="normal">40</span>
<span class="normal">41</span>
<span class="normal">42</span>
<span class="normal">43</span>
<span class="normal">44</span>
<span class="normal">45</span>
<span class="normal">46</span>
<span class="normal">47</span>
<span class="normal">48</span>
<span class="normal">49</span>
<span class="normal">50</span>
<span class="normal">51</span>
<span class="normal">52</span>
<span class="normal">53</span>
<span class="normal">54</span>
<span class="normal">55</span>
<span class="normal">56</span>
<span class="normal">57</span>
<span class="normal">58</span>
<span class="normal">59</span>
<span class="normal">60</span>
<span class="normal">61</span>
<span class="normal">62</span>
<span class="normal">63</span>
<span class="normal">64</span>
<span class="normal">65</span>
<span class="normal">66</span>
<span class="normal">67</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/npcs.py </span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span><span class="p">,</span> <span class="n">AttributeProperty</span>
<span class="kn">from</span> <span class="nn">.characters</span> <span class="kn">import</span> <span class="n">LivingMixin</span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">Ability</span>
<span class="hll"><span class="k">class</span> <span class="nc">EvAdventureNPC</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
</span><span class="w"> </span><span class="sd">&quot;&quot;&quot;Base class for NPCs&quot;&quot;&quot;</span>
<span class="hll"> <span class="n">is_pc</span> <span class="o">=</span> <span class="kc">False</span>
</span><span class="hll"> <span class="n">hit_dice</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span> <span class="n">armor</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># +10 to get armor defense</span>
<span class="hll"> <span class="n">hp_multiplier</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># 4 default in Knave</span>
</span> <span class="n">hp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># internal tracking, use .hp property</span>
<span class="hll"> <span class="n">morale</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">9</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span> <span class="n">allegiance</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="n">Ability</span><span class="o">.</span><span class="n">ALLEGIANCE_HOSTILE</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="hll">
</span> <span class="n">weapon</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="n">BARE_HANDS</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># instead of inventory</span>
<span class="n">coins</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># coin loot</span>
<span class="hll"> <span class="n">is_idle</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span>
<span class="hll"> <span class="nd">@property</span>
</span> <span class="k">def</span> <span class="nf">strength</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">dexterity</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">constitution</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">intelligence</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">wisdom</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">charisma</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">hp_max</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_multiplier</span>
<span class="k">def</span> <span class="nf">at_object_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Start with max health.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span>
<span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">&quot;npcs&quot;</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">&quot;group&quot;</span><span class="p">)</span>
</span>
<span class="hll"> <span class="k">def</span> <span class="nf">ai_next_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span><span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> The system should regularly poll this method to have </span>
<span class="sd"> the NPC do their next AI action. </span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">pass</span>
</pre></div></td></tr></table></div>
</div>
<ul class="simple">
<li><p><strong>Line 9</strong>: By use of <em>multiple inheritance</em> we use the <code class="docutils literal notranslate"><span class="pre">LinvingMixin</span></code> we created in the <a class="reference internal" href="Beginner-Tutorial-Characters.html"><span class="doc std std-doc">Character lesson</span></a>. This includes a lot of useful methods, such as showing our hurt level, methods to use to heal, hooks to call when getting attacked, hurt and so on. We can re-use all of those in upcoming NPC subclasses.</p></li>
<li><p><strong>Line 12</strong>: The <code class="docutils literal notranslate"><span class="pre">is_pc</span></code> is a quick and convenient way to check if this is, well, a PC or not. We will use it in the upcoming <a class="reference internal" href="Beginner-Tutorial-Combat-Base.html"><span class="doc std std-doc">Combat base lesson</span></a>.</p></li>
<li><p><strong>Line 13</strong>: The NPC is simplified in that all stats are just based on the <code class="docutils literal notranslate"><span class="pre">Hit</span> <span class="pre">dice</span></code> number (see <strong>Lines 25-51</strong>). We store <code class="docutils literal notranslate"><span class="pre">armor</span></code> and a <code class="docutils literal notranslate"><span class="pre">weapon</span></code> as direct <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attributes</span></a> on the class rather than bother implementing a full equipment system.</p></li>
<li><p><strong>Lines 17, 18</strong>: The <code class="docutils literal notranslate"><span class="pre">morale</span></code> and <code class="docutils literal notranslate"><span class="pre">allegiance</span></code> are <em>Knave</em> properties determining how likely the NPC is to flee in a combat situation and if they are hostile or friendly.</p></li>
<li><p><strong>Line 19</strong>: The <code class="docutils literal notranslate"><span class="pre">is_idle</span></code> Attribute is a useful property. It should be available on all NPCs and will be used to disable AI entirely.</p></li>
<li><p><strong>Line 59</strong>: We make sure to tag NPCs. We may want to group different NPCs together later, for example to have all NPCs with the same tag respond if one of them is attacked.</p></li>
<li><p><strong>Line 61</strong>: The <code class="docutils literal notranslate"><span class="pre">ai_next_action</span></code> is a method we prepare for the system to be able to ask the NPC what do you want to do next?. In it we will add all logic related to the artificial intelligence of the NPC - such as walking around, attacking and performing other actions.</p></li>
</ul>
</section>
<section id="testing">
<h2><span class="section-number">8.2. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/tests/test_npcs.py</span></code></p>
</div></blockquote>
<p>Not so much to test yet, but we will be using the same module to test other aspects of NPCs in the future, so lets create it now.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/tests/test_npcs.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_object</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTest</span>
<span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">npcs</span>
<span class="k">class</span> <span class="nc">TestNPCBase</span><span class="p">(</span><span class="n">EvenniaTest</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Test the NPC base class&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">test_npc_base</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">npc</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span>
<span class="n">npcs</span><span class="o">.</span><span class="n">EvAdventureNPC</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;TestNPC&quot;</span><span class="p">,</span>
<span class="n">attributes</span><span class="o">=</span><span class="p">[(</span><span class="s2">&quot;hit_dice&quot;</span><span class="p">,</span> <span class="mi">4</span><span class="p">)],</span> <span class="c1"># set hit_dice to 4</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">hp_multiplier</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">hp</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">strength</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">charisma</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
</pre></div>
</div>
<p>Nothing special here. Note how the <code class="docutils literal notranslate"><span class="pre">create_object</span></code> helper function takes <code class="docutils literal notranslate"><span class="pre">attributes</span></code> as a keyword. This is a list of tuples we use to set different values than the default ones to Attributes. We then check a few of the properties to make sure they return what we expect.</p>
</section>
<section id="conclusions">
<h2><span class="section-number">8.3. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline"></a></h2>
<p>In <em>Knave</em>, an NPC is a simplified version of a Player Character. In other games and rule systems, they may be all but identical.</p>
<p>With the NPC class in place, we have enough to create a test dummy. Since it has no AI yet, it wont fight back, but it will be enough to have something to hit when we test our combat in the upcoming lessons.</p>
</section>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-Combat-Base.html" title="9. Combat base framework"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rooms.html" title="7. In-game Rooms"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">8. </span>Non-Player-Characters</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,612 @@
<!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>4. In-game Objects and items &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="5. Handling Equipment" href="Beginner-Tutorial-Equipment.html" />
<link rel="prev" title="3. Player Characters" href="Beginner-Tutorial-Characters.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Equipment.html" title="5. Handling Equipment"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Characters.html" title="3. Player Characters"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">4. </span>In-game Objects and items</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">4. In-game Objects and items</a><ul>
<li><a class="reference internal" href="#new-enums">4.1. New Enums</a></li>
<li><a class="reference internal" href="#the-base-object">4.2. The base object</a><ul>
<li><a class="reference internal" href="#using-attributes-or-not">4.2.1. Using Attributes or not</a></li>
<li><a class="reference internal" href="#creating-tags-in-at-object-creation">4.2.2. Creating tags in <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#other-object-types">4.3. Other object types</a></li>
<li><a class="reference internal" href="#consumables">4.4. Consumables</a></li>
<li><a class="reference internal" href="#weapons">4.5. Weapons</a></li>
<li><a class="reference internal" href="#magic">4.6. Magic</a></li>
<li><a class="reference internal" href="#armor">4.7. Armor</a></li>
<li><a class="reference internal" href="#your-bare-hands">4.8. Your Bare hands</a></li>
<li><a class="reference internal" href="#testing-and-extra-credits">4.9. Testing and Extra credits</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Characters.html"
title="previous chapter"><span class="section-number">3. </span>Player Characters</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Equipment.html"
title="next chapter"><span class="section-number">5. </span>Handling Equipment</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Objects.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="in-game-objects-and-items">
<h1><span class="section-number">4. </span>In-game Objects and items<a class="headerlink" href="#in-game-objects-and-items" title="Permalink to this headline"></a></h1>
<p>In the previous lesson we established what a Character is in our game. Before we continue
we also need to have a notion what an item or object is.</p>
<p>Looking at <em>Knave</em>s item lists, we can get some ideas of what we need to track:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">size</span></code> - this is how many slots the item uses in the characters inventory.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">value</span></code> - a base value if we want to sell or buy the item.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">inventory_use_slot</span></code> - some items can be worn or wielded. For example, a helmet needs to be worn on the head and a shield in the shield hand. Some items cant be used this way at all, but only belong in the backpack.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">obj_type</span></code> - Which type of item this is.</p></li>
</ul>
<section id="new-enums">
<h2><span class="section-number">4.1. </span>New Enums<a class="headerlink" href="#new-enums" title="Permalink to this headline"></a></h2>
<p>We added a few enumberations for Abilities back in the <a class="reference internal" href="Beginner-Tutorial-Utilities.html"><span class="doc std std-doc">Utilities tutorial</span></a>.
Before we continue, lets expand with enums for use-slots and object types.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/enums.py</span>
<span class="c1"># ...</span>
<span class="k">class</span> <span class="nc">WieldLocation</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<span class="n">BACKPACK</span> <span class="o">=</span> <span class="s2">&quot;backpack&quot;</span>
<span class="n">WEAPON_HAND</span> <span class="o">=</span> <span class="s2">&quot;weapon_hand&quot;</span>
<span class="n">SHIELD_HAND</span> <span class="o">=</span> <span class="s2">&quot;shield_hand&quot;</span>
<span class="n">TWO_HANDS</span> <span class="o">=</span> <span class="s2">&quot;two_handed_weapons&quot;</span>
<span class="n">BODY</span> <span class="o">=</span> <span class="s2">&quot;body&quot;</span> <span class="c1"># armor</span>
<span class="n">HEAD</span> <span class="o">=</span> <span class="s2">&quot;head&quot;</span> <span class="c1"># helmets</span>
<span class="k">class</span> <span class="nc">ObjType</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<span class="n">WEAPON</span> <span class="o">=</span> <span class="s2">&quot;weapon&quot;</span>
<span class="n">ARMOR</span> <span class="o">=</span> <span class="s2">&quot;armor&quot;</span>
<span class="n">SHIELD</span> <span class="o">=</span> <span class="s2">&quot;shield&quot;</span>
<span class="n">HELMET</span> <span class="o">=</span> <span class="s2">&quot;helmet&quot;</span>
<span class="n">CONSUMABLE</span> <span class="o">=</span> <span class="s2">&quot;consumable&quot;</span>
<span class="n">GEAR</span> <span class="o">=</span> <span class="s2">&quot;gear&quot;</span>
<span class="n">MAGIC</span> <span class="o">=</span> <span class="s2">&quot;magic&quot;</span>
<span class="n">QUEST</span> <span class="o">=</span> <span class="s2">&quot;quest&quot;</span>
<span class="n">TREASURE</span> <span class="o">=</span> <span class="s2">&quot;treasure&quot;</span>
</pre></div>
</div>
<p>Once we have these enums, we will use them for referencing things.</p>
</section>
<section id="the-base-object">
<h2><span class="section-number">4.2. </span>The base object<a class="headerlink" href="#the-base-object" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/objects.py</span></code></p>
</div></blockquote>
<aside class="sidebar">
<p><a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.objects.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/objects.py</span></a> has
a full set of objects implemented.</p>
</aside>
<div style="clear: right;"></div>
<p>We will make a base <code class="docutils literal notranslate"><span class="pre">EvAdventureObject</span></code> class off Evennias standard <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>. We will then add child classes to represent the relevant types:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">AttributeProperty</span><span class="p">,</span> <span class="n">DefaultObject</span>
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">make_iter</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">get_obj_stats</span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">ObjType</span>
<span class="k">class</span> <span class="nc">EvAdventureObject</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Base for all evadventure objects. </span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span>
<span class="n">size</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="c1"># this can be either a single type or a list of types (for objects able to be </span>
<span class="c1"># act as multiple). This is used to tag this object during creation.</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">GEAR</span>
<span class="c1"># default evennia hooks</span>
<span class="k">def</span> <span class="nf">at_object_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called when this object is first created. We convert the .obj_type </span>
<span class="sd"> property to a database tag.&quot;&quot;&quot;</span>
<span class="k">for</span> <span class="n">obj_type</span> <span class="ow">in</span> <span class="n">make_iter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj_type</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj_type</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">&quot;obj_type&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_display_header</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The top of the description&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="s2">&quot;&quot;</span>
<span class="k">def</span> <span class="nf">get_display_desc</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The main display - show object stats&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">get_obj_stats</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="o">=</span><span class="n">looker</span><span class="p">)</span>
<span class="c1"># custom evadventure methods</span>
<span class="k">def</span> <span class="nf">has_obj_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">objtype</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Check if object is of a certain type&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">objtype</span><span class="o">.</span><span class="n">value</span> <span class="ow">in</span> <span class="n">make_iter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj_type</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called before use. If returning False, can&#39;t be used&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="kc">True</span>
<span class="k">def</span> <span class="nf">use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Use this object, whatever that means&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">post_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Always called after use.&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">get_help</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Get any help text for this item&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="s2">&quot;No help for this item&quot;</span>
</pre></div>
</div>
<section id="using-attributes-or-not">
<h3><span class="section-number">4.2.1. </span>Using Attributes or not<a class="headerlink" href="#using-attributes-or-not" title="Permalink to this headline"></a></h3>
<p>In theory, <code class="docutils literal notranslate"><span class="pre">size</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span></code> does not change and <em>could</em> also be just set as a regular Python
property on the class:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">EvAdventureObject</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</span>
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span>
<span class="n">size</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">value</span> <span class="o">=</span> <span class="mi">0</span>
</pre></div>
</div>
<p>The problem with this is that if we want to make a new object of <code class="docutils literal notranslate"><span class="pre">size</span> <span class="pre">3</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span> <span class="pre">20</span></code>, we have to make a new class for it. We cant change it on the fly because the change would only be in memory and be lost on next server reload.</p>
<p>Because we use <code class="docutils literal notranslate"><span class="pre">AttributeProperties</span></code>, we can set <code class="docutils literal notranslate"><span class="pre">size</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span></code> to whatever we like when we create the object (or later), and the Attributes will remember our changes to that object indefinitely.</p>
<p>To make this a little more efficient, we use <code class="docutils literal notranslate"><span class="pre">autocreate=False</span></code>. Normally when you create a new object with defined <code class="docutils literal notranslate"><span class="pre">AttributeProperties</span></code>, a matching <code class="docutils literal notranslate"><span class="pre">Attribute</span></code> is immediately created at the same time. So normally, the object would be created along with two Attributes <code class="docutils literal notranslate"><span class="pre">size</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span></code>. With <code class="docutils literal notranslate"><span class="pre">autocreate=False</span></code>, no Attribute will be created <em>unless the default is changed</em>. That is, as long as your object has <code class="docutils literal notranslate"><span class="pre">size=1</span></code> no database <code class="docutils literal notranslate"><span class="pre">Attribute</span></code> will be created at all. This saves time and resources when creating large number of objects.</p>
<p>The drawback is that since no Attribute is created you cant refer to it with <code class="docutils literal notranslate"><span class="pre">obj.db.size</span></code> or <code class="docutils literal notranslate"><span class="pre">obj.attributes.get(&quot;size&quot;)</span></code> <em>unless you change its default</em>. You also cant query the database for all objects with <code class="docutils literal notranslate"><span class="pre">size=1</span></code>, since most objects would not yet have an in-database
<code class="docutils literal notranslate"><span class="pre">size</span></code> Attribute to search for.</p>
<p>In our case, well only refer to these properties as <code class="docutils literal notranslate"><span class="pre">obj.size</span></code> etc, and have no need to find
all objects of a particular size. So we should be safe.</p>
</section>
<section id="creating-tags-in-at-object-creation">
<h3><span class="section-number">4.2.2. </span>Creating tags in <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code><a class="headerlink" href="#creating-tags-in-at-object-creation" title="Permalink to this headline"></a></h3>
<p>The <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> is a method Evennia calls on every child of <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> whenever it is first created.</p>
<p>We do a tricky thing here, converting our <code class="docutils literal notranslate"><span class="pre">.obj_type</span></code> to one or more <a class="reference internal" href="../../../Components/Tags.html"><span class="doc std std-doc">Tags</span></a>. Tagging the object like this means you can later efficiently find all objects of a given type (or combination of
types) with Evennias search functions:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">ObjType</span>
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">search</span>
<span class="c1"># get all shields in the game</span>
<span class="n">all_shields</span> <span class="o">=</span> <span class="n">search</span><span class="o">.</span><span class="n">search_object_by_tag</span><span class="p">(</span><span class="n">ObjType</span><span class="o">.</span><span class="n">SHIELD</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">&quot;obj_type&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>We allow <code class="docutils literal notranslate"><span class="pre">.obj_type</span></code> to be given as a single value or a list of values. We use <code class="docutils literal notranslate"><span class="pre">make_iter</span></code> from the evennia utility library to make sure we dont balk at either. This means you could have a Shield that is also Magical, for example.</p>
</section>
</section>
<section id="other-object-types">
<h2><span class="section-number">4.3. </span>Other object types<a class="headerlink" href="#other-object-types" title="Permalink to this headline"></a></h2>
<p>Some of the other object types are very simple so far.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">AttributeProperty</span><span class="p">,</span> <span class="n">DefaultObject</span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">ObjType</span>
<span class="k">class</span> <span class="nc">EvAdventureObject</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureQuestObject</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Quest objects should usually not be possible to sell or trade.&quot;&quot;&quot;</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">QUEST</span>
<span class="k">class</span> <span class="nc">EvAdventureTreasure</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Treasure is usually just for selling for coin&quot;&quot;&quot;</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">TREASURE</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="consumables">
<h2><span class="section-number">4.4. </span>Consumables<a class="headerlink" href="#consumables" title="Permalink to this headline"></a></h2>
<p>A consumable is an item that has a certain number of uses. Once fully consumed, it cant be used anymore. An example would be a health potion.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureConsumable</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;An item that can be used up&quot;&quot;&quot;</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">CONSUMABLE</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mf">0.25</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">uses</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called before using. If returning False, abort use.&quot;&quot;&quot;</span>
<span class="k">if</span> <span class="n">target</span> <span class="ow">and</span> <span class="n">user</span><span class="o">.</span><span class="n">location</span> <span class="o">!=</span> <span class="n">target</span><span class="o">.</span><span class="n">location</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You are not close enough to the target!&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;|w</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is used up.|n&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">def</span> <span class="nf">use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called when using the item&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">at_post_use</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called after using the item&quot;&quot;&quot;</span>
<span class="c1"># detract a usage, deleting the item if used up.</span>
<span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> was used up.&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>
</pre></div>
</div>
<p>In <code class="docutils literal notranslate"><span class="pre">at_pre_use</span></code> we check if we have specified a target (heal someone else or throw a fire bomb at an enemy?), making sure we are in the same location. We also make sure we have <code class="docutils literal notranslate"><span class="pre">usages</span></code> left. In <code class="docutils literal notranslate"><span class="pre">at_post_use</span></code> we make sure to tick off usages.</p>
<p>What exactly each consumable does will vary - we will need to implement children of this class later, overriding <code class="docutils literal notranslate"><span class="pre">at_use</span></code> with different effects.</p>
</section>
<section id="weapons">
<h2><span class="section-number">4.5. </span>Weapons<a class="headerlink" href="#weapons" title="Permalink to this headline"></a></h2>
<p>All weapons need properties that describe how efficient they are in battle. To use a weapon means to attack with it, so we can let the weapon itself handle all logic around performing an attack. Having the attack code on the weapon also means that if we in the future wanted a weapon doing something special on-attack (for example, a vampiric sword that heals the attacker when hurting the enemy), we could easily add that on the weapon subclass in question without modifying other code.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">ObjType</span><span class="p">,</span> <span class="n">Ability</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureWeapon</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Base class for all weapons&quot;&quot;&quot;</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">WEAPON</span>
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">quality</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">attack_type</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">defend_type</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">Ability</span><span class="o">.</span><span class="n">ARMOR</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">damage_roll</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="s2">&quot;1d6&quot;</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="n">args</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">target</span> <span class="ow">and</span> <span class="n">user</span><span class="o">.</span><span class="n">location</span> <span class="o">!=</span> <span class="n">target</span><span class="o">.</span><span class="n">location</span><span class="p">:</span>
<span class="c1"># we assume weapons can only be used in the same location</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You are not close enough to the target!&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">user</span><span class="p">)</span><span class="si">}</span><span class="s2"> is broken and can&#39;t be used!&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="n">target</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">advantage</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">disadvantage</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>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;When a weapon is used, it attacks an opponent&quot;&quot;&quot;</span>
<span class="n">location</span> <span class="o">=</span> <span class="n">attacker</span><span class="o">.</span><span class="n">location</span>
<span class="n">is_hit</span><span class="p">,</span> <span class="n">quality</span><span class="p">,</span> <span class="n">txt</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">dice</span><span class="o">.</span><span class="n">opposed_saving_throw</span><span class="p">(</span>
<span class="n">attacker</span><span class="p">,</span>
<span class="n">target</span><span class="p">,</span>
<span class="n">attack_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">attack_type</span><span class="p">,</span>
<span class="n">defense_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">defense_type</span><span class="p">,</span>
<span class="n">advantage</span><span class="o">=</span><span class="n">advantage</span><span class="p">,</span>
<span class="n">disadvantage</span><span class="o">=</span><span class="n">disadvantage</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(attack) $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) with </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">txt</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">,</span>
<span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span>
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">},</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">is_hit</span><span class="p">:</span>
<span class="c1"># enemy hit, calculate damage</span>
<span class="n">dmg</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">damage_roll</span><span class="p">)</span>
<span class="k">if</span> <span class="n">quality</span> <span class="ow">is</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CRITICAL_SUCCESS</span><span class="p">:</span>
<span class="c1"># doble damage roll for critical success</span>
<span class="n">dmg</span> <span class="o">+=</span> <span class="n">rules</span><span class="o">.</span><span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">damage_roll</span><span class="p">)</span>
<span class="n">message</span> <span class="o">=</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot; $You() |ycritically|n $conj(hit) $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) for |r</span><span class="si">{</span><span class="n">dmg</span><span class="si">}</span><span class="s2">|n damage!&quot;</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">message</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot; $You() $conj(hit) $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) for |r</span><span class="si">{</span><span class="n">dmg</span><span class="si">}</span><span class="s2">|n damage!&quot;</span>
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span> <span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">})</span>
<span class="c1"># call hook</span>
<span class="n">target</span><span class="o">.</span><span class="n">at_damage</span><span class="p">(</span><span class="n">dmg</span><span class="p">,</span> <span class="n">attacker</span><span class="o">=</span><span class="n">attacker</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># a miss</span>
<span class="n">message</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot; $You() $conj(miss) $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">).&quot;</span>
<span class="k">if</span> <span class="n">quality</span> <span class="ow">is</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CRITICAL_FAILURE</span><span class="p">:</span>
<span class="n">message</span> <span class="o">+=</span> <span class="s2">&quot;.. it&#39;s a |rcritical miss!|n, damaging the weapon.&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span> <span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">})</span>
<span class="k">def</span> <span class="nf">at_post_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</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="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;|r</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">user</span><span class="p">)</span><span class="si">}</span><span class="s2"> breaks and can no longer be used!&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>In EvAdventure, we will assume all weapons (including bows etc) are used in the same location as the target. Weapons also have a <code class="docutils literal notranslate"><span class="pre">quality</span></code> attribute that gets worn down if the user rolls a critical failure. Once quality is down to 0, the weapon is broken and needs to be repaired.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">quality</span></code> is something we need to track in <em>Knave</em>. When getting critical failures on attacks, a weapons quality will go down. When it reaches 0, it will break. We assume that a <code class="docutils literal notranslate"><span class="pre">quality</span></code> of <code class="docutils literal notranslate"><span class="pre">None</span></code> means that quality doesnt apply (that is, the item is unbreakable), so we must consider that when checking.</p>
<p>The attack/defend type tracks how we resolve attacks with the weapon, like <code class="docutils literal notranslate"><span class="pre">roll</span> <span class="pre">+</span> <span class="pre">STR</span> <span class="pre">vs</span> <span class="pre">ARMOR</span> <span class="pre">+</span> <span class="pre">10</span></code>.</p>
<p>In the <code class="docutils literal notranslate"><span class="pre">use</span></code> method we make use of the <code class="docutils literal notranslate"><span class="pre">rules</span></code> module we <a class="reference internal" href="Beginner-Tutorial-Rules.html"><span class="doc std std-doc">created earlier</span></a> to perform all the dice rolls needed to resolve the attack.</p>
<p>This code requires some additional explanation:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(attack) $you(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) with </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">txt</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">,</span>
<span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span>
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">},</span>
<span class="p">)</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">location.msg_contents</span></code> sends a message to everyone in <code class="docutils literal notranslate"><span class="pre">location</span></code>. Since people will usually notice if you swing a sword at somone, this makes sense to tell people about. This message should however look <em>different</em> depending on who sees it.</p>
<p>I should see:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You attack Grendel with sword: &lt;dice roll results&gt;
</pre></div>
</div>
<p>Others should see</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Beowulf attacks Grendel with sword: &lt;dice roll results&gt;
</pre></div>
</div>
<p>And Grendel should see</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Beowulf attacks you with sword: &lt;dice roll results&gt;
</pre></div>
</div>
<p>We provide the following string to <code class="docutils literal notranslate"><span class="pre">msg_contents</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sa">f</span><span class="s2">&quot;$You() $conj(attack) $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) with </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">txt</span><span class="si">}</span><span class="s2">&quot;</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">{...}</span></code> are normal f-string formatting markers like those we have used before. The <code class="docutils literal notranslate"><span class="pre">$func(...)</span></code> bits are <a class="reference internal" href="../../../Components/FuncParser.html"><span class="doc std std-doc">Evennnia FuncParser</span></a> function calls. FuncParser calls are executed as functions and the result replaces their position in the string. As this string is parsed by Evennia, this is what happens:</p>
<p>First the f-string markers are replaced, so that we get this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="s2">&quot;$You() $cobj(attack) $you(Grendel) with sword: </span><span class="se">\n</span><span class="s2"> rolled 8 on d20 ...&quot;</span>
</pre></div>
</div>
<p>Next the funcparser functions are run:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">$You()</span></code> becomes the name or <code class="docutils literal notranslate"><span class="pre">You</span></code> depending on if the string is to be sent to that object or not. It uses the <code class="docutils literal notranslate"><span class="pre">from_obj=</span></code> kwarg to the <code class="docutils literal notranslate"><span class="pre">msg_contents</span></code> method to know this. Since <code class="docutils literal notranslate"><span class="pre">msg_contents=attacker</span></code> , this becomes <code class="docutils literal notranslate"><span class="pre">You</span></code> or <code class="docutils literal notranslate"><span class="pre">Beowulf</span></code> in this example.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">$you(Grendel)</span></code> looks for the <code class="docutils literal notranslate"><span class="pre">mapping=</span></code> kwarg to <code class="docutils literal notranslate"><span class="pre">msg_contents</span></code> to determine who should be addressed here. If will replace this with the display name or the lowercase <code class="docutils literal notranslate"><span class="pre">you</span></code>. We have added <code class="docutils literal notranslate"><span class="pre">mapping={target.key:</span> <span class="pre">target}</span></code> - that is <code class="docutils literal notranslate"><span class="pre">{&quot;Grendel&quot;:</span> <span class="pre">&lt;grendel_obj&gt;}</span></code>. So this will become <code class="docutils literal notranslate"><span class="pre">you</span></code> or <code class="docutils literal notranslate"><span class="pre">Grendel</span></code> depending on who sees the string.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">$conj(attack)</span></code> <em>conjugates</em> the verb depending on who sees it. The result will be <code class="docutils literal notranslate"><span class="pre">You</span> <span class="pre">attack</span> <span class="pre">...</span></code> or <code class="docutils literal notranslate"><span class="pre">Beowulf</span> <span class="pre">attacks</span></code> (note the extra <code class="docutils literal notranslate"><span class="pre">s</span></code>).</p></li>
</ul>
<p>A few funcparser calls compacts all these points of view into one string!</p>
</section>
<section id="magic">
<h2><span class="section-number">4.6. </span>Magic<a class="headerlink" href="#magic" title="Permalink to this headline"></a></h2>
<p>In <em>Knave</em>, anyone can use magic if they are wielding a rune stone (our name for spell books) in both hands. You can only use a rune stone once per rest. So a rune stone is an example of a magical weapon that is also a consumable of sorts.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureConsumable</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureWeapon</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureRuneStone</span><span class="p">(</span><span class="n">EvAdventureWeapon</span><span class="p">,</span> <span class="n">EvAdventureConsumable</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Base for all magical rune stones&quot;&quot;&quot;</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="p">(</span><span class="n">ObjType</span><span class="o">.</span><span class="n">WEAPON</span><span class="p">,</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">MAGIC</span><span class="p">)</span>
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span> <span class="c1"># always two hands for magic</span>
<span class="n">quality</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">attack_type</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">Ability</span><span class="o">.</span><span class="n">INT</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">defend_type</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">Ability</span><span class="o">.</span><span class="n">DEX</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">damage_roll</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="s2">&quot;1d8&quot;</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">at_post_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called after usage/spell was cast&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="c1"># we don&#39;t delete the rune stone here, but </span>
<span class="c1"># it must be reset on next rest.</span>
<span class="k">def</span> <span class="nf">refresh</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Refresh the rune stone (normally after rest)&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o">=</span> <span class="mi">1</span>
</pre></div>
</div>
<p>We make the rune stone a mix of weapon and consumable. Note that we dont have to add <code class="docutils literal notranslate"><span class="pre">.uses</span></code> again, its inherited from <code class="docutils literal notranslate"><span class="pre">EvAdventureConsumable</span></code> parent. The <code class="docutils literal notranslate"><span class="pre">at_pre_use</span></code> and <code class="docutils literal notranslate"><span class="pre">use</span></code> methods are also inherited; we only override <code class="docutils literal notranslate"><span class="pre">at_post_use</span></code> since we dont want the runestone to be deleted when it runs out of uses.</p>
<p>We add a little convenience method <code class="docutils literal notranslate"><span class="pre">refresh</span></code> - we should call this when the character rests, to make the runestone active again.</p>
<p>Exactly what rune stones <em>do</em> will be implemented in the <code class="docutils literal notranslate"><span class="pre">at_use</span></code> methods of subclasses to this base class. Since magic in <em>Knave</em> tends to be pretty custom, it makes sense that it will lead to a lot of custom code.</p>
</section>
<section id="armor">
<h2><span class="section-number">4.7. </span>Armor<a class="headerlink" href="#armor" title="Permalink to this headline"></a></h2>
<p>Armor, shields and helmets increase the <code class="docutils literal notranslate"><span class="pre">ARMOR</span></code> stat of the character. In <em>Knave</em>, what is stored is the defense value of the armor (values 11-20). We will instead store the armor bonus (1-10). As we know, defending is always <code class="docutils literal notranslate"><span class="pre">bonus</span> <span class="pre">+</span> <span class="pre">10</span></code>, so the result will be the same - this means we can use <code class="docutils literal notranslate"><span class="pre">Ability.ARMOR</span></code> as any other defensive ability without worrying about a special case.</p>
<p>``</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureAmor</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">ARMOR</span>
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BODY</span>
<span class="n">armor</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">quality</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">EvAdventureShield</span><span class="p">(</span><span class="n">EvAdventureArmor</span><span class="p">):</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">SHIELD</span>
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span>
<span class="k">class</span> <span class="nc">EvAdventureHelmet</span><span class="p">(</span><span class="n">EvAdventureArmor</span><span class="p">):</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">HELMET</span>
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">HEAD</span>
</pre></div>
</div>
</section>
<section id="your-bare-hands">
<h2><span class="section-number">4.8. </span>Your Bare hands<a class="headerlink" href="#your-bare-hands" title="Permalink to this headline"></a></h2>
<p>When we dont have any weapons, well be using our bare fists to fight.</p>
<p>We will use this in the upcoming <a class="reference internal" href="Beginner-Tutorial-Equipment.html"><span class="doc std std-doc">Equipment tutorial lesson</span></a> to represent when you have nothing in your hands. This way we dont need to add any special case for this.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">search_object</span><span class="p">,</span> <span class="n">create_object</span>
<span class="n">_BARE_HANDS</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">WeaponBareHands</span><span class="p">(</span><span class="n">EvAdventureWeapon</span><span class="p">)</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">WEAPON</span>
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span>
<span class="n">attack_type</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">STR</span>
<span class="n">defense_type</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">ARMOR</span>
<span class="n">damage_roll</span> <span class="o">=</span> <span class="s2">&quot;1d4&quot;</span>
<span class="n">quality</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># let&#39;s assume fists are indestructible ...</span>
<span class="k">def</span> <span class="nf">get_bare_hands</span><span class="p">():</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Get the bare hands&quot;&quot;&quot;</span>
<span class="k">global</span> <span class="n">_BARE_HANDS</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">_BARE_HANDS</span><span class="p">:</span>
<span class="n">_BARE_HANDS</span> <span class="o">=</span> <span class="n">search_object</span><span class="p">(</span><span class="s2">&quot;Bare hands&quot;</span><span class="p">,</span> <span class="n">typeclass</span><span class="o">=</span><span class="n">WeaponBareHands</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">_BARE_HANDS</span><span class="p">:</span>
<span class="n">_BARE_HANDS</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">WeaponBareHands</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;Bare hands&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">_BARE_HANDS</span>
</pre></div>
</div>
<aside class="sidebar">
<p>Creating a single instance of something that is used everywhere is called to create a <em>Singleton</em>.</p>
</aside>
<p>Since everyones empty hands are the same (in our game), we create <em>one</em> <code class="docutils literal notranslate"><span class="pre">Bare</span> <span class="pre">hands</span></code> weapon object that everyone shares. We do this by searching for the object with <code class="docutils literal notranslate"><span class="pre">search_object</span></code> (the <code class="docutils literal notranslate"><span class="pre">.first()</span></code> means we grab the first one even if we should by accident have created multiple hands, see <a class="reference internal" href="../Part1/Beginner-Tutorial-Django-queries.html"><span class="doc std std-doc">The Django querying tutorial</span></a> for more info). If we find none, we create it.</p>
<p>By use of the <code class="docutils literal notranslate"><span class="pre">global</span></code> Python keyword, we cache the bare hands object get/create in a module level property <code class="docutils literal notranslate"><span class="pre">_BARE_HANDS</span></code>. So this acts as a cache to not have to search the database more than necessary.</p>
<p>From now on, other modules can just import and run this function to get the bare hands.</p>
</section>
<section id="testing-and-extra-credits">
<h2><span class="section-number">4.9. </span>Testing and Extra credits<a class="headerlink" href="#testing-and-extra-credits" title="Permalink to this headline"></a></h2>
<p>Remember the <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> function from the <a class="reference internal" href="Beginner-Tutorial-Utilities.html"><span class="doc std std-doc">Utility Tutorial</span></a> earlier? We had to use dummy-values since we didnt yet know how we would store properties on Objects in the game.</p>
<p>Well, we just figured out all we need! You can go back and update <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> to properly read the data from the object it receives.</p>
<p>When you change this function you must also update the related unit test - so your existing test becomes a nice way to test your new Objects as well! Add more tests showing the output of feeding different object-types to <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code>.</p>
<p>Try it out yourself. If you need help, a finished utility example is found in <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.utils.html#evennia.contrib.tutorials.evadventure.utils.get_obj_stats" title="evennia.contrib.tutorials.evadventure.utils.get_obj_stats"><span class="xref myst py py-func">evennia/contrib/tutorials/evadventure/utils.py</span></a>.</p>
</section>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-Equipment.html" title="5. Handling Equipment"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Characters.html" title="3. Player Characters"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">4. </span>In-game Objects and items</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,311 @@
<!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>Part 3: How We Get There (Example Game) &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="1. Code structure and Utilities" href="Beginner-Tutorial-Utilities.html" />
<link rel="prev" title="3. Planning our tutorial game" href="../Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Utilities.html" title="1. Code structure and Utilities"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="../Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html" title="3. Planning our tutorial game"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" accesskey="U">Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Part 3: How We Get There (Example Game)</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Part 3: How We Get There (Example Game)</a><ul>
<li><a class="reference internal" href="#lessons">Lessons</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="../Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html"
title="previous chapter"><span class="section-number">3. </span>Planning our tutorial game</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Utilities.html"
title="next chapter"><span class="section-number">1. </span>Code structure and Utilities</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Part3-Overview.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="part-3-how-we-get-there-example-game">
<h1>Part 3: How We Get There (Example Game)<a class="headerlink" href="#part-3-how-we-get-there-example-game" title="Permalink to this headline"></a></h1>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>The tutorial game is under development and is not yet complete, nor tested. Use the existing lessons as inspiration and to help get you going, but dont expect out-of-the-box perfection from it at this time.</p>
</div>
<aside class="sidebar">
<p class="sidebar-title">Beginner Tutorial Parts</p>
<ul class="simple">
<li><p><a class="reference internal" href="../Beginner-Tutorial-Overview.html"><span class="doc std std-doc">Introduction</span></a>
<br>Getting set up.</p></li>
<li><p>Part 1: <a class="reference internal" href="../Part1/Beginner-Tutorial-Part1-Overview.html"><span class="doc std std-doc">What We Have</span></a>
<br>A tour of Evennia and how to use the tools, including an introduction to Python.</p></li>
<li><p>Part 2: <a class="reference internal" href="../Part2/Beginner-Tutorial-Part2-Overview.html"><span class="doc std std-doc">What We Want</span></a>
<br>Planning our tutorial game and what to consider when planning your own.</p></li>
<li><p><em>Part 3: <a class="reference internal" href="#"><span class="doc std std-doc">How We Get There</span></a></em>
<br>Getting down to the meat of extending Evennia to make your game.</p></li>
<li><p>Part 4: <a class="reference internal" href="../Part4/Beginner-Tutorial-Part4-Overview.html"><span class="doc std std-doc">Using What We Created</span></a>
<br>Building a tech-demo and world content to go with our code.</p></li>
<li><p>Part 5: <a class="reference internal" href="../Part5/Beginner-Tutorial-Part5-Overview.html"><span class="doc std std-doc">Showing the World</span></a>
<br>Taking our new game online and letting players try it out.</p></li>
</ul>
</aside>
<p>In part three of the Evennia Beginner tutorial we will go through the actual creation of
our tutorial game <em>EvAdventure</em>, based on the <a class="reference external" href="https://www.drivethrurpg.com/product/250888/Knave">Knave</a> RPG ruleset.</p>
<p>If you followed the previous parts of this tutorial series you will have some notions about Python and where to find and make use of things in Evennia. We also have a good idea of the type of game we will create.</p>
<p>Even if this is not the game-style you are interested in, following along will give you a lot
of experience using Evennia and be really helpful for doing your own thing later! The EvAdventure game code is also built to easily be expanded upon.</p>
<p>Fully coded examples of all code we make in this part can be found in the
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure</span></a> package. There are three common ways to learn from this:</p>
<ol class="simple">
<li><p>Follow the tutorial lessons in sequence and use it to write your own code, referring to the ready-made code as extra help, context, or as a facit to check yourself.</p></li>
<li><p>Read through the code in the package and refer to the tutorial lesson for each part for more information on what you see.</p></li>
<li><p>Some mix of the two.</p></li>
</ol>
<p>Which approach you choose is individual - we all learn in different ways.</p>
<p>Either way, this is a big part. Youll be seeing a lot of code and there are plenty of lessons to go through. We are making a whole game from scratch after all. Take your time!</p>
<section id="lessons">
<h2>Lessons<a class="headerlink" href="#lessons" title="Permalink to this headline"></a></h2>
<div class="toctree-wrapper compound">
<ul>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Utilities.html">1. Code structure and Utilities</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#folder-structure">1.1. Folder structure</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#enums">1.2. Enums</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#utility-module">1.3. Utility module</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#testing">1.4. Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#summary">1.5. Summary</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Rules.html">2. Rules and dice rolling</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#summary-of-knave-rules">2.1. Summary of <em>Knave</em> rules</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#making-a-rule-module">2.2. Making a rule module</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#rolling-dice">2.3. Rolling dice</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#testing">2.4. Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#summary">2.5. Summary</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Characters.html">3. Player Characters</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#inheritance-structure">3.1. Inheritance structure</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#living-mixin-class">3.2. Living mixin class</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#character-class">3.3. Character class</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#connecting-the-character-with-evennia">3.4. Connecting the Character with Evennia</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#unit-testing">3.5. Unit Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#about-races-and-classes">3.6. About races and classes</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#summary">3.7. Summary</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Objects.html">4. In-game Objects and items</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#new-enums">4.1. New Enums</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#the-base-object">4.2. The base object</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#other-object-types">4.3. Other object types</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#consumables">4.4. Consumables</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#weapons">4.5. Weapons</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#magic">4.6. Magic</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#armor">4.7. Armor</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#your-bare-hands">4.8. Your Bare hands</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#testing-and-extra-credits">4.9. Testing and Extra credits</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Equipment.html">5. Handling Equipment</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#equipmenthandler-that-saves">5.1. EquipmentHandler that saves</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#connecting-the-equipmenthandler">5.2. Connecting the EquipmentHandler</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#expanding-the-equipmenthandler">5.3. Expanding the Equipmenthandler</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#validate-slot-usage">5.4. <code class="docutils literal notranslate"><span class="pre">.validate_slot_usage</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#add-and-remove">5.5. <code class="docutils literal notranslate"><span class="pre">.add</span></code> and <code class="docutils literal notranslate"><span class="pre">.remove</span></code></a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#moving-things-around">5.6. Moving things around</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#get-everything">5.7. Get everything</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#weapon-and-armor">5.8. Weapon and armor</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#extra-credits">5.9. Extra credits</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#unit-testing">5.10. Unit Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#summary">5.11. Summary</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Chargen.html">6. Character Generation</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#how-it-will-work">6.1. How it will work</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#random-tables">6.2. Random tables</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#storing-state-of-the-menu">6.3. Storing state of the menu</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#initializing-evmenu">6.4. Initializing EvMenu</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#main-node-choosing-what-to-do">6.5. Main Node: Choosing what to do</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#node-changing-your-name">6.6. Node: Changing your name</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#node-swapping-abilities-around">6.7. Node: Swapping Abilities around</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#node-creating-the-character">6.8. Node: Creating the Character</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#tying-the-nodes-together">6.9. Tying the nodes together</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#conclusions">6.10. Conclusions</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Rooms.html">7. In-game Rooms</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#the-base-room">7.1. The base room</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#pvp-room">7.2. PvP room</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#adding-a-room-map">7.3. Adding a room map</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#adding-life-to-a-room">7.4. Adding life to a room</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#testing">7.5. Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#conclusion">7.6. Conclusion</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-NPCs.html">8. Non-Player-Characters</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-NPCs.html#the-npc-base-class">8.1. The NPC base class</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-NPCs.html#testing">8.2. Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-NPCs.html#conclusions">8.3. Conclusions</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html">9. Combat base framework</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#combathandler">9.1. CombatHandler</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#actions">9.2. Actions</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#action-dicts">9.3. Action dicts</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#action-classes">9.4. Action classes</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#testing">9.5. Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#conclusions">9.6. Conclusions</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html">10. Twitch Combat</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#general-principle">10.1. General principle</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#twitch-combat-handler">10.2. Twitch combat handler</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#commands">10.3. Commands</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#grouping-commands-for-use">10.4. Grouping Commands for use</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#unit-testing">10.5. Unit Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#a-small-combat-test">10.6. A small combat test</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#conclusions">10.7. Conclusions</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html">11. Turnbased Combat</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#general-principle">11.1. General Principle</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#turnbased-combat-handler">11.2. Turnbased combat handler</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#using-evmenu-for-the-combat-menu">11.3. Using EvMenu for the combat menu</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#menu-for-turnbased-combat">11.4. Menu for Turnbased combat</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#attack-command">11.5. Attack Command</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#making-sure-the-menu-stops">11.6. Making sure the menu stops</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#testing">11.7. Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#a-small-combat-test">11.8. A small combat test</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#conclusions">11.9. Conclusions</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-AI.html">12. NPC and monster AI</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Dungeon.html">13. Dynamically generated Dungeon</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Quests.html">14. Game Quests</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Shops.html">15. In-game Shops</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Commands.html">16. In-game Commands</a></li>
</ul>
</div>
</section>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-Utilities.html" title="1. Code structure and Utilities"
>next</a> |</li>
<li class="right" >
<a href="../Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html" title="3. Planning our tutorial game"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Part 3: How We Get There (Example Game)</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,151 @@
<!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>14. Game Quests &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="15. In-game Shops" href="Beginner-Tutorial-Shops.html" />
<link rel="prev" title="13. Dynamically generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">14. </span>Game Quests</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Dungeon.html"
title="previous chapter"><span class="section-number">13. </span>Dynamically generated Dungeon</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Shops.html"
title="next chapter"><span class="section-number">15. </span>In-game Shops</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Quests.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="game-quests">
<h1><span class="section-number">14. </span>Game Quests<a class="headerlink" href="#game-quests" title="Permalink to this headline"></a></h1>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>This part of the Beginner tutorial is still being developed.</p>
</div>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">14. </span>Game Quests</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,502 @@
<!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>7. In-game Rooms &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="8. Non-Player-Characters" href="Beginner-Tutorial-NPCs.html" />
<link rel="prev" title="6. Character Generation" href="Beginner-Tutorial-Chargen.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Chargen.html" title="6. Character Generation"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">7. </span>In-game Rooms</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">7. In-game Rooms</a><ul>
<li><a class="reference internal" href="#the-base-room">7.1. The base room</a></li>
<li><a class="reference internal" href="#pvp-room">7.2. PvP room</a></li>
<li><a class="reference internal" href="#adding-a-room-map">7.3. Adding a room map</a></li>
<li><a class="reference internal" href="#adding-life-to-a-room">7.4. Adding life to a room</a></li>
<li><a class="reference internal" href="#testing">7.5. Testing</a></li>
<li><a class="reference internal" href="#conclusion">7.6. Conclusion</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Chargen.html"
title="previous chapter"><span class="section-number">6. </span>Character Generation</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-NPCs.html"
title="next chapter"><span class="section-number">8. </span>Non-Player-Characters</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Rooms.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="in-game-rooms">
<h1><span class="section-number">7. </span>In-game Rooms<a class="headerlink" href="#in-game-rooms" title="Permalink to this headline"></a></h1>
<p>A <em>room</em> describes a specific location in the game world. Being an abstract concept, it can represent any area of game content that is convenient to group together. In this lesson we will also create a small in-game automap.</p>
<p>In EvAdventure, we will have two main types of rooms:</p>
<ul class="simple">
<li><p>Normal, above-ground rooms. Based on a fixed map, these will be created once and then dont change. Well cover them in this lesson.</p></li>
<li><p>Dungeon rooms - these will be examples of <em>procedurally generated</em> rooms, created on the fly as the players explore the underworld. Being subclasses of the normal room, well get to them in the <a class="reference internal" href="Beginner-Tutorial-Dungeon.html"><span class="doc std std-doc">Dungeon generation lesson</span></a>.</p></li>
</ul>
<section id="the-base-room">
<h2><span class="section-number">7.1. </span>The base room<a class="headerlink" href="#the-base-room" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/rooms.py</span></code>.</p>
</div></blockquote>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/rooms.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">AttributeProperty</span><span class="p">,</span> <span class="n">DefaultRoom</span>
<span class="k">class</span> <span class="nc">EvAdventureRoom</span><span class="p">(</span><span class="n">DefaultRoom</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Simple room supporting some EvAdventure-specifics.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">allow_combat</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">allow_pvp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">allow_death</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</pre></div>
</div>
<p>Our <code class="docutils literal notranslate"><span class="pre">EvadventureRoom</span></code> is very simple. We use Evennias <code class="docutils literal notranslate"><span class="pre">DefaultRoom</span></code> as a base and just add three additional Attributes that defines</p>
<ul class="simple">
<li><p>If combat is allowed to start in the room at all.</p></li>
<li><p>If combat is allowed, if PvP (player vs player) combat is allowed.</p></li>
<li><p>If combat is allowed, if any side is allowed to die from it.</p></li>
</ul>
<p>Later on we must make sure our combat systems honors these values.</p>
</section>
<section id="pvp-room">
<h2><span class="section-number">7.2. </span>PvP room<a class="headerlink" href="#pvp-room" title="Permalink to this headline"></a></h2>
<p>Heres a room that allows non-lethal PvP (sparring):</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/rooms.py</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventurePvPRoom</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Room where PvP can happen, but noone gets killed.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">allow_combat</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">True</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">allow_pvp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">True</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_display_footer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Customize footer of description.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="s2">&quot;|yNon-lethal PvP combat is allowed here!|n&quot;</span>
</pre></div>
</div>
<p>The return of <code class="docutils literal notranslate"><span class="pre">get_display_footer</span></code> will show after the <a class="reference internal" href="../../../Components/Objects.html#changing-an-objects-appearance"><span class="std std-doc">main room description</span></a>, showing that the room is a sparring room. This means that when a player drops to 0 HP, they will lose the combat, but dont stand any risk of dying (weapons wear out normally during sparring though).</p>
</section>
<section id="adding-a-room-map">
<h2><span class="section-number">7.3. </span>Adding a room map<a class="headerlink" href="#adding-a-room-map" title="Permalink to this headline"></a></h2>
<p>We want a dynamic map that visualizes the exits you can use at any moment. Heres how our room will display:</p>
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span><span class="w"> </span>o<span class="w"> </span>o<span class="w"> </span>o
<span class="w"> </span><span class="se">\|</span>/
<span class="w"> </span>o-@-o
<span class="w"> </span><span class="p">|</span><span class="w"> </span>
<span class="w"> </span>o
The<span class="w"> </span>crossroads<span class="w"> </span>
A<span class="w"> </span>place<span class="w"> </span>where<span class="w"> </span>many<span class="w"> </span>roads<span class="w"> </span>meet.<span class="w"> </span>
Exits:<span class="w"> </span>north,<span class="w"> </span>northeast,<span class="w"> </span>south,<span class="w"> </span>west,<span class="w"> </span>and<span class="w"> </span>northwest
</pre></div>
</div>
<blockquote>
<div><p>Documentation does not show ansi colors.</p>
</div></blockquote>
<p>Lets expand the base <code class="docutils literal notranslate"><span class="pre">EvAdventureRoom</span></code> with the map.</p>
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span>
<span class="normal">12</span>
<span class="normal">13</span>
<span class="normal">14</span>
<span class="normal">15</span>
<span class="normal">16</span>
<span class="normal">17</span>
<span class="normal">18</span>
<span class="normal">19</span>
<span class="normal">20</span>
<span class="normal">21</span>
<span class="normal">22</span>
<span class="normal">23</span>
<span class="normal">24</span>
<span class="normal">25</span>
<span class="normal">26</span>
<span class="normal">27</span>
<span class="normal">28</span>
<span class="normal">29</span>
<span class="normal">30</span>
<span class="normal">31</span>
<span class="normal">32</span>
<span class="normal">33</span>
<span class="normal">34</span>
<span class="normal">35</span>
<span class="normal">36</span>
<span class="normal">37</span>
<span class="normal">38</span>
<span class="normal">39</span>
<span class="normal">40</span>
<span class="normal">41</span>
<span class="normal">42</span>
<span class="normal">43</span>
<span class="normal">44</span>
<span class="normal">45</span>
<span class="normal">46</span>
<span class="normal">47</span>
<span class="normal">48</span>
<span class="normal">49</span>
<span class="normal">50</span>
<span class="normal">51</span>
<span class="normal">52</span>
<span class="normal">53</span>
<span class="normal">54</span>
<span class="normal">55</span>
<span class="normal">56</span>
<span class="normal">57</span>
<span class="normal">58</span>
<span class="normal">59</span>
<span class="normal">60</span>
<span class="normal">61</span>
<span class="normal">62</span>
<span class="normal">63</span>
<span class="normal">64</span>
<span class="normal">65</span>
<span class="normal">66</span>
<span class="normal">67</span>
<span class="normal">68</span>
<span class="normal">69</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventyre/rooms.py</span>
<span class="c1"># ... </span>
<span class="kn">from</span> <span class="nn">copy</span> <span class="kn">import</span> <span class="n">deepcopy</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span>
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">inherits_from</span>
<span class="n">CHAR_SYMBOL</span> <span class="o">=</span> <span class="s2">&quot;|w@|n&quot;</span>
<span class="n">CHAR_ALT_SYMBOL</span> <span class="o">=</span> <span class="s2">&quot;|w&gt;|n&quot;</span>
<span class="n">ROOM_SYMBOL</span> <span class="o">=</span> <span class="s2">&quot;|bo|n&quot;</span>
<span class="hll"><span class="n">LINK_COLOR</span> <span class="o">=</span> <span class="s2">&quot;|B&quot;</span>
</span>
<span class="n">_MAP_GRID</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">[</span><span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">],</span>
<span class="p">[</span><span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">],</span>
<span class="p">[</span><span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot;@&quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">],</span>
<span class="p">[</span><span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">],</span>
<span class="hll"> <span class="p">[</span><span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">],</span>
</span><span class="p">]</span>
<span class="n">_EXIT_GRID_SHIFT</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;north&quot;</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;||&quot;</span><span class="p">),</span>
<span class="s2">&quot;east&quot;</span><span class="p">:</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">&quot;-&quot;</span><span class="p">),</span>
<span class="s2">&quot;south&quot;</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;||&quot;</span><span class="p">),</span>
<span class="s2">&quot;west&quot;</span><span class="p">:</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">&quot;-&quot;</span><span class="p">),</span>
<span class="s2">&quot;northeast&quot;</span><span class="p">:</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;/&quot;</span><span class="p">),</span>
<span class="s2">&quot;southeast&quot;</span><span class="p">:</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;</span><span class="se">\\</span><span class="s2">&quot;</span><span class="p">),</span>
<span class="s2">&quot;southwest&quot;</span><span class="p">:</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;/&quot;</span><span class="p">),</span>
<span class="s2">&quot;northwest&quot;</span><span class="p">:</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;</span><span class="se">\\</span><span class="s2">&quot;</span><span class="p">),</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">EvAdventureRoom</span><span class="p">(</span><span class="n">DefaultRoom</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">format_appearance</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">appearance</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Don&#39;t left-strip the appearance string&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">appearance</span><span class="o">.</span><span class="n">rstrip</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">get_display_header</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Display the current location as a mini-map.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># make sure to not show make a map for users of screenreaders.</span>
<span class="c1"># for optimization we also don&#39;t show it to npcs/mobs</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">inherits_from</span><span class="p">(</span><span class="n">looker</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span>
<span class="n">looker</span><span class="o">.</span><span class="n">account</span> <span class="ow">and</span> <span class="n">looker</span><span class="o">.</span><span class="n">account</span><span class="o">.</span><span class="n">uses_screenreader</span><span class="p">()</span>
<span class="p">):</span>
<span class="k">return</span> <span class="s2">&quot;&quot;</span>
<span class="hll">
</span><span class="hll"> <span class="c1"># build a map</span>
</span> <span class="n">map_grid</span> <span class="o">=</span> <span class="n">deepcopy</span><span class="p">(</span><span class="n">_MAP_GRID</span><span class="p">)</span>
<span class="n">dx0</span><span class="p">,</span> <span class="n">dy0</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span>
<span class="n">map_grid</span><span class="p">[</span><span class="n">dy0</span><span class="p">][</span><span class="n">dx0</span><span class="p">]</span> <span class="o">=</span> <span class="n">CHAR_SYMBOL</span>
<span class="k">for</span> <span class="n">exi</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">exits</span><span class="p">:</span>
<span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="p">,</span> <span class="n">symbol</span> <span class="o">=</span> <span class="n">_EXIT_GRID_SHIFT</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">exi</span><span class="o">.</span><span class="n">key</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="kc">None</span><span class="p">))</span>
<span class="hll"> <span class="k">if</span> <span class="n">symbol</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span> <span class="c1"># we have a non-cardinal direction to go to - indicate this</span>
<span class="n">map_grid</span><span class="p">[</span><span class="n">dy0</span><span class="p">][</span><span class="n">dx0</span><span class="p">]</span> <span class="o">=</span> <span class="n">CHAR_ALT_SYMBOL</span>
<span class="k">continue</span>
<span class="n">map_grid</span><span class="p">[</span><span class="n">dy0</span> <span class="o">+</span> <span class="n">dy</span><span class="p">][</span><span class="n">dx0</span> <span class="o">+</span> <span class="n">dx</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">LINK_COLOR</span><span class="si">}{</span><span class="n">symbol</span><span class="si">}</span><span class="s2">|n&quot;</span>
<span class="k">if</span> <span class="n">exi</span><span class="o">.</span><span class="n">destination</span> <span class="o">!=</span> <span class="bp">self</span><span class="p">:</span>
<span class="n">map_grid</span><span class="p">[</span><span class="n">dy0</span> <span class="o">+</span> <span class="n">dy</span> <span class="o">+</span> <span class="n">dy</span><span class="p">][</span><span class="n">dx0</span> <span class="o">+</span> <span class="n">dx</span> <span class="o">+</span> <span class="n">dx</span><span class="p">]</span> <span class="o">=</span> <span class="n">ROOM_SYMBOL</span>
<span class="c1"># Note that on the grid, dy is really going *downwards* (origo is</span>
<span class="hll"> <span class="c1"># in the top left), so we need to reverse the order at the end to mirror it</span>
</span> <span class="c1"># vertically and have it come out right.</span>
<span class="k">return</span> <span class="s2">&quot; &quot;</span> <span class="o">+</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2"> &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="n">map_grid</span><span class="p">))</span>
</pre></div></td></tr></table></div>
</div>
<p>The string returned from <code class="docutils literal notranslate"><span class="pre">get_display_header</span></code> will end up at the top of the <span class="xref myst">room description</span>, a good place to have the map appear!</p>
<ul class="simple">
<li><p><strong>Line 12</strong>: The map itself consists of the 2D matrix <code class="docutils literal notranslate"><span class="pre">_MAP_GRID</span></code>. This is a 2D area described by a list of Python lists. To find a given place in the list, you first first need to find which of the nested lists to use, and then which element to use in that list. Indices start from 0 in Python. So to draw the <code class="docutils literal notranslate"><span class="pre">o</span></code> symbol for the southermost room, youd need to do so at <code class="docutils literal notranslate"><span class="pre">_MAP_GRID[4][2]</span></code>.</p></li>
<li><p><strong>Line 19</strong>: The <code class="docutils literal notranslate"><span class="pre">_EXIT_GRID_SHIFT</span></code> indicates the direction to go for each cardinal exit, along with the map symbol to draw at that point. So <code class="docutils literal notranslate"><span class="pre">&quot;east&quot;:</span> <span class="pre">(1,</span> <span class="pre">0,</span> <span class="pre">&quot;-&quot;)</span></code> means the east exit will be drawn one step in the positive x direction (to the right), using the “-” symbol. For symbols like <code class="docutils literal notranslate"><span class="pre">|</span></code> and “\” we need to escape with a double-symbol since these would otherwise be interpreted as part of other formatting.</p></li>
<li><p><strong>Line 51</strong>: We start by making a <code class="docutils literal notranslate"><span class="pre">deepcopy</span></code> of the <code class="docutils literal notranslate"><span class="pre">_MAP_GRID</span></code>. This is so that we dont modify the original but always have an empty template to work from.</p></li>
<li><p><strong>Line 52</strong>: We use <code class="docutils literal notranslate"><span class="pre">&#64;</span></code> to indicate the location of the player (at coordinate <code class="docutils literal notranslate"><span class="pre">(2,</span> <span class="pre">2)</span></code>). We then take the actual exits from the room use their names to figure out what symbols to draw out from the center.</p></li>
<li><p><strong>Line 58</strong>: We want to be able to get on/off the grid if so needed. So if a room has a non-cardinal exit in it (like back or up/down), well indicate this by showing the <code class="docutils literal notranslate"><span class="pre">&gt;</span></code> symbol instead of the <code class="docutils literal notranslate"><span class="pre">&#64;</span></code> in your current room.</p></li>
<li><p><strong>Line 67</strong>: Once we have placed all the exit- and room-symbols in the grid, we merge it all together into a single string. At the end we use Pythons standard <a class="reference external" href="https://www.w3schools.com/python/ref_string_join.asp">join</a> to convert the grid into a single string. In doing so we must flip the grid upside down (reverse the outermost list). Why is this? If you think about how a MUD game displays its data - by printing at the bottom and then scrolling upwards - youll realize that Evennia has to send out the top of your map <em>first</em> and the bottom of it <em>last</em> for it to show correctly to the user.</p></li>
</ul>
</section>
<section id="adding-life-to-a-room">
<h2><span class="section-number">7.4. </span>Adding life to a room<a class="headerlink" href="#adding-life-to-a-room" title="Permalink to this headline"></a></h2>
<p>Normally the room is static until you do something in it. But lets say you are in a room described to be a bustling market. Would it not be nice to occasionally get some random messages like</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&quot;You hear a merchant calling out his wares.&quot;
&quot;The sound of music drifts over the square from an open tavern door.&quot;
&quot;The sound of commerse rises and fall in a steady rythm.&quot;
</pre></div>
</div>
<p>Heres an example of how to accomplish this:</p>
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span>
<span class="normal">12</span>
<span class="normal">13</span>
<span class="normal">14</span>
<span class="normal">15</span>
<span class="normal">16</span>
<span class="normal">17</span>
<span class="normal">18</span>
<span class="normal">19</span>
<span class="normal">20</span>
<span class="normal">21</span>
<span class="normal">22</span>
<span class="normal">23</span>
<span class="normal">24</span>
<span class="normal">25</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/rooms.py </span>
<span class="c1"># ... </span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">choice</span><span class="p">,</span> <span class="n">random</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">TICKER_HANDLER</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EchoingRoom</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;A room that randomly echoes messages to everyone inside it&quot;&quot;&quot;</span>
<span class="n">echoes</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">echo_rate</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">60</span> <span class="o">*</span> <span class="mi">2</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">echo_chance</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mf">0.1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">send_echo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">echoes</span> <span class="ow">and</span> <span class="n">random</span><span class="p">()</span> <span class="o">&lt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">echo_chance</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="n">choice</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">echoes</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">start_echo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="hll"> <span class="n">TICKER_HANDLER</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">echo_rate</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">send_echo</span><span class="p">)</span>
</span>
<span class="k">def</span> <span class="nf">stop_echo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="hll"> <span class="n">TICKER_HANDLER</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">echo_rate</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">send_echo</span><span class="p">)</span>
</span></pre></div></td></tr></table></div>
</div>
<p>The <a class="reference internal" href="../../../Components/TickerHandler.html"><span class="doc std std-doc">TickerHandler</span></a>. This is acts as a please tick me - subscription service. In <strong>Line 22</strong> we tell add our <code class="docutils literal notranslate"><span class="pre">.send_echo</span></code> method to the handler and tell the TickerHandler to call that method every <code class="docutils literal notranslate"><span class="pre">.echo_rate</span></code> seconds.</p>
<p>When the <code class="docutils literal notranslate"><span class="pre">.send_echo</span></code> method is called, it will use <code class="docutils literal notranslate"><span class="pre">random.random()</span></code> to check if we should <em>actually</em> do anything. In our example we only show a message 10% of the time. In that case we use Pythons <code class="docutils literal notranslate"><span class="pre">random.choice()</span></code> to grab a random text string from the <code class="docutils literal notranslate"><span class="pre">.echoes</span></code> list to send to everyone inside this room.</p>
<p>Heres how youd use this room in-game:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; dig market:evadventure.rooms.EchoingRoom = market,back
&gt; market
&gt; set here/echoes = [&quot;You hear a merchant shouting&quot;, &quot;You hear the clatter of coins&quot;]
&gt; py here.start_echo()
</pre></div>
</div>
<p>If you wait a while youll eventually see one of the two echoes show up. Use <code class="docutils literal notranslate"><span class="pre">py</span> <span class="pre">here.stop_echo()</span></code> if you want.</p>
<p>Its a good idea to be able to turn on/off the echoes at will, if nothing else because youd be surprised how annoying they can be if they show too often.</p>
<p>In this example we had to resort to <code class="docutils literal notranslate"><span class="pre">py</span></code> to activate/deactivate the echoes, but you could very easily make little utility <a class="reference internal" href="../Part1/Beginner-Tutorial-Adding-Commands.html"><span class="doc std std-doc">Commands</span></a> <code class="docutils literal notranslate"><span class="pre">startecho</span></code> and <code class="docutils literal notranslate"><span class="pre">stopecho</span></code> to do it for you. This we leave as a bonus exercise.</p>
</section>
<section id="testing">
<h2><span class="section-number">7.5. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/tests/test_rooms.py</span></code>.</p>
</div></blockquote>
<aside class="sidebar">
<p>You can find a ready testing module <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_rooms.html#evennia-contrib-tutorials-evadventure-tests-test-rooms"><span class="std std-ref">here in the tutorial folder</span></a>.</p>
</aside>
<p>The main thing to test with our new rooms is the map. Heres the basic principle for how to do this testing:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/tests/test_rooms.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultExit</span><span class="p">,</span> <span class="n">create_object</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTestCase</span>
<span class="kn">from</span> <span class="nn">..characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
<span class="kn">from</span> <span class="nn">..rooms</span> <span class="kn">import</span> <span class="n">EvAdventureRoom</span>
<span class="k">class</span> <span class="nc">EvAdventureRoomTest</span><span class="p">(</span><span class="n">EvenniaTestCase</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">test_map</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">center_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room_center&quot;</span><span class="p">)</span>
<span class="n">n_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room_n)</span>
<span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;north&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">n_room</span><span class="p">)</span>
<span class="n">ne_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room=ne&quot;</span><span class="p">)</span>
<span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;northeast&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">ne_room</span><span class="p">)</span>
<span class="c1"># ... etc for all cardinal directions </span>
<span class="n">char</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;TestChar&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">)</span>
<span class="n">desc</span> <span class="o">=</span> <span class="n">center_room</span><span class="o">.</span><span class="n">return_appearance</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
<span class="c1"># compare the desc we got with the expected description here</span>
</pre></div>
</div>
<p>So we create a bunch of rooms, link them to one centr room and then make sure the map in that room looks like wed expect.</p>
</section>
<section id="conclusion">
<h2><span class="section-number">7.6. </span>Conclusion<a class="headerlink" href="#conclusion" title="Permalink to this headline"></a></h2>
<p>In this lesson we manipulated strings and made a map. Changing the description of an object is a big part of changing the graphics of a text-based game, so checking out the <span class="xref myst">parts making up an object description</span> is good extra reading.</p>
</section>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Chargen.html" title="6. Character Generation"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">7. </span>In-game Rooms</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,790 @@
<!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>2. Rules and dice rolling &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="3. Player Characters" href="Beginner-Tutorial-Characters.html" />
<link rel="prev" title="1. Code structure and Utilities" href="Beginner-Tutorial-Utilities.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Characters.html" title="3. Player Characters"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Utilities.html" title="1. Code structure and Utilities"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">2. </span>Rules and dice rolling</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">2. Rules and dice rolling</a><ul>
<li><a class="reference internal" href="#summary-of-knave-rules">2.1. Summary of <em>Knave</em> rules</a></li>
<li><a class="reference internal" href="#making-a-rule-module">2.2. Making a rule module</a></li>
<li><a class="reference internal" href="#rolling-dice">2.3. Rolling dice</a><ul>
<li><a class="reference internal" href="#generic-dice-roller">2.3.1. Generic dice roller</a></li>
<li><a class="reference internal" href="#rolling-with-advantage">2.3.2. Rolling with advantage</a></li>
<li><a class="reference internal" href="#saving-throws">2.3.3. Saving throws</a></li>
<li><a class="reference internal" href="#opposed-saving-throw">2.3.4. Opposed saving throw</a></li>
<li><a class="reference internal" href="#morale-check">2.3.5. Morale check</a></li>
<li><a class="reference internal" href="#roll-for-healing">2.3.6. Roll for Healing</a></li>
<li><a class="reference internal" href="#rolling-on-a-table">2.3.7. Rolling on a table</a></li>
<li><a class="reference internal" href="#roll-for-death">2.3.8. Roll for death</a></li>
</ul>
</li>
<li><a class="reference internal" href="#testing">2.4. Testing</a><ul>
<li><a class="reference internal" href="#mocking-and-patching">2.4.1. Mocking and patching</a></li>
</ul>
</li>
<li><a class="reference internal" href="#summary">2.5. Summary</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Utilities.html"
title="previous chapter"><span class="section-number">1. </span>Code structure and Utilities</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Characters.html"
title="next chapter"><span class="section-number">3. </span>Player Characters</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Rules.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="rules-and-dice-rolling">
<h1><span class="section-number">2. </span>Rules and dice rolling<a class="headerlink" href="#rules-and-dice-rolling" title="Permalink to this headline"></a></h1>
<p>In <em>EvAdventure</em> we have decided to use the <a class="reference external" href="https://www.drivethrurpg.com/product/250888/Knave">Knave</a>
RPG ruleset. This is commercial, but released under Creative Commons 4.0, meaning its okay to share and
adapt <em>Knave</em> for any purpose, even commercially. If you dont want to buy it but still follow
along, you can find a <a class="reference external" href="http://abominablefancy.blogspot.com/2018/10/knaves-fancypants.html">free fan-version here</a>.</p>
<section id="summary-of-knave-rules">
<h2><span class="section-number">2.1. </span>Summary of <em>Knave</em> rules<a class="headerlink" href="#summary-of-knave-rules" title="Permalink to this headline"></a></h2>
<p>Knave, being inspired by early Dungeons &amp; Dragons, is very simple.</p>
<ul class="simple">
<li><p>It uses six Ability bonuses
<em>Strength</em> (STR), <em>Dexterity</em> (DEX), <em>Constitution</em> (CON), <em>Intelligence</em> (INT), <em>Wisdom</em> (WIS)
and <em>Charisma</em> (CHA). These are rated from <code class="docutils literal notranslate"><span class="pre">+1</span></code> to <code class="docutils literal notranslate"><span class="pre">+10</span></code>.</p></li>
<li><p>Rolls are made with a twenty-sided die (<code class="docutils literal notranslate"><span class="pre">1d20</span></code>), usually adding a suitable Ability bonus to the roll.</p></li>
<li><p>If you roll <em>with advantage</em>, you roll <code class="docutils literal notranslate"><span class="pre">2d20</span></code> and pick the
<em>highest</em> value, If you roll <em>with disadvantage</em>, you roll <code class="docutils literal notranslate"><span class="pre">2d20</span></code> and pick the <em>lowest</em>.</p></li>
<li><p>Rolling a natural <code class="docutils literal notranslate"><span class="pre">1</span></code> is a <em>critical failure</em>. A natural <code class="docutils literal notranslate"><span class="pre">20</span></code> is a <em>critical success</em>. Rolling such
in combat means your weapon or armor loses quality, which will eventually destroy it.</p></li>
<li><p>A <em>saving throw</em> (trying to succeed against the environment) means making a roll to beat <code class="docutils literal notranslate"><span class="pre">15</span></code> (always).
So if you are lifting a heavy stone and have <code class="docutils literal notranslate"><span class="pre">STR</span> <span class="pre">+2</span></code>, youd roll <code class="docutils literal notranslate"><span class="pre">1d20</span> <span class="pre">+</span> <span class="pre">2</span></code> and hope the result
is higher than <code class="docutils literal notranslate"><span class="pre">15</span></code>.</p></li>
<li><p>An <em>opposed saving throw</em> means beating the enemys suitable Ability defense, which is always their
<code class="docutils literal notranslate"><span class="pre">Ability</span> <span class="pre">bonus</span> <span class="pre">+</span> <span class="pre">10</span></code>. So if you have <code class="docutils literal notranslate"><span class="pre">STR</span> <span class="pre">+1</span></code> and are arm wrestling someone with <code class="docutils literal notranslate"><span class="pre">STR</span> <span class="pre">+2</span></code>, you roll
<code class="docutils literal notranslate"><span class="pre">1d20</span> <span class="pre">+</span> <span class="pre">1</span></code> and hope to roll higher than <code class="docutils literal notranslate"><span class="pre">2</span> <span class="pre">+</span> <span class="pre">10</span> <span class="pre">=</span> <span class="pre">12</span></code>.</p></li>
<li><p>A special bonus is <code class="docutils literal notranslate"><span class="pre">Armor</span></code>, <code class="docutils literal notranslate"><span class="pre">+1</span></code> is unarmored, additional armor is given by equipment. Melee attacks
test <code class="docutils literal notranslate"><span class="pre">STR</span></code> versus the <code class="docutils literal notranslate"><span class="pre">Armor</span></code> defense value while ranged attacks uses <code class="docutils literal notranslate"><span class="pre">WIS</span></code> vs <code class="docutils literal notranslate"><span class="pre">Armor</span></code>.</p></li>
<li><p><em>Knave</em> has no skills or classes. Everyone can use all items and using magic means having a special
rune stone in your hands; one spell per stone and day.</p></li>
<li><p>A character has <code class="docutils literal notranslate"><span class="pre">CON</span> <span class="pre">+</span> <span class="pre">10</span></code> carry slots. Most normal items uses one slot, armor and large weapons uses
two or three.</p></li>
<li><p>Healing is random, <code class="docutils literal notranslate"><span class="pre">1d8</span> <span class="pre">+</span> <span class="pre">CON</span></code> health healed after food and sleep.</p></li>
<li><p>Monster difficulty is listed by hy many 1d8 HP they have; this is called their “hit die” or HD. If
needing to test Abilities, monsters have HD bonus in every Ability.</p></li>
<li><p>Monsters have a <em>morale rating</em>. When things go bad, they have a chance to panic and flee if
rolling <code class="docutils literal notranslate"><span class="pre">2d6</span></code> over their morale rating.</p></li>
<li><p>All Characters in <em>Knave</em> are mostly randomly generated. HP is <code class="docutils literal notranslate"><span class="pre">&lt;level&gt;d8</span></code> but we give every
new character max HP to start.</p></li>
<li><p><em>Knave</em> also have random tables, such as for starting equipment and to see if dying when
hitting 0. Death, if it happens, is permanent.</p></li>
</ul>
</section>
<section id="making-a-rule-module">
<h2><span class="section-number">2.2. </span>Making a rule module<a class="headerlink" href="#making-a-rule-module" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module mygame/evadventure/rules.py</p>
</div></blockquote>
<aside class="sidebar">
<p>A complete version of the rule module is found in
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.rules.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/rules.py</span></a>.</p>
</aside>
<p>There are three broad sets of rules for most RPGS:</p>
<ul class="simple">
<li><p>Character generation rules, often only used during character creation</p></li>
<li><p>Regular gameplay rules - rolling dice and resolving game situations</p></li>
<li><p>Character improvement - getting and spending experience to improve the character</p></li>
</ul>
<p>We want our <code class="docutils literal notranslate"><span class="pre">rules</span></code> module to cover as many aspeects of what wed otherwise would have to look up
in a rulebook.</p>
</section>
<section id="rolling-dice">
<h2><span class="section-number">2.3. </span>Rolling dice<a class="headerlink" href="#rolling-dice" title="Permalink to this headline"></a></h2>
<p>We will start by making a dice roller. Lets group all of our dice rolling into a structure like this
(not functional code yet):</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">roll</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
<span class="c1"># get result of one generic roll, for any type and number of dice</span>
<span class="k">def</span> <span class="nf">roll_with_advantage_or_disadvantage</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="c1"># get result of normal d20 roll, with advantage/disadvantage (or not)</span>
<span class="k">def</span> <span class="nf">saving_throw</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
<span class="c1"># do a saving throw against a specific target number</span>
<span class="k">def</span> <span class="nf">opposed_saving_throw</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
<span class="c1"># do an opposed saving throw against a target&#39;s defense</span>
<span class="k">def</span> <span class="nf">roll_random_table</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
<span class="c1"># make a roll against a random table (loaded elsewere)</span>
<span class="k">def</span> <span class="nf">morale_check</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
<span class="c1"># roll a 2d6 morale check for a target</span>
<span class="k">def</span> <span class="nf">heal_from_rest</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
<span class="c1"># heal 1d8 when resting+eating, but not more than max value.</span>
<span class="k">def</span> <span class="nf">roll_death</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
<span class="c1"># roll to determine penalty when hitting 0 HP. </span>
<span class="n">dice</span> <span class="o">=</span> <span class="n">EvAdventureRollEngine</span><span class="p">()</span>
</pre></div>
</div>
<aside class="sidebar">
<p>This groups all dice-related code into one container that is easy to import. But its mostly a matter
of taste. You <em>could</em> also break up the class methods into normal functions at the top-level of the
module if you wanted.</p>
</aside>
<p>This structure (called a <em>singleton</em>) means we group all dice rolls into one class that we then initiate
into a variable <code class="docutils literal notranslate"><span class="pre">dice</span></code> at the end of the module. This means that we can do the following from other
modules:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="kn">from</span> <span class="nn">.rules</span> <span class="kn">import</span> <span class="n">dice</span>
<span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d8&quot;</span><span class="p">)</span>
</pre></div>
</div>
<section id="generic-dice-roller">
<h3><span class="section-number">2.3.1. </span>Generic dice roller<a class="headerlink" href="#generic-dice-roller" title="Permalink to this headline"></a></h3>
<p>We want to be able to do <code class="docutils literal notranslate"><span class="pre">roll(&quot;1d20&quot;)</span></code> and get a random result back from the roll.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span>
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">roll</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">roll_string</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Roll XdY dice, where X is the number of dice </span>
<span class="sd"> and Y the number of sides per die. </span>
<span class="sd"> </span>
<span class="sd"> Args:</span>
<span class="sd"> roll_string (str): A dice string on the form XdY.</span>
<span class="sd"> Returns:</span>
<span class="sd"> int: The result of the roll. </span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># split the XdY input on the &#39;d&#39; one time</span>
<span class="n">number</span><span class="p">,</span> <span class="n">diesize</span> <span class="o">=</span> <span class="n">roll_string</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;d&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="c1"># convert from string to integers</span>
<span class="n">number</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">number</span><span class="p">)</span>
<span class="n">diesize</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">diesize</span><span class="p">)</span>
<span class="c1"># make the roll</span>
<span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">diesize</span><span class="p">)</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">number</span><span class="p">))</span>
</pre></div>
</div>
<aside class="sidebar">
<p>For this tutorial we have opted to not use any contribs, so we create
our own dice roller. But normally you could instead use the <a class="reference internal" href="../../../Contribs/Contrib-Dice.html"><span class="doc std std-doc">dice</span></a> contrib for this.
Well point out possible helpful contribs in sidebars as we proceed.</p>
</aside>
<p>The <code class="docutils literal notranslate"><span class="pre">randint</span></code> standard Python library module produces a random integer<br />
in a specific range. The line</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nb">sum</span><span class="p">(</span><span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">diesize</span><span class="p">)</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">number</span><span class="p">))</span>
</pre></div>
</div>
<p>works like this:</p>
<ul class="simple">
<li><p>For a certain <code class="docutils literal notranslate"><span class="pre">number</span></code> of times …</p></li>
<li><p>… create a random integer between <code class="docutils literal notranslate"><span class="pre">1</span></code> and <code class="docutils literal notranslate"><span class="pre">diesize</span></code></p></li>
<li><p>… and <code class="docutils literal notranslate"><span class="pre">sum</span></code> all those integers together.</p></li>
</ul>
<p>You could write the same thing less compactly like this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">rolls</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">number</span><span class="p">):</span>
<span class="n">random_result</span> <span class="o">=</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">diesize</span><span class="p">)</span>
<span class="n">rolls</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">random_result</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">rolls</span><span class="p">)</span>
</pre></div>
</div>
<aside class="sidebar">
<p>Note that <code class="docutils literal notranslate"><span class="pre">range</span></code> generates a value <code class="docutils literal notranslate"><span class="pre">0...number-1</span></code>. We use <code class="docutils literal notranslate"><span class="pre">_</span></code> in the <code class="docutils literal notranslate"><span class="pre">for</span></code> loop to
indicate we dont really care what this value is - we just want to repeat the loop
a certain amount of times.</p>
</aside>
<p>We dont ever expect end users to call this method; if we did, we would have to validate the inputs
much more - We would have to make sure that <code class="docutils literal notranslate"><span class="pre">number</span></code> or <code class="docutils literal notranslate"><span class="pre">diesize</span></code> are valid inputs and not
crazy big so the loop takes forever!</p>
</section>
<section id="rolling-with-advantage">
<h3><span class="section-number">2.3.2. </span>Rolling with advantage<a class="headerlink" href="#rolling-with-advantage" title="Permalink to this headline"></a></h3>
<p>Now that we have the generic roller, we can start using it to do a more complex roll.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">roll</span><span class="p">(</span><span class="n">roll_string</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">roll_with_advantage_or_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">advantage</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">disadvantage</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">advantage</span> <span class="ow">or</span> <span class="n">disadvantage</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">advantage</span> <span class="ow">and</span> <span class="n">disadvantage</span><span class="p">):</span>
<span class="c1"># normal roll - advantage/disadvantage not set or they cancel </span>
<span class="c1"># each other out </span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">advantage</span><span class="p">:</span>
<span class="c1"># highest of two d20 rolls</span>
<span class="k">return</span> <span class="nb">max</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">),</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># disadvantage - lowest of two d20 rolls </span>
<span class="k">return</span> <span class="nb">min</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">),</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d20&quot;</span><span class="p">))</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">min()</span></code> and <code class="docutils literal notranslate"><span class="pre">max()</span></code> functions are standard Python fare for getting the biggest/smallest
of two arguments.</p>
</section>
<section id="saving-throws">
<h3><span class="section-number">2.3.3. </span>Saving throws<a class="headerlink" href="#saving-throws" title="Permalink to this headline"></a></h3>
<p>We want the saving throw to itself figure out if it succeeded or not. This means it needs to know
the Ability bonus (like STR <code class="docutils literal notranslate"><span class="pre">+1</span></code>). It would be convenient if we could just pass the entity
doing the saving throw to this method, tell it what type of save was needed, and then
have it figure things out:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">result</span><span class="p">,</span> <span class="n">quality</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">saving_throw</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">)</span>
</pre></div>
</div>
<p>The return will be a boolean <code class="docutils literal notranslate"><span class="pre">True/False</span></code> if they pass, as well as a <code class="docutils literal notranslate"><span class="pre">quality</span></code> that tells us if
a perfect fail/success was rolled or not.</p>
<p>To make the saving throw method this clever, we need to think some more about how we want to store our
data on the character.</p>
<p>For our purposes it sounds reasonable that we will be using <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attributes</span></a> for storing
the Ability scores. To make it easy, we will name them the same as the
<a class="reference internal" href="Beginner-Tutorial-Utilities.html#enums"><span class="std std-doc">Enum values</span></a> we set up in the previous lesson. So if we have
an enum <code class="docutils literal notranslate"><span class="pre">STR</span> <span class="pre">=</span> <span class="pre">&quot;strength&quot;</span></code>, we want to store the Ability on the character as an Attribute <code class="docutils literal notranslate"><span class="pre">strength</span></code>.</p>
<p>From the Attribute documentation, we can see that we can use <code class="docutils literal notranslate"><span class="pre">AttributeProperty</span></code> to make it so the
Attribute is available as <code class="docutils literal notranslate"><span class="pre">character.strength</span></code>, and this is what we will do.</p>
<p>So, in short, well create the saving throws method with the assumption that we will be able to do
<code class="docutils literal notranslate"><span class="pre">character.strength</span></code>, <code class="docutils literal notranslate"><span class="pre">character.constitution</span></code>, <code class="docutils literal notranslate"><span class="pre">character.charisma</span></code> etc to get the relevant Abilities.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
<span class="c1"># ...</span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">Ability</span>
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">roll</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="c1"># ...</span>
<span class="k">def</span> <span class="nf">roll_with_advantage_or_disadvantage</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="c1"># ...</span>
<span class="k">def</span> <span class="nf">saving_throw</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">character</span><span class="p">,</span> <span class="n">bonus_type</span><span class="o">=</span><span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="mi">15</span><span class="p">,</span>
<span class="n">advantage</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">disadvantage</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Do a saving throw, trying to beat a target.</span>
<span class="sd"> </span>
<span class="sd"> Args:</span>
<span class="sd"> character (Character): A character (assumed to have Ability bonuses</span>
<span class="sd"> stored on itself as Attributes).</span>
<span class="sd"> bonus_type (Ability): A valid Ability bonus enum.</span>
<span class="sd"> target (int): The target number to beat. Always 15 in Knave.</span>
<span class="sd"> advantage (bool): If character has advantage on this roll.</span>
<span class="sd"> disadvantage (bool): If character has disadvantage on this roll.</span>
<span class="sd"> </span>
<span class="sd"> Returns:</span>
<span class="sd"> tuple: A tuple (bool, Ability), showing if the throw succeeded and </span>
<span class="sd"> the quality is one of None or Ability.CRITICAL_FAILURE/SUCCESS</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># make a roll </span>
<span class="n">dice_roll</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll_with_advantage_or_disadvantage</span><span class="p">(</span><span class="n">advantage</span><span class="p">,</span> <span class="n">disadvantage</span><span class="p">)</span>
<span class="c1"># figure out if we had critical failure/success</span>
<span class="n">quality</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">dice_roll</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">quality</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CRITICAL_FAILURE</span>
<span class="k">elif</span> <span class="n">dice_roll</span> <span class="o">==</span> <span class="mi">20</span><span class="p">:</span>
<span class="n">quality</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CRITICAL_SUCCESS</span>
<span class="c1"># figure out bonus</span>
<span class="n">bonus</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="n">bonus_type</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="c1"># return a tuple (bool, quality)</span>
<span class="k">return</span> <span class="p">(</span><span class="n">dice_roll</span> <span class="o">+</span> <span class="n">bonus</span><span class="p">)</span> <span class="o">&gt;</span> <span class="n">target</span><span class="p">,</span> <span class="n">quality</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">getattr(obj,</span> <span class="pre">attrname,</span> <span class="pre">default)</span></code> function is a very useful Python tool for getting an attribute
off an object and getting a default value if the attribute is not defined.</p>
</section>
<section id="opposed-saving-throw">
<h3><span class="section-number">2.3.4. </span>Opposed saving throw<a class="headerlink" href="#opposed-saving-throw" title="Permalink to this headline"></a></h3>
<p>With the building pieces we already created, this method is simple. Remember that the defense you have
to beat is always the relevant bonus + 10 in <em>Knave</em>. So if the enemy defends with <code class="docutils literal notranslate"><span class="pre">STR</span> <span class="pre">+3</span></code>, you must
roll higher than <code class="docutils literal notranslate"><span class="pre">13</span></code>.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">Ability</span>
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">roll</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">roll_with_advantage_or_disadvantage</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">saving_throw</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">opposed_saving_throw</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attacker</span><span class="p">,</span> <span class="n">defender</span><span class="p">,</span>
<span class="n">attack_type</span><span class="o">=</span><span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">,</span> <span class="n">defense_type</span><span class="o">=</span><span class="n">Ability</span><span class="o">.</span><span class="n">ARMOR</span><span class="p">,</span>
<span class="n">advantage</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">disadvantage</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
<span class="n">defender_defense</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">defender</span><span class="p">,</span> <span class="n">defense_type</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="mi">10</span>
<span class="n">result</span><span class="p">,</span> <span class="n">quality</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">saving_throw</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">bonus_type</span><span class="o">=</span><span class="n">attack_type</span><span class="p">,</span>
<span class="n">target</span><span class="o">=</span><span class="n">defender_defense</span><span class="p">,</span>
<span class="n">advantage</span><span class="o">=</span><span class="n">advantage</span><span class="p">,</span> <span class="n">disadvantage</span><span class="o">=</span><span class="n">disadvantage</span><span class="p">)</span>
<span class="k">return</span> <span class="n">result</span><span class="p">,</span> <span class="n">quality</span>
</pre></div>
</div>
</section>
<section id="morale-check">
<h3><span class="section-number">2.3.5. </span>Morale check<a class="headerlink" href="#morale-check" title="Permalink to this headline"></a></h3>
<p>We will make the assumption that the <code class="docutils literal notranslate"><span class="pre">morale</span></code> value is available from the creature simply as
<code class="docutils literal notranslate"><span class="pre">monster.morale</span></code> - we need to remember to make this so later!</p>
<p>In <em>Knave</em>, a creature have roll with <code class="docutils literal notranslate"><span class="pre">2d6</span></code> equal or under its morale to not flee or surrender
when things go south. The standard morale value is 9.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
<span class="c1"># ...</span>
<span class="k">def</span> <span class="nf">morale_check</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">defender</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;2d6&quot;</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">defender</span><span class="p">,</span> <span class="s2">&quot;morale&quot;</span><span class="p">,</span> <span class="mi">9</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="roll-for-healing">
<h3><span class="section-number">2.3.6. </span>Roll for Healing<a class="headerlink" href="#roll-for-healing" title="Permalink to this headline"></a></h3>
<p>To be able to handle healing, we need to make some more assumptions about how we store
health on game entities. We will need <code class="docutils literal notranslate"><span class="pre">hp_max</span></code> (the total amount of available HP) and <code class="docutils literal notranslate"><span class="pre">hp</span></code>
(the current health value). We again assume these will be available as <code class="docutils literal notranslate"><span class="pre">obj.hp</span></code> and <code class="docutils literal notranslate"><span class="pre">obj.hp_max</span></code>.</p>
<p>According to the rules, after consuming a ration and having a full nights sleep, a character regains
<code class="docutils literal notranslate"><span class="pre">1d8</span> <span class="pre">+</span> <span class="pre">CON</span></code> HP.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">Ability</span>
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">heal_from_rest</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">character</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> A night&#39;s rest retains 1d8 + CON HP </span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">con_bonus</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CON</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">character</span><span class="o">.</span><span class="n">heal</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d8&quot;</span><span class="p">)</span> <span class="o">+</span> <span class="n">con_bonus</span><span class="p">)</span>
</pre></div>
</div>
<p>We make another assumption here - that <code class="docutils literal notranslate"><span class="pre">character.heal()</span></code> is a thing. We tell this function how
much the character should heal, and it will do so, making sure to not heal more than its max
number of HPs</p>
<blockquote>
<div><p>Knowing what is available on the character and what rule rolls we need is a bit of a chicken-and-egg
problem. We will make sure to implement the matching <em>Character</em> class next lesson.</p>
</div></blockquote>
</section>
<section id="rolling-on-a-table">
<h3><span class="section-number">2.3.7. </span>Rolling on a table<a class="headerlink" href="#rolling-on-a-table" title="Permalink to this headline"></a></h3>
<p>We occasionally need to roll on a table - a selection of choices. There are two main table-types
we need to support:</p>
<p>Simply one element per row of the table (same odds to get each result).</p>
<table class="colwidths-auto docutils align-default">
<thead>
<tr class="row-odd"><th class="text-align:center head"><p>Result</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td class="text-align:center"><p>item1</p></td>
</tr>
<tr class="row-odd"><td class="text-align:center"><p>item2</p></td>
</tr>
<tr class="row-even"><td class="text-align:center"><p>item3</p></td>
</tr>
<tr class="row-odd"><td class="text-align:center"><p>item4</p></td>
</tr>
</tbody>
</table>
<p>This we will simply represent as a plain list</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="s2">&quot;item1&quot;</span><span class="p">,</span> <span class="s2">&quot;item2&quot;</span><span class="p">,</span> <span class="s2">&quot;item3&quot;</span><span class="p">,</span> <span class="s2">&quot;item4&quot;</span><span class="p">]</span>
</pre></div>
</div>
<p>Ranges per item (varying odds per result):</p>
<table class="colwidths-auto docutils align-default">
<thead>
<tr class="row-odd"><th class="text-align:center head"><p>Range</p></th>
<th class="text-align:center head"><p>Result</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td class="text-align:center"><p>1-5</p></td>
<td class="text-align:center"><p>item1</p></td>
</tr>
<tr class="row-odd"><td class="text-align:center"><p>6-15</p></td>
<td class="text-align:center"><p>item2</p></td>
</tr>
<tr class="row-even"><td class="text-align:center"><p>16-19</p></td>
<td class="text-align:center"><p>item3</p></td>
</tr>
<tr class="row-odd"><td class="text-align:center"><p>20</p></td>
<td class="text-align:center"><p>item4</p></td>
</tr>
</tbody>
</table>
<p>This we will represent as a list of tuples:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="p">[(</span><span class="s2">&quot;1-5&quot;</span><span class="p">,</span> <span class="s2">&quot;item1&quot;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&quot;6-15&quot;</span><span class="p">,</span> <span class="s2">&quot;item2&quot;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&quot;16-19&quot;</span><span class="p">,</span> <span class="s2">&quot;item4&quot;</span><span class="p">),</span> <span class="p">(</span><span class="s2">&quot;20&quot;</span><span class="p">,</span> <span class="s2">&quot;item5&quot;</span><span class="p">)]</span>
</pre></div>
</div>
<p>We also need to know what die to roll to get a result on the table (it may not always
be obvious, and in some games you could be asked to roll a lower dice to only get
early table results, for example).</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span><span class="p">,</span> <span class="n">choice</span>
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">roll_random_table</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dieroll</span><span class="p">,</span> <span class="n">table_choices</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Args: </span>
<span class="sd"> dieroll (str): A die roll string, like &quot;1d20&quot;.</span>
<span class="sd"> table_choices (iterable): A list of either single elements or </span>
<span class="sd"> of tuples.</span>
<span class="sd"> Returns: </span>
<span class="sd"> Any: A random result from the given list of choices.</span>
<span class="sd"> </span>
<span class="sd"> Raises:</span>
<span class="sd"> RuntimeError: If rolling dice giving results outside the table.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">roll_result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="n">dieroll</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">table_choices</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">(</span><span class="nb">tuple</span><span class="p">,</span> <span class="nb">list</span><span class="p">)):</span>
<span class="c1"># the first element is a tuple/list; treat as on the form [(&quot;1-5&quot;, &quot;item&quot;),...]</span>
<span class="k">for</span> <span class="p">(</span><span class="n">valrange</span><span class="p">,</span> <span class="n">choice</span><span class="p">)</span> <span class="ow">in</span> <span class="n">table_choices</span><span class="p">:</span>
<span class="n">minval</span><span class="p">,</span> <span class="o">*</span><span class="n">maxval</span> <span class="o">=</span> <span class="n">valrange</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;-&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">minval</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">minval</span><span class="p">))</span>
<span class="n">maxval</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">maxval</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">if</span> <span class="n">maxval</span> <span class="k">else</span> <span class="n">minval</span><span class="p">)</span>
<span class="k">if</span> <span class="n">minval</span> <span class="o">&lt;=</span> <span class="n">roll_result</span> <span class="o">&lt;=</span> <span class="n">maxval</span><span class="p">:</span>
<span class="k">return</span> <span class="n">choice</span>
<span class="c1"># if we get here we must have set a dieroll producing a value </span>
<span class="c1"># outside of the table boundaries - raise error</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">&quot;roll_random_table: Invalid die roll&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># a simple regular list</span>
<span class="n">roll_result</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">min</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">table_choices</span><span class="p">),</span> <span class="n">roll_result</span><span class="p">))</span>
<span class="k">return</span> <span class="n">table_choices</span><span class="p">[</span><span class="n">roll_result</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span>
</pre></div>
</div>
<p>Check that you understand what this does.</p>
<p>This may be confusing:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">minval</span><span class="p">,</span> <span class="o">*</span><span class="n">maxval</span> <span class="o">=</span> <span class="n">valrange</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;-&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">minval</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">minval</span><span class="p">))</span>
<span class="n">maxval</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">maxval</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">if</span> <span class="n">maxval</span> <span class="k">else</span> <span class="n">minval</span><span class="p">)</span>
</pre></div>
</div>
<p>If <code class="docutils literal notranslate"><span class="pre">valrange</span></code> is the string <code class="docutils literal notranslate"><span class="pre">1-5</span></code>, then <code class="docutils literal notranslate"><span class="pre">valrange.split(&quot;-&quot;,</span> <span class="pre">1)</span></code> would result in a tuple <code class="docutils literal notranslate"><span class="pre">(&quot;1&quot;,</span> <span class="pre">&quot;5&quot;)</span></code>.
But if the string was in fact just <code class="docutils literal notranslate"><span class="pre">&quot;20&quot;</span></code> (possible for a single entry in an RPG table), this would
lead to an error since it would only split out a single element - and we expected two.</p>
<p>By using <code class="docutils literal notranslate"><span class="pre">*maxval</span></code> (with the <code class="docutils literal notranslate"><span class="pre">*</span></code>), <code class="docutils literal notranslate"><span class="pre">maxval</span></code> is told to expect <em>0 or more</em> elements in a tuple.
So the result for <code class="docutils literal notranslate"><span class="pre">1-5</span></code> will be <code class="docutils literal notranslate"><span class="pre">(&quot;1&quot;,</span> <span class="pre">(&quot;5&quot;,))</span></code> and for <code class="docutils literal notranslate"><span class="pre">20</span></code> it will become <code class="docutils literal notranslate"><span class="pre">(&quot;20&quot;,</span> <span class="pre">())</span></code>. In the line</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">maxval</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">maxval</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">if</span> <span class="n">maxval</span> <span class="k">else</span> <span class="n">minval</span><span class="p">)</span>
</pre></div>
</div>
<p>we check if <code class="docutils literal notranslate"><span class="pre">maxval</span></code> actually has a value <code class="docutils literal notranslate"><span class="pre">(&quot;5&quot;,)</span></code> or if its empty <code class="docutils literal notranslate"><span class="pre">()</span></code>. The result is either
<code class="docutils literal notranslate"><span class="pre">&quot;5&quot;</span></code> or the value of <code class="docutils literal notranslate"><span class="pre">minval</span></code>.</p>
</section>
<section id="roll-for-death">
<h3><span class="section-number">2.3.8. </span>Roll for death<a class="headerlink" href="#roll-for-death" title="Permalink to this headline"></a></h3>
<p>While original Knave suggests hitting 0 HP means insta-death, we will grab the optional “death table” from the “prettified” Knaves optional rules to make it a little less punishing. We also changed the result of <code class="docutils literal notranslate"><span class="pre">2</span></code> to dead since we dont simulate dismemberment in this tutorial:</p>
<table class="colwidths-auto docutils align-default">
<thead>
<tr class="row-odd"><th class="text-align:center head"><p>Roll</p></th>
<th class="text-align:center head"><p>Result</p></th>
<th class="text-align:center head"><p>-1d4 Loss of Ability</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td class="text-align:center"><p>1-2</p></td>
<td class="text-align:center"><p>dead</p></td>
<td class="text-align:center"><p>-</p></td>
</tr>
<tr class="row-odd"><td class="text-align:center"><p>3</p></td>
<td class="text-align:center"><p>weakened</p></td>
<td class="text-align:center"><p>STR</p></td>
</tr>
<tr class="row-even"><td class="text-align:center"><p>4</p></td>
<td class="text-align:center"><p>unsteady</p></td>
<td class="text-align:center"><p>DEX</p></td>
</tr>
<tr class="row-odd"><td class="text-align:center"><p>5</p></td>
<td class="text-align:center"><p>sickly</p></td>
<td class="text-align:center"><p>CON</p></td>
</tr>
<tr class="row-even"><td class="text-align:center"><p>6</p></td>
<td class="text-align:center"><p>addled</p></td>
<td class="text-align:center"><p>INT</p></td>
</tr>
<tr class="row-odd"><td class="text-align:center"><p>7</p></td>
<td class="text-align:center"><p>rattled</p></td>
<td class="text-align:center"><p>WIS</p></td>
</tr>
<tr class="row-even"><td class="text-align:center"><p>8</p></td>
<td class="text-align:center"><p>disfigured</p></td>
<td class="text-align:center"><p>CHA</p></td>
</tr>
</tbody>
</table>
<p>All the non-dead values map to a loss of 1d4 in one of the six Abilities (but you get HP back). We need to map back to this from the above table. One also cannot have less than -10 Ability bonus, if you do, you die too.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
<span class="n">death_table</span> <span class="o">=</span> <span class="p">(</span>
<span class="p">(</span><span class="s2">&quot;1-2&quot;</span><span class="p">,</span> <span class="s2">&quot;dead&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;3&quot;</span><span class="p">,</span> <span class="s2">&quot;strength&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;4&quot;</span><span class="p">,</span> <span class="s2">&quot;dexterity&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;5&quot;</span><span class="p">,</span> <span class="s2">&quot;constitution&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;6&quot;</span><span class="p">,</span> <span class="s2">&quot;intelligence&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;7&quot;</span><span class="p">,</span> <span class="s2">&quot;wisdom&quot;</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;8&quot;</span><span class="p">,</span> <span class="s2">&quot;charisma&quot;</span><span class="p">),</span>
<span class="p">)</span>
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">roll_random_table</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">roll_death</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">character</span><span class="p">):</span>
<span class="n">ability_name</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">&quot;1d8&quot;</span><span class="p">,</span> <span class="n">death_table</span><span class="p">)</span>
<span class="k">if</span> <span class="n">ability_name</span> <span class="o">==</span> <span class="s2">&quot;dead&quot;</span><span class="p">:</span>
<span class="c1"># TODO - kill the character! </span>
<span class="k">pass</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">loss</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d4&quot;</span><span class="p">)</span>
<span class="n">current_ability</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="n">ability_name</span><span class="p">)</span>
<span class="n">current_ability</span> <span class="o">-=</span> <span class="n">loss</span>
<span class="k">if</span> <span class="n">current_ability</span> <span class="o">&lt;</span> <span class="o">-</span><span class="mi">10</span><span class="p">:</span>
<span class="c1"># TODO - kill the character!</span>
<span class="k">pass</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># refresh 1d4 health, but suffer 1d4 ability loss</span>
<span class="bp">self</span><span class="o">.</span><span class="n">heal</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d4&quot;</span><span class="p">))</span>
<span class="nb">setattr</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="n">ability_name</span><span class="p">,</span> <span class="n">current_ability</span><span class="p">)</span>
<span class="n">character</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="s2">&quot;You survive your brush with death, and while you recover &quot;</span>
<span class="sa">f</span><span class="s2">&quot;some health, you permanently lose </span><span class="si">{</span><span class="n">loss</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">ability_name</span><span class="si">}</span><span class="s2"> instead.&quot;</span>
<span class="p">)</span>
<span class="n">dice</span> <span class="o">=</span> <span class="n">EvAdventureRollEngine</span><span class="p">()</span>
</pre></div>
</div>
<p>Here we roll on the death table from the rules to see what happens. We give the character
a message if they survive, to let them know what happened.</p>
<p>We dont yet know what killing the character technically means, so we mark this as <code class="docutils literal notranslate"><span class="pre">TODO</span></code> and return to it in a later lesson. We just know that we need to do <em>something</em> here to kill off the character!</p>
</section>
</section>
<section id="testing">
<h2><span class="section-number">2.4. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Make a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/tests/test_rules.py</span></code></p>
</div></blockquote>
<p>Testing the <code class="docutils literal notranslate"><span class="pre">rules</span></code> module will also showcase some very useful tools when testing.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/tests/test_rules.py </span>
<span class="kn">from</span> <span class="nn">unittest.mock</span> <span class="kn">import</span> <span class="n">patch</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">BaseEvenniaTest</span>
<span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">rules</span>
<span class="k">class</span> <span class="nc">TestEvAdventureRuleEngine</span><span class="p">(</span><span class="n">BaseEvenniaTest</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called before every test method&quot;&quot;&quot;</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">roll_engine</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">EvAdventureRollEngine</span><span class="p">()</span>
<span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evadventure.rules.randint&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_roll</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
<span class="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">4</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">roll_engine</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;1d6&quot;</span><span class="p">),</span> <span class="mi">4</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">roll_engine</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">&quot;2d6&quot;</span><span class="p">),</span> <span class="mi">2</span> <span class="o">*</span> <span class="mi">4</span><span class="p">)</span>
<span class="c1"># test of the other rule methods below ...</span>
</pre></div>
</div>
<p>As before, run the specific test with</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings settings.py .evadventure.tests.test_rules
</pre></div>
</div>
<section id="mocking-and-patching">
<h3><span class="section-number">2.4.1. </span>Mocking and patching<a class="headerlink" href="#mocking-and-patching" title="Permalink to this headline"></a></h3>
<aside class="sidebar">
<p>In <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_rules.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/tests/test_rules.py</span></a>
has a complete example of rule testing.</p>
</aside>
<p>The <code class="docutils literal notranslate"><span class="pre">setUp</span></code> method is a special method of the testing class. It will be run before every
test method. We use <code class="docutils literal notranslate"><span class="pre">super().setUp()</span></code> to make sure the parent class version of this method
always fire. Then we create a fresh <code class="docutils literal notranslate"><span class="pre">EvAdventureRollEngine</span></code> we can test with.</p>
<p>In our test, we import <code class="docutils literal notranslate"><span class="pre">patch</span></code> from the <code class="docutils literal notranslate"><span class="pre">unittest.mock</span></code> library. This is a very useful tool for testing.
Normally the <code class="docutils literal notranslate"><span class="pre">randint</span></code> function we imported in <code class="docutils literal notranslate"><span class="pre">rules</span></code> will return a random value. Thats very hard to test for, since the value will be different every test.</p>
<p>With <code class="docutils literal notranslate"><span class="pre">&#64;patch</span></code> (this is called a <em>decorator</em>), we temporarily replace <code class="docutils literal notranslate"><span class="pre">rules.randint</span></code> with a mock - a dummy entity. This mock is passed into the testing method. We then take this <code class="docutils literal notranslate"><span class="pre">mock_randint</span></code> and set <code class="docutils literal notranslate"><span class="pre">.return_value</span> <span class="pre">=</span> <span class="pre">4</span></code> on it.</p>
<p>Adding <code class="docutils literal notranslate"><span class="pre">return_value</span></code> to the mock means that every time this mock is called, it will return 4. For the duration of the test we can now check with <code class="docutils literal notranslate"><span class="pre">self.assertEqual</span></code> that our <code class="docutils literal notranslate"><span class="pre">roll</span></code> method always returns a result as-if the random result was 4.</p>
<p>There are <a class="reference external" href="https://realpython.com/python-mock-library/">many resources for understanding mock</a>, refer to
them for further help.</p>
<blockquote>
<div><p>The <code class="docutils literal notranslate"><span class="pre">EvAdventureRollEngine</span></code> have many methods to test. We leave this as an extra exercise!</p>
</div></blockquote>
</section>
</section>
<section id="summary">
<h2><span class="section-number">2.5. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline"></a></h2>
<p>This concludes all the core rule mechanics of <em>Knave</em> - the rules used during play. We noticed here that we are going to soon need to establish how our <em>Character</em> actually stores data. So we will address that next.</p>
</section>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-Characters.html" title="3. Player Characters"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Utilities.html" title="1. Code structure and Utilities"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">2. </span>Rules and dice rolling</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,151 @@
<!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>15. In-game Shops &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="16. In-game Commands" href="Beginner-Tutorial-Commands.html" />
<link rel="prev" title="14. Game Quests" href="Beginner-Tutorial-Quests.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Commands.html" title="16. In-game Commands"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Quests.html" title="14. Game Quests"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">15. </span>In-game Shops</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Quests.html"
title="previous chapter"><span class="section-number">14. </span>Game Quests</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Commands.html"
title="next chapter"><span class="section-number">16. </span>In-game Commands</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Shops.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="in-game-shops">
<h1><span class="section-number">15. </span>In-game Shops<a class="headerlink" href="#in-game-shops" title="Permalink to this headline"></a></h1>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>This part of the Beginner tutorial is still being developed.</p>
</div>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-Commands.html" title="16. In-game Commands"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Quests.html" title="14. Game Quests"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">15. </span>In-game Shops</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,434 @@
<!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>1. Code structure and Utilities &#8212; Evennia 2.x 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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="2. Rules and dice rolling" href="Beginner-Tutorial-Rules.html" />
<link rel="prev" title="Part 3: How We Get There (Example Game)" href="Beginner-Tutorial-Part3-Overview.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</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"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rules.html" title="2. Rules and dice rolling"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Part3-Overview.html" title="Part 3: How We Get There (Example Game)"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">1. </span>Code structure and Utilities</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<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>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">1. Code structure and Utilities</a><ul>
<li><a class="reference internal" href="#folder-structure">1.1. Folder structure</a></li>
<li><a class="reference internal" href="#enums">1.2. Enums</a></li>
<li><a class="reference internal" href="#utility-module">1.3. Utility module</a></li>
<li><a class="reference internal" href="#testing">1.4. Testing</a><ul>
<li><a class="reference internal" href="#running-your-test">1.4.1. Running your test</a></li>
</ul>
</li>
<li><a class="reference internal" href="#summary">1.5. Summary</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Part3-Overview.html"
title="previous chapter">Part 3: How We Get There (Example Game)</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Rules.html"
title="next chapter"><span class="section-number">2. </span>Rules and dice rolling</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Utilities.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/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>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="code-structure-and-utilities">
<h1><span class="section-number">1. </span>Code structure and Utilities<a class="headerlink" href="#code-structure-and-utilities" title="Permalink to this headline"></a></h1>
<p>In this lesson we will set up the file structure for <em>EvAdventure</em>. We will make some
utilities that will be useful later. We will also learn how to write <em>tests</em>.</p>
<section id="folder-structure">
<h2><span class="section-number">1.1. </span>Folder structure<a class="headerlink" href="#folder-structure" title="Permalink to this headline"></a></h2>
<aside class="sidebar">
<p class="sidebar-title">This layout is for the tutorial!</p>
<p>We make the <code class="docutils literal notranslate"><span class="pre">evadventure</span></code> folder stand-alone for the sake of the tutorial only. Leaving the code isolated makes it clear what we changed - and for you to grab what you want later. It also makes it easier to refer to matching code in <code class="docutils literal notranslate"><span class="pre">evennia/contrib/tutorials/evadventure</span></code>.</p>
<p>For your own game you are encouraged to modify your game dir in-place instead (such as add to <code class="docutils literal notranslate"><span class="pre">commands/commands.py</span></code> and to modify the <code class="docutils literal notranslate"><span class="pre">typeclasses/</span></code> modules directly). Except for the <code class="docutils literal notranslate"><span class="pre">server/</span></code> folder, you are infact free to structure your game dir code pretty much as you like.</p>
</aside>
<p>Create a new folder under your <code class="docutils literal notranslate"><span class="pre">mygame</span></code> folder, named <code class="docutils literal notranslate"><span class="pre">evadventure</span></code>. Inside it, create
another folder <code class="docutils literal notranslate"><span class="pre">tests/</span></code> and make sure to put empty <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> files in both. This turns both
folders into packages Python understands to import from.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">mygame</span><span class="o">/</span>
<span class="n">commands</span><span class="o">/</span>
<span class="n">evadventure</span><span class="o">/</span> <span class="o">&lt;---</span>
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span> <span class="o">&lt;---</span>
<span class="n">tests</span><span class="o">/</span> <span class="o">&lt;---</span>
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span> <span class="o">&lt;---</span>
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span>
<span class="n">README</span><span class="o">.</span><span class="n">md</span>
<span class="n">server</span><span class="o">/</span>
<span class="n">typeclasses</span><span class="o">/</span>
<span class="n">web</span><span class="o">/</span>
<span class="n">world</span><span class="o">/</span>
</pre></div>
</div>
<p>Importing anything from inside this folder from anywhere else under <code class="docutils literal notranslate"><span class="pre">mygame</span></code> will be done by</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># from anywhere in mygame/</span>
<span class="kn">from</span> <span class="nn">evadventure.yourmodulename</span> <span class="kn">import</span> <span class="n">whatever</span>
</pre></div>
</div>
<p>This is the absolute path` type of import.</p>
<p>Between two modules both in <code class="docutils literal notranslate"><span class="pre">evadventure/</span></code>, you can use a relative import with <code class="docutils literal notranslate"><span class="pre">.</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># from a module inside mygame/evadventure</span>
<span class="kn">from</span> <span class="nn">.yourmodulename</span> <span class="kn">import</span> <span class="n">whatever</span>
</pre></div>
</div>
<p>From e.g. inside <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/tests/</span></code> you can import from one level above using <code class="docutils literal notranslate"><span class="pre">..</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># from mygame/evadventure/tests/ </span>
<span class="kn">from</span> <span class="nn">..yourmodulename</span> <span class="kn">import</span> <span class="n">whatever</span>
</pre></div>
</div>
</section>
<section id="enums">
<h2><span class="section-number">1.2. </span>Enums<a class="headerlink" href="#enums" title="Permalink to this headline"></a></h2>
<aside class="sidebar">
<p>A full example of the enum module is found in
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.enums.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/enums.py</span></a>.</p>
</aside>
<p>Create a new file <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/enums.py</span></code>.</p>
<p>An <a class="reference external" href="https://docs.python.org/3/library/enum.html">enum</a> (enumeration) is a way to establish constants in Python. Best is to show an example:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in a file mygame/evadventure/enums.py</span>
<span class="kn">from</span> <span class="nn">enum</span> <span class="kn">import</span> <span class="n">Enum</span>
<span class="k">class</span> <span class="nc">Ability</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<span class="n">STR</span> <span class="o">=</span> <span class="s2">&quot;strength&quot;</span>
</pre></div>
</div>
<p>You access an enum like this:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># from another module in mygame/evadventure</span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">Ability</span>
<span class="n">Ability</span><span class="o">.</span><span class="n">STR</span> <span class="c1"># the enum itself </span>
<span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="o">.</span><span class="n">value</span> <span class="c1"># this is the string &quot;strength&quot;</span>
</pre></div>
</div>
<p>Having enums is recommended practice. With them set up, it means we can make sure to refer to the same thing every time. Having all enums in one place also means you have a good overview of the constants you are dealing with.</p>
<p>The alternative would be to for example pass around a string <code class="docutils literal notranslate"><span class="pre">&quot;constitution&quot;</span></code>. If you mis-spell this (<code class="docutils literal notranslate"><span class="pre">&quot;consitution&quot;</span></code>), you would not necessarily know it right away - the error would happen later when the string is not recognized. If you make a typo getting <code class="docutils literal notranslate"><span class="pre">Ability.COM</span></code> instead of <code class="docutils literal notranslate"><span class="pre">Ability.CON</span></code>, Python will immediately raise an error since this enum is not recognized.</p>
<p>With enums you can also do nice direct comparisons like <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">ability</span> <span class="pre">is</span> <span class="pre">Ability.WIS:</span> <span class="pre">&lt;do</span> <span class="pre">stuff&gt;</span></code>.</p>
<p>Note that the <code class="docutils literal notranslate"><span class="pre">Ability.STR</span></code> enum does not have the actual <em>value</em> of e.g. your Strength. Its just a fixed label for the Strength ability.</p>
<p>Here is the <code class="docutils literal notranslate"><span class="pre">enum.py</span></code> module needed for <em>Knave</em>. It covers the basic aspects of rule systems we need to track (check out the <em>Knave</em> rules. If you use another rule system youll likely gradually expand on your enums as you figure out what youll need).</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/enums.py</span>
<span class="k">class</span> <span class="nc">Ability</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> The six base ability-bonuses and other </span>
<span class="sd"> abilities</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">STR</span> <span class="o">=</span> <span class="s2">&quot;strength&quot;</span>
<span class="n">DEX</span> <span class="o">=</span> <span class="s2">&quot;dexterity&quot;</span>
<span class="n">CON</span> <span class="o">=</span> <span class="s2">&quot;constitution&quot;</span>
<span class="n">INT</span> <span class="o">=</span> <span class="s2">&quot;intelligence&quot;</span>
<span class="n">WIS</span> <span class="o">=</span> <span class="s2">&quot;wisdom&quot;</span>
<span class="n">CHA</span> <span class="o">=</span> <span class="s2">&quot;charisma&quot;</span>
<span class="n">ARMOR</span> <span class="o">=</span> <span class="s2">&quot;armor&quot;</span>
<span class="n">CRITICAL_FAILURE</span> <span class="o">=</span> <span class="s2">&quot;critical_failure&quot;</span>
<span class="n">CRITICAL_SUCCESS</span> <span class="o">=</span> <span class="s2">&quot;critical_success&quot;</span>
<span class="n">ALLEGIANCE_HOSTILE</span> <span class="o">=</span> <span class="s2">&quot;hostile&quot;</span>
<span class="n">ALLEGIANCE_NEUTRAL</span> <span class="o">=</span> <span class="s2">&quot;neutral&quot;</span>
<span class="n">ALLEGIANCE_FRIENDLY</span> <span class="o">=</span> <span class="s2">&quot;friendly&quot;</span>
<span class="n">ABILITY_REVERSE_MAP</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;str&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">,</span>
<span class="s2">&quot;dex&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">DEX</span><span class="p">,</span>
<span class="s2">&quot;con&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CON</span><span class="p">,</span>
<span class="s2">&quot;int&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">INT</span><span class="p">,</span>
<span class="s2">&quot;wis&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">WIS</span><span class="p">,</span>
<span class="s2">&quot;cha&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CHA</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Here the <code class="docutils literal notranslate"><span class="pre">Ability</span></code> class holds basic properties of a character sheet.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">ABILITY_REVERSE_MAP</span></code> is a convenient map to go the other way - if you in some command were to enter the string cha, we could use this mapping to directly convert your input to the correct <code class="docutils literal notranslate"><span class="pre">Ability</span></code>:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>ability = ABILITY_REVERSE_MAP.get(your_input)
</pre></div>
</div>
</section>
<section id="utility-module">
<h2><span class="section-number">1.3. </span>Utility module<a class="headerlink" href="#utility-module" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/utils.py</span></code></p>
</div></blockquote>
<aside class="sidebar">
<p>An example of the utility module is found in
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.utils.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/utils.py</span></a></p>
</aside>
<p>This is for general functions we may need from all over. In this case we only picture one utility, a function that produces a pretty display of any object we pass to it.</p>
<p>This is an example of the string we want to see:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Chipped</span> <span class="n">Sword</span>
<span class="n">Value</span><span class="p">:</span> <span class="o">~</span><span class="mi">10</span> <span class="n">coins</span> <span class="p">[</span><span class="n">wielded</span> <span class="ow">in</span> <span class="n">Weapon</span> <span class="n">hand</span><span class="p">]</span>
<span class="n">A</span> <span class="n">simple</span> <span class="n">sword</span> <span class="n">used</span> <span class="n">by</span> <span class="n">mercenaries</span> <span class="nb">all</span> <span class="n">over</span>
<span class="n">the</span> <span class="n">world</span><span class="o">.</span>
<span class="n">Slots</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">Used</span> <span class="n">from</span><span class="p">:</span> <span class="n">weapon</span> <span class="n">hand</span>
<span class="n">Quality</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="n">Uses</span><span class="p">:</span> <span class="kc">None</span>
<span class="n">Attacks</span> <span class="n">using</span> <span class="n">strength</span> <span class="n">against</span> <span class="n">armor</span><span class="o">.</span>
<span class="n">Damage</span> <span class="n">roll</span><span class="p">:</span> <span class="mi">1</span><span class="n">d6</span>
</pre></div>
</div>
<p>Heres the start of how the function could look:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/utils.py</span>
<span class="n">_OBJ_STATS</span> <span class="o">=</span> <span class="s2">&quot;&quot;&quot;</span>
<span class="s2">|c</span><span class="si">{key}</span><span class="s2">|n</span>
<span class="s2">Value: ~|y</span><span class="si">{value}</span><span class="s2">|n coins</span><span class="si">{carried}</span>
<span class="si">{desc}</span>
<span class="s2">Slots: |w</span><span class="si">{size}</span><span class="s2">|n, Used from: |w</span><span class="si">{use_slot_name}</span><span class="s2">|n</span>
<span class="s2">Quality: |w</span><span class="si">{quality}</span><span class="s2">|n, Uses: |w</span><span class="si">{uses}</span><span class="s2">|n</span>
<span class="s2">Attacks using |w</span><span class="si">{attack_type_name}</span><span class="s2">|n against |w</span><span class="si">{defense_type_name}</span><span class="s2">|n</span>
<span class="s2">Damage roll: |w</span><span class="si">{damage_roll}</span><span class="s2">|n</span>
<span class="s2">&quot;&quot;&quot;</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">get_obj_stats</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">owner</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Get a string of stats about the object.</span>
<span class="sd"> </span>
<span class="sd"> Args:</span>
<span class="sd"> obj (Object): The object to get stats for.</span>
<span class="sd"> owner (Object): The one currently owning/carrying `obj`, if any. Can be </span>
<span class="sd"> used to show e.g. where they are wielding it.</span>
<span class="sd"> Returns:</span>
<span class="sd"> str: A nice info string to display about the object.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">_OBJ_STATS</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
<span class="n">key</span><span class="o">=</span><span class="n">obj</span><span class="o">.</span><span class="n">key</span><span class="p">,</span>
<span class="n">value</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span>
<span class="n">carried</span><span class="o">=</span><span class="s2">&quot;[Not carried]&quot;</span><span class="p">,</span>
<span class="n">desc</span><span class="o">=</span><span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">desc</span><span class="p">,</span>
<span class="n">size</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
<span class="n">quality</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
<span class="n">uses</span><span class="o">=</span><span class="s2">&quot;infinite&quot;</span><span class="p">,</span>
<span class="n">use_slot_name</span><span class="o">=</span><span class="s2">&quot;backpack&quot;</span><span class="p">,</span>
<span class="n">attack_type_name</span><span class="o">=</span><span class="s2">&quot;strength&quot;</span><span class="p">,</span>
<span class="n">defense_type_name</span><span class="o">=</span><span class="s2">&quot;armor&quot;</span><span class="p">,</span>
<span class="n">damage_roll</span><span class="o">=</span><span class="s2">&quot;1d6&quot;</span>
<span class="p">)</span>
</pre></div>
</div>
<p>Here we set up the string template with place holders for where every piece of info should go. Study this string so you understand what it does. The <code class="docutils literal notranslate"><span class="pre">|c</span></code>, <code class="docutils literal notranslate"><span class="pre">|y</span></code>, <code class="docutils literal notranslate"><span class="pre">|w</span></code> and <code class="docutils literal notranslate"><span class="pre">|n</span></code> markers are <a class="reference internal" href="../../../Concepts/Colors.html"><span class="doc std std-doc">Evennia color markup</span></a> for making the text cyan, yellow, white and neutral-color respectively.</p>
<p>We can guess some things, such that <code class="docutils literal notranslate"><span class="pre">obj.key</span></code> is the name of the object, and that <code class="docutils literal notranslate"><span class="pre">obj.db.desc</span></code> will hold its description (this is how it is in default Evennia).</p>
<p>But so far we have not established how to get any of the other properties like <code class="docutils literal notranslate"><span class="pre">size</span></code> or <code class="docutils literal notranslate"><span class="pre">attack_type</span></code>. So we just set them to dummy values. Well need to get back to this when we have more code in place!</p>
</section>
<section id="testing">
<h2><span class="section-number">1.4. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/tests/test_utils.py</span></code></p>
</div></blockquote>
<p>How do you know if you made a typo in the code above? You could <em>manually</em> test it by reloading your Evennia server and do the following from in-game:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>py from evadventure.utils import get_obj_stats;print(get_obj_stats(self))
</pre></div>
</div>
<p>You should get back a nice string about yourself! If that works, great! But youll need to remember doing that test when you change this code later.</p>
<aside class="sidebar">
<p>In <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_utils.html#evennia-contrib-tutorials-evadventure-tests-test-utils"><span class="std std-ref">evennia/contrib/tutorials/evadventure/tests/test_utils.py</span></a>
is an example of the testing module. To dive deeper into unit testing in Evennia, see the <a class="reference internal" href="../../../Coding/Unit-Testing.html"><span class="doc std std-doc">Unit testing</span></a> documentation.</p>
</aside>
<p>A <em>unit test</em> allows you to set up automated testing of code. Once youve written your test you can run it over and over and make sure later changes to your code didnt break things.</p>
<p>In this particular case, we <em>expect</em> to later have to update the test when <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> becomes more complete and returns more reasonable data.</p>
<p>Evennia comes with extensive functionality to help you test your code. Heres a module for
testing <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code>.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/tests/test_utils.py</span>
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">create</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTest</span>
<span class="kn">from</span> <span class="nn">..import</span> <span class="n">utils</span>
<span class="k">class</span> <span class="nc">TestUtils</span><span class="p">(</span><span class="n">EvenniaTest</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">test_get_obj_stats</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># make a simple object to test with </span>
<span class="n">obj</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;testobj&quot;</span><span class="p">,</span>
<span class="n">attributes</span><span class="o">=</span><span class="p">((</span><span class="s2">&quot;desc&quot;</span><span class="p">,</span> <span class="s2">&quot;A test object&quot;</span><span class="p">),)</span>
<span class="p">)</span>
<span class="c1"># run it through the function </span>
<span class="n">result</span> <span class="o">=</span> <span class="n">utils</span><span class="o">.</span><span class="n">get_obj_stats</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="c1"># check that the result is what we expected</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span>
<span class="n">result</span><span class="p">,</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd">|ctestobj|n</span>
<span class="sd">Value: ~|y10|n coins[not carried]</span>
<span class="sd">A test object</span>
<span class="sd">Slots: |w1|n, Used from: |wbackpack|n</span>
<span class="sd">Quality: |w3|n, Uses: |winfinite|n</span>
<span class="sd">Attacks using |wstrength|n against |warmor|n</span>
<span class="sd">Damage roll: |w1d6|n</span>
<span class="sd">&quot;&quot;&quot;</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="p">)</span>
</pre></div>
</div>
<p>What happens here is that we create a new test-class <code class="docutils literal notranslate"><span class="pre">TestUtils</span></code> that inherits from <code class="docutils literal notranslate"><span class="pre">EvenniaTest</span></code>. This inheritance is what makes this a testing class.</p>
<div class="admonition important">
<p class="admonition-title">Important</p>
<p>Its useful for any game dev to know how to effectively test their code. So well try to include a <em>Testing</em> section at the end of each of the implementation lessons to follow. Writing tests for your code is optional but highly recommended. It can feel a little cumbersome or time-consuming at first … but youll thank yourself later.</p>
</div>
<p>We can have any number of methods on this class. To have a method recognized as one containing code to test, its name <em>must</em> start with <code class="docutils literal notranslate"><span class="pre">test_</span></code>. We have one - <code class="docutils literal notranslate"><span class="pre">test_get_obj_stats</span></code>.</p>
<p>In this method we create a dummy <code class="docutils literal notranslate"><span class="pre">obj</span></code> and gives it a <code class="docutils literal notranslate"><span class="pre">key</span></code> “testobj”. Note how we add the <code class="docutils literal notranslate"><span class="pre">desc</span></code> <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attribute</span></a> directly in the <code class="docutils literal notranslate"><span class="pre">create_object</span></code> call by specifying the attribute as a tuple <code class="docutils literal notranslate"><span class="pre">(name,</span> <span class="pre">value)</span></code>!</p>
<p>We then get the result of passing this dummy-object through <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> we imported earlier.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">assertEqual</span></code> method is available on all testing classes and checks that the <code class="docutils literal notranslate"><span class="pre">result</span></code> is equal to the string we specify. If they are the same, the test <em>passes</em>, otherwise it <em>fails</em> and we need to investigate what went wrong.</p>
<section id="running-your-test">
<h3><span class="section-number">1.4.1. </span>Running your test<a class="headerlink" href="#running-your-test" title="Permalink to this headline"></a></h3>
<p>To run your test you need to stand inside your <code class="docutils literal notranslate"><span class="pre">mygame</span></code> folder and execute the following command:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings settings.py evadventure.tests
</pre></div>
</div>
<p>This will run all your <code class="docutils literal notranslate"><span class="pre">evadventure</span></code> tests (if you had more of them). To only run your utility tests you could do</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings settings.py evadventure.tests.test_utils
</pre></div>
</div>
<p>If all goes well, you should get an <code class="docutils literal notranslate"><span class="pre">OK</span></code> back. Otherwise you need to check the failure, maybe your return string doesnt quite match what you expected.</p>
<blockquote>
<div><p>Hint: The example unit test code above contains a deliberate error in capitalization. See if you can interpret the error and fix it!</p>
</div></blockquote>
</section>
</section>
<section id="summary">
<h2><span class="section-number">1.5. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline"></a></h2>
<p>Its very important to understand how you import code between modules in Python, so if this is still confusing to you, its worth to read up on this more.</p>
<p>That said, many newcomers are confused with how to begin, so by creating the folder structure, some small modules and even making your first unit test, you are off to a great start!</p>
</section>
</section>
</div>
</div>
</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="right" >
<a href="Beginner-Tutorial-Rules.html" title="2. Rules and dice rolling"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Part3-Overview.html" title="Part 3: How We Get There (Example Game)"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-Tos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">1. </span>Code structure and Utilities</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>