mirror of
https://github.com/evennia/evennia.git
synced 2026-04-05 15:37:17 +02:00
Updated HTML docs
This commit is contained in:
parent
937794ad0b
commit
dcc4cbe66f
316 changed files with 34330 additions and 3279 deletions
|
|
@ -1,897 +0,0 @@
|
|||
|
||||
<!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>Making a sittable object — 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="Turn based Combat System" href="Turn-based-Combat-System.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="Turn-based-Combat-System.html" title="Turn based Combat System"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Making a sittable object</a></li>
|
||||
</ul>
|
||||
<div class="develop">develop branch</div>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<p><a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">prev lesson</span></a> | <a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">next lesson</span></a></p>
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="making-a-sittable-object">
|
||||
<h1>Making a sittable object<a class="headerlink" href="#making-a-sittable-object" title="Permalink to this headline">¶</a></h1>
|
||||
<p>In this lesson we will go through how to make a chair you can sit on. Sounds easy, right?
|
||||
Well it is. But in the process of making the chair we will need to consider the various ways
|
||||
to do it depending on how we want our game to work.</p>
|
||||
<p>The goals of this lesson are as follows:</p>
|
||||
<ul class="simple">
|
||||
<li><p>We want a new ‘sittable’ object, a Chair 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 we’ll
|
||||
set a flag “Resting” on the Character sitting in 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>
|
||||
<li><p>A character should be able to stand up and move away from the chair.</p></li>
|
||||
</ul>
|
||||
<p>There are two main ways to design the commands for sitting and standing up.</p>
|
||||
<ul class="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. But first
|
||||
we need to handle some basics.</p>
|
||||
<section id="dont-move-us-when-resting">
|
||||
<h2>Don’t move us when resting<a class="headerlink" href="#dont-move-us-when-resting" 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 <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/characters.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Character</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_move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">destination</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Called by self.move_to when trying to move somewhere. If this returns</span>
|
||||
<span class="sd"> False, the move is immediately cancelled.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">is_resting</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You can't go anywhere while resting."</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="kc">False</span>
|
||||
<span class="k">return</span> <span class="kc">True</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>When moving somewhere, <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">character.move_to</span></a> is called. This in turn
|
||||
will call <code class="docutils literal notranslate"><span class="pre">character.at_pre_move</span></code>. Here we look for an Attribute <code class="docutils literal notranslate"><span class="pre">is_resting</span></code> (which we will assign below)
|
||||
to determine if we are stuck on the chair or not.</p>
|
||||
</section>
|
||||
<section id="making-the-chair-itself">
|
||||
<h2>Making the Chair itself<a class="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 <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/sittables.py</span></code> with the following content:</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">DefaultObject</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Sittable</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</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="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">do_sit</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sitter</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Called when trying to sit on/in this object.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> sitter (Object): The one trying to sit down.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">current</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span>
|
||||
<span class="k">if</span> <span class="n">current</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">current</span> <span class="o">==</span> <span class="n">sitter</span><span class="p">:</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You are already sitting on </span><span class="si">{self.key}</span><span class="s2">."</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You can't sit on </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> "</span>
|
||||
<span class="sa">f</span><span class="s2">"- </span><span class="si">{</span><span class="n">current</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is already sitting there!"</span><span class="p">)</span>
|
||||
<span class="k">return</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitting</span> <span class="o">=</span> <span class="n">sitter</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">is_resting</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You sit on </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">do_stand</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">stander</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Called when trying to stand from this object.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> stander (Object): The one trying to stand up.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">current</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">stander</span> <span class="o">==</span> <span class="n">current</span><span class="p">:</span>
|
||||
<span class="n">stander</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You are not sitting on </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitting</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">stander</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">is_resting</span> <span class="o">=</span> <span class="kc">False</span>
|
||||
<span class="n">stander</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You stand up from </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here we have a small Typeclass that handles someone trying to sit on it. It has two methods that we can simply
|
||||
call from a Command later. We set the <code class="docutils literal notranslate"><span class="pre">is_resting</span></code> Attribute on the one sitting down.</p>
|
||||
<p>One could imagine that one could have the future <code class="docutils literal notranslate"><span class="pre">sit</span></code> command check if someone is already sitting in the
|
||||
chair instead. This would work too, but letting the <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> class handle the logic around who can sit on it makes
|
||||
logical 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. But it’s not perfect. The <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> class is general. What if you want to
|
||||
make an armchair. You sit “in” an armchair rather than “on” it. We <em>could</em> make a child class of <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> named
|
||||
<code class="docutils literal notranslate"><span class="pre">SittableIn</span></code> that makes this change, but that feels excessive. Instead we will make it so that Sittables can
|
||||
modify this per-instance:</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">DefaultObject</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Sittable</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</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="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="c1"># do you sit "on" or "in" this object?</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">adjective</span> <span class="o">=</span> <span class="s2">"on"</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">do_sit</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sitter</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Called when trying to sit on/in this object.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> sitter (Object): The one trying to sit down.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">adjective</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">adjective</span>
|
||||
<span class="n">current</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span>
|
||||
<span class="k">if</span> <span class="n">current</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">current</span> <span class="o">==</span> <span class="n">sitter</span><span class="p">:</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You are already sitting </span><span class="si">{</span><span class="n">adjective</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"You can't sit </span><span class="si">{</span><span class="n">adjective</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> "</span>
|
||||
<span class="sa">f</span><span class="s2">"- </span><span class="si">{</span><span class="n">current</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is already sitting there!"</span><span class="p">)</span>
|
||||
<span class="k">return</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitting</span> <span class="o">=</span> <span class="n">sitter</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">is_resting</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You sit </span><span class="si">{</span><span class="n">adjective</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">do_stand</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">stander</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Called when trying to stand from this object.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> stander (Object): The one trying to stand up.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">current</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">stander</span> <span class="o">==</span> <span class="n">current</span><span class="p">:</span>
|
||||
<span class="n">stander</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You are not sitting </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">adjective</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitting</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">stander</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">is_resting</span> <span class="o">=</span> <span class="kc">False</span>
|
||||
<span class="n">stander</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You stand up from </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We added a new Attribute <code class="docutils literal notranslate"><span class="pre">adjective</span></code> which will probably usually be <code class="docutils literal notranslate"><span class="pre">in</span></code> or <code class="docutils literal notranslate"><span class="pre">on</span></code> but could also be <code class="docutils literal notranslate"><span class="pre">at</span></code> if you
|
||||
want to be able to sit <em>at a desk</em> for example. A regular builder would use it like this:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> create/drop armchair : sittables.Sittable
|
||||
> set armchair/adjective = in
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This is probably enough. But all those strings are hard-coded. What if we want some more dramatic flair when you
|
||||
sit down?</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You sit down and a whoopie cushion makes a loud fart noise!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>For this we need to allow some further customization. Let’s let the current strings be defaults that
|
||||
we can replace.</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">DefaultObject</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Sittable</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> An object one can sit on</span>
|
||||
|
||||
<span class="sd"> Customizable Attributes:</span>
|
||||
<span class="sd"> adjective: How to sit (on, in, at etc)</span>
|
||||
<span class="sd"> Return messages (set as Attributes):</span>
|
||||
<span class="sd"> msg_already_sitting: Already sitting here</span>
|
||||
<span class="sd"> format tokens {adjective} and {key}</span>
|
||||
<span class="sd"> msg_other_sitting: Someone else is sitting here.</span>
|
||||
<span class="sd"> format tokens {adjective}, {key} and {other}</span>
|
||||
<span class="sd"> msg_sitting_down: Successfully sit down</span>
|
||||
<span class="sd"> format tokens {adjective}, {key}</span>
|
||||
<span class="sd"> msg_standing_fail: Fail to stand because not sitting.</span>
|
||||
<span class="sd"> format tokens {adjective}, {key}</span>
|
||||
<span class="sd"> msg_standing_up: Successfully stand up</span>
|
||||
<span class="sd"> format tokens {adjective}, {key}</span>
|
||||
|
||||
<span class="sd"> """</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="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="c1"># do you sit "on" or "in" this object?</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">adjective</span> <span class="o">=</span> <span class="s2">"on"</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">do_sit</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sitter</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Called when trying to sit on/in this object.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> sitter (Object): The one trying to sit down.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">adjective</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">adjective</span>
|
||||
<span class="n">current</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span>
|
||||
<span class="k">if</span> <span class="n">current</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">current</span> <span class="o">==</span> <span class="n">sitter</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">msg_already_sitting</span><span class="p">:</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">msg_already_sitting</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
|
||||
<span class="n">adjective</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">adjective</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">key</span><span class="p">))</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You are already sitting </span><span class="si">{</span><span class="n">adjective</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">msg_other_sitting</span><span class="p">:</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">msg_already_sitting</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
|
||||
<span class="n">other</span><span class="o">=</span><span class="n">current</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="n">adjective</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">adjective</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">key</span><span class="p">))</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You can't sit </span><span class="si">{</span><span class="n">adjective</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> "</span>
|
||||
<span class="sa">f</span><span class="s2">"- </span><span class="si">{</span><span class="n">current</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is already sitting there!"</span><span class="p">)</span>
|
||||
<span class="k">return</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitting</span> <span class="o">=</span> <span class="n">sitter</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">is_resting</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">msg_sitting_down</span><span class="p">:</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">msg_sitting_down</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">adjective</span><span class="o">=</span><span class="n">adjective</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">key</span><span class="p">))</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You sit </span><span class="si">{</span><span class="n">adjective</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">do_stand</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">stander</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Called when trying to stand from this object.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> stander (Object): The one trying to stand up.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">current</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">stander</span> <span class="o">==</span> <span class="n">current</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">msg_standing_fail</span><span class="p">:</span>
|
||||
<span class="n">stander</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">msg_standing_fail</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
|
||||
<span class="n">adjective</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">adjective</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">key</span><span class="p">))</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">stander</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You are not sitting </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">adjective</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitting</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">stander</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">is_resting</span> <span class="o">=</span> <span class="kc">False</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">msg_standing_up</span><span class="p">:</span>
|
||||
<span class="n">stander</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">msg_standing_up</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
|
||||
<span class="n">adjective</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">adjective</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">key</span><span class="p">))</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">stander</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You stand up from </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here we really went all out with flexibility. If you need this much is up to you.
|
||||
We added a bunch of optional Attributes to hold alternative versions of all the messages.
|
||||
There are some things to note:</p>
|
||||
<ul>
|
||||
<li><p>We don’t actually initiate those Attributes in <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code>. This is a simple
|
||||
optimization. The assumption is that <em>most</em> chairs will probably not be this customized.
|
||||
So initiating a bunch of Attributes to, say, empty strings would be a lot of useless database calls.
|
||||
The drawback is that the available Attributes become less visible when reading the code. So we add a long
|
||||
describing docstring to the end to explain all you can use.</p></li>
|
||||
<li><p>We use <code class="docutils literal notranslate"><span class="pre">.format</span></code> to inject formatting-tokens in the text. The good thing about such formatting
|
||||
markers is that they are <em>optional</em>. They are there if you want them, but Python will not complain
|
||||
if you don’t include some or any of them. Let’s see an example:</p>
|
||||
<blockquote>
|
||||
<div><p>reload # if you have new code
|
||||
create/drop armchair : sittables.Sittable
|
||||
set armchair/adjective = in
|
||||
set armchair/msg_sitting_down = As you sit down {adjective} {key}, life feels easier.
|
||||
set armchair/msg_standing_up = You stand up from {key}. Life resumes.</p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
</ul>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">{key}</span></code> and <code class="docutils literal notranslate"><span class="pre">{adjective}</span></code> are examples of optional formatting markers. Whenever the message is
|
||||
returned, the format-tokens within will be replaced with <code class="docutils literal notranslate"><span class="pre">armchair</span></code> and <code class="docutils literal notranslate"><span class="pre">in</span></code> respectively. Should we
|
||||
rename the chair later, this will show in the messages automatically (since <code class="docutils literal notranslate"><span class="pre">{key}</span></code> will change).</p>
|
||||
<p>We have no Command to use this chair yet. But we can try it out with <code class="docutils literal notranslate"><span class="pre">py</span></code>:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py self.search("armchair").do_sit(self)
|
||||
As you sit down in armchair, life feels easier.
|
||||
> self.db.resting
|
||||
True
|
||||
> py self.search("armchair").do_stand(self)
|
||||
You stand up from armchair. Life resumes
|
||||
> self.db.resting
|
||||
False
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you follow along and get a result like this, all seems to be working well!</p>
|
||||
</section>
|
||||
<section id="command-variant-1-commands-on-the-chair">
|
||||
<h2>Command variant 1: Commands on the chair<a class="headerlink" href="#command-variant-1-commands-on-the-chair" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This way to implement <code class="docutils literal notranslate"><span class="pre">sit</span></code> and <code class="docutils literal notranslate"><span class="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 will look if <code class="docutils literal notranslate"><span class="pre">armchair</span></code> is in the room:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> sit
|
||||
As you sit down in armchair, life feels easier.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>What happens if there are sittables <code class="docutils literal notranslate"><span class="pre">sofa</span></code> and <code class="docutils literal notranslate"><span class="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>
|
||||
<div class="highlight-none notranslate"><div class="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 <code class="docutils literal notranslate"><span class="pre">mygame/commands/sittables.py</span></code>:</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Separate Commands and Typeclasses?</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You can organize these things as you like. If you wanted you could put the sit-command + cmdset
|
||||
together with the `Sittable` typeclass in `mygame/typeclasses/sittables.py`. 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.
|
||||
</pre></div>
|
||||
</div>
|
||||
</aside>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span><span class="p">,</span> <span class="n">CmdSet</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdSit</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Sit down.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"sit"</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">do_sit</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdStand</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Stand up.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"stand"</span>
|
||||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">do_stand</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdSetSit</span><span class="p">(</span><span class="n">CmdSet</span><span class="p">):</span>
|
||||
<span class="n">priority</span> <span class="o">=</span> <span class="mi">1</span>
|
||||
<span class="k">def</span> <span class="nf">at_cmdset_creation</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">add</span><span class="p">(</span><span class="n">CmdSit</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdStand</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>As seen, the commands are nearly trivial. <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> is the object to which we added the cmdset with this
|
||||
Command (so for example a chair). We just call the <code class="docutils literal notranslate"><span class="pre">do_sit/stand</span></code> on that object and the <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> will
|
||||
do the rest.</p>
|
||||
<p>Why that <code class="docutils literal notranslate"><span class="pre">priority</span> <span class="pre">=</span> <span class="pre">1</span></code> on <code class="docutils literal notranslate"><span class="pre">CmdSetSit</span></code>? This makes same-named Commands from this cmdset merge with a bit higher
|
||||
priority than Commands from the Character-cmdset. Why this is a good idea will become clear shortly.</p>
|
||||
<p>We also need to make a change to our <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> typeclass. Open <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/sittables.py</span></code>:</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">DefaultObject</span>
|
||||
<span class="kn">from</span> <span class="nn">commands.sittables</span> <span class="kn">import</span> <span class="n">CmdSetSit</span> <span class="c1"># <- new</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Sittable</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> (docstring)</span>
|
||||
<span class="sd"> """</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="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="c1"># do you sit "on" or "in" this object?</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">adjective</span> <span class="o">=</span> <span class="s2">"on"</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">cmdset</span><span class="o">.</span><span class="n">add_default</span><span class="p">(</span><span class="n">CmdSetSit</span><span class="p">)</span> <span class="c1"># <- new</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Any <em>new</em> Sittables will now have your <code class="docutils literal notranslate"><span class="pre">sit</span></code> Command. Your existing <code class="docutils literal notranslate"><span class="pre">armchair</span></code> will not,
|
||||
since <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> will not re-run for already existing objects. We can update it manually:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> update armchair
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We could also update all existing sittables (all on one line):</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py from typeclasses.sittables import Sittable ;
|
||||
[sittable.at_object_creation() for sittable in Sittable.objects.all()]
|
||||
</pre></div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div><p>The above shows 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
|
||||
<a class="reference external" href="https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions">here in the Python docs</a>.</p>
|
||||
</div></blockquote>
|
||||
<p>We should now be able to use <code class="docutils literal notranslate"><span class="pre">sit</span></code> while in the room with the armchair.</p>
|
||||
<div class="highlight-none notranslate"><div class="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 <code class="docutils literal notranslate"><span class="pre">sit</span></code> (or <code class="docutils literal notranslate"><span class="pre">stand</span></code>) Command “on” the chair is that it will not be available when in a
|
||||
room without a Sittable object:</p>
|
||||
<div class="highlight-none notranslate"><div class="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 <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">mygame/commands/sittables.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># ...</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdNoSitStand</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Sit down or Stand up</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"sit"</span>
|
||||
<span class="n">aliases</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"stand"</span><span class="p">]</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">cmdname</span> <span class="o">==</span> <span class="s2">"sit"</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You have nothing to sit on."</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You are not sitting down."</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here we have a Command that is actually two - it will answer to both <code class="docutils literal notranslate"><span class="pre">sit</span></code> and <code class="docutils literal notranslate"><span class="pre">stand</span></code> since we
|
||||
added <code class="docutils literal notranslate"><span class="pre">stand</span></code> to its <code class="docutils literal notranslate"><span class="pre">aliases</span></code>. In the command we look at <code class="docutils literal notranslate"><span class="pre">self.cmdname</span></code>, which is the string
|
||||
<em>actually used</em> to call this command. We use this to return different messages.</p>
|
||||
<p>We don’t need a separate CmdSet for this, instead we will add this
|
||||
to the default Character cmdset. Open <code class="docutils literal notranslate"><span class="pre">mygame/commands/default_cmdsets.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># ...</span>
|
||||
<span class="kn">from</span> <span class="nn">commands</span> <span class="kn">import</span> <span class="n">sittables</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CharacterCmdSet</span><span class="p">(</span><span class="n">CmdSet</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> (docstring)</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">def</span> <span class="nf">at_cmdset_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="c1"># ...</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">sittables</span><span class="o">.</span><span class="n">CmdNoSitStand</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>To test we’ll build a new location without any comfy armchairs and go there:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> 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 <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">sit</span></code> Command. As you may remember we
|
||||
set the chair’s cmdset to <code class="docutils literal notranslate"><span class="pre">priority</span> <span class="pre">=</span> <span class="pre">1</span></code>. This is where that matters. The default Character cmdset has a
|
||||
priority of 0. This means that whenever we enter a room with a Sittable thing, the <code class="docutils literal notranslate"><span class="pre">sit</span></code> command
|
||||
from <em>its</em> cmdset will take <em>precedence</em> over the Character cmdset’s version. So we are actually picking
|
||||
<em>different</em> <code class="docutils literal notranslate"><span class="pre">sit</span></code> commands depending on circumstance! The user will never be the wiser.</p>
|
||||
<p>So this handles <code class="docutils literal notranslate"><span class="pre">sit</span></code>. What about <code class="docutils literal notranslate"><span class="pre">stand</span></code>? That will work just fine:</p>
|
||||
<div class="highlight-none notranslate"><div class="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 <code class="docutils literal notranslate"><span class="pre">stand</span></code> though - what happens when you are sitting down and try to
|
||||
<code class="docutils literal notranslate"><span class="pre">stand</span></code> in a room with more than one chair:</p>
|
||||
<div class="highlight-none notranslate"><div class="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 <code class="docutils literal notranslate"><span class="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 and non-intuitive. With <code class="docutils literal notranslate"><span class="pre">sit</span></code> it
|
||||
was okay to get a choice - Evennia can’t know which chair we intended to sit on. But we know which chair we
|
||||
sit on so we should only get <em>its</em> <code class="docutils literal notranslate"><span class="pre">stand</span></code> command.</p>
|
||||
<p>We will fix this with a <code class="docutils literal notranslate"><span class="pre">lock</span></code> and a custom <code class="docutils literal notranslate"><span class="pre">lock</span> <span class="pre">function</span></code>. We want a lock on the <code class="docutils literal notranslate"><span class="pre">stand</span></code> Command that only
|
||||
makes it available when the caller is actually sitting on the chair the <code class="docutils literal notranslate"><span class="pre">stand</span></code> command is on.</p>
|
||||
<p>First let’s add the lock so we see what we want. Open <code class="docutils literal notranslate"><span class="pre">mygame/commands/sittables.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># ...</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdStand</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Stand up.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"stand"</span>
|
||||
<span class="n">lock</span> <span class="o">=</span> <span class="s2">"cmd:sitsonthis()"</span> <span class="c1"># < this is new</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">do_stand</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span>
|
||||
<span class="c1"># ...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We define a <a class="reference internal" href="../../../Components/Locks.html"><span class="doc std std-doc">Lock</span></a> on the command. The <code class="docutils literal notranslate"><span class="pre">cmd:</span></code> is in what situation Evennia will check
|
||||
the lock. The <code class="docutils literal notranslate"><span class="pre">cmd</span></code> means that it will check the lock when determining if a user has access to this command or not.
|
||||
What will be checked is the <code class="docutils literal notranslate"><span class="pre">sitsonthis</span></code> <em>lock function</em> which doesn’t exist yet.</p>
|
||||
<p>Open <code class="docutils literal notranslate"><span class="pre">mygame/server/conf/lockfuncs.py</span></code> to add it!</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">"""</span>
|
||||
<span class="sd">(module lockstring)</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">sitsonthis</span><span class="p">(</span><span class="n">accessing_obj</span><span class="p">,</span> <span class="n">accessed_obj</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">"""</span>
|
||||
<span class="sd"> True if accessing_obj is sitting on/in the accessed_obj.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">return</span> <span class="n">accessed_obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitting</span> <span class="o">==</span> <span class="n">accessing_obj</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Evennia knows that all functions in <code class="docutils literal notranslate"><span class="pre">mygame/server/conf/lockfuncs</span></code> should be possible to use in a lock definition.
|
||||
The arguments are required and Evennia will pass all relevant objects to them:</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Lockfuncs</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>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.
|
||||
</pre></div>
|
||||
</div>
|
||||
</aside>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">accessing_obj</span></code> is the one trying to access the lock. So us, in this case.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">accessed_obj</span></code> is the entity we are trying to gain a particular type of access to. So the chair.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">args</span></code> is a tuple holding any arguments passed to the lockfunc. Since we use <code class="docutils literal notranslate"><span class="pre">sitsondthis()</span></code> this will
|
||||
be empty (and if we add anything, it will be ignored).</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="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>If you are superuser, it’s important that you <code class="docutils literal notranslate"><span class="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>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> quell
|
||||
> stand
|
||||
You stand up from armchair
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>None of the other sittables’ <code class="docutils literal notranslate"><span class="pre">stand</span></code> commands passed the lock and only the one we are actually sitting on did.</p>
|
||||
<p>Adding a Command to the chair object like this is powerful and a good technique to know. It does come with some
|
||||
caveats though that one needs to keep in mind.</p>
|
||||
<p>We’ll now try another way to add the <code class="docutils literal notranslate"><span class="pre">sit/stand</span></code> commands.</p>
|
||||
</section>
|
||||
<section id="command-variant-2-command-on-character">
|
||||
<h2>Command variant 2: Command on Character<a class="headerlink" href="#command-variant-2-command-on-character" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Before we start with this, delete the chairs you’ve created (<code class="docutils literal notranslate"><span class="pre">del</span> <span class="pre">armchair</span></code> etc) and then do the following
|
||||
changes:</p>
|
||||
<ul class="simple">
|
||||
<li><p>In <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/sittables.py</span></code>, comment out the line <code class="docutils literal notranslate"><span class="pre">self.cmdset.add_default(CmdSetSit)</span></code>.</p></li>
|
||||
<li><p>In <code class="docutils literal notranslate"><span class="pre">mygame/commands/default_cmdsets.py</span></code>, comment out the line <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">reload</span></code> so the
|
||||
changes are known to Evennia.</p>
|
||||
<p>In this variation we will put the <code class="docutils literal notranslate"><span class="pre">sit</span></code> and <code class="docutils literal notranslate"><span class="pre">stand</span></code> commands on the <code class="docutils literal notranslate"><span class="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 <code class="docutils literal notranslate"><span class="pre">sit</span></code> anymore. This is how it will work.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> sit <chair>
|
||||
You sit on chair.
|
||||
> stand
|
||||
You stand up from chair.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Open <code class="docutils literal notranslate"><span class="pre">mygame/commands.sittables.py</span></code> again. We’ll add a new sit-command. We name the class <code class="docutils literal notranslate"><span class="pre">CmdSit2</span></code> since
|
||||
we already have <code class="docutils literal notranslate"><span class="pre">CmdSit</span></code> from the previous example. We put everything at the end of the module to
|
||||
keep it separate.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span><span class="p">,</span> <span class="n">CmdSet</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">InterruptCommand</span> <span class="c1"># <- this is new</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdSit</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="c1"># new from here</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdSit2</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Sit down.</span>
|
||||
|
||||
<span class="sd"> Usage:</span>
|
||||
<span class="sd"> sit <sittable></span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"sit"</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">parse</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">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Sit on what?"</span><span class="p">)</span>
|
||||
<span class="k">raise</span> <span class="n">InterruptCommand</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
|
||||
<span class="c1"># self.search handles all error messages etc.</span>
|
||||
<span class="n">sittable</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">sittable</span><span class="p">:</span>
|
||||
<span class="k">return</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="n">sittable</span><span class="o">.</span><span class="n">do_sit</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span>
|
||||
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You can't sit on that!"</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>With this Command-variation we need to search for the sittable. A series of methods on the Command
|
||||
are run in sequence:</p>
|
||||
<ol class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">Command.at_pre_command</span></code> - this is not used by default</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">Command.parse</span></code> - this should parse the input</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">Command.func</span></code> - this should implement the actual Command functionality</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">Command.at_post_func</span></code> - this is not used by default</p></li>
|
||||
</ol>
|
||||
<p>So if we just <code class="docutils literal notranslate"><span class="pre">return</span></code> in <code class="docutils literal notranslate"><span class="pre">.parse</span></code>, <code class="docutils literal notranslate"><span class="pre">.func</span></code> will still run, which is not what we want. To immediately
|
||||
abort this sequence we need to <code class="docutils literal notranslate"><span class="pre">raise</span> <span class="pre">InterruptCommand</span></code>.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Raising exceptions</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>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 `caught` with
|
||||
a `try ... except` or reaches the outermost scope where it'll be logged or displayed.
|
||||
</pre></div>
|
||||
</div>
|
||||
</aside>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">InterruptCommand</span></code> is an <em>exception</em> that the Command-system catches with the understanding that we want
|
||||
to do a clean abort. In the <code class="docutils literal notranslate"><span class="pre">.parse</span></code> method we strip any whitespaces from the argument and
|
||||
sure there actuall <em>is</em> an argument. We abort immediately if there isn’t.</p>
|
||||
<p>We we get to <code class="docutils literal notranslate"><span class="pre">.func</span></code> at all, we know that we have an argument. We search for this and abort if we there was
|
||||
a problem finding the target.</p>
|
||||
<blockquote>
|
||||
<div><p>We could have done <code class="docutils literal notranslate"><span class="pre">raise</span> <span class="pre">InterruptCommand</span></code> in <code class="docutils literal notranslate"><span class="pre">.func</span></code> as well, but <code class="docutils literal notranslate"><span class="pre">return</span></code> is a little shorter to write
|
||||
and there is no harm done if <code class="docutils literal notranslate"><span class="pre">at_post_func</span></code> runs since it’s empty.</p>
|
||||
</div></blockquote>
|
||||
<p>Next we call the found sittable’s <code class="docutils literal notranslate"><span class="pre">do_sit</span></code> method. Note that we wrap this call like this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="c1"># code</span>
|
||||
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
|
||||
<span class="c1"># stuff to do if AttributeError exception was raised</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The reason is that <code class="docutils literal notranslate"><span class="pre">caller.search</span></code> has no idea we are looking for a Sittable. The user could have tried
|
||||
<code class="docutils literal notranslate"><span class="pre">sit</span> <span class="pre">wall</span></code> or <code class="docutils literal notranslate"><span class="pre">sit</span> <span class="pre">sword</span></code>. These don’t have a <code class="docutils literal notranslate"><span class="pre">do_sit</span></code> method <em>but we call it anyway and handle the error</em>.
|
||||
This is a very “Pythonic” thing to do. The concept is often called “leap before you look” or “it’s easier to
|
||||
ask for forgiveness than for permission”. If <code class="docutils literal notranslate"><span class="pre">sittable.do_sit</span></code> does not exist, Python will raise an <code class="docutils literal notranslate"><span class="pre">AttributeError</span></code>.
|
||||
We catch this with <code class="docutils literal notranslate"><span class="pre">try</span> <span class="pre">...</span> <span class="pre">except</span> <span class="pre">AttributeError</span></code> and convert it to a proper error message.</p>
|
||||
<p>While it’s useful to learn about <code class="docutils literal notranslate"><span class="pre">try</span> <span class="pre">...</span> <span class="pre">except</span></code>, there is also a way to leverage Evennia to do this without
|
||||
<code class="docutils literal notranslate"><span class="pre">try</span> <span class="pre">...</span> <span class="pre">except</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
|
||||
<span class="c1"># self.search handles all error messages etc.</span>
|
||||
<span class="n">sittable</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">,</span>
|
||||
<span class="n">typeclass</span><span class="o">=</span><span class="s2">"typeclasses.sittables.Sittable"</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">sittable</span><span class="p">:</span>
|
||||
<span class="k">return</span>
|
||||
<span class="n">sittable</span><span class="o">.</span><span class="n">do_sit</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Continuing across multiple lines</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Note how the `.search()` method's arguments are spread out over multiple
|
||||
lines. This works for all lists, tuples and other listings and is
|
||||
a good way to avoid very long and hard-to-read lines.
|
||||
</pre></div>
|
||||
</div>
|
||||
</aside>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">caller.search</span></code> method has an keyword argument <code class="docutils literal notranslate"><span class="pre">typeclass</span></code> that can take either a python-path to a
|
||||
typeclass, the typeclass itself, or a list of either to widen the allowed options. In this case we know
|
||||
for sure that the <code class="docutils literal notranslate"><span class="pre">sittable</span></code> we get is actually a <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> class and we can call <code class="docutils literal notranslate"><span class="pre">sittable.do_sit</span></code> without
|
||||
needing to worry about catching errors.</p>
|
||||
<p>Let’s do the <code class="docutils literal notranslate"><span class="pre">stand</span></code> command while we are at it. Again, since the Command is external to the chair we don’t
|
||||
know which object we are sitting in and have to search for it.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||||
<span class="k">class</span> <span class="nc">CmdStand2</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Stand up.</span>
|
||||
|
||||
<span class="sd"> Usage:</span>
|
||||
<span class="sd"> stand</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"stand"</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
|
||||
<span class="n">caller</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span>
|
||||
<span class="c1"># find the thing we are sitting on/in, by finding the object</span>
|
||||
<span class="c1"># in the current location that as an Attribute "sitter" set</span>
|
||||
<span class="c1"># to the caller</span>
|
||||
<span class="n">sittable</span> <span class="o">=</span> <span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span>
|
||||
<span class="n">caller</span><span class="p">,</span>
|
||||
<span class="n">candidates</span><span class="o">=</span><span class="n">caller</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">contents</span><span class="p">,</span>
|
||||
<span class="n">attribute_name</span><span class="o">=</span><span class="s2">"sitter"</span><span class="p">,</span>
|
||||
<span class="n">typeclass</span><span class="o">=</span><span class="s2">"typeclasses.sittables.Sittable"</span><span class="p">)</span>
|
||||
<span class="c1"># if this is None, the error was already reported to user</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">sittable</span><span class="p">:</span>
|
||||
<span class="k">return</span>
|
||||
|
||||
<span class="n">sittable</span><span class="o">.</span><span class="n">do_stand</span><span class="p">(</span><span class="n">caller</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This forced us to to use the full power of the <code class="docutils literal notranslate"><span class="pre">caller.search</span></code> method. If we wanted to search for something
|
||||
more complex we would likely need to break out a <a class="reference internal" href="../Part1/Django-queries.html"><span class="doc std std-doc">Django query</span></a> to do it. The key here is that
|
||||
we know that the object we are looking for is a <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> and that it must have an Attribute named <code class="docutils literal notranslate"><span class="pre">sitter</span></code>
|
||||
which should be set to us, the one sitting on/in the thing. Once we have that we just call <code class="docutils literal notranslate"><span class="pre">.do_stand</span></code> on it
|
||||
and let the Typeclass handle the rest.</p>
|
||||
<p>All that is left now is to make this available to us. This type of Command should be available to us all the time
|
||||
so we can put it in the default Cmdset<code class="docutils literal notranslate"><span class="pre">on</span> <span class="pre">the</span> <span class="pre">Character.</span> <span class="pre">Open</span></code>mygame/default_cmdsets.py`</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># ...</span>
|
||||
<span class="kn">from</span> <span class="nn">commands</span> <span class="kn">import</span> <span class="n">sittables</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CharacterCmdSet</span><span class="p">(</span><span class="n">CmdSet</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> (docstring)</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">def</span> <span class="nf">at_cmdset_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="c1"># ...</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">sittables</span><span class="o">.</span><span class="n">CmdSit2</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">sittables</span><span class="o">.</span><span class="n">CmdStand2</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now let’s try it out:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> create/drop sofa : sittables.Sittable
|
||||
> sit sofa
|
||||
You sit down on sofa.
|
||||
> stand
|
||||
You stand up from sofa.
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="conclusions">
|
||||
<h2>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In this lesson we accomplished quite a bit:</p>
|
||||
<ul class="simple">
|
||||
<li><p>We modified our <code class="docutils literal notranslate"><span class="pre">Character</span></code> class to avoid moving when sitting down.</p></li>
|
||||
<li><p>We made a new <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> typeclass</p></li>
|
||||
<li><p>We tried two ways to allow a user to interact with sittables using <code class="docutils literal notranslate"><span class="pre">sit</span></code> and <code class="docutils literal notranslate"><span class="pre">stand</span></code> commands.</p></li>
|
||||
</ul>
|
||||
<p>Eagle-eyed readers will notice that the <code class="docutils literal notranslate"><span class="pre">stand</span></code> command sitting “on” the chair (variant 1) would work just fine
|
||||
together with the <code class="docutils literal notranslate"><span class="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><a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">prev lesson</span></a> | <a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">next lesson</span></a></p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<p><h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Making a sittable object</a><ul>
|
||||
<li><a class="reference internal" href="#dont-move-us-when-resting">Don’t move us when resting</a></li>
|
||||
<li><a class="reference internal" href="#making-the-chair-itself">Making the Chair itself</a></li>
|
||||
<li><a class="reference internal" href="#command-variant-1-commands-on-the-chair">Command variant 1: Commands on the chair</a></li>
|
||||
<li><a class="reference internal" href="#command-variant-2-command-on-character">Command variant 2: Command on Character</a></li>
|
||||
<li><a class="reference internal" href="#conclusions">Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Turn-based-Combat-System.html"
|
||||
title="previous chapter">Turn based Combat System</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/A-Sittable-Object.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="A-Sittable-Object.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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Part4/Beginner-Tutorial-Part4-Intro.html" title="Part 4: Using what we created"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Turn-based-Combat-System.html" title="Turn based Combat System"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Making a sittable object</a></li>
|
||||
</ul>
|
||||
<div class="develop">develop branch</div>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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 — 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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</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="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 Evennia’s
|
||||
<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 don’t want to code this separately for every class but we no longer have a common parent
|
||||
class to put it on. So instead we’ll 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#evennia-contrib-tutorials-evadventure-characters"><span class="std std-ref">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>. It’s 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>Let’s 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">""" </span>
|
||||
<span class="sd"> Heal hp amount of health, not allowing to exceed our max hp</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</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">"You heal for </span><span class="si">{healed}</span><span class="s2"> HP."</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">"""When paying coins, make sure to never detract more than we have"""</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">"""Called when attacked and taking damage."""</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">"""Called when defeated. By default this means death."""</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">"""Called when this thing dies."""</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">"""Called when looting another entity"""</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">"""Called when looted by another entity"""</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">"1d10"</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">""" </span>
|
||||
<span class="sd"> A character to use for EvAdventure. </span>
|
||||
<span class="sd"> """</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">"""Characters roll on the death table"""</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">"$You() $conj(collapse) in a heap, alive but beaten."</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">"""We rolled 'dead' on the death table."""</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">"$You() collapse in a heap, embraced by death."</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("strength")</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">"$You() $conj(collapse) in a heap, alive but beaten."</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">"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."</span></code></p></li>
|
||||
<li><p>Others in the room will see: <code class="docutils literal notranslate"><span class="pre">"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."</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 didn’t 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 let’s 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">"1d8"</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">"dead"</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"># <------ 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"><</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"># <------- 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">"evadventure.characters.EvAdventureCharacter"</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 doesn’t 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">"testchar"</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'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'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> doesn’t have any D&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 you’d
|
||||
add these functions.</p>
|
||||
<p>In the framework we have sketched out for <em>Knave</em>, it would be simple - you’d 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">"Fighter"</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">"Human"</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>We’d 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 haven’t 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. That’s 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 class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<p><h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</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">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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 — 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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</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="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 we’ll just keep in mind
|
||||
that’s how we’ll 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 you’ll 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 'STR INT'. 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 you’ll 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#evennia-contrib-tutorials-evadventure-random-tables"><span class="std std-ref">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 haven’t 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">"physique"</span><span class="p">:</span> <span class="p">[</span>
|
||||
<span class="s2">"athletic"</span><span class="p">,</span> <span class="s2">"brawny"</span><span class="p">,</span> <span class="s2">"corpulent"</span><span class="p">,</span> <span class="s2">"delicate"</span><span class="p">,</span> <span class="s2">"gaunt"</span><span class="p">,</span> <span class="s2">"hulking"</span><span class="p">,</span> <span class="s2">"lanky"</span><span class="p">,</span>
|
||||
<span class="s2">"ripped"</span><span class="p">,</span> <span class="s2">"rugged"</span><span class="p">,</span> <span class="s2">"scrawny"</span><span class="p">,</span> <span class="s2">"short"</span><span class="p">,</span> <span class="s2">"sinewy"</span><span class="p">,</span> <span class="s2">"slender"</span><span class="p">,</span> <span class="s2">"flabby"</span><span class="p">,</span>
|
||||
<span class="s2">"statuesque"</span><span class="p">,</span> <span class="s2">"stout"</span><span class="p">,</span> <span class="s2">"tiny"</span><span class="p">,</span> <span class="s2">"towering"</span><span class="p">,</span> <span class="s2">"willowy"</span><span class="p">,</span> <span class="s2">"wiry"</span><span class="p">,</span>
|
||||
<span class="p">],</span>
|
||||
<span class="s2">"face"</span><span class="p">:</span> <span class="p">[</span>
|
||||
<span class="s2">"bloated"</span><span class="p">,</span> <span class="s2">"blunt"</span><span class="p">,</span> <span class="s2">"bony"</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 we’ll 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#evennia-contrib-tutorials-evadventure-chargen"><span class="std std-ref">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">"1d6"</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">"1d6"</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">"1d6"</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">"1d282"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"name"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"physique"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"face"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"skin"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"hair"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"clothing"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"speech"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"virtue"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"vice"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"background"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"misfortune"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"alignment"</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">"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,"</span>
|
||||
<span class="sa">f</span><span class="s2">" 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"</span>
|
||||
<span class="sa">f</span><span class="s2">" </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"</span>
|
||||
<span class="sa">f</span><span class="s2">" </span><span class="si">{</span><span class="n">alignment</span><span class="si">}</span><span class="s2"> alignment."</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">"1d8"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"armor"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"helmets and shields"</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">"helmet"</span> <span class="k">if</span> <span class="s2">"helmet"</span> <span class="ow">in</span> <span class="n">_helmet_and_shield</span> <span class="k">else</span> <span class="s2">"none"</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">shield</span> <span class="o">=</span> <span class="s2">"shield"</span> <span class="k">if</span> <span class="s2">"shield"</span> <span class="ow">in</span> <span class="n">_helmet_and_shield</span> <span class="k">else</span> <span class="s2">"none"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"starting weapon"</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">"ration"</span><span class="p">,</span>
|
||||
<span class="s2">"ration"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"dungeoning gear"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"dungeoning gear"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"general gear 1"</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">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"general gear 2"</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">"""</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">"""</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">", "</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">"strength"</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">"dexterity"</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">"constitution"</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">"intelligence"</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">"wisdom"</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">"charisma"</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">"hp"</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">"hp_max"</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">"desc"</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
|
||||
it’s 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 haven’t actually created those prototypes yet, so for now we’ll 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">"""</span>
|
||||
<span class="sd"> This is a start point for spinning up the chargen from a command later.</span>
|
||||
|
||||
<span class="sd"> """</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 we’ll 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">"tmp_character"</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">"desc"</span><span class="p">:</span> <span class="s2">"Change your name"</span><span class="p">,</span>
|
||||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"node_change_name"</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"><=</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">"desc"</span><span class="p">:</span> <span class="s2">"Swap two of your ability scores (once)"</span><span class="p">,</span>
|
||||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"node_swap_abilities"</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">"desc"</span><span class="p">:</span> <span class="s2">"Accept and create character"</span><span class="p">,</span>
|
||||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"node_apply_character"</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, it’s 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">"key"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"name"</span><span class="o">.</span> <span class="s2">"alias1"</span><span class="p">,</span> <span class="s2">"alias2"</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">"desc"</span><span class="p">:</span> <span class="s2">"text to describe what happens when selecting option"</span><span class="p">,</span><span class="o">.</span>
|
||||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"name of node or a callable"</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 haven’t 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 it’s 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">"""</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"> """</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">"tmp_character"</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">"node_chargen"</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">"""</span>
|
||||
<span class="sd"> Change the random name of the character.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"tmp_character"</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">"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. "</span>
|
||||
<span class="s2">"Enter a new name or leave empty to abort."</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="n">options</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="s2">"_default"</span><span class="p">,</span>
|
||||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_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 user’s 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. It’s 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 don’t
|
||||
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">"STR"</span><span class="p">:</span> <span class="s2">"strength"</span><span class="p">,</span>
|
||||
<span class="s2">"DEX"</span><span class="p">:</span> <span class="s2">"dexterity"</span><span class="p">,</span>
|
||||
<span class="s2">"CON"</span><span class="p">:</span> <span class="s2">"constitution"</span><span class="p">,</span>
|
||||
<span class="s2">"INT"</span><span class="p">:</span> <span class="s2">"intelligence"</span><span class="p">,</span>
|
||||
<span class="s2">"WIS"</span><span class="p">:</span> <span class="s2">"wisdom"</span><span class="p">,</span>
|
||||
<span class="s2">"CHA"</span><span class="p">:</span> <span class="s2">"charisma"</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">"""</span>
|
||||
<span class="sd"> Used by node_swap_abilities to parse the user's input and swap ability</span>
|
||||
<span class="sd"> values.</span>
|
||||
|
||||
<span class="sd"> """</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">" "</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">"That doesn't look right."</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">"Not a familiar set of abilites."</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">"tmp_character"</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">"node_chargen"</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">""" </span>
|
||||
<span class="sd"> One is allowed to swap the values of two abilities around, once.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"tmp_character"</span><span class="p">]</span>
|
||||
|
||||
<span class="n">text</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"""</span>
|
||||
<span class="s2">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">"""</span>
|
||||
|
||||
<span class="n">options</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"_default"</span><span class="p">,</span> <span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_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 didn’t 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 (it’s 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">""" </span>
|
||||
<span class="sd"> End chargen and create the character. We will also puppet it.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"tmp_character"</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">"Character created!"</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">"""</span>
|
||||
<span class="sd">This is a start point for spinning up the chargen from a command later.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">menutree</span> <span class="o">=</span> <span class="p">{</span> <span class="c1"># <----- can now add this!</span>
|
||||
<span class="s2">"node_chargen"</span><span class="p">:</span> <span class="n">node_chargen</span><span class="p">,</span>
|
||||
<span class="s2">"node_change_name"</span><span class="p">:</span> <span class="n">node_change_name</span><span class="p">,</span>
|
||||
<span class="s2">"node_swap_abilities"</span><span class="p">:</span> <span class="n">node_swap_abilities</span><span class="p">,</span>
|
||||
<span class="s2">"node_apply_character"</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">"node_chargen"</span><span class="p">,</span> <span class="c1"># <----- </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 we’ll address how EvAdventure <em>Rooms</em> work.</p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<p><h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</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">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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 — 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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</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="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 class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</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">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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 — 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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</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="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 class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</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">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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 — 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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</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="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 can’t 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">"backpack"</span>
|
||||
<span class="n">WEAPON_HAND</span> <span class="o">=</span> <span class="s2">"weapon_hand"</span>
|
||||
<span class="n">SHIELD_HAND</span> <span class="o">=</span> <span class="s2">"shield_hand"</span>
|
||||
<span class="n">TWO_HANDS</span> <span class="o">=</span> <span class="s2">"two_handed_weapons"</span>
|
||||
<span class="n">BODY</span> <span class="o">=</span> <span class="s2">"body"</span> <span class="c1"># armor</span>
|
||||
<span class="n">HEAD</span> <span class="o">=</span> <span class="s2">"head"</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">"inventory_slots"</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">"""Load our data from an Attribute on `self.obj`"""</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">"inventory"</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">"""Save our data back to the same Attribute"""</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">"inventory"</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">@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 it’s 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, we’ll
|
||||
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">"""Called by Evennia before object arrives 'in' 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"> """</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">""" </span>
|
||||
<span class="sd"> Called by Evennia when an object arrives 'in' the character.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</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">""" </span>
|
||||
<span class="sd"> Called by Evennia when object leaves the Character. </span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</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 haven’t 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>Let’s 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">"""All types of equipment-errors"""</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">"""Max amount of slots, based on CON defense (CON + 10)"""</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">"""Count current slot usage"""</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">"size"</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">"size"</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">"""</span>
|
||||
<span class="sd"> Check if obj can fit in equipment, based on its size.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</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">"</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."</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"><=</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">@property</span></code> decorator turns a method into a property so you don’t 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, it’s 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. Let’s 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 don’t 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">"Constitution"</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 > 5] # [6, 7, 8, 9]
|
||||
all_below_5 = [num for num in range(10) if num < 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">"size"</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">"size"</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">"""</span>
|
||||
<span class="sd"> Put something in the backpack.</span>
|
||||
<span class="sd"> """</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">"""</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"> """</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">"""Move object from backpack to its intended `inventory_use_slot`."""</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">"inventory_use_slot"</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'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'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'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">"""</span>
|
||||
<span class="sd"> Get all objects in inventory, regardless of location.</span>
|
||||
<span class="sd"> """</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>It’s 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 you’d 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">"armor"</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">"armor"</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">"armor"</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#evennia-contrib-tutorials-evadventure-equipment"><span class="std std-ref">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#evennia-contrib-tutorials-evadventure-tests-test-equipment"><span class="std std-ref">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 handler’s 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">'testchar'</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">"helmet"</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">"weapon"</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 shouldn’t 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 class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<p><h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</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">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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) — 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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</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="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 class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</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">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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 — 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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</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="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 character’s 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 can’t 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, let’s 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">"backpack"</span>
|
||||
<span class="n">WEAPON_HAND</span> <span class="o">=</span> <span class="s2">"weapon_hand"</span>
|
||||
<span class="n">SHIELD_HAND</span> <span class="o">=</span> <span class="s2">"shield_hand"</span>
|
||||
<span class="n">TWO_HANDS</span> <span class="o">=</span> <span class="s2">"two_handed_weapons"</span>
|
||||
<span class="n">BODY</span> <span class="o">=</span> <span class="s2">"body"</span> <span class="c1"># armor</span>
|
||||
<span class="n">HEAD</span> <span class="o">=</span> <span class="s2">"head"</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">"weapon"</span>
|
||||
<span class="n">ARMOR</span> <span class="o">=</span> <span class="s2">"armor"</span>
|
||||
<span class="n">SHIELD</span> <span class="o">=</span> <span class="s2">"shield"</span>
|
||||
<span class="n">HELMET</span> <span class="o">=</span> <span class="s2">"helmet"</span>
|
||||
<span class="n">CONSUMABLE</span> <span class="o">=</span> <span class="s2">"consumable"</span>
|
||||
<span class="n">GEAR</span> <span class="o">=</span> <span class="s2">"gear"</span>
|
||||
<span class="n">MAGIC</span> <span class="o">=</span> <span class="s2">"magic"</span>
|
||||
<span class="n">QUEST</span> <span class="o">=</span> <span class="s2">"quest"</span>
|
||||
<span class="n">TREASURE</span> <span class="o">=</span> <span class="s2">"treasure"</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#evennia-contrib-tutorials-evadventure-objects"><span class="std std-ref">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 Evennia’s 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">""" </span>
|
||||
<span class="sd"> Base for all evadventure objects. </span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</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">"""Called when this object is first created. We convert the .obj_type </span>
|
||||
<span class="sd"> property to a database tag."""</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">"obj_type"</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">"""Get any help text for this item"""</span>
|
||||
<span class="k">return</span> <span class="s2">"No help for this item"</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 can’t 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 can’t 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("size")</span></code> <em>unless you change its default</em>. You also can’t 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, we’ll 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 Evennia’s 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">"obj_type"</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 don’t 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">"""Quest objects should usually not be possible to sell or trade."""</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">"""Treasure is usually just for selling for coin"""</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 can’t 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">"""An item that can be used up"""</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">"""Called before using. If returning False, abort use."""</span>
|
||||
<span class="k">return</span> <span class="n">uses</span> <span class="o">></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">"""Called when using the item"""</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">"""Called after using the item"""</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"><=</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">"</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."</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">"""Base class for all weapons"""</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">"1d6"</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 weapon’s 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">"""Base for all magical rune stones"""</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">"1d8"</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">"""Called after usage/spell was cast"""</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'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">"""Refresh the rune stone (normally after rest)"""</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 don’t have to add <code class="docutils literal notranslate"><span class="pre">.uses</span></code>
|
||||
again, it’s 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 don’t 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 don’t 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">"Empty Fists"</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">"1d4"</span>
|
||||
<span class="n">quality</span> <span class="o">=</span> <span class="mi">100000</span> <span class="c1"># let'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">"<WeaponEmptyHand>"</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 didn’t 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 class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<p><h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</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">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -17,8 +17,8 @@
|
|||
<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="Implementing a game rule system" href="Implementing-a-game-rule-system.html" />
|
||||
<link rel="prev" title="Planning our tutorial game" href="../Part2/Planning-The-Tutorial-Game.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>
|
||||
|
|
@ -30,10 +30,10 @@
|
|||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Implementing-a-game-rule-system.html" title="Implementing a game rule system"
|
||||
<a href="Beginner-Tutorial-Utilities.html" title="Code structure and Utilities"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Part2/Planning-The-Tutorial-Game.html" title="Planning our tutorial game"
|
||||
<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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
|
|
@ -50,6 +50,12 @@
|
|||
|
||||
<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 don’t expect out-of-the-box perfection
|
||||
from it at this time.</p>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Beginner Tutorial Parts</p>
|
||||
<dl class="simple">
|
||||
|
|
@ -67,42 +73,145 @@
|
|||
</dd>
|
||||
</dl>
|
||||
</aside>
|
||||
<p>In part three of the Evennia Beginner tutorial we will go through the creation of several key parts of our tutorial
|
||||
game <em>EvAdventure</em>. This is a pretty big part with plenty of examples.</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 want.
|
||||
Even if this is not the game-style you are interested in, following along will give you a lot of experience
|
||||
with using Evennia. This be of much use when doing your own thing later.</p>
|
||||
<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. You’ll 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#evennia-contrib-tutorials-evadventure"><span class="std std-ref">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>
|
||||
<p><em>TODO</em></p>
|
||||
<div class="toctree-wrapper compound">
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Implementing-a-game-rule-system.html">Implementing a game rule system</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Turn-based-Combat-System.html">Turn based Combat System</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="A-Sittable-Object.html">Making a sittable object</a></li>
|
||||
<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>
|
||||
<ol class="simple">
|
||||
<li><p><a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">Changing settings</span></a></p></li>
|
||||
<li><p><a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">Applying contribs</span></a></p></li>
|
||||
<li><p><a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">Creating a rule module</span></a></p></li>
|
||||
<li><p><a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">Tweaking the base Typeclasses</span></a></p></li>
|
||||
<li><p><a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">Character creation menu</span></a></p></li>
|
||||
<li><p><a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">Wearing armor and wielding weapons</span></a></p></li>
|
||||
<li><p><a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">Two types of combat</span></a></p></li>
|
||||
<li><p><a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">Monsters and AI</span></a></p></li>
|
||||
<li><p><a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">Questing and rewards</span></a></p></li>
|
||||
<li><p><a class="reference internal" href="../../../Unimplemented.html"><span class="doc std std-doc">Overview of Tech demo</span></a></p></li>
|
||||
</ol>
|
||||
</section>
|
||||
<section id="table-of-contents">
|
||||
<h2>Table of Contents<a class="headerlink" href="#table-of-contents" title="Permalink to this headline">¶</a></h2>
|
||||
<p><em>TODO</em></p>
|
||||
<div class="toctree-wrapper compound">
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Implementing-a-game-rule-system.html">Implementing a game rule system</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="A-Sittable-Object.html">Making a sittable object</a></li>
|
||||
<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>
|
||||
|
|
@ -138,11 +247,11 @@ with using Evennia. This be of much use when doing your own thing later.</p>
|
|||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="../Part2/Planning-The-Tutorial-Game.html"
|
||||
<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="Implementing-a-game-rule-system.html"
|
||||
title="next chapter">Implementing a game rule system</a></p>
|
||||
<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">
|
||||
|
|
@ -180,10 +289,10 @@ with using Evennia. This be of much use when doing your own thing later.</p>
|
|||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Implementing-a-game-rule-system.html" title="Implementing a game rule system"
|
||||
<a href="Beginner-Tutorial-Utilities.html" title="Code structure and Utilities"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Part2/Planning-The-Tutorial-Game.html" title="Planning our tutorial game"
|
||||
<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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
|
|
|
|||
|
|
@ -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 — 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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</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="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 class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</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">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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 — 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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</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="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 class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</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">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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 — 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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</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="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 it’s okay to share and
|
||||
adapt <em>Knave</em> for any purpose, even commercially. If you don’t 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 & 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>, you’d 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 enemy’s 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"><level>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#evennia-contrib-tutorials-evadventure-rules"><span class="std std-ref">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 we’d 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. Let’s 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'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 it’s 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">"1d8"</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("1d20")</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">""" </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"> """</span>
|
||||
|
||||
<span class="c1"># split the XdY input on the 'd' 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">"d"</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.
|
||||
We’ll 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 don’t really care what this value is - we just want to repeat the loop
|
||||
a certain amount of times.</p>
|
||||
</aside>
|
||||
<p>We don’t 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">"1d20"</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">"1d20"</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">"1d20"</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">"1d20"</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">"1d20"</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">"strength"</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, we’ll 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">""" </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"> """</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">></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">"2d6"</span><span class="p">)</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="s2">"morale"</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 night’s 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">""" </span>
|
||||
<span class="sd"> A night's rest retains 1d8 + CON HP </span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</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">"1d8"</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">"item1"</span><span class="p">,</span> <span class="s2">"item2"</span><span class="p">,</span> <span class="s2">"item3"</span><span class="p">,</span> <span class="s2">"item4"</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">"1-5"</span><span class="p">,</span> <span class="s2">"item1"</span><span class="p">),</span> <span class="p">(</span><span class="s2">"6-15"</span><span class="p">,</span> <span class="s2">"item2"</span><span class="p">),</span> <span class="p">(</span><span class="s2">"16-19"</span><span class="p">,</span> <span class="s2">"item4"</span><span class="p">),</span> <span class="p">(</span><span class="s2">"20"</span><span class="p">,</span> <span class="s2">"item5"</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">""" </span>
|
||||
<span class="sd"> Args: </span>
|
||||
<span class="sd"> dieroll (str): A die roll string, like "1d20".</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"> """</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 [("1-5", "item"),...]</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">"-"</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"><=</span> <span class="n">roll_result</span> <span class="o"><=</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">"roll_random_table: Invalid die roll"</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">"-"</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("-",</span> <span class="pre">1)</span></code> would result in a tuple <code class="docutils literal notranslate"><span class="pre">("1",</span> <span class="pre">"5")</span></code>.
|
||||
But if the string was in fact just <code class="docutils literal notranslate"><span class="pre">"20"</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">("1",</span> <span class="pre">("5",))</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">("20",</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">("5",)</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">"5"</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” Knave’s 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 don’t 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">"1-2"</span><span class="p">,</span> <span class="s2">"dead"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"3"</span><span class="p">:</span> <span class="s2">"strength"</span><span class="p">,</span>
|
||||
<span class="p">(</span><span class="s2">"4"</span><span class="p">:</span> <span class="s2">"dexterity"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"5"</span><span class="p">:</span> <span class="s2">"constitution"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"6"</span><span class="p">:</span> <span class="s2">"intelligence"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"7"</span><span class="p">:</span> <span class="s2">"wisdom"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"8"</span><span class="p">:</span> <span class="s2">"charisma"</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">"1d8"</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">"dead"</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">"1d4"</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"><</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">"1d4"</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">"You survive your brush with death, and while you recover "</span>
|
||||
<span class="sa">f</span><span class="s2">"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."</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 don’t 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">"""Called before every test method"""</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">"evadventure.rules.randint"</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">"1d6"</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">"2d6"</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#evennia-contrib-tutorials-evadventure-tests-test-rules"><span class="std std-ref">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. That’s very hard to
|
||||
test for, since the value will be different every test.</p>
|
||||
<p>With <code class="docutils literal notranslate"><span class="pre">@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 class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<p><h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</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">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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 — 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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</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="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 class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</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">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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 — 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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</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="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"><---</span>
|
||||
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span> <span class="o"><---</span>
|
||||
<span class="n">tests</span><span class="o">/</span> <span class="o"><---</span>
|
||||
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span> <span class="o"><---</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#evennia-contrib-tutorials-evadventure-enums"><span class="std std-ref">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">"strength"</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 "strength"</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">"constitution"</span></code>. If you mis-spell
|
||||
this (<code class="docutils literal notranslate"><span class="pre">"consitution"</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"><do</span> <span class="pre">stuff></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.
|
||||
It’s 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 you’ll
|
||||
likely gradually expand on your enums as you figure out what you’ll 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">"""</span>
|
||||
<span class="sd"> The six base ability-bonuses and other </span>
|
||||
<span class="sd"> abilities</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="n">STR</span> <span class="o">=</span> <span class="s2">"strength"</span>
|
||||
<span class="n">DEX</span> <span class="o">=</span> <span class="s2">"dexterity"</span>
|
||||
<span class="n">CON</span> <span class="o">=</span> <span class="s2">"constitution"</span>
|
||||
<span class="n">INT</span> <span class="o">=</span> <span class="s2">"intelligence"</span>
|
||||
<span class="n">WIS</span> <span class="o">=</span> <span class="s2">"wisdom"</span>
|
||||
<span class="n">CHA</span> <span class="o">=</span> <span class="s2">"charisma"</span>
|
||||
|
||||
<span class="n">ARMOR</span> <span class="o">=</span> <span class="s2">"armor"</span>
|
||||
|
||||
<span class="n">CRITICAL_FAILURE</span> <span class="o">=</span> <span class="s2">"critical_failure"</span>
|
||||
<span class="n">CRITICAL_SUCCESS</span> <span class="o">=</span> <span class="s2">"critical_success"</span>
|
||||
|
||||
<span class="n">ALLEGIANCE_HOSTILE</span> <span class="o">=</span> <span class="s2">"hostile"</span>
|
||||
<span class="n">ALLEGIANCE_NEUTRAL</span> <span class="o">=</span> <span class="s2">"neutral"</span>
|
||||
<span class="n">ALLEGIANCE_FRIENDLY</span> <span class="o">=</span> <span class="s2">"friendly"</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#evennia-contrib-tutorials-evadventure-utils"><span class="std std-ref">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>Here’s 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">"""</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">"""</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">""" </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"> """</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">"[Not carried]"</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">"infinite"</span>
|
||||
<span class="n">use_slot_name</span><span class="o">=</span><span class="s2">"backpack"</span><span class="p">,</span>
|
||||
<span class="n">attack_type_name</span><span class="o">=</span><span class="s2">"strength"</span>
|
||||
<span class="n">defense_type_name</span><span class="o">=</span><span class="s2">"armor"</span>
|
||||
<span class="n">damage_roll</span><span class="o">=</span><span class="s2">"1d6"</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. We’ll 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>It’s useful for any game dev to know how to effectively test their code. So we’ll 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 you’ll 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 you’ll 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 you’ve written your test you
|
||||
can run it over and over and make sure later changes to your code didn’t 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. Here’s 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">"testobj"</span><span class="p">,</span>
|
||||
<span class="n">attributes</span><span class="o">=</span><span class="p">((</span><span class="s2">"desc"</span><span class="p">,</span> <span class="s2">"A test object"</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">""" </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">"""</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 doesn’t 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>It’s very important to understand how you import code between modules in Python, so if this is still
|
||||
confusing to you, it’s 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 class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<p><h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</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">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,408 +0,0 @@
|
|||
|
||||
<!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>Implementing a game rule system — 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="Turn based Combat System" href="Turn-based-Combat-System.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="Turn-based-Combat-System.html" title="Turn based Combat System"
|
||||
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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Implementing a game rule system</a></li>
|
||||
</ul>
|
||||
<div class="develop">develop branch</div>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="implementing-a-game-rule-system">
|
||||
<h1>Implementing a game rule system<a class="headerlink" href="#implementing-a-game-rule-system" title="Permalink to this headline">¶</a></h1>
|
||||
<p>The simplest way to create an online roleplaying game (at least from a code perspective) is to
|
||||
simply grab a paperback RPG rule book, get a staff of game masters together and start to run scenes
|
||||
with whomever logs in. Game masters can roll their dice in front of their computers and tell the
|
||||
players the results. This is only one step away from a traditional tabletop game and puts heavy
|
||||
demands on the staff - it is unlikely staff will be able to keep up around the clock even if they
|
||||
are very dedicated.</p>
|
||||
<p>Many games, even the most roleplay-dedicated, thus tend to allow for players to mediate themselves
|
||||
to some extent. A common way to do this is to introduce <em>coded systems</em> - that is, to let the
|
||||
computer do some of the heavy lifting. A basic thing is to add an online dice-roller so everyone can
|
||||
make rolls and make sure noone is cheating. Somewhere at this level you find the most bare-bones
|
||||
roleplaying MUSHes.</p>
|
||||
<p>The advantage of a coded system is that as long as the rules are fair the computer is too - it makes
|
||||
no judgement calls and holds no personal grudges (and cannot be accused of holding any). Also, the
|
||||
computer doesn’t need to sleep and can always be online regardless of when a player logs on. The
|
||||
drawback is that a coded system is not flexible and won’t adapt to the unprogrammed actions human
|
||||
players may come up with in role play. For this reason many roleplay-heavy MUDs do a hybrid
|
||||
variation - they use coded systems for things like combat and skill progression but leave role play
|
||||
to be mostly freeform, overseen by staff game masters.</p>
|
||||
<p>Finally, on the other end of the scale are less- or no-roleplay games, where game mechanics (and
|
||||
thus player fairness) is the most important aspect. In such games the only events with in-game value
|
||||
are those resulting from code. Such games are very common and include everything from hack-and-slash
|
||||
MUDs to various tactical simulations.</p>
|
||||
<p>So your first decision needs to be just what type of system you are aiming for. This page will try
|
||||
to give some ideas for how to organize the “coded” part of your system, however big that may be.</p>
|
||||
<section id="overall-system-infrastructure">
|
||||
<h2>Overall system infrastructure<a class="headerlink" href="#overall-system-infrastructure" title="Permalink to this headline">¶</a></h2>
|
||||
<p>We strongly recommend that you code your rule system as stand-alone as possible. That is, don’t
|
||||
spread your skill check code, race bonus calculation, die modifiers or what have you all over your
|
||||
game.</p>
|
||||
<ul>
|
||||
<li><p>Put everything you would need to look up in a rule book into a module in <code class="docutils literal notranslate"><span class="pre">mygame/world</span></code>. Hide away
|
||||
as much as you can. Think of it as a black box (or maybe the code representation of an all-knowing
|
||||
game master). The rest of your game will ask this black box questions and get answers back. Exactly
|
||||
how it arrives at those results should not need to be known outside the box. Doing it this way
|
||||
makes it easier to change and update things in one place later.</p></li>
|
||||
<li><p>Store only the minimum stuff you need with each game object. That is, if your Characters need
|
||||
values for Health, a list of skills etc, store those things on the Character - don’t store how to
|
||||
roll or change them.</p></li>
|
||||
<li><p>Next is to determine just how you want to store things on your Objects and Characters. You can
|
||||
choose to either store things as individual <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attributes</span></a>, like <code class="docutils literal notranslate"><span class="pre">character.db.STR=34</span></code> and
|
||||
<code class="docutils literal notranslate"><span class="pre">character.db.Hunting_skill=20</span></code>. But you could also use some custom storage method, like a
|
||||
dictionary <code class="docutils literal notranslate"><span class="pre">character.db.skills</span> <span class="pre">=</span> <span class="pre">{"Hunting":34,</span> <span class="pre">"Fishing":20,</span> <span class="pre">...}</span></code>. A much more fancy solution is
|
||||
to look at the Ainneve <a class="reference external" href="https://github.com/evennia/ainneve/blob/master/world/traits.py">Trait
|
||||
handler</a>. Finally you could even go
|
||||
with a <a class="reference internal" href="../../../Concepts/New-Models.html"><span class="doc std std-doc">custom django model</span></a>. Which is the better depends on your game and the
|
||||
complexity of your system.</p></li>
|
||||
<li><p>Make a clear <a class="reference external" href="https://en.wikipedia.org/wiki/Application_programming_interface">API</a> into your
|
||||
rules. That is, make methods/functions that you feed with, say, your Character and which skill you
|
||||
want to check. That is, you want something similar to this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="kn">from</span> <span class="nn">world</span> <span class="kn">import</span> <span class="n">rules</span>
|
||||
<span class="n">result</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">roll_skill</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="s2">"hunting"</span><span class="p">)</span>
|
||||
<span class="n">result</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">roll_challenge</span><span class="p">(</span><span class="n">character1</span><span class="p">,</span> <span class="n">character2</span><span class="p">,</span> <span class="s2">"swords"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<p>You might need to make these functions more or less complex depending on your game. For example the
|
||||
properties of the room might matter to the outcome of a roll (if the room is dark, burning etc).
|
||||
Establishing just what you need to send into your game mechanic module is a great way to also get a
|
||||
feel for what you need to add to your engine.</p>
|
||||
</section>
|
||||
<section id="coded-systems">
|
||||
<h2>Coded systems<a class="headerlink" href="#coded-systems" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Inspired by tabletop role playing games, most game systems mimic some sort of die mechanic. To this
|
||||
end Evennia offers a full <a class="reference external" href="https://github.com/evennia/evennia/blob/master/evennia/contrib/dice.py">dice
|
||||
roller</a> in its <code class="docutils literal notranslate"><span class="pre">contrib</span></code>
|
||||
folder. For custom implementations, Python offers many ways to randomize a result using its in-built
|
||||
<code class="docutils literal notranslate"><span class="pre">random</span></code> module. No matter how it’s implemented, we will in this text refer to the action of
|
||||
determining an outcome as a “roll”.</p>
|
||||
<p>In a freeform system, the result of the roll is just compared with values and people (or the game
|
||||
master) just agree on what it means. In a coded system the result now needs to be processed somehow.
|
||||
There are many things that may happen as a result of rule enforcement:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Health may be added or deducted. This can effect the character in various ways.</p></li>
|
||||
<li><p>Experience may need to be added, and if a level-based system is used, the player might need to be
|
||||
informed they have increased a level.</p></li>
|
||||
<li><p>Room-wide effects need to be reported to the room, possibly affecting everyone in the room.</p></li>
|
||||
</ul>
|
||||
<p>There are also a slew of other things that fall under “Coded systems”, including things like
|
||||
weather, NPC artificial intelligence and game economy. Basically everything about the world that a
|
||||
Game master would control in a tabletop role playing game can be mimicked to some level by coded
|
||||
systems.</p>
|
||||
</section>
|
||||
<section id="example-of-rule-module">
|
||||
<h2>Example of Rule module<a class="headerlink" href="#example-of-rule-module" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Here is a simple example of a rule module. This is what we assume about our simple example game:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Characters have only four numerical values:</p>
|
||||
<ul>
|
||||
<li><p>Their <code class="docutils literal notranslate"><span class="pre">level</span></code>, which starts at 1.</p></li>
|
||||
<li><p>A skill <code class="docutils literal notranslate"><span class="pre">combat</span></code>, which determines how good they are at hitting things. Starts between 5 and</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ol class="simple">
|
||||
<li><ul class="simple">
|
||||
<li><p>Their Strength, <code class="docutils literal notranslate"><span class="pre">STR</span></code>, which determine how much damage they do. Starts between 1 and 10.</p></li>
|
||||
<li><p>Their Health points, <code class="docutils literal notranslate"><span class="pre">HP</span></code>, which starts at 100.</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<ul class="simple">
|
||||
<li><p>When a Character reaches <code class="docutils literal notranslate"><span class="pre">HP</span> <span class="pre">=</span> <span class="pre">0</span></code>, they are presumed “defeated”. Their HP is reset and they get a
|
||||
failure message (as a stand-in for death code).</p></li>
|
||||
<li><p>Abilities are stored as simple Attributes on the Character.</p></li>
|
||||
<li><p>“Rolls” are done by rolling a 100-sided die. If the result is below the <code class="docutils literal notranslate"><span class="pre">combat</span></code> value, it’s a
|
||||
success and damage is rolled. Damage is rolled as a six-sided die + the value of <code class="docutils literal notranslate"><span class="pre">STR</span></code> (for this
|
||||
example we ignore weapons and assume <code class="docutils literal notranslate"><span class="pre">STR</span></code> is all that matters).</p></li>
|
||||
<li><p>Every successful <code class="docutils literal notranslate"><span class="pre">attack</span></code> roll gives 1-3 experience points (<code class="docutils literal notranslate"><span class="pre">XP</span></code>). Every time the number of <code class="docutils literal notranslate"><span class="pre">XP</span></code>
|
||||
reaches <code class="docutils literal notranslate"><span class="pre">(level</span> <span class="pre">+</span> <span class="pre">1)</span> <span class="pre">**</span> <span class="pre">2</span></code>, the Character levels up. When leveling up, the Character’s <code class="docutils literal notranslate"><span class="pre">combat</span></code>
|
||||
value goes up by 2 points and <code class="docutils literal notranslate"><span class="pre">STR</span></code> by one (this is a stand-in for a real progression system).</p></li>
|
||||
</ul>
|
||||
<section id="character">
|
||||
<h3>Character<a class="headerlink" href="#character" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The Character typeclass is simple. It goes in <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/characters.py</span></code>. There is already
|
||||
an empty <code class="docutils literal notranslate"><span class="pre">Character</span></code> class there that Evennia will look to and use.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</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">Character</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Custom rule-restricted character. We randomize</span>
|
||||
<span class="sd"> the initial skill and ability values bettween 1-10.</span>
|
||||
<span class="sd"> """</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="s2">"Called only when first created"</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">level</span> <span class="o">=</span> <span class="mi">1</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">HP</span> <span class="o">=</span> <span class="mi">100</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</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">db</span><span class="o">.</span><span class="n">STR</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="mi">10</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span> <span class="o">=</span> <span class="n">randint</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">@reload</span></code> the server to load up the new code. Doing <code class="docutils literal notranslate"><span class="pre">examine</span> <span class="pre">self</span></code> will however <em>not</em> show the new
|
||||
Attributes on yourself. This is because the <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> hook is only called on <em>new</em>
|
||||
Characters. Your Character was already created and will thus not have them. To force a reload, use
|
||||
the following command:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@typeclass</span><span class="o">/</span><span class="n">force</span><span class="o">/</span><span class="n">reset</span> <span class="bp">self</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">examine</span> <span class="pre">self</span></code> command will now show the new Attributes.</p>
|
||||
</section>
|
||||
<section id="rule-module">
|
||||
<h3>Rule module<a class="headerlink" href="#rule-module" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This is a module <code class="docutils literal notranslate"><span class="pre">mygame/world/rules.py</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll_hit</span><span class="p">():</span>
|
||||
<span class="s2">"Roll 1d100"</span>
|
||||
<span class="k">return</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll_dmg</span><span class="p">():</span>
|
||||
<span class="s2">"Roll 1d6"</span>
|
||||
<span class="k">return</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">check_defeat</span><span class="p">(</span><span class="n">character</span><span class="p">):</span>
|
||||
<span class="s2">"Checks if a character is 'defeated'."</span>
|
||||
<span class="k">if</span> <span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">HP</span> <span class="o"><=</span> <span class="mi">0</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">"You fall down, defeated!"</span><span class="p">)</span>
|
||||
<span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">HP</span> <span class="o">=</span> <span class="mi">100</span> <span class="c1"># reset</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">add_XP</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="n">amount</span><span class="p">):</span>
|
||||
<span class="s2">"Add XP to character, tracking level increases."</span>
|
||||
<span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">XP</span> <span class="o">+=</span> <span class="n">amount</span>
|
||||
<span class="k">if</span> <span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">XP</span> <span class="o">>=</span> <span class="p">(</span><span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">level</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span><span class="p">:</span>
|
||||
<span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">level</span> <span class="o">+=</span> <span class="mi">1</span>
|
||||
<span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">STR</span> <span class="o">+=</span> <span class="mi">1</span>
|
||||
<span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span> <span class="o">+=</span> <span class="mi">2</span>
|
||||
<span class="n">character</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You are now level </span><span class="si">{</span><span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">level</span><span class="si">}</span><span class="s2">!"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">skill_combat</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> This determines outcome of combat. The one who</span>
|
||||
<span class="sd"> rolls under their combat skill AND higher than</span>
|
||||
<span class="sd"> their opponent's roll hits.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">char1</span><span class="p">,</span> <span class="n">char2</span> <span class="o">=</span> <span class="n">args</span>
|
||||
<span class="n">roll1</span><span class="p">,</span> <span class="n">roll2</span> <span class="o">=</span> <span class="n">roll_hit</span><span class="p">(),</span> <span class="n">roll_hit</span><span class="p">()</span>
|
||||
<span class="n">failtext_template</span> <span class="o">=</span> <span class="s2">"You are hit by </span><span class="si">{attacker}</span><span class="s2"> for </span><span class="si">{dmg}</span><span class="s2"> damage!"</span>
|
||||
<span class="n">wintext_template</span> <span class="o">=</span> <span class="s2">"You hit </span><span class="si">{target}</span><span class="s2"> for </span><span class="si">{dmg}</span><span class="s2"> damage!"</span>
|
||||
<span class="n">xp_gain</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="mi">3</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">char1</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span> <span class="o">>=</span> <span class="n">roll1</span> <span class="o">></span> <span class="n">roll2</span><span class="p">:</span>
|
||||
<span class="c1"># char 1 hits</span>
|
||||
<span class="n">dmg</span> <span class="o">=</span> <span class="n">roll_dmg</span><span class="p">()</span> <span class="o">+</span> <span class="n">char1</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">STR</span>
|
||||
<span class="n">char1</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">wintext_template</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">char2</span><span class="p">,</span> <span class="n">dmg</span><span class="o">=</span><span class="n">dmg</span><span class="p">))</span>
|
||||
<span class="n">add_XP</span><span class="p">(</span><span class="n">char1</span><span class="p">,</span> <span class="n">xp_gain</span><span class="p">)</span>
|
||||
<span class="n">char2</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">failtext_template</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">attacker</span><span class="o">=</span><span class="n">char1</span><span class="p">,</span> <span class="n">dmg</span><span class="o">=</span><span class="n">dmg</span><span class="p">))</span>
|
||||
<span class="n">char2</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">HP</span> <span class="o">-=</span> <span class="n">dmg</span>
|
||||
<span class="n">check_defeat</span><span class="p">(</span><span class="n">char2</span><span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">char2</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span> <span class="o">>=</span> <span class="n">roll2</span> <span class="o">></span> <span class="n">roll1</span><span class="p">:</span>
|
||||
<span class="c1"># char 2 hits</span>
|
||||
<span class="n">dmg</span> <span class="o">=</span> <span class="n">roll_dmg</span><span class="p">()</span> <span class="o">+</span> <span class="n">char2</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">STR</span>
|
||||
<span class="n">char1</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">failtext_template</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">attacker</span><span class="o">=</span><span class="n">char2</span><span class="p">,</span> <span class="n">dmg</span><span class="o">=</span><span class="n">dmg</span><span class="p">))</span>
|
||||
<span class="n">char1</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">HP</span> <span class="o">-=</span> <span class="n">dmg</span>
|
||||
<span class="n">check_defeat</span><span class="p">(</span><span class="n">char1</span><span class="p">)</span>
|
||||
<span class="n">char2</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">wintext_template</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">char1</span><span class="p">,</span> <span class="n">dmg</span><span class="o">=</span><span class="n">dmg</span><span class="p">))</span>
|
||||
<span class="n">add_XP</span><span class="p">(</span><span class="n">char2</span><span class="p">,</span> <span class="n">xp_gain</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># a draw</span>
|
||||
<span class="n">drawtext</span> <span class="o">=</span> <span class="s2">"Neither of you can find an opening."</span>
|
||||
<span class="n">char1</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">drawtext</span><span class="p">)</span>
|
||||
<span class="n">char2</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">drawtext</span><span class="p">)</span>
|
||||
|
||||
<span class="n">SKILLS</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"combat"</span><span class="p">:</span> <span class="n">skill_combat</span><span class="p">}</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll_challenge</span><span class="p">(</span><span class="n">character1</span><span class="p">,</span> <span class="n">character2</span><span class="p">,</span> <span class="n">skillname</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Determine the outcome of a skill challenge between</span>
|
||||
<span class="sd"> two characters based on the skillname given.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="n">skillname</span> <span class="ow">in</span> <span class="n">SKILLS</span><span class="p">:</span>
|
||||
<span class="n">SKILLS</span><span class="p">[</span><span class="n">skillname</span><span class="p">](</span><span class="n">character1</span><span class="p">,</span> <span class="n">character2</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="n">RunTimeError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Skillname </span><span class="si">{</span><span class="n">skillname</span><span class="si">}</span><span class="s2"> not found."</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>These few functions implement the entirety of our simple rule system. We have a function to check
|
||||
the “defeat” condition and reset the <code class="docutils literal notranslate"><span class="pre">HP</span></code> back to 100 again. We define a generic “skill” function.
|
||||
Multiple skills could all be added with the same signature; our <code class="docutils literal notranslate"><span class="pre">SKILLS</span></code> dictionary makes it easy to
|
||||
look up the skills regardless of what their actual functions are called. Finally, the access
|
||||
function <code class="docutils literal notranslate"><span class="pre">roll_challenge</span></code> just picks the skill and gets the result.</p>
|
||||
<p>In this example, the skill function actually does a lot - it not only rolls results, it also informs
|
||||
everyone of their results via <code class="docutils literal notranslate"><span class="pre">character.msg()</span></code> calls.</p>
|
||||
<p>Here is an example of usage in a game command:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span>
|
||||
<span class="kn">from</span> <span class="nn">world</span> <span class="kn">import</span> <span class="n">rules</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdAttack</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> attack an opponent</span>
|
||||
|
||||
<span class="sd"> Usage:</span>
|
||||
<span class="sd"> attack <target></span>
|
||||
|
||||
<span class="sd"> This will attack a target in the same room, dealing</span>
|
||||
<span class="sd"> damage with your bare hands.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="s2">"Implementing combat"</span>
|
||||
|
||||
<span class="n">caller</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">:</span>
|
||||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You need to pick a target to attack."</span><span class="p">)</span>
|
||||
<span class="k">return</span>
|
||||
|
||||
<span class="n">target</span> <span class="o">=</span> <span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">target</span><span class="p">:</span>
|
||||
<span class="n">rules</span><span class="o">.</span><span class="n">roll_challenge</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="s2">"combat"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Note how simple the command becomes and how generic you can make it. It becomes simple to offer any
|
||||
number of Combat commands by just extending this functionality - you can easily roll challenges and
|
||||
pick different skills to check. And if you ever decided to, say, change how to determine hit chance,
|
||||
you don’t have to change every command, but need only change the single <code class="docutils literal notranslate"><span class="pre">roll_hit</span></code> function inside
|
||||
your <code class="docutils literal notranslate"><span class="pre">rules</span></code> module.</p>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<p><h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Implementing a game rule system</a><ul>
|
||||
<li><a class="reference internal" href="#overall-system-infrastructure">Overall system infrastructure</a></li>
|
||||
<li><a class="reference internal" href="#coded-systems">Coded systems</a></li>
|
||||
<li><a class="reference internal" href="#example-of-rule-module">Example of Rule module</a><ul>
|
||||
<li><a class="reference internal" href="#character">Character</a></li>
|
||||
<li><a class="reference internal" href="#rule-module">Rule module</a></li>
|
||||
</ul>
|
||||
</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="Turn-based-Combat-System.html"
|
||||
title="next chapter">Turn based Combat System</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/Implementing-a-game-rule-system.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="Implementing-a-game-rule-system.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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Turn-based-Combat-System.html" title="Turn based Combat System"
|
||||
>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> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Implementing a game rule system</a></li>
|
||||
</ul>
|
||||
<div class="develop">develop branch</div>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,651 +0,0 @@
|
|||
|
||||
<!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>Turn based Combat System — 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="Making a sittable object" href="A-Sittable-Object.html" />
|
||||
<link rel="prev" title="Implementing a game rule system" href="Implementing-a-game-rule-system.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="A-Sittable-Object.html" title="Making a sittable object"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Implementing-a-game-rule-system.html" title="Implementing a game rule system"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" accesskey="U">Part 3: How we get there</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Turn based Combat System</a></li>
|
||||
</ul>
|
||||
<div class="develop">develop branch</div>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="turn-based-combat-system">
|
||||
<h1>Turn based Combat System<a class="headerlink" href="#turn-based-combat-system" title="Permalink to this headline">¶</a></h1>
|
||||
<p>This tutorial gives an example of a full, if simplified, combat system for Evennia. It was inspired
|
||||
by the discussions held on the <a class="reference external" href="https://groups.google.com/forum/#%21msg/evennia/wnJNM2sXSfs/-dbLRrgWnYMJ">mailing
|
||||
list</a>.</p>
|
||||
<section id="overview-of-combat-system-concepts">
|
||||
<h2>Overview of combat system concepts<a class="headerlink" href="#overview-of-combat-system-concepts" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Most MUDs will use some sort of combat system. There are several main variations:</p>
|
||||
<ul class="simple">
|
||||
<li><p><em>Freeform</em> - the simplest form of combat to implement, common to MUSH-style roleplaying games.
|
||||
This means the system only supplies dice rollers or maybe commands to compare skills and spit out
|
||||
the result. Dice rolls are done to resolve combat according to the rules of the game and to direct
|
||||
the scene. A game master may be required to resolve rule disputes.</p></li>
|
||||
<li><p><em>Twitch</em> - This is the traditional MUD hack&slash style combat. In a twitch system there is often
|
||||
no difference between your normal “move-around-and-explore mode” and the “combat mode”. You enter an
|
||||
attack command and the system will calculate if the attack hits and how much damage was caused.
|
||||
Normally attack commands have some sort of timeout or notion of recovery/balance to reduce the
|
||||
advantage of spamming or client scripting. Whereas the simplest systems just means entering <code class="docutils literal notranslate"><span class="pre">kill</span> <span class="pre"><target></span></code> over and over, more sophisticated twitch systems include anything from defensive stances
|
||||
to tactical positioning.</p></li>
|
||||
<li><p><em>Turn-based</em> - a turn based system means that the system pauses to make sure all combatants can
|
||||
choose their actions before continuing. In some systems, such entered actions happen immediately
|
||||
(like twitch-based) whereas in others the resolution happens simultaneously at the end of the turn.
|
||||
The disadvantage of a turn-based system is that the game must switch to a “combat mode” and one also
|
||||
needs to take special care of how to handle new combatants and the passage of time. The advantage is
|
||||
that success is not dependent on typing speed or of setting up quick client macros. This potentially
|
||||
allows for emoting as part of combat which is an advantage for roleplay-heavy games.</p></li>
|
||||
</ul>
|
||||
<p>To implement a freeform combat system all you need is a dice roller and a roleplaying rulebook. See
|
||||
<a class="reference external" href="https://github.com/evennia/evennia/blob/master/evennia/contrib/dice.py">contrib/dice.py</a> for an
|
||||
example dice roller. To implement at twitch-based system you basically need a few combat
|
||||
<a class="reference internal" href="../../../Components/Commands.html"><span class="doc std std-doc">commands</span></a>, possibly ones with a <a class="reference internal" href="../../Command-Cooldown.html"><span class="doc std std-doc">cooldown</span></a>. You also need a <a class="reference internal" href="Implementing-a-game-rule-system.html"><span class="doc std std-doc">game rule
|
||||
module</span></a> that makes use of it. We will focus on the turn-based
|
||||
variety here.</p>
|
||||
</section>
|
||||
<section id="tutorial-overview">
|
||||
<h2>Tutorial overview<a class="headerlink" href="#tutorial-overview" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This tutorial will implement the slightly more complex turn-based combat system. Our example has the
|
||||
following properties:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Combat is initiated with <code class="docutils literal notranslate"><span class="pre">attack</span> <span class="pre"><target></span></code>, this initiates the combat mode.</p></li>
|
||||
<li><p>Characters may join an ongoing battle using <code class="docutils literal notranslate"><span class="pre">attack</span> <span class="pre"><target></span></code> against a character already in
|
||||
combat.</p></li>
|
||||
<li><p>Each turn every combating character will get to enter two commands, their internal order matters
|
||||
and they are compared one-to-one in the order given by each combatant. Use of <code class="docutils literal notranslate"><span class="pre">say</span></code> and <code class="docutils literal notranslate"><span class="pre">pose</span></code> is
|
||||
free.</p></li>
|
||||
<li><p>The commands are (in our example) simple; they can either <code class="docutils literal notranslate"><span class="pre">hit</span> <span class="pre"><target></span></code>, <code class="docutils literal notranslate"><span class="pre">feint</span> <span class="pre"><target></span></code> or
|
||||
<code class="docutils literal notranslate"><span class="pre">parry</span> <span class="pre"><target></span></code>. They can also <code class="docutils literal notranslate"><span class="pre">defend</span></code>, a generic passive defense. Finally they may choose to
|
||||
<code class="docutils literal notranslate"><span class="pre">disengage/flee</span></code>.</p></li>
|
||||
<li><p>When attacking we use a classic [rock-paper-scissors](<a class="reference external" href="https://en.wikipedia.org/wiki/Rock-paper-">https://en.wikipedia.org/wiki/Rock-paper-</a>
|
||||
scissors) mechanic to determine success: <code class="docutils literal notranslate"><span class="pre">hit</span></code> defeats <code class="docutils literal notranslate"><span class="pre">feint</span></code>, which defeats <code class="docutils literal notranslate"><span class="pre">parry</span></code> which defeats
|
||||
<code class="docutils literal notranslate"><span class="pre">hit</span></code>. <code class="docutils literal notranslate"><span class="pre">defend</span></code> is a general passive action that has a percentage chance to win against <code class="docutils literal notranslate"><span class="pre">hit</span></code>
|
||||
(only).</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">disengage/flee</span></code> must be entered two times in a row and will only succeed if there is no <code class="docutils literal notranslate"><span class="pre">hit</span></code>
|
||||
against them in that time. If so they will leave combat mode.</p></li>
|
||||
<li><p>Once every player has entered two commands, all commands are resolved in order and the result is
|
||||
reported. A new turn then begins.</p></li>
|
||||
<li><p>If players are too slow the turn will time out and any unset commands will be set to <code class="docutils literal notranslate"><span class="pre">defend</span></code>.</p></li>
|
||||
</ul>
|
||||
<p>For creating the combat system we will need the following components:</p>
|
||||
<ul class="simple">
|
||||
<li><p>A combat handler. This is the main mechanic of the system. This is a <a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Script</span></a> object
|
||||
created for each combat. It is not assigned to a specific object but is shared by the combating
|
||||
characters and handles all the combat information. Since Scripts are database entities it also means
|
||||
that the combat will not be affected by a server reload.</p></li>
|
||||
<li><p>A combat <a class="reference internal" href="../../../Components/Command-Sets.html"><span class="doc std std-doc">command set</span></a> with the relevant commands needed for combat, such as the
|
||||
various attack/defend options and the <code class="docutils literal notranslate"><span class="pre">flee/disengage</span></code> command to leave the combat mode.</p></li>
|
||||
<li><p>A rule resolution system. The basics of making such a module is described in the <a class="reference internal" href="Implementing-a-game-rule-system.html"><span class="doc std std-doc">rule system
|
||||
tutorial</span></a>. We will only sketch such a module here for our end-turn
|
||||
combat resolution.</p></li>
|
||||
<li><p>An <code class="docutils literal notranslate"><span class="pre">attack</span></code> <a class="reference internal" href="../../../Components/Commands.html"><span class="doc std std-doc">command</span></a> for initiating the combat mode. This is added to the default
|
||||
command set. It will create the combat handler and add the character(s) to it. It will also assign
|
||||
the combat command set to the characters.</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="the-combat-handler">
|
||||
<h2>The combat handler<a class="headerlink" href="#the-combat-handler" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The <em>combat handler</em> is implemented as a stand-alone <a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Script</span></a>. This Script is created when
|
||||
the first Character decides to attack another and is deleted when no one is fighting any more. Each
|
||||
handler represents one instance of combat and one combat only. Each instance of combat can hold any
|
||||
number of characters but each character can only be part of one combat at a time (a player would
|
||||
need to disengage from the first combat before they could join another).</p>
|
||||
<p>The reason we don’t store this Script “on” any specific character is because any character may leave
|
||||
the combat at any time. Instead the script holds references to all characters involved in the
|
||||
combat. Vice-versa, all characters holds a back-reference to the current combat handler. While we
|
||||
don’t use this very much here this might allow the combat commands on the characters to access and
|
||||
update the combat handler state directly.</p>
|
||||
<p><em>Note: Another way to implement a combat handler would be to use a normal Python object and handle
|
||||
time-keeping with the <a class="reference internal" href="../../../Components/TickerHandler.html"><span class="doc std std-doc">TickerHandler</span></a>. This would require either adding custom hook
|
||||
methods on the character or to implement a custom child of the TickerHandler class to track turns.
|
||||
Whereas the TickerHandler is easy to use, a Script offers more power in this case.</em></p>
|
||||
<p>Here is a basic combat handler. Assuming our game folder is named <code class="docutils literal notranslate"><span class="pre">mygame</span></code>, we store it in
|
||||
<code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/combat_handler.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/typeclasses/combat_handler.py</span>
|
||||
|
||||
<span class="kn">import</span> <span class="nn">random</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultScript</span>
|
||||
<span class="kn">from</span> <span class="nn">world.rules</span> <span class="kn">import</span> <span class="n">resolve_combat</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CombatHandler</span><span class="p">(</span><span class="n">DefaultScript</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> This implements the combat handler.</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="c1"># standard Script hooks </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_script_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="s2">"Called when script is first created"</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"combat_handler_</span><span class="si">{</span><span class="n">random</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="mi">1000</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">desc</span> <span class="o">=</span> <span class="s2">"handles combat"</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">interval</span> <span class="o">=</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">2</span> <span class="c1"># two minute timeout</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">start_delay</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">persistent</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
<span class="c1"># store all combatants</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">characters</span> <span class="o">=</span> <span class="p">{}</span>
|
||||
<span class="c1"># store all actions for each turn</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">turn_actions</span> <span class="o">=</span> <span class="p">{}</span>
|
||||
<span class="c1"># number of actions entered per combatant</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">action_count</span> <span class="o">=</span> <span class="p">{}</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_init_character</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">"""</span>
|
||||
<span class="sd"> This initializes handler back-reference </span>
|
||||
<span class="sd"> and combat cmdset on a character</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">character</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combat_handler</span> <span class="o">=</span> <span class="bp">self</span>
|
||||
<span class="n">character</span><span class="o">.</span><span class="n">cmdset</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"commands.combat.CombatCmdSet"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_cleanup_character</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">"""</span>
|
||||
<span class="sd"> Remove character from handler and clean </span>
|
||||
<span class="sd"> it of the back-reference and cmdset</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">dbref</span> <span class="o">=</span> <span class="n">character</span><span class="o">.</span><span class="n">id</span>
|
||||
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">characters</span><span class="p">[</span><span class="n">dbref</span><span class="p">]</span>
|
||||
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">turn_actions</span><span class="p">[</span><span class="n">dbref</span><span class="p">]</span>
|
||||
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">action_count</span><span class="p">[</span><span class="n">dbref</span><span class="p">]</span>
|
||||
<span class="k">del</span> <span class="n">character</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combat_handler</span>
|
||||
<span class="n">character</span><span class="o">.</span><span class="n">cmdset</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s2">"commands.combat.CombatCmdSet"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_start</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> This is called on first start but also when the script is restarted</span>
|
||||
<span class="sd"> after a server reboot. We need to re-assign this combat handler to </span>
|
||||
<span class="sd"> all characters as well as re-assign the cmdset.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">for</span> <span class="n">character</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">characters</span><span class="o">.</span><span class="n">values</span><span class="p">():</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_init_character</span><span class="p">(</span><span class="n">character</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_stop</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="s2">"Called just before the script is stopped/destroyed."</span>
|
||||
<span class="k">for</span> <span class="n">character</span> <span class="ow">in</span> <span class="nb">list</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">characters</span><span class="o">.</span><span class="n">values</span><span class="p">()):</span>
|
||||
<span class="c1"># note: the list() call above disconnects list from database</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_cleanup_character</span><span class="p">(</span><span class="n">character</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_repeat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> This is called every self.interval seconds (turn timeout) or </span>
|
||||
<span class="sd"> when force_repeat is called (because everyone has entered their </span>
|
||||
<span class="sd"> commands). We know this by checking the existence of the</span>
|
||||
<span class="sd"> `normal_turn_end` NAttribute, set just before calling </span>
|
||||
<span class="sd"> force_repeat.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">normal_turn_end</span><span class="p">:</span>
|
||||
<span class="c1"># we get here because the turn ended normally</span>
|
||||
<span class="c1"># (force_repeat was called) - no msg output</span>
|
||||
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">normal_turn_end</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># turn timeout</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">msg_all</span><span class="p">(</span><span class="s2">"Turn timer timed out. Continuing."</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">end_turn</span><span class="p">()</span>
|
||||
|
||||
<span class="c1"># Combat-handler methods</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">add_character</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="s2">"Add combatant to handler"</span>
|
||||
<span class="n">dbref</span> <span class="o">=</span> <span class="n">character</span><span class="o">.</span><span class="n">id</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">characters</span><span class="p">[</span><span class="n">dbref</span><span class="p">]</span> <span class="o">=</span> <span class="n">character</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">action_count</span><span class="p">[</span><span class="n">dbref</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">turn_actions</span><span class="p">[</span><span class="n">dbref</span><span class="p">]</span> <span class="o">=</span> <span class="p">[(</span><span class="s2">"defend"</span><span class="p">,</span> <span class="n">character</span><span class="p">,</span> <span class="kc">None</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"defend"</span><span class="p">,</span> <span class="n">character</span><span class="p">,</span> <span class="kc">None</span><span class="p">)]</span>
|
||||
<span class="c1"># set up back-reference</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_init_character</span><span class="p">(</span><span class="n">character</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">remove_character</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="s2">"Remove combatant from handler"</span>
|
||||
<span class="k">if</span> <span class="n">character</span><span class="o">.</span><span class="n">id</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">characters</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_cleanup_character</span><span class="p">(</span><span class="n">character</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">characters</span><span class="p">:</span>
|
||||
<span class="c1"># if no more characters in battle, kill this handler</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">msg_all</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span>
|
||||
<span class="s2">"Send message to all combatants"</span>
|
||||
<span class="k">for</span> <span class="n">character</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">characters</span><span class="o">.</span><span class="n">values</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="n">message</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">add_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">action</span><span class="p">,</span> <span class="n">character</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Called by combat commands to register an action with the handler.</span>
|
||||
|
||||
<span class="sd"> action - string identifying the action, like "hit" or "parry"</span>
|
||||
<span class="sd"> character - the character performing the action</span>
|
||||
<span class="sd"> target - the target character or None</span>
|
||||
|
||||
<span class="sd"> actions are stored in a dictionary keyed to each character, each</span>
|
||||
<span class="sd"> of which holds a list of max 2 actions. An action is stored as</span>
|
||||
<span class="sd"> a tuple (character, action, target). </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">dbref</span> <span class="o">=</span> <span class="n">character</span><span class="o">.</span><span class="n">id</span>
|
||||
<span class="n">count</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">action_count</span><span class="p">[</span><span class="n">dbref</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">count</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">:</span> <span class="c1"># only allow 2 actions </span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">turn_actions</span><span class="p">[</span><span class="n">dbref</span><span class="p">][</span><span class="n">count</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">action</span><span class="p">,</span> <span class="n">character</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># report if we already used too many actions</span>
|
||||
<span class="k">return</span> <span class="kc">False</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">action_count</span><span class="p">[</span><span class="n">dbref</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
|
||||
<span class="k">return</span> <span class="kc">True</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">check_end_turn</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> Called by the command to eventually trigger </span>
|
||||
<span class="sd"> the resolution of the turn. We check if everyone</span>
|
||||
<span class="sd"> has added all their actions; if so we call force the</span>
|
||||
<span class="sd"> script to repeat immediately (which will call</span>
|
||||
<span class="sd"> `self.at_repeat()` while resetting all timers). </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="nb">all</span><span class="p">(</span><span class="n">count</span> <span class="o">></span> <span class="mi">1</span> <span class="k">for</span> <span class="n">count</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">action_count</span><span class="o">.</span><span class="n">values</span><span class="p">()):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">normal_turn_end</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">force_repeat</span><span class="p">()</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">end_turn</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> This resolves all actions by calling the rules module. </span>
|
||||
<span class="sd"> It then resets everything and starts the next turn. It</span>
|
||||
<span class="sd"> is called by at_repeat().</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">resolve_combat</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">db</span><span class="o">.</span><span class="n">turn_actions</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">characters</span><span class="p">)</span> <span class="o"><</span> <span class="mi">2</span><span class="p">:</span>
|
||||
<span class="c1"># less than 2 characters in battle, kill this handler</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">msg_all</span><span class="p">(</span><span class="s2">"Combat has ended"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># reset counters before next turn</span>
|
||||
<span class="k">for</span> <span class="n">character</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">characters</span><span class="o">.</span><span class="n">values</span><span class="p">():</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">characters</span><span class="p">[</span><span class="n">character</span><span class="o">.</span><span class="n">id</span><span class="p">]</span> <span class="o">=</span> <span class="n">character</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">action_count</span><span class="p">[</span><span class="n">character</span><span class="o">.</span><span class="n">id</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">turn_actions</span><span class="p">[</span><span class="n">character</span><span class="o">.</span><span class="n">id</span><span class="p">]</span> <span class="o">=</span> <span class="p">[(</span><span class="s2">"defend"</span><span class="p">,</span> <span class="n">character</span><span class="p">,</span> <span class="kc">None</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"defend"</span><span class="p">,</span> <span class="n">character</span><span class="p">,</span> <span class="kc">None</span><span class="p">)]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">msg_all</span><span class="p">(</span><span class="s2">"Next turn begins ..."</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This implements all the useful properties of our combat handler. This Script will survive a reboot
|
||||
and will automatically re-assert itself when it comes back online. Even the current state of the
|
||||
combat should be unaffected since it is saved in Attributes at every turn. An important part to note
|
||||
is the use of the Script’s standard <code class="docutils literal notranslate"><span class="pre">at_repeat</span></code> hook and the <code class="docutils literal notranslate"><span class="pre">force_repeat</span></code> method to end each turn.
|
||||
This allows for everything to go through the same mechanisms with minimal repetition of code.</p>
|
||||
<p>What is not present in this handler is a way for players to view the actions they set or to change
|
||||
their actions once they have been added (but before the last one has added theirs). We leave this as
|
||||
an exercise.</p>
|
||||
</section>
|
||||
<section id="combat-commands">
|
||||
<h2>Combat commands<a class="headerlink" href="#combat-commands" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Our combat commands - the commands that are to be available to us during the combat - are (in our
|
||||
example) very simple. In a full implementation the commands available might be determined by the
|
||||
weapon(s) held by the player or by which skills they know.</p>
|
||||
<p>We create them in <code class="docutils literal notranslate"><span class="pre">mygame/commands/combat.py</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/commands/combat.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdHit</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> hit an enemy</span>
|
||||
|
||||
<span class="sd"> Usage:</span>
|
||||
<span class="sd"> hit <target></span>
|
||||
|
||||
<span class="sd"> Strikes the given enemy with your current weapon.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"hit"</span>
|
||||
<span class="n">aliases</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"strike"</span><span class="p">,</span> <span class="s2">"slash"</span><span class="p">]</span>
|
||||
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">"combat"</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="s2">"Implements the command"</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Usage: hit <target>"</span><span class="p">)</span>
|
||||
<span class="k">return</span>
|
||||
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">target</span><span class="p">:</span>
|
||||
<span class="k">return</span>
|
||||
<span class="n">ok</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combat_handler</span><span class="o">.</span><span class="n">add_action</span><span class="p">(</span><span class="s2">"hit"</span><span class="p">,</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">,</span>
|
||||
<span class="n">target</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">ok</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You add 'hit' to the combat queue"</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">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You can only queue two actions per turn!"</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># tell the handler to check if turn is over</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combat_handler</span><span class="o">.</span><span class="n">check_end_turn</span><span class="p">()</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The other commands <code class="docutils literal notranslate"><span class="pre">CmdParry</span></code>, <code class="docutils literal notranslate"><span class="pre">CmdFeint</span></code>, <code class="docutils literal notranslate"><span class="pre">CmdDefend</span></code> and <code class="docutils literal notranslate"><span class="pre">CmdDisengage</span></code> look basically the same.
|
||||
We should also add a custom <code class="docutils literal notranslate"><span class="pre">help</span></code> command to list all the available combat commands and what they
|
||||
do.</p>
|
||||
<p>We just need to put them all in a cmdset. We do this at the end of the same module:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/commands/combat.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">CmdSet</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">default_cmds</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CombatCmdSet</span><span class="p">(</span><span class="n">CmdSet</span><span class="p">):</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"combat_cmdset"</span>
|
||||
<span class="n">mergetype</span> <span class="o">=</span> <span class="s2">"Replace"</span>
|
||||
<span class="n">priority</span> <span class="o">=</span> <span class="mi">10</span>
|
||||
<span class="n">no_exits</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_cmdset_creation</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">add</span><span class="p">(</span><span class="n">CmdHit</span><span class="p">())</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdParry</span><span class="p">())</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdFeint</span><span class="p">())</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdDefend</span><span class="p">())</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdDisengage</span><span class="p">())</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdHelp</span><span class="p">())</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CmdPose</span><span class="p">())</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CmdSay</span><span class="p">())</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="rules-module">
|
||||
<h2>Rules module<a class="headerlink" href="#rules-module" title="Permalink to this headline">¶</a></h2>
|
||||
<p>A general way to implement a rule module is found in the [rule system tutorial](Implementing-a-game-
|
||||
rule-system). Proper resolution would likely require us to change our Characters to store things
|
||||
like strength, weapon skills and so on. So for this example we will settle for a very simplistic
|
||||
rock-paper-scissors kind of setup with some randomness thrown in. We will not deal with damage here
|
||||
but just announce the results of each turn. In a real system the Character objects would hold stats
|
||||
to affect their skills, their chosen weapon affect the choices, they would be able to lose health
|
||||
etc.</p>
|
||||
<p>Within each turn, there are “sub-turns”, each consisting of one action per character. The actions
|
||||
within each sub-turn happens simultaneously and only once they have all been resolved we move on to
|
||||
the next sub-turn (or end the full turn).</p>
|
||||
<p><em>Note: In our simple example the sub-turns don’t affect each other (except for <code class="docutils literal notranslate"><span class="pre">disengage/flee</span></code>),
|
||||
nor do any effects carry over between turns. The real power of a turn-based system would be to add
|
||||
real tactical possibilities here though; For example if your hit got parried you could be out of
|
||||
balance and your next action would be at a disadvantage. A successful feint would open up for a
|
||||
subsequent attack and so on …</em></p>
|
||||
<p>Our rock-paper-scissor setup works like this:</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">hit</span></code> beats <code class="docutils literal notranslate"><span class="pre">feint</span></code> and <code class="docutils literal notranslate"><span class="pre">flee/disengage</span></code>. It has a random chance to fail against <code class="docutils literal notranslate"><span class="pre">defend</span></code>.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">parry</span></code> beats <code class="docutils literal notranslate"><span class="pre">hit</span></code>.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">feint</span></code> beats <code class="docutils literal notranslate"><span class="pre">parry</span></code> and is then counted as a <code class="docutils literal notranslate"><span class="pre">hit</span></code>.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">defend</span></code> does nothing but has a chance to beat <code class="docutils literal notranslate"><span class="pre">hit</span></code>.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">flee/disengage</span></code> must succeed two times in a row (i.e. not beaten by a <code class="docutils literal notranslate"><span class="pre">hit</span></code> once during the
|
||||
turn). If so the character leaves combat.</p></li>
|
||||
</ul>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/world/rules.py</span>
|
||||
|
||||
<span class="kn">import</span> <span class="nn">random</span>
|
||||
|
||||
<span class="c1"># messages </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">resolve_combat</span><span class="p">(</span><span class="n">combat_handler</span><span class="p">,</span> <span class="n">actiondict</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> This is called by the combat handler</span>
|
||||
<span class="sd"> actiondict is a dictionary with a list of two actions</span>
|
||||
<span class="sd"> for each character:</span>
|
||||
<span class="sd"> {char.id:[(action1, char, target), (action2, char, target)], ...}</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">flee</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># track number of flee commands per character</span>
|
||||
<span class="k">for</span> <span class="n">isub</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">):</span>
|
||||
<span class="c1"># loop over sub-turns</span>
|
||||
<span class="n">messages</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">for</span> <span class="n">subturn</span> <span class="ow">in</span> <span class="p">(</span><span class="n">sub</span><span class="p">[</span><span class="n">isub</span><span class="p">]</span> <span class="k">for</span> <span class="n">sub</span> <span class="ow">in</span> <span class="n">actiondict</span><span class="o">.</span><span class="n">values</span><span class="p">()):</span>
|
||||
<span class="c1"># for each character, resolve the sub-turn</span>
|
||||
<span class="n">action</span><span class="p">,</span> <span class="n">char</span><span class="p">,</span> <span class="n">target</span> <span class="o">=</span> <span class="n">subturn</span>
|
||||
<span class="k">if</span> <span class="n">target</span><span class="p">:</span>
|
||||
<span class="n">taction</span><span class="p">,</span> <span class="n">tchar</span><span class="p">,</span> <span class="n">ttarget</span> <span class="o">=</span> <span class="n">actiondict</span><span class="p">[</span><span class="n">target</span><span class="o">.</span><span class="n">id</span><span class="p">][</span><span class="n">isub</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="n">action</span> <span class="o">==</span> <span class="s2">"hit"</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">taction</span> <span class="o">==</span> <span class="s2">"parry"</span> <span class="ow">and</span> <span class="n">ttarget</span> <span class="o">==</span> <span class="n">char</span><span class="p">:</span>
|
||||
<span class="n">messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">char</span><span class="si">}</span><span class="s2"> tries to hit </span><span class="si">{</span><span class="n">tchar</span><span class="si">}</span><span class="s2">, but </span><span class="si">{</span><span class="n">tchar</span><span class="si">}</span><span class="s2"> parries the attack!"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">taction</span> <span class="o">==</span> <span class="s2">"defend"</span> <span class="ow">and</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o"><</span> <span class="mf">0.5</span><span class="p">:</span>
|
||||
<span class="n">messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">tchar</span><span class="si">}</span><span class="s2"> defends against the attack by </span><span class="si">{</span><span class="n">char</span><span class="si">}</span><span class="s2">."</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">taction</span> <span class="o">==</span> <span class="s2">"flee"</span><span class="p">:</span>
|
||||
<span class="n">flee</span><span class="p">[</span><span class="n">tchar</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="mi">2</span>
|
||||
<span class="n">messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">char</span><span class="si">}</span><span class="s2"> stops </span><span class="si">{</span><span class="n">tchar</span><span class="si">}</span><span class="s2"> from disengaging, with a hit!"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">char</span><span class="si">}</span><span class="s2"> hits </span><span class="si">{</span><span class="n">tchar</span><span class="si">}</span><span class="s2">, bypassing their </span><span class="si">{</span><span class="n">taction</span><span class="si">}</span><span class="s2">!"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">action</span> <span class="o">==</span> <span class="s2">"parry"</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">taction</span> <span class="o">==</span> <span class="s2">"hit"</span><span class="p">:</span>
|
||||
<span class="n">messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">char</span><span class="si">}</span><span class="s2"> parries the attack by </span><span class="si">{</span><span class="n">tchar</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">taction</span> <span class="o">==</span> <span class="s2">"feint"</span><span class="p">:</span>
|
||||
<span class="n">messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">char</span><span class="si">}</span><span class="s2"> tries to parry, but </span><span class="si">{</span><span class="n">tchar</span><span class="si">}</span><span class="s2"> feints and hits!"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">char</span><span class="si">}</span><span class="s2"> parries to no avail."</span><span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">action</span> <span class="o">==</span> <span class="s2">"feint"</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">taction</span> <span class="o">==</span> <span class="s2">"parry"</span><span class="p">:</span>
|
||||
<span class="n">messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">char</span><span class="si">}</span><span class="s2"> feints past </span><span class="si">{</span><span class="n">tchar</span><span class="si">}</span><span class="s2">'s parry, landing a hit!"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">taction</span> <span class="o">==</span> <span class="s2">"hit"</span><span class="p">:</span>
|
||||
<span class="n">messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">char</span><span class="si">}</span><span class="s2"> feints but is defeated by </span><span class="si">{</span><span class="n">tchar</span><span class="si">}</span><span class="s2">'s hit!"</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">char</span><span class="si">}</span><span class="s2"> feints to no avail."</span><span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">action</span> <span class="o">==</span> <span class="s2">"defend"</span><span class="p">:</span>
|
||||
<span class="n">messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">char</span><span class="si">}</span><span class="s2"> defends."</span><span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">action</span> <span class="o">==</span> <span class="s2">"flee"</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">char</span> <span class="ow">in</span> <span class="n">flee</span><span class="p">:</span>
|
||||
<span class="n">flee</span><span class="p">[</span><span class="n">char</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">flee</span><span class="p">[</span><span class="n">char</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>
|
||||
<span class="n">messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">char</span><span class="si">}</span><span class="s2"> tries to disengage (two subsequent turns needed)"</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="c1"># echo results of each subturn</span>
|
||||
<span class="n">combat_handler</span><span class="o">.</span><span class="n">msg_all</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">messages</span><span class="p">))</span>
|
||||
|
||||
<span class="c1"># at the end of both sub-turns, test if anyone fled</span>
|
||||
<span class="k">for</span> <span class="p">(</span><span class="n">char</span><span class="p">,</span> <span class="n">fleevalue</span><span class="p">)</span> <span class="ow">in</span> <span class="n">flee</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
|
||||
<span class="k">if</span> <span class="n">fleevalue</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
|
||||
<span class="n">combat_handler</span><span class="o">.</span><span class="n">msg_all</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">char</span><span class="si">}</span><span class="s2"> withdraws from combat."</span><span class="p">)</span>
|
||||
<span class="n">combat_handler</span><span class="o">.</span><span class="n">remove_character</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>To make it simple (and to save space), this example rule module actually resolves each interchange
|
||||
twice - first when it gets to each character and then again when handling the target. Also, since we
|
||||
use the combat handler’s <code class="docutils literal notranslate"><span class="pre">msg_all</span></code> method here, the system will get pretty spammy. To clean it up,
|
||||
one could imagine tracking all the possible interactions to make sure each pair is only handled and
|
||||
reported once.</p>
|
||||
</section>
|
||||
<section id="combat-initiator-command">
|
||||
<h2>Combat initiator command<a class="headerlink" href="#combat-initiator-command" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This is the last component we need, a command to initiate combat. This will tie everything together.
|
||||
We store this with the other combat commands.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/commands/combat.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_script</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdAttack</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd"> initiates combat</span>
|
||||
|
||||
<span class="sd"> Usage:</span>
|
||||
<span class="sd"> attack <target></span>
|
||||
|
||||
<span class="sd"> This will initiate combat with <target>. If <target is</span>
|
||||
<span class="sd"> already in combat, you will join the combat. </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"attack"</span>
|
||||
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">"General"</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="s2">"Handle command"</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Usage: attack <target>"</span><span class="p">)</span>
|
||||
<span class="k">return</span>
|
||||
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">target</span><span class="p">:</span>
|
||||
<span class="k">return</span>
|
||||
<span class="c1"># set up combat</span>
|
||||
<span class="k">if</span> <span class="n">target</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combat_handler</span><span class="p">:</span>
|
||||
<span class="c1"># target is already in combat - join it </span>
|
||||
<span class="n">target</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combat_handler</span><span class="o">.</span><span class="n">add_character</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span>
|
||||
<span class="n">target</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combat_handler</span><span class="o">.</span><span class="n">msg_all</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="si">}</span><span class="s2"> joins combat!"</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># create a new combat handler</span>
|
||||
<span class="n">chandler</span> <span class="o">=</span> <span class="n">create_script</span><span class="p">(</span><span class="s2">"combat_handler.CombatHandler"</span><span class="p">)</span>
|
||||
<span class="n">chandler</span><span class="o">.</span><span class="n">add_character</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span>
|
||||
<span class="n">chandler</span><span class="o">.</span><span class="n">add_character</span><span class="p">(</span><span class="n">target</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You attack </span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2">! You are in combat."</span><span class="p">)</span>
|
||||
<span class="n">target</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="si">}</span><span class="s2"> attacks you! You are in combat."</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">attack</span></code> command will not go into the combat cmdset but rather into the default cmdset. See e.g.
|
||||
the <a class="reference internal" href="../Part1/Adding-Commands.html"><span class="doc std std-doc">Adding Command Tutorial</span></a> if you are unsure about how to do this.</p>
|
||||
</section>
|
||||
<section id="expanding-the-example">
|
||||
<h2>Expanding the example<a class="headerlink" href="#expanding-the-example" title="Permalink to this headline">¶</a></h2>
|
||||
<p>At this point you should have a simple but flexible turn-based combat system. We have taken several
|
||||
shortcuts and simplifications in this example. The output to the players is likely too verbose
|
||||
during combat and too limited when it comes to informing about things surrounding it. Methods for
|
||||
changing your commands or list them, view who is in combat etc is likely needed - this will require
|
||||
play testing for each game and style. There is also currently no information displayed for other
|
||||
people happening to be in the same room as the combat - some less detailed information should
|
||||
probably be echoed to the room to
|
||||
show others what’s going on.</p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<p><h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Turn based Combat System</a><ul>
|
||||
<li><a class="reference internal" href="#overview-of-combat-system-concepts">Overview of combat system concepts</a></li>
|
||||
<li><a class="reference internal" href="#tutorial-overview">Tutorial overview</a></li>
|
||||
<li><a class="reference internal" href="#the-combat-handler">The combat handler</a></li>
|
||||
<li><a class="reference internal" href="#combat-commands">Combat commands</a></li>
|
||||
<li><a class="reference internal" href="#rules-module">Rules module</a></li>
|
||||
<li><a class="reference internal" href="#combat-initiator-command">Combat initiator command</a></li>
|
||||
<li><a class="reference internal" href="#expanding-the-example">Expanding the example</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Implementing-a-game-rule-system.html"
|
||||
title="previous chapter">Implementing a game rule system</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="A-Sittable-Object.html"
|
||||
title="next chapter">Making a sittable object</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/Turn-based-Combat-System.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="Turn-based-Combat-System.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="clearer"></div>
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="A-Sittable-Object.html" title="Making a sittable object"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Implementing-a-game-rule-system.html" title="Implementing a game rule system"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howto’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Intro.html" >Part 3: How we get there</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Turn based Combat System</a></li>
|
||||
</ul>
|
||||
<div class="develop">develop branch</div>
|
||||
</div>
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2020, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue