mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
568 lines
No EOL
64 KiB
HTML
568 lines
No EOL
64 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<html lang="en" data-content_root="../../../">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
|
||
<title>14. Game Quests — Evennia latest documentation</title>
|
||
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css?v=d75fae25" />
|
||
<link rel="stylesheet" type="text/css" href="../../../_static/nature.css?v=279e0f84" />
|
||
<link rel="stylesheet" type="text/css" href="../../../_static/custom.css?v=e4a91a55" />
|
||
<script src="../../../_static/documentation_options.js?v=c6e86fd7"></script>
|
||
<script src="../../../_static/doctools.js?v=9bcbadda"></script>
|
||
<script src="../../../_static/sphinx_highlight.js?v=dc90522c"></script>
|
||
<link rel="icon" href="../../../_static/favicon.ico"/>
|
||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||
<link rel="search" title="Search" href="../../../search.html" />
|
||
<link rel="next" title="15. In-game Shops" href="Beginner-Tutorial-Shops.html" />
|
||
<link rel="prev" title="13. Procedurally generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
|
||
</head><body>
|
||
<div class="related" role="navigation" aria-label="Related">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../../../genindex.html" title="General Index"
|
||
accesskey="I">index</a></li>
|
||
<li class="right" >
|
||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="right" >
|
||
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
|
||
accesskey="N">next</a> |</li>
|
||
<li class="right" >
|
||
<a href="Beginner-Tutorial-Dungeon.html" title="13. Procedurally generated Dungeon"
|
||
accesskey="P">previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">14. </span>Game Quests</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="document">
|
||
<div class="documentwrapper">
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<section class="tex2jax_ignore mathjax_ignore" id="game-quests">
|
||
<h1><span class="section-number">14. </span>Game Quests<a class="headerlink" href="#game-quests" title="Link to this heading">¶</a></h1>
|
||
<div class="admonition warning">
|
||
<p class="admonition-title">Warning</p>
|
||
<p>This tutorial lesson is not yet complete, and has some serious bugs in its implementation. So use this as a reference, but the code is not yet ready to use directly.</p>
|
||
</div>
|
||
<p>A <em>quest</em> is a common feature of games. From classic fetch-quests like retrieving 10 flowers to complex quest chains involving drama and intrigue, quests need to be properly tracked in our game.</p>
|
||
<p>A quest follows a specific development:</p>
|
||
<ol class="arabic simple">
|
||
<li><p>The quest is <em>started</em>. This normally involves the player accepting the quest, from a quest-giver, job board or other source. But the quest could also be thrust on the player (“save the family from the burning house before it collapses!”)</p></li>
|
||
<li><p>Once a quest has been accepted and assigned to a character, it is either either <code class="docutils literal notranslate"><span class="pre">Started</span></code> (that is, ‘in progress’), <code class="docutils literal notranslate"><span class="pre">Abandoned</span></code>, <code class="docutils literal notranslate"><span class="pre">Failed</span></code> or <code class="docutils literal notranslate"><span class="pre">Complete</span></code>.</p></li>
|
||
<li><p>A quest may consist of one or more ‘steps’. Each step has its own set of finish conditions.</p></li>
|
||
<li><p>At suitable times the quest’s <em>progress</em> is checked. This could happen on a timer or when trying to ‘hand in’ the quest. When checking, the current ‘step’ is checked against its finish conditions. If ok, that step is closed and the next step is checked until it either hits a step that is not yet complete, or there are no more steps, in which case the entire quest is complete.</p></li>
|
||
</ol>
|
||
<aside class="sidebar">
|
||
<p>An example implementation of quests is found under <code class="docutils literal notranslate"><span class="pre">evennia/contrib/tutorials</span></code>, in <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.quests.html#evennia-contrib-tutorials-evadventure-quests"><span class="std std-ref">evadventure/quests.py</span></a>.</p>
|
||
</aside>
|
||
<p>To represent quests in code, we need</p>
|
||
<ul class="simple">
|
||
<li><p>A convenient flexible way to code how we check the status and current steps of the quest. We want this scripting to be as flexible as possible. Ideally we want to be able to code the quests’s logic in full Python.</p></li>
|
||
<li><p>Persistence. The fact that we accepted the quest, as well as its status and other flags must be saved in the database and survive a server reboot.</p></li>
|
||
</ul>
|
||
<p>We’ll accomplish this using two pieces of Python code:</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">EvAdventureQuest</span></code>: A Python class with helper methods that we can call to check current quest status, figure if a given quest-step is complete or not. We will create and script new quests by simply inheriting from this base class and implement new methods on it in a standardized way.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">EvAdventureQuestHandler</span></code> will sit ‘on’ each Character as <code class="docutils literal notranslate"><span class="pre">character.quests</span></code>. It will hold all <code class="docutils literal notranslate"><span class="pre">EvAdventureQuest</span></code>s that the character is or has been involved in. It is also responsible for storing quest state using <a class="reference internal" href="../../../Components/Attributes.html"><span class="std std-doc">Attributes</span></a> on the Character.</p></li>
|
||
</ul>
|
||
<section id="the-quest-handler">
|
||
<h2><span class="section-number">14.1. </span>The Quest Handler<a class="headerlink" href="#the-quest-handler" title="Link to this heading">¶</a></h2>
|
||
<blockquote>
|
||
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/quests.py</span></code>.</p>
|
||
</div></blockquote>
|
||
<p>We saw the implementation of an on-object handler back in the <a class="reference internal" href="Beginner-Tutorial-AI.html#the-aihandler"><span class="std std-ref">lesson about NPC and monster AI</span></a> (the <code class="docutils literal notranslate"><span class="pre">AIHandler</span></code>).</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1"># in evadventure/quests.py</span>
|
||
<span class="linenos"> 2</span>
|
||
<span class="linenos"> 3</span><span class="k">class</span><span class="w"> </span><span class="nc">EvAdventureQuestHandler</span><span class="p">:</span>
|
||
<span class="linenos"> 4</span> <span class="n">quest_storage_attribute_key</span> <span class="o">=</span> <span class="s2">"_quests"</span>
|
||
<span class="linenos"> 5</span> <span class="n">quest_storage_attribute_category</span> <span class="o">=</span> <span class="s2">"evadventure"</span>
|
||
<span class="linenos"> 6</span>
|
||
<span class="linenos"> 7</span> <span class="k">def</span><span class="w"> </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="linenos"> 8</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="hll"><span class="linenos"> 9</span> <span class="bp">self</span><span class="o">.</span><span class="n">quest_classes</span> <span class="o">=</span> <span class="p">{}</span>
|
||
</span><span class="hll"><span class="linenos">10</span> <span class="bp">self</span><span class="o">.</span><span class="n">quests</span> <span class="o">=</span> <span class="p">{}</span>
|
||
</span><span class="linenos">11</span> <span class="bp">self</span><span class="o">.</span><span class="n">_load</span><span class="p">()</span>
|
||
<span class="linenos">12</span>
|
||
<span class="hll"><span class="linenos">13</span> <span class="k">def</span><span class="w"> </span><span class="nf">_load</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
</span><span class="hll"><span class="linenos">14</span> <span class="bp">self</span><span class="o">.</span><span class="n">quest_classes</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><span class="hll"><span class="linenos">15</span> <span class="bp">self</span><span class="o">.</span><span class="n">quest_storage_attribute_key</span><span class="p">,</span>
|
||
</span><span class="hll"><span class="linenos">16</span> <span class="n">category</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">quest_storage_attribute_category</span><span class="p">,</span>
|
||
</span><span class="hll"><span class="linenos">17</span> <span class="n">default</span><span class="o">=</span><span class="p">{},</span>
|
||
</span><span class="linenos">18</span> <span class="p">)</span>
|
||
<span class="linenos">19</span> <span class="c1"># instantiate all quests</span>
|
||
<span class="hll"><span class="linenos">20</span> <span class="k">for</span> <span class="n">quest_key</span><span class="p">,</span> <span class="n">quest_class</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">quest_classes</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
|
||
</span><span class="linenos">21</span> <span class="bp">self</span><span class="o">.</span><span class="n">quests</span><span class="p">[</span><span class="n">quest_key</span><span class="p">]</span> <span class="o">=</span> <span class="n">quest_class</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">questhandler</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
|
||
<span class="linenos">22</span>
|
||
<span class="hll"><span class="linenos">23</span> <span class="k">def</span><span class="w"> </span><span class="nf">_save</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
</span><span class="hll"><span class="linenos">24</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><span class="hll"><span class="linenos">25</span> <span class="bp">self</span><span class="o">.</span><span class="n">quest_storage_attribute_key</span><span class="p">,</span>
|
||
</span><span class="hll"><span class="linenos">26</span> <span class="bp">self</span><span class="o">.</span><span class="n">quest_classes</span><span class="p">,</span>
|
||
</span><span class="hll"><span class="linenos">27</span> <span class="n">category</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">quest_storage_attribute_category</span><span class="p">,</span>
|
||
</span><span class="linenos">28</span> <span class="p">)</span>
|
||
<span class="linenos">29</span>
|
||
<span class="linenos">30</span> <span class="k">def</span><span class="w"> </span><span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">quest_key</span><span class="p">):</span>
|
||
<span class="linenos">31</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">quests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">quest_key</span><span class="p">)</span>
|
||
<span class="linenos">32</span>
|
||
<span class="linenos">33</span> <span class="k">def</span><span class="w"> </span><span class="nf">all</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="linenos">34</span> <span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">quests</span><span class="o">.</span><span class="n">values</span><span class="p">())</span>
|
||
<span class="linenos">35</span>
|
||
<span class="linenos">36</span> <span class="k">def</span><span class="w"> </span><span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">quest_class</span><span class="p">):</span>
|
||
<span class="linenos">37</span> <span class="bp">self</span><span class="o">.</span><span class="n">quest_classes</span><span class="p">[</span><span class="n">quest_class</span><span class="o">.</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">quest_class</span>
|
||
<span class="linenos">38</span> <span class="bp">self</span><span class="o">.</span><span class="n">quests</span><span class="p">[</span><span class="n">quest_class</span><span class="o">.</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">quest_class</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">questhandler</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
|
||
<span class="linenos">39</span> <span class="bp">self</span><span class="o">.</span><span class="n">_save</span><span class="p">()</span>
|
||
<span class="linenos">40</span>
|
||
<span class="linenos">41</span> <span class="k">def</span><span class="w"> </span><span class="nf">remove</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">quest_key</span><span class="p">):</span>
|
||
<span class="linenos">42</span> <span class="n">quest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">quests</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="n">quest_key</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="linenos">43</span> <span class="bp">self</span><span class="o">.</span><span class="n">quest_classes</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="n">quest_key</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="linenos">44</span> <span class="bp">self</span><span class="o">.</span><span class="n">quests</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="n">quest_key</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="linenos">45</span> <span class="bp">self</span><span class="o">.</span><span class="n">_save</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<aside class="sidebar">
|
||
<p class="sidebar-title">Persistent handler pattern</p>
|
||
<p>Persistent handlers are commonly used throughout Evennia. You can read more about them in the <a class="reference internal" href="../../Tutorial-Persistent-Handler.html"><span class="std std-doc">Making a Persistent object Handler</span></a> tutorial.</p>
|
||
</aside>
|
||
<ul class="simple">
|
||
<li><p><strong>Line 9</strong>: We know that the quests themselves will be Python classes inheriting from <code class="docutils literal notranslate"><span class="pre">EvAdventureQuest</span></code> (which we haven’t created yet). We will store those classes in <code class="docutils literal notranslate"><span class="pre">self.quest_classes</span></code> on the handler. Note that there is a difference between a class and an <em>instance</em> of a class! The class cannot hold any <em>state</em> on its own, such as the status of that quest is for this particular character. The class only holds python code.</p></li>
|
||
<li><p><strong>Line 10</strong>: We set aside another property on the handler - <code class="docutils literal notranslate"><span class="pre">self.quest</span></code> This is dictionary that will hold <code class="docutils literal notranslate"><span class="pre">EvAdventureQuest</span></code> <em>instances</em>.</p></li>
|
||
<li><p><strong>Line 11</strong>: Note that we call the <code class="docutils literal notranslate"><span class="pre">self._load()</span></code> method here, this loads up data from the database whenever this handler is accessed.</p></li>
|
||
<li><p><strong>Lines 14-18</strong>: We use <code class="docutils literal notranslate"><span class="pre">self.obj.attributes.get</span></code> to fetch an <a class="reference internal" href="../../../Components/Attributes.html"><span class="std std-doc">Attribute</span></a> on the Character named <code class="docutils literal notranslate"><span class="pre">_quests</span></code> and with a category of <code class="docutils literal notranslate"><span class="pre">evadventure</span></code>. If it doesn’t exist yet (because we never started any quests), we just return an empty dict.</p></li>
|
||
<li><p><strong>Line 21</strong>: Here we loop over all the classes and instantiate them. We haven’t defined how these quest-classes look yet, but by instantiating them with <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> (the Character) we should be covered - from the Character class the quest will be able to get to everything else (this handler itself will be accessible as <code class="docutils literal notranslate"><span class="pre">obj.quests</span></code> from that quest instance after all).</p></li>
|
||
<li><p><strong>Line 24</strong>: Here we do the corresponding save operation.</p></li>
|
||
</ul>
|
||
<p>The rest of the handler are just access methods for getting, adding and removing quests from the handler. We make one assumption in those code, namely that the quest class has a property <code class="docutils literal notranslate"><span class="pre">.key</span></code> being the unique quest-name.</p>
|
||
<p>This is how it would be used in practice:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in some questing code </span>
|
||
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">evennia</span><span class="w"> </span><span class="kn">import</span> <span class="n">search_object</span>
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">evadventure</span><span class="w"> </span><span class="kn">import</span> <span class="n">quests</span>
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">EvAdventureSuperQuest</span><span class="p">(</span><span class="n">quests</span><span class="o">.</span><span class="n">EvAdventureQuest</span><span class="p">):</span>
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"superquest"</span>
|
||
<span class="c1"># quest implementation here</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">start_super_quest</span><span class="p">(</span><span class="n">character</span><span class="p">):</span>
|
||
<span class="n">character</span><span class="o">.</span><span class="n">quests</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">EvAdventureSuperQuest</span><span class="p">)</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<aside class="sidebar">
|
||
<p class="sidebar-title">What can be saved in Attributes?</p>
|
||
<p>For more details, see <a class="reference internal" href="../../../Components/Attributes.html#what-types-of-data-can-i-save-in-an-attribute"><span class="std std-ref">the Attributes documentation</span></a> on the matter.</p>
|
||
</aside>
|
||
<p>We chose to store classes and not instances of classes above. The reason for this has to do with what can be stored in a database <code class="docutils literal notranslate"><span class="pre">Attribute</span></code> - one limitation of an Attribute is that we can’t save a class instance <em>with other database entities baked inside it</em>. If we saved quest instances as-is, it’s highly likely they’d contain database entities ‘hidden’ inside them - a reference to the Character, maybe to objects required for the quest to be complete etc. Evennia would fail trying to save that data.
|
||
Instead we store only the classes, instantiate those classes with the Character, and let the quest store its state flags separately, like this:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/quests.py </span>
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">EvAdventureQuestHandler</span><span class="p">:</span>
|
||
|
||
<span class="c1"># ... </span>
|
||
<span class="n">quest_data_attribute_template</span> <span class="o">=</span> <span class="s2">"_quest_data_</span><span class="si">{quest_key}</span><span class="s2">"</span>
|
||
<span class="n">quest_data_attribute_category</span> <span class="o">=</span> <span class="s2">"evadventure"</span>
|
||
|
||
<span class="c1"># ... </span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">save_quest_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">quest_key</span><span class="p">):</span>
|
||
<span class="n">quest</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">quest_key</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">quest</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">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">quest_data_attribute_template</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">quest_key</span><span class="o">=</span><span class="n">quest_key</span><span class="p">),</span>
|
||
<span class="n">quest</span><span class="o">.</span><span class="n">data</span><span class="p">,</span>
|
||
<span class="n">category</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">quest_data_attribute_category</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">load_quest_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">quest_key</span><span class="p">):</span>
|
||
<span class="k">return</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">quest_data_attribute_template</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">quest_key</span><span class="o">=</span><span class="n">quest_key</span><span class="p">),</span>
|
||
<span class="n">category</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">quest_data_attribute_category</span><span class="p">,</span>
|
||
<span class="n">default</span><span class="o">=</span><span class="p">{},</span>
|
||
<span class="p">)</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>This works the same as the <code class="docutils literal notranslate"><span class="pre">_load</span></code> and <code class="docutils literal notranslate"><span class="pre">_save</span></code> methods, except it fetches a property <code class="docutils literal notranslate"><span class="pre">.data</span></code> (this will be a <code class="docutils literal notranslate"><span class="pre">dict</span></code>) on the quest instance and save it. As long as we make sure to call these methods from the quest the quest whenever that <code class="docutils literal notranslate"><span class="pre">.data</span></code> property is changed, all will be well - this is because Attributes know how to properly analyze a <code class="docutils literal notranslate"><span class="pre">dict</span></code> to find and safely serialize any database entities found within.</p>
|
||
<p>Our handler is ready. We created the <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> class back in the <a class="reference internal" href="Beginner-Tutorial-Characters.html"><span class="std std-doc">Character lesson</span></a> - let’s add quest-support to it.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/characters.py</span>
|
||
|
||
<span class="c1"># ...</span>
|
||
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">evennia.utils</span><span class="w"> </span><span class="kn">import</span> <span class="n">lazy_property</span>
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">evadventure.quests</span><span class="w"> </span><span class="kn">import</span> <span class="n">EvAdventureQuestHandler</span>
|
||
|
||
<span class="k">class</span><span class="w"> </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="w"> </span><span class="nf">quests</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">EvAdventureQuestHandler</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
|
||
|
||
<span class="c1"># ...</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>We also need a way to represent the quests themselves though!</p>
|
||
</section>
|
||
<section id="the-quest-class">
|
||
<h2><span class="section-number">14.2. </span>The Quest class<a class="headerlink" href="#the-quest-class" title="Link to this heading">¶</a></h2>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1"># in evadventure/quests.py</span>
|
||
<span class="linenos"> 2</span>
|
||
<span class="linenos"> 3</span><span class="c1"># ...</span>
|
||
<span class="linenos"> 4</span>
|
||
<span class="linenos"> 5</span><span class="k">class</span><span class="w"> </span><span class="nc">EvAdventureQuest</span><span class="p">:</span>
|
||
<span class="linenos"> 6</span>
|
||
<span class="hll"><span class="linenos"> 7</span> <span class="n">key</span> <span class="o">=</span> <span class="s2">"base-quest"</span>
|
||
</span><span class="linenos"> 8</span> <span class="n">desc</span> <span class="o">=</span> <span class="s2">"Base quest"</span>
|
||
<span class="linenos"> 9</span> <span class="n">start_step</span> <span class="o">=</span> <span class="s2">"start"</span>
|
||
<span class="hll"><span class="linenos">10</span>
|
||
</span><span class="hll"><span class="linenos">11</span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">quester</span><span class="p">,</span> <span class="n">questhandler</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||
</span><span class="hll"><span class="linenos">12</span> <span class="bp">self</span><span class="o">.</span><span class="n">quester</span> <span class="o">=</span> <span class="n">quester</span>
|
||
</span><span class="hll"><span class="linenos">13</span> <span class="bp">self</span><span class="o">.</span><span class="n">_questhandler</span> <span class="o">=</span> <span class="n">questhandler</span>
|
||
</span><span class="hll"><span class="linenos">14</span> <span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">questhandler</span><span class="o">.</span><span class="n">load_quest_data</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="p">)</span>
|
||
</span><span class="linenos">15</span> <span class="bp">self</span><span class="o">.</span><span class="n">_current_step</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_data</span><span class="p">(</span><span class="s2">"current_step"</span><span class="p">)</span>
|
||
<span class="linenos">16</span>
|
||
<span class="hll"><span class="linenos">17</span> <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_step</span><span class="p">:</span>
|
||
</span><span class="linenos">18</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_step</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">start_step</span>
|
||
<span class="linenos">19</span>
|
||
<span class="linenos">20</span> <span class="k">def</span><span class="w"> </span><span class="nf">add_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||
<span class="linenos">21</span> <span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
|
||
<span class="linenos">22</span> <span class="bp">self</span><span class="o">.</span><span class="n">questhandler</span><span class="o">.</span><span class="n">save_quest_data</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="linenos">23</span>
|
||
<span class="hll"><span class="linenos">24</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||
</span><span class="linenos">25</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">default</span><span class="p">)</span>
|
||
<span class="linenos">26</span>
|
||
<span class="linenos">27</span> <span class="k">def</span><span class="w"> </span><span class="nf">remove_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
|
||
<span class="linenos">28</span> <span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="linenos">29</span> <span class="bp">self</span><span class="o">.</span><span class="n">questhandler</span><span class="o">.</span><span class="n">save_quest_data</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="linenos">30</span>
|
||
<span class="hll"><span class="linenos">31</span> <span class="nd">@property</span>
|
||
</span><span class="linenos">32</span> <span class="k">def</span><span class="w"> </span><span class="nf">questhandler</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="linenos">33</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_questhandler</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_questhandler</span> <span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">quester</span><span class="o">.</span><span class="n">quests</span>
|
||
<span class="linenos">34</span>
|
||
<span class="linenos">35</span> <span class="nd">@property</span>
|
||
<span class="linenos">36</span> <span class="k">def</span><span class="w"> </span><span class="nf">current_step</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="linenos">37</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_current_step</span>
|
||
<span class="linenos">38</span>
|
||
<span class="linenos">39</span> <span class="nd">@current_step</span><span class="o">.</span><span class="n">setter</span>
|
||
<span class="linenos">40</span> <span class="k">def</span><span class="w"> </span><span class="nf">current_step</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">step_name</span><span class="p">):</span>
|
||
<span class="linenos">41</span> <span class="bp">self</span><span class="o">.</span><span class="n">_current_step</span> <span class="o">=</span> <span class="n">step_name</span>
|
||
<span class="linenos">42</span> <span class="bp">self</span><span class="o">.</span><span class="n">add_data</span><span class="p">(</span><span class="s2">"current_step"</span><span class="p">,</span> <span class="n">step_name</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<ul class="simple">
|
||
<li><p><strong>Line 7</strong>: Each class must have a <code class="docutils literal notranslate"><span class="pre">.key</span></code> property unquely identifying the quest. We depend on this in the quest-handler.</p></li>
|
||
<li><p><strong>Line 12</strong>: <code class="docutils literal notranslate"><span class="pre">quester</span></code> (the Character) is passed into this class when it is initiated inside <code class="docutils literal notranslate"><span class="pre">EvAdventureQuestHandler._load()</span></code>.</p></li>
|
||
<li><p><strong>Line 13</strong>: The handler is also passed in during loading, so this quest instance can use it directly without triggering recursion during lazy loading.</p></li>
|
||
<li><p><strong>Lines 17, 24 and 31</strong>: <code class="docutils literal notranslate"><span class="pre">add_data</span></code> and <code class="docutils literal notranslate"><span class="pre">remove_data</span></code> call back to <code class="docutils literal notranslate"><span class="pre">questhandler.save_quest_data</span></code> so persistence happens in one place.</p></li>
|
||
</ul>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">add/get/remove_data</span></code> methods are convenient wrappers for getting data in and out of the database. When we implement a quest we should prefer to use <code class="docutils literal notranslate"><span class="pre">.get_data</span></code>, <code class="docutils literal notranslate"><span class="pre">add_data</span></code> and <code class="docutils literal notranslate"><span class="pre">remove_data</span></code> over manipulating <code class="docutils literal notranslate"><span class="pre">.data</span></code> directly, since the former will make sure to save said that to the database automatically.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">current_step</span></code> tracks the current quest ‘step’ we are in; what this means is up to each Quest. We set up convenient properties for setting the <code class="docutils literal notranslate"><span class="pre">current_state</span></code> and also make sure to save it in the data dict as “current_step”.</p>
|
||
<p>The quest can have a few possible statuses: “started”, “completed”, “abandoned” and “failed”. We create a few properties and methods for easily control that, while saving everything under the hood:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/quests.py</span>
|
||
|
||
<span class="c1"># ... </span>
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">EvAdventureQuest</span><span class="p">:</span>
|
||
|
||
<span class="c1"># ... </span>
|
||
|
||
<span class="nd">@property</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">status</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_data</span><span class="p">(</span><span class="s2">"status"</span><span class="p">,</span> <span class="s2">"started"</span><span class="p">)</span>
|
||
|
||
<span class="nd">@status</span><span class="o">.</span><span class="n">setter</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">status</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">add_data</span><span class="p">(</span><span class="s2">"status"</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
|
||
|
||
<span class="nd">@property</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">is_completed</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="s2">"completed"</span>
|
||
|
||
<span class="nd">@property</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">is_abandoned</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="s2">"abandoned"</span>
|
||
|
||
<span class="nd">@property</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">is_failed</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="s2">"failed"</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">complete</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">status</span> <span class="o">=</span> <span class="s2">"completed"</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">abandon</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">status</span> <span class="o">=</span> <span class="s2">"abandoned"</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">fail</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">status</span> <span class="o">=</span> <span class="s2">"failed"</span>
|
||
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>So far we have only added convenience functions for checking statuses. How will the actual “quest” aspect of this work?</p>
|
||
<p>What will happen when the system wants to check the progress of the quest, is that it will call a method <code class="docutils literal notranslate"><span class="pre">.progress()</span></code> on this class. Similarly, to get help for the current step, it will call a method <code class="docutils literal notranslate"><span class="pre">.help()</span></code></p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||
<span class="n">start_step</span> <span class="o">=</span> <span class="s2">"start"</span>
|
||
|
||
<span class="c1"># help entries for quests (could also be methods)</span>
|
||
<span class="n">help_start</span> <span class="o">=</span> <span class="s2">"You need to start first"</span>
|
||
<span class="n">help_end</span> <span class="o">=</span> <span class="s2">"You need to end the quest"</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">progress</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="sa">f</span><span class="s2">"step_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">current_step</span><span class="si">}</span><span class="s2">"</span><span class="p">)(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">help</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">status</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">"abandoned"</span><span class="p">,</span> <span class="s2">"completed"</span><span class="p">,</span> <span class="s2">"failed"</span><span class="p">):</span>
|
||
<span class="n">help_resource</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="sa">f</span><span class="s2">"help_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">status</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span>
|
||
<span class="sa">f</span><span class="s2">"You have </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">status</span><span class="si">}</span><span class="s2"> this quest."</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">help_resource</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="sa">f</span><span class="s2">"help_</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">current_step</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="s2">"No help available."</span><span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="nb">callable</span><span class="p">(</span><span class="n">help_resource</span><span class="p">):</span>
|
||
<span class="c1"># the help_* methods can be used to dynamically generate help</span>
|
||
<span class="k">return</span> <span class="n">help_resource</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="c1"># normally it's just a string</span>
|
||
<span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">help_resource</span><span class="p">)</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<aside class="sidebar">
|
||
<p class="sidebar-title">What’s with the *args, **kwargs?</p>
|
||
<p>These are optional, but allow you to pass extra information into your quest-check. This could be very powerful if you want to add extra context to determine if a quest-step is currently complete or not.</p>
|
||
</aside>
|
||
<p>Calling the <code class="docutils literal notranslate"><span class="pre">.progress(*args,</span> <span class="pre">**kwargs)</span></code> method will call a method named <code class="docutils literal notranslate"><span class="pre">step_<current_step>(*args,</span> <span class="pre">**kwargs)</span></code> on this class. That is, if we are on the <em>start</em> step, the method called will be <code class="docutils literal notranslate"><span class="pre">self.step_start(*args,</span> <span class="pre">**kwargs)</span></code>. Where is this method? It has not been implemented yet! In fact, it’s up to us to implement methods like this for each quest. By just adding a correctly added method, we will easily be able to add more steps to a quest.</p>
|
||
<p>Similarly, calling <code class="docutils literal notranslate"><span class="pre">.help(*args,</span> <span class="pre">**kwargs)</span></code> will try to find a property <code class="docutils literal notranslate"><span class="pre">help_<current_step></span></code>. If this is a callable, it will be called as for example <code class="docutils literal notranslate"><span class="pre">self.help_start(*args,</span> <span class="pre">**kwargs)</span></code>. If it is given as a string, then the string will be returned as-is and the <code class="docutils literal notranslate"><span class="pre">*args,</span> <span class="pre">**kwargs</span></code> will be ignored.</p>
|
||
<section id="example-quest">
|
||
<h3><span class="section-number">14.2.1. </span>Example quest<a class="headerlink" href="#example-quest" title="Link to this heading">¶</a></h3>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in some quest module, like world/myquests.py</span>
|
||
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">evadventure.quests</span><span class="w"> </span><span class="kn">import</span> <span class="n">EvAdventureQuest</span>
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">ShortQuest</span><span class="p">(</span><span class="n">EvAdventureQuest</span><span class="p">):</span>
|
||
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"simple-quest"</span>
|
||
<span class="n">desc</span> <span class="o">=</span> <span class="s2">"A very simple quest."</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">step_start</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Example step!"""</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">quester</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Quest started!"</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">current_step</span> <span class="o">=</span> <span class="s2">"end"</span>
|
||
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">step_end</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">is_completed</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">quester</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Quest ended!"</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">complete</span><span class="p">()</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
<p>This is a very simple quest that will resolve on its own after two <code class="docutils literal notranslate"><span class="pre">.progress()</span></code> checks. Here’s the full life cycle of this quest:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in some module somewhere, using evennia shell or in-game using py</span>
|
||
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">evennia</span><span class="w"> </span><span class="kn">import</span> <span class="n">search_object</span>
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">world.myquests</span><span class="w"> </span><span class="kn">import</span> <span class="n">ShortQuest</span>
|
||
|
||
<span class="n">character</span> <span class="o">=</span> <span class="n">search_object</span><span class="p">(</span><span class="s2">"MyCharacterName"</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
|
||
<span class="n">character</span><span class="o">.</span><span class="n">quests</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">ShortQuest</span><span class="p">)</span>
|
||
|
||
<span class="c1"># this will echo "Quest started!" to character</span>
|
||
<span class="n">character</span><span class="o">.</span><span class="n">quests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"short-quest"</span><span class="p">)</span><span class="o">.</span><span class="n">progress</span><span class="p">()</span>
|
||
<span class="c1"># this will echo "Quest ended!" to character</span>
|
||
<span class="n">character</span><span class="o">.</span><span class="n">quests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"short-quest"</span><span class="p">)</span><span class="o">.</span><span class="n">progress</span><span class="p">()</span>
|
||
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="a-useful-command">
|
||
<h3><span class="section-number">14.2.2. </span>A useful Command<a class="headerlink" href="#a-useful-command" title="Link to this heading">¶</a></h3>
|
||
<p>The player must know which quests they have and be able to inspect them. Here’s a simple <code class="docutils literal notranslate"><span class="pre">quests</span></code> command to handle this:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/quests.py</span>
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">CmdQuests</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""</span>
|
||
<span class="sd"> List all quests and their statuses as well as get info about the status of</span>
|
||
<span class="sd"> a specific quest.</span>
|
||
|
||
<span class="sd"> Usage:</span>
|
||
<span class="sd"> quests</span>
|
||
<span class="sd"> quest <questname></span>
|
||
|
||
<span class="sd"> """</span>
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"quests"</span>
|
||
<span class="n">aliases</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"quest"</span><span class="p">]</span>
|
||
|
||
<span class="k">def</span><span class="w"> </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">quest_name</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">def</span><span class="w"> </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">quest_name</span><span class="p">:</span>
|
||
<span class="n">quest</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">quests</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">quest_name</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">quest</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Quest </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">quest_name</span><span class="si">}</span><span class="s2"> not found."</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Quest </span><span class="si">{</span><span class="n">quest</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">quest</span><span class="o">.</span><span class="n">status</span><span class="si">}</span><span class="se">\n</span><span class="si">{</span><span class="n">quest</span><span class="o">.</span><span class="n">help</span><span class="p">()</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="n">quests</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">quests</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">quests</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">"No quests."</span><span class="p">)</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="k">for</span> <span class="n">quest</span> <span class="ow">in</span> <span class="n">quests</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Quest </span><span class="si">{</span><span class="n">quest</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">quest</span><span class="o">.</span><span class="n">status</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Add this to the <code class="docutils literal notranslate"><span class="pre">CharacterCmdSet</span></code> in <code class="docutils literal notranslate"><span class="pre">mygame/commands/default_cmdsets.py</span></code>. Follow the <a class="reference internal" href="../Part1/Beginner-Tutorial-Adding-Commands.html#add-the-echo-command-to-the-default-cmdset"><span class="std std-ref">Adding a command lesson</span></a> if you are unsure how to do this. Reload and if you are playing as an <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> you should be able to use <code class="docutils literal notranslate"><span class="pre">quests</span></code> to view your quests.</p>
|
||
</section>
|
||
</section>
|
||
<section id="testing">
|
||
<h2><span class="section-number">14.3. </span>Testing<a class="headerlink" href="#testing" title="Link to this heading">¶</a></h2>
|
||
<blockquote>
|
||
<div><p>Create a new folder <code class="docutils literal notranslate"><span class="pre">evadventure/tests/test_quests.py</span></code>.</p>
|
||
</div></blockquote>
|
||
<aside class="sidebar">
|
||
<p>An example test suite for quests is found in <code class="docutils literal notranslate"><span class="pre">evennia/contrib/tutorials/evadventure</span></code>, as <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_quests.html#evennia-contrib-tutorials-evadventure-tests-test-quests"><span class="std std-ref">tests/test_quests.py</span></a>.</p>
|
||
</aside>
|
||
<p>Testing of the quests means creating a test character, making a dummy quest, add it to the character’s quest handler and making sure all methods work correcly. Create the testing quest so that it will automatically step forward when calling <code class="docutils literal notranslate"><span class="pre">.progress()</span></code>, so you can make sure it works as intended.</p>
|
||
</section>
|
||
<section id="conclusions">
|
||
<h2><span class="section-number">14.4. </span>Conclusions<a class="headerlink" href="#conclusions" title="Link to this heading">¶</a></h2>
|
||
<p>What we created here is just the framework for questing. The actual complexity will come when creating the quests themselves (that is, implementing the <code class="docutils literal notranslate"><span class="pre">step_<current_step>(*args,</span> <span class="pre">**kwargs)</span></code> methods), which is something we’ll get to later, in <a class="reference internal" href="../Part4/Beginner-Tutorial-Part4-Overview.html"><span class="std std-doc">Part 4</span></a> of this tutorial.</p>
|
||
</section>
|
||
</section>
|
||
|
||
|
||
<div class="clearer"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sphinxsidebar" role="navigation" aria-label="Main">
|
||
<div class="sphinxsidebarwrapper">
|
||
<p class="logo"><a href="../../../index.html">
|
||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo of Evennia"/>
|
||
</a></p>
|
||
<search 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" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
|
||
<input type="submit" value="Go" />
|
||
</form>
|
||
</div>
|
||
</search>
|
||
<script>document.getElementById('searchbox').style.display = "block"</script>
|
||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">14. Game Quests</a><ul>
|
||
<li><a class="reference internal" href="#the-quest-handler">14.1. The Quest Handler</a></li>
|
||
<li><a class="reference internal" href="#the-quest-class">14.2. The Quest class</a><ul>
|
||
<li><a class="reference internal" href="#example-quest">14.2.1. Example quest</a></li>
|
||
<li><a class="reference internal" href="#a-useful-command">14.2.2. A useful Command</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#testing">14.3. Testing</a></li>
|
||
<li><a class="reference internal" href="#conclusions">14.4. Conclusions</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<div>
|
||
<h4>Previous topic</h4>
|
||
<p class="topless"><a href="Beginner-Tutorial-Dungeon.html"
|
||
title="previous chapter"><span class="section-number">13. </span>Procedurally generated Dungeon</a></p>
|
||
</div>
|
||
<div>
|
||
<h4>Next topic</h4>
|
||
<p class="topless"><a href="Beginner-Tutorial-Shops.html"
|
||
title="next chapter"><span class="section-number">15. </span>In-game Shops</a></p>
|
||
</div>
|
||
<div role="note" aria-label="source link">
|
||
<!--h3>This Page</h3-->
|
||
<ul class="this-page-menu">
|
||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Quests.md.txt"
|
||
rel="nofollow">Show Page Source</a></li>
|
||
</ul>
|
||
</div><h3>Links</h3>
|
||
<ul>
|
||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||
<li>
|
||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||
</li>
|
||
</ul>
|
||
<h3>Doc Versions</h3>
|
||
<ul>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/latest/index.html">latest (main branch)</a>
|
||
</li>
|
||
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/5.x/index.html">v5.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/4.x/index.html">v4.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/3.x/index.html">v3.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/2.x/index.html">v2.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/1.x/index.html">v1.0.0 branch (outdated)</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://www.evennia.com/docs/0.x/index.html">v0.9.5 branch (outdated)</a>
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="clearer"></div>
|
||
</div>
|
||
<div class="related" role="navigation" aria-label="Related">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../../../genindex.html" title="General Index"
|
||
>index</a></li>
|
||
<li class="right" >
|
||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="right" >
|
||
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
|
||
>next</a> |</li>
|
||
<li class="right" >
|
||
<a href="Beginner-Tutorial-Dungeon.html" title="13. Procedurally generated Dungeon"
|
||
>previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">14. </span>Game Quests</a></li>
|
||
</ul>
|
||
</div>
|
||
<div class="footer" role="contentinfo">
|
||
© Copyright 2024, The Evennia developer community.
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.2.3.
|
||
</div>
|
||
</body>
|
||
</html> |