mirror of
https://github.com/evennia/evennia.git
synced 2026-03-29 12:07:17 +02:00
Updated HTML docs.
This commit is contained in:
parent
c8a7741a3c
commit
55bfcc5139
48 changed files with 1752 additions and 455 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# Sphinx build info version 1
|
||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||
config: 47b2acfcc7e68d85f9b32017d81e582e
|
||||
config: 7affaae6c1e8c49761a6482900603589
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
|
|
|||
|
|
@ -684,8 +684,34 @@ Click here to see the full index of all parts and lessons of the Beginner-Tutori
|
|||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#conclusions">11.9. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-AI.html">12. NPC and monster AI</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Dungeon.html">13. Dynamically generated Dungeon</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-AI.html">12. NPC and monster AI</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-AI.html#our-requirements">12.1. Our requirements</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-AI.html#the-aihandler">12.2. The AIHandler</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-AI.html#more-helpers-on-the-ai-handler">12.2.1. More helpers on the AI handler</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-AI.html#adding-ai-to-an-entity">12.3. Adding AI to an entity</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-AI.html#idle-state">12.3.1. Idle state</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-AI.html#roam-state">12.3.2. Roam state</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-AI.html#flee-state">12.3.3. Flee state</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-AI.html#combat-state">12.3.4. Combat state</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-AI.html#unit-testing">12.4. Unit Testing</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-AI.html#conclusions">12.5. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Dungeon.html">13. Procedurally generated Dungeon</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Dungeon.html#design-concept">13.1. Design Concept</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Dungeon.html#the-starting-room">13.1.1. The starting room</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Dungeon.html#generating-new-branch-rooms">13.1.2. Generating new branch rooms</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Dungeon.html#making-the-dungeon-dangerous">13.1.3. Making the dungeon dangerous</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Dungeon.html#difficulty-scaling">13.1.4. Difficulty scaling</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Dungeon.html#implementation">13.2. Implementation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Quests.html">14. Game Quests</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Shops.html">15. In-game Shops</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Commands.html">16. In-game Commands</a></li>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="13. Dynamically generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
|
||||
<link rel="next" title="13. Procedurally generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
|
||||
<link rel="prev" title="11. Turnbased Combat" href="Beginner-Tutorial-Combat-Turnbased.html" />
|
||||
</head><body>
|
||||
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
|
||||
<a href="Beginner-Tutorial-Dungeon.html" title="13. Procedurally generated Dungeon"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Combat-Turnbased.html" title="11. Turnbased Combat"
|
||||
|
|
@ -65,12 +65,33 @@
|
|||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">12. NPC and monster AI</a><ul>
|
||||
<li><a class="reference internal" href="#our-requirements">12.1. Our requirements</a></li>
|
||||
<li><a class="reference internal" href="#the-aihandler">12.2. The AIHandler</a><ul>
|
||||
<li><a class="reference internal" href="#more-helpers-on-the-ai-handler">12.2.1. More helpers on the AI handler</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#adding-ai-to-an-entity">12.3. Adding AI to an entity</a><ul>
|
||||
<li><a class="reference internal" href="#idle-state">12.3.1. Idle state</a></li>
|
||||
<li><a class="reference internal" href="#roam-state">12.3.2. Roam state</a></li>
|
||||
<li><a class="reference internal" href="#flee-state">12.3.3. Flee state</a></li>
|
||||
<li><a class="reference internal" href="#combat-state">12.3.4. Combat state</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#unit-testing">12.4. Unit Testing</a></li>
|
||||
<li><a class="reference internal" href="#conclusions">12.5. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Combat-Turnbased.html"
|
||||
title="previous chapter"><span class="section-number">11. </span>Turnbased Combat</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Dungeon.html"
|
||||
title="next chapter"><span class="section-number">13. </span>Dynamically generated Dungeon</a></p>
|
||||
title="next chapter"><span class="section-number">13. </span>Procedurally generated Dungeon</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
|
|
@ -114,10 +135,501 @@
|
|||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="npc-and-monster-ai">
|
||||
<h1><span class="section-number">12. </span>NPC and monster AI<a class="headerlink" href="#npc-and-monster-ai" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="admonition warning">
|
||||
<p class="admonition-title">Warning</p>
|
||||
<p>This part of the Beginner tutorial is still being developed.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Artificial Intelligence sounds complex</p>
|
||||
<p>The term “Artificial Intelligence” can sound daunting. It evokes images of supercomputers, machine learning, neural networks and large language models. For our use case though, you can get something that feels pretty ‘intelligent’ by just using a few if-statements.</p>
|
||||
</aside>
|
||||
<p>Not every entity in the game are controlled by a player. NPCs and enemies need to be controlled by the computer - that is, we need to give them artificial intelligence (AI).</p>
|
||||
<p>For our game we will implement a type of AI called a ‘state machine’. It means that the entity (like an NPC or mob) is always in a given ‘state’. An example of a state could be ‘idle’, ‘roaming’ or ‘attacking’.
|
||||
At regular intervals, the AI entity will be ‘ticked’ by Evennia. This ‘tick’ starts with an evaluation which determines if the entity should switch to another state, or stay and perform one (or more) actions inside the current state.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Mobs and NPC</p>
|
||||
<p>‘Mob’ is short for ‘Mobile’ and is a common MUD term for an entity that can move between rooms. The term is usually used for aggressive enemies. A Mob is also an ‘NPC’ (Non-Player Character), but the latter term is often used for more peaceful entities, like shopkeeprs and quest givers.</p>
|
||||
</aside>
|
||||
<p>For example, if a mob in a ‘roaming’ state comes upon a player character, it may switch into the ‘attack’ state. In combat it could move between different combat actions, and if it survives combat it would go back to its ‘roaming’ state.</p>
|
||||
<p>The AI can be ‘ticked’ on different time scales depending on how your game works. For example, while a mob is moving, they might automatically move from room to room every 20 seconds. But once it enters turn-based combat (if you use that), the AI will ‘tick’ only on every turn.</p>
|
||||
<section id="our-requirements">
|
||||
<h2><span class="section-number">12.1. </span>Our requirements<a class="headerlink" href="#our-requirements" title="Permalink to this headline">¶</a></h2>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Shopkeepers and quest givers</p>
|
||||
<p>NPC shopkeepers and quest givers will be assumed to always be in the ‘idle’ state in our game - the functionality of talking to or shopping from them will be explored in a future lesson.</p>
|
||||
</aside>
|
||||
<p>For this tutorial game, we’ll need AI entities to be able to be in the following states:</p>
|
||||
<ul class="simple">
|
||||
<li><p><em>Idle</em> - don’t do anything, just stand around.</p></li>
|
||||
<li><p><em>Roam</em> - move from room to room. It’s important that we add the ability to limit where the AI can roam to. For example, if we have non-combat areas we want to be able to <a class="reference internal" href="../../../Components/Locks.html"><span class="doc std std-doc">lock</span></a> all exits leading into those areas so aggressive mods doesn’t walk into them.</p></li>
|
||||
<li><p><em>Combat</em> - initiate and perform combat with PCs. This state will make use of the <a class="reference internal" href="Beginner-Tutorial-Combat-Base.html"><span class="doc std std-doc">Combat Tutorial</span></a> to randomly select combat actions (turn-based or tick-based as appropriately).</p></li>
|
||||
<li><p><em>Flee</em> - this is like <em>Roam</em> except the AI will move so as to avoid entering rooms with PCs, if possible.</p></li>
|
||||
</ul>
|
||||
<p>We will organize the AI code like this:</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">AIHandler</span></code> this will be a handler stored as <code class="docutils literal notranslate"><span class="pre">.ai</span></code> on the AI entity. It is responsible for storing the AI’s state. To ‘tick’ the AI, we run <code class="docutils literal notranslate"><span class="pre">.ai.run()</span></code>. How often we crank the wheels of the AI this way we leave up to other game systems.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">.ai_<state_name></span></code> methods on the NPC/Mob class - when the <code class="docutils literal notranslate"><span class="pre">ai.run()</span></code> method is called, it is responsible for finding a method named like its current state (e.g. <code class="docutils literal notranslate"><span class="pre">.ai_combat</span></code> if we are in the <em>combat</em> state). Having methods like this makes it easy to add new states - just add a new method named appropriately and the AI now knows how to handle that state!</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="the-aihandler">
|
||||
<h2><span class="section-number">12.2. </span>The AIHandler<a class="headerlink" href="#the-aihandler" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This is the core logic for managing AI states. Create a new file <code class="docutils literal notranslate"><span class="pre">evadventure/ai.py</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span>
|
||||
<span class="normal">22</span>
|
||||
<span class="normal">23</span>
|
||||
<span class="normal">24</span>
|
||||
<span class="normal">25</span>
|
||||
<span class="normal">26</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/ai.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia.logger</span> <span class="kn">import</span> <span class="n">log_trace</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">AIHandler</span><span class="p">:</span>
|
||||
<span class="n">attribute_name</span> <span class="o">=</span> <span class="s2">"ai_state"</span>
|
||||
<span class="n">attribute_category</span> <span class="o">=</span> <span class="s2">"ai_state"</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="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="n">obj</span>
|
||||
</span><span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">ai_state</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">attribute_name</span><span class="p">,</span>
|
||||
</span><span class="hll"> <span class="n">category</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">attribute_category</span><span class="p">,</span>
|
||||
</span><span class="hll"> <span class="n">default</span><span class="o">=</span><span class="s2">"idle"</span><span class="p">)</span>
|
||||
</span> <span class="k">def</span> <span class="nf">set_state</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">state</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai_state</span> <span class="o">=</span> <span class="n">state</span>
|
||||
<span class="hll"> <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">attribute_name</span><span class="p">,</span> <span class="n">state</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">attribute_category</span><span class="p">)</span>
|
||||
</span>
|
||||
<span class="k">def</span> <span class="nf">get_state</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">ai_state</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="hll"> <span class="n">state</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_state</span><span class="p">()</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="sa">f</span><span class="s2">"ai_</span><span class="si">{</span><span class="n">state</span><span class="si">}</span><span class="s2">"</span><span class="p">)()</span>
|
||||
<span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
|
||||
<span class="n">log_trace</span><span class="p">(</span><span class="sa">f</span><span class="s2">"AI error in </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2"> (running state: </span><span class="si">{</span><span class="n">state</span><span class="si">}</span><span class="s2">)"</span><span class="p">)</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<p>The AIHandler is an example of an <a class="reference internal" href="../../Tutorial-Persistent-Handler.html"><span class="doc std std-doc">Object Handler</span></a>. This is a design style that groups all functionality together. To look-ahead a little, this handler will be added to the object like this:</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">lazy_property</p>
|
||||
<p>This is an Evennia <a class="reference external" href="https://realpython.com/primer-on-python-decorators/">@decorator</a> that makes it so that the handler won’t be initialized until someone actually tries to access <code class="docutils literal notranslate"><span class="pre">obj.ai</span></code> for the first time. On subsequent calls, the already initialized handler is returned. This is a very useful performance optimization when you have a lot of objects and also important for the functionality of handlers.</p>
|
||||
</aside>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># just an example, don't put this anywhere yet</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">lazy_property</span>
|
||||
<span class="kn">from</span> <span class="nn">evadventure.ai</span> <span class="kn">import</span> <span class="n">AIHandler</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">MyMob</span><span class="p">(</span><span class="n">SomeParent</span><span class="p">):</span>
|
||||
|
||||
<span class="nd">@lazy_property</span>
|
||||
<span class="k">class</span> <span class="nc">ai</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="n">AIHandler</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>So in short, accessing the <code class="docutils literal notranslate"><span class="pre">.ai</span></code> property will initialize an instance of <code class="docutils literal notranslate"><span class="pre">AIHandler</span></code>, to which we pass <code class="docutils literal notranslate"><span class="pre">self</span></code> (the current object). In the <code class="docutils literal notranslate"><span class="pre">AIHandler.__init__</span></code> we take this input and store it as <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> (<strong>lines 10-13</strong>). This way the handler can always operate on the entity it’s “sitting on” by accessing <code class="docutils literal notranslate"><span class="pre">self.obj</span></code>. The <code class="docutils literal notranslate"><span class="pre">lazy_property</span></code> makes sure that this initialization only happens once per server reload.</p>
|
||||
<p>More key functionality:</p>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 11</strong>: We (re)load the AI state by accessing <code class="docutils literal notranslate"><span class="pre">self.obj.attributes.get()</span></code>. This loads a database <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attribute</span></a> with a given name and category. If one is not (yet) saved, return “idle”. Note that we must access <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> (the NPC/mob) since that is the only thing with access to the database.</p></li>
|
||||
<li><p><strong>Line 16</strong>: In the <code class="docutils literal notranslate"><span class="pre">set_state</span></code> method we force the handler to switch to a given state. When we do, we make sure to save it to the database as well, so its state survives a reload. But we also store it in <code class="docutils literal notranslate"><span class="pre">self.ai_state</span></code> so we don’t need to hit the database on every fetch.</p></li>
|
||||
<li><p><strong>line 23</strong>: The <code class="docutils literal notranslate"><span class="pre">getattr</span></code> function is an in-built Python function for getting a named property on an object. This allows us to, based on the current state, call a method <code class="docutils literal notranslate"><span class="pre">ai_<statename></span></code> defined on the NPC/mob. We must wrap this call in a <code class="docutils literal notranslate"><span class="pre">try...except</span></code> block to properly handle errors in the AI method. Evennia’s <code class="docutils literal notranslate"><span class="pre">log_trace</span></code> will make sure to log the error, including its traceback for debugging.</p></li>
|
||||
</ul>
|
||||
<section id="more-helpers-on-the-ai-handler">
|
||||
<h3><span class="section-number">12.2.1. </span>More helpers on the AI handler<a class="headerlink" href="#more-helpers-on-the-ai-handler" title="Permalink to this headline">¶</a></h3>
|
||||
<p>It’s also convenient to put a few helpers on the AIHandler. This makes them easily available from inside the <code class="docutils literal notranslate"><span class="pre">ai_<state></span></code> methods, callable as e.g. <code class="docutils literal notranslate"><span class="pre">self.ai.get_targets()</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span>
|
||||
<span class="normal">22</span>
|
||||
<span class="normal">23</span>
|
||||
<span class="normal">24</span>
|
||||
<span class="normal">25</span>
|
||||
<span class="normal">26</span>
|
||||
<span class="normal">27</span>
|
||||
<span class="normal">28</span>
|
||||
<span class="normal">29</span>
|
||||
<span class="normal">30</span>
|
||||
<span class="normal">31</span>
|
||||
<span class="normal">32</span>
|
||||
<span class="normal">33</span>
|
||||
<span class="normal">34</span>
|
||||
<span class="normal">35</span>
|
||||
<span class="normal">36</span>
|
||||
<span class="normal">37</span>
|
||||
<span class="normal">38</span>
|
||||
<span class="normal">39</span>
|
||||
<span class="normal">40</span>
|
||||
<span class="normal">41</span>
|
||||
<span class="normal">42</span>
|
||||
<span class="normal">43</span>
|
||||
<span class="normal">44</span>
|
||||
<span class="normal">45</span>
|
||||
<span class="normal">46</span>
|
||||
<span class="normal">47</span>
|
||||
<span class="normal">48</span>
|
||||
<span class="normal">49</span>
|
||||
<span class="normal">50</span>
|
||||
<span class="normal">51</span>
|
||||
<span class="normal">52</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/ai.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
<span class="kn">import</span> <span class="nn">random</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">AIHandler</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_targets</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Get a list of potential targets for the NPC to combat.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">return</span> <span class="p">[</span><span class="n">obj</span> <span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">contents</span> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="s2">"is_pc"</span><span class="p">)</span> <span class="ow">and</span> <span class="n">obj</span><span class="o">.</span><span class="n">is_pc</span><span class="p">]</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_traversable_exits</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exclude_destination</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Get a list of exits that the NPC can traverse. Optionally exclude a destination.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> exclude_destination (Object, optional): Exclude exits with this destination.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">return</span> <span class="p">[</span>
|
||||
<span class="n">exi</span>
|
||||
<span class="k">for</span> <span class="n">exi</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">exits</span>
|
||||
<span class="k">if</span> <span class="n">exi</span><span class="o">.</span><span class="n">destination</span> <span class="o">!=</span> <span class="n">exclude_destination</span> <span class="ow">and</span> <span class="n">exi</span><span class="o">.</span><span class="n">access</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">"traverse"</span><span class="p">)</span>
|
||||
<span class="p">]</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">random_probability</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">probabilities</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Given a dictionary of probabilities, return the key of the chosen probability.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> probabilities (dict): A dictionary of probabilities, where the key is the action and the</span>
|
||||
<span class="sd"> value is the probability of that action.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="c1"># sort probabilities from higheest to lowest, making sure to normalize them 0..1</span>
|
||||
<span class="hll"> <span class="n">prob_total</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">probabilities</span><span class="o">.</span><span class="n">values</span><span class="p">())</span>
|
||||
</span><span class="hll"> <span class="n">sorted_probs</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span>
|
||||
</span> <span class="p">((</span><span class="n">key</span><span class="p">,</span> <span class="n">prob</span> <span class="o">/</span> <span class="n">prob_total</span><span class="p">)</span> <span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">prob</span> <span class="ow">in</span> <span class="n">probabilities</span><span class="o">.</span><span class="n">items</span><span class="p">()),</span>
|
||||
<span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span>
|
||||
<span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="hll"> <span class="n">rand</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span>
|
||||
</span> <span class="n">total</span> <span class="o">=</span> <span class="mi">0</span>
|
||||
<span class="hll"> <span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">prob</span> <span class="ow">in</span> <span class="n">sorted_probs</span><span class="p">:</span>
|
||||
</span> <span class="n">total</span> <span class="o">+=</span> <span class="n">prob</span>
|
||||
<span class="k">if</span> <span class="n">rand</span> <span class="o"><=</span> <span class="n">total</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="n">key</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Locking exits</p>
|
||||
<p>The ‘traverse’ lock is the default lock-type checked by Evennia before allowing something to pass through an exit. Since only PCs have the <code class="docutils literal notranslate"><span class="pre">is_pc</span></code> property, we could lock down exits to <em>only</em> allow entities with the property to pass through.</p>
|
||||
<p>In game:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>lock north = traverse:attr(is_pc, True)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Or in code:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>exit_obj.locks.add(
|
||||
"traverse:attr(is_ic, True)")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>See <a class="reference internal" href="../../../Components/Locks.html"><span class="doc std std-doc">Locks</span></a> for a lot more information about Evennia locks.</p>
|
||||
</aside>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">get_targets</span></code> checks if any of the other objects in the same location as the <code class="docutils literal notranslate"><span class="pre">is_pc</span></code> property set on their typeclass. For simplicity we assume Mobs will only ever attack PCs (no monster in-fighting!).</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">get_traversable_exits</span></code> fetches all valid exits from the current location, excluding those with a provided destination <em>or</em> those which doesn’t pass the “traverse” access check.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">get_random_probability</span></code> takes a dict <code class="docutils literal notranslate"><span class="pre">{action:</span> <span class="pre">probability,</span> <span class="pre">...}</span></code>. This will randomly select an action, but the higher the probability, the more likely it is that it will be picked. We will use this for the combat state later, to allow different combatants to more or less likely to perform different combat actions. This algorithm uses a few useful Python tools:</p>
|
||||
<ul>
|
||||
<li><p><strong>Line 41</strong>: Remember <code class="docutils literal notranslate"><span class="pre">probabilities</span></code> is a <code class="docutils literal notranslate"><span class="pre">dict</span></code> <code class="docutils literal notranslate"><span class="pre">{key:</span> <span class="pre">value,</span> <span class="pre">...}</span></code>, where the values are the probabilities. So <code class="docutils literal notranslate"><span class="pre">probabilities.values()</span></code> gets us a list of only the probabilities. Running <code class="docutils literal notranslate"><span class="pre">sum()</span></code> on them gets us the total sum of those probabilities. We need that to normalize all probabilities between 0 and 1.0 on the line below.</p></li>
|
||||
<li><p><strong>Lines 42-46</strong>: Here we create a new iterable of tuples <code class="docutils literal notranslate"><span class="pre">(key,</span> <span class="pre">prob/prob_total)</span></code>. We sort them using the Python <code class="docutils literal notranslate"><span class="pre">sorted</span></code> helper. The <code class="docutils literal notranslate"><span class="pre">key=lambda</span> <span class="pre">x:</span> <span class="pre">x[1]</span></code> means that we sort on the second element of each tuple (the probability). The <code class="docutils literal notranslate"><span class="pre">reverse=True</span></code> means that we’ll sort from highest probability to lowest.</p></li>
|
||||
<li><p><strong>Line 47</strong>:The <code class="docutils literal notranslate"><span class="pre">random.random()</span></code> call generates a random value between 0 and 1.</p></li>
|
||||
<li><p><strong>Line 49</strong>: Since the probabilities are sorted from highest to lowest, we loop over them until we find the first one fitting in the random value - this is the action/key we are looking for.</p></li>
|
||||
<li><p>To give an example, if you have a <code class="docutils literal notranslate"><span class="pre">probability</span></code> input of <code class="docutils literal notranslate"><span class="pre">{"attack":</span> <span class="pre">0.5,</span> <span class="pre">"defend":</span> <span class="pre">0.1,</span> <span class="pre">"idle":</span> <span class="pre">0.4}</span></code>, this would become a sorted iterable <code class="docutils literal notranslate"><span class="pre">(("attack",</span> <span class="pre">0.5),</span> <span class="pre">("idle",</span> <span class="pre">0.4),</span> <span class="pre">("defend":</span> <span class="pre">0.1))</span></code>, and if <code class="docutils literal notranslate"><span class="pre">random.random()</span></code> returned 0.65, the outcome would be “idle”. If <code class="docutils literal notranslate"><span class="pre">random.random()</span></code> returned <code class="docutils literal notranslate"><span class="pre">0.90</span></code>, it would be “defend”. That is, this AI entity would attack 50% of the time, idle 40% and defend 10% of the time.</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
<section id="adding-ai-to-an-entity">
|
||||
<h2><span class="section-number">12.3. </span>Adding AI to an entity<a class="headerlink" href="#adding-ai-to-an-entity" title="Permalink to this headline">¶</a></h2>
|
||||
<p>All we need to add AI-support to a game entity is to add the AI handler and a bunch of <code class="docutils literal notranslate"><span class="pre">.ai_statename()</span></code> methods onto that object’s typeclass.</p>
|
||||
<p>We already sketched out NPCs and Mob typeclasses back in the <span class="xref myst">NPC tutorial</span>. Open <code class="docutils literal notranslate"><span class="pre">evadventure/npcs.py</span></code> and expand the so-far empty <code class="docutils literal notranslate"><span class="pre">EvAdventureMob</span></code> class.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/npcs.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">lazy_property</span>
|
||||
<span class="kn">from</span> <span class="nn">.ai</span> <span class="kn">import</span> <span class="n">AIHandler</span>
|
||||
|
||||
<span class="c1"># ... </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="nd">@lazy_property</span>
|
||||
<span class="k">def</span> <span class="nf">ai</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="n">AIHandler</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">ai_idle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">ai_roam</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">ai_roam</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">ai_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">ai_flee</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>All the remaining logic will go into each state-method.</p>
|
||||
<section id="idle-state">
|
||||
<h3><span class="section-number">12.3.1. </span>Idle state<a class="headerlink" href="#idle-state" title="Permalink to this headline">¶</a></h3>
|
||||
<p>In the idle state the mob does nothing, so we just leave the <code class="docutils literal notranslate"><span class="pre">ai_idle</span></code> method as it is - with just an empty <code class="docutils literal notranslate"><span class="pre">pass</span></code> in it. This means that it will also not attack PCs in the same room - but if a PC attacks it, we must make sure to force it into a combat state (otherwise it will be defenseless).</p>
|
||||
</section>
|
||||
<section id="roam-state">
|
||||
<h3><span class="section-number">12.3.2. </span>Roam state<a class="headerlink" href="#roam-state" title="Permalink to this headline">¶</a></h3>
|
||||
<p>In this state the mob should move around from room to room until it finds PCs to attack.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/npcs.py</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="kn">import</span> <span class="nn">random</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"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">ai_roam</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> roam, moving randomly to a new room. If a target is found, switch to combat state.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="n">targets</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_targets</span><span class="p">():</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"combat"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">execute_cmd</span><span class="p">(</span><span class="sa">f</span><span class="s2">"attack </span><span class="si">{</span><span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">targets</span><span class="p">)</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">exits</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_traversable_exits</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="n">exits</span><span class="p">:</span>
|
||||
<span class="n">exi</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">exits</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">execute_cmd</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">exi</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>Every time the AI is ticked, this method will be called. It will first check if there are any valid targets in the room (using the <code class="docutils literal notranslate"><span class="pre">get_targets()</span></code> helper we made on the <code class="docutils literal notranslate"><span class="pre">AIHandler</span></code>). If so, we switch to the <code class="docutils literal notranslate"><span class="pre">combat</span></code> state and immediately call the <code class="docutils literal notranslate"><span class="pre">attack</span></code> command to initiate/join combat (see the <a class="reference internal" href="Beginner-Tutorial-Combat-Base.html"><span class="doc std std-doc">Combat tutorial</span></a>).</p>
|
||||
<p>If no target is found, we get a list of traversible exits (exits that fail the <code class="docutils literal notranslate"><span class="pre">traverse</span></code> lock check is already excluded from this list). Using Python’s in-bult <code class="docutils literal notranslate"><span class="pre">random.choice</span></code> function we grab a random exit from that list and moves through it by its name.</p>
|
||||
</section>
|
||||
<section id="flee-state">
|
||||
<h3><span class="section-number">12.3.3. </span>Flee state<a class="headerlink" href="#flee-state" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Flee is similar to <em>Roam</em> except the the AI never tries to attack anything and will make sure to not return the way it came.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/npcs.py</span>
|
||||
|
||||
<span class="c1"># ... </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"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">ai_flee</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Flee from the current room, avoiding going back to the room from which we came. If no exits</span>
|
||||
<span class="sd"> are found, switch to roam state.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">current_room</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span>
|
||||
<span class="n">past_room</span> <span class="o">=</span> <span class="bp">self</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="s2">"past_room"</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"ai_state"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
|
||||
<span class="n">exits</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_traversable_exits</span><span class="p">(</span><span class="n">exclude_destination</span><span class="o">=</span><span class="n">past_room</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">exits</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s2">"past_room"</span><span class="p">,</span> <span class="n">current_room</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"ai_state"</span><span class="p">)</span>
|
||||
<span class="n">exi</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">exits</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">execute_cmd</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">exi</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="c1"># if in a dead end, roam will allow for backing out</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"roam"</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We store the <code class="docutils literal notranslate"><span class="pre">past_room</span></code> in an Attribute “past_room” on ourselves and make sure to exclude it when trying to find random exits to traverse to.</p>
|
||||
<p>If we end up in a dead end we switch to <em>Roam</em> mode so that it can get back out (and also start attacking things again). So the effect of this is that the mob will flee in terror as far as it can before ‘calming down’.</p>
|
||||
</section>
|
||||
<section id="combat-state">
|
||||
<h3><span class="section-number">12.3.4. </span>Combat state<a class="headerlink" href="#combat-state" title="Permalink to this headline">¶</a></h3>
|
||||
<p>While in the combat state, the mob will use one of the combat systems we’ve designed (either <a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html"><span class="doc std std-doc">twitch-based combat</span></a> or <a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html"><span class="doc std std-doc">turn-based combat</span></a>). This means that every time the AI ticks, and we are in the combat state, the entity needs to perform one of the available combat actions, <em>hold</em>, <em>attack</em>, <em>do a stunt</em>, <em>use an item</em> or <em>flee</em>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span>
|
||||
<span class="normal">22</span>
|
||||
<span class="normal">23</span>
|
||||
<span class="normal">24</span>
|
||||
<span class="normal">25</span>
|
||||
<span class="normal">26</span>
|
||||
<span class="normal">27</span>
|
||||
<span class="normal">28</span>
|
||||
<span class="normal">29</span>
|
||||
<span class="normal">30</span>
|
||||
<span class="normal">31</span>
|
||||
<span class="normal">32</span>
|
||||
<span class="normal">33</span>
|
||||
<span class="normal">34</span>
|
||||
<span class="normal">35</span>
|
||||
<span class="normal">36</span>
|
||||
<span class="normal">37</span>
|
||||
<span class="normal">38</span>
|
||||
<span class="normal">39</span>
|
||||
<span class="normal">40</span>
|
||||
<span class="normal">41</span>
|
||||
<span class="normal">42</span>
|
||||
<span class="normal">43</span>
|
||||
<span class="normal">44</span>
|
||||
<span class="normal">45</span>
|
||||
<span class="normal">46</span>
|
||||
<span class="normal">47</span>
|
||||
<span class="normal">48</span>
|
||||
<span class="normal">49</span>
|
||||
<span class="normal">50</span>
|
||||
<span class="normal">51</span>
|
||||
<span class="normal">52</span>
|
||||
<span class="normal">53</span>
|
||||
<span class="normal">54</span>
|
||||
<span class="normal">55</span>
|
||||
<span class="normal">56</span>
|
||||
<span class="normal">57</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/npcs.py </span>
|
||||
|
||||
<span class="c1"># ... </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="hll"> <span class="n">combat_probabilities</span> <span class="o">=</span> <span class="p">{</span>
|
||||
</span> <span class="s2">"hold"</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span>
|
||||
<span class="s2">"attack"</span><span class="p">:</span> <span class="mf">0.85</span><span class="p">,</span>
|
||||
<span class="s2">"stunt"</span><span class="p">:</span> <span class="mf">0.05</span><span class="p">,</span>
|
||||
<span class="s2">"item"</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span>
|
||||
<span class="s2">"flee"</span><span class="p">:</span> <span class="mf">0.05</span><span class="p">,</span>
|
||||
<span class="p">}</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">ai_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Manage the combat/combat state of the mob.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="hll"> <span class="k">if</span> <span class="n">combathandler</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">nbd</span><span class="o">.</span><span class="n">combathandler</span><span class="p">:</span>
|
||||
</span> <span class="c1"># already in combat</span>
|
||||
<span class="hll"> <span class="n">allies</span><span class="p">,</span> <span class="n">enemies</span> <span class="o">=</span> <span class="n">combathandler</span><span class="o">.</span><span class="n">get_sides</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
|
||||
</span><span class="hll"> <span class="n">action</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">random_probability</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combat_probabilities</span><span class="p">)</span>
|
||||
</span>
|
||||
<span class="k">match</span> <span class="n">action</span><span class="p">:</span>
|
||||
<span class="k">case</span> <span class="s2">"hold"</span><span class="p">:</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">({</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"hold"</span><span class="p">})</span>
|
||||
<span class="k">case</span> <span class="s2">"combat"</span><span class="p">:</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">({</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"attack"</span><span class="p">,</span> <span class="s2">"target"</span><span class="p">:</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">enemies</span><span class="p">)})</span>
|
||||
<span class="k">case</span> <span class="s2">"stunt"</span><span class="p">:</span>
|
||||
<span class="c1"># choose a random ally to help</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span>
|
||||
<span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="s2">"stunt"</span><span class="p">,</span>
|
||||
<span class="s2">"recipient"</span><span class="p">:</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">allies</span><span class="p">),</span>
|
||||
<span class="s2">"advantage"</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
|
||||
<span class="s2">"stunt"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">,</span>
|
||||
<span class="s2">"defense"</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="p">}</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">case</span> <span class="s2">"item"</span><span class="p">:</span>
|
||||
<span class="c1"># use a random item on a random ally</span>
|
||||
<span class="n">target</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">allies</span><span class="p">)</span>
|
||||
<span class="n">valid_items</span> <span class="o">=</span> <span class="p">[</span><span class="n">item</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">contents</span> <span class="k">if</span> <span class="n">item</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">target</span><span class="p">)]</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span>
|
||||
<span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"item"</span><span class="p">,</span> <span class="s2">"item"</span><span class="p">:</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">valid_items</span><span class="p">),</span> <span class="s2">"target"</span><span class="p">:</span> <span class="n">target</span><span class="p">}</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">case</span> <span class="s2">"flee"</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"flee"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">elif</span> <span class="ow">not</span> <span class="p">(</span><span class="n">targets</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_targets</span><span class="p">()):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"roam"</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">target</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">targets</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">execute_cmd</span><span class="p">(</span><span class="sa">f</span><span class="s2">"attack </span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Lines 7-13</strong>: This dict describe how likely the mob is to perform a given combat action. By just modifying this dictionary we can easily creating mobs that behave very differently, like using items more or being more prone to fleeing. You can also turn off certain action entirely - by default his mob never “holds” or “uses items”.</p></li>
|
||||
<li><p><strong>Line 22</strong>: If we are in combat, a <code class="docutils literal notranslate"><span class="pre">CombadHandler</span></code> should be initialized on us, available as as <code class="docutils literal notranslate"><span class="pre">self.ndb.combathandler</span></code> (see the <a class="reference internal" href="Beginner-Tutorial-Combat-Base.html"><span class="doc std std-doc">base combat tutorial</span></a>).</p></li>
|
||||
<li><p><strong>Line 24</strong>: The <code class="docutils literal notranslate"><span class="pre">combathandler.get_sides()</span></code> produces the allies and enemies for the one passed to it.</p></li>
|
||||
<li><p><strong>Line 25</strong>: Now that <code class="docutils literal notranslate"><span class="pre">random_probability</span></code> method we created earlier in this lesson becomes handy!</p></li>
|
||||
</ul>
|
||||
<p>The rest of this method just takes the randomly chosen action and performs the required operations to queue it as a new action with the <code class="docutils literal notranslate"><span class="pre">CombatHandler</span></code>. For simplicity, we only use stunts to boost our allies, not to hamper our enemies.</p>
|
||||
<p>Finally, if we are not currently in combat and there are no enemies nearby, we switch to roaming - otherwise we start another fight!</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="unit-testing">
|
||||
<h2><span class="section-number">12.4. </span>Unit Testing<a class="headerlink" href="#unit-testing" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Create a new file <code class="docutils literal notranslate"><span class="pre">evadventure/tests/test_ai.py</span></code>.</p>
|
||||
</div></blockquote>
|
||||
<p>Testing the AI handler and mob is straightforward if you have followed along with previous lessons. Create an <code class="docutils literal notranslate"><span class="pre">EvAdventureMob</span></code> and test that calling the various ai-related methods and handlers on it works as expected. A complexity is to mock the output from <code class="docutils literal notranslate"><span class="pre">random</span></code> so that you always get the same random result to compare against. We leave the implementation of AI tests as an extra exercise for the reader.</p>
|
||||
</section>
|
||||
<section id="conclusions">
|
||||
<h2><span class="section-number">12.5. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>You can easily expand this simple system to make Mobs more ‘clever’. For example, instead of just randomly decide which action to take in combat, the mob could consider more factors - maybe some support mobs could use stunts to pave the way for their heavy hitters or use health potions when badly hurt.</p>
|
||||
<p>It’s also simple to add a ‘hunt’ state, where mobs check adjoining rooms for targets before moving there.</p>
|
||||
<p>And while implementing a functional game AI system requires no advanced math or machine learning techniques, there’s of course no limit to what kind of advanced things you could add if you really wanted to!</p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
||||
|
|
@ -136,7 +648,7 @@
|
|||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
|
||||
<a href="Beginner-Tutorial-Dungeon.html" title="13. Procedurally generated Dungeon"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Combat-Turnbased.html" title="11. Turnbased Combat"
|
||||
|
|
|
|||
|
|
@ -1487,7 +1487,7 @@ This is new compared to the base handler.</p>
|
|||
<section id="testing">
|
||||
<h2><span class="section-number">11.7. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline">¶</a></h2>
|
||||
<aside class="sidebar">
|
||||
<p>See <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia-contrib-tutorials-evadventure-tests-test-combat"><span class="std std-ref">evennia/contrib/tutorials/evadventure/tests/test_combat.py</span></a></p>
|
||||
<p>See an example tests in <code class="docutils literal notranslate"><span class="pre">evennia/contrib/tutorials</span></code>, in <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia-contrib-tutorials-evadventure-tests-test-combat"><span class="std std-ref">evadventure/tests/test_combat.py</span></a></p>
|
||||
</aside>
|
||||
<p>Unit testing of the Turnbased combat handler is straight forward, you follow the process of earlier lessons to test that each method on the handler returns what you expect with mocked inputs.</p>
|
||||
<p>Unit-testing the menu is more complex. You will find examples of doing this in <a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/utils/testss/test_evmenu.py">evennia.utils.tests.test_evmenu</a>.</p>
|
||||
|
|
@ -1503,7 +1503,7 @@ This is new compared to the base handler.</p>
|
|||
<li><p>An item (like a potion) we can <code class="docutils literal notranslate"><span class="pre">use</span></code>.</p></li>
|
||||
</ul>
|
||||
<aside class="sidebar">
|
||||
<p>You can find an example batch-code script in <a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/contrib/tutorials/evadventure/batchscripts/turnbased_combat_demo.py">evennia/contrib/tutorials/evadventure/batchscripts/turnbased_combat_demo.py</a></p>
|
||||
<p>You can find an example combat batch-code script in <code class="docutils literal notranslate"><span class="pre">evennia/contrib/tutorials/evadventure/</span></code>, in <a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/contrib/tutorials/evadventure/batchscripts/turnbased_combat_demo.py">batchscripts/turnbased_combat_demo.py</a></p>
|
||||
</aside>
|
||||
<p>In <a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html"><span class="doc std std-doc">The Twitch combat lesson</span></a> we used a <a class="reference internal" href="../../../Components/Batch-Command-Processor.html"><span class="doc std std-doc">batch-command script</span></a> to create the testing environment in game. This runs in-game Evennia commands in sequence. For demonstration purposes we’ll instead use a <a class="reference internal" href="../../../Components/Batch-Code-Processor.html"><span class="doc std std-doc">batch-code script</span></a>, which runs raw Python code in a repeatable way. A batch-code script is much more flexible than a batch-command script.</p>
|
||||
<blockquote>
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ The<span class="w"> </span>battle<span class="w"> </span>is<span class="w"> </sp
|
|||
<section id="general-principle">
|
||||
<h2><span class="section-number">10.1. </span>General principle<a class="headerlink" href="#general-principle" title="Permalink to this headline">¶</a></h2>
|
||||
<aside class="sidebar">
|
||||
<p>An example of an implemented Twitch combat system can be found in <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia-contrib-tutorials-evadventure-combat-twitch"><span class="std std-ref">evennia/contrib/tutorials/evadventure/combat_twitch.py</span></a>.</p>
|
||||
<p>An example of an implemented Twitch combat system can be found in <code class="docutils literal notranslate"><span class="pre">evennia/contrib/tutorials</span></code>, in <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia-contrib-tutorials-evadventure-combat-twitch"><span class="std std-ref">evadventure/combat_twitch.py</span></a>.</p>
|
||||
</aside>
|
||||
<p>Here is the general design of the Twitch-based combat handler:</p>
|
||||
<ul class="simple">
|
||||
|
|
@ -1177,7 +1177,7 @@ You<span class="w"> </span><span class="o">(</span>Wounded<span class="o">)</spa
|
|||
<section id="unit-testing">
|
||||
<h2><span class="section-number">10.5. </span>Unit Testing<a class="headerlink" href="#unit-testing" title="Permalink to this headline">¶</a></h2>
|
||||
<aside class="sidebar">
|
||||
<p>See <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia-contrib-tutorials-evadventure-tests-test-combat"><span class="std std-ref">evennia/contrib/tutorials/evadventure/tests/test_combat.py</span></a> for an example of a full suite of combat tests.</p>
|
||||
<p>For examples of unit tests, see <code class="docutils literal notranslate"><span class="pre">evennia/contrib/tutorials</span></code>, in <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia-contrib-tutorials-evadventure-tests-test-combat"><span class="std std-ref">evadventure/tests/test_combat.py</span></a> for an example of a full suite of combat tests.</p>
|
||||
</aside>
|
||||
<blockquote>
|
||||
<div><p>Create <code class="docutils literal notranslate"><span class="pre">evadventure/tests/test_combat.py</span></code> (if you don’t already have it).</p>
|
||||
|
|
@ -1217,7 +1217,7 @@ You<span class="w"> </span><span class="o">(</span>Wounded<span class="o">)</spa
|
|||
<section id="a-small-combat-test">
|
||||
<h2><span class="section-number">10.6. </span>A small combat test<a class="headerlink" href="#a-small-combat-test" title="Permalink to this headline">¶</a></h2>
|
||||
<aside class="sidebar">
|
||||
<p>You can find an example batch-command script in <a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/contrib/tutorials/evadventure/batchscripts/twitch_combat_demo.ev">evennia/contrib/tutorials/evadventure/batchscripts/twitch_combat_demo.ev</a></p>
|
||||
<p>You can find an example batch-command script at <code class="docutils literal notranslate"><span class="pre">evennia/contrib/tutorials/evadventure</span></code>, in <a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/contrib/tutorials/evadventure/batchscripts/twitch_combat_demo.ev">batchscripts/twitch_combat_demo.ev</a></p>
|
||||
</aside>
|
||||
<p>Showing that the individual pieces of code works (unit testing) is not enough to be sure that your combat system is actually working. We need to test all the pieces <em>together</em>. This is often called <em>functional testing</em>. While functional testing can also be automated, wouldn’t it be fun to be able to actually see our code in action?</p>
|
||||
<p>This is what we need for a minimal test:</p>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
||||
|
||||
<title>13. Dynamically generated Dungeon — Evennia latest documentation</title>
|
||||
<title>13. Procedurally generated Dungeon — Evennia latest 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>
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
<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">13. </span>Dynamically generated Dungeon</a></li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">13. </span>Procedurally generated Dungeon</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
@ -65,6 +65,21 @@
|
|||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">13. Procedurally generated Dungeon</a><ul>
|
||||
<li><a class="reference internal" href="#design-concept">13.1. Design Concept</a><ul>
|
||||
<li><a class="reference internal" href="#the-starting-room">13.1.1. The starting room</a></li>
|
||||
<li><a class="reference internal" href="#generating-new-branch-rooms">13.1.2. Generating new branch rooms</a></li>
|
||||
<li><a class="reference internal" href="#making-the-dungeon-dangerous">13.1.3. Making the dungeon dangerous</a></li>
|
||||
<li><a class="reference internal" href="#difficulty-scaling">13.1.4. Difficulty scaling</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#implementation">13.2. Implementation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-AI.html"
|
||||
title="previous chapter"><span class="section-number">12. </span>NPC and monster AI</a></p>
|
||||
|
|
@ -112,12 +127,223 @@
|
|||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="dynamically-generated-dungeon">
|
||||
<h1><span class="section-number">13. </span>Dynamically generated Dungeon<a class="headerlink" href="#dynamically-generated-dungeon" title="Permalink to this headline">¶</a></h1>
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="procedurally-generated-dungeon">
|
||||
<h1><span class="section-number">13. </span>Procedurally generated Dungeon<a class="headerlink" href="#procedurally-generated-dungeon" title="Permalink to this headline">¶</a></h1>
|
||||
<p>The rooms that we discussed in the <a class="reference internal" href="Beginner-Tutorial-Rooms.html"><span class="doc std std-doc">lesson about Rooms</span></a> are all <em>manually</em> generated. That is, a human builder would have to sit down and spawn each room manually, either in-game or using code.</p>
|
||||
<p>In this lesson we’ll explore <em>procedural</em> generation of the rooms making up our game’s underground dungeon. Procedural means that its rooms are spawned automatically and semi-randomly as players explore, creating a different dungeon layout every time.</p>
|
||||
<section id="design-concept">
|
||||
<h2><span class="section-number">13.1. </span>Design Concept<a class="headerlink" href="#design-concept" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This describes how the procedural generation should work at a high level. It’s important to understand this before we start writing code.</p>
|
||||
<p>We will assume our dungeon exists on a 2D plane (x,y, no z directions). We will only use N,E,S,W compass directions, but there is no reason this design couldn’t work with SE, NW etc, except that this could make it harder for the player to visualize. More possible directions also make it more likely to produce collisions and one-way exits (see below).</p>
|
||||
<p>This design is pretty simple, but just by playing with some of its settings, it can produce very different-feeling dungeon systems.</p>
|
||||
<section id="the-starting-room">
|
||||
<h3><span class="section-number">13.1.1. </span>The starting room<a class="headerlink" href="#the-starting-room" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The idea is that all players will descend down a well to get to the start of the dungeon. The bottom of the well is a statically created room that won’t change.</p>
|
||||
<div class="literal-block-wrapper docutils container" id="id1">
|
||||
<div class="code-block-caption"><span class="caption-text">Starting room</span><a class="headerlink" href="#id1" title="Permalink to this code">¶</a></div>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span> Branch N
|
||||
▲
|
||||
│
|
||||
┌────────┼────────┐
|
||||
│ │n │
|
||||
│ ▼ │
|
||||
│ │
|
||||
│ e│
|
||||
Branch W ◄─┼─► up▲ ◄─┼─► Branch E1
|
||||
│w │
|
||||
│ │
|
||||
│ ▲ │
|
||||
│ │s │
|
||||
└────────┼────────┘
|
||||
│
|
||||
▼
|
||||
Branch S
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<p>The magic happens when you choose one of the exits from this room (except the one leading you back to the surface). Let’s assume a PC descends down to the start room and moves <code class="docutils literal notranslate"><span class="pre">east</span></code>:</p>
|
||||
<ul class="simple">
|
||||
<li><p>The first person to go east will spawn a new “Dungeon branch” (Branch E1 in the diagram). This is a separate “instance” of dungeon compared to what would spawn if moving through any of the other exits. Rooms spawned within one dungeon branch will never overlap with that of another dungeon branch.</p></li>
|
||||
<li><p>A timer starts. While this timer is active, everyone going <code class="docutils literal notranslate"><span class="pre">east</span></code> will end up in Branch E1. This allows for players to team up and collaborate to take on a branch.</p></li>
|
||||
<li><p>After the timer runs out, everyone going <code class="docutils literal notranslate"><span class="pre">east</span></code> will instead end up in a <em>new</em> Branch E2. This is a new branch that has no overlap with Branch E1.</p></li>
|
||||
<li><p>PCs in Branches E1 and E2 can always retreat <code class="docutils literal notranslate"><span class="pre">west</span></code> back to the starting room, but after the timer runs out this is now a one-way exit - they won’t be able to return to their old branches if they do.</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="generating-new-branch-rooms">
|
||||
<h3><span class="section-number">13.1.2. </span>Generating new branch rooms<a class="headerlink" href="#generating-new-branch-rooms" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Each branch is managed by an branch <em>orchestrator</em>. The orchestrator tracks the layout of rooms belonging to this branch on an (X, Y) coordinate grid.</p>
|
||||
<div class="literal-block-wrapper docutils container" id="id2">
|
||||
<div class="code-block-caption"><span class="caption-text">Creating the eastern branch and its first room</span><a class="headerlink" href="#id2" title="Permalink to this code">¶</a></div>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span> ?
|
||||
▲
|
||||
│
|
||||
┌─────────┐ ┌────┼────┐
|
||||
│ │ │A │ │
|
||||
│ │ │ PC │
|
||||
│ start◄─┼───┼─► is ──┼──►?
|
||||
│ │ │ here │
|
||||
│ │ │ │ │
|
||||
└─────────┘ └────┼────┘
|
||||
│
|
||||
▼
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<p>The start room is always at coordinate <code class="docutils literal notranslate"><span class="pre">(0,</span> <span class="pre">0)</span></code>.</p>
|
||||
<p>A dungeon room is only created when actually moving to it. In the above example, the PC moved <code class="docutils literal notranslate"><span class="pre">east</span></code> from the start room, which initiated a new dungeon branch with its own branch orchestrator. The orchestrator also created a new room (room <code class="docutils literal notranslate"><span class="pre">A</span></code>) at coordinate <code class="docutils literal notranslate"><span class="pre">(1,0)</span></code>. In this case it (randomly) seeded this room with three exits <code class="docutils literal notranslate"><span class="pre">north</span></code>, <code class="docutils literal notranslate"><span class="pre">east</span></code> and <code class="docutils literal notranslate"><span class="pre">south</span></code>.
|
||||
Since this branch was just created, the exit back to the start room is still two-way.</p>
|
||||
<p>This is the procedure the orchestrator follows when spawning a new room:</p>
|
||||
<ul class="simple">
|
||||
<li><p>It always creates an exit back to the room we came from.</p></li>
|
||||
<li><p>It checks how many unexplored exits we have in the dungeon right now. That is, how many exits we haven’t yet traversed. This number must never be zero unless we want a dungeon that can be ‘finished’. The maximum number of unexplored exits open at any given time is a setting we can experiment with. A small max number leads to linear dungeon, a bigger number makes the dungeon sprawling and maze-like.</p></li>
|
||||
<li><p>Outgoing exits (exits not leading back to where we came) are generated with the following rules:</p>
|
||||
<ul>
|
||||
<li><p>Randomly create between 0 and the number of outgoing exits allowed by the room and the branches’ current budget of allowed open unexplored exits.</p></li>
|
||||
<li><p>Create 0 outgoing exits (a dead-end) only if this would leave at least one unexplored exit open somewhere in the dungeon branch.</p></li>
|
||||
<li><p>Do <em>not</em> create an exit that would connect the exit to a previously generated room (so we prefer exits leading to new places rather than back to old ones)</p></li>
|
||||
<li><p>If a previously created exit end up pointing to a newly created room, this <em>is</em> allowed, and is the only time a one-way exit will happen (example below). All other exits are always two-way exits. This also presents the only small chance of closing out a dungeon with no way to proceed but to return to the start.</p></li>
|
||||
<li><p>Never create an exit back to the start room (e.g. from another direction). The only way to get back to the start room is by back tracking.</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>In the following examples, we assume the maximum number of unexplored exits allowed open at any time is set to 4.</p>
|
||||
<div class="literal-block-wrapper docutils container" id="id3">
|
||||
<div class="code-block-caption"><span class="caption-text">After four steps in the eastern dungeon branch</span><a class="headerlink" href="#id3" title="Permalink to this code">¶</a></div>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span> ?
|
||||
▲
|
||||
│
|
||||
┌─────────┐ ┌────┼────┐
|
||||
│ │ │A │ │
|
||||
│ │ │ │
|
||||
│ start◄─┼───┼─ ──┼─►?
|
||||
│ │ │ ▲ │
|
||||
│ │ │ │ │
|
||||
└─────────┘ └────┼────┘
|
||||
│
|
||||
┌────┼────┐ ┌─────────┐ ┌─────────┐
|
||||
│B │ │ │C │ │D │
|
||||
│ ▼ │ │ │ │ PC │
|
||||
?◄──┼─ ◄─┼───┼─► ◄─┼───┼─► is │
|
||||
│ │ │ │ │ here │
|
||||
│ │ │ │ │ │
|
||||
└─────────┘ └─────────┘ └─────────┘
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="simple">
|
||||
<li><p>PC moves <code class="docutils literal notranslate"><span class="pre">east</span></code> from the start room. A new room <code class="docutils literal notranslate"><span class="pre">A</span></code> (coordinate <code class="docutils literal notranslate"><span class="pre">(1,</span> <span class="pre">0)</span></code> ) is created. After a while the exit back to the start room becomes a one-way exit. The branch can have at most 4 unexplored exits, and the orchestrator randomly adds three additional exits out of room <code class="docutils literal notranslate"><span class="pre">A</span></code>.</p></li>
|
||||
<li><p>PC moves <code class="docutils literal notranslate"><span class="pre">south</span></code>. A new room <code class="docutils literal notranslate"><span class="pre">B</span></code> (<code class="docutils literal notranslate"><span class="pre">(1,-1)</span></code>) is created, with two random exits, which is as many as the orchetrator is allowed to create at this time (4 are now open). It also always creates an exit back to the previous room (<code class="docutils literal notranslate"><span class="pre">A</span></code>)</p></li>
|
||||
<li><p>PC moves <code class="docutils literal notranslate"><span class="pre">east</span></code> (coordinate (<code class="docutils literal notranslate"><span class="pre">(2,</span> <span class="pre">-1)</span></code>). A new room <code class="docutils literal notranslate"><span class="pre">C</span></code> is created. The orchestrator already has 3 exits unexplored, so it can only add one exit our of this room.</p></li>
|
||||
<li><p>PC moves <code class="docutils literal notranslate"><span class="pre">east</span></code> (<code class="docutils literal notranslate"><span class="pre">(3,</span> <span class="pre">-1)</span></code>). While the orchestrator still has a budget of one exit, it knows there are other unexplored exits elsewhere, and is allowed to randomly create 0 exits. This is a dead end. The PC must go back and explore another direction.</p></li>
|
||||
</ol>
|
||||
<p>Let’s change the dungeon a bit to do another example:</p>
|
||||
<div class="literal-block-wrapper docutils container" id="id4">
|
||||
<div class="code-block-caption"><span class="caption-text">Looping around</span><a class="headerlink" href="#id4" title="Permalink to this code">¶</a></div>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span> ?
|
||||
▲
|
||||
│
|
||||
┌─────────┐ ┌────┼────┐
|
||||
│ │ │A │ │
|
||||
│ │ │ │
|
||||
│ start◄─┼───┼─ ──┼──►?
|
||||
│ │ │ ▲ │
|
||||
│ │ │ │ │ ?
|
||||
└─────────┘ └────┼────┘ ▲
|
||||
│ │
|
||||
┌────┼────┐ ┌────┼────┐
|
||||
│B │ │ │C │ │
|
||||
│ ▼ │ │ PC │
|
||||
?◄──┼─ ◄─┼───┼─► is │
|
||||
│ │ │ here │
|
||||
│ │ │ │
|
||||
└─────────┘ └─────────┘
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<p>In this example the PC moved <code class="docutils literal notranslate"><span class="pre">east</span></code>, <code class="docutils literal notranslate"><span class="pre">south</span></code>, <code class="docutils literal notranslate"><span class="pre">east</span></code> but the exit out of room <code class="docutils literal notranslate"><span class="pre">C</span></code> is leading north, into a coordinate where <code class="docutils literal notranslate"><span class="pre">A</span></code> already has an exit pointing to. Going <code class="docutils literal notranslate"><span class="pre">north</span></code> here leads to the following:</p>
|
||||
<div class="literal-block-wrapper docutils container" id="id5">
|
||||
<div class="code-block-caption"><span class="caption-text">Creation of a one-way exit</span><a class="headerlink" href="#id5" title="Permalink to this code">¶</a></div>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span> ?
|
||||
▲
|
||||
│
|
||||
┌─────────┐ ┌────┼────┐ ┌─────────┐
|
||||
│ │ │A │ │ │D PC │
|
||||
│ │ │ │ │ is │
|
||||
│ start◄─┼───┼─ ──┼───┼─► here │
|
||||
│ │ │ ▲ │ │ ▲ │
|
||||
│ │ │ │ │ │ │ │
|
||||
└─────────┘ └────┼────┘ └────┼────┘
|
||||
│ │
|
||||
┌────┼────┐ ┌────┼────┐
|
||||
│B │ │ │C │ │
|
||||
│ ▼ │ │ ▼ │
|
||||
?◄──┼─ ◄─┼───┼─► │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
└─────────┘ └─────────┘
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<p>As the PC moves <code class="docutils literal notranslate"><span class="pre">north</span></code>, the room <code class="docutils literal notranslate"><span class="pre">D</span></code> is created at <code class="docutils literal notranslate"><span class="pre">(2,0)</span></code>.</p>
|
||||
<p>While <code class="docutils literal notranslate"><span class="pre">C</span></code> to <code class="docutils literal notranslate"><span class="pre">D</span></code> get a two-way exit as normal, this creates a one-way exit from <code class="docutils literal notranslate"><span class="pre">A</span></code> to <code class="docutils literal notranslate"><span class="pre">D</span></code>.</p>
|
||||
<p>Whichever exit leads to actually creating the room gets the two-way exit, so if the PC had walked back from <code class="docutils literal notranslate"><span class="pre">C</span></code> and created room <code class="docutils literal notranslate"><span class="pre">D</span></code> by going <code class="docutils literal notranslate"><span class="pre">east</span></code> from room <code class="docutils literal notranslate"><span class="pre">A</span></code>, then the one-way exit would be from room <code class="docutils literal notranslate"><span class="pre">C</span></code> instead.</p>
|
||||
<blockquote>
|
||||
<div><p>If the maximum allowed number of open unexplored exits is small, this case is the only situation where it’s possible to ‘finish’ the dungeon (having no more unexplored exits to follow). We accept this as a case where the PCs just have to turn back.</p>
|
||||
</div></blockquote>
|
||||
<div class="literal-block-wrapper docutils container" id="id6">
|
||||
<div class="code-block-caption"><span class="caption-text">Never link back to start room</span><a class="headerlink" href="#id6" title="Permalink to this code">¶</a></div>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span> ?
|
||||
▲
|
||||
│
|
||||
┌─────────┐ ┌────┼────┐ ┌─────────┐
|
||||
│ │ │A │ │ │D │
|
||||
│ │ │ │ │ │
|
||||
│ start◄─┼───┼─ ──┼───┼─► │
|
||||
│ │ │ ▲ │ │ ▲ │
|
||||
│ │ │ │ │ │ │ │
|
||||
└─────────┘ └────┼────┘ └────┼────┘
|
||||
│ │
|
||||
┌─────────┐ ┌────┼────┐ ┌────┼────┐
|
||||
│E │ │B │ │ │C │ │
|
||||
│ PC │ │ ▼ │ │ ▼ │
|
||||
│ is ◄─┼───┼─► ◄─┼───┼─► │
|
||||
│ here │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
└─────────┘ └─────────┘ └─────────┘
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<p>Here the PC moved <code class="docutils literal notranslate"><span class="pre">west</span></code> from room <code class="docutils literal notranslate"><span class="pre">B</span></code> creating room <code class="docutils literal notranslate"><span class="pre">E</span></code> at <code class="docutils literal notranslate"><span class="pre">(0,</span> <span class="pre">-1)</span></code>.</p>
|
||||
<p>The orchestrator never creates a link back to the start room, but it <em>could</em> have created up to two new exits <code class="docutils literal notranslate"><span class="pre">west</span></code> and/or <code class="docutils literal notranslate"><span class="pre">south</span></code>. Since there’s still an unexplored exit <code class="docutils literal notranslate"><span class="pre">north</span></code> from room <code class="docutils literal notranslate"><span class="pre">A</span></code>, the orchestrator is also allowed to randomly assign 0 exits, which is what it did here.</p>
|
||||
<p>The PC needs to backtrack and go <code class="docutils literal notranslate"><span class="pre">north</span></code> from <code class="docutils literal notranslate"><span class="pre">A</span></code> to continue exploring this dungeon branch.</p>
|
||||
</section>
|
||||
<section id="making-the-dungeon-dangerous">
|
||||
<h3><span class="section-number">13.1.3. </span>Making the dungeon dangerous<a class="headerlink" href="#making-the-dungeon-dangerous" title="Permalink to this headline">¶</a></h3>
|
||||
<p>A dungeon would not be interesting without peril! There needs to be monsters to slay, puzzles to solve and treasure to be had.</p>
|
||||
<p>When PCs first enters a room, that room is marked as <code class="docutils literal notranslate"><span class="pre">not</span> <span class="pre">clear</span></code>. While a room is not cleared, the PCs <em>cannot use any of the unexplored exits out of that room</em>. They <em>can</em> still retreat back the way they came unless they become locked in combat, in which case they have to flee from that first.</p>
|
||||
<p>Once PCs have overcome the challenge of the room (and probably earned some reward), will it change to <code class="docutils literal notranslate"><span class="pre">clear</span></code> . A room can auto-clear if it is spawned empty or has no challenge meant to block the PCs (like a written hint for a puzzle elsewhere).</p>
|
||||
<p>Note that clear/non-clear only relates to the challenge associated with that room. Roaming monsters (see the <a class="reference internal" href="Beginner-Tutorial-AI.html"><span class="doc std std-doc">AI tutorial</span></a>) can lead to combat taking place in previously ‘cleared’ rooms.</p>
|
||||
</section>
|
||||
<section id="difficulty-scaling">
|
||||
<h3><span class="section-number">13.1.4. </span>Difficulty scaling<a class="headerlink" href="#difficulty-scaling" title="Permalink to this headline">¶</a></h3>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Risk and reward</p>
|
||||
<p>The concept of dungeon depth/difficulty works well together with limited resources. If healing is limited to what can be carried, this leads to players having to decide if they want to risk push deeper or take their current spoils and retreat back to the surface to recover.</p>
|
||||
</aside>
|
||||
<p>The “difficulty” of the dungeon is measured by the “depth” PCs have delved to. This is given as the <em>radial distance</em> from the start room, rounded down, found by the good old <a class="reference external" href="https://en.wikipedia.org/wiki/Pythagorean_theorem">Pythagorean theorem</a>:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>depth = int(math.sqrt(x**2 + y**2))
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>So if you are in room <code class="docutils literal notranslate"><span class="pre">(1,</span> <span class="pre">1)</span></code> you are at difficulty 1. Conversely at room coordinate <code class="docutils literal notranslate"><span class="pre">(4,-5)</span></code> the difficulty is 6. Increasing depth should lead to tougher challenges but greater rewards.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="implementation">
|
||||
<h2><span class="section-number">13.2. </span>Implementation<a class="headerlink" href="#implementation" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="admonition warning">
|
||||
<p class="admonition-title">Warning</p>
|
||||
<p>This part of the Beginner tutorial is still being developed.</p>
|
||||
<p>TODO: This part is TODO.</p>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
||||
|
|
@ -145,7 +371,7 @@
|
|||
<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">13. </span>Dynamically generated Dungeon</a></li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">13. </span>Procedurally generated Dungeon</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -204,8 +204,7 @@
|
|||
<span class="normal">63</span>
|
||||
<span class="normal">64</span>
|
||||
<span class="normal">65</span>
|
||||
<span class="normal">66</span>
|
||||
<span class="normal">67</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/npcs.py </span>
|
||||
<span class="normal">66</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/npcs.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span><span class="p">,</span> <span class="n">AttributeProperty</span>
|
||||
|
||||
|
|
@ -264,14 +263,13 @@
|
|||
<span class="sd"> """</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span>
|
||||
<span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"npcs"</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"group"</span><span class="p">)</span>
|
||||
</span>
|
||||
<span class="hll"> <span class="k">def</span> <span class="nf">ai_next_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
</span><span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> The system should regularly poll this method to have </span>
|
||||
<span class="sd"> the NPC do their next AI action. </span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span>
|
||||
</span>
|
||||
<span class="hll">
|
||||
</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="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Mob(ile) NPC to be used for enemies.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<ul class="simple">
|
||||
|
|
@ -281,8 +279,8 @@
|
|||
<li><p><strong>Lines 17, 18</strong>: The <code class="docutils literal notranslate"><span class="pre">morale</span></code> and <code class="docutils literal notranslate"><span class="pre">allegiance</span></code> are <em>Knave</em> properties determining how likely the NPC is to flee in a combat situation and if they are hostile or friendly.</p></li>
|
||||
<li><p><strong>Line 19</strong>: The <code class="docutils literal notranslate"><span class="pre">is_idle</span></code> Attribute is a useful property. It should be available on all NPCs and will be used to disable AI entirely.</p></li>
|
||||
<li><p><strong>Line 59</strong>: We make sure to tag NPCs. We may want to group different NPCs together later, for example to have all NPCs with the same tag respond if one of them is attacked.</p></li>
|
||||
<li><p><strong>Line 61</strong>: The <code class="docutils literal notranslate"><span class="pre">ai_next_action</span></code> is a method we prepare for the system to be able to ask the NPC ‘what do you want to do next?’. In it we will add all logic related to the artificial intelligence of the NPC - such as walking around, attacking and performing other actions.</p></li>
|
||||
</ul>
|
||||
<p>We make an empty subclass <code class="docutils literal notranslate"><span class="pre">EvAdventureMob</span></code>. A ‘mob’ (short for ‘mobile’) is a common MUD term for NPCs that can move around on their own. We will in the future use this class to represent enemies in the game. We will get back to this class [in the lesson about adding AI][Beginner-Tutoroal-AI].</p>
|
||||
</section>
|
||||
<section id="testing">
|
||||
<h2><span class="section-number">8.2. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline">¶</a></h2>
|
||||
|
|
|
|||
|
|
@ -271,8 +271,19 @@ of experience using Evennia and be really helpful for doing your own thing later
|
|||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#conclusions">11.9. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-AI.html">12. NPC and monster AI</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Dungeon.html">13. Dynamically generated Dungeon</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-AI.html">12. NPC and monster AI</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-AI.html#our-requirements">12.1. Our requirements</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-AI.html#the-aihandler">12.2. The AIHandler</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-AI.html#adding-ai-to-an-entity">12.3. Adding AI to an entity</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-AI.html#unit-testing">12.4. Unit Testing</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-AI.html#conclusions">12.5. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Dungeon.html">13. Procedurally generated Dungeon</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Dungeon.html#design-concept">13.1. Design Concept</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Dungeon.html#implementation">13.2. Implementation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Quests.html">14. Game Quests</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Shops.html">15. In-game Shops</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Commands.html">16. In-game Commands</a></li>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="15. In-game Shops" href="Beginner-Tutorial-Shops.html" />
|
||||
<link rel="prev" title="13. Dynamically generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
|
||||
<link rel="prev" title="13. Procedurally generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
|
||||
<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 latest</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
<script>$('#searchbox').show(0);</script>
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Dungeon.html"
|
||||
title="previous chapter"><span class="section-number">13. </span>Dynamically generated Dungeon</a></p>
|
||||
title="previous chapter"><span class="section-number">13. </span>Procedurally generated Dungeon</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Shops.html"
|
||||
title="next chapter"><span class="section-number">15. </span>In-game Shops</a></p>
|
||||
|
|
@ -139,7 +139,7 @@
|
|||
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
|
||||
<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 latest</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@
|
|||
|
||||
<span class="sd">mob = create_object(MyMob, key="Goblin", location=room)</span>
|
||||
|
||||
<span class="sd">mob.ai.set_state("patrol")</span>
|
||||
<span class="sd">mob.ai.set_state("roam")</span>
|
||||
|
||||
<span class="sd"># tick the ai whenever needed</span>
|
||||
<span class="sd">mob.ai.run()</span>
|
||||
|
|
@ -131,27 +131,42 @@
|
|||
<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">.enums</span> <span class="kn">import</span> <span class="n">Ability</span>
|
||||
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">random_probability</span>
|
||||
|
||||
|
||||
<div class="viewcode-block" id="AIHandler"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIHandler">[docs]</a><span class="k">class</span> <span class="nc">AIHandler</span><span class="p">:</span>
|
||||
|
||||
<span class="n">attribute_name</span> <span class="o">=</span> <span class="s2">"ai_state"</span>
|
||||
<span class="n">attribute_category</span> <span class="o">=</span> <span class="s2">"ai_state"</span>
|
||||
|
||||
<div class="viewcode-block" id="AIHandler.__init__"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIHandler.__init__">[docs]</a> <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="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">ai_state</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="s2">"ai_state"</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"ai_state"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="s2">"idle"</span><span class="p">)</span></div>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai_state</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">attribute_name</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">attribute_category</span><span class="p">,</span>
|
||||
<span class="n">default</span><span class="o">=</span><span class="s2">"idle"</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="AIHandler.set_state"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIHandler.set_state">[docs]</a> <span class="k">def</span> <span class="nf">set_state</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">state</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai_state</span> <span class="o">=</span> <span class="n">state</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="s2">"ai_state"</span><span class="p">,</span> <span class="n">state</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"ai_state"</span><span class="p">)</span></div>
|
||||
<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">attribute_name</span><span class="p">,</span> <span class="n">state</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">attribute_category</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="AIHandler.get_state"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIHandler.get_state">[docs]</a> <span class="k">def</span> <span class="nf">get_state</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">ai_state</span></div>
|
||||
|
||||
<div class="viewcode-block" id="AIHandler.get_targets"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIHandler.get_targets">[docs]</a> <span class="k">def</span> <span class="nf">get_targets</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Get a list of potential targets for the NPC to attack</span>
|
||||
<span class="sd"> Get a list of potential targets for the NPC to combat.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">return</span> <span class="p">[</span><span class="n">obj</span> <span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">contents</span> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="s2">"is_pc"</span><span class="p">)</span> <span class="ow">and</span> <span class="n">obj</span><span class="o">.</span><span class="n">is_pc</span><span class="p">]</span></div>
|
||||
|
||||
<div class="viewcode-block" id="AIHandler.get_traversable_exits"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIHandler.get_traversable_exits">[docs]</a> <span class="k">def</span> <span class="nf">get_traversable_exits</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exclude_destination</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Get a list of exits that the NPC can traverse. Optionally exclude a destination.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> exclude_destination (Object, optional): Exclude exits with this destination.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">return</span> <span class="p">[</span>
|
||||
<span class="n">exi</span>
|
||||
<span class="k">for</span> <span class="n">exi</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">exits</span>
|
||||
|
|
@ -162,8 +177,11 @@
|
|||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Given a dictionary of probabilities, return the key of the chosen probability.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> probabilities (dict): A dictionary of probabilities, where the key is the action and the</span>
|
||||
<span class="sd"> value is the probability of that action.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">r</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span>
|
||||
<span class="c1"># sort probabilities from higheest to lowest, making sure to normalize them 0..1</span>
|
||||
<span class="n">prob_total</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">probabilities</span><span class="o">.</span><span class="n">values</span><span class="p">())</span>
|
||||
<span class="n">sorted_probs</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span>
|
||||
|
|
@ -171,10 +189,12 @@
|
|||
<span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span>
|
||||
<span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="n">rand</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span>
|
||||
<span class="n">total</span> <span class="o">=</span> <span class="mi">0</span>
|
||||
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">prob</span> <span class="ow">in</span> <span class="n">sorted_probs</span><span class="p">:</span>
|
||||
<span class="n">total</span> <span class="o">+=</span> <span class="n">prob</span>
|
||||
<span class="k">if</span> <span class="n">r</span> <span class="o"><=</span> <span class="n">total</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">rand</span> <span class="o"><=</span> <span class="n">total</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="n">key</span></div>
|
||||
|
||||
<div class="viewcode-block" id="AIHandler.run"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIHandler.run">[docs]</a> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
|
|
@ -190,138 +210,13 @@
|
|||
<span class="sd"> Mixin for adding AI to an Object. This is a simple state machine. Just add more `ai_*` methods</span>
|
||||
<span class="sd"> to the object to make it do more things.</span>
|
||||
|
||||
<span class="sd"> In the tutorial, the handler is added directly to the Mob class, to avoid going into the details</span>
|
||||
<span class="sd"> of multiple inheritance. In a real game, you would probably want to use a mixin like this.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="c1"># combat probabilities should add up to 1.0</span>
|
||||
<span class="n">combat_probabilities</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"hold"</span><span class="p">:</span> <span class="mf">0.1</span><span class="p">,</span>
|
||||
<span class="s2">"attack"</span><span class="p">:</span> <span class="mf">0.9</span><span class="p">,</span>
|
||||
<span class="s2">"stunt"</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span>
|
||||
<span class="s2">"item"</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span>
|
||||
<span class="s2">"flee"</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span>
|
||||
<span class="p">}</span>
|
||||
|
||||
<div class="viewcode-block" id="AIMixin.ai"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai">[docs]</a> <span class="nd">@lazy_property</span>
|
||||
<span class="k">def</span> <span class="nf">ai</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="n">AIHandler</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="AIMixin.ai_idle"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_idle">[docs]</a> <span class="k">def</span> <span class="nf">ai_idle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">pass</span></div>
|
||||
|
||||
<div class="viewcode-block" id="AIMixin.ai_attack"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_attack">[docs]</a> <span class="k">def</span> <span class="nf">ai_attack</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">pass</span></div>
|
||||
|
||||
<div class="viewcode-block" id="AIMixin.ai_patrol"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_patrol">[docs]</a> <span class="k">def</span> <span class="nf">ai_patrol</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">pass</span></div>
|
||||
|
||||
<div class="viewcode-block" id="AIMixin.ai_flee"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_flee">[docs]</a> <span class="k">def</span> <span class="nf">ai_flee</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">pass</span></div></div>
|
||||
|
||||
|
||||
<div class="viewcode-block" id="IdleMobMixin"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.IdleMobMixin">[docs]</a><span class="k">class</span> <span class="nc">IdleMobMixin</span><span class="p">(</span><span class="n">AIMixin</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> A simple mob that understands AI commands, but does nothing.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<div class="viewcode-block" id="IdleMobMixin.ai_idle"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.IdleMobMixin.ai_idle">[docs]</a> <span class="k">def</span> <span class="nf">ai_idle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">pass</span></div></div>
|
||||
|
||||
|
||||
<div class="viewcode-block" id="AggressiveMobMixin"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin">[docs]</a><span class="k">class</span> <span class="nc">AggressiveMobMixin</span><span class="p">(</span><span class="n">AIMixin</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> A simple aggressive mob that can roam, attack and flee.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="n">combat_probabilities</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"hold"</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span>
|
||||
<span class="s2">"attack"</span><span class="p">:</span> <span class="mf">0.85</span><span class="p">,</span>
|
||||
<span class="s2">"stunt"</span><span class="p">:</span> <span class="mf">0.05</span><span class="p">,</span>
|
||||
<span class="s2">"item"</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span>
|
||||
<span class="s2">"flee"</span><span class="p">:</span> <span class="mf">0.05</span><span class="p">,</span>
|
||||
<span class="p">}</span>
|
||||
|
||||
<div class="viewcode-block" id="AggressiveMobMixin.ai_idle"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_idle">[docs]</a> <span class="k">def</span> <span class="nf">ai_idle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Do nothing, but switch to attack state if a target is found.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_targets</span><span class="p">():</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"attack"</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="AggressiveMobMixin.ai_attack"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_attack">[docs]</a> <span class="k">def</span> <span class="nf">ai_attack</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Manage the attack/combat state of the mob.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="n">combathandler</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">nbd</span><span class="o">.</span><span class="n">combathandler</span><span class="p">:</span>
|
||||
<span class="c1"># already in combat</span>
|
||||
<span class="n">allies</span><span class="p">,</span> <span class="n">enemies</span> <span class="o">=</span> <span class="n">combathandler</span><span class="o">.</span><span class="n">get_sides</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
|
||||
<span class="n">action</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">random_probability</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combat_probabilities</span><span class="p">)</span>
|
||||
|
||||
<span class="k">match</span> <span class="n">action</span><span class="p">:</span>
|
||||
<span class="k">case</span> <span class="s2">"hold"</span><span class="p">:</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">({</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"hold"</span><span class="p">})</span>
|
||||
<span class="k">case</span> <span class="s2">"attack"</span><span class="p">:</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">({</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"attack"</span><span class="p">,</span> <span class="s2">"target"</span><span class="p">:</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">enemies</span><span class="p">)})</span>
|
||||
<span class="k">case</span> <span class="s2">"stunt"</span><span class="p">:</span>
|
||||
<span class="c1"># choose a random ally to help</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span>
|
||||
<span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="s2">"stunt"</span><span class="p">,</span>
|
||||
<span class="s2">"recipient"</span><span class="p">:</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">allies</span><span class="p">),</span>
|
||||
<span class="s2">"advantage"</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
|
||||
<span class="s2">"stunt"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">,</span>
|
||||
<span class="s2">"defense"</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="p">}</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">case</span> <span class="s2">"item"</span><span class="p">:</span>
|
||||
<span class="c1"># use a random item on a random ally</span>
|
||||
<span class="n">target</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">allies</span><span class="p">)</span>
|
||||
<span class="n">valid_items</span> <span class="o">=</span> <span class="p">[</span><span class="n">item</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">contents</span> <span class="k">if</span> <span class="n">item</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">target</span><span class="p">)]</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span>
|
||||
<span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"item"</span><span class="p">,</span> <span class="s2">"item"</span><span class="p">:</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">valid_items</span><span class="p">),</span> <span class="s2">"target"</span><span class="p">:</span> <span class="n">target</span><span class="p">}</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">case</span> <span class="s2">"flee"</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"flee"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">targets</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_targets</span><span class="p">()):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"patrol"</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">target</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">targets</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">execute_cmd</span><span class="p">(</span><span class="sa">f</span><span class="s2">"attack </span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="AggressiveMobMixin.ai_patrol"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_patrol">[docs]</a> <span class="k">def</span> <span class="nf">ai_patrol</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Patrol, moving randomly to a new room. If a target is found, switch to attack state.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="n">targets</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_targets</span><span class="p">():</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"attack"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">execute_cmd</span><span class="p">(</span><span class="sa">f</span><span class="s2">"attack </span><span class="si">{</span><span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">targets</span><span class="p">)</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">exits</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_traversable_exits</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="n">exits</span><span class="p">:</span>
|
||||
<span class="n">exi</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">exits</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">execute_cmd</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">exi</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="AggressiveMobMixin.ai_flee"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_flee">[docs]</a> <span class="k">def</span> <span class="nf">ai_flee</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Flee from the current room, avoiding going back to the room from which we came. If no exits</span>
|
||||
<span class="sd"> are found, switch to patrol state.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">current_room</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span>
|
||||
<span class="n">past_room</span> <span class="o">=</span> <span class="bp">self</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="s2">"past_room"</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"ai_state"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
|
||||
<span class="n">exits</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_traversable_exits</span><span class="p">(</span><span class="n">exclude_destination</span><span class="o">=</span><span class="n">past_room</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">exits</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s2">"past_room"</span><span class="p">,</span> <span class="n">current_room</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"ai_state"</span><span class="p">)</span>
|
||||
<span class="n">exi</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">exits</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">execute_cmd</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">exi</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="c1"># if in a dead end, patrol will allow for backing out</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"patrol"</span><span class="p">)</span></div></div>
|
||||
<span class="k">return</span> <span class="n">AIHandler</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span></div></div>
|
||||
</pre></div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -100,9 +100,9 @@
|
|||
<span class="kn">from</span> <span class="nn">evennia.typeclasses.attributes</span> <span class="kn">import</span> <span class="n">AttributeProperty</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.typeclasses.tags</span> <span class="kn">import</span> <span class="n">TagProperty</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils.evmenu</span> <span class="kn">import</span> <span class="n">EvMenu</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">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">lazy_property</span><span class="p">,</span> <span class="n">make_iter</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.ai</span> <span class="kn">import</span> <span class="n">AggressiveMobMixin</span>
|
||||
<span class="kn">from</span> <span class="nn">.ai</span> <span class="kn">import</span> <span class="n">AIHandler</span>
|
||||
<span class="kn">from</span> <span class="nn">.characters</span> <span class="kn">import</span> <span class="n">LivingMixin</span>
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">Ability</span><span class="p">,</span> <span class="n">WieldLocation</span>
|
||||
<span class="kn">from</span> <span class="nn">.objects</span> <span class="kn">import</span> <span class="n">get_bare_hands</span>
|
||||
|
|
@ -340,73 +340,110 @@
|
|||
<span class="p">)</span></div></div>
|
||||
|
||||
|
||||
<div class="viewcode-block" id="EvAdventureMob"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob">[docs]</a><span class="k">class</span> <span class="nc">EvAdventureMob</span><span class="p">(</span><span class="n">AggressiveMobMixin</span><span class="p">,</span> <span class="n">EvAdventureNPC</span><span class="p">):</span>
|
||||
<div class="viewcode-block" id="EvAdventureMob"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob">[docs]</a><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="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Mob (mobile) NPC; this is usually an enemy.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="c1"># change this to make the mob more or less likely to perform different actions</span>
|
||||
<span class="n">combat_probabilities</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"hold"</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span>
|
||||
<span class="s2">"attack"</span><span class="p">:</span> <span class="mf">0.85</span><span class="p">,</span>
|
||||
<span class="s2">"stunt"</span><span class="p">:</span> <span class="mf">0.05</span><span class="p">,</span>
|
||||
<span class="s2">"item"</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span>
|
||||
<span class="s2">"flee"</span><span class="p">:</span> <span class="mf">0.05</span><span class="p">,</span>
|
||||
<span class="p">}</span>
|
||||
|
||||
<span class="c1"># chance (%) that this enemy will loot you when defeating you</span>
|
||||
<span class="n">loot_chance</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">75</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
<div class="viewcode-block" id="EvAdventureMob.ai"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai">[docs]</a> <span class="nd">@lazy_property</span>
|
||||
<span class="k">def</span> <span class="nf">ai</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="n">AIHandler</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="EvAdventureMob.ai_idle"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_idle">[docs]</a> <span class="k">def</span> <span class="nf">ai_idle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Do nothing.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span></div>
|
||||
|
||||
<div class="viewcode-block" id="EvAdventureMob.ai_combat"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_combat">[docs]</a> <span class="k">def</span> <span class="nf">ai_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Manage the combat/combat state of the mob.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="n">combathandler</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">nbd</span><span class="o">.</span><span class="n">combathandler</span><span class="p">:</span>
|
||||
<span class="c1"># already in combat</span>
|
||||
<span class="n">allies</span><span class="p">,</span> <span class="n">enemies</span> <span class="o">=</span> <span class="n">combathandler</span><span class="o">.</span><span class="n">get_sides</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
|
||||
<span class="n">action</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">random_probability</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combat_probabilities</span><span class="p">)</span>
|
||||
|
||||
<span class="k">match</span> <span class="n">action</span><span class="p">:</span>
|
||||
<span class="k">case</span> <span class="s2">"hold"</span><span class="p">:</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">({</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"hold"</span><span class="p">})</span>
|
||||
<span class="k">case</span> <span class="s2">"combat"</span><span class="p">:</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">({</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"attack"</span><span class="p">,</span> <span class="s2">"target"</span><span class="p">:</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">enemies</span><span class="p">)})</span>
|
||||
<span class="k">case</span> <span class="s2">"stunt"</span><span class="p">:</span>
|
||||
<span class="c1"># choose a random ally to help</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span>
|
||||
<span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="s2">"stunt"</span><span class="p">,</span>
|
||||
<span class="s2">"recipient"</span><span class="p">:</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">allies</span><span class="p">),</span>
|
||||
<span class="s2">"advantage"</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
|
||||
<span class="s2">"stunt"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">,</span>
|
||||
<span class="s2">"defense"</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="p">}</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">case</span> <span class="s2">"item"</span><span class="p">:</span>
|
||||
<span class="c1"># use a random item on a random ally</span>
|
||||
<span class="n">target</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">allies</span><span class="p">)</span>
|
||||
<span class="n">valid_items</span> <span class="o">=</span> <span class="p">[</span><span class="n">item</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">contents</span> <span class="k">if</span> <span class="n">item</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">target</span><span class="p">)]</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span>
|
||||
<span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"item"</span><span class="p">,</span> <span class="s2">"item"</span><span class="p">:</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">valid_items</span><span class="p">),</span> <span class="s2">"target"</span><span class="p">:</span> <span class="n">target</span><span class="p">}</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">case</span> <span class="s2">"flee"</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"flee"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">elif</span> <span class="ow">not</span> <span class="p">(</span><span class="n">targets</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_targets</span><span class="p">()):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"roam"</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">target</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">targets</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">execute_cmd</span><span class="p">(</span><span class="sa">f</span><span class="s2">"attack </span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="EvAdventureMob.ai_roam"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_roam">[docs]</a> <span class="k">def</span> <span class="nf">ai_roam</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> roam, moving randomly to a new room. If a target is found, switch to combat state.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="n">targets</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_targets</span><span class="p">():</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"combat"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">execute_cmd</span><span class="p">(</span><span class="sa">f</span><span class="s2">"attack </span><span class="si">{</span><span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">targets</span><span class="p">)</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">exits</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_traversable_exits</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="n">exits</span><span class="p">:</span>
|
||||
<span class="n">exi</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">exits</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">execute_cmd</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">exi</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="EvAdventureMob.ai_flee"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_flee">[docs]</a> <span class="k">def</span> <span class="nf">ai_flee</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Flee from the current room, avoiding going back to the room from which we came. If no exits</span>
|
||||
<span class="sd"> are found, switch to roam state.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">current_room</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span>
|
||||
<span class="n">past_room</span> <span class="o">=</span> <span class="bp">self</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="s2">"past_room"</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"ai_state"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
|
||||
<span class="n">exits</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_traversable_exits</span><span class="p">(</span><span class="n">exclude_destination</span><span class="o">=</span><span class="n">past_room</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">exits</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s2">"past_room"</span><span class="p">,</span> <span class="n">current_room</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"ai_state"</span><span class="p">)</span>
|
||||
<span class="n">exi</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">exits</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">execute_cmd</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">exi</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="c1"># if in a dead end, roam will allow for backing out</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"roam"</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="EvAdventureMob.at_defeat"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.at_defeat">[docs]</a> <span class="k">def</span> <span class="nf">at_defeat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Mobs die right away when defeated, no death-table rolls.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">at_death</span><span class="p">()</span></div>
|
||||
|
||||
<div class="viewcode-block" id="EvAdventureMob.at_do_loot"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.at_do_loot">[docs]</a> <span class="k">def</span> <span class="nf">at_do_loot</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looted</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Called when mob gets to loot a PC.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d100"</span><span class="p">)</span> <span class="o">></span> <span class="bp">self</span><span class="o">.</span><span class="n">loot_chance</span><span class="p">:</span>
|
||||
<span class="c1"># don't loot</span>
|
||||
<span class="k">return</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">looted</span><span class="o">.</span><span class="n">coins</span><span class="p">:</span>
|
||||
<span class="c1"># looter prefer coins</span>
|
||||
<span class="n">loot</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">"1d20"</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">looted</span><span class="o">.</span><span class="n">coins</span> <span class="o"><</span> <span class="n">loot</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_location</span><span class="p">(</span>
|
||||
<span class="s2">"$You(looter) loots $You() for all coin!"</span><span class="p">,</span>
|
||||
<span class="n">from_obj</span><span class="o">=</span><span class="n">looted</span><span class="p">,</span>
|
||||
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="s2">"looter"</span><span class="p">:</span> <span class="bp">self</span><span class="p">},</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_location</span><span class="p">(</span>
|
||||
<span class="s2">"$You(looter) loots $You() for |y</span><span class="si">{loot}</span><span class="s2">|n coins!"</span><span class="p">,</span>
|
||||
<span class="n">from_obj</span><span class="o">=</span><span class="n">looted</span><span class="p">,</span>
|
||||
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="s2">"looter"</span><span class="p">:</span> <span class="bp">self</span><span class="p">},</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">looted</span><span class="p">,</span> <span class="s2">"equipment"</span><span class="p">):</span>
|
||||
<span class="c1"># go through backpack, first usable, then wieldable, wearable items</span>
|
||||
<span class="c1"># and finally stuff wielded</span>
|
||||
<span class="n">stealable</span> <span class="o">=</span> <span class="n">looted</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">get_usable_objects_from_backpack</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">stealable</span><span class="p">:</span>
|
||||
<span class="n">stealable</span> <span class="o">=</span> <span class="n">looted</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">get_wieldable_objects_from_backpack</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">stealable</span><span class="p">:</span>
|
||||
<span class="n">stealable</span> <span class="o">=</span> <span class="n">looted</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">get_wearable_objects_from_backpack</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">stealable</span><span class="p">:</span>
|
||||
<span class="n">stealable</span> <span class="o">=</span> <span class="p">[</span><span class="n">looted</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">SHIELD_HAND</span><span class="p">]]</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">stealable</span><span class="p">:</span>
|
||||
<span class="n">stealable</span> <span class="o">=</span> <span class="p">[</span><span class="n">looted</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">HEAD</span><span class="p">]]</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">stealable</span><span class="p">:</span>
|
||||
<span class="n">stealable</span> <span class="o">=</span> <span class="p">[</span><span class="n">looted</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">ARMOR</span><span class="p">]]</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">stealable</span><span class="p">:</span>
|
||||
<span class="n">stealable</span> <span class="o">=</span> <span class="p">[</span><span class="n">looted</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">WEAPON_HAND</span><span class="p">]]</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">stealable</span><span class="p">:</span>
|
||||
<span class="n">stealable</span> <span class="o">=</span> <span class="p">[</span><span class="n">looted</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">TWO_HANDS</span><span class="p">]]</span>
|
||||
|
||||
<span class="n">stolen</span> <span class="o">=</span> <span class="n">looted</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">choice</span><span class="p">(</span><span class="n">stealable</span><span class="p">))</span>
|
||||
<span class="n">stolen</span><span class="o">.</span><span class="n">location</span> <span class="o">=</span> <span class="bp">self</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_location</span><span class="p">(</span>
|
||||
<span class="s2">"$You(looter) steals </span><span class="si">{stolen.key}</span><span class="s2"> from $You()!"</span><span class="p">,</span>
|
||||
<span class="n">from_obj</span><span class="o">=</span><span class="n">looted</span><span class="p">,</span>
|
||||
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="s2">"looter"</span><span class="p">:</span> <span class="bp">self</span><span class="p">},</span>
|
||||
<span class="p">)</span></div></div>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">at_death</span><span class="p">()</span></div></div>
|
||||
</pre></div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -117,26 +117,26 @@
|
|||
<span class="nd">@patch</span><span class="p">(</span><span class="s2">"evennia.contrib.tutorials.evadventure.ai.log_trace"</span><span class="p">)</span>
|
||||
<span class="k">def</span> <span class="nf">test_ai_methods</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_log_trace</span><span class="p">,</span> <span class="n">mock_random</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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_state</span><span class="p">(),</span> <span class="s2">"idle"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"patrol"</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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_state</span><span class="p">(),</span> <span class="s2">"patrol"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"roam"</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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_state</span><span class="p">(),</span> <span class="s2">"roam"</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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_targets</span><span class="p">(),</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">pc</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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_traversable_exits</span><span class="p">(),</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">exit</span><span class="p">])</span>
|
||||
|
||||
<span class="n">probs</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"hold"</span><span class="p">:</span> <span class="mf">0.1</span><span class="p">,</span> <span class="s2">"attack"</span><span class="p">:</span> <span class="mf">0.5</span><span class="p">,</span> <span class="s2">"flee"</span><span class="p">:</span> <span class="mf">0.4</span><span class="p">}</span>
|
||||
<span class="n">probs</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"hold"</span><span class="p">:</span> <span class="mf">0.1</span><span class="p">,</span> <span class="s2">"combat"</span><span class="p">:</span> <span class="mf">0.5</span><span class="p">,</span> <span class="s2">"flee"</span><span class="p">:</span> <span class="mf">0.4</span><span class="p">}</span>
|
||||
<span class="n">mock_random</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mf">0.3</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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">random_probability</span><span class="p">(</span><span class="n">probs</span><span class="p">),</span> <span class="s2">"attack"</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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">random_probability</span><span class="p">(</span><span class="n">probs</span><span class="p">),</span> <span class="s2">"combat"</span><span class="p">)</span>
|
||||
<span class="n">mock_random</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mf">0.7</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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">random_probability</span><span class="p">(</span><span class="n">probs</span><span class="p">),</span> <span class="s2">"flee"</span><span class="p">)</span>
|
||||
<span class="n">mock_random</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mf">0.95</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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">random_probability</span><span class="p">(</span><span class="n">probs</span><span class="p">),</span> <span class="s2">"hold"</span><span class="p">)</span></div>
|
||||
|
||||
<div class="viewcode-block" id="TestAI.test_ai_run"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_ai.html#evennia.contrib.tutorials.evadventure.tests.test_ai.TestAI.test_ai_run">[docs]</a> <span class="k">def</span> <span class="nf">test_ai_run</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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"patrol"</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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_state</span><span class="p">(),</span> <span class="s2">"patrol"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"roam"</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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_state</span><span class="p">(),</span> <span class="s2">"roam"</span><span class="p">)</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">run</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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_state</span><span class="p">(),</span> <span class="s2">"attack"</span><span class="p">)</span></div></div>
|
||||
<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">npc</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">get_state</span><span class="p">(),</span> <span class="s2">"combat"</span><span class="p">)</span></div></div>
|
||||
</pre></div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -95,6 +95,8 @@
|
|||
|
||||
<span class="sd">"""</span>
|
||||
|
||||
<span class="kn">import</span> <span class="nn">random</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>
|
||||
|
|
@ -107,6 +109,7 @@
|
|||
<span class="s2">Damage roll: |w</span><span class="si">{damage_roll}</span><span class="s2">|n"""</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||||
|
||||
|
||||
|
||||
<div class="viewcode-block" id="get_obj_stats"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.utils.html#evennia.contrib.tutorials.evadventure.utils.get_obj_stats">[docs]</a><span class="k">def</span> <span class="nf">get_obj_stats</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">owner</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Get a string of stats about the object.</span>
|
||||
|
|
@ -142,6 +145,30 @@
|
|||
<span class="n">defense_type_name</span><span class="o">=</span><span class="n">defense_type</span><span class="o">.</span><span class="n">value</span> <span class="k">if</span> <span class="n">defense_type</span> <span class="k">else</span> <span class="s2">"No defense"</span><span class="p">,</span>
|
||||
<span class="n">damage_roll</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">"damage_roll"</span><span class="p">,</span> <span class="s2">"None"</span><span class="p">),</span>
|
||||
<span class="p">)</span></div>
|
||||
|
||||
|
||||
<div class="viewcode-block" id="random_probability"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.utils.html#evennia.contrib.tutorials.evadventure.utils.random_probability">[docs]</a><span class="k">def</span> <span class="nf">random_probability</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">probabilities</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Given a dictionary of probabilities, return the key of the chosen probability.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> probabilities (dict): A dictionary of probabilities, where the key is the action and the</span>
|
||||
<span class="sd"> value is the probability of that action.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">r</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span>
|
||||
<span class="c1"># sort probabilities from higheest to lowest, making sure to normalize them 0..1</span>
|
||||
<span class="n">prob_total</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">probabilities</span><span class="o">.</span><span class="n">values</span><span class="p">())</span>
|
||||
<span class="n">sorted_probs</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span>
|
||||
<span class="p">((</span><span class="n">key</span><span class="p">,</span> <span class="n">prob</span> <span class="o">/</span> <span class="n">prob_total</span><span class="p">)</span> <span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">prob</span> <span class="ow">in</span> <span class="n">probabilities</span><span class="o">.</span><span class="n">items</span><span class="p">()),</span>
|
||||
<span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span>
|
||||
<span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">total</span> <span class="o">=</span> <span class="mi">0</span>
|
||||
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">prob</span> <span class="ow">in</span> <span class="n">sorted_probs</span><span class="p">:</span>
|
||||
<span class="n">total</span> <span class="o">+=</span> <span class="n">prob</span>
|
||||
<span class="k">if</span> <span class="n">r</span> <span class="o"><=</span> <span class="n">total</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="n">key</span></div>
|
||||
</pre></div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -116,17 +116,9 @@
|
|||
<span class="kn">from</span> <span class="nn">evennia.typeclasses.attributes</span> <span class="kn">import</span> <span class="n">ModelAttributeBackend</span><span class="p">,</span> <span class="n">NickHandler</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.typeclasses.models</span> <span class="kn">import</span> <span class="n">TypeclassBase</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">ansi</span><span class="p">,</span> <span class="n">create</span><span class="p">,</span> <span class="n">funcparser</span><span class="p">,</span> <span class="n">logger</span><span class="p">,</span> <span class="n">search</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="p">(</span>
|
||||
<span class="n">class_from_module</span><span class="p">,</span>
|
||||
<span class="n">dbref</span><span class="p">,</span>
|
||||
<span class="n">is_iter</span><span class="p">,</span>
|
||||
<span class="n">iter_to_str</span><span class="p">,</span>
|
||||
<span class="n">lazy_property</span><span class="p">,</span>
|
||||
<span class="n">make_iter</span><span class="p">,</span>
|
||||
<span class="n">compress_whitespace</span><span class="p">,</span>
|
||||
<span class="n">to_str</span><span class="p">,</span>
|
||||
<span class="n">variable_from_module</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="p">(</span><span class="n">class_from_module</span><span class="p">,</span> <span class="n">compress_whitespace</span><span class="p">,</span> <span class="n">dbref</span><span class="p">,</span>
|
||||
<span class="n">is_iter</span><span class="p">,</span> <span class="n">iter_to_str</span><span class="p">,</span> <span class="n">lazy_property</span><span class="p">,</span>
|
||||
<span class="n">make_iter</span><span class="p">,</span> <span class="n">to_str</span><span class="p">,</span> <span class="n">variable_from_module</span><span class="p">)</span>
|
||||
|
||||
<span class="n">_INFLECT</span> <span class="o">=</span> <span class="n">inflect</span><span class="o">.</span><span class="n">engine</span><span class="p">()</span>
|
||||
<span class="n">_MULTISESSION_MODE</span> <span class="o">=</span> <span class="n">settings</span><span class="o">.</span><span class="n">MULTISESSION_MODE</span>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,394 @@
|
|||
# NPC and monster AI
|
||||
|
||||
```{warning}
|
||||
This part of the Beginner tutorial is still being developed.
|
||||
```
|
||||
```{sidebar} Artificial Intelligence sounds complex
|
||||
The term "Artificial Intelligence" can sound daunting. It evokes images of supercomputers, machine learning, neural networks and large language models. For our use case though, you can get something that feels pretty 'intelligent' by just using a few if-statements.
|
||||
```
|
||||
Not every entity in the game are controlled by a player. NPCs and enemies need to be controlled by the computer - that is, we need to give them artificial intelligence (AI).
|
||||
|
||||
For our game we will implement a type of AI called a 'state machine'. It means that the entity (like an NPC or mob) is always in a given 'state'. An example of a state could be 'idle', 'roaming' or 'attacking'.
|
||||
At regular intervals, the AI entity will be 'ticked' by Evennia. This 'tick' starts with an evaluation which determines if the entity should switch to another state, or stay and perform one (or more) actions inside the current state.
|
||||
|
||||
```{sidebar} Mobs and NPC
|
||||
'Mob' is short for 'Mobile' and is a common MUD term for an entity that can move between rooms. The term is usually used for aggressive enemies. A Mob is also an 'NPC' (Non-Player Character), but the latter term is often used for more peaceful entities, like shopkeeprs and quest givers.
|
||||
```
|
||||
|
||||
For example, if a mob in a 'roaming' state comes upon a player character, it may switch into the 'attack' state. In combat it could move between different combat actions, and if it survives combat it would go back to its 'roaming' state.
|
||||
|
||||
The AI can be 'ticked' on different time scales depending on how your game works. For example, while a mob is moving, they might automatically move from room to room every 20 seconds. But once it enters turn-based combat (if you use that), the AI will 'tick' only on every turn.
|
||||
|
||||
## Our requirements
|
||||
|
||||
```{sidebar} Shopkeepers and quest givers
|
||||
NPC shopkeepers and quest givers will be assumed to always be in the 'idle' state in our game - the functionality of talking to or shopping from them will be explored in a future lesson.
|
||||
```
|
||||
|
||||
For this tutorial game, we'll need AI entities to be able to be in the following states:
|
||||
|
||||
- _Idle_ - don't do anything, just stand around.
|
||||
- _Roam_ - move from room to room. It's important that we add the ability to limit where the AI can roam to. For example, if we have non-combat areas we want to be able to [lock](../../../Components/Locks.md) all exits leading into those areas so aggressive mods doesn't walk into them.
|
||||
- _Combat_ - initiate and perform combat with PCs. This state will make use of the [Combat Tutorial](./Beginner-Tutorial-Combat-Base.md) to randomly select combat actions (turn-based or tick-based as appropriately).
|
||||
- _Flee_ - this is like _Roam_ except the AI will move so as to avoid entering rooms with PCs, if possible.
|
||||
|
||||
We will organize the AI code like this:
|
||||
- `AIHandler` this will be a handler stored as `.ai` on the AI entity. It is responsible for storing the AI's state. To 'tick' the AI, we run `.ai.run()`. How often we crank the wheels of the AI this way we leave up to other game systems.
|
||||
- `.ai_<state_name>` methods on the NPC/Mob class - when the `ai.run()` method is called, it is responsible for finding a method named like its current state (e.g. `.ai_combat` if we are in the _combat_ state). Having methods like this makes it easy to add new states - just add a new method named appropriately and the AI now knows how to handle that state!
|
||||
|
||||
## The AIHandler
|
||||
|
||||
```{{sidebar}}
|
||||
You can find an AIHandler implemented in `evennia/contrib/tutorials`, in [evadventure/tests/test_ai.py](evennia.contrib.tutorials.evadventure.ai)
|
||||
```
|
||||
This is the core logic for managing AI states. Create a new file `evadventure/ai.py`.
|
||||
|
||||
```{code-block} python
|
||||
:linenos:
|
||||
:emphasize-lines: 10,11-13,16,23
|
||||
# in evadventure/ai.py
|
||||
|
||||
from evennia.logger import log_trace
|
||||
|
||||
class AIHandler:
|
||||
attribute_name = "ai_state"
|
||||
attribute_category = "ai_state"
|
||||
|
||||
def __init__(self, obj):
|
||||
self.obj = obj
|
||||
self.ai_state = obj.attributes.get(self.attribute_name,
|
||||
category=self.attribute_category,
|
||||
default="idle")
|
||||
def set_state(self, state):
|
||||
self.ai_state = state
|
||||
self.obj.attributes.add(self.attribute_name, state, category=self.attribute_category)
|
||||
|
||||
def get_state(self):
|
||||
return self.ai_state
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
state = self.get_state()
|
||||
getattr(self.obj, f"ai_{state}")()
|
||||
except Exception:
|
||||
log_trace(f"AI error in {self.obj.name} (running state: {state})")
|
||||
|
||||
|
||||
```
|
||||
|
||||
The AIHandler is an example of an [Object Handler](../../Tutorial-Persistent-Handler.md). This is a design style that groups all functionality together. To look-ahead a little, this handler will be added to the object like this:
|
||||
```{sidebar} lazy_property
|
||||
This is an Evennia [@decorator](https://realpython.com/primer-on-python-decorators/) that makes it so that the handler won't be initialized until someone actually tries to access `obj.ai` for the first time. On subsequent calls, the already initialized handler is returned. This is a very useful performance optimization when you have a lot of objects and also important for the functionality of handlers.
|
||||
```
|
||||
|
||||
```python
|
||||
# just an example, don't put this anywhere yet
|
||||
|
||||
from evennia.utils import lazy_property
|
||||
from evadventure.ai import AIHandler
|
||||
|
||||
class MyMob(SomeParent):
|
||||
|
||||
@lazy_property
|
||||
class ai(self):
|
||||
return AIHandler(self)
|
||||
```
|
||||
|
||||
So in short, accessing the `.ai` property will initialize an instance of `AIHandler`, to which we pass `self` (the current object). In the `AIHandler.__init__` we take this input and store it as `self.obj` (**lines 10-13**). This way the handler can always operate on the entity it's "sitting on" by accessing `self.obj`. The `lazy_property` makes sure that this initialization only happens once per server reload.
|
||||
|
||||
More key functionality:
|
||||
|
||||
- **Line 11**: We (re)load the AI state by accessing `self.obj.attributes.get()`. This loads a database [Attribute](../../../Components/Attributes.md) with a given name and category. If one is not (yet) saved, return "idle". Note that we must access `self.obj` (the NPC/mob) since that is the only thing with access to the database.
|
||||
- **Line 16**: In the `set_state` method we force the handler to switch to a given state. When we do, we make sure to save it to the database as well, so its state survives a reload. But we also store it in `self.ai_state` so we don't need to hit the database on every fetch.
|
||||
- **line 23**: The `getattr` function is an in-built Python function for getting a named property on an object. This allows us to, based on the current state, call a method `ai_<statename>` defined on the NPC/mob. We must wrap this call in a `try...except` block to properly handle errors in the AI method. Evennia's `log_trace` will make sure to log the error, including its traceback for debugging.
|
||||
|
||||
### More helpers on the AI handler
|
||||
|
||||
It's also convenient to put a few helpers on the AIHandler. This makes them easily available from inside the `ai_<state>` methods, callable as e.g. `self.ai.get_targets()`.
|
||||
|
||||
```{code-block} python
|
||||
:linenos:
|
||||
:emphasize-lines: 41,42,47,49
|
||||
# in evadventure/ai.py
|
||||
|
||||
# ...
|
||||
import random
|
||||
|
||||
class AIHandler:
|
||||
|
||||
# ...
|
||||
|
||||
def get_targets(self):
|
||||
"""
|
||||
Get a list of potential targets for the NPC to combat.
|
||||
|
||||
"""
|
||||
return [obj for obj in self.obj.location.contents if hasattr(obj, "is_pc") and obj.is_pc]
|
||||
|
||||
def get_traversable_exits(self, exclude_destination=None):
|
||||
"""
|
||||
Get a list of exits that the NPC can traverse. Optionally exclude a destination.
|
||||
|
||||
Args:
|
||||
exclude_destination (Object, optional): Exclude exits with this destination.
|
||||
|
||||
"""
|
||||
return [
|
||||
exi
|
||||
for exi in self.obj.location.exits
|
||||
if exi.destination != exclude_destination and exi.access(self, "traverse")
|
||||
]
|
||||
|
||||
def random_probability(self, probabilities):
|
||||
"""
|
||||
Given a dictionary of probabilities, return the key of the chosen probability.
|
||||
|
||||
Args:
|
||||
probabilities (dict): A dictionary of probabilities, where the key is the action and the
|
||||
value is the probability of that action.
|
||||
|
||||
"""
|
||||
# sort probabilities from higheest to lowest, making sure to normalize them 0..1
|
||||
prob_total = sum(probabilities.values())
|
||||
sorted_probs = sorted(
|
||||
((key, prob / prob_total) for key, prob in probabilities.items()),
|
||||
key=lambda x: x[1],
|
||||
reverse=True,
|
||||
)
|
||||
rand = random.random()
|
||||
total = 0
|
||||
for key, prob in sorted_probs:
|
||||
total += prob
|
||||
if rand <= total:
|
||||
return key
|
||||
```
|
||||
|
||||
```{sidebar} Locking exits
|
||||
The 'traverse' lock is the default lock-type checked by Evennia before allowing something to pass through an exit. Since only PCs have the `is_pc` property, we could lock down exits to _only_ allow entities with the property to pass through.
|
||||
|
||||
In game:
|
||||
|
||||
lock north = traverse:attr(is_pc, True)
|
||||
|
||||
Or in code:
|
||||
|
||||
exit_obj.locks.add(
|
||||
"traverse:attr(is_ic, True)")
|
||||
|
||||
See [Locks](../../../Components/Locks.md) for a lot more information about Evennia locks.
|
||||
```
|
||||
- `get_targets` checks if any of the other objects in the same location as the `is_pc` property set on their typeclass. For simplicity we assume Mobs will only ever attack PCs (no monster in-fighting!).
|
||||
- `get_traversable_exits` fetches all valid exits from the current location, excluding those with a provided destination _or_ those which doesn't pass the "traverse" access check.
|
||||
- `get_random_probability` takes a dict `{action: probability, ...}`. This will randomly select an action, but the higher the probability, the more likely it is that it will be picked. We will use this for the combat state later, to allow different combatants to more or less likely to perform different combat actions. This algorithm uses a few useful Python tools:
|
||||
- **Line 41**: Remember `probabilities` is a `dict` `{key: value, ...}`, where the values are the probabilities. So `probabilities.values()` gets us a list of only the probabilities. Running `sum()` on them gets us the total sum of those probabilities. We need that to normalize all probabilities between 0 and 1.0 on the line below.
|
||||
- **Lines 42-46**: Here we create a new iterable of tuples `(key, prob/prob_total)`. We sort them using the Python `sorted` helper. The `key=lambda x: x[1]` means that we sort on the second element of each tuple (the probability). The `reverse=True` means that we'll sort from highest probability to lowest.
|
||||
- **Line 47**:The `random.random()` call generates a random value between 0 and 1.
|
||||
- **Line 49**: Since the probabilities are sorted from highest to lowest, we loop over them until we find the first one fitting in the random value - this is the action/key we are looking for.
|
||||
- To give an example, if you have a `probability` input of `{"attack": 0.5, "defend": 0.1, "idle": 0.4}`, this would become a sorted iterable `(("attack", 0.5), ("idle", 0.4), ("defend": 0.1))`, and if `random.random()` returned 0.65, the outcome would be "idle". If `random.random()` returned `0.90`, it would be "defend". That is, this AI entity would attack 50% of the time, idle 40% and defend 10% of the time.
|
||||
|
||||
|
||||
## Adding AI to an entity
|
||||
|
||||
All we need to add AI-support to a game entity is to add the AI handler and a bunch of `.ai_statename()` methods onto that object's typeclass.
|
||||
|
||||
We already sketched out NPCs and Mob typeclasses back in the [NPC tutorial](Beginner-Tutorial_NPCs). Open `evadventure/npcs.py` and expand the so-far empty `EvAdventureMob` class.
|
||||
|
||||
```python
|
||||
# in evadventure/npcs.py
|
||||
|
||||
# ...
|
||||
|
||||
from evennia.utils import lazy_property
|
||||
from .ai import AIHandler
|
||||
|
||||
# ...
|
||||
|
||||
class EvAdventureMob(EvAdventureNPC):
|
||||
|
||||
@lazy_property
|
||||
def ai(self):
|
||||
return AIHandler(self)
|
||||
|
||||
def ai_idle(self):
|
||||
pass
|
||||
|
||||
def ai_roam(self):
|
||||
pass
|
||||
|
||||
def ai_roam(self):
|
||||
pass
|
||||
|
||||
def ai_combat(self):
|
||||
pass
|
||||
|
||||
def ai_flee(self):
|
||||
pass
|
||||
|
||||
```
|
||||
|
||||
All the remaining logic will go into each state-method.
|
||||
|
||||
### Idle state
|
||||
|
||||
In the idle state the mob does nothing, so we just leave the `ai_idle` method as it is - with just an empty `pass` in it. This means that it will also not attack PCs in the same room - but if a PC attacks it, we must make sure to force it into a combat state (otherwise it will be defenseless).
|
||||
|
||||
### Roam state
|
||||
|
||||
In this state the mob should move around from room to room until it finds PCs to attack.
|
||||
|
||||
```python
|
||||
# in evadventure/npcs.py
|
||||
|
||||
# ...
|
||||
|
||||
import random
|
||||
|
||||
class EvAdventureMob(EvAdventureNPC):
|
||||
|
||||
# ...
|
||||
|
||||
def ai_roam(self):
|
||||
"""
|
||||
roam, moving randomly to a new room. If a target is found, switch to combat state.
|
||||
|
||||
"""
|
||||
if targets := self.ai.get_targets():
|
||||
self.ai.set_state("combat")
|
||||
self.execute_cmd(f"attack {random.choice(targets).key}")
|
||||
else:
|
||||
exits = self.ai.get_traversable_exits()
|
||||
if exits:
|
||||
exi = random.choice(exits)
|
||||
self.execute_cmd(f"{exi.key}")
|
||||
```
|
||||
|
||||
Every time the AI is ticked, this method will be called. It will first check if there are any valid targets in the room (using the `get_targets()` helper we made on the `AIHandler`). If so, we switch to the `combat` state and immediately call the `attack` command to initiate/join combat (see the [Combat tutorial](./Beginner-Tutorial-Combat-Base.md)).
|
||||
|
||||
If no target is found, we get a list of traversible exits (exits that fail the `traverse` lock check is already excluded from this list). Using Python's in-bult `random.choice` function we grab a random exit from that list and moves through it by its name.
|
||||
|
||||
### Flee state
|
||||
|
||||
Flee is similar to _Roam_ except the the AI never tries to attack anything and will make sure to not return the way it came.
|
||||
|
||||
```python
|
||||
# in evadventure/npcs.py
|
||||
|
||||
# ...
|
||||
|
||||
class EvAdventureMob(EvAdventureNPC):
|
||||
|
||||
# ...
|
||||
|
||||
def ai_flee(self):
|
||||
"""
|
||||
Flee from the current room, avoiding going back to the room from which we came. If no exits
|
||||
are found, switch to roam state.
|
||||
|
||||
"""
|
||||
current_room = self.location
|
||||
past_room = self.attributes.get("past_room", category="ai_state", default=None)
|
||||
exits = self.ai.get_traversable_exits(exclude_destination=past_room)
|
||||
if exits:
|
||||
self.attributes.set("past_room", current_room, category="ai_state")
|
||||
exi = random.choice(exits)
|
||||
self.execute_cmd(f"{exi.key}")
|
||||
else:
|
||||
# if in a dead end, roam will allow for backing out
|
||||
self.ai.set_state("roam")
|
||||
|
||||
```
|
||||
|
||||
We store the `past_room` in an Attribute "past_room" on ourselves and make sure to exclude it when trying to find random exits to traverse to.
|
||||
|
||||
If we end up in a dead end we switch to _Roam_ mode so that it can get back out (and also start attacking things again). So the effect of this is that the mob will flee in terror as far as it can before 'calming down'.
|
||||
|
||||
### Combat state
|
||||
|
||||
While in the combat state, the mob will use one of the combat systems we've designed (either [twitch-based combat](./Beginner-Tutorial-Combat-Twitch.md) or [turn-based combat](./Beginner-Tutorial-Combat-Turnbased.md)). This means that every time the AI ticks, and we are in the combat state, the entity needs to perform one of the available combat actions, _hold_, _attack_, _do a stunt_, _use an item_ or _flee_.
|
||||
|
||||
```{code-block} python
|
||||
:linenos:
|
||||
:emphasize-lines: 7,22,24,25
|
||||
# in evadventure/npcs.py
|
||||
|
||||
# ...
|
||||
|
||||
class EvAdventureMob(EvAdventureNPC):
|
||||
|
||||
combat_probabilities = {
|
||||
"hold": 0.0,
|
||||
"attack": 0.85,
|
||||
"stunt": 0.05,
|
||||
"item": 0.0,
|
||||
"flee": 0.05,
|
||||
}
|
||||
|
||||
# ...
|
||||
|
||||
def ai_combat(self):
|
||||
"""
|
||||
Manage the combat/combat state of the mob.
|
||||
|
||||
"""
|
||||
if combathandler := self.nbd.combathandler:
|
||||
# already in combat
|
||||
allies, enemies = combathandler.get_sides(self)
|
||||
action = self.ai.random_probability(self.combat_probabilities)
|
||||
|
||||
match action:
|
||||
case "hold":
|
||||
combathandler.queue_action({"key": "hold"})
|
||||
case "combat":
|
||||
combathandler.queue_action({"key": "attack", "target": random.choice(enemies)})
|
||||
case "stunt":
|
||||
# choose a random ally to help
|
||||
combathandler.queue_action(
|
||||
{
|
||||
"key": "stunt",
|
||||
"recipient": random.choice(allies),
|
||||
"advantage": True,
|
||||
"stunt": Ability.STR,
|
||||
"defense": Ability.DEX,
|
||||
}
|
||||
)
|
||||
case "item":
|
||||
# use a random item on a random ally
|
||||
target = random.choice(allies)
|
||||
valid_items = [item for item in self.contents if item.at_pre_use(self, target)]
|
||||
combathandler.queue_action(
|
||||
{"key": "item", "item": random.choice(valid_items), "target": target}
|
||||
)
|
||||
case "flee":
|
||||
self.ai.set_state("flee")
|
||||
|
||||
elif not (targets := self.ai.get_targets()):
|
||||
self.ai.set_state("roam")
|
||||
else:
|
||||
target = random.choice(targets)
|
||||
self.execute_cmd(f"attack {target.key}")
|
||||
|
||||
```
|
||||
|
||||
- **Lines 7-13**: This dict describe how likely the mob is to perform a given combat action. By just modifying this dictionary we can easily creating mobs that behave very differently, like using items more or being more prone to fleeing. You can also turn off certain action entirely - by default his mob never "holds" or "uses items".
|
||||
- **Line 22**: If we are in combat, a `CombadHandler` should be initialized on us, available as as `self.ndb.combathandler` (see the [base combat tutorial](./Beginner-Tutorial-Combat-Base.md)).
|
||||
- **Line 24**: The `combathandler.get_sides()` produces the allies and enemies for the one passed to it.
|
||||
- **Line 25**: Now that `random_probability` method we created earlier in this lesson becomes handy!
|
||||
|
||||
The rest of this method just takes the randomly chosen action and performs the required operations to queue it as a new action with the `CombatHandler`. For simplicity, we only use stunts to boost our allies, not to hamper our enemies.
|
||||
|
||||
Finally, if we are not currently in combat and there are no enemies nearby, we switch to roaming - otherwise we start another fight!
|
||||
|
||||
## Unit Testing
|
||||
|
||||
```{{sidebar}}
|
||||
Find an example of AI tests in [evennia/contrib/tutorials/tests/test_ai.py](evennia.contrib.tutorials.evadventure.tests.test_ai).
|
||||
```
|
||||
> Create a new file `evadventure/tests/test_ai.py`.
|
||||
|
||||
Testing the AI handler and mob is straightforward if you have followed along with previous lessons. Create an `EvAdventureMob` and test that calling the various ai-related methods and handlers on it works as expected. A complexity is to mock the output from `random` so that you always get the same random result to compare against. We leave the implementation of AI tests as an extra exercise for the reader.
|
||||
|
||||
## Conclusions
|
||||
|
||||
You can easily expand this simple system to make Mobs more 'clever'. For example, instead of just randomly decide which action to take in combat, the mob could consider more factors - maybe some support mobs could use stunts to pave the way for their heavy hitters or use health potions when badly hurt.
|
||||
|
||||
It's also simple to add a 'hunt' state, where mobs check adjoining rooms for targets before moving there.
|
||||
|
||||
And while implementing a functional game AI system requires no advanced math or machine learning techniques, there's of course no limit to what kind of advanced things you could add if you really wanted to!
|
||||
|
||||
|
|
|
|||
|
|
@ -1219,7 +1219,7 @@ Our turnbased combat system is complete!
|
|||
## Testing
|
||||
|
||||
```{sidebar}
|
||||
See [evennia/contrib/tutorials/evadventure/tests/test_combat.py](evennia.contrib.tutorials.evadventure.tests.test_combat)
|
||||
See an example tests in `evennia/contrib/tutorials`, in [evadventure/tests/test_combat.py](evennia.contrib.tutorials.evadventure.tests.test_combat)
|
||||
```
|
||||
Unit testing of the Turnbased combat handler is straight forward, you follow the process of earlier lessons to test that each method on the handler returns what you expect with mocked inputs.
|
||||
|
||||
|
|
@ -1237,7 +1237,7 @@ Unit testing the code is not enough to see that combat works. We need to also ma
|
|||
- An item (like a potion) we can `use`.
|
||||
|
||||
```{sidebar}
|
||||
You can find an example batch-code script in [evennia/contrib/tutorials/evadventure/batchscripts/turnbased_combat_demo.py](github:evennia/contrib/tutorials/evadventure/batchscripts/turnbased_combat_demo.py)
|
||||
You can find an example combat batch-code script in `evennia/contrib/tutorials/evadventure/`, in [batchscripts/turnbased_combat_demo.py](github:evennia/contrib/tutorials/evadventure/batchscripts/turnbased_combat_demo.py)
|
||||
```
|
||||
|
||||
In [The Twitch combat lesson](./Beginner-Tutorial-Combat-Twitch.md) we used a [batch-command script](../../../Components/Batch-Command-Processor.md) to create the testing environment in game. This runs in-game Evennia commands in sequence. For demonstration purposes we'll instead use a [batch-code script](../../../Components/Batch-Code-Processor.md), which runs raw Python code in a repeatable way. A batch-code script is much more flexible than a batch-command script.
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ You can change up your strategy by performing other actions (like drinking a pot
|
|||
## General principle
|
||||
|
||||
```{sidebar}
|
||||
An example of an implemented Twitch combat system can be found in [evennia/contrib/tutorials/evadventure/combat_twitch.py](evennia.contrib.tutorials.evadventure.combat_twitch).
|
||||
An example of an implemented Twitch combat system can be found in `evennia/contrib/tutorials`, in [evadventure/combat_twitch.py](evennia.contrib.tutorials.evadventure.combat_twitch).
|
||||
```
|
||||
Here is the general design of the Twitch-based combat handler:
|
||||
|
||||
|
|
@ -874,7 +874,7 @@ Now that we have the Look command set, we can finish the Twitch combat handler.
|
|||
## Unit Testing
|
||||
|
||||
```{sidebar}
|
||||
See [evennia/contrib/tutorials/evadventure/tests/test_combat.py](evennia.contrib.tutorials.evadventure.tests.test_combat) for an example of a full suite of combat tests.
|
||||
For examples of unit tests, see `evennia/contrib/tutorials`, in [evadventure/tests/test_combat.py](evennia.contrib.tutorials.evadventure.tests.test_combat) for an example of a full suite of combat tests.
|
||||
```
|
||||
|
||||
> Create `evadventure/tests/test_combat.py` (if you don't already have it).
|
||||
|
|
@ -920,7 +920,7 @@ Inside the test, we use the `self.call()` method to explicitly fire the Command
|
|||
## A small combat test
|
||||
|
||||
```{sidebar}
|
||||
You can find an example batch-command script in [evennia/contrib/tutorials/evadventure/batchscripts/twitch_combat_demo.ev](github:evennia/contrib/tutorials/evadventure/batchscripts/twitch_combat_demo.ev)
|
||||
You can find an example batch-command script at `evennia/contrib/tutorials/evadventure`, in [batchscripts/twitch_combat_demo.ev](github:evennia/contrib/tutorials/evadventure/batchscripts/twitch_combat_demo.ev)
|
||||
```
|
||||
Showing that the individual pieces of code works (unit testing) is not enough to be sure that your combat system is actually working. We need to test all the pieces _together_. This is often called _functional testing_. While functional testing can also be automated, wouldn't it be fun to be able to actually see our code in action?
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,224 @@
|
|||
# Dynamically generated Dungeon
|
||||
# Procedurally generated Dungeon
|
||||
|
||||
The rooms that we discussed in the [lesson about Rooms](./Beginner-Tutorial-Rooms.md) are all _manually_ generated. That is, a human builder would have to sit down and spawn each room manually, either in-game or using code.
|
||||
|
||||
In this lesson we'll explore _procedural_ generation of the rooms making up our game's underground dungeon. Procedural means that its rooms are spawned automatically and semi-randomly as players explore, creating a different dungeon layout every time.
|
||||
|
||||
## Design Concept
|
||||
|
||||
This describes how the procedural generation should work at a high level. It's important to understand this before we start writing code.
|
||||
|
||||
We will assume our dungeon exists on a 2D plane (x,y, no z directions). We will only use N,E,S,W compass directions, but there is no reason this design couldn't work with SE, NW etc, except that this could make it harder for the player to visualize. More possible directions also make it more likely to produce collisions and one-way exits (see below).
|
||||
|
||||
This design is pretty simple, but just by playing with some of its settings, it can produce very different-feeling dungeon systems.
|
||||
|
||||
### The starting room
|
||||
|
||||
The idea is that all players will descend down a well to get to the start of the dungeon. The bottom of the well is a statically created room that won't change.
|
||||
|
||||
```{code-block}
|
||||
:caption: Starting room
|
||||
|
||||
Branch N
|
||||
▲
|
||||
│
|
||||
┌────────┼────────┐
|
||||
│ │n │
|
||||
│ ▼ │
|
||||
│ │
|
||||
│ e│
|
||||
Branch W ◄─┼─► up▲ ◄─┼─► Branch E1
|
||||
│w │
|
||||
│ │
|
||||
│ ▲ │
|
||||
│ │s │
|
||||
└────────┼────────┘
|
||||
│
|
||||
▼
|
||||
Branch S
|
||||
```
|
||||
|
||||
The magic happens when you choose one of the exits from this room (except the one leading you back to the surface). Let's assume a PC descends down to the start room and moves `east`:
|
||||
|
||||
- The first person to go east will spawn a new "Dungeon branch" (Branch E1 in the diagram). This is a separate "instance" of dungeon compared to what would spawn if moving through any of the other exits. Rooms spawned within one dungeon branch will never overlap with that of another dungeon branch.
|
||||
- A timer starts. While this timer is active, everyone going `east` will end up in Branch E1. This allows for players to team up and collaborate to take on a branch.
|
||||
- After the timer runs out, everyone going `east` will instead end up in a _new_ Branch E2. This is a new branch that has no overlap with Branch E1.
|
||||
- PCs in Branches E1 and E2 can always retreat `west` back to the starting room, but after the timer runs out this is now a one-way exit - they won't be able to return to their old branches if they do.
|
||||
|
||||
### Generating new branch rooms
|
||||
|
||||
Each branch is managed by an branch _orchestrator_. The orchestrator tracks the layout of rooms belonging to this branch on an (X, Y) coordinate grid.
|
||||
|
||||
```{code-block}
|
||||
:caption: Creating the eastern branch and its first room
|
||||
?
|
||||
▲
|
||||
│
|
||||
┌─────────┐ ┌────┼────┐
|
||||
│ │ │A │ │
|
||||
│ │ │ PC │
|
||||
│ start◄─┼───┼─► is ──┼──►?
|
||||
│ │ │ here │
|
||||
│ │ │ │ │
|
||||
└─────────┘ └────┼────┘
|
||||
│
|
||||
▼
|
||||
```
|
||||
|
||||
The start room is always at coordinate `(0, 0)`.
|
||||
|
||||
A dungeon room is only created when actually moving to it. In the above example, the PC moved `east` from the start room, which initiated a new dungeon branch with its own branch orchestrator. The orchestrator also created a new room (room `A`) at coordinate `(1,0)`. In this case it (randomly) seeded this room with three exits `north`, `east` and `south`.
|
||||
Since this branch was just created, the exit back to the start room is still two-way.
|
||||
|
||||
This is the procedure the orchestrator follows when spawning a new room:
|
||||
|
||||
- It always creates an exit back to the room we came from.
|
||||
- It checks how many unexplored exits we have in the dungeon right now. That is, how many exits we haven't yet traversed. This number must never be zero unless we want a dungeon that can be 'finished'. The maximum number of unexplored exits open at any given time is a setting we can experiment with. A small max number leads to linear dungeon, a bigger number makes the dungeon sprawling and maze-like.
|
||||
- Outgoing exits (exits not leading back to where we came) are generated with the following rules:
|
||||
- Randomly create between 0 and the number of outgoing exits allowed by the room and the branches' current budget of allowed open unexplored exits.
|
||||
- Create 0 outgoing exits (a dead-end) only if this would leave at least one unexplored exit open somewhere in the dungeon branch.
|
||||
- Do _not_ create an exit that would connect the exit to a previously generated room (so we prefer exits leading to new places rather than back to old ones)
|
||||
- If a previously created exit end up pointing to a newly created room, this _is_ allowed, and is the only time a one-way exit will happen (example below). All other exits are always two-way exits. This also presents the only small chance of closing out a dungeon with no way to proceed but to return to the start.
|
||||
- Never create an exit back to the start room (e.g. from another direction). The only way to get back to the start room is by back tracking.
|
||||
|
||||
In the following examples, we assume the maximum number of unexplored exits allowed open at any time is set to 4.
|
||||
|
||||
```{code-block}
|
||||
:caption: After four steps in the eastern dungeon branch
|
||||
?
|
||||
▲
|
||||
│
|
||||
┌─────────┐ ┌────┼────┐
|
||||
│ │ │A │ │
|
||||
│ │ │ │
|
||||
│ start◄─┼───┼─ ──┼─►?
|
||||
│ │ │ ▲ │
|
||||
│ │ │ │ │
|
||||
└─────────┘ └────┼────┘
|
||||
│
|
||||
┌────┼────┐ ┌─────────┐ ┌─────────┐
|
||||
│B │ │ │C │ │D │
|
||||
│ ▼ │ │ │ │ PC │
|
||||
?◄──┼─ ◄─┼───┼─► ◄─┼───┼─► is │
|
||||
│ │ │ │ │ here │
|
||||
│ │ │ │ │ │
|
||||
└─────────┘ └─────────┘ └─────────┘
|
||||
```
|
||||
|
||||
1. PC moves `east` from the start room. A new room `A` (coordinate `(1, 0)` ) is created. After a while the exit back to the start room becomes a one-way exit. The branch can have at most 4 unexplored exits, and the orchestrator randomly adds three additional exits out of room `A`.
|
||||
2. PC moves `south`. A new room `B` (`(1,-1)`) is created, with two random exits, which is as many as the orchetrator is allowed to create at this time (4 are now open). It also always creates an exit back to the previous room (`A`)
|
||||
3. PC moves `east` (coordinate (`(2, -1)`). A new room `C` is created. The orchestrator already has 3 exits unexplored, so it can only add one exit our of this room.
|
||||
4. PC moves `east` (`(3, -1)`). While the orchestrator still has a budget of one exit, it knows there are other unexplored exits elsewhere, and is allowed to randomly create 0 exits. This is a dead end. The PC must go back and explore another direction.
|
||||
|
||||
Let's change the dungeon a bit to do another example:
|
||||
|
||||
```{code-block}
|
||||
:caption: Looping around
|
||||
?
|
||||
▲
|
||||
│
|
||||
┌─────────┐ ┌────┼────┐
|
||||
│ │ │A │ │
|
||||
│ │ │ │
|
||||
│ start◄─┼───┼─ ──┼──►?
|
||||
│ │ │ ▲ │
|
||||
│ │ │ │ │ ?
|
||||
└─────────┘ └────┼────┘ ▲
|
||||
│ │
|
||||
┌────┼────┐ ┌────┼────┐
|
||||
│B │ │ │C │ │
|
||||
│ ▼ │ │ PC │
|
||||
?◄──┼─ ◄─┼───┼─► is │
|
||||
│ │ │ here │
|
||||
│ │ │ │
|
||||
└─────────┘ └─────────┘
|
||||
|
||||
```
|
||||
|
||||
In this example the PC moved `east`, `south`, `east` but the exit out of room `C` is leading north, into a coordinate where `A` already has an exit pointing to. Going `north` here leads to the following:
|
||||
|
||||
```{code-block}
|
||||
:caption: Creation of a one-way exit
|
||||
?
|
||||
▲
|
||||
│
|
||||
┌─────────┐ ┌────┼────┐ ┌─────────┐
|
||||
│ │ │A │ │ │D PC │
|
||||
│ │ │ │ │ is │
|
||||
│ start◄─┼───┼─ ──┼───┼─► here │
|
||||
│ │ │ ▲ │ │ ▲ │
|
||||
│ │ │ │ │ │ │ │
|
||||
└─────────┘ └────┼────┘ └────┼────┘
|
||||
│ │
|
||||
┌────┼────┐ ┌────┼────┐
|
||||
│B │ │ │C │ │
|
||||
│ ▼ │ │ ▼ │
|
||||
?◄──┼─ ◄─┼───┼─► │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
└─────────┘ └─────────┘
|
||||
```
|
||||
|
||||
As the PC moves `north`, the room `D` is created at `(2,0)`.
|
||||
|
||||
While `C` to `D` get a two-way exit as normal, this creates a one-way exit from `A` to `D`.
|
||||
|
||||
Whichever exit leads to actually creating the room gets the two-way exit, so if the PC had walked back from `C` and created room `D` by going `east` from room `A`, then the one-way exit would be from room `C` instead.
|
||||
|
||||
> If the maximum allowed number of open unexplored exits is small, this case is the only situation where it's possible to 'finish' the dungeon (having no more unexplored exits to follow). We accept this as a case where the PCs just have to turn back.
|
||||
|
||||
```{code-block}
|
||||
:caption: Never link back to start room
|
||||
?
|
||||
▲
|
||||
│
|
||||
┌─────────┐ ┌────┼────┐ ┌─────────┐
|
||||
│ │ │A │ │ │D │
|
||||
│ │ │ │ │ │
|
||||
│ start◄─┼───┼─ ──┼───┼─► │
|
||||
│ │ │ ▲ │ │ ▲ │
|
||||
│ │ │ │ │ │ │ │
|
||||
└─────────┘ └────┼────┘ └────┼────┘
|
||||
│ │
|
||||
┌─────────┐ ┌────┼────┐ ┌────┼────┐
|
||||
│E │ │B │ │ │C │ │
|
||||
│ PC │ │ ▼ │ │ ▼ │
|
||||
│ is ◄─┼───┼─► ◄─┼───┼─► │
|
||||
│ here │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
└─────────┘ └─────────┘ └─────────┘
|
||||
```
|
||||
|
||||
Here the PC moved `west` from room `B` creating room `E` at `(0, -1)`.
|
||||
|
||||
The orchestrator never creates a link back to the start room, but it _could_ have created up to two new exits `west` and/or `south`. Since there's still an unexplored exit `north` from room `A`, the orchestrator is also allowed to randomly assign 0 exits, which is what it did here.
|
||||
|
||||
The PC needs to backtrack and go `north` from `A` to continue exploring this dungeon branch.
|
||||
|
||||
### Making the dungeon dangerous
|
||||
|
||||
A dungeon would not be interesting without peril! There needs to be monsters to slay, puzzles to solve and treasure to be had.
|
||||
|
||||
When PCs first enters a room, that room is marked as `not clear`. While a room is not cleared, the PCs _cannot use any of the unexplored exits out of that room_. They _can_ still retreat back the way they came unless they become locked in combat, in which case they have to flee from that first.
|
||||
|
||||
Once PCs have overcome the challenge of the room (and probably earned some reward), will it change to `clear` . A room can auto-clear if it is spawned empty or has no challenge meant to block the PCs (like a written hint for a puzzle elsewhere).
|
||||
|
||||
Note that clear/non-clear only relates to the challenge associated with that room. Roaming monsters (see the [AI tutorial](./Beginner-Tutorial-AI.md)) can lead to combat taking place in previously 'cleared' rooms.
|
||||
|
||||
### Difficulty scaling
|
||||
|
||||
```{sidebar} Risk and reward
|
||||
The concept of dungeon depth/difficulty works well together with limited resources. If healing is limited to what can be carried, this leads to players having to decide if they want to risk push deeper or take their current spoils and retreat back to the surface to recover.
|
||||
```
|
||||
|
||||
The "difficulty" of the dungeon is measured by the "depth" PCs have delved to. This is given as the _radial distance_ from the start room, rounded down, found by the good old [Pythagorean theorem](https://en.wikipedia.org/wiki/Pythagorean_theorem):
|
||||
|
||||
depth = int(math.sqrt(x**2 + y**2))
|
||||
|
||||
So if you are in room `(1, 1)` you are at difficulty 1. Conversely at room coordinate `(4,-5)` the difficulty is 6. Increasing depth should lead to tougher challenges but greater rewards.
|
||||
|
||||
## Implementation
|
||||
|
||||
```{warning}
|
||||
This part of the Beginner tutorial is still being developed.
|
||||
TODO: This part is TODO.
|
||||
```
|
||||
|
|
@ -80,14 +80,14 @@ class EvAdventureNPC(LivingMixin, DefaultCharacter):
|
|||
"""
|
||||
self.hp = self.hp_max
|
||||
self.tags.add("npcs", category="group")
|
||||
|
||||
def ai_next_action(self, **kwargs):
|
||||
"""
|
||||
The system should regularly poll this method to have
|
||||
the NPC do their next AI action.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class EvAdventureMob(EvAdventureNPC):
|
||||
"""
|
||||
Mob(ile) NPC to be used for enemies.
|
||||
|
||||
"""
|
||||
|
||||
```
|
||||
|
||||
- **Line 9**: By use of _multiple inheritance_ we use the `LinvingMixin` we created in the [Character lesson](./Beginner-Tutorial-Characters.md). This includes a lot of useful methods, such as showing our 'hurt level', methods to use to heal, hooks to call when getting attacked, hurt and so on. We can re-use all of those in upcoming NPC subclasses.
|
||||
|
|
@ -96,8 +96,8 @@ class EvAdventureNPC(LivingMixin, DefaultCharacter):
|
|||
- **Lines 17, 18**: The `morale` and `allegiance` are _Knave_ properties determining how likely the NPC is to flee in a combat situation and if they are hostile or friendly.
|
||||
- **Line 19**: The `is_idle` Attribute is a useful property. It should be available on all NPCs and will be used to disable AI entirely.
|
||||
- **Line 59**: We make sure to tag NPCs. We may want to group different NPCs together later, for example to have all NPCs with the same tag respond if one of them is attacked.
|
||||
- **Line 61**: The `ai_next_action` is a method we prepare for the system to be able to ask the NPC 'what do you want to do next?'. In it we will add all logic related to the artificial intelligence of the NPC - such as walking around, attacking and performing other actions.
|
||||
|
||||
We make an empty subclass `EvAdventureMob`. A 'mob' (short for 'mobile') is a common MUD term for NPCs that can move around on their own. We will in the future use this class to represent enemies in the game. We will get back to this class [in the lesson about adding AI][Beginner-Tutoroal-AI].
|
||||
|
||||
## Testing
|
||||
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ to accounts respectively.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.admin.CmdEmit.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['remit', 'pemit']</em><a class="headerlink" href="#evennia.commands.default.admin.CmdEmit.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['pemit', 'remit']</em><a class="headerlink" href="#evennia.commands.default.admin.CmdEmit.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -362,7 +362,7 @@ to accounts respectively.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.admin.CmdEmit.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'remit pemit', 'category': 'admin', 'key': 'emit', 'no_prefix': ' remit pemit', 'tags': '', 'text': '\n admin command for emitting message to multiple objects\n\n Usage:\n emit[/switches] [<obj>, <obj>, ... =] <message>\n remit [<obj>, <obj>, ... =] <message>\n pemit [<obj>, <obj>, ... =] <message>\n\n Switches:\n room - limit emits to rooms only (default)\n accounts - limit emits to accounts only\n contents - send to the contents of matched objects too\n\n Emits a message to the selected objects or to\n your immediate surroundings. If the object is a room,\n send to its contents. remit and pemit are just\n limited forms of emit, for sending to rooms and\n to accounts respectively.\n '}</em><a class="headerlink" href="#evennia.commands.default.admin.CmdEmit.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'pemit remit', 'category': 'admin', 'key': 'emit', 'no_prefix': ' pemit remit', 'tags': '', 'text': '\n admin command for emitting message to multiple objects\n\n Usage:\n emit[/switches] [<obj>, <obj>, ... =] <message>\n remit [<obj>, <obj>, ... =] <message>\n pemit [<obj>, <obj>, ... =] <message>\n\n Switches:\n room - limit emits to rooms only (default)\n accounts - limit emits to accounts only\n contents - send to the contents of matched objects too\n\n Emits a message to the selected objects or to\n your immediate surroundings. If the object is a room,\n send to its contents. remit and pemit are just\n limited forms of emit, for sending to rooms and\n to accounts respectively.\n '}</em><a class="headerlink" href="#evennia.commands.default.admin.CmdEmit.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ skipping, reloading etc.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.batchprocess.CmdBatchCommands.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['batchcommand', 'batchcmd']</em><a class="headerlink" href="#evennia.commands.default.batchprocess.CmdBatchCommands.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['batchcmd', 'batchcommand']</em><a class="headerlink" href="#evennia.commands.default.batchprocess.CmdBatchCommands.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -183,7 +183,7 @@ skipping, reloading etc.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.batchprocess.CmdBatchCommands.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'batchcommand batchcmd', 'category': 'building', 'key': 'batchcommands', 'no_prefix': ' batchcommand batchcmd', 'tags': '', 'text': '\n build from batch-command file\n\n Usage:\n batchcommands[/interactive] <python.path.to.file>\n\n Switch:\n interactive - this mode will offer more control when\n executing the batch file, like stepping,\n skipping, reloading etc.\n\n Runs batches of commands from a batch-cmd text file (*.ev).\n\n '}</em><a class="headerlink" href="#evennia.commands.default.batchprocess.CmdBatchCommands.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'batchcmd batchcommand', 'category': 'building', 'key': 'batchcommands', 'no_prefix': ' batchcmd batchcommand', 'tags': '', 'text': '\n build from batch-command file\n\n Usage:\n batchcommands[/interactive] <python.path.to.file>\n\n Switch:\n interactive - this mode will offer more control when\n executing the batch file, like stepping,\n skipping, reloading etc.\n\n Runs batches of commands from a batch-cmd text file (*.ev).\n\n '}</em><a class="headerlink" href="#evennia.commands.default.batchprocess.CmdBatchCommands.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -643,7 +643,7 @@ You can specify the /force switch to bypass this confirmation.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.building.CmdDestroy.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['@del', '@delete']</em><a class="headerlink" href="#evennia.commands.default.building.CmdDestroy.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['@delete', '@del']</em><a class="headerlink" href="#evennia.commands.default.building.CmdDestroy.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -684,7 +684,7 @@ You can specify the /force switch to bypass this confirmation.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.building.CmdDestroy.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '@del @delete', 'category': 'building', 'key': '@destroy', 'no_prefix': 'destroy del delete', 'tags': '', 'text': '\n permanently delete objects\n\n Usage:\n destroy[/switches] [obj, obj2, obj3, [dbref-dbref], ...]\n\n Switches:\n override - The destroy command will usually avoid accidentally\n destroying account objects. This switch overrides this safety.\n force - destroy without confirmation.\n Examples:\n destroy house, roof, door, 44-78\n destroy 5-10, flower, 45\n destroy/force north\n\n Destroys one or many objects. If dbrefs are used, a range to delete can be\n given, e.g. 4-10. Also the end points will be deleted. This command\n displays a confirmation before destroying, to make sure of your choice.\n You can specify the /force switch to bypass this confirmation.\n '}</em><a class="headerlink" href="#evennia.commands.default.building.CmdDestroy.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '@delete @del', 'category': 'building', 'key': '@destroy', 'no_prefix': 'destroy delete del', 'tags': '', 'text': '\n permanently delete objects\n\n Usage:\n destroy[/switches] [obj, obj2, obj3, [dbref-dbref], ...]\n\n Switches:\n override - The destroy command will usually avoid accidentally\n destroying account objects. This switch overrides this safety.\n force - destroy without confirmation.\n Examples:\n destroy house, roof, door, 44-78\n destroy 5-10, flower, 45\n destroy/force north\n\n Destroys one or many objects. If dbrefs are used, a range to delete can be\n given, e.g. 4-10. Also the end points will be deleted. This command\n displays a confirmation before destroying, to make sure of your choice.\n You can specify the /force switch to bypass this confirmation.\n '}</em><a class="headerlink" href="#evennia.commands.default.building.CmdDestroy.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
@ -1411,7 +1411,7 @@ server settings.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.building.CmdTypeclass.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['@swap', '@update', '@type', '@typeclasses', '@parent']</em><a class="headerlink" href="#evennia.commands.default.building.CmdTypeclass.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['@swap', '@type', '@parent', '@update', '@typeclasses']</em><a class="headerlink" href="#evennia.commands.default.building.CmdTypeclass.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -1442,7 +1442,7 @@ server settings.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.building.CmdTypeclass.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '@swap @update @type @typeclasses @parent', 'category': 'building', 'key': '@typeclass', 'no_prefix': 'typeclass swap update type typeclasses parent', 'tags': '', 'text': "\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] <object> [= typeclass.path]\n typeclass/prototype <object> = prototype_key\n\n typeclasses or typeclass/list/show [typeclass.path]\n swap - this is a shorthand for using /force/reset flags.\n update - this is a shorthand for using the /force/reload flag.\n\n Switch:\n show, examine - display the current typeclass of object (default) or, if\n given a typeclass path, show the docstring of that typeclass.\n update - *only* re-run at_object_creation on this object\n meaning locks or other properties set later may remain.\n reset - clean out *all* the attributes and properties on the\n object - basically making this a new clean object. This will also\n reset cmdsets!\n force - change to the typeclass also if the object\n already has a typeclass of the same name.\n list - show available typeclasses. Only typeclasses in modules actually\n imported or used from somewhere in the code will show up here\n (those typeclasses are still available if you know the path)\n prototype - clean and overwrite the object with the specified\n prototype key - effectively making a whole new object.\n\n Example:\n type button = examples.red_button.RedButton\n type/prototype button=a red button\n\n If the typeclass_path is not given, the current object's typeclass is\n assumed.\n\n View or set an object's typeclass. If setting, the creation hooks of the\n new typeclass will be run on the object. If you have clashing properties on\n the old class, use /reset. By default you are protected from changing to a\n typeclass of the same name as the one you already have - use /force to\n override this protection.\n\n The given typeclass must be identified by its location using python\n dot-notation pointing to the correct module and class. If no typeclass is\n given (or a wrong typeclass is given). Errors in the path or new typeclass\n will lead to the old typeclass being kept. The location of the typeclass\n module is searched from the default typeclass directory, as defined in the\n server settings.\n\n "}</em><a class="headerlink" href="#evennia.commands.default.building.CmdTypeclass.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '@swap @type @parent @update @typeclasses', 'category': 'building', 'key': '@typeclass', 'no_prefix': 'typeclass swap type parent update typeclasses', 'tags': '', 'text': "\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] <object> [= typeclass.path]\n typeclass/prototype <object> = prototype_key\n\n typeclasses or typeclass/list/show [typeclass.path]\n swap - this is a shorthand for using /force/reset flags.\n update - this is a shorthand for using the /force/reload flag.\n\n Switch:\n show, examine - display the current typeclass of object (default) or, if\n given a typeclass path, show the docstring of that typeclass.\n update - *only* re-run at_object_creation on this object\n meaning locks or other properties set later may remain.\n reset - clean out *all* the attributes and properties on the\n object - basically making this a new clean object. This will also\n reset cmdsets!\n force - change to the typeclass also if the object\n already has a typeclass of the same name.\n list - show available typeclasses. Only typeclasses in modules actually\n imported or used from somewhere in the code will show up here\n (those typeclasses are still available if you know the path)\n prototype - clean and overwrite the object with the specified\n prototype key - effectively making a whole new object.\n\n Example:\n type button = examples.red_button.RedButton\n type/prototype button=a red button\n\n If the typeclass_path is not given, the current object's typeclass is\n assumed.\n\n View or set an object's typeclass. If setting, the creation hooks of the\n new typeclass will be run on the object. If you have clashing properties on\n the old class, use /reset. By default you are protected from changing to a\n typeclass of the same name as the one you already have - use /force to\n override this protection.\n\n The given typeclass must be identified by its location using python\n dot-notation pointing to the correct module and class. If no typeclass is\n given (or a wrong typeclass is given). Errors in the path or new typeclass\n will lead to the old typeclass being kept. The location of the typeclass\n module is searched from the default typeclass directory, as defined in the\n server settings.\n\n "}</em><a class="headerlink" href="#evennia.commands.default.building.CmdTypeclass.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ for everyone to use, you need build privileges and the alias command.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.general.CmdNick.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['nickname', 'nicks']</em><a class="headerlink" href="#evennia.commands.default.general.CmdNick.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['nicks', 'nickname']</em><a class="headerlink" href="#evennia.commands.default.general.CmdNick.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -314,7 +314,7 @@ for everyone to use, you need build privileges and the alias command.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.general.CmdNick.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'nickname nicks', 'category': 'general', 'key': 'nick', 'no_prefix': ' nickname nicks', 'tags': '', 'text': '\n define a personal alias/nick by defining a string to\n match and replace it with another on the fly\n\n Usage:\n nick[/switches] <string> [= [replacement_string]]\n nick[/switches] <template> = <replacement_template>\n nick/delete <string> or number\n nicks\n\n Switches:\n inputline - replace on the inputline (default)\n object - replace on object-lookup\n account - replace on account-lookup\n list - show all defined aliases (also "nicks" works)\n delete - remove nick by index in /list\n clearall - clear all nicks\n\n Examples:\n nick hi = say Hello, I\'m Sarah!\n nick/object tom = the tall man\n nick build $1 $2 = create/drop $1;$2\n nick tell $1 $2=page $1=$2\n nick tm?$1=page tallman=$1\n nick tm\\=$1=page tallman=$1\n\n A \'nick\' is a personal string replacement. Use $1, $2, ... to catch arguments.\n Put the last $-marker without an ending space to catch all remaining text. You\n can also use unix-glob matching for the left-hand side <string>:\n\n * - matches everything\n ? - matches 0 or 1 single characters\n [abcd] - matches these chars in any order\n [!abcd] - matches everything not among these chars\n \\= - escape literal \'=\' you want in your <string>\n\n Note that no objects are actually renamed or changed by this command - your nicks\n are only available to you. If you want to permanently add keywords to an object\n for everyone to use, you need build privileges and the alias command.\n\n '}</em><a class="headerlink" href="#evennia.commands.default.general.CmdNick.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'nicks nickname', 'category': 'general', 'key': 'nick', 'no_prefix': ' nicks nickname', 'tags': '', 'text': '\n define a personal alias/nick by defining a string to\n match and replace it with another on the fly\n\n Usage:\n nick[/switches] <string> [= [replacement_string]]\n nick[/switches] <template> = <replacement_template>\n nick/delete <string> or number\n nicks\n\n Switches:\n inputline - replace on the inputline (default)\n object - replace on object-lookup\n account - replace on account-lookup\n list - show all defined aliases (also "nicks" works)\n delete - remove nick by index in /list\n clearall - clear all nicks\n\n Examples:\n nick hi = say Hello, I\'m Sarah!\n nick/object tom = the tall man\n nick build $1 $2 = create/drop $1;$2\n nick tell $1 $2=page $1=$2\n nick tm?$1=page tallman=$1\n nick tm\\=$1=page tallman=$1\n\n A \'nick\' is a personal string replacement. Use $1, $2, ... to catch arguments.\n Put the last $-marker without an ending space to catch all remaining text. You\n can also use unix-glob matching for the left-hand side <string>:\n\n * - matches everything\n ? - matches 0 or 1 single characters\n [abcd] - matches these chars in any order\n [!abcd] - matches everything not among these chars\n \\= - escape literal \'=\' you want in your <string>\n\n Note that no objects are actually renamed or changed by this command - your nicks\n are only available to you. If you want to permanently add keywords to an object\n for everyone to use, you need build privileges and the alias command.\n\n '}</em><a class="headerlink" href="#evennia.commands.default.general.CmdNick.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -697,7 +697,7 @@ See <a href="#id11"><span class="problematic" id="id12">|</span></a>luhttps://ww
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.system.CmdTasks.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['@delays', '@task']</em><a class="headerlink" href="#evennia.commands.default.system.CmdTasks.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['@task', '@delays']</em><a class="headerlink" href="#evennia.commands.default.system.CmdTasks.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -743,7 +743,7 @@ to all the variables defined therein.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.system.CmdTasks.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '@delays @task', 'category': 'system', 'key': '@tasks', 'no_prefix': 'tasks delays task', 'tags': '', 'text': "\n Display or terminate active tasks (delays).\n\n Usage:\n tasks[/switch] [task_id or function_name]\n\n Switches:\n pause - Pause the callback of a task.\n unpause - Process all callbacks made since pause() was called.\n do_task - Execute the task (call its callback).\n call - Call the callback of this task.\n remove - Remove a task without executing it.\n cancel - Stop a task from automatically executing.\n\n Notes:\n A task is a single use method of delaying the call of a function. Calls are created\n in code, using `evennia.utils.delay`.\n See |luhttps://www.evennia.com/docs/latest/Command-Duration.html|ltthe docs|le for help.\n\n By default, tasks that are canceled and never called are cleaned up after one minute.\n\n Examples:\n - `tasks/cancel move_callback` - Cancels all movement delays from the slow_exit contrib.\n In this example slow exits creates it's tasks with\n `utils.delay(move_delay, move_callback)`\n - `tasks/cancel 2` - Cancel task id 2.\n\n "}</em><a class="headerlink" href="#evennia.commands.default.system.CmdTasks.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '@task @delays', 'category': 'system', 'key': '@tasks', 'no_prefix': 'tasks task delays', 'tags': '', 'text': "\n Display or terminate active tasks (delays).\n\n Usage:\n tasks[/switch] [task_id or function_name]\n\n Switches:\n pause - Pause the callback of a task.\n unpause - Process all callbacks made since pause() was called.\n do_task - Execute the task (call its callback).\n call - Call the callback of this task.\n remove - Remove a task without executing it.\n cancel - Stop a task from automatically executing.\n\n Notes:\n A task is a single use method of delaying the call of a function. Calls are created\n in code, using `evennia.utils.delay`.\n See |luhttps://www.evennia.com/docs/latest/Command-Duration.html|ltthe docs|le for help.\n\n By default, tasks that are canceled and never called are cleaned up after one minute.\n\n Examples:\n - `tasks/cancel move_callback` - Cancels all movement delays from the slow_exit contrib.\n In this example slow exits creates it's tasks with\n `utils.delay(move_delay, move_callback)`\n - `tasks/cancel 2` - Cancel task id 2.\n\n "}</em><a class="headerlink" href="#evennia.commands.default.system.CmdTasks.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -975,7 +975,7 @@ main test suite started with</p>
|
|||
<p>Test the batch processor.</p>
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.tests.TestBatchProcess.red_button">
|
||||
<code class="sig-name descname">red_button</code><em class="property"> = <module 'evennia.contrib.tutorials.red_button.red_button' from '/tmp/tmp8ivem2a_/05ab1c2a9b064f8ca98761a000e56f035ff2ae22/evennia/contrib/tutorials/red_button/red_button.py'></em><a class="headerlink" href="#evennia.commands.default.tests.TestBatchProcess.red_button" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">red_button</code><em class="property"> = <module 'evennia.contrib.tutorials.red_button.red_button' from '/tmp/tmpv8a4t67i/8085aa30db7dde4b4f0f92d6428745441344b73d/evennia/contrib/tutorials/red_button/red_button.py'></em><a class="headerlink" href="#evennia.commands.default.tests.TestBatchProcess.red_button" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ connect “account name” “pass word”</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.unloggedin.CmdUnconnectedConnect.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['conn', 'co', 'con']</em><a class="headerlink" href="#evennia.commands.default.unloggedin.CmdUnconnectedConnect.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['conn', 'con', 'co']</em><a class="headerlink" href="#evennia.commands.default.unloggedin.CmdUnconnectedConnect.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -171,7 +171,7 @@ there is no object yet before the account has logged in)</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.commands.default.unloggedin.CmdUnconnectedConnect.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'conn co con', 'category': 'general', 'key': 'connect', 'no_prefix': ' conn co con', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect "account name" "pass word"\n\n Use the create command to first create an account before logging in.\n\n If you have spaces in your name, enclose it in double quotes.\n '}</em><a class="headerlink" href="#evennia.commands.default.unloggedin.CmdUnconnectedConnect.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'conn con co', 'category': 'general', 'key': 'connect', 'no_prefix': ' conn con co', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect "account name" "pass word"\n\n Use the create command to first create an account before logging in.\n\n If you have spaces in your name, enclose it in double quotes.\n '}</em><a class="headerlink" href="#evennia.commands.default.unloggedin.CmdUnconnectedConnect.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ the module given by settings.CONNECTION_SCREEN_MODULE.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.base_systems.email_login.email_login.CmdUnconnectedConnect.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['conn', 'co', 'con']</em><a class="headerlink" href="#evennia.contrib.base_systems.email_login.email_login.CmdUnconnectedConnect.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['conn', 'con', 'co']</em><a class="headerlink" href="#evennia.contrib.base_systems.email_login.email_login.CmdUnconnectedConnect.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -183,7 +183,7 @@ there is no object yet before the account has logged in)</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.base_systems.email_login.email_login.CmdUnconnectedConnect.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'conn co con', 'category': 'general', 'key': 'connect', 'no_prefix': ' conn co con', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect <email> <password>\n\n Use the create command to first create an account before logging in.\n '}</em><a class="headerlink" href="#evennia.contrib.base_systems.email_login.email_login.CmdUnconnectedConnect.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'conn con co', 'category': 'general', 'key': 'connect', 'no_prefix': ' conn con co', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect <email> <password>\n\n Use the create command to first create an account before logging in.\n '}</em><a class="headerlink" href="#evennia.contrib.base_systems.email_login.email_login.CmdUnconnectedConnect.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.base_systems.ingame_python.commands.CmdCallback.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['@callbacks', '@calls', '@callback']</em><a class="headerlink" href="#evennia.contrib.base_systems.ingame_python.commands.CmdCallback.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['@callback', '@callbacks', '@calls']</em><a class="headerlink" href="#evennia.contrib.base_systems.ingame_python.commands.CmdCallback.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -211,7 +211,7 @@ on user permission.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.base_systems.ingame_python.commands.CmdCallback.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '@callbacks @calls @callback', 'category': 'building', 'key': '@call', 'no_prefix': 'call callbacks calls callback', 'tags': '', 'text': '\n Command to edit callbacks.\n '}</em><a class="headerlink" href="#evennia.contrib.base_systems.ingame_python.commands.CmdCallback.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '@callback @callbacks @calls', 'category': 'building', 'key': '@call', 'no_prefix': 'call callback callbacks calls', 'tags': '', 'text': '\n Command to edit callbacks.\n '}</em><a class="headerlink" href="#evennia.contrib.base_systems.ingame_python.commands.CmdCallback.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ aliases to an already joined channel.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.CmdAddCom.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['aliaschan', 'chanalias']</em><a class="headerlink" href="#evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.CmdAddCom.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['chanalias', 'aliaschan']</em><a class="headerlink" href="#evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.CmdAddCom.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -205,7 +205,7 @@ aliases to an already joined channel.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.CmdAddCom.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'aliaschan chanalias', 'category': 'comms', 'key': 'addcom', 'no_prefix': ' aliaschan chanalias', 'tags': '', 'text': '\n Add a channel alias and/or subscribe to a channel\n\n Usage:\n addcom [alias=] <channel>\n\n Joins a given channel. If alias is given, this will allow you to\n refer to the channel by this alias rather than the full channel\n name. Subsequent calls of this command can be used to add multiple\n aliases to an already joined channel.\n '}</em><a class="headerlink" href="#evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.CmdAddCom.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'chanalias aliaschan', 'category': 'comms', 'key': 'addcom', 'no_prefix': ' chanalias aliaschan', 'tags': '', 'text': '\n Add a channel alias and/or subscribe to a channel\n\n Usage:\n addcom [alias=] <channel>\n\n Joins a given channel. If alias is given, this will allow you to\n refer to the channel by this alias rather than the full channel\n name. Subsequent calls of this command can be used to add multiple\n aliases to an already joined channel.\n '}</em><a class="headerlink" href="#evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.CmdAddCom.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ the operation will be general or on the room.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.full_systems.evscaperoom.commands.CmdGiveUp.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['q', 'chicken out', 'quit', 'abort']</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdGiveUp.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['q', 'abort', 'quit', 'chicken out']</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdGiveUp.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
|
|
@ -249,7 +249,7 @@ set in self.parse())</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.full_systems.evscaperoom.commands.CmdGiveUp.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'q chicken out quit abort', 'category': 'evscaperoom', 'key': 'give up', 'no_prefix': ' q chicken out quit abort', 'tags': '', 'text': '\n Give up\n\n Usage:\n give up\n\n Abandons your attempts at escaping and of ever winning the pie-eating contest.\n\n '}</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdGiveUp.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'q abort quit chicken out', 'category': 'evscaperoom', 'key': 'give up', 'no_prefix': ' q abort quit chicken out', 'tags': '', 'text': '\n Give up\n\n Usage:\n give up\n\n Abandons your attempts at escaping and of ever winning the pie-eating contest.\n\n '}</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdGiveUp.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
@ -385,7 +385,7 @@ shout</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.full_systems.evscaperoom.commands.CmdSpeak.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['shout', 'whisper', ';']</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdSpeak.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['whisper', ';', 'shout']</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdSpeak.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -414,7 +414,7 @@ set in self.parse())</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.full_systems.evscaperoom.commands.CmdSpeak.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'shout whisper ;', 'category': 'general', 'key': 'say', 'no_prefix': ' shout whisper ;', 'tags': '', 'text': '\n Perform an communication action.\n\n Usage:\n say <text>\n whisper\n shout\n\n '}</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdSpeak.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'whisper ; shout', 'category': 'general', 'key': 'say', 'no_prefix': ' whisper ; shout', 'tags': '', 'text': '\n Perform an communication action.\n\n Usage:\n say <text>\n whisper\n shout\n\n '}</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdSpeak.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
@ -504,7 +504,7 @@ looks and what actions is available.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.full_systems.evscaperoom.commands.CmdFocus.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['examine', 'ex', 'e', 'unfocus']</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdFocus.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['e', 'ex', 'examine', 'unfocus']</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdFocus.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -533,7 +533,7 @@ set in self.parse())</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.full_systems.evscaperoom.commands.CmdFocus.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'examine ex e unfocus', 'category': 'evscaperoom', 'key': 'focus', 'no_prefix': ' examine ex e unfocus', 'tags': '', 'text': '\n Focus your attention on a target.\n\n Usage:\n focus <obj>\n\n Once focusing on an object, use look to get more information about how it\n looks and what actions is available.\n\n '}</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdFocus.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'e ex examine unfocus', 'category': 'evscaperoom', 'key': 'focus', 'no_prefix': ' e ex examine unfocus', 'tags': '', 'text': '\n Focus your attention on a target.\n\n Usage:\n focus <obj>\n\n Once focusing on an object, use look to get more information about how it\n looks and what actions is available.\n\n '}</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdFocus.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
@ -595,7 +595,7 @@ set in self.parse())</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.full_systems.evscaperoom.commands.CmdGet.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['give', 'i', 'inventory', 'inv']</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdGet.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['i', 'inv', 'inventory', 'give']</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdGet.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
|
|
@ -619,7 +619,7 @@ set in self.parse())</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.full_systems.evscaperoom.commands.CmdGet.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'give i inventory inv', 'category': 'evscaperoom', 'key': 'get', 'no_prefix': ' give i inventory inv', 'tags': '', 'text': '\n Use focus / examine instead.\n\n '}</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdGet.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'i inv inventory give', 'category': 'evscaperoom', 'key': 'get', 'no_prefix': ' i inv inventory give', 'tags': '', 'text': '\n Use focus / examine instead.\n\n '}</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdGet.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
@ -640,7 +640,7 @@ set in self.parse())</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.full_systems.evscaperoom.commands.CmdRerouter.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['@dig', '@open']</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdRerouter.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['@open', '@dig']</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdRerouter.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
|
|
@ -663,7 +663,7 @@ to all the variables defined therein.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.full_systems.evscaperoom.commands.CmdRerouter.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '@dig @open', 'category': 'general', 'key': 'open', 'no_prefix': ' dig open', 'tags': '', 'text': '\n Interact with an object in focus.\n\n Usage:\n <action> [arg]\n\n '}</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdRerouter.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '@open @dig', 'category': 'general', 'key': 'open', 'no_prefix': ' open dig', 'tags': '', 'text': '\n Interact with an object in focus.\n\n Usage:\n <action> [arg]\n\n '}</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdRerouter.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -436,7 +436,7 @@ there is no room above/below you, your movement will fail.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.grid.xyzgrid.commands.CmdFlyAndDive.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['fly', 'dive']</em><a class="headerlink" href="#evennia.contrib.grid.xyzgrid.commands.CmdFlyAndDive.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['dive', 'fly']</em><a class="headerlink" href="#evennia.contrib.grid.xyzgrid.commands.CmdFlyAndDive.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
|
|
@ -459,7 +459,7 @@ to all the variables defined therein.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.grid.xyzgrid.commands.CmdFlyAndDive.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'fly dive', 'category': 'general', 'key': 'fly or dive', 'no_prefix': ' fly dive', 'tags': '', 'text': '\n Fly or Dive up and down.\n\n Usage:\n fly\n dive\n\n Will fly up one room or dive down one room at your current position. If\n there is no room above/below you, your movement will fail.\n\n '}</em><a class="headerlink" href="#evennia.contrib.grid.xyzgrid.commands.CmdFlyAndDive.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'dive fly', 'category': 'general', 'key': 'fly or dive', 'no_prefix': ' dive fly', 'tags': '', 'text': '\n Fly or Dive up and down.\n\n Usage:\n fly\n dive\n\n Will fly up one room or dive down one room at your current position. If\n there is no room above/below you, your movement will fail.\n\n '}</em><a class="headerlink" href="#evennia.contrib.grid.xyzgrid.commands.CmdFlyAndDive.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -340,7 +340,7 @@ everyone but the person rolling.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.rpg.dice.dice.CmdDice.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['roll', '@dice']</em><a class="headerlink" href="#evennia.contrib.rpg.dice.dice.CmdDice.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['@dice', 'roll']</em><a class="headerlink" href="#evennia.contrib.rpg.dice.dice.CmdDice.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -366,7 +366,7 @@ everyone but the person rolling.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.rpg.dice.dice.CmdDice.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'roll @dice', 'category': 'general', 'key': 'dice', 'no_prefix': ' roll dice', 'tags': '', 'text': "\n roll dice\n\n Usage:\n dice[/switch] <nr>d<sides> [modifier] [success condition]\n\n Switch:\n hidden - tell the room the roll is being done, but don't show the result\n secret - don't inform the room about neither roll nor result\n\n Examples:\n dice 3d6 + 4\n dice 1d100 - 2 < 50\n\n This will roll the given number of dice with given sides and modifiers.\n So e.g. 2d6 + 3 means to 'roll a 6-sided die 2 times and add the result,\n then add 3 to the total'.\n Accepted modifiers are +, -, * and /.\n A success condition is given as normal Python conditionals\n (<,>,<=,>=,==,!=). So e.g. 2d6 + 3 > 10 means that the roll will succeed\n only if the final result is above 8. If a success condition is given, the\n outcome (pass/fail) will be echoed along with how much it succeeded/failed\n with. The hidden/secret switches will hide all or parts of the roll from\n everyone but the person rolling.\n "}</em><a class="headerlink" href="#evennia.contrib.rpg.dice.dice.CmdDice.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '@dice roll', 'category': 'general', 'key': 'dice', 'no_prefix': ' dice roll', 'tags': '', 'text': "\n roll dice\n\n Usage:\n dice[/switch] <nr>d<sides> [modifier] [success condition]\n\n Switch:\n hidden - tell the room the roll is being done, but don't show the result\n secret - don't inform the room about neither roll nor result\n\n Examples:\n dice 3d6 + 4\n dice 1d100 - 2 < 50\n\n This will roll the given number of dice with given sides and modifiers.\n So e.g. 2d6 + 3 means to 'roll a 6-sided die 2 times and add the result,\n then add 3 to the total'.\n Accepted modifiers are +, -, * and /.\n A success condition is given as normal Python conditionals\n (<,>,<=,>=,==,!=). So e.g. 2d6 + 3 > 10 means that the roll will succeed\n only if the final result is above 8. If a success condition is given, the\n outcome (pass/fail) will be echoed along with how much it succeeded/failed\n with. The hidden/secret switches will hide all or parts of the roll from\n everyone but the person rolling.\n "}</em><a class="headerlink" href="#evennia.contrib.rpg.dice.dice.CmdDice.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -908,7 +908,7 @@ Using the command without arguments will list all current recogs.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.rpg.rpsystem.rpsystem.CmdRecog.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['recognize', 'forget']</em><a class="headerlink" href="#evennia.contrib.rpg.rpsystem.rpsystem.CmdRecog.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['forget', 'recognize']</em><a class="headerlink" href="#evennia.contrib.rpg.rpsystem.rpsystem.CmdRecog.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
|
|
@ -935,7 +935,7 @@ Using the command without arguments will list all current recogs.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.rpg.rpsystem.rpsystem.CmdRecog.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'recognize forget', 'category': 'general', 'key': 'recog', 'no_prefix': ' recognize forget', 'tags': '', 'text': '\n Recognize another person in the same room.\n\n Usage:\n recog\n recog sdesc as alias\n forget alias\n\n Example:\n recog tall man as Griatch\n forget griatch\n\n This will assign a personal alias for a person, or forget said alias.\n Using the command without arguments will list all current recogs.\n\n '}</em><a class="headerlink" href="#evennia.contrib.rpg.rpsystem.rpsystem.CmdRecog.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'forget recognize', 'category': 'general', 'key': 'recog', 'no_prefix': ' forget recognize', 'tags': '', 'text': '\n Recognize another person in the same room.\n\n Usage:\n recog\n recog sdesc as alias\n forget alias\n\n Example:\n recog tall man as Griatch\n forget griatch\n\n This will assign a personal alias for a person, or forget said alias.\n Using the command without arguments will list all current recogs.\n\n '}</em><a class="headerlink" href="#evennia.contrib.rpg.rpsystem.rpsystem.CmdRecog.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ reference to the AIHandler and a few basic <strong>ai_*</strong> methods for bas
|
|||
|
||||
<span class="n">mob</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">MyMob</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"Goblin"</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">room</span><span class="p">)</span>
|
||||
|
||||
<span class="n">mob</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"patrol"</span><span class="p">)</span>
|
||||
<span class="n">mob</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">set_state</span><span class="p">(</span><span class="s2">"roam"</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># tick the ai whenever needed</span>
|
||||
<span class="n">mob</span><span class="o">.</span><span class="n">ai</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
|
||||
|
|
@ -145,6 +145,16 @@ reference to the AIHandler and a few basic <strong>ai_*</strong> methods for bas
|
|||
<dt id="evennia.contrib.tutorials.evadventure.ai.AIHandler">
|
||||
<em class="property">class </em><code class="sig-prename descclassname">evennia.contrib.tutorials.evadventure.ai.</code><code class="sig-name descname">AIHandler</code><span class="sig-paren">(</span><em class="sig-param"><span class="n">obj</span></em><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AIHandler"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AIHandler" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></p>
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AIHandler.attribute_name">
|
||||
<code class="sig-name descname">attribute_name</code><em class="property"> = 'ai_state'</em><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AIHandler.attribute_name" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AIHandler.attribute_category">
|
||||
<code class="sig-name descname">attribute_category</code><em class="property"> = 'ai_state'</em><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AIHandler.attribute_category" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AIHandler.__init__">
|
||||
<code class="sig-name descname">__init__</code><span class="sig-paren">(</span><em class="sig-param"><span class="n">obj</span></em><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AIHandler.__init__"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AIHandler.__init__" title="Permalink to this definition">¶</a></dt>
|
||||
|
|
@ -164,18 +174,30 @@ reference to the AIHandler and a few basic <strong>ai_*</strong> methods for bas
|
|||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AIHandler.get_targets">
|
||||
<code class="sig-name descname">get_targets</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AIHandler.get_targets"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AIHandler.get_targets" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Get a list of potential targets for the NPC to attack</p>
|
||||
<dd><p>Get a list of potential targets for the NPC to combat.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AIHandler.get_traversable_exits">
|
||||
<code class="sig-name descname">get_traversable_exits</code><span class="sig-paren">(</span><em class="sig-param"><span class="n">exclude_destination</span><span class="o">=</span><span class="default_value">None</span></em><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AIHandler.get_traversable_exits"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AIHandler.get_traversable_exits" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
<dd><p>Get a list of exits that the NPC can traverse. Optionally exclude a destination.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><p><strong>exclude_destination</strong> (<em>Object</em><em>, </em><em>optional</em>) – Exclude exits with this destination.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AIHandler.random_probability">
|
||||
<code class="sig-name descname">random_probability</code><span class="sig-paren">(</span><em class="sig-param"><span class="n">probabilities</span></em><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AIHandler.random_probability"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AIHandler.random_probability" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Given a dictionary of probabilities, return the key of the chosen probability.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><p><strong>probabilities</strong> (<em>dict</em>) – A dictionary of probabilities, where the key is the action and the
|
||||
value is the probability of that action.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
|
|
@ -191,85 +213,13 @@ reference to the AIHandler and a few basic <strong>ai_*</strong> methods for bas
|
|||
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></p>
|
||||
<p>Mixin for adding AI to an Object. This is a simple state machine. Just add more <strong>ai_*</strong> methods
|
||||
to the object to make it do more things.</p>
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AIMixin.combat_probabilities">
|
||||
<code class="sig-name descname">combat_probabilities</code><em class="property"> = {'attack': 0.9, 'flee': 0.0, 'hold': 0.1, 'item': 0.0, 'stunt': 0.0}</em><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AIMixin.combat_probabilities" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<p>In the tutorial, the handler is added directly to the Mob class, to avoid going into the details
|
||||
of multiple inheritance. In a real game, you would probably want to use a mixin like this.</p>
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AIMixin.ai">
|
||||
<code class="sig-name descname">ai</code><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AIMixin.ai"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_idle">
|
||||
<code class="sig-name descname">ai_idle</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AIMixin.ai_idle"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_idle" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_attack">
|
||||
<code class="sig-name descname">ai_attack</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AIMixin.ai_attack"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_attack" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_patrol">
|
||||
<code class="sig-name descname">ai_patrol</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AIMixin.ai_patrol"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_patrol" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_flee">
|
||||
<code class="sig-name descname">ai_flee</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AIMixin.ai_flee"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_flee" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py class">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.IdleMobMixin">
|
||||
<em class="property">class </em><code class="sig-prename descclassname">evennia.contrib.tutorials.evadventure.ai.</code><code class="sig-name descname">IdleMobMixin</code><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#IdleMobMixin"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.IdleMobMixin" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Bases: <a class="reference internal" href="#evennia.contrib.tutorials.evadventure.ai.AIMixin" title="evennia.contrib.tutorials.evadventure.ai.AIMixin"><code class="xref py py-class docutils literal notranslate"><span class="pre">evennia.contrib.tutorials.evadventure.ai.AIMixin</span></code></a></p>
|
||||
<p>A simple mob that understands AI commands, but does nothing.</p>
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.IdleMobMixin.ai_idle">
|
||||
<code class="sig-name descname">ai_idle</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#IdleMobMixin.ai_idle"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.IdleMobMixin.ai_idle" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py class">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin">
|
||||
<em class="property">class </em><code class="sig-prename descclassname">evennia.contrib.tutorials.evadventure.ai.</code><code class="sig-name descname">AggressiveMobMixin</code><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AggressiveMobMixin"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Bases: <a class="reference internal" href="#evennia.contrib.tutorials.evadventure.ai.AIMixin" title="evennia.contrib.tutorials.evadventure.ai.AIMixin"><code class="xref py py-class docutils literal notranslate"><span class="pre">evennia.contrib.tutorials.evadventure.ai.AIMixin</span></code></a></p>
|
||||
<p>A simple aggressive mob that can roam, attack and flee.</p>
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.combat_probabilities">
|
||||
<code class="sig-name descname">combat_probabilities</code><em class="property"> = {'attack': 0.85, 'flee': 0.05, 'hold': 0.0, 'item': 0.0, 'stunt': 0.05}</em><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.combat_probabilities" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_idle">
|
||||
<code class="sig-name descname">ai_idle</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AggressiveMobMixin.ai_idle"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_idle" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Do nothing, but switch to attack state if a target is found.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_attack">
|
||||
<code class="sig-name descname">ai_attack</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AggressiveMobMixin.ai_attack"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_attack" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Manage the attack/combat state of the mob.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_patrol">
|
||||
<code class="sig-name descname">ai_patrol</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AggressiveMobMixin.ai_patrol"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_patrol" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Patrol, moving randomly to a new room. If a target is found, switch to attack state.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_flee">
|
||||
<code class="sig-name descname">ai_flee</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/ai.html#AggressiveMobMixin.ai_flee"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_flee" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Flee from the current room, avoiding going back to the room from which we came. If no exits
|
||||
are found, switch to patrol state.</p>
|
||||
</dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -480,7 +480,7 @@ turn of combat, performing everyone’s actions in random order.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.combat_turnbased.CmdTurnAttack.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['hit', 'turnbased combat']</em><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.combat_turnbased.CmdTurnAttack.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['turnbased combat', 'hit']</em><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.combat_turnbased.CmdTurnAttack.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -526,7 +526,7 @@ set in self.parse())</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.combat_turnbased.CmdTurnAttack.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'hit turnbased combat', 'category': 'general', 'key': 'attack', 'no_prefix': ' hit turnbased combat', 'tags': '', 'text': '\n Start or join combat.\n\n Usage:\n attack [<target>]\n\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.combat_turnbased.CmdTurnAttack.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'turnbased combat hit', 'category': 'general', 'key': 'attack', 'no_prefix': ' turnbased combat hit', 'tags': '', 'text': '\n Start or join combat.\n\n Usage:\n attack [<target>]\n\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.combat_turnbased.CmdTurnAttack.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -307,7 +307,7 @@ unwear <item></p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.commands.CmdRemove.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['unwear', 'unwield']</em><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.commands.CmdRemove.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['unwield', 'unwear']</em><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.commands.CmdRemove.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
|
|
@ -331,7 +331,7 @@ set in self.parse())</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.commands.CmdRemove.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'unwear unwield', 'category': 'general', 'key': 'remove', 'no_prefix': ' unwear unwield', 'tags': '', 'text': '\n Remove a remove a weapon/shield, armor or helmet.\n\n Usage:\n remove <item>\n unwield <item>\n unwear <item>\n\n To remove an item from the backpack, use |wdrop|n instead.\n\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.commands.CmdRemove.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'unwield unwear', 'category': 'general', 'key': 'remove', 'no_prefix': ' unwield unwear', 'tags': '', 'text': '\n Remove a remove a weapon/shield, armor or helmet.\n\n Usage:\n remove <item>\n unwield <item>\n unwear <item>\n\n To remove an item from the backpack, use |wdrop|n instead.\n\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.commands.CmdRemove.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -480,12 +480,41 @@ based on nodes named <strong>node_start_*</strong> are available in the node tre
|
|||
<dl class="py class">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob">
|
||||
<em class="property">class </em><code class="sig-prename descclassname">evennia.contrib.tutorials.evadventure.npcs.</code><code class="sig-name descname">EvAdventureMob</code><span class="sig-paren">(</span><em class="sig-param"><span class="o">*</span><span class="n">args</span></em>, <em class="sig-param"><span class="o">**</span><span class="n">kwargs</span></em><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/npcs.html#EvAdventureMob"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Bases: <a class="reference internal" href="evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin" title="evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin"><code class="xref py py-class docutils literal notranslate"><span class="pre">evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin</span></code></a>, <a class="reference internal" href="#evennia.contrib.tutorials.evadventure.npcs.EvAdventureNPC" title="evennia.contrib.tutorials.evadventure.npcs.EvAdventureNPC"><code class="xref py py-class docutils literal notranslate"><span class="pre">evennia.contrib.tutorials.evadventure.npcs.EvAdventureNPC</span></code></a></p>
|
||||
<dd><p>Bases: <a class="reference internal" href="#evennia.contrib.tutorials.evadventure.npcs.EvAdventureNPC" title="evennia.contrib.tutorials.evadventure.npcs.EvAdventureNPC"><code class="xref py py-class docutils literal notranslate"><span class="pre">evennia.contrib.tutorials.evadventure.npcs.EvAdventureNPC</span></code></a></p>
|
||||
<p>Mob (mobile) NPC; this is usually an enemy.</p>
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.loot_chance">
|
||||
<code class="sig-name descname">loot_chance</code><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.loot_chance" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>AttributeProperty.</p>
|
||||
<dt id="evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.combat_probabilities">
|
||||
<code class="sig-name descname">combat_probabilities</code><em class="property"> = {'attack': 0.85, 'flee': 0.05, 'hold': 0.0, 'item': 0.0, 'stunt': 0.05}</em><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.combat_probabilities" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai">
|
||||
<code class="sig-name descname">ai</code><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/npcs.html#EvAdventureMob.ai"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_idle">
|
||||
<code class="sig-name descname">ai_idle</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/npcs.html#EvAdventureMob.ai_idle"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_idle" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Do nothing.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_combat">
|
||||
<code class="sig-name descname">ai_combat</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/npcs.html#EvAdventureMob.ai_combat"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_combat" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Manage the combat/combat state of the mob.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_roam">
|
||||
<code class="sig-name descname">ai_roam</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/npcs.html#EvAdventureMob.ai_roam"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_roam" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>roam, moving randomly to a new room. If a target is found, switch to combat state.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_flee">
|
||||
<code class="sig-name descname">ai_flee</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/npcs.html#EvAdventureMob.ai_flee"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_flee" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Flee from the current room, avoiding going back to the room from which we came. If no exits
|
||||
are found, switch to roam state.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
|
|
@ -494,12 +523,6 @@ based on nodes named <strong>node_start_*</strong> are available in the node tre
|
|||
<dd><p>Mobs die right away when defeated, no death-table rolls.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py method">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.at_do_loot">
|
||||
<code class="sig-name descname">at_do_loot</code><span class="sig-paren">(</span><em class="sig-param"><span class="n">looted</span></em><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/npcs.html#EvAdventureMob.at_do_loot"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.at_do_loot" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Called when mob gets to loot a PC.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py exception">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.DoesNotExist">
|
||||
<em class="property">exception </em><code class="sig-name descname">DoesNotExist</code><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.DoesNotExist" title="Permalink to this definition">¶</a></dt>
|
||||
|
|
|
|||
|
|
@ -136,6 +136,18 @@ also get information about if the item is currently worn/wielded.</p></li>
|
|||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py function">
|
||||
<dt id="evennia.contrib.tutorials.evadventure.utils.random_probability">
|
||||
<code class="sig-prename descclassname">evennia.contrib.tutorials.evadventure.utils.</code><code class="sig-name descname">random_probability</code><span class="sig-paren">(</span><em class="sig-param"><span class="n">self</span></em>, <em class="sig-param"><span class="n">probabilities</span></em><span class="sig-paren">)</span><a class="reference internal" href="../_modules/evennia/contrib/tutorials/evadventure/utils.html#random_probability"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#evennia.contrib.tutorials.evadventure.utils.random_probability" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Given a dictionary of probabilities, return the key of the chosen probability.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><p><strong>probabilities</strong> (<em>dict</em>) – A dictionary of probabilities, where the key is the action and the
|
||||
value is the probability of that action.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ check if the lid is open or closed.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.red_button.red_button.CmdSmashGlass.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['smash lid', 'break lid', 'smash']</em><a class="headerlink" href="#evennia.contrib.tutorials.red_button.red_button.CmdSmashGlass.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['break lid', 'smash lid', 'smash']</em><a class="headerlink" href="#evennia.contrib.tutorials.red_button.red_button.CmdSmashGlass.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -293,7 +293,7 @@ break.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.red_button.red_button.CmdSmashGlass.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'smash lid break lid smash', 'category': 'general', 'key': 'smash glass', 'no_prefix': ' smash lid break lid smash', 'tags': '', 'text': '\n Smash the protective glass.\n\n Usage:\n smash glass\n\n Try to smash the glass of the button.\n\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.red_button.red_button.CmdSmashGlass.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'break lid smash lid smash', 'category': 'general', 'key': 'smash glass', 'no_prefix': ' break lid smash lid smash', 'tags': '', 'text': '\n Smash the protective glass.\n\n Usage:\n smash glass\n\n Try to smash the glass of the button.\n\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.red_button.red_button.CmdSmashGlass.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
@ -520,7 +520,7 @@ be mutually exclusive.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.red_button.red_button.CmdBlindLook.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['ex', 'l', 'examine', 'feel', 'get', 'listen']</em><a class="headerlink" href="#evennia.contrib.tutorials.red_button.red_button.CmdBlindLook.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['get', 'feel', 'examine', 'listen', 'ex', 'l']</em><a class="headerlink" href="#evennia.contrib.tutorials.red_button.red_button.CmdBlindLook.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -546,7 +546,7 @@ be mutually exclusive.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.red_button.red_button.CmdBlindLook.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'ex l examine feel get listen', 'category': 'general', 'key': 'look', 'no_prefix': ' ex l examine feel get listen', 'tags': '', 'text': "\n Looking around in darkness\n\n Usage:\n look <obj>\n\n ... not that there's much to see in the dark.\n\n "}</em><a class="headerlink" href="#evennia.contrib.tutorials.red_button.red_button.CmdBlindLook.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'get feel examine listen ex l', 'category': 'general', 'key': 'look', 'no_prefix': ' get feel examine listen ex l', 'tags': '', 'text': "\n Looking around in darkness\n\n Usage:\n look <obj>\n\n ... not that there's much to see in the dark.\n\n "}</em><a class="headerlink" href="#evennia.contrib.tutorials.red_button.red_button.CmdBlindLook.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -570,7 +570,7 @@ shift green root up/down</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.tutorial_world.objects.CmdShiftRoot.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['pull', 'shiftroot', 'move', 'push']</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.objects.CmdShiftRoot.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['pull', 'shiftroot', 'push', 'move']</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.objects.CmdShiftRoot.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -606,7 +606,7 @@ yellow/green - horizontal roots</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.tutorial_world.objects.CmdShiftRoot.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'pull shiftroot move push', 'category': 'tutorialworld', 'key': 'shift', 'no_prefix': ' pull shiftroot move push', 'tags': '', 'text': '\n Shifts roots around.\n\n Usage:\n shift blue root left/right\n shift red root left/right\n shift yellow root up/down\n shift green root up/down\n\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.objects.CmdShiftRoot.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'pull shiftroot push move', 'category': 'tutorialworld', 'key': 'shift', 'no_prefix': ' pull shiftroot push move', 'tags': '', 'text': '\n Shifts roots around.\n\n Usage:\n shift blue root left/right\n shift red root left/right\n shift yellow root up/down\n shift green root up/down\n\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.objects.CmdShiftRoot.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
@ -623,7 +623,7 @@ yellow/green - horizontal roots</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.tutorial_world.objects.CmdPressButton.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['push button', 'press button', 'button']</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.objects.CmdPressButton.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['press button', 'button', 'push button']</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.objects.CmdPressButton.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -649,7 +649,7 @@ yellow/green - horizontal roots</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.tutorial_world.objects.CmdPressButton.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'push button press button button', 'category': 'tutorialworld', 'key': 'press', 'no_prefix': ' push button press button button', 'tags': '', 'text': '\n Presses a button.\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.objects.CmdPressButton.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'press button button push button', 'category': 'tutorialworld', 'key': 'press', 'no_prefix': ' press button button push button', 'tags': '', 'text': '\n Presses a button.\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.objects.CmdPressButton.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
@ -793,7 +793,7 @@ parry - forgoes your attack but will make you harder to hit on next</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.tutorial_world.objects.CmdAttack.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['fight', 'bash', 'defend', 'chop', 'pierce', 'kill', 'slash', 'parry', 'thrust', 'hit', 'stab']</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.objects.CmdAttack.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['bash', 'pierce', 'defend', 'stab', 'slash', 'chop', 'kill', 'hit', 'fight', 'parry', 'thrust']</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.objects.CmdAttack.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -819,7 +819,7 @@ parry - forgoes your attack but will make you harder to hit on next</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.tutorial_world.objects.CmdAttack.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'fight bash defend chop pierce kill slash parry thrust hit stab', 'category': 'tutorialworld', 'key': 'attack', 'no_prefix': ' fight bash defend chop pierce kill slash parry thrust hit stab', 'tags': '', 'text': '\n Attack the enemy. Commands:\n\n stab <enemy>\n slash <enemy>\n parry\n\n stab - (thrust) makes a lot of damage but is harder to hit with.\n slash - is easier to land, but does not make as much damage.\n parry - forgoes your attack but will make you harder to hit on next\n enemy attack.\n\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.objects.CmdAttack.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'bash pierce defend stab slash chop kill hit fight parry thrust', 'category': 'tutorialworld', 'key': 'attack', 'no_prefix': ' bash pierce defend stab slash chop kill hit fight parry thrust', 'tags': '', 'text': '\n Attack the enemy. Commands:\n\n stab <enemy>\n slash <enemy>\n parry\n\n stab - (thrust) makes a lot of damage but is harder to hit with.\n slash - is easier to land, but does not make as much damage.\n parry - forgoes your attack but will make you harder to hit on next\n enemy attack.\n\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.objects.CmdAttack.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -982,7 +982,7 @@ to find something.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.tutorial_world.rooms.CmdLookDark.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['l', 'fiddle', 'search', 'feel', 'feel around']</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.rooms.CmdLookDark.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['feel around', 'l', 'feel', 'search', 'fiddle']</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.rooms.CmdLookDark.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -1010,7 +1010,7 @@ random chance of eventually finding a light source.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.tutorials.tutorial_world.rooms.CmdLookDark.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'l fiddle search feel feel around', 'category': 'tutorialworld', 'key': 'look', 'no_prefix': ' l fiddle search feel feel around', 'tags': '', 'text': '\n Look around in darkness\n\n Usage:\n look\n\n Look around in the darkness, trying\n to find something.\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.rooms.CmdLookDark.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'feel around l feel search fiddle', 'category': 'tutorialworld', 'key': 'look', 'no_prefix': ' feel around l feel search fiddle', 'tags': '', 'text': '\n Look around in darkness\n\n Usage:\n look\n\n Look around in the darkness, trying\n to find something.\n '}</em><a class="headerlink" href="#evennia.contrib.tutorials.tutorial_world.rooms.CmdLookDark.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ git evennia pull - Pull the latest evennia code.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.utils.git_integration.git_integration.CmdGitEvennia.directory">
|
||||
<code class="sig-name descname">directory</code><em class="property"> = '/tmp/tmp8ivem2a_/05ab1c2a9b064f8ca98761a000e56f035ff2ae22/evennia'</em><a class="headerlink" href="#evennia.contrib.utils.git_integration.git_integration.CmdGitEvennia.directory" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">directory</code><em class="property"> = '/tmp/tmpv8a4t67i/8085aa30db7dde4b4f0f92d6428745441344b73d/evennia'</em><a class="headerlink" href="#evennia.contrib.utils.git_integration.git_integration.CmdGitEvennia.directory" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -283,7 +283,7 @@ git pull - Pull the latest code from your current branch.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.contrib.utils.git_integration.git_integration.CmdGit.directory">
|
||||
<code class="sig-name descname">directory</code><em class="property"> = '/tmp/tmp8ivem2a_/05ab1c2a9b064f8ca98761a000e56f035ff2ae22/evennia/game_template'</em><a class="headerlink" href="#evennia.contrib.utils.git_integration.git_integration.CmdGit.directory" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">directory</code><em class="property"> = '/tmp/tmpv8a4t67i/8085aa30db7dde4b4f0f92d6428745441344b73d/evennia/game_template'</em><a class="headerlink" href="#evennia.contrib.utils.git_integration.git_integration.CmdGit.directory" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
|
|||
|
|
@ -350,7 +350,7 @@ indentation.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.utils.eveditor.CmdEditorGroup.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = [':h', ':q!', ':echo', ':I', ':f', ':fd', ':dd', ':x', ':>', ':uu', ':A', ':r', ':S', ':', ':s', ':<', ':fi', ':wq', ':DD', ':u', ':!', ':i', ':=', ':j', ':dw', ':UU', ':w', ':::', ':p', '::', ':q', ':y']</em><a class="headerlink" href="#evennia.utils.eveditor.CmdEditorGroup.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = [':!', ':UU', ':I', ':S', ':p', ':>', ':<', ':y', ':DD', ':=', ':::', ':f', ':w', ':s', ':dw', ':wq', ':u', ':echo', ':fi', ':dd', '::', ':', ':uu', ':A', ':h', ':q', ':i', ':x', ':fd', ':r', ':q!', ':j']</em><a class="headerlink" href="#evennia.utils.eveditor.CmdEditorGroup.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -378,7 +378,7 @@ efficient presentation.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.utils.eveditor.CmdEditorGroup.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': ':h :q! :echo :I :f :fd :dd :x :> :uu :A :r :S : :s :< :fi :wq :DD :u :! :i := :j :dw :UU :w ::: :p :: :q :y', 'category': 'general', 'key': ':editor_command_group', 'no_prefix': ' :h :q! :echo :I :f :fd :dd :x :> :uu :A :r :S : :s :< :fi :wq :DD :u :! :i := :j :dw :UU :w ::: :p :: :q :y', 'tags': '', 'text': '\n Commands for the editor\n '}</em><a class="headerlink" href="#evennia.utils.eveditor.CmdEditorGroup.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': ':! :UU :I :S :p :> :< :y :DD := ::: :f :w :s :dw :wq :u :echo :fi :dd :: : :uu :A :h :q :i :x :fd :r :q! :j', 'category': 'general', 'key': ':editor_command_group', 'no_prefix': ' :! :UU :I :S :p :> :< :y :DD := ::: :f :w :s :dw :wq :u :echo :fi :dd :: : :uu :A :h :q :i :x :fd :r :q! :j', 'tags': '', 'text': '\n Commands for the editor\n '}</em><a class="headerlink" href="#evennia.utils.eveditor.CmdEditorGroup.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -953,7 +953,7 @@ single question.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.utils.evmenu.CmdYesNoQuestion.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['yes', 'a', 'y', 'abort', 'no', 'n', '__nomatch_command']</em><a class="headerlink" href="#evennia.utils.evmenu.CmdYesNoQuestion.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['n', 'y', 'a', '__nomatch_command', 'yes', 'no', 'abort']</em><a class="headerlink" href="#evennia.utils.evmenu.CmdYesNoQuestion.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -979,7 +979,7 @@ single question.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.utils.evmenu.CmdYesNoQuestion.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'yes a y abort no n __nomatch_command', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' yes a y abort no n __nomatch_command', 'tags': '', 'text': '\n Handle a prompt for yes or no. Press [return] for the default choice.\n\n '}</em><a class="headerlink" href="#evennia.utils.evmenu.CmdYesNoQuestion.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'n y a __nomatch_command yes no abort', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' n y a __nomatch_command yes no abort', 'tags': '', 'text': '\n Handle a prompt for yes or no. Press [return] for the default choice.\n\n '}</em><a class="headerlink" href="#evennia.utils.evmenu.CmdYesNoQuestion.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ the <strong>caller.msg()</strong> construct every time the page is updated.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.utils.evmore.CmdMore.aliases">
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['top', 'p', 't', 'end', 'a', 'next', 'abort', 'previous', 'e', 'q', 'n', 'quit']</em><a class="headerlink" href="#evennia.utils.evmore.CmdMore.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">aliases</code><em class="property"> = ['e', 'n', 'a', 'next', 'top', 'previous', 'end', 'quit', 't', 'p', 'q', 'abort']</em><a class="headerlink" href="#evennia.utils.evmore.CmdMore.aliases" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
|
@ -177,7 +177,7 @@ the <strong>caller.msg()</strong> construct every time the page is updated.</p>
|
|||
|
||||
<dl class="py attribute">
|
||||
<dt id="evennia.utils.evmore.CmdMore.search_index_entry">
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'top p t end a next abort previous e q n quit', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' top p t end a next abort previous e q n quit', 'tags': '', 'text': '\n Manipulate the text paging. Catch no-input with aliases.\n '}</em><a class="headerlink" href="#evennia.utils.evmore.CmdMore.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'e n a next top previous end quit t p q abort', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' e n a next top previous end quit t p q abort', 'tags': '', 'text': '\n Manipulate the text paging. Catch no-input with aliases.\n '}</em><a class="headerlink" href="#evennia.utils.evmore.CmdMore.search_index_entry" title="Permalink to this definition">¶</a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
|
|
|||
|
|
@ -810,39 +810,23 @@
|
|||
<li><a href="api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler.advantage_against">advantage_against (evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler attribute)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.combat_turnbased.html#evennia.contrib.tutorials.evadventure.combat_turnbased.EvAdventureTurnbasedCombatHandler.advantage_matrix">advantage_matrix (evennia.contrib.tutorials.evadventure.combat_turnbased.EvAdventureTurnbasedCombatHandler attribute)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin">AggressiveMobMixin (class in evennia.contrib.tutorials.evadventure.ai)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai">ai (evennia.contrib.tutorials.evadventure.ai.AIMixin attribute)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_attack">ai_attack() (evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin method)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_attack">(evennia.contrib.tutorials.evadventure.ai.AIMixin method)</a>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai">(evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob attribute)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_flee">ai_flee() (evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin method)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_flee">(evennia.contrib.tutorials.evadventure.ai.AIMixin method)</a>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_combat">ai_combat() (evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_idle">ai_idle() (evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin method)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_idle">(evennia.contrib.tutorials.evadventure.ai.AIMixin method)</a>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_flee">ai_flee() (evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob method)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.IdleMobMixin.ai_idle">(evennia.contrib.tutorials.evadventure.ai.IdleMobMixin method)</a>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_idle">ai_idle() (evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureNPC.ai_next_action">ai_next_action() (evennia.contrib.tutorials.evadventure.npcs.EvAdventureNPC method)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.ai_patrol">ai_patrol() (evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin method)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIMixin.ai_patrol">(evennia.contrib.tutorials.evadventure.ai.AIMixin method)</a>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_roam">ai_roam() (evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIHandler">AIHandler (class in evennia.contrib.tutorials.evadventure.ai)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIMixin">AIMixin (class in evennia.contrib.tutorials.evadventure.ai)</a>
|
||||
|
|
@ -1623,14 +1607,14 @@
|
|||
</li>
|
||||
<li><a href="api/evennia.locks.lockhandler.html#evennia.locks.lockhandler.LockHandler.append">append() (evennia.locks.lockhandler.LockHandler method)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="api/evennia.server.portal.telnet.html#evennia.server.portal.telnet.TelnetProtocol.applicationDataReceived">applicationDataReceived() (evennia.server.portal.telnet.TelnetProtocol method)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.chargen.html#evennia.contrib.tutorials.evadventure.chargen.TemporaryCharacterSheet.apply">apply() (evennia.contrib.tutorials.evadventure.chargen.TemporaryCharacterSheet method)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.game_systems.turnbattle.tb_basic.html#evennia.contrib.game_systems.turnbattle.tb_basic.BasicCombatRules.apply_damage">apply_damage() (evennia.contrib.game_systems.turnbattle.tb_basic.BasicCombatRules method)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="api/evennia.contrib.game_systems.turnbattle.tb_items.html#evennia.contrib.game_systems.turnbattle.tb_items.TBItemsCharacter.apply_turn_conditions">apply_turn_conditions() (evennia.contrib.game_systems.turnbattle.tb_items.TBItemsCharacter method)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.game_systems.turnbattle.tb_range.html#evennia.contrib.game_systems.turnbattle.tb_range.RangedCombatRules.approach">approach() (evennia.contrib.game_systems.turnbattle.tb_range.RangedCombatRules method)</a>
|
||||
|
|
@ -2022,11 +2006,7 @@
|
|||
<li><a href="api/evennia.contrib.rpg.buffs.buff.html#evennia.contrib.rpg.buffs.buff.BaseBuff.at_dispel">at_dispel() (evennia.contrib.rpg.buffs.buff.BaseBuff method)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.characters.html#evennia.contrib.tutorials.evadventure.characters.LivingMixin.at_do_loot">at_do_loot() (evennia.contrib.tutorials.evadventure.characters.LivingMixin method)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.at_do_loot">(evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="api/evennia.contrib.game_systems.turnbattle.tb_equip.html#evennia.contrib.game_systems.turnbattle.tb_equip.TBEArmor.at_drop">at_drop() (evennia.contrib.game_systems.turnbattle.tb_equip.TBEArmor method)</a>
|
||||
|
||||
<ul>
|
||||
|
|
@ -2838,6 +2818,10 @@
|
|||
<li><a href="api/evennia.typeclasses.attributes.html#evennia.typeclasses.attributes.Attribute.DoesNotExist">Attribute.DoesNotExist</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.typeclasses.attributes.html#evennia.typeclasses.attributes.Attribute.MultipleObjectsReturned">Attribute.MultipleObjectsReturned</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIHandler.attribute_category">attribute_category (evennia.contrib.tutorials.evadventure.ai.AIHandler attribute)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIHandler.attribute_name">attribute_name (evennia.contrib.tutorials.evadventure.ai.AIHandler attribute)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.web.admin.attributes.html#evennia.web.admin.attributes.AttributeForm">AttributeForm (class in evennia.web.admin.attributes)</a>
|
||||
</li>
|
||||
|
|
@ -4511,12 +4495,8 @@
|
|||
<li><a href="api/evennia.contrib.game_systems.turnbattle.tb_range.html#evennia.contrib.game_systems.turnbattle.tb_range.CmdCombatHelp.combat_help_text">(evennia.contrib.game_systems.turnbattle.tb_range.CmdCombatHelp attribute)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin.combat_probabilities">combat_probabilities (evennia.contrib.tutorials.evadventure.ai.AggressiveMobMixin attribute)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIMixin.combat_probabilities">(evennia.contrib.tutorials.evadventure.ai.AIMixin attribute)</a>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.combat_probabilities">combat_probabilities (evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob attribute)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="api/evennia.contrib.game_systems.turnbattle.tb_basic.html#evennia.contrib.game_systems.turnbattle.tb_basic.COMBAT_RULES">COMBAT_RULES (in module evennia.contrib.game_systems.turnbattle.tb_basic)</a>
|
||||
|
||||
<ul>
|
||||
|
|
@ -12221,8 +12201,6 @@
|
|||
<li><a href="api/evennia.objects.objects.html#evennia.objects.objects.DefaultCharacter.idle_time">(evennia.objects.objects.DefaultCharacter property)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.IdleMobMixin">IdleMobMixin (class in evennia.contrib.tutorials.evadventure.ai)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.commands.cmdsethandler.html#evennia.commands.cmdsethandler.import_cmdset">import_cmdset() (in module evennia.commands.cmdsethandler)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.utils.eveditor.html#evennia.utils.eveditor.EvEditor.increase_indent">increase_indent() (evennia.utils.eveditor.EvEditor method)</a>
|
||||
|
|
@ -14592,8 +14570,6 @@
|
|||
</li>
|
||||
</ul></li>
|
||||
<li><a href="api/evennia.contrib.grid.extended_room.extended_room.html#evennia.contrib.grid.extended_room.extended_room.CmdExtendedRoomLook.look_detail">look_detail() (evennia.contrib.grid.extended_room.extended_room.CmdExtendedRoomLook method)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.loot_chance">loot_chance (evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob attribute)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.utils.ansi.html#evennia.utils.ansi.ANSIString.lower">lower() (evennia.utils.ansi.ANSIString method)</a>
|
||||
</li>
|
||||
|
|
@ -17999,7 +17975,11 @@
|
|||
</li>
|
||||
</ul></li>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.ai.html#evennia.contrib.tutorials.evadventure.ai.AIHandler.random_probability">random_probability() (evennia.contrib.tutorials.evadventure.ai.AIHandler method)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="api/evennia.contrib.tutorials.evadventure.utils.html#evennia.contrib.tutorials.evadventure.utils.random_probability">(in module evennia.contrib.tutorials.evadventure.utils)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="api/evennia.utils.utils.html#evennia.utils.utils.random_string_from_module">random_string_from_module() (in module evennia.utils.utils)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.contrib.utils.random_string_generator.random_string_generator.html#evennia.contrib.utils.random_string_generator.random_string_generator.RandomStringGenerator">RandomStringGenerator (class in evennia.contrib.utils.random_string_generator.random_string_generator)</a>
|
||||
|
|
@ -18390,10 +18370,10 @@
|
|||
<li><a href="api/evennia.utils.utils.html#evennia.utils.utils.repeat">(in module evennia.utils.utils)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="api/evennia.contrib.grid.extended_room.extended_room.html#evennia.contrib.grid.extended_room.extended_room.ExtendedRoom.repeat_broadcast_message_to_room">repeat_broadcast_message_to_room() (evennia.contrib.grid.extended_room.extended_room.ExtendedRoom method)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="api/evennia.contrib.grid.extended_room.extended_room.html#evennia.contrib.grid.extended_room.extended_room.ExtendedRoom.repeat_broadcast_message_to_room">repeat_broadcast_message_to_room() (evennia.contrib.grid.extended_room.extended_room.ExtendedRoom method)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.scripts.models.html#evennia.scripts.models.ScriptDB.repeats">repeats() (evennia.scripts.models.ScriptDB property)</a>
|
||||
</li>
|
||||
<li><a href="api/evennia.locks.lockhandler.html#evennia.locks.lockhandler.LockHandler.replace">replace() (evennia.locks.lockhandler.LockHandler method)</a>
|
||||
|
|
|
|||
Binary file not shown.
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue