Revert "Updated HTML docs."

This reverts commit 51d5840b8b.
This commit is contained in:
Griatch 2022-11-14 22:43:45 +01:00
parent 51d5840b8b
commit e34f258a92
2504 changed files with 820160 additions and 0 deletions

View file

@ -0,0 +1,540 @@
<!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>Player Characters &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<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="In-game Objects and items" href="Beginner-Tutorial-Objects.html" />
<link rel="prev" title="Rules and dice rolling" href="Beginner-Tutorial-Rules.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Objects.html" title="In-game Objects and items"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rules.html" title="Rules and dice rolling"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Player Characters</a></li>
</ul>
<div class="develop">develop branch</div>
</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="#">Player Characters</a><ul>
<li><a class="reference internal" href="#inheritance-structure">Inheritance structure</a></li>
<li><a class="reference internal" href="#living-mixin-class">Living mixin class</a></li>
<li><a class="reference internal" href="#character-class">Character class</a><ul>
<li><a class="reference internal" href="#funcparser-inlines">Funcparser inlines</a></li>
<li><a class="reference internal" href="#backtracking">Backtracking</a></li>
</ul>
</li>
<li><a class="reference internal" href="#connecting-the-character-with-evennia">Connecting the Character with Evennia</a></li>
<li><a class="reference internal" href="#unit-testing">Unit Testing</a></li>
<li><a class="reference internal" href="#about-races-and-classes">About races and classes</a></li>
<li><a class="reference internal" href="#summary">Summary</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Rules.html"
title="previous chapter">Rules and dice rolling</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Objects.html"
title="next chapter">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">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Characters.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="player-characters">
<h1>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>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>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="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="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="s2">&quot;You heal for </span><span class="si">{healed}</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="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_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="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="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="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="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="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>
</section>
<section id="character-class">
<h2>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="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="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="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>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>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>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_CLASS</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>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>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>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="In-game Objects and items"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rules.html" title="Rules and dice rolling"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Player Characters</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, 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,808 @@
<!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>Character Generation &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<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="In-game Rooms" href="Beginner-Tutorial-Rooms.html" />
<link rel="prev" title="Handling Equipment" href="Beginner-Tutorial-Equipment.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rooms.html" title="In-game Rooms"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Equipment.html" title="Handling Equipment"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Character Generation</a></li>
</ul>
<div class="develop">develop branch</div>
</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="#">Character Generation</a><ul>
<li><a class="reference internal" href="#how-it-will-work">How it will work</a></li>
<li><a class="reference internal" href="#random-tables">Random tables</a></li>
<li><a class="reference internal" href="#storing-state-of-the-menu">Storing state of the menu</a><ul>
<li><a class="reference internal" href="#showing-the-sheet">Showing the sheet</a></li>
<li><a class="reference internal" href="#apply-character">Apply character</a></li>
</ul>
</li>
<li><a class="reference internal" href="#initializing-evmenu">Initializing EvMenu</a></li>
<li><a class="reference internal" href="#main-node-choosing-what-to-do">Main Node: Choosing what to do</a></li>
<li><a class="reference internal" href="#node-changing-your-name">Node: Changing your name</a></li>
<li><a class="reference internal" href="#node-swapping-abilities-around">Node: Swapping Abilities around</a></li>
<li><a class="reference internal" href="#node-creating-the-character">Node: Creating the Character</a></li>
<li><a class="reference internal" href="#tying-the-nodes-together">Tying the nodes together</a></li>
<li><a class="reference internal" href="#conclusions">Conclusions</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Equipment.html"
title="previous chapter">Handling Equipment</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Rooms.html"
title="next chapter">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">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Chargen.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="character-generation">
<h1>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>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>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>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>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"></span>
<span class="s2">STR +</span><span class="si">{strength}</span><span class="s2"></span>
<span class="s2">DEX +</span><span class="si">{dexterity}</span><span class="s2"></span>
<span class="s2">CON +</span><span class="si">{constitution}</span><span class="s2"></span>
<span class="s2">INT +</span><span class="si">{intelligence}</span><span class="s2"></span>
<span class="s2">WIS +</span><span class="si">{wisdom}</span><span class="s2"></span>
<span class="s2">CHA +</span><span class="si">{charisma}</span><span class="s2"></span>
<span class="si">{description}</span><span class="s2"></span>
<span class="s2"> </span>
<span class="s2">Your belongings:</span>
<span class="si">{equipment}</span><span class="s2"></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>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>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="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>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>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="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="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>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="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="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"></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"></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"></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"></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"></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"></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>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="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">db</span><span class="o">.</span><span class="n">_playable_characters</span> <span class="o">=</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>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">tmp_character</span><span class="o">.</span><span class="n">generate</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>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="In-game Rooms"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Equipment.html" title="Handling Equipment"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Character Generation</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, 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,141 @@
<!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>In-game Commands &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<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-Intro.html" />
<link rel="prev" title="Dynamically generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="../Part4/Beginner-Tutorial-Part4-Intro.html" title="Part 4: Using what we created"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Dungeon.html" title="Dynamically generated Dungeon"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">In-game Commands</a></li>
</ul>
<div class="develop">develop branch</div>
</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">Dynamically generated Dungeon</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="../Part4/Beginner-Tutorial-Part4-Intro.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">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Commands.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="in-game-commands">
<h1>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-Intro.html" title="Part 4: Using what we created"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Dungeon.html" title="Dynamically generated Dungeon"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">In-game Commands</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, 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,141 @@
<!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>Dynamically generated Dungeon &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<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="In-game Commands" href="Beginner-Tutorial-Commands.html" />
<link rel="prev" title="In-game Shops" href="Beginner-Tutorial-Shops.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Commands.html" title="In-game Commands"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Shops.html" title="In-game Shops"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Dynamically generated Dungeon</a></li>
</ul>
<div class="develop">develop branch</div>
</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">In-game Shops</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Commands.html"
title="next chapter">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-Dungeon.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Dungeon.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="dynamically-generated-dungeon">
<h1>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-Commands.html" title="In-game Commands"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Shops.html" title="In-game Shops"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Dynamically generated Dungeon</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, 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,708 @@
<!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>Handling Equipment &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<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="Character Generation" href="Beginner-Tutorial-Chargen.html" />
<link rel="prev" title="In-game Objects and items" href="Beginner-Tutorial-Objects.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Chargen.html" title="Character Generation"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Objects.html" title="In-game Objects and items"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Handling Equipment</a></li>
</ul>
<div class="develop">develop branch</div>
</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="#">Handling Equipment</a><ul>
<li><a class="reference internal" href="#equipmenthandler-that-saves">EquipmentHandler that saves</a></li>
<li><a class="reference internal" href="#connecting-the-equipmenthandler">Connecting the EquipmentHandler</a></li>
<li><a class="reference internal" href="#expanding-the-equipmenthandler">Expanding the Equipmenthandler</a></li>
<li><a class="reference internal" href="#validate-slot-usage"><code class="docutils literal notranslate"><span class="pre">.validate_slot_usage</span></code></a><ul>
<li><a class="reference internal" href="#max-slots"><code class="docutils literal notranslate"><span class="pre">.max_slots</span></code></a></li>
<li><a class="reference internal" href="#count-slots"><code class="docutils literal notranslate"><span class="pre">.count_slots</span></code></a></li>
<li><a class="reference internal" href="#validating-slots">Validating slots</a></li>
</ul>
</li>
<li><a class="reference internal" href="#add-and-remove"><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">Moving things around</a></li>
<li><a class="reference internal" href="#get-everything">Get everything</a></li>
<li><a class="reference internal" href="#weapon-and-armor">Weapon and armor</a></li>
<li><a class="reference internal" href="#extra-credits">Extra credits</a></li>
<li><a class="reference internal" href="#unit-testing">Unit Testing</a></li>
<li><a class="reference internal" href="#summary">Summary</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Objects.html"
title="previous chapter">In-game Objects and items</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Chargen.html"
title="next chapter">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">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Equipment.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="handling-equipment">
<h1>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>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="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="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>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="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="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="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>
</section>
<section id="expanding-the-equipmenthandler">
<h2>Expanding the Equipmenthandler<a class="headerlink" href="#expanding-the-equipmenthandler" title="Permalink to this headline"></a></h2>
</section>
<section id="validate-slot-usage">
<h2><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="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="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="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="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><span class="p">:</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><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><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>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><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="sd">&quot;&quot;&quot;</span>
<span class="sd"> Put something in the backpack.</span>
<span class="sd"> &quot;&quot;&quot;</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">slot</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Remove contents of a particular slot, for</span>
<span class="sd"> example `equipment.remove(WieldLocation.SHIELD_HAND)`</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="n">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">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="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">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="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>Both of these should be straight forward to follow. 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">.delete</span></code>, we allow emptying by <code class="docutils literal notranslate"><span class="pre">WieldLocation</span></code> - we figure out what slot it is and return
the item within (if any). 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.</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>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="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="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>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="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>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">.objects</span> <span class="kn">import</span> <span class="n">WeaponEmptyHand</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="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="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">WeaponEmptyHand</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 fake weapon <code class="docutils literal notranslate"><span class="pre">WeaponEmptyHand</span></code> which is just a dummy
object that represents your bare hands with damage and all.
(created in <a class="reference internal" href="Beginner-Tutorial-Objects.html#your-bare-hands"><span class="std std-doc">The Object tutorial</span></a> earlier).</p>
</section>
<section id="extra-credits">
<h2>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>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">EvAdventureRoom</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">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>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="Character Generation"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Objects.html" title="In-game Objects and items"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Handling Equipment</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, 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,141 @@
<!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>Non-Player-Characters (NPCs) &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<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="Game Quests" href="Beginner-Tutorial-Quests.html" />
<link rel="prev" title="In-game Rooms" href="Beginner-Tutorial-Rooms.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Quests.html" title="Game Quests"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rooms.html" title="In-game Rooms"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Non-Player-Characters (NPCs)</a></li>
</ul>
<div class="develop">develop branch</div>
</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-Rooms.html"
title="previous chapter">In-game Rooms</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Quests.html"
title="next chapter">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-NPCs.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-NPCs.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="non-player-characters-npcs">
<h1>Non-Player-Characters (NPCs)<a class="headerlink" href="#non-player-characters-npcs" 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="Game Quests"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rooms.html" title="In-game Rooms"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Non-Player-Characters (NPCs)</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, 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,484 @@
<!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>In-game Objects and items &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<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="Handling Equipment" href="Beginner-Tutorial-Equipment.html" />
<link rel="prev" title="Player Characters" href="Beginner-Tutorial-Characters.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Equipment.html" title="Handling Equipment"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Characters.html" title="Player Characters"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">In-game Objects and items</a></li>
</ul>
<div class="develop">develop branch</div>
</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="#">In-game Objects and items</a><ul>
<li><a class="reference internal" href="#new-enums">New Enums</a></li>
<li><a class="reference internal" href="#the-base-object">The base object</a><ul>
<li><a class="reference internal" href="#using-attributes-or-not">Using Attributes or not</a></li>
<li><a class="reference internal" href="#creating-tags-in-at-object-creation">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">Other object types</a></li>
<li><a class="reference internal" href="#consumables">Consumables</a></li>
<li><a class="reference internal" href="#weapons">Weapons</a></li>
<li><a class="reference internal" href="#magic">Magic</a></li>
<li><a class="reference internal" href="#armor">Armor</a></li>
<li><a class="reference internal" href="#your-bare-hands">Your Bare hands</a></li>
<li><a class="reference internal" href="#testing-and-extra-credits">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">Player Characters</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Equipment.html"
title="next chapter">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">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Objects.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</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>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>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>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="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="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="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_help</span><span class="p">(</span><span class="bp">self</span><span class="p">):</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>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>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>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="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="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>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="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="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="sd">&quot;&quot;&quot;Called before using. If returning False, abort use.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">uses</span> <span class="o">&gt;</span> <span class="mi">0</span>
<span class="k">def</span> <span class="nf">at_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="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="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>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>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.</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="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">AttibuteProperty</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">AttibuteProperty</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">AttibuteProperty</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>
</pre></div>
</div>
<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.</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>
</section>
<section id="magic">
<h2>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="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">AttibuteProperty</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">AttibuteProperty</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">AttibuteProperty</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="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="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">at_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>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>Your Bare hands<a class="headerlink" href="#your-bare-hands" title="Permalink to this headline"></a></h2>
<p>This is a dummy object that is not stored in the database. 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="k">class</span> <span class="nc">WeaponEmptyHand</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">key</span> <span class="o">=</span> <span class="s2">&quot;Empty Fists&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">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="mi">100000</span> <span class="c1"># let&#39;s assume fists are always available ...</span>
<span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="s2">&quot;&lt;WeaponEmptyHand&gt;&quot;</span>
</pre></div>
</div>
</section>
<section id="testing-and-extra-credits">
<h2>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="Handling Equipment"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Characters.html" title="Player Characters"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">In-game Objects and items</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, 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,309 @@
<!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 &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<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="Code structure and Utilities" href="Beginner-Tutorial-Utilities.html" />
<link rel="prev" title="Planning our tutorial game" href="../Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Utilities.html" title="Code structure and Utilities"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="../Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html" title="Planning our tutorial game"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" accesskey="U">Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Part 3: How we get there</a></li>
</ul>
<div class="develop">develop branch</div>
</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</a><ul>
<li><a class="reference internal" href="#lessons">Lessons</a></li>
<li><a class="reference internal" href="#table-of-contents">Table of Contents</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">Planning our tutorial game</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Utilities.html"
title="next chapter">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-Intro.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Part3-Intro.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</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">
<h1>Part 3: How we get there<a class="headerlink" href="#part-3-how-we-get-there" 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-Intro.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-Intro.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-Intro.html"><span class="doc std std-doc">What we want</span></a>
<br>Planning our tutorial game and what to think about when planning your own in the future.</p></li>
<li><p><strong>Part 3: <a class="reference internal" href="#"><span class="doc std std-doc">How we get there</span></a></strong>
<br>Getting down to the meat of extending Evennia to make our game</p></li>
<li><p>Part 4: <a class="reference internal" href="../Part4/Beginner-Tutorial-Part4-Intro.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-Intro.html"><span class="doc std std-doc">Showing the world</span></a>
<br>Taking our new game online and let 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>This is a big part. Youll be seeing a lot of code and there are plenty of lessons to go through.
Take your time!</p>
<p>If you followed the previous parts of this tutorial 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!</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.</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">Code structure and Utilities</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Rules.html">Rules and dice rolling</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Characters.html">Player Characters</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Objects.html">In-game Objects and items</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Equipment.html">Handling Equipment</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Chargen.html">Character Generation</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Rooms.html">In-game Rooms</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-NPCs.html">Non-Player-Characters (NPCs)</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Quests.html">Game Quests</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Shops.html">In-game Shops</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Dungeon.html">Dynamically generated Dungeon</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Commands.html">In-game Commands</a></li>
</ul>
</div>
</section>
<section id="table-of-contents">
<h2>Table of Contents<a class="headerlink" href="#table-of-contents" 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">Code structure and Utilities</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#folder-structure">Folder structure</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#enums">Enums</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#utility-module">Utility module</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#testing">Testing</a><ul>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#running-your-test">Running your test</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#summary">Summary</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Rules.html">Rules and dice rolling</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#summary-of-knave-rules">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">Making a rule module</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#rolling-dice">Rolling dice</a><ul>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Rules.html#generic-dice-roller">Generic dice roller</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Rules.html#rolling-with-advantage">Rolling with advantage</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Rules.html#saving-throws">Saving throws</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Rules.html#opposed-saving-throw">Opposed saving throw</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Rules.html#morale-check">Morale check</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Rules.html#roll-for-healing">Roll for Healing</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Rules.html#rolling-on-a-table">Rolling on a table</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Rules.html#roll-for-death">Roll for death</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#testing">Testing</a><ul>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Rules.html#mocking-and-patching">Mocking and patching</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#summary">Summary</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Characters.html">Player Characters</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#inheritance-structure">Inheritance structure</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#living-mixin-class">Living mixin class</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#character-class">Character class</a><ul>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Characters.html#funcparser-inlines">Funcparser inlines</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Characters.html#backtracking">Backtracking</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#connecting-the-character-with-evennia">Connecting the Character with Evennia</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#unit-testing">Unit Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#about-races-and-classes">About races and classes</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#summary">Summary</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Objects.html">In-game Objects and items</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#new-enums">New Enums</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#the-base-object">The base object</a><ul>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Objects.html#using-attributes-or-not">Using Attributes or not</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Objects.html#creating-tags-in-at-object-creation">Creating tags in <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code></a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#other-object-types">Other object types</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#consumables">Consumables</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#weapons">Weapons</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#magic">Magic</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#armor">Armor</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#your-bare-hands">Your Bare hands</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#testing-and-extra-credits">Testing and Extra credits</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Equipment.html">Handling Equipment</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#equipmenthandler-that-saves">EquipmentHandler that saves</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#connecting-the-equipmenthandler">Connecting the EquipmentHandler</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#expanding-the-equipmenthandler">Expanding the Equipmenthandler</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#validate-slot-usage"><code class="docutils literal notranslate"><span class="pre">.validate_slot_usage</span></code></a><ul>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#max-slots"><code class="docutils literal notranslate"><span class="pre">.max_slots</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#count-slots"><code class="docutils literal notranslate"><span class="pre">.count_slots</span></code></a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#validating-slots">Validating slots</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#add-and-remove"><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">Moving things around</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#get-everything">Get everything</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#weapon-and-armor">Weapon and armor</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#extra-credits">Extra credits</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#unit-testing">Unit Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#summary">Summary</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Chargen.html">Character Generation</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#how-it-will-work">How it will work</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#random-tables">Random tables</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#storing-state-of-the-menu">Storing state of the menu</a><ul>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#showing-the-sheet">Showing the sheet</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#apply-character">Apply character</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#initializing-evmenu">Initializing EvMenu</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#main-node-choosing-what-to-do">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">Node: Changing your name</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#node-swapping-abilities-around">Node: Swapping Abilities around</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#node-creating-the-character">Node: Creating the Character</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#tying-the-nodes-together">Tying the nodes together</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#conclusions">Conclusions</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Rooms.html">In-game Rooms</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-NPCs.html">Non-Player-Characters (NPCs)</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Quests.html">Game Quests</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Shops.html">In-game Shops</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Dungeon.html">Dynamically generated Dungeon</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Commands.html">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="Code structure and Utilities"
>next</a> |</li>
<li class="right" >
<a href="../Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html" title="Planning our tutorial game"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Part 3: How we get there</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, 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,141 @@
<!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>Game Quests &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<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="In-game Shops" href="Beginner-Tutorial-Shops.html" />
<link rel="prev" title="Non-Player-Characters (NPCs)" href="Beginner-Tutorial-NPCs.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Shops.html" title="In-game Shops"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-NPCs.html" title="Non-Player-Characters (NPCs)"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Game Quests</a></li>
</ul>
<div class="develop">develop branch</div>
</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-NPCs.html"
title="previous chapter">Non-Player-Characters (NPCs)</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Shops.html"
title="next chapter">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">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Quests.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="game-quests">
<h1>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="In-game Shops"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-NPCs.html" title="Non-Player-Characters (NPCs)"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Game Quests</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, 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,141 @@
<!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>In-game Rooms &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<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="Non-Player-Characters (NPCs)" href="Beginner-Tutorial-NPCs.html" />
<link rel="prev" title="Character Generation" href="Beginner-Tutorial-Chargen.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-NPCs.html" title="Non-Player-Characters (NPCs)"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Chargen.html" title="Character Generation"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">In-game Rooms</a></li>
</ul>
<div class="develop">develop branch</div>
</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-Chargen.html"
title="previous chapter">Character Generation</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-NPCs.html"
title="next chapter">Non-Player-Characters (NPCs)</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">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Rooms.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="in-game-rooms">
<h1>In-game Rooms<a class="headerlink" href="#in-game-rooms" 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-NPCs.html" title="Non-Player-Characters (NPCs)"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Chargen.html" title="Character Generation"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">In-game Rooms</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, 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,793 @@
<!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>Rules and dice rolling &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<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="Player Characters" href="Beginner-Tutorial-Characters.html" />
<link rel="prev" title="Code structure and Utilities" href="Beginner-Tutorial-Utilities.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Characters.html" title="Player Characters"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Utilities.html" title="Code structure and Utilities"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Rules and dice rolling</a></li>
</ul>
<div class="develop">develop branch</div>
</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="#">Rules and dice rolling</a><ul>
<li><a class="reference internal" href="#summary-of-knave-rules">Summary of <em>Knave</em> rules</a></li>
<li><a class="reference internal" href="#making-a-rule-module">Making a rule module</a></li>
<li><a class="reference internal" href="#rolling-dice">Rolling dice</a><ul>
<li><a class="reference internal" href="#generic-dice-roller">Generic dice roller</a></li>
<li><a class="reference internal" href="#rolling-with-advantage">Rolling with advantage</a></li>
<li><a class="reference internal" href="#saving-throws">Saving throws</a></li>
<li><a class="reference internal" href="#opposed-saving-throw">Opposed saving throw</a></li>
<li><a class="reference internal" href="#morale-check">Morale check</a></li>
<li><a class="reference internal" href="#roll-for-healing">Roll for Healing</a></li>
<li><a class="reference internal" href="#rolling-on-a-table">Rolling on a table</a></li>
<li><a class="reference internal" href="#roll-for-death">Roll for death</a></li>
</ul>
</li>
<li><a class="reference internal" href="#testing">Testing</a><ul>
<li><a class="reference internal" href="#mocking-and-patching">Mocking and patching</a></li>
</ul>
</li>
<li><a class="reference internal" href="#summary">Summary</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Utilities.html"
title="previous chapter">Code structure and Utilities</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Characters.html"
title="next chapter">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">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Rules.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</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>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>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>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>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>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="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>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-default 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>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="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>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">advantave</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>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>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="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>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="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>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>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="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>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>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="Player Characters"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Utilities.html" title="Code structure and Utilities"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Rules and dice rolling</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, 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,141 @@
<!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>In-game Shops &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<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="Dynamically generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
<link rel="prev" title="Game Quests" href="Beginner-Tutorial-Quests.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Dungeon.html" title="Dynamically generated Dungeon"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Quests.html" title="Game Quests"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">In-game Shops</a></li>
</ul>
<div class="develop">develop branch</div>
</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">Game Quests</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Dungeon.html"
title="next chapter">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-Shops.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Shops.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="in-game-shops">
<h1>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-Dungeon.html" title="Dynamically generated Dungeon"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Quests.html" title="Game Quests"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">In-game Shops</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, 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,435 @@
<!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>Code structure and Utilities &#8212; Evennia 1.0-dev documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<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="Rules and dice rolling" href="Beginner-Tutorial-Rules.html" />
<link rel="prev" title="Part 3: How we get there" href="Beginner-Tutorial-Part3-Intro.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rules.html" title="Rules and dice rolling"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Part3-Intro.html" title="Part 3: How we get there"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Code structure and Utilities</a></li>
</ul>
<div class="develop">develop branch</div>
</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="#">Code structure and Utilities</a><ul>
<li><a class="reference internal" href="#folder-structure">Folder structure</a></li>
<li><a class="reference internal" href="#enums">Enums</a></li>
<li><a class="reference internal" href="#utility-module">Utility module</a></li>
<li><a class="reference internal" href="#testing">Testing</a><ul>
<li><a class="reference internal" href="#running-your-test">Running your test</a></li>
</ul>
</li>
<li><a class="reference internal" href="#summary">Summary</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Part3-Intro.html"
title="previous chapter">Part 3: How we get there</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Rules.html"
title="next chapter">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">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Utilities.html">1.0-dev (develop branch)</a></li>
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</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>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>Folder structure<a class="headerlink" href="#folder-structure" title="Permalink to this headline"></a></h2>
<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>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="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>
</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>
</section>
<section id="utility-module">
<h2>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="s2"></span>
<span class="si">{desc}</span><span class="s2"></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: |wuses|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="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="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="n">defense_type_name</span><span class="o">=</span><span class="s2">&quot;armor&quot;</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>Testing<a class="headerlink" href="#testing" title="Permalink to this headline"></a></h2>
<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 at first, but youll thank yourself later.</p>
</div>
<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 [evennia/contrib/tutorials/evadventure/tests/test_utils.py](evennia.contrib.tutorials.
evadventure.tests.test_utils)
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">BaseEvenniaTest</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">BaseEvenniaTest</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="sd">&quot;&quot;&quot; </span>
<span class="sd">|ctestobj|n</span>
<span class="sd">Value: ~|y10|n coins</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">BaseEvenniaTest</span></code>.
This inheritance is what makes this a testing class.</p>
<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>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>
</section>
</section>
<section id="summary">
<h2>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="Rules and dice rolling"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Part3-Intro.html" title="Part 3: How we get there"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Code structure and Utilities</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>