<pclass="last">You are reading an old version of the Evennia documentation. <ahref="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
<h1><spanclass="section-number">13. </span>Building a chair you can sit on<aclass="headerlink"href="#building-a-chair-you-can-sit-on"title="Permalink to this headline">¶</a></h1>
<p>In this lesson we will make use of what we have learned to create a new game object: a chair you can sit on.</p>
<p>Our goals are:</p>
<ulclass="simple">
<li><p>We want a new ‘sittable’ object, a <codeclass="docutils literal notranslate"><spanclass="pre">Chair</span></code> in particular.</p></li>
<li><p>We want to be able to use a command to sit in the chair.</p></li>
<li><p>Once we are sitting in the chair it should affect us somehow. To demonstrate this store
the current chair in an attribute <codeclass="docutils literal notranslate"><spanclass="pre">is_sitting</span></code>. Other systems could check this to affect us in different ways.</p></li>
<li><p>A character should be able to stand up and move away from the chair.</p></li>
<li><p>When you sit down you should not be able to walk to another room without first standing up.</p></li>
<h2><spanclass="section-number">13.1. </span>Make us not able to move while sitting<aclass="headerlink"href="#make-us-not-able-to-move-while-sitting"title="Permalink to this headline">¶</a></h2>
<p>When you are sitting in a chair you can’t just walk off without first standing up.
This requires a change to our Character typeclass. Open <codeclass="docutils literal notranslate"><spanclass="pre">mygame/typeclasses/characters.py</span></code>:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in mygame/typeclasses/characters.py</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You need to stand up first."</span><spanclass="p">)</span>
<p>When moving somewhere, <aclass="reference internal"href="../../../api/evennia.objects.objects.html#evennia.objects.objects.DefaultObject.move_to"title="evennia.objects.objects.DefaultObject.move_to"><spanclass="xref myst py py-meth">character.move_to</span></a> is called. This in turn
will call <codeclass="docutils literal notranslate"><spanclass="pre">character.at_pre_move</span></code>. If this returns <codeclass="docutils literal notranslate"><spanclass="pre">False</span></code>, the move is aborted.</p>
<p>Here we look for an Attribute <codeclass="docutils literal notranslate"><spanclass="pre">is_sitting</span></code> (which we will assign below) to determine if we are stuck on the chair or not.</p>
</section>
<sectionid="making-the-chair-itself">
<h2><spanclass="section-number">13.2. </span>Making the Chair itself<aclass="headerlink"href="#making-the-chair-itself"title="Permalink to this headline">¶</a></h2>
<p>Next we need the Chair itself, or rather a whole family of “things you can sit on” that we will call <em>sittables</em>. We can’t just use a default Object since we want a sittable to contain some custom code. We need a new, custom Typeclass. Create a new module <codeclass="docutils literal notranslate"><spanclass="pre">mygame/typeclasses/sittables.py</span></code> with the following content:</p>
<spanclass="n">sitter</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"You are already sitting on </span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="si">}</span><spanclass="s2">."</span><spanclass="p">)</span>
<spanclass="k">else</span><spanclass="p">:</span>
<spanclass="n">sitter</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"You can't sit on </span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="si">}</span><spanclass="s2">"</span>
<spanclass="sa">f</span><spanclass="s2">"- </span><spanclass="si">{</span><spanclass="n">current</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="si">}</span><spanclass="s2"> is already sitting there!"</span><spanclass="p">)</span>
</span><spanclass="hll"><spanclass="n">sitter</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"You sit on </span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="si">}</span><spanclass="s2">"</span><spanclass="p">)</span>
</span></pre></div></td></tr></table></div>
</div>
<p>This handles the logic of someone sitting down on the chair.</p>
<ulclass="simple">
<li><p><strong>Line 3</strong>: We inherit from the empty <codeclass="docutils literal notranslate"><spanclass="pre">Object</span></code> class in <codeclass="docutils literal notranslate"><spanclass="pre">mygame/typeclasses/objects.py</span></code>. This means we can theoretically modify that in the future and have those changes affect sittables too.</p></li>
<li><p><strong>Line 7</strong>: The <codeclass="docutils literal notranslate"><spanclass="pre">do_sit</span></code> method expects to be called with the argument <codeclass="docutils literal notranslate"><spanclass="pre">sitter</span></code>, which is to be an <codeclass="docutils literal notranslate"><spanclass="pre">Object</span></code> (most likely a <codeclass="docutils literal notranslate"><spanclass="pre">Character</span></code>). This is the one wanting to sit down.</p></li>
<li><p><strong>Line 15</strong>: Note that, if the <aclass="reference internal"href="../../../Components/Attributes.html"><spanclass="doc std std-doc">Attribute</span></a><codeclass="docutils literal notranslate"><spanclass="pre">sitter</span></code> is not defined on the chair (because this is the first time someone sits in it), this will simply return <codeclass="docutils literal notranslate"><spanclass="pre">None</span></code>, which is fine.</p></li>
<li><p><strong>Lines 16-22</strong> We check if someone is already sitting on the chair and returns appropriate error messages depending on if it’s you or someone else. We use <codeclass="docutils literal notranslate"><spanclass="pre">return</span></code> to abort the sit-action.</p></li>
<li><p><strong>Line 23</strong>: If we get to this point, <codeclass="docutils literal notranslate"><spanclass="pre">sitter</span></code> gets to, well, sit down. We store them in the <codeclass="docutils literal notranslate"><spanclass="pre">sitter</span></code> Attribute on the chair.</p></li>
<li><p><strong>Line 24</strong>: <codeclass="docutils literal notranslate"><spanclass="pre">self.obj</span></code> is the chair this command is attachd to. We store that in the <codeclass="docutils literal notranslate"><spanclass="pre">is_sitting</span></code> Attribute on the <codeclass="docutils literal notranslate"><spanclass="pre">sitter</span></code> itself.</p></li>
<li><p><strong>Line 25</strong>: Finally we tell the sitter that they could sit down.</p></li>
<spanclass="normal">17</span></pre></div></td><tdclass="code"><div><pre><span></span><spanclass="c1"># add this right after the `do_sit method` in the same class </span>
</span><spanclass="n">stander</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"You are not sitting on </span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="si">}</span><spanclass="s2">."</span><spanclass="p">)</span>
</span><spanclass="hll"><spanclass="n">stander</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"You stand up from </span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="si">}</span><spanclass="s2">."</span><spanclass="p">)</span>
</span></pre></div></td></tr></table></div>
</div>
<p>This is the inverse of sitting down; we need to do some cleanup.</p>
<ulclass="simple">
<li><p><strong>Line 12</strong>: If we are not sitting on the chair, it makes no sense to stand up from it.</p></li>
<li><p><strong>Line 15</strong>: If we get here, we could stand up. We make sure to un-set the <codeclass="docutils literal notranslate"><spanclass="pre">sitter</span></code> Attribute so someone else could use the chair later.</p></li>
<li><p><strong>Line 16</strong>: The character is no longer sitting, so we delete their <codeclass="docutils literal notranslate"><spanclass="pre">is_sitting</span></code> Attribute. We could also have done <codeclass="docutils literal notranslate"><spanclass="pre">stander.db.is_sitting</span><spanclass="pre">=</span><spanclass="pre">None</span></code> here, but deleting the Attribute feels cleaner.</p></li>
<li><p><strong>Line 17</strong>: Finally, we inform them that they stood up successfully.</p></li>
</ul>
<p>One could imagine that one could have the future <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> command (which we haven’t created yet) check if someone is already sitting in the chair instead. This would work too, but letting the <codeclass="docutils literal notranslate"><spanclass="pre">Sittable</span></code> class handle the logic around who can sit on it makes sense.</p>
<p>We let the typeclass handle the logic, and also let it do all the return messaging. This makes it easy to churn out a bunch of chairs for people to sit on.</p>
<sectionid="sitting-on-or-in">
<h3><spanclass="section-number">13.2.1. </span>Sitting on or in?<aclass="headerlink"href="#sitting-on-or-in"title="Permalink to this headline">¶</a></h3>
<p>It’s fine to sit ‘on’ a chair. But what if our Sittable is an armchair?</p>
<p>This is not grammatically correct, you actually sit “in” an armchair rather than “on” it. It’s also possible to both sit ‘in’ or ‘on’ a chair depending on the type of chair (English is weird). We want to be able to control this.</p>
<p>We <em>could</em> make a child class of <codeclass="docutils literal notranslate"><spanclass="pre">Sittable</span></code> named <codeclass="docutils literal notranslate"><spanclass="pre">SittableIn</span></code> that makes this change, but that feels excessive. Instead we will modify what we have:</p>
<spanclass="hll"><spanclass="n">sitter</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"You are already sitting </span><spanclass="si">{</span><spanclass="n">adjective</span><spanclass="si">}</span><spanclass="s2"></span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="si">}</span><spanclass="s2">."</span><spanclass="p">)</span>
<spanclass="hll"><spanclass="sa">f</span><spanclass="s2">"You can't sit </span><spanclass="si">{</span><spanclass="n">adjective</span><spanclass="si">}</span><spanclass="s2"></span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="si">}</span><spanclass="s2">"</span>
</span><spanclass="sa">f</span><spanclass="s2">"- </span><spanclass="si">{</span><spanclass="n">current</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="si">}</span><spanclass="s2"> is already sitting there!"</span><spanclass="p">)</span>
<spanclass="hll"><spanclass="n">sitter</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"You sit </span><spanclass="si">{</span><spanclass="n">adjective</span><spanclass="si">}</span><spanclass="s2"></span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="si">}</span><spanclass="s2">"</span><spanclass="p">)</span>
<spanclass="hll"><spanclass="n">stander</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"You are not sitting </span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">db</span><spanclass="o">.</span><spanclass="n">adjective</span><spanclass="si">}</span><spanclass="s2"></span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="si">}</span><spanclass="s2">."</span><spanclass="p">)</span>
<spanclass="hll"><spanclass="n">stander</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"You stand up from </span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">key</span><spanclass="si">}</span><spanclass="s2">."</span><spanclass="p">)</span>
</span></pre></div></td></tr></table></div>
</div>
<ulclass="simple">
<li><p><strong>Line 15</strong>: We grab the <codeclass="docutils literal notranslate"><spanclass="pre">adjective</span></code> Attribute. Using <codeclass="docutils literal notranslate"><spanclass="pre">self.db.adjective</span><spanclass="pre">or</span><spanclass="pre">"on"</span></code> here means that if the Attribute is not set (is <codeclass="docutils literal notranslate"><spanclass="pre">None</span></code>/falsy) the default “on” string will be assumed.</p></li>
<li><p><strong>Lines 19,22,27,39, and 43</strong>: We use this adjective to modify the return text we see.</p></li>
</ul>
<p><codeclass="docutils literal notranslate"><spanclass="pre">reload</span></code> the server. An advantage of using Attributes like this is that they can be modified on the fly, in-game. Let’s look at a builder could use this by normal building commands (no need for <codeclass="docutils literal notranslate"><spanclass="pre">py</span></code>):</p>
<p>Since we haven’t added the <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> command yet, we must still use <codeclass="docutils literal notranslate"><spanclass="pre">py</span></code> to test:</p>
<h3><spanclass="section-number">13.2.2. </span>Extra credits<aclass="headerlink"href="#extra-credits"title="Permalink to this headline">¶</a></h3>
<p>What if we want some more dramatic flair when you sit down in certain chairs?</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>You sit down and a whoopie cushion makes a loud fart noise!
</pre></div>
</div>
<p>You can make this happen by tweaking your <codeclass="docutils literal notranslate"><spanclass="pre">Sittable</span></code> class having the return messages be replaceable by <codeclass="docutils literal notranslate"><spanclass="pre">Attributes</span></code> that you can set on the object you create. You want something like this:</p>
> chair.db.msg_sitting_down = "You sit down and a whoopie cushion makes a loud fart noise!"
> chair.do_sit(me)
You sit down and a whoopie cushion makes a loud fart noise!
</pre></div>
</div>
<p>That is, if you are not setting the Attribute, you should get a default value. We leave this implementation up to the reader.</p>
</section>
</section>
<sectionid="adding-commands">
<h2><spanclass="section-number">13.3. </span>Adding commands<aclass="headerlink"href="#adding-commands"title="Permalink to this headline">¶</a></h2>
<p>As we discussed in the <aclass="reference internal"href="Beginner-Tutorial-More-on-Commands.html"><spanclass="doc std std-doc">lesson about adding Commands</span></a>, there are two main ways to design the commands for sitting and standing up:</p>
<ulclass="simple">
<li><p>You can store the commands on the chair so they are only available when a chair is in the room</p></li>
<li><p>You can store the commands on the Character so they are always available and you must always specify which chair to sit on.</p></li>
</ul>
<p>Both of these are very useful to know about, so in this lesson we’ll try both.</p>
<h3><spanclass="section-number">13.3.1. </span>Command variant 1: Commands on the chair<aclass="headerlink"href="#command-variant-1-commands-on-the-chair"title="Permalink to this headline">¶</a></h3>
<p>This way to implement <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> puts new cmdsets on the Sittable itself.
As we’ve learned before, commands on objects are made available to others in the room.
This makes the command easy but instead adds some complexity in the management of the CmdSet.</p>
<p>This is how it could look if <codeclass="docutils literal notranslate"><spanclass="pre">armchair</span></code> is in the room (Extra credits: Change the sit message on the armchair to match this output instead of getting the default <codeclass="docutils literal notranslate"><spanclass="pre">You</span><spanclass="pre">sit</span><spanclass="pre">in</span><spanclass="pre">armchair</span></code>!):</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>> sit
As you sit down in armchair, life feels easier.
</pre></div>
</div>
<p>What happens if there are sittables <codeclass="docutils literal notranslate"><spanclass="pre">sofa</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">barstool</span></code> also in the room? Evennia will
automatically handle this for us and allow us to specify which one we want:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>> sit
More than one match for 'sit' (please narrow target):
sit-1 (armchair)
sit-2 (sofa)
sit-3 (barstool)
> sit-1
As you sit down in armchair, life feels easier.
</pre></div>
</div>
<p>To keep things separate we’ll make a new module <codeclass="docutils literal notranslate"><spanclass="pre">mygame/commands/sittables.py</span></code>:</p>
<asideclass="sidebar">
<pclass="sidebar-title">Separate Commands and Typeclasses?</p>
<p>You can organize these things as you like. If you wanted you could put the sit-command + cmdset together with the <codeclass="docutils literal notranslate"><spanclass="pre">Sittable</span></code> typeclass in <codeclass="docutils literal notranslate"><spanclass="pre">mygame/typeclasses/sittables.py</span></code>. That has the advantage of keeping everything related to sitting in one place. But there is also some organizational merit to keeping all Commands in one place as we do here.</p>
<li><p><strong>Lines 11 and 19</strong>: The <codeclass="docutils literal notranslate"><spanclass="pre">self.obj</span></code> is the object to which we added the cmdset with this Command (so the chair). We just call the <codeclass="docutils literal notranslate"><spanclass="pre">do_sit/stand</span></code> on that object and pass the <codeclass="docutils literal notranslate"><spanclass="pre">caller</span></code> (the person sitting down). The <codeclass="docutils literal notranslate"><spanclass="pre">Sittable</span></code> will do the rest.</p></li>
<li><p><strong>Line 23</strong>: The <codeclass="docutils literal notranslate"><spanclass="pre">priority</span><spanclass="pre">=</span><spanclass="pre">1</span></code> on <codeclass="docutils literal notranslate"><spanclass="pre">CmdSetSit</span></code> means that same-named Commands from this cmdset merge with a bit higher priority than Commands from the on-Character-cmdset (which has <codeclass="docutils literal notranslate"><spanclass="pre">priority</span><spanclass="pre">=</span><spanclass="pre">0</span></code>). This means that if you have a <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> command on your Character and comes into a room with a chair, the <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> command on the chair will take precedence.</p></li>
</ul>
<p>We also need to make a change to our <codeclass="docutils literal notranslate"><spanclass="pre">Sittable</span></code> typeclass. Open <codeclass="docutils literal notranslate"><spanclass="pre">mygame/typeclasses/sittables.py</span></code>:</p>
<li><p><strong>Line 4</strong>: We must install the <codeclass="docutils literal notranslate"><spanclass="pre">CmdSetSit</span></code> .</p></li>
<li><p><strong>Line 10</strong>: The <codeclass="docutils literal notranslate"><spanclass="pre">at_object_creation</span></code> method will only be called once, when the object is first created.</p></li>
<li><p><strong>Line 11</strong>: We add the command-set as a ‘default’ cmdset with <codeclass="docutils literal notranslate"><spanclass="pre">add_default</span></code>. This makes it persistent also protects it from being deleted should another cmdset be added. See <aclass="reference internal"href="../../../Components/Command-Sets.html"><spanclass="doc std std-doc">Command Sets</span></a> for more info.</p></li>
</ul>
<p>Make sure to <codeclass="docutils literal notranslate"><spanclass="pre">reload</span></code> to make the code changes available.</p>
<p>All <em>new</em> Sittables will now have your <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> Command. Your existing <codeclass="docutils literal notranslate"><spanclass="pre">armchair</span></code> will not though. This is because <codeclass="docutils literal notranslate"><spanclass="pre">at_object_creation</span></code> will not re-run for already existing objects. We can update it manually:</p>
<p>We could also update all existing sittables (all on one line):</p>
<asideclass="sidebar">
<pclass="sidebar-title">List comprehensions </p>
<p><codeclass="docutils literal notranslate"><spanclass="pre">[obj</span><spanclass="pre">for</span><spanclass="pre">obj</span><spanclass="pre">in</span><spanclass="pre">iterator]</span></code> is an example of a <em>list comprehension</em>. Think of it as an efficient way to construct a new list all in one line. You can read more about list comprehensions <aclass="reference external"href="https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions">here in the Python docs</a>.</p>
</aside>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>> py from typeclasses.sittables import Sittable ;
[sittable.at_object_creation() for sittable in Sittable.objects.all()]
</pre></div>
</div>
<p>We should now be able to use <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> while in the room with the armchair.</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>> sit
As you sit down in armchair, life feels easier.
> stand
You stand up from armchair.
</pre></div>
</div>
<p>One issue with placing the <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> (or <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code>) Command “on” the chair is that it will not be available when in a room without a Sittable object:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>> sit
Command 'sit' is not available. ...
</pre></div>
</div>
<p>This is practical but not so good-looking; it makes it harder for the user to know a <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> action is at all possible. Here is a trick for fixing this. Let’s add <em>another</em> Command to the bottom
of <codeclass="docutils literal notranslate"><spanclass="pre">mygame/commands/sittables.py</span></code>:</p>
<spanclass="normal">15</span></pre></div></td><tdclass="code"><div><pre><span></span><spanclass="c1"># after the other commands in mygame/commands/sittables.py</span>
</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You have nothing to sit on."</span><spanclass="p">)</span>
<spanclass="k">else</span><spanclass="p">:</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You are not sitting down."</span><spanclass="p">)</span>
</pre></div></td></tr></table></div>
</div>
<ulclass="simple">
<li><p><strong>Line 9</strong>: This command responds both to <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> because we added <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> to its <codeclass="docutils literal notranslate"><spanclass="pre">aliases</span></code> list. Command aliases have the same ‘weight’ as the <codeclass="docutils literal notranslate"><spanclass="pre">key</span></code> of the command, both equally identify the Command.</p></li>
<li><p><strong>Line 12</strong>: The <codeclass="docutils literal notranslate"><spanclass="pre">.cmdname</span></code> of a <codeclass="docutils literal notranslate"><spanclass="pre">Command</span></code> holds the name actually used to call it. This will be one of <codeclass="docutils literal notranslate"><spanclass="pre">"sit"</span></code> or <codeclass="docutils literal notranslate"><spanclass="pre">"stand"</span></code>. This leads to different return messages.</p></li>
</ul>
<p>We don’t need a new CmdSet for this, instead we will add this to the default Character cmdset. Open <codeclass="docutils literal notranslate"><spanclass="pre">mygame/commands/default_cmdsets.py</span></code>:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in mygame/commands/default_cmdsets.py</span>
<p>As usual, make sure to <codeclass="docutils literal notranslate"><spanclass="pre">reload</span></code> the server to have the new code recognized.</p>
<p>To test we’ll build a new location without any comfy armchairs and go there:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>> tunnel n = kitchen
north
> sit
You have nothing to sit on.
> south
sit
As you sit down in armchair, life feels easier.
</pre></div>
</div>
<p>We now have a fully functioning <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> action that is contained with the chair itself. When no chair is around, a default error message is shown.</p>
<p>How does this work? There are two cmdsets at play, both of which have a <codeclass="docutils literal notranslate"><spanclass="pre">sit/stand</span></code> Command - one on the <codeclass="docutils literal notranslate"><spanclass="pre">Sittable</span></code> (armchair) and the other on us (via the <codeclass="docutils literal notranslate"><spanclass="pre">CharacterCmdSet</span></code>). Since we set a <codeclass="docutils literal notranslate"><spanclass="pre">priority=1</span></code> on the chair’s cmdset (and <codeclass="docutils literal notranslate"><spanclass="pre">CharacterCmdSet</span></code> has <codeclass="docutils literal notranslate"><spanclass="pre">priority=0</span></code>), there will be no command-collision: the chair’s <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> takes precedence over the <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> defined on us … until there is no chair around.</p>
<p>So this handles <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code>. What about <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code>? That will work just fine:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>> stand
You stand up from armchair.
> north
> stand
You are not sitting down.
</pre></div>
</div>
<p>We have one remaining problem with <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> though - what happens when you are sitting down and try to <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> in a room with more than one <codeclass="docutils literal notranslate"><spanclass="pre">Sittable</span></code>:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>> stand
More than one match for 'stand' (please narrow target):
stand-1 (armchair)
stand-2 (sofa)
stand-3 (barstool)
</pre></div>
</div>
<p>Since all the sittables have the <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> Command on them, you’ll get a multi-match error. This <em>works</em> … but you could pick <em>any</em> of those sittables to “stand up from”. That’s really weird.</p>
<p>With <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> it was okay to get a choice - Evennia can’t know which chair we intended to sit on. But once we sit we sure know from which chair we should stand up from! We must make sure that we only get the command from the chair we are actually sitting on.</p>
<p>We will fix this with a <aclass="reference internal"href="../../../Components/Locks.html"><spanclass="doc std std-doc">Lock</span></a> and a custom <codeclass="docutils literal notranslate"><spanclass="pre">lock</span><spanclass="pre">function</span></code>. We want a lock on the <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> Command that only makes it available when the caller is actually sitting on the chair that particular <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> command is attached to.</p>
<p>First let’s add the lock so we see what we want. Open <codeclass="docutils literal notranslate"><spanclass="pre">mygame/commands/sittables.py</span></code>:</p>
<li><p><strong>Line 10</strong>: This is the lock definition. It’s on the form <codeclass="docutils literal notranslate"><spanclass="pre">condition:lockfunc</span></code>. The <codeclass="docutils literal notranslate"><spanclass="pre">cmd:</span></code> type lock is checked by Evennia when determining if a user has access to a Command at all. We want the lock-function to only return <codeclass="docutils literal notranslate"><spanclass="pre">True</span></code> if this command is on a chair which the caller is sitting on.
What will be checked is the <codeclass="docutils literal notranslate"><spanclass="pre">sitsonthis</span></code><em>lock function</em> which doesn’t exist yet.</p></li>
</ul>
<p>Open <codeclass="docutils literal notranslate"><spanclass="pre">mygame/server/conf/lockfuncs.py</span></code> to add it!</p>
<p>Evennia knows that <em>all</em> functions in <codeclass="docutils literal notranslate"><spanclass="pre">mygame/server/conf/lockfuncs</span></code> should be possible to use in a lock definition.</p>
<p>All lock functions must acccept the same arguments. The arguments are required and Evennia will pass all relevant objects as needed.</p>
<asideclass="sidebar">
<pclass="sidebar-title">Lockfuncs</p>
<p>Evennia provides a large number of default lockfuncs, such as checking permission-levels, if you are carrying or are inside the accessed object etc. There is no concept of ‘sitting’ in default Evennia however, so this we need to specify ourselves.</p>
</aside>
<ulclass="simple">
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">accessing_obj</span></code> is the one trying to access the lock. So us, in this case.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">accessed_obj</span></code> is the entity we are trying to gain a particular type of access to. Since we define the lock on the <codeclass="docutils literal notranslate"><spanclass="pre">CmdStand</span></code> class, this is <em>the command instance</em>. We are however not interested in that, but the object the command is assigned to (the chair). The object is available on the Command as <codeclass="docutils literal notranslate"><spanclass="pre">.obj</span></code>. So here, <codeclass="docutils literal notranslate"><spanclass="pre">accessed_obj.obj</span></code> is the chair.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">args</span></code> is a tuple holding any arguments passed to the lockfunc. Since we use <codeclass="docutils literal notranslate"><spanclass="pre">sitsondthis()</span></code> this will be empty (and if we add anything, it will be ignored).</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">kwargs</span></code> is a tuple of keyword arguments passed to the lockfuncs. This will be empty as well in our example.</p></li>
</ul>
<p>Make sure you <codeclass="docutils literal notranslate"><spanclass="pre">reload</span></code>.</p>
<p>If you are superuser, it’s important that you <codeclass="docutils literal notranslate"><spanclass="pre">quell</span></code> yourself before trying this out. This is because the superuser bypasses all locks - it can never get locked out, but it means it will also not see the effects of a lock like this.</p>
<p>None of the other sittables’<codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> commands passed the lock and only the one we are actually sitting on did! This is a fully functional chair now!</p>
<p>Adding a Command to the chair object like this is powerful and is a good technique to know. It does come with some caveats though, as we’ve seen.</p>
<p>We’ll now try another way to add the <codeclass="docutils literal notranslate"><spanclass="pre">sit/stand</span></code> commands.</p>
<h3><spanclass="section-number">13.3.2. </span>Command variant 2: Command on Character<aclass="headerlink"href="#command-variant-2-command-on-character"title="Permalink to this headline">¶</a></h3>
<p>Before we start with this, delete the chairs you’ve created:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>> del armchair
> del sofa
> (etc)
</pre></div>
</div>
<p>The make the following changes:</p>
<ulclass="simple">
<li><p>In <codeclass="docutils literal notranslate"><spanclass="pre">mygame/typeclasses/sittables.py</span></code>, comment out the entire <codeclass="docutils literal notranslate"><spanclass="pre">at_object_creation</span></code> method.</p></li>
<li><p>In <codeclass="docutils literal notranslate"><spanclass="pre">mygame/commands/default_cmdsets.py</span></code>, comment out the line <codeclass="docutils literal notranslate"><spanclass="pre">self.add(sittables.CmdNoSitStand)</span></code>.</p></li>
</ul>
<p>This disables the on-object command solution so we can try an alternative. Make sure to <codeclass="docutils literal notranslate"><spanclass="pre">reload</span></code> so the changes are known to Evennia.</p>
<p>In this variation we will put the <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> commands on the <codeclass="docutils literal notranslate"><spanclass="pre">Character</span></code> instead of on the chair. This makes some things easier, but makes the Commands themselves more complex because they will not know which chair to sit on. We can’t just do <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> anymore. This is how it will work:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>> sit <chair>
You sit on chair.
> stand
You stand up from chair.
</pre></div>
</div>
<p>Open <codeclass="docutils literal notranslate"><spanclass="pre">mygame/commands/sittables.py</span></code> again. We’ll add a new sit-command. We name the class <codeclass="docutils literal notranslate"><spanclass="pre">CmdSit2</span></code> since we already have <codeclass="docutils literal notranslate"><spanclass="pre">CmdSit</span></code> from the previous example. We put everything at the end of the module to keep it separate.</p>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"Sit on what?"</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You can't sit on that!"</span><spanclass="p">)</span>
</pre></div></td></tr></table></div>
</div>
<asideclass="sidebar">
<pclass="sidebar-title">Raising exceptions</p>
<p>Raising an exception allows for immediately interrupting the current program flow. Python automatically raises error-exceptions when detecting problems with the code. It will be raised up through the sequence of called code (the ‘stack’) until it’s either <codeclass="docutils literal notranslate"><spanclass="pre">caught</span></code> with a <codeclass="docutils literal notranslate"><spanclass="pre">try</span><spanclass="pre">...</span><spanclass="pre">except</span></code> or reaches the outermost scope where it’ll be logged or displayed. In this case Evennia knows to catch the <codeclass="docutils literal notranslate"><spanclass="pre">InterruptCommand</span></code> exception and stop the command execution early.</p>
</aside>
<ulclass="simple">
<li><p><strong>Line 4</strong>: We need the <codeclass="docutils literal notranslate"><spanclass="pre">InterruptCommand</span></code> to be able to abort command parsing early (see below).</p></li>
<li><p><strong>Line 27</strong>: The <codeclass="docutils literal notranslate"><spanclass="pre">parse</span></code> method runs before the <codeclass="docutils literal notranslate"><spanclass="pre">func</span></code> method on a <codeclass="docutils literal notranslate"><spanclass="pre">Command</span></code>. If no argument is provided to the command, we want to fail early, already in <codeclass="docutils literal notranslate"><spanclass="pre">parse</span></code>, so <codeclass="docutils literal notranslate"><spanclass="pre">func</span></code> never fires. Just <codeclass="docutils literal notranslate"><spanclass="pre">return</span></code> is not enough to do that, we need to <codeclass="docutils literal notranslate"><spanclass="pre">raise</span><spanclass="pre">InterruptCommand</span></code>. Evennia will see a raised <codeclass="docutils literal notranslate"><spanclass="pre">InterruptCommand</span></code> as a sign it should immediately abort the command execution.</p></li>
<li><p><strong>Line 32</strong>: We use the parsed command arguments as the target-chair to search for. As discussed in the <aclass="reference internal"href="Beginner-Tutorial-Searching-Things.html"><spanclass="doc std std-doc">search tutorial</span></a>, <codeclass="docutils literal notranslate"><spanclass="pre">self.caller.search()</span></code> will handle error messages itself. So if it returns <codeclass="docutils literal notranslate"><spanclass="pre">None</span></code>, we can just <codeclass="docutils literal notranslate"><spanclass="pre">return</span></code>.</p></li>
<li><p><strong>Line 35-38</strong>: The <codeclass="docutils literal notranslate"><spanclass="pre">try...except</span></code> block ‘catches’ and exception and handles it. In this case we try to run <codeclass="docutils literal notranslate"><spanclass="pre">do_sit</span></code> on the object. If the object we found is <em>not</em> a <codeclass="docutils literal notranslate"><spanclass="pre">Sittable</span></code>, it will likely not have a <codeclass="docutils literal notranslate"><spanclass="pre">do_sit</span></code> method and an <codeclass="docutils literal notranslate"><spanclass="pre">AttributeError</span></code> will be raised. We should handle that case gracefully.</p></li>
</ul>
<p>Let’s do the <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> command while we are at it. Since the Command is external to the chair we need to figure out if we are sitting down or not.</p>
<spanclass="n">caller</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You are not sitting down."</span><spanclass="p">)</span>
<li><p><strong>Line 17</strong>: We didn’t need the <codeclass="docutils literal notranslate"><spanclass="pre">is_sitting</span></code> Attribute for the first version of these Commands, but we do need it now. Since we have this, we don’t need to search and know just which chair we sit on. If we don’t have this Attribute set, we are not sitting anywhere.</p></li>
<li><p><strong>Line 21</strong>: We stand up using the sittable we found.</p></li>
</ul>
<p>All that is left now is to make <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> available to us. This type of Command should be available to us all the time so we can put it in the default Cmdset on the Character. Open <codeclass="docutils literal notranslate"><spanclass="pre">mygame/commands/default_cmdsets.py</span></code>.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in mygame/commands/default_cmdsets.py</span>
<p>Storing commands on the Character centralizes them, but you must instead search or store any external objects you want that command to interact on.</p>
</section>
</section>
<sectionid="conclusions">
<h2><spanclass="section-number">13.4. </span>Conclusions<aclass="headerlink"href="#conclusions"title="Permalink to this headline">¶</a></h2>
<p>In this lesson we built ourselves a chair and even a sofa!</p>
<ulclass="simple">
<li><p>We modified our <codeclass="docutils literal notranslate"><spanclass="pre">Character</span></code> class to avoid moving when sitting down.</p></li>
<li><p>We made a new <codeclass="docutils literal notranslate"><spanclass="pre">Sittable</span></code> typeclass</p></li>
<li><p>We tried two ways to allow a user to interact with sittables using <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> commands.</p></li>
</ul>
<p>Eagle-eyed readers will notice that the <codeclass="docutils literal notranslate"><spanclass="pre">stand</span></code> command sitting “on” the chair (variant 1) would work just fine together with the <codeclass="docutils literal notranslate"><spanclass="pre">sit</span></code> command sitting “on” the Character (variant 2). There is nothing stopping you from mixing them, or even try a third solution that better fits what you have in mind.</p>
<p>This concludes the first part of the Beginner tutorial!</p>
<pclass="last">You are reading an old version of the Evennia documentation. <ahref="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.