Updated HTML docs.

This commit is contained in:
Evennia docbuilder action 2023-05-19 18:51:48 +00:00
parent 2c44fb26f8
commit b902667df5
144 changed files with 16138 additions and 4920 deletions

View file

@ -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: 6ac8f4ad0d0d27d757eb26c49bc83b52
config: d27787ec5e67a4834f84072bd796b13a
tags: 645f666f9bcd5a90fca523b33c5a78b7

View file

@ -62,6 +62,7 @@
<h3><a href="../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Changelog</a><ul>
<li><a class="reference internal" href="#main-branch">Main branch</a></li>
<li><a class="reference internal" href="#evennia-1-3-0">Evennia 1.3.0</a></li>
<li><a class="reference internal" href="#evennia-1-2-1">Evennia 1.2.1</a></li>
<li><a class="reference internal" href="#evennia-1-2-0">Evennia 1.2.0</a></li>
@ -169,6 +170,17 @@
<section class="tex2jax_ignore mathjax_ignore" id="changelog">
<h1>Changelog<a class="headerlink" href="#changelog" title="Permalink to this headline"></a></h1>
<section id="main-branch">
<h2>Main branch<a class="headerlink" href="#main-branch" title="Permalink to this headline"></a></h2>
<ul class="simple">
<li><p>Contrib: Container typeclass with new commands for storing and retrieving
things inside them (InspectorCaracal)</p></li>
<li><p>Fix: The <code class="docutils literal notranslate"><span class="pre">AttributeHandler.all()</span></code> now actually accepts <code class="docutils literal notranslate"><span class="pre">category=</span></code> as
keyword arg, like our docs already claimed it should (Volund)</p></li>
<li><p>Docs: New Beginner-Tutorial lessons for NPCs, Base-Combat Twitch-Combat and
Turnbased-combat (note that the Beginner tutorial is still WIP).</p></li>
</ul>
</section>
<section id="evennia-1-3-0">
<h2>Evennia 1.3.0<a class="headerlink" href="#evennia-1-3-0" title="Permalink to this headline"></a></h2>
<p>Apr 29, 2023</p>

View file

@ -162,6 +162,7 @@ make your game, also if you never coded before.</p>
<div class="toctree-wrapper compound">
<ul>
<li class="toctree-l1"><a class="reference internal" href="Changelog.html">Changelog</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Changelog.html#main-branch">Main branch</a></li>
<li class="toctree-l2"><a class="reference internal" href="Changelog.html#evennia-1-3-0">Evennia 1.3.0</a></li>
<li class="toctree-l2"><a class="reference internal" href="Changelog.html#evennia-1-2-1">Evennia 1.2.1</a></li>
<li class="toctree-l2"><a class="reference internal" href="Changelog.html#evennia-1-2-0">Evennia 1.2.0</a></li>

View file

@ -111,31 +111,26 @@
<section class="tex2jax_ignore mathjax_ignore" id="soft-code">
<h1>Soft Code<a class="headerlink" href="#soft-code" title="Permalink to this headline"></a></h1>
<p>Softcode is a very simple programming language that was created for in-game development on TinyMUD derivatives such as MUX, PennMUSH, TinyMUSH, and RhostMUSH. The idea is that by providing a stripped down, minimalistic language for in-game use, you can allow quick and easy building and game development to happen without having to learn C/C++. There is an added benefit of not having to have to hand out shell access to all developers, and permissions can be used to alleviate many security problems.</p>
<p>Writing and installing softcode is done through a MUD client. Thus it is not a formatted language.
Each softcode function is a single line of varying size. Some functions can be a half of a page long
or more which is obviously not very readable nor (easily) maintainable over time.</p>
<p>Softcode is a simple programming language that was created for in-game development on TinyMUD derivatives such as MUX, PennMUSH, TinyMUSH, and RhostMUSH. The idea was that by providing a stripped down, minimalistic language for in-game use, you could allow quick and easy building and game development to happen without builders having to learn the hardcode language for those servers (C/C++). There is an added benefit of not having to have to hand out shell access to all developers. Permissions in softcode can be used to alleviate many security problems.</p>
<p>Writing and installing softcode is done through a MUD client. Thus it is not a formatted language. Each softcode function is a single line of varying size. Some functions can be a half of a page long or more which is obviously not very readable nor (easily) maintainable over time.</p>
<section id="examples-of-softcode">
<h2>Examples of Softcode<a class="headerlink" href="#examples-of-softcode" title="Permalink to this headline"></a></h2>
<p>Here is a simple Hello World! command:</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="w"> </span>@set<span class="w"> </span><span class="nv">me</span><span class="o">=</span>HELLO_WORLD.C:<span class="nv">$hello</span>:@pemit<span class="w"> </span>%#<span class="o">=</span>Hello<span class="w"> </span>World!
</pre></div>
</div>
<p>Pasting this into a MUX/MUSH and typing hello will theoretically yield Hello World!, assuming
certain flags are not set on your account object.</p>
<p>Setting attributes is done via <code class="docutils literal notranslate"><span class="pre">&#64;set</span></code>. Softcode also allows the use of the ampersand (<code class="docutils literal notranslate"><span class="pre">&amp;</span></code>) symbol.
This shorter version looks like this:</p>
<p>Pasting this into a MUD client, sending it to a MUX/MUSH server and typing hello will theoretically yield Hello World!, assuming certain flags are not set on your account object.</p>
<p>Setting attributes in Softcode is done via <code class="docutils literal notranslate"><span class="pre">&#64;set</span></code>. Softcode also allows the use of the ampersand (<code class="docutils literal notranslate"><span class="pre">&amp;</span></code>) symbol. This shorter version looks like this:</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="w"> </span><span class="p">&amp;</span>HELLO_WORLD.C<span class="w"> </span><span class="nv">me</span><span class="o">=</span><span class="nv">$hello</span>:@pemit<span class="w"> </span>%#<span class="o">=</span>Hello<span class="w"> </span>World!
</pre></div>
</div>
<p>Perhaps I want to break the Hello World into an attribute which is retrieved when emitting:</p>
<p>We could also read the text from an attribute which is retrieved when emitting:</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="w"> </span><span class="p">&amp;</span>HELLO_VALUE.D<span class="w"> </span><span class="nv">me</span><span class="o">=</span>Hello<span class="w"> </span>World
<span class="w"> </span><span class="p">&amp;</span>HELLO_WORLD.C<span class="w"> </span><span class="nv">me</span><span class="o">=</span><span class="nv">$hello</span>:@pemit<span class="w"> </span>%#<span class="o">=[</span>v<span class="o">(</span>HELLO_VALUE.D<span class="o">)]</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">v()</span></code> function returns the <code class="docutils literal notranslate"><span class="pre">HELLO_VALUE.D</span></code> attribute on the object that the command resides
(<code class="docutils literal notranslate"><span class="pre">me</span></code>, which is yourself in this case). This should yield the same output as the first example.</p>
<p>If you are still curious about how Softcode works, take a look at some external resources:</p>
<p>The <code class="docutils literal notranslate"><span class="pre">v()</span></code> function returns the <code class="docutils literal notranslate"><span class="pre">HELLO_VALUE.D</span></code> attribute on the object that the command resides (<code class="docutils literal notranslate"><span class="pre">me</span></code>, which is yourself in this case). This should yield the same output as the first example.</p>
<p>If you are curious about how MUSH/MUX Softcode works, take a look at some external resources:</p>
<ul class="simple">
<li><p><a class="reference external" href="https://wiki.tinymux.org/index.php/Softcode">https://wiki.tinymux.org/index.php/Softcode</a></p></li>
<li><p><a class="reference external" href="https://www.duh.com/discordia/mushman/man2x1">https://www.duh.com/discordia/mushman/man2x1</a></p></li>
@ -144,25 +139,24 @@ This shorter version looks like this:</p>
<section id="problems-with-softcode">
<h2>Problems with Softcode<a class="headerlink" href="#problems-with-softcode" title="Permalink to this headline"></a></h2>
<p>Softcode is excellent at what it was intended for: <em>simple things</em>. It is a great tool for making an interactive object, a room with ambiance, simple global commands, simple economies and coded systems. However, once you start to try to write something like a complex combat system or a higher end economy, youre likely to find yourself buried under a mountain of functions that span multiple objects across your entire code.</p>
<p>Not to mention, softcode is not an inherently fast language. It is not compiled, it is parsed with each calling of a function. While MUX and MUSH parsers have jumped light years ahead of where they once were they can still stutter under the weight of more complex systems if not designed properly.</p>
<p>Not to mention, softcode is not an inherently fast language. It is not compiled, it is parsed with each calling of a function. While MUX and MUSH parsers have jumped light years ahead of where they once were, they can still stutter under the weight of more complex systems if those are not designed properly.</p>
<p>Also, Softcode is not a standardized language. Different servers each have their own slight variations. Code tools and resources are also limited to the documentation from those servers.</p>
</section>
<section id="changing-times">
<h2>Changing Times<a class="headerlink" href="#changing-times" title="Permalink to this headline"></a></h2>
<p>Now that starting text-based games is easy and an option for even the most technically inarticulate, new projects are a dime a dozen. People are starting new MUDs every day with varying levels of commitment and ability. Because of this shift from fewer, larger, well-staffed games to a bunch of small, one or two developer games, some of the benefit of softcode fades.</p>
<p>Softcode is great in that it allows a mid to large sized staff all work on the same game without stepping on one anothers toes. As mentioned before, shell access is not necessary to develop a MUX or a MUSH. However, now that we are seeing a lot more small, one or two-man shops, the issue of shell access and stepping on each others toes is a lot less.</p>
<p>Now that starting text-based games is easy and an option for even the most technically inarticulate, new projects are a dime a dozen. People are starting new MUDs every day with varying levels of commitment and ability. Because of this shift from fewer, larger, well-staffed games to a bunch of small, one or two developer games, the benefit of softcode fades.</p>
<p>Softcode is great in that it allows a mid to large sized staff all work on the same game without stepping on one anothers toes without shell access. However, the rise of modern code collaboration tools (such as private github/gitlab repos) has made it trivial to collaborate on code.</p>
</section>
<section id="our-solution">
<h2>Our Solution<a class="headerlink" href="#our-solution" title="Permalink to this headline"></a></h2>
<p>Evennia shuns in-game softcode for on-disk Python modules. Python is a popular, mature and
professional programming language. You code it using the conveniences of modern text editors.
Evennia developers have access to the entire library of Python modules out there in the wild - not
to mention the vast online help resources available. Python code is not bound to one-line functions
on objects but complex systems may be organized neatly into real source code modules, sub-modules, or even broken out into entire Python packages as desired.</p>
<p>So what is <em>not</em> included in Evennia is a MUX/MOO-like online player-coding system. Advanced coding in Evennia is primarily intended to be done outside the game, in full-fledged Python modules. Advanced building is best handled by extending Evennias command system with your own sophisticated building commands. We feel that with a small development team you are better off using a professional source-control system (svn, git, bazaar, mercurial etc) anyway.</p>
<p>Evennia shuns in-game softcode for on-disk Python modules. Python is a popular, mature and professional programming language. Evennia developers have access to the entire library of Python modules out there in the wild - not to mention the vast online help resources available. Python code is not bound to one-line functions on objects; complex systems may be organized neatly into real source code modules, sub-modules, or even broken out into entire Python packages as desired.</p>
<p>So what is <em>not</em> included in Evennia is a MUX/MOO-like online player-coding system (aka Softcode). Advanced coding in Evennia is primarily intended to be done outside the game, in full-fledged Python modules (what MUSH would call hardcode). Advanced building is best handled by extending Evennias command system with your own sophisticated building commands.</p>
<p>In Evennia you develop your MU like you would any piece of modern software - using your favorite code editor/IDE and online code sharing tools.</p>
</section>
<section id="your-solution">
<h2>Your Solution<a class="headerlink" href="#your-solution" title="Permalink to this headline"></a></h2>
<p>Adding advanced and flexible building commands to your game is easy and will probably be enough to satisfy most creative builders. However, if you really, <em>really</em> want to offer online coding, there is of course nothing stopping you from adding that to Evennia, no matter our recommendations. You could even re-implement MUX softcode in Python should you be very ambitious. The <a class="reference internal" href="../Contribs/Contrib-Ingame-Python.html"><span class="doc std std-doc">in-game-python</span></a> is an optional pseudo-softcode plugin aimed at developers wanting to script their game from inside it.</p>
<p>Adding advanced and flexible building commands to your game is easy and will probably be enough to satisfy most creative builders. However, if you really, <em>really</em> want to offer online coding, there is of course nothing stopping you from adding that to Evennia, no matter our recommendations. You could even re-implement MUX softcode in Python should you be very ambitious.</p>
<p>In default Evennia, the <span class="xref myst">Funcparser</span> system allows for simple remapping of text on-demand without becomeing a full softcode language. The <span class="xref myst">contribs</span> has several tools and utililities to start from when adding more complex in-game building.</p>
</section>
</section>

View file

@ -196,6 +196,7 @@
<li class="toctree-l2"><a class="reference internal" href="Models.html#overview-of-database-tables">Overview of database tables</a></li>
<li class="toctree-l2"><a class="reference internal" href="Models.html#adding-a-new-database-table">Adding a new database table</a></li>
<li class="toctree-l2"><a class="reference internal" href="Models.html#defining-your-models">Defining your models</a></li>
<li class="toctree-l2"><a class="reference internal" href="Models.html#referencing-existing-models-and-typeclasses">Referencing existing models and typeclasses</a></li>
<li class="toctree-l2"><a class="reference internal" href="Models.html#creating-a-new-model-instance">Creating a new model instance</a></li>
<li class="toctree-l2"><a class="reference internal" href="Models.html#using-the-sharedmemorymodel-parent">Using the <code class="docutils literal notranslate"><span class="pre">SharedMemoryModel</span></code> parent</a></li>
<li class="toctree-l2"><a class="reference internal" href="Models.html#searching-for-your-models">Searching for your models</a></li>

View file

@ -65,6 +65,7 @@
<li><a class="reference internal" href="#overview-of-database-tables">Overview of database tables</a></li>
<li><a class="reference internal" href="#adding-a-new-database-table">Adding a new database table</a></li>
<li><a class="reference internal" href="#defining-your-models">Defining your models</a></li>
<li><a class="reference internal" href="#referencing-existing-models-and-typeclasses">Referencing existing models and typeclasses</a></li>
<li><a class="reference internal" href="#creating-a-new-model-instance">Creating a new model instance</a></li>
<li><a class="reference internal" href="#using-the-sharedmemorymodel-parent">Using the <code class="docutils literal notranslate"><span class="pre">SharedMemoryModel</span></code> parent</a></li>
<li><a class="reference internal" href="#searching-for-your-models">Searching for your models</a></li>
@ -112,13 +113,8 @@
<section class="tex2jax_ignore mathjax_ignore" id="new-models">
<h1>New Models<a class="headerlink" href="#new-models" title="Permalink to this headline"></a></h1>
<p><em>Note: This is considered an advanced topic.</em></p>
<p>Evennia offers many convenient ways to store object data, such as via Attributes or Scripts. This is
sufficient for most use cases. But if you aim to build a large stand-alone system, trying to squeeze
your storage requirements into those may be more complex than you bargain for. Examples may be to
store guild data for guild members to be able to change, tracking the flow of money across a game-
wide economic system or implement other custom game systems that requires the storage of custom data
in a quickly accessible way. Whereas <a class="reference internal" href="../Components/Tags.html"><span class="doc std std-doc">Tags</span></a> or <a class="reference internal" href="../Components/Scripts.html"><span class="doc std std-doc">Scripts</span></a> can handle many situations,
sometimes things may be easier to handle by adding your own database model.</p>
<p>Evennia offers many convenient ways to store object data, such as via Attributes or Scripts. This is sufficient for most use cases. But if you aim to build a large stand-alone system, trying to squeeze your storage requirements into those may be more complex than you bargain for. Examples may be to store guild data for guild members to be able to change, tracking the flow of money across a game-wide economic system or implement other custom game systems that requires the storage of custom data in a quickly accessible way.</p>
<p>Whereas <a class="reference internal" href="../Components/Tags.html"><span class="doc std std-doc">Tags</span></a> or <a class="reference internal" href="../Components/Scripts.html"><span class="doc std std-doc">Scripts</span></a> can handle many situations, sometimes things may be easier to handle by adding your own <em>database model</em>.</p>
<section id="overview-of-database-tables">
<h2>Overview of database tables<a class="headerlink" href="#overview-of-database-tables" title="Permalink to this headline"></a></h2>
<p>SQL-type databases (which is what Evennia supports) are basically highly optimized systems for
@ -129,9 +125,7 @@ retrieving text stored in tables. A table may look like this</p>
<span class="mi">2</span> <span class="o">|</span> <span class="n">Rock</span> <span class="o">|</span> <span class="n">evennia</span><span class="o">.</span><span class="n">DefaultObject</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">...</span>
</pre></div>
</div>
<p>Each line is considerably longer in your database. Each column is referred to as a “field” and every
row is a separate object. You can check this out for yourself. If you use the default sqlite3
database, go to your game folder and run</p>
<p>Each line is considerably longer in your database. Each column is referred to as a “field” and every row is a separate object. You can check this out for yourself. If you use the default sqlite3 database, go to your game folder and run</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> evennia dbshell
</pre></div>
</div>
@ -149,59 +143,38 @@ database, go to your game folder and run</p>
sqlite&gt; .exit
</pre></div>
</div>
<p>Evennia uses <a class="reference external" href="https://docs.djangoproject.com">Django</a>, which abstracts away the database SQL
manipulation and allows you to search and manipulate your database entirely in Python. Each database
table is in Django represented by a class commonly called a <em>model</em> since it describes the look of
the table. In Evennia, Objects, Scripts, Channels etc are examples of Django models that we then
extend and build on.</p>
<p>Evennia uses <a class="reference external" href="https://docs.djangoproject.com">Django</a>, which abstracts away the database SQL manipulation and allows you to search and manipulate your database entirely in Python. Each database table is in Django represented by a class commonly called a <em>model</em> since it describes the look of the table. In Evennia, Objects, Scripts, Channels etc are examples of Django models that we then extend and build on.</p>
</section>
<section id="adding-a-new-database-table">
<h2>Adding a new database table<a class="headerlink" href="#adding-a-new-database-table" title="Permalink to this headline"></a></h2>
<p>Here is how you add your own database table/models:</p>
<ol>
<li><p>In Django lingo, we will create a new “application” - a subsystem under the main Evennia program.
For this example well call it “myapp”. Run the following (you need to have a working Evennia
running before you do this, so make sure you have run the steps in [Setup Quickstart](Getting-
Started) first):</p>
<li><p>In Django lingo, we will create a new “application” - a subsystem under the main Evennia program. For this example well call it “myapp”. Run the following (you need to have a working Evennia running before you do this, so make sure you have run the steps in [Setup Quickstart](Getting- Started) first):</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> cd mygame/world
evennia startapp myapp
</pre></div>
</div>
</li>
<li><p>A new folder <code class="docutils literal notranslate"><span class="pre">myapp</span></code> is created. “myapp” will also be the name (the “app label”) from now on. We
chose to put it in the <code class="docutils literal notranslate"><span class="pre">world/</span></code> subfolder here, but you could put it in the root of your <code class="docutils literal notranslate"><span class="pre">mygame</span></code> if
that makes more sense.</p></li>
<li><p>The <code class="docutils literal notranslate"><span class="pre">myapp</span></code> folder contains a few empty default files. What we are
interested in for now is <code class="docutils literal notranslate"><span class="pre">models.py</span></code>. In <code class="docutils literal notranslate"><span class="pre">models.py</span></code> you define your model(s). Each model will be a
table in the database. See the next section and dont continue until you have added the models you
want.</p></li>
<li><p>You now need to tell Evennia that the models of your app should be a part of your database
scheme. Add this line to your <code class="docutils literal notranslate"><span class="pre">mygame/server/conf/settings.py</span></code>file (make sure to use the path where
you put <code class="docutils literal notranslate"><span class="pre">myapp</span></code> and dont forget the comma at the end of the tuple):</p>
<li><p>A new folder <code class="docutils literal notranslate"><span class="pre">myapp</span></code> is created. “myapp” will also be the name (the “app label”) from now on. We chose to put it in the <code class="docutils literal notranslate"><span class="pre">world/</span></code> subfolder here, but you could put it in the root of your <code class="docutils literal notranslate"><span class="pre">mygame</span></code> if that makes more sense. 1. The <code class="docutils literal notranslate"><span class="pre">myapp</span></code> folder contains a few empty default files. What we are interested in for now is <code class="docutils literal notranslate"><span class="pre">models.py</span></code>. In <code class="docutils literal notranslate"><span class="pre">models.py</span></code> you define your model(s). Each model will be a table in the database. See the next section and dont continue until you have added the models you want.</p></li>
<li><p>You now need to tell Evennia that the models of your app should be a part of your database scheme. Add this line to your <code class="docutils literal notranslate"><span class="pre">mygame/server/conf/settings.py</span></code>file (make sure to use the path where you put <code class="docutils literal notranslate"><span class="pre">myapp</span></code> and dont forget the comma at the end of the tuple):</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">INSTALLED_APPS</span> <span class="o">=</span> <span class="n">INSTALLED_APPS</span> <span class="o">+</span> <span class="p">(</span><span class="s2">&quot;world.myapp&quot;</span><span class="p">,</span> <span class="p">)</span>
</pre></div>
</div>
</li>
<li><p>From <code class="docutils literal notranslate"><span class="pre">mygame/</span></code>, run</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> evennia makemigrations myapp
evennia migrate
evennia migrate myapp
</pre></div>
</div>
</li>
</ol>
<p>This will add your new database table to the database. If you have put your game under version
control (if not, <a class="reference internal" href="../Coding/Version-Control.html"><span class="doc std std-doc">you should</span></a>), dont forget to <code class="docutils literal notranslate"><span class="pre">git</span> <span class="pre">add</span> <span class="pre">myapp/*</span></code> to add all items
<p>This will add your new database table to the database. If you have put your game under version control (if not, <a class="reference internal" href="../Coding/Version-Control.html"><span class="doc std std-doc">you should</span></a>), dont forget to <code class="docutils literal notranslate"><span class="pre">git</span> <span class="pre">add</span> <span class="pre">myapp/*</span></code> to add all items
to version control.</p>
</section>
<section id="defining-your-models">
<h2>Defining your models<a class="headerlink" href="#defining-your-models" title="Permalink to this headline"></a></h2>
<p>A Django <em>model</em> is the Python representation of a database table. It can be handled like any other
Python class. It defines <em>fields</em> on itself, objects of a special type. These become the “columns”
of the database table. Finally, you create new instances of the model to add new rows to the
database.</p>
<p>We wont describe all aspects of Django models here, for that we refer to the vast <a class="reference external" href="https://docs.djangoproject.com/en/4.1/topics/db/models/">Django
documentation</a> on the subject. Here is a
(very) brief example:</p>
<p>A Django <em>model</em> is the Python representation of a database table. It can be handled like any other Python class. It defines <em>fields</em> on itself, objects of a special type. These become the “columns” of the database table. Finally, you create new instances of the model to add new rows to the database.</p>
<p>We wont describe all aspects of Django models here, for that we refer to the vast <a class="reference external" href="https://docs.djangoproject.com/en/4.1/topics/db/models/">Django documentation</a> on the subject. Here is a (very) brief example:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
<span class="k">class</span> <span class="nc">MyDataStore</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
@ -218,26 +191,44 @@ documentation</a> on the subject. Here is a
<p>We create four fields: two character fields of limited length and one text field which has no
maximum length. Finally we create a field containing the current time of us creating this object.</p>
<blockquote>
<div><p>The <code class="docutils literal notranslate"><span class="pre">db_date_created</span></code> field, with exactly this name, is <em>required</em> if you want to be able to store
instances of your custom model in an Evennia <a class="reference internal" href="../Components/Attributes.html"><span class="doc std std-doc">Attribute</span></a>. It will automatically be set
upon creation and can after that not be changed. Having this field will allow you to do e.g.
<code class="docutils literal notranslate"><span class="pre">obj.db.myinstance</span> <span class="pre">=</span> <span class="pre">mydatastore</span></code>. If you know youll never store your model instances in Attributes
the <code class="docutils literal notranslate"><span class="pre">db_date_created</span></code> field is optional.</p>
<div><p>The <code class="docutils literal notranslate"><span class="pre">db_date_created</span></code> field, with exactly this name, is <em>required</em> if you want to be able to store instances of your custom model in an Evennia <a class="reference internal" href="../Components/Attributes.html"><span class="doc std std-doc">Attribute</span></a>. It will automatically be set upon creation and can after that not be changed. Having this field will allow you to do e.g. <code class="docutils literal notranslate"><span class="pre">obj.db.myinstance</span> <span class="pre">=</span> <span class="pre">mydatastore</span></code>. If you know youll never store your model instances in Attributes the <code class="docutils literal notranslate"><span class="pre">db_date_created</span></code> field is optional.</p>
</div></blockquote>
<p>You dont <em>have</em> to start field names with <code class="docutils literal notranslate"><span class="pre">db_</span></code>, this is an Evennia convention. Its nevertheless
recommended that you do use <code class="docutils literal notranslate"><span class="pre">db_</span></code>, partly for clarity and consistency with Evennia (if you ever want
to share your code) and partly for the case of you later deciding to use Evennias
<p>You dont <em>have</em> to start field names with <code class="docutils literal notranslate"><span class="pre">db_</span></code>, this is an Evennia convention. Its nevertheless recommended that you do use <code class="docutils literal notranslate"><span class="pre">db_</span></code>, partly for clarity and consistency with Evennia (if you ever want to share your code) and partly for the case of you later deciding to use Evennias
<code class="docutils literal notranslate"><span class="pre">SharedMemoryModel</span></code> parent down the line.</p>
<p>The field keyword <code class="docutils literal notranslate"><span class="pre">db_index</span></code> creates a <em>database index</em> for this field, which allows quicker
lookups, so its recommended to put it on fields you know youll often use in queries. The
<code class="docutils literal notranslate"><span class="pre">null=True</span></code> and <code class="docutils literal notranslate"><span class="pre">blank=True</span></code> keywords means that these fields may be left empty or set to the empty
string without the database complaining. There are many other field types and keywords to define
them, see django docs for more info.</p>
<p>Similar to using <a class="reference external" href="https://docs.djangoproject.com/en/4.1/howto/legacy-databases/">django-admin</a> you
are able to do <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">inspectdb</span></code> to get an automated listing of model information for an existing
database. As is the case with any model generating tool you should only use this as a starting
<p>The field keyword <code class="docutils literal notranslate"><span class="pre">db_index</span></code> creates a <em>database index</em> for this field, which allows quicker lookups, so its recommended to put it on fields you know youll often use in queries. The <code class="docutils literal notranslate"><span class="pre">null=True</span></code> and <code class="docutils literal notranslate"><span class="pre">blank=True</span></code> keywords means that these fields may be left empty or set to the empty string without the database complaining. There are many other field types and keywords to define them, see django docs for more info.</p>
<p>Similar to using <a class="reference external" href="https://docs.djangoproject.com/en/4.1/howto/legacy-databases/">django-admin</a> you are able to do <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">inspectdb</span></code> to get an automated listing of model information for an existing database. As is the case with any model generating tool you should only use this as a starting
point for your models.</p>
</section>
<section id="referencing-existing-models-and-typeclasses">
<h2>Referencing existing models and typeclasses<a class="headerlink" href="#referencing-existing-models-and-typeclasses" title="Permalink to this headline"></a></h2>
<p>You may want to use <code class="docutils literal notranslate"><span class="pre">ForeignKey</span></code> or <code class="docutils literal notranslate"><span class="pre">ManyToManyField</span></code> to relate your new model to existing ones.</p>
<p>To do this we need to specify the app-path for the root object type we want to store as a string (we must use a string rather than the class directly or youll run into problems with models not having been initialized yet).</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;objects.ObjectDB&quot;</span></code> for all <a class="reference internal" href="../Components/Objects.html"><span class="doc std std-doc">Objects</span></a> (like exits, rooms, characters etc)</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;accounts.AccountDB&quot;</span></code> for <a class="reference internal" href="../Components/Accounts.html"><span class="doc std std-doc">Accounts</span></a>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;scripts.ScriptDB&quot;</span></code> for <a class="reference internal" href="../Components/Scripts.html"><span class="doc std std-doc">Scripts</span></a>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;comms.ChannelDB&quot;</span></code> for <a class="reference internal" href="../Components/Channels.html"><span class="doc std std-doc">Channels</span></a>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;comms.Msg&quot;</span></code> for <a class="reference internal" href="../Components/Msg.html"><span class="doc std std-doc">Msg</span></a> objects.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">&quot;help.HelpEntry&quot;</span></code> for <a class="reference internal" href="../Components/Help-System.html"><span class="doc std std-doc">Help Entries</span></a>.</p></li>
</ul>
<p>Heres an example:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
<span class="k">class</span> <span class="nc">MySpecial</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
<span class="n">db_character</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s2">&quot;objects.ObjectDB&quot;</span><span class="p">)</span>
<span class="n">db_items</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ManyToManyField</span><span class="p">(</span><span class="s2">&quot;objects.ObjectDB&quot;</span><span class="p">)</span>
<span class="n">db_account</span> <span class="o">=</span> <span class="n">modeles</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s2">&quot;accounts.AccountDB&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>It may seem counter-intuitive, but this will work correctly:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>myspecial.db_character = my_character # a Character instance
my_character = myspecial.db_character # still a Character
</pre></div>
</div>
<p>This works because when the <code class="docutils literal notranslate"><span class="pre">.db_character</span></code> field is loaded into Python, the entity itself knows that its supposed to be a <code class="docutils literal notranslate"><span class="pre">Character</span></code> and loads itself to that form.</p>
<p>The drawback of this is that the database wont <em>enforce</em> the type of object you store in the relation. This is the price we pay for many of the other advantages of the Typeclass system.</p>
<p>While the <code class="docutils literal notranslate"><span class="pre">db_character</span></code> field fail if you try to store an <code class="docutils literal notranslate"><span class="pre">Account</span></code>, it will gladly accept any instance of a typeclass that inherits from <code class="docutils literal notranslate"><span class="pre">ObjectDB</span></code>, such as rooms, exits or other non-character Objects. Its up to you to validate that what you store is what you expect it to be.</p>
</section>
<section id="creating-a-new-model-instance">
<h2>Creating a new model instance<a class="headerlink" href="#creating-a-new-model-instance" title="Permalink to this headline"></a></h2>
<p>To create a new row in your table, you instantiate the model and then call its <code class="docutils literal notranslate"><span class="pre">save()</span></code> method:</p>
@ -251,70 +242,41 @@ point for your models.</p>
</pre></div>
</div>
<p>Note that the <code class="docutils literal notranslate"><span class="pre">db_date_created</span></code> field of the model is not specified. Its flag <code class="docutils literal notranslate"><span class="pre">at_now_add=True</span></code>
makes sure to set it to the current date when the object is created (it can also not be changed
further after creation).</p>
<p>When you update an existing object with some new field value, remember that you have to save the
object afterwards, otherwise the database will not update:</p>
<p>Note that the <code class="docutils literal notranslate"><span class="pre">db_date_created</span></code> field of the model is not specified. Its flag <code class="docutils literal notranslate"><span class="pre">at_now_add=True</span></code> makes sure to set it to the current date when the object is created (it can also not be changed further after creation).</p>
<p>When you update an existing object with some new field value, remember that you have to save the object afterwards, otherwise the database will not update:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="n">my_datastore</span><span class="o">.</span><span class="n">db_key</span> <span class="o">=</span> <span class="s2">&quot;Larger Sword&quot;</span>
<span class="n">my_datastore</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
</pre></div>
</div>
<p>Evennias normal models dont need to explicitly save, since they are based on <code class="docutils literal notranslate"><span class="pre">SharedMemoryModel</span></code>
rather than the raw django model. This is covered in the next section.</p>
<p>Evennias normal models dont need to explicitly save, since they are based on <code class="docutils literal notranslate"><span class="pre">SharedMemoryModel</span></code> rather than the raw django model. This is covered in the next section.</p>
</section>
<section id="using-the-sharedmemorymodel-parent">
<h2>Using the <code class="docutils literal notranslate"><span class="pre">SharedMemoryModel</span></code> parent<a class="headerlink" href="#using-the-sharedmemorymodel-parent" title="Permalink to this headline"></a></h2>
<p>Evennia doesnt base most of its models on the raw <code class="docutils literal notranslate"><span class="pre">django.db.models</span></code> but on the Evennia base model
<code class="docutils literal notranslate"><span class="pre">evennia.utils.idmapper.models.SharedMemoryModel</span></code>. There are two main reasons for this:</p>
<p>Evennia doesnt base most of its models on the raw <code class="docutils literal notranslate"><span class="pre">django.db.models.Model</span></code> but on the Evennia base model <code class="docutils literal notranslate"><span class="pre">evennia.utils.idmapper.models.SharedMemoryModel</span></code>. There are two main reasons for this:</p>
<ol class="simple">
<li><p>Ease of updating fields without having to explicitly call <code class="docutils literal notranslate"><span class="pre">save()</span></code></p></li>
<li><p>On-object memory persistence and database caching</p></li>
</ol>
<p>The first (and least important) point means that as long as you named your fields <code class="docutils literal notranslate"><span class="pre">db_*</span></code>, Evennia
will automatically create field wrappers for them. This happens in the models
<a class="reference external" href="http://en.wikibooks.org/wiki/Python_Programming/Metaclasses">Metaclass</a> so there is no speed
penalty for this. The name of the wrapper will be the same name as the field, minus the <code class="docutils literal notranslate"><span class="pre">db_</span></code>
prefix. So the <code class="docutils literal notranslate"><span class="pre">db_key</span></code> field will have a wrapper property named <code class="docutils literal notranslate"><span class="pre">key</span></code>. You can then do:</p>
<p>The first (and least important) point means that as long as you named your fields <code class="docutils literal notranslate"><span class="pre">db_*</span></code>, Evennia will automatically create field wrappers for them. This happens in the models <a class="reference external" href="http://en.wikibooks.org/wiki/Python_Programming/Metaclasses">Metaclass</a> so there is no speed penalty for this. The name of the wrapper will be the same name as the field, minus the <code class="docutils literal notranslate"><span class="pre">db_</span></code> prefix. So the <code class="docutils literal notranslate"><span class="pre">db_key</span></code> field will have a wrapper property named <code class="docutils literal notranslate"><span class="pre">key</span></code>. You can then do:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="n">my_datastore</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;Larger Sword&quot;</span>
</pre></div>
</div>
<p>and dont have to explicitly call <code class="docutils literal notranslate"><span class="pre">save()</span></code> afterwards. The saving also happens in a more efficient
way under the hood, updating only the field rather than the entire model using django optimizations.
Note that if you were to manually add the property or method <code class="docutils literal notranslate"><span class="pre">key</span></code> to your model, this will be used
instead of the automatic wrapper and allows you to fully customize access as needed.</p>
<p>To explain the second and more important point, consider the following example using the default
Django model parent:</p>
<p>and dont have to explicitly call <code class="docutils literal notranslate"><span class="pre">save()</span></code> afterwards. The saving also happens in a more efficient way under the hood, updating only the field rather than the entire model using django optimizations. Note that if you were to manually add the property or method <code class="docutils literal notranslate"><span class="pre">key</span></code> to your model, this will be used instead of the automatic wrapper and allows you to fully customize access as needed.</p>
<p>To explain the second and more important point, consider the following example using the default Django model parent:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="n">shield</span> <span class="o">=</span> <span class="n">MyDataStore</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">db_key</span><span class="o">=</span><span class="s2">&quot;SmallShield&quot;</span><span class="p">)</span>
<span class="n">shield</span><span class="o">.</span><span class="n">cracked</span> <span class="o">=</span> <span class="kc">True</span> <span class="c1"># where cracked is not a database field</span>
</pre></div>
</div>
<p>And then later:</p>
<p>And then in another function you do</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="n">shield</span> <span class="o">=</span> <span class="n">MyDataStore</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">db_key</span><span class="o">=</span><span class="s2">&quot;SmallShield&quot;</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">shield</span><span class="o">.</span><span class="n">cracked</span><span class="p">)</span> <span class="c1"># error!</span>
</pre></div>
</div>
<p>The outcome of that last print statement is <em>undefined</em>! It could <em>maybe</em> randomly work but most
likely you will get an <code class="docutils literal notranslate"><span class="pre">AttributeError</span></code> for not finding the <code class="docutils literal notranslate"><span class="pre">cracked</span></code> property. The reason is that
<code class="docutils literal notranslate"><span class="pre">cracked</span></code> doesnt represent an actual field in the database. It was just added at run-time and thus
Django dont care about it. When you retrieve your shield-match later there is <em>no</em> guarantee you
will get back the <em>same Python instance</em> of the model where you defined <code class="docutils literal notranslate"><span class="pre">cracked</span></code>, even if you
search for the same database object.</p>
<p>Evennia relies heavily on on-model handlers and other dynamically created properties. So rather than
using the vanilla Django models, Evennia uses <code class="docutils literal notranslate"><span class="pre">SharedMemoryModel</span></code>, which levies something called
<em>idmapper</em>. The idmapper caches model instances so that we will always get the <em>same</em> instance back
after the first lookup of a given object. Using idmapper, the above example would work fine and you
could retrieve your <code class="docutils literal notranslate"><span class="pre">cracked</span></code> property at any time - until you rebooted when all non-persistent data
goes.</p>
<p>The outcome of that last print statement is <em>undefined</em>! It could <em>maybe</em> randomly work but most likely you will get an <code class="docutils literal notranslate"><span class="pre">AttributeError</span></code> for not finding the <code class="docutils literal notranslate"><span class="pre">cracked</span></code> property. The reason is that <code class="docutils literal notranslate"><span class="pre">cracked</span></code> doesnt represent an actual field in the database. It was just added at run-time and thus Django dont care about it. When you retrieve your shield-match later there is <em>no</em> guarantee you will get back the <em>same Python instance</em> of the model where you defined <code class="docutils literal notranslate"><span class="pre">cracked</span></code>, even if you search for the same database object.</p>
<p>Evennia relies heavily on on-model handlers and other dynamically created properties. So rather than using the vanilla Django models, Evennia uses <code class="docutils literal notranslate"><span class="pre">SharedMemoryModel</span></code>, which levies something called <em>idmapper</em>. The idmapper caches model instances so that we will always get the <em>same</em> instance back after the first lookup of a given object. Using idmapper, the above example would work fine and you could retrieve your <code class="docutils literal notranslate"><span class="pre">cracked</span></code> property at any time - until you rebooted when all non-persistent data goes.</p>
<p>Using the idmapper is both more intuitive and more efficient <em>per object</em>; it leads to a lot less
reading from disk. The drawback is that this system tends to be more memory hungry <em>overall</em>. So if
you know that youll <em>never</em> need to add new properties to running instances or know that you will
create new objects all the time yet rarely access them again (like for a log system), you are
probably better off making “plain” Django models rather than using <code class="docutils literal notranslate"><span class="pre">SharedMemoryModel</span></code> and its
idmapper.</p>
<p>To use the idmapper and the field-wrapper functionality you just have to have your model classes
inherit from <code class="docutils literal notranslate"><span class="pre">evennia.utils.idmapper.models.SharedMemoryModel</span></code> instead of from the default
<code class="docutils literal notranslate"><span class="pre">django.db.models.Model</span></code>:</p>
reading from disk. The drawback is that this system tends to be more memory hungry <em>overall</em>. So if you know that youll <em>never</em> need to add new properties to running instances or know that you will create new objects all the time yet rarely access them again (like for a log system), you are probably better off making “plain” Django models rather than using <code class="docutils literal notranslate"><span class="pre">SharedMemoryModel</span></code> and its idmapper.</p>
<p>To use the idmapper and the field-wrapper functionality you just have to have your model classes inherit from <code class="docutils literal notranslate"><span class="pre">evennia.utils.idmapper.models.SharedMemoryModel</span></code> instead of from the default <code class="docutils literal notranslate"><span class="pre">django.db.models.Model</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia.utils.idmapper.models</span> <span class="kn">import</span> <span class="n">SharedMemoryModel</span>
<span class="k">class</span> <span class="nc">MyDataStore</span><span class="p">(</span><span class="n">SharedMemoryModel</span><span class="p">):</span>
@ -330,9 +292,7 @@ inherit from <code class="docutils literal notranslate"><span class="pre">evenni
</section>
<section id="searching-for-your-models">
<h2>Searching for your models<a class="headerlink" href="#searching-for-your-models" title="Permalink to this headline"></a></h2>
<p>To search your new custom database table you need to use its database <em>manager</em> to build a <em>query</em>.
Note that even if you use <code class="docutils literal notranslate"><span class="pre">SharedMemoryModel</span></code> as described in the previous section, you have to use
the actual <em>field names</em> in the query, not the wrapper name (so <code class="docutils literal notranslate"><span class="pre">db_key</span></code> and not just <code class="docutils literal notranslate"><span class="pre">key</span></code>).</p>
<p>To search your new custom database table you need to use its database <em>manager</em> to build a <em>query</em>. Note that even if you use <code class="docutils literal notranslate"><span class="pre">SharedMemoryModel</span></code> as described in the previous section, you have to use the actual <em>field names</em> in the query, not the wrapper name (so <code class="docutils literal notranslate"><span class="pre">db_key</span></code> and not just <code class="docutils literal notranslate"><span class="pre">key</span></code>).</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="kn">from</span> <span class="nn">world.myapp</span> <span class="kn">import</span> <span class="n">MyDataStore</span>
<span class="c1"># get all datastore objects exactly matching a given key</span>
@ -346,8 +306,7 @@ the actual <em>field names</em> in the query, not the wrapper name (so <code cla
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">match</span><span class="o">.</span><span class="n">db_text</span><span class="p">)</span>
</pre></div>
</div>
<p>See the <a class="reference external" href="https://docs.djangoproject.com/en/4.1/topics/db/queries/">Django query documentation</a> for a
lot more information about querying the database.</p>
<p>See the <a class="reference internal" href="../Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Django-queries.html"><span class="doc std std-doc">Beginner Tutorial lesson on Django querying</span></a> for a lot more information about querying the database.</p>
</section>
</section>

View file

@ -107,18 +107,17 @@
<section class="tex2jax_ignore mathjax_ignore" id="evadventure">
<h1>EvAdventure<a class="headerlink" href="#evadventure" title="Permalink to this headline"></a></h1>
<p>Contrib by Griatch 2022</p>
<p>Contrib by Griatch 2023-</p>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>NOTE - this tutorial is WIP and NOT complete! It was put on hold to focus on
releasing Evennia 1.0. You will still learn things from it, but dont expect
perfection.</p>
<p>NOTE - this tutorial is WIP and NOT complete yet! You will still learn
things from it, but dont expect perfection.</p>
</div>
<p>A complete example MUD using Evennia. This is the final result of what is
implemented if you follow the Getting-Started tutorial. Its recommended
that you follow the tutorial step by step and write your own code. But if
you prefer you can also pick apart or use this as a starting point for your
own game.</p>
implemented if you follow <a class="reference internal" href="../Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Part3-Overview.html"><span class="doc std std-doc">Part 3 of the Getting-Started tutorial</span></a>.
Its recommended that you follow the tutorial step by step and write your own
code. But if you prefer you can also pick apart or use this as a starting point
for your own game.</p>
<section id="features">
<h2>Features<a class="headerlink" href="#features" title="Permalink to this headline"></a></h2>
<ul class="simple">

View file

@ -743,12 +743,11 @@ character make small verbal observations at irregular intervals.</p>
</section>
<section id="evadventure">
<h3><code class="docutils literal notranslate"><span class="pre">evadventure</span></code><a class="headerlink" href="#evadventure" title="Permalink to this headline"></a></h3>
<p><em>Contrib by Griatch 2022</em></p>
<p><em>Contrib by Griatch 2023-</em></p>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>NOTE - this tutorial is WIP and NOT complete! It was put on hold to focus on
releasing Evennia 1.0. You will still learn things from it, but dont expect
perfection.</p>
<p>NOTE - this tutorial is WIP and NOT complete yet! You will still learn
things from it, but dont expect perfection.</p>
</div>
<p><a class="reference internal" href="Contrib-Evadventure.html"><span class="doc std std-doc">Read the documentation</span></a> - <a class="reference internal" href="../api/evennia.contrib.tutorials.evadventure.html#evennia-contrib-tutorials-evadventure"><span class="std std-ref">Browse the Code</span></a></p>
</section>

View file

@ -157,32 +157,20 @@
at the root of <code class="docutils literal notranslate"><span class="pre">evennia/docs/source/</span></code>.</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">source/Components/</span></code> are docs describing separate Evennia building blocks, that is, things
that you can import and use. This extends and elaborates on what can be found out by reading
the api docs themselves. Example are documentation for <code class="docutils literal notranslate"><span class="pre">Accounts</span></code>, <code class="docutils literal notranslate"><span class="pre">Objects</span></code> and <code class="docutils literal notranslate"><span class="pre">Commands</span></code>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source/Concepts/</span></code> describes how larger-scale features of Evennia hang together - things that
cant easily be broken down into one isolated component. This can be general descriptions of
how Models and Typeclasses interact to the path a message takes from the client to the server
and back.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source/Setup/</span></code> holds detailed docs on installing, running and maintaining the Evennia server and
the infrastructure around it.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source/Coding/</span></code> has help on how to interact with, use and navigate the Evennia codebase itself.
This also has non-Evennia-specific help on general development concepts and how to set up a sane development environment.</p></li>
that you can import and use. This extends and elaborates on what can be found out by reading the api docs themselves. Example are documentation for <code class="docutils literal notranslate"><span class="pre">Accounts</span></code>, <code class="docutils literal notranslate"><span class="pre">Objects</span></code> and <code class="docutils literal notranslate"><span class="pre">Commands</span></code>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source/Concepts/</span></code> describes how larger-scale features of Evennia hang together - things that cant easily be broken down into one isolated component. This can be general descriptions of how Models and Typeclasses interact to the path a message takes from the client to the server and back.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source/Setup/</span></code> holds detailed docs on installing, running and maintaining the Evennia server and the infrastructure around it.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source/Coding/</span></code> has help on how to interact with, use and navigate the Evennia codebase itself. This also has non-Evennia-specific help on general development concepts and how to set up a sane development environment.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source/Contribs/</span></code> holds documentation specifically for packages in the <code class="docutils literal notranslate"><span class="pre">evennia/contribs/</span></code> folder. Any contrib-specific tutorials will be found here instead of in <code class="docutils literal notranslate"><span class="pre">Howtos</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source/Howtos/</span></code> holds docs that describe how to achieve a specific goal, effect or
result in Evennia. This is often on a tutorial or FAQ form and will refer to the rest of the
documentation for further reading.</p>
<ul>
result in Evennia. This is often on a tutorial or FAQ form and will refer to the rest of the documentation for further reading.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source/Howtos/Beginner-Tutorial/</span></code> holds all documents part of the initial tutorial sequence.</p></li>
</ul>
</li>
</ul>
<p>Other files and folders:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">source/api/</span></code> contains the auto-generated API documentation as <code class="docutils literal notranslate"><span class="pre">.html</span></code> files. Dont edit these
files manually, they are auto-generated from sources.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source/api/</span></code> contains the auto-generated API documentation as <code class="docutils literal notranslate"><span class="pre">.html</span></code> files. Dont edit these files manually, they are auto-generated from sources.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source/_templates</span></code> and <code class="docutils literal notranslate"><span class="pre">source/_static</span></code> hold files for the doc itself. They should only be modified if wanting to change the look and structure of the documentation generation itself.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">conf.py</span></code> holds the Sphinx configuration. It should usually not be modified except to update
the Evennia version on a new branch.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">conf.py</span></code> holds the Sphinx configuration. It should usually not be modified except to update the Evennia version on a new branch.</p></li>
</ul>
</section>
<section id="editing-syntax">

View file

@ -553,12 +553,108 @@ 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-Chargen.html#conclusions">6.10. Conclusions</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html">7. In-game Rooms</a></li>
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-NPCs.html">8. Non-Player-Characters (NPCs)</a></li>
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Quests.html">9. Game Quests</a></li>
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Shops.html">10. In-game Shops</a></li>
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Dungeon.html">11. Dynamically generated Dungeon</a></li>
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Commands.html">12. In-game Commands</a></li>
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html">7. In-game Rooms</a><ul>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html#the-base-room">7.1. The base room</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html#pvp-room">7.2. PvP room</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html#adding-a-room-map">7.3. Adding a room map</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html#adding-life-to-a-room">7.4. Adding life to a room</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html#testing">7.5. Testing</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html#conclusion">7.6. Conclusion</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-NPCs.html">8. Non-Player-Characters</a><ul>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-NPCs.html#the-npc-base-class">8.1. The NPC base class</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-NPCs.html#testing">8.2. Testing</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-NPCs.html#conclusions">8.3. Conclusions</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html">9. Combat base framework</a><ul>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#combathandler">9.1. CombatHandler</a><ul>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#combathandler-get-or-create-combathandler">9.1.1. CombatHandler.get_or_create_combathandler</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#combathandler-msg">9.1.2. CombatHandler.msg</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#combathandler-get-combat-summary">9.1.3. Combathandler.get_combat_summary</a></li>
</ul>
</li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#actions">9.2. Actions</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#action-dicts">9.3. Action dicts</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#action-classes">9.4. Action classes</a><ul>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#hold-action">9.4.1. Hold Action</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#attack-action">9.4.2. Attack Action</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#stunt-action">9.4.3. Stunt Action</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#use-item-action">9.4.4. Use Item Action</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#wield-action">9.4.5. Wield Action</a></li>
</ul>
</li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#testing">9.5. Testing</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#conclusions">9.6. Conclusions</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html">10. Twitch Combat</a><ul>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#general-principle">10.1. General principle</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#twitch-combat-handler">10.2. Twitch combat handler</a><ul>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#getting-the-sides-of-combat">10.2.1. Getting the sides of combat</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#tracking-advantage-disadvantage">10.2.2. Tracking Advantage / Disadvantage</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#queue-action">10.2.3. Queue action</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#execute-an-action">10.2.4. Execute an action</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#checking-and-stopping-combat">10.2.5. Checking and stopping combat</a></li>
</ul>
</li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#commands">10.3. Commands</a><ul>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#base-combat-class">10.3.1. Base Combat class</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#in-combat-look-command">10.3.2. In-combat look command</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#hold-command">10.3.3. Hold command</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#attack-command">10.3.4. Attack command</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#using-items">10.3.5. Using items</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#wielding-new-weapons-and-equipment">10.3.6. Wielding new weapons and equipment</a></li>
</ul>
</li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#grouping-commands-for-use">10.4. Grouping Commands for use</a><ul>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#combat-startup-and-cleanup">10.4.1. Combat startup and cleanup</a></li>
</ul>
</li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#unit-testing">10.5. Unit Testing</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#a-small-combat-test">10.6. A small combat test</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#conclusions">10.7. Conclusions</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html">11. Turnbased Combat</a><ul>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#general-principle">11.1. General Principle</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#turnbased-combat-handler">11.2. Turnbased combat handler</a><ul>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#getting-the-sides-of-combat">11.2.1. Getting the sides of combat</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#tracking-advantage-disadvantage">11.2.2. Tracking Advantage/Disadvantage</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#adding-and-removing-combatants">11.2.3. Adding and removing combatants</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#flee-action">11.2.4. Flee Action</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#queue-action">11.2.5. Queue action</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#execute-an-action-and-tick-the-round">11.2.6. Execute an action and tick the round</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#check-and-stop-combat">11.2.7. Check and stop combat</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#start-combat">11.2.8. Start combat</a></li>
</ul>
</li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#using-evmenu-for-the-combat-menu">11.3. Using EvMenu for the combat menu</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#menu-for-turnbased-combat">11.4. Menu for Turnbased combat</a><ul>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#the-node-index">11.4.1. The node index</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#getting-or-setting-the-combathandler">11.4.2. Getting or setting the combathandler</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#queue-an-action">11.4.3. Queue an action</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#rerun-a-node">11.4.4. Rerun a node</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#stepping-through-the-wizard">11.4.5. Stepping through the wizard</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#choosing-targets-and-recipients">11.4.6. Choosing targets and recipients</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#choose-an-ability">11.4.7. Choose an Ability</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#choose-an-item-to-use-or-wield">11.4.8. Choose an item to use or wield</a></li>
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#the-main-menu-node">11.4.9. The main menu node</a></li>
</ul>
</li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#attack-command">11.5. Attack Command</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#making-sure-the-menu-stops">11.6. Making sure the menu stops</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#testing">11.7. Testing</a></li>
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#a-small-combat-test">11.8. A small combat test</a></li>
<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-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>
</ul>
</li>
</ul>

View file

@ -132,12 +132,10 @@ if we cannot find and use it afterwards.</p>
<span class="n">accts</span> <span class="o">=</span> <span class="n">evennia</span><span class="o">.</span><span class="n">search_account</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="s2">&quot;MyAccountName&quot;</span><span class="p">,</span> <span class="n">email</span><span class="o">=</span><span class="s2">&quot;foo@bar.com&quot;</span><span class="p">)</span>
</pre></div>
</div>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>
```{sidebar} Querysets
What is returned from the main search functions is actually a `queryset`. They can be treated like lists except that they can&#39;t modified in-place. We&#39;ll discuss querysets in the `next lesson` &lt;Django-queries&gt;`_.
</pre></div>
</div>
<aside class="sidebar">
<p class="sidebar-title">Querysets</p>
<p>What is returned from the main search functions is actually a <code class="docutils literal notranslate"><span class="pre">queryset</span></code>. They can be treated like lists except that they cant modified in-place. Well discuss querysets in the <code class="docutils literal notranslate"><span class="pre">next</span> <span class="pre">lesson</span></code> <Django-queries>`_.</p>
</aside>
<p>Strings are always case-insensitive, so searching for <code class="docutils literal notranslate"><span class="pre">&quot;rose&quot;</span></code>, <code class="docutils literal notranslate"><span class="pre">&quot;Rose&quot;</span></code> or <code class="docutils literal notranslate"><span class="pre">&quot;rOsE&quot;</span></code> give the same results. Its important to remember that what is returned from these search methods is a <em>listing</em> of zero, one or more elements - all the matches to your search. To get the first match:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>rose = roses[0]
</pre></div>
@ -187,7 +185,7 @@ What is returned from the main search functions is actually a `queryset`. They c
<span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">result</span>
<span class="k">return</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Found match for </span><span class="si">{</span><span class="n">query</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">foo</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Found match for </span><span class="si">{</span><span class="n">query</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">foo</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Remember, <code class="docutils literal notranslate"><span class="pre">self.caller</span></code> is the one calling the command. This is usually a Character, which

View file

@ -0,0 +1,142 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>12. NPC and monster AI &#8212; Evennia 1.0 documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="13. Dynamically generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
<link rel="prev" title="11. Turnbased Combat" href="Beginner-Tutorial-Combat-Turnbased.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Combat-Turnbased.html" title="11. Turnbased Combat"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</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> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">12. </span>NPC and monster AI</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="../../../index.html">
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-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>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-AI.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-AI.html">1.0 (main branch)</a></li>
<ul>
<li><a href="../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="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>
</div>
</section>
</div>
</div>
</div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Combat-Turnbased.html" title="11. Turnbased Combat"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How we get there (example game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">12. </span>NPC and monster AI</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -212,6 +212,29 @@ since it can also get confusing to follow the code.</p>
<span class="c1"># makes it easy for mobs to know to attack PCs</span>
<span class="n">is_pc</span> <span class="o">=</span> <span class="kc">False</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">hurt_level</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> String describing how hurt this character is.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">percent</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">min</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">100</span> <span class="o">*</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">/</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span><span class="p">)))</span>
<span class="k">if</span> <span class="mi">95</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">100</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|gPerfect|n&quot;</span>
<span class="k">elif</span> <span class="mi">80</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">95</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|gScraped|n&quot;</span>
<span class="k">elif</span> <span class="mi">60</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">80</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|GBruised|n&quot;</span>
<span class="k">elif</span> <span class="mi">45</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">60</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|yHurt|n&quot;</span>
<span class="k">elif</span> <span class="mi">30</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">45</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|yWounded|n&quot;</span>
<span class="k">elif</span> <span class="mi">15</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">30</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|rBadly wounded|n&quot;</span>
<span class="k">elif</span> <span class="mi">1</span> <span class="o">&lt;</span> <span class="n">percent</span> <span class="o">&lt;=</span> <span class="mi">15</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|rBarely hanging on|n&quot;</span>
<span class="k">elif</span> <span class="n">percent</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">return</span> <span class="s2">&quot;|RCollapsed!|n&quot;</span>
<span class="k">def</span> <span class="nf">heal</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">hp</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Heal hp amount of health, not allowing to exceed our max hp</span>
@ -229,6 +252,10 @@ since it can also get confusing to follow the code.</p>
<span class="bp">self</span><span class="o">.</span><span class="n">coins</span> <span class="o">-=</span> <span class="n">amount</span>
<span class="k">return</span> <span class="n">amount</span>
<span class="k">def</span> <span class="nf">at_attacked</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attacker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called when being attacked and combat starts.&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">at_damage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">damage</span><span class="p">,</span> <span class="n">attacker</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called when attacked and taking damage.&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">-=</span> <span class="n">damage</span>
@ -256,8 +283,8 @@ since it can also get confusing to follow the code.</p>
</pre></div>
</div>
<p>Most of these are empty since they will behave differently for characters and npcs. But having them
in the mixin means we can expect these methods to be available for all living things.</p>
<p>Most of these are empty since they will behave differently for characters and npcs. But having them in the mixin means we can expect these methods to be available for all living things.</p>
<p>Once we create more of our game, we will need to remember to actually call these hook methods so they serve a purpose. For example, once we implement combat, we must remember to call <code class="docutils literal notranslate"><span class="pre">at_attacked</span></code> as well as the other methods involving taking damage, getting defeated or dying.</p>
</section>
<section id="character-class">
<h2><span class="section-number">3.3. </span>Character class<a class="headerlink" href="#character-class" title="Permalink to this headline"></a></h2>
@ -310,21 +337,17 @@ in the mixin means we can expect these methods to be available for all living th
<span class="c1"># TODO - go back into chargen to make a new character! </span>
</pre></div>
</div>
<p>We make an assumption about our rooms here - that they have a property <code class="docutils literal notranslate"><span class="pre">.allow_death</span></code>. We need
to make a note to actually add such a property to rooms later!</p>
<p>We make an assumption about our rooms here - that they have a property <code class="docutils literal notranslate"><span class="pre">.allow_death</span></code>. We need to make a note to actually add such a property to rooms later!</p>
<p>In our <code class="docutils literal notranslate"><span class="pre">Character</span></code> class we implement all attributes we want to simulate from the <em>Knave</em> ruleset.
The <code class="docutils literal notranslate"><span class="pre">AttributeProperty</span></code> is one way to add an Attribute in a field-like way; these will be accessible
on every character in several ways:</p>
The <code class="docutils literal notranslate"><span class="pre">AttributeProperty</span></code> is one way to add an Attribute in a field-like way; these will be accessible on every character in several ways:</p>
<ul class="simple">
<li><p>As <code class="docutils literal notranslate"><span class="pre">character.strength</span></code></p></li>
<li><p>As <code class="docutils literal notranslate"><span class="pre">character.db.strength</span></code></p></li>
<li><p>As <code class="docutils literal notranslate"><span class="pre">character.attributes.get(&quot;strength&quot;)</span></code></p></li>
</ul>
<p>See <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attributes</span></a> for seeing how Attributes work.</p>
<p>Unlike in base <em>Knave</em>, we store <code class="docutils literal notranslate"><span class="pre">coins</span></code> as a separate Attribute rather than as items in the inventory,
this makes it easier to handle barter and trading later.</p>
<p>We implement the Player Character versions of <code class="docutils literal notranslate"><span class="pre">at_defeat</span></code> and <code class="docutils literal notranslate"><span class="pre">at_death</span></code>. We also make use of <code class="docutils literal notranslate"><span class="pre">.heal()</span></code>
from the <code class="docutils literal notranslate"><span class="pre">LivingMixin</span></code> class.</p>
<p>Unlike in base <em>Knave</em>, we store <code class="docutils literal notranslate"><span class="pre">coins</span></code> as a separate Attribute rather than as items in the inventory, this makes it easier to handle barter and trading later.</p>
<p>We implement the Player Character versions of <code class="docutils literal notranslate"><span class="pre">at_defeat</span></code> and <code class="docutils literal notranslate"><span class="pre">at_death</span></code>. We also make use of <code class="docutils literal notranslate"><span class="pre">.heal()</span></code> from the <code class="docutils literal notranslate"><span class="pre">LivingMixin</span></code> class.</p>
<section id="funcparser-inlines">
<h3><span class="section-number">3.3.1. </span>Funcparser inlines<a class="headerlink" href="#funcparser-inlines" title="Permalink to this headline"></a></h3>
<p>This piece of code is worth some more explanation:</p>
@ -333,14 +356,9 @@ from the <code class="docutils literal notranslate"><span class="pre">LivingMixi
<span class="n">from_obj</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
</pre></div>
</div>
<p>Remember that <code class="docutils literal notranslate"><span class="pre">self</span></code> is the Character instance here. So <code class="docutils literal notranslate"><span class="pre">self.location.msg_contents</span></code> means “send a
message to everything inside my current location”. In other words, send a message to everyone
in the same place as the character.</p>
<p>Remember that <code class="docutils literal notranslate"><span class="pre">self</span></code> is the Character instance here. So <code class="docutils literal notranslate"><span class="pre">self.location.msg_contents</span></code> means “send a message to everything inside my current location”. In other words, send a message to everyone in the same place as the character.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">$You()</span> <span class="pre">$conj(collapse)</span></code> are <a class="reference internal" href="../../../Components/FuncParser.html"><span class="doc std std-doc">FuncParser inlines</span></a>. These are functions that
execute
in the string. The resulting string may look different for different audiences. The <code class="docutils literal notranslate"><span class="pre">$You()</span></code> inline
function will use <code class="docutils literal notranslate"><span class="pre">from_obj</span></code> to figure out who you are and either show your name or You.
The <code class="docutils literal notranslate"><span class="pre">$conj()</span></code> (verb conjugator) will tweak the (English) verb to match.</p>
execute in the string. The resulting string may look different for different audiences. The <code class="docutils literal notranslate"><span class="pre">$You()</span></code> inline function will use <code class="docutils literal notranslate"><span class="pre">from_obj</span></code> to figure out who you are and either show your name or You. The <code class="docutils literal notranslate"><span class="pre">$conj()</span></code> (verb conjugator) will tweak the (English) verb to match.</p>
<ul class="simple">
<li><p>You will see: <code class="docutils literal notranslate"><span class="pre">&quot;You</span> <span class="pre">collapse</span> <span class="pre">in</span> <span class="pre">a</span> <span class="pre">heap,</span> <span class="pre">alive</span> <span class="pre">but</span> <span class="pre">beaten.&quot;</span></code></p></li>
<li><p>Others in the room will see: <code class="docutils literal notranslate"><span class="pre">&quot;Thomas</span> <span class="pre">collapses</span> <span class="pre">in</span> <span class="pre">a</span> <span class="pre">heap,</span> <span class="pre">alive</span> <span class="pre">but</span> <span class="pre">beaten.&quot;</span></code></p></li>
@ -349,9 +367,7 @@ The <code class="docutils literal notranslate"><span class="pre">$conj()</span><
</section>
<section id="backtracking">
<h3><span class="section-number">3.3.2. </span>Backtracking<a class="headerlink" href="#backtracking" title="Permalink to this headline"></a></h3>
<p>We make our first use of the <code class="docutils literal notranslate"><span class="pre">rules.dice</span></code> roller to roll on the death table! As you may recall, in the
previous lesson, we didnt know just what to do when rolling dead on this table. Now we know - we
should be calling <code class="docutils literal notranslate"><span class="pre">at_death</span></code> on the character. So lets add that where we had TODOs before:</p>
<p>We make our first use of the <code class="docutils literal notranslate"><span class="pre">rules.dice</span></code> roller to roll on the death table! As you may recall, in the previous lesson, we didnt know just what to do when rolling dead on this table. Now we know - we should be calling <code class="docutils literal notranslate"><span class="pre">at_death</span></code> on the character. So lets add that where we had TODOs before:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/rules.py </span>
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>

View file

@ -134,12 +134,7 @@ using a menu when they log in.</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>AUTO_CREATE_CHARACTER_WITH_ACCOUNT = False
</pre></div>
</div>
<p>When doing this, connecting with the game with a new account will land you in “OOC” mode. The
ooc-version of <code class="docutils literal notranslate"><span class="pre">look</span></code> (sitting in the Account cmdset) will show a list of available characters
if you have any. You can also enter <code class="docutils literal notranslate"><span class="pre">charcreate</span></code> to make a new character. The <code class="docutils literal notranslate"><span class="pre">charcreate</span></code> is a
simple command coming with Evennia that just lets you make a new character with a given name and
description. We will later modify that to kick off our chargen. For now well just keep in mind
thats how well start off the menu.</p>
<p>When doing this, connecting with the game with a new account will land you in “OOC” mode. The ooc-version of <code class="docutils literal notranslate"><span class="pre">look</span></code> (sitting in the Account cmdset) will show a list of available characters if you have any. You can also enter <code class="docutils literal notranslate"><span class="pre">charcreate</span></code> to make a new character. The <code class="docutils literal notranslate"><span class="pre">charcreate</span></code> is a simple command coming with Evennia that just lets you make a new character with a given name and description. We will later modify that to kick off our chargen. For now well just keep in mind thats how well start off the menu.</p>
<p>In <em>Knave</em>, most of the character-generation is random. This means this tutorial can be pretty
compact while still showing the basic idea. What we will create is a menu looking like this:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Silas</span>
@ -309,11 +304,8 @@ keep in here.</p>
<span class="p">]</span>
</pre></div>
</div>
<p>Here we have followed the <em>Knave</em> rulebook to randomize abilities, description and equipment.
The <code class="docutils literal notranslate"><span class="pre">dice.roll()</span></code> and <code class="docutils literal notranslate"><span class="pre">dice.roll_random_table</span></code> methods now become very useful! Everything here
should be easy to follow.</p>
<p>The main difference from baseline <em>Knave</em> is that we make a table of “starting weapon” (in Knave
you can pick whatever you like).</p>
<p>Here we have followed the <em>Knave</em> rulebook to randomize abilities, description and equipment. The <code class="docutils literal notranslate"><span class="pre">dice.roll()</span></code> and <code class="docutils literal notranslate"><span class="pre">dice.roll_random_table</span></code> methods now become very useful! Everything here should be easy to follow.</p>
<p>The main difference from baseline <em>Knave</em> is that we make a table of “starting weapon” (in Knave you can pick whatever you like).</p>
<p>We also initialize <code class="docutils literal notranslate"><span class="pre">.ability_changes</span> <span class="pre">=</span> <span class="pre">0</span></code>. Knave only allows us to swap the values of two
Abilities <em>once</em>. We will use this to know if it has been done or not.</p>
<section id="showing-the-sheet">
@ -362,9 +354,7 @@ Abilities <em>once</em>. We will use this to know if it has been done or not.</p
</pre></div>
</div>
<p>The new <code class="docutils literal notranslate"><span class="pre">show_sheet</span></code> method collect the data from the temporary sheet and return it in a pretty
form. Making a template string like <code class="docutils literal notranslate"><span class="pre">_TEMP_SHEET</span></code> makes it easier to change things later if you want
to change how things look.</p>
<p>The new <code class="docutils literal notranslate"><span class="pre">show_sheet</span></code> method collect the data from the temporary sheet and return it in a pretty form. Making a template string like <code class="docutils literal notranslate"><span class="pre">_TEMP_SHEET</span></code> makes it easier to change things later if you want to change how things look.</p>
</section>
<section id="apply-character">
<h3><span class="section-number">6.3.2. </span>Apply character<a class="headerlink" href="#apply-character" title="Permalink to this headline"></a></h3>
@ -421,8 +411,7 @@ This is a bit more involved.</p>
<span class="k">return</span> <span class="n">new_character</span>
</pre></div>
</div>
<p>We use <code class="docutils literal notranslate"><span class="pre">create_object</span></code> to create a new <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code>. We feed it with all relevant data
from the temporary character sheet. This is when these become an actual character.</p>
<p>We use <code class="docutils literal notranslate"><span class="pre">create_object</span></code> to create a new <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code>. We feed it with all relevant data from the temporary character sheet. This is when these become an actual character.</p>
<aside class="sidebar">
<p>A prototype is basically a <code class="docutils literal notranslate"><span class="pre">dict</span></code> describing how the object should be created. Since
its just a piece of code, it can stored in a Python module and used to quickly <em>spawn</em> (create)
@ -431,9 +420,7 @@ things from those prototypes.</p>
<p>Each piece of equipment is an object in in its own right. We will here assume that all game
items are defined as <a class="reference internal" href="../../../Components/Prototypes.html"><span class="doc std std-doc">Prototypes</span></a> keyed to its name, such as “sword”, “brigandine
armor” etc.</p>
<p>We havent actually created those prototypes yet, so for now well need to assume they are there.
Once a piece of equipment has been spawned, we make sure to move it into the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> we
created in the <a class="reference internal" href="Beginner-Tutorial-Equipment.html"><span class="doc std std-doc">Equipment lesson</span></a>.</p>
<p>We havent actually created those prototypes yet, so for now well need to assume they are there. Once a piece of equipment has been spawned, we make sure to move it into the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> we created in the <a class="reference internal" href="Beginner-Tutorial-Equipment.html"><span class="doc std std-doc">Equipment lesson</span></a>.</p>
</section>
</section>
<section id="initializing-evmenu">
@ -468,11 +455,8 @@ created in the <a class="reference internal" href="Beginner-Tutorial-Equipment.h
</div>
<p>This first function is what we will call from elsewhere (for example from a custom <code class="docutils literal notranslate"><span class="pre">charcreate</span></code>
command) to kick the menu into gear.</p>
<p>It takes the <code class="docutils literal notranslate"><span class="pre">caller</span></code> (the one to want to start the menu) and a <code class="docutils literal notranslate"><span class="pre">session</span></code> argument. The latter will help
track just which client-connection we are using (depending on Evennia settings, you could be
connecting with multiple clients).</p>
<p>We create a <code class="docutils literal notranslate"><span class="pre">TemporaryCharacterSheet</span></code> and call <code class="docutils literal notranslate"><span class="pre">.generate()</span></code> to make a random character. We then
feed all this into <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code>.</p>
<p>It takes the <code class="docutils literal notranslate"><span class="pre">caller</span></code> (the one to want to start the menu) and a <code class="docutils literal notranslate"><span class="pre">session</span></code> argument. The latter will help track just which client-connection we are using (depending on Evennia settings, you could be connecting with multiple clients).</p>
<p>We create a <code class="docutils literal notranslate"><span class="pre">TemporaryCharacterSheet</span></code> and call <code class="docutils literal notranslate"><span class="pre">.generate()</span></code> to make a random character. We then feed all this into <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code>.</p>
<p>The moment this happens, the user will be in the menu, there are no further steps needed.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">menutree</span></code> is what well create next. It describes which menu nodes are available to jump
between.</p>
@ -520,12 +504,7 @@ actions.</p>
</div>
<p>A lot to unpack here! In Evennia, its convention to name your node-functions <code class="docutils literal notranslate"><span class="pre">node_*</span></code>. While
not required, it helps you track what is a node and not.</p>
<p>Every menu-node, should accept <code class="docutils literal notranslate"><span class="pre">caller,</span> <span class="pre">raw_string,</span> <span class="pre">**kwargs</span></code> as arguments. Here <code class="docutils literal notranslate"><span class="pre">caller</span></code> is the
<code class="docutils literal notranslate"><span class="pre">caller</span></code> you passed into the <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> call. <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> is the input given by the user in order
to <em>get to this node</em>, so currently empty. The <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> are all extra keyword arguments passed
into <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code>. They can also be passed between nodes. In this case, we passed the
keyword <code class="docutils literal notranslate"><span class="pre">tmp_character</span></code> to <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code>. We now have the temporary character sheet available in the
node!</p>
<p>Every menu-node, should accept <code class="docutils literal notranslate"><span class="pre">caller,</span> <span class="pre">raw_string,</span> <span class="pre">**kwargs</span></code> as arguments. Here <code class="docutils literal notranslate"><span class="pre">caller</span></code> is the <code class="docutils literal notranslate"><span class="pre">caller</span></code> you passed into the <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> call. <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> is the input given by the user in order to <em>get to this node</em>, so currently empty. The <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> are all extra keyword arguments passed into <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code>. They can also be passed between nodes. In this case, we passed the keyword <code class="docutils literal notranslate"><span class="pre">tmp_character</span></code> to <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code>. We now have the temporary character sheet available in the node!</p>
<p>An <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> node must always return two things - <code class="docutils literal notranslate"><span class="pre">text</span></code> and <code class="docutils literal notranslate"><span class="pre">options</span></code>. The <code class="docutils literal notranslate"><span class="pre">text</span></code> is what will
show to the user when looking at this node. The <code class="docutils literal notranslate"><span class="pre">options</span></code> are, well, what options should be
presented to move on from here to some other place.</p>
@ -546,8 +525,7 @@ pass kwargs (as a dict). This will be made available as <code class="docutils li
<p>In our <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code> node, we point to three nodes by name: <code class="docutils literal notranslate"><span class="pre">node_change_name</span></code>,
<code class="docutils literal notranslate"><span class="pre">node_swap_abilities</span></code>, and <code class="docutils literal notranslate"><span class="pre">node_apply_character</span></code>. We also make sure to pass along <code class="docutils literal notranslate"><span class="pre">kwargs</span></code>
to each node, since that contains our temporary character sheet.</p>
<p>The middle of these options only appear if we havent already switched two abilities around - to
know this, we check the <code class="docutils literal notranslate"><span class="pre">.ability_changes</span></code> property to make sure its still 0.</p>
<p>The middle of these options only appear if we havent already switched two abilities around - to know this, we check the <code class="docutils literal notranslate"><span class="pre">.ability_changes</span></code> property to make sure its still 0.</p>
</section>
<section id="node-changing-your-name">
<h2><span class="section-number">6.6. </span>Node: Changing your name<a class="headerlink" href="#node-changing-your-name" title="Permalink to this headline"></a></h2>
@ -595,16 +573,13 @@ know this, we check the <code class="docutils literal notranslate"><span class="
helper <em>goto_function</em> (<code class="docutils literal notranslate"><span class="pre">_update_name</span></code>) to handle the users input.</p>
<p>For the (single) option, we use a special <code class="docutils literal notranslate"><span class="pre">key</span></code> named <code class="docutils literal notranslate"><span class="pre">_default</span></code>. This makes this option
a catch-all: If the user enters something that does not match any other option, this is
the option that will be used.
Since we have no other options here, we will always use this option no matter what the user enters.</p>
the option that will be used. Since we have no other options here, we will always use this option no matter what the user enters.</p>
<p>Also note that the <code class="docutils literal notranslate"><span class="pre">goto</span></code> part of the option points to the <code class="docutils literal notranslate"><span class="pre">_update_name</span></code> callable rather than to
the name of a node. Its important we keep passing <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> along to it!</p>
<p>When a user writes anything at this node, the <code class="docutils literal notranslate"><span class="pre">_update_name</span></code> callable will be called. This has
the same arguments as a node, but it is <em>not</em> a node - we will only use it to <em>figure out</em> which
node to go to next.</p>
<p>In <code class="docutils literal notranslate"><span class="pre">_update_name</span></code> we now have a use for the <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> argument - this is what was written by
the user on the previous node, remember? This is now either an empty string (meaning to ignore
it) or the new name of the character.</p>
<p>In <code class="docutils literal notranslate"><span class="pre">_update_name</span></code> we now have a use for the <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> argument - this is what was written by the user on the previous node, remember? This is now either an empty string (meaning to ignore it) or the new name of the character.</p>
<p>A goto-function like <code class="docutils literal notranslate"><span class="pre">_update_name</span></code> must return the name of the next node to use. It can also
optionally return the <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> to pass into that node - we want to always do this, so we dont
loose our temporary character sheet. Here we will always go back to the <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code>.</p>
@ -698,13 +673,8 @@ node (such as <code class="docutils literal notranslate"><span class="pre">WIS</
<p>In <code class="docutils literal notranslate"><span class="pre">_swap_abilities</span></code>, we need to analyze the <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> from the user to see what they
want to do.</p>
<p>Most code in the helper is validating the user didnt enter nonsense. If they did,
we use <code class="docutils literal notranslate"><span class="pre">caller.msg()</span></code> to tell them and then return <code class="docutils literal notranslate"><span class="pre">None,</span> <span class="pre">kwargs</span></code>, which re-runs the same node (the
name-selection) all over again.</p>
<p>Since we want users to be able to write “CON” instead of the longer “constitution”, we need a
mapping <code class="docutils literal notranslate"><span class="pre">_ABILITIES</span></code> to easily convert between the two (its stored as <code class="docutils literal notranslate"><span class="pre">consitution</span></code> on the
temporary character sheet). Once we know which abilities they want to swap, we do so and tick up
the <code class="docutils literal notranslate"><span class="pre">.ability_changes</span></code> counter. This means this option will no longer be available from the main
node.</p>
we use <code class="docutils literal notranslate"><span class="pre">caller.msg()</span></code> to tell them and then return <code class="docutils literal notranslate"><span class="pre">None,</span> <span class="pre">kwargs</span></code>, which re-runs the same node (the name-selection) all over again.</p>
<p>Since we want users to be able to write “CON” instead of the longer “constitution”, we need a mapping <code class="docutils literal notranslate"><span class="pre">_ABILITIES</span></code> to easily convert between the two (its stored as <code class="docutils literal notranslate"><span class="pre">consitution</span></code> on the temporary character sheet). Once we know which abilities they want to swap, we do so and tick up the <code class="docutils literal notranslate"><span class="pre">.ability_changes</span></code> counter. This means this option will no longer be available from the main node.</p>
<p>Finally, we return to <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code> again.</p>
</section>
<section id="node-creating-the-character">
@ -725,12 +695,8 @@ node.</p>
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="kc">None</span>
</pre></div>
</div>
<p>When entering the node, we will take the Temporary character sheet and use its <code class="docutils literal notranslate"><span class="pre">.appy</span></code> method to
create a new Character with all equipment.</p>
<p>This is what is called an <em>end node</em>, because it returns <code class="docutils literal notranslate"><span class="pre">None</span></code> instead of options. After this,
the menu will exit. We will be back to the default character selection screen. The characters
found on that screen are the ones listed in the <code class="docutils literal notranslate"><span class="pre">_playable_characters</span></code> Attribute, so we need to
also the new character to it.</p>
<p>When entering the node, we will take the Temporary character sheet and use its <code class="docutils literal notranslate"><span class="pre">.appy</span></code> method to create a new Character with all equipment.</p>
<p>This is what is called an <em>end node</em>, because it returns <code class="docutils literal notranslate"><span class="pre">None</span></code> instead of options. After this, the menu will exit. We will be back to the default character selection screen. The characters found on that screen are the ones listed in the <code class="docutils literal notranslate"><span class="pre">_playable_characters</span></code> Attribute, so we need to also the new character to it.</p>
</section>
<section id="tying-the-nodes-together">
<h2><span class="section-number">6.9. </span>Tying the nodes together<a class="headerlink" href="#tying-the-nodes-together" title="Permalink to this headline"></a></h2>
@ -756,17 +722,12 @@ also the new character to it.</p>
</pre></div>
</div>
<p>Now that we have all the nodes, we add them to the <code class="docutils literal notranslate"><span class="pre">menutree</span></code> we left empty before. We only add
the nodes, <em>not</em> the goto-helpers! The keys we set in the <code class="docutils literal notranslate"><span class="pre">menutree</span></code> dictionary are the names we
should use to point to nodes from inside the menu (and we did).</p>
<p>We also add a keyword argument <code class="docutils literal notranslate"><span class="pre">startnode</span></code> pointing to the <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code> node. This tells EvMenu
to first jump into that node when the menu is starting up.</p>
<p>Now that we have all the nodes, we add them to the <code class="docutils literal notranslate"><span class="pre">menutree</span></code> we left empty before. We only add the nodes, <em>not</em> the goto-helpers! The keys we set in the <code class="docutils literal notranslate"><span class="pre">menutree</span></code> dictionary are the names we should use to point to nodes from inside the menu (and we did).</p>
<p>We also add a keyword argument <code class="docutils literal notranslate"><span class="pre">startnode</span></code> pointing to the <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code> node. This tells EvMenu to first jump into that node when the menu is starting up.</p>
</section>
<section id="conclusions">
<h2><span class="section-number">6.10. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline"></a></h2>
<p>This lesson taught us how to use <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> to make an interactive character generator. In an RPG
more complex than <em>Knave</em>, the menu would be bigger and more intricate, but the same principles
apply.</p>
<p>This lesson taught us how to use <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> to make an interactive character generator. In an RPG more complex than <em>Knave</em>, the menu would be bigger and more intricate, but the same principles apply.</p>
<p>Together with the previous lessons we have now fished most of the basics around player
characters - how they store their stats, handle their equipment and how to create them.</p>
<p>In the next lesson well address how EvAdventure <em>Rooms</em> work.</p>

View file

@ -0,0 +1,978 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>9. Combat base framework &#8212; Evennia 1.0 documentation</title>
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<script src="../../../_static/language_data.js"></script>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="10. Twitch Combat" href="Beginner-Tutorial-Combat-Twitch.html" />
<link rel="prev" title="8. Non-Player-Characters" href="Beginner-Tutorial-NPCs.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Combat-Twitch.html" title="10. Twitch Combat"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</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> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">9. </span>Combat base framework</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="../../../index.html">
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">9. Combat base framework</a><ul>
<li><a class="reference internal" href="#combathandler">9.1. CombatHandler</a><ul>
<li><a class="reference internal" href="#combathandler-get-or-create-combathandler">9.1.1. CombatHandler.get_or_create_combathandler</a></li>
<li><a class="reference internal" href="#combathandler-msg">9.1.2. CombatHandler.msg</a></li>
<li><a class="reference internal" href="#combathandler-get-combat-summary">9.1.3. Combathandler.get_combat_summary</a></li>
</ul>
</li>
<li><a class="reference internal" href="#actions">9.2. Actions</a></li>
<li><a class="reference internal" href="#action-dicts">9.3. Action dicts</a></li>
<li><a class="reference internal" href="#action-classes">9.4. Action classes</a><ul>
<li><a class="reference internal" href="#hold-action">9.4.1. Hold Action</a></li>
<li><a class="reference internal" href="#attack-action">9.4.2. Attack Action</a></li>
<li><a class="reference internal" href="#stunt-action">9.4.3. Stunt Action</a></li>
<li><a class="reference internal" href="#use-item-action">9.4.4. Use Item Action</a></li>
<li><a class="reference internal" href="#wield-action">9.4.5. Wield Action</a></li>
</ul>
</li>
<li><a class="reference internal" href="#testing">9.5. Testing</a></li>
<li><a class="reference internal" href="#conclusions">9.6. Conclusions</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-NPCs.html"
title="previous chapter"><span class="section-number">8. </span>Non-Player-Characters</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Combat-Twitch.html"
title="next chapter"><span class="section-number">10. </span>Twitch Combat</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Base.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="Beginner-Tutorial-Combat-Base.html">1.0 (main branch)</a></li>
<ul>
<li><a href="../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="combat-base-framework">
<h1><span class="section-number">9. </span>Combat base framework<a class="headerlink" href="#combat-base-framework" title="Permalink to this headline"></a></h1>
<p>Combat is core to many games. Exactly how it works is very game-dependent. In this lesson we will build a framework to implement two common flavors:</p>
<ul class="simple">
<li><p>“Twitch-based” combat (<a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html"><span class="doc std std-doc">specific lesson here</span></a>) means that you perform a combat action by entering a command, and after some delay (which may depend on your skills etc), the action happens. Its called twitch because actions often happen fast enough that changing your strategy may involve some element of quick thinking and a twitchy trigger finger.</p></li>
<li><p>“Turn-based” combat (<a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html"><span class="doc std std-doc">specific lesson here</span></a>) means that players input actions in clear turns. Timeout for entering/queuing your actions is often much longer than twitch-based style. Once everyone made their choice (or the timeout is reached), everyones action happens all at once, after which the next turn starts. This style of combat requires less player reflexes.</p></li>
</ul>
<p>We will design a base combat system that supports both styles.</p>
<ul class="simple">
<li><p>We need a <code class="docutils literal notranslate"><span class="pre">CombatHandler</span></code> to track the progress of combat. This will be a <a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Script</span></a>. Exactly how this works (and where it is stored) will be a bit different between Twitch- and Turnbased combat. We will create its common framework in this lesson.</p></li>
<li><p>Combat are divided into <em>actions</em>. We want to be able to easily extend our combat with more possible actions. An action needs Python code to show what actually happens when the action is performed. We will define such code in <code class="docutils literal notranslate"><span class="pre">Action</span></code> classes.</p></li>
<li><p>We also need a way to describe a <em>specific instance</em> of a given action. That is, when we do an “attack” action, we need at the minimum to know who is being attacked. For this will we use Python <code class="docutils literal notranslate"><span class="pre">dicts</span></code> that we will refer to as <code class="docutils literal notranslate"><span class="pre">action_dicts</span></code>.</p></li>
</ul>
<section id="combathandler">
<h2><span class="section-number">9.1. </span>CombatHandler<a class="headerlink" href="#combathandler" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/combat_base.py</span></code></p>
</div></blockquote>
<aside class="sidebar">
<p>In <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia-contrib-tutorials-evadventure-combat-base"><span class="std std-ref">evennia/contrib/tutorials/evadventure/combat_base.py</span></a> youll find a complete implementation of the base combat module.</p>
</aside>
<p>Our “Combat Handler” will handle the administration around combat. It needs to be <em>persistent</em> (even is we reload the server your combat should keep going).</p>
<p>Creating the CombatHandler is a little of a catch-22 - how it works depends on how Actions and Action-dicts look. But without having the CombatHandler, its hard to know how to design Actions and Action-dicts. So well start with its general structure and fill out the details later in this lesson.</p>
<p>Below, methods with <code class="docutils literal notranslate"><span class="pre">pass</span></code> will be filled out this lesson while those raising <code class="docutils literal notranslate"><span class="pre">NotImplementedError</span></code> will be different for Twitch/Turnbased combat and will be implemented in their respective lessons following this one.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultScript</span>
<span class="k">class</span> <span class="nc">CombatFailure</span><span class="p">(</span><span class="ne">RuntimeError</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;If some error happens in combat&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">EvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">DefaultSCript</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> This should be created when combat starts. It &#39;ticks&#39; the combat </span>
<span class="sd"> and tracks all sides of it.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># common for all types of combat</span>
<span class="n">action_classes</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># to fill in later </span>
<span class="n">fallback_action_dict</span> <span class="o">=</span> <span class="p">{}</span>
<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">get_or_create_combathandler</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; Get or create combathandler on `obj`.&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">msg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">combatant</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Send a message to all combatants.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">pass</span> <span class="c1"># TODO</span>
<span class="k">def</span> <span class="nf">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Get a nicely formatted &#39;battle report&#39; of combat, from the </span>
<span class="sd"> perspective of the combatant.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">pass</span> <span class="c1"># TODO</span>
<span class="c1"># implemented differently by Twitch- and Turnbased combat</span>
<span class="k">def</span> <span class="nf">get_sides</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Get who&#39;s still alive on the two sides of combat, as a </span>
<span class="sd"> tuple `([allies], [enemies])` from the perspective of `combatant` </span>
<span class="sd"> (who is _not_ included in the `allies` list.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">give_advantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Give advantage to recipient against target.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">give_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Give disadvantage to recipient against target. </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">has_advantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Does combatant have advantage against target?</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">has_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Does combatant have disadvantage against target?</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">queue_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Queue an action for the combatant by providing </span>
<span class="sd"> action dict.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">execute_next_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Perform a combatant&#39;s next action.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">start_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Start combat.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">check_stop_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Check if the combat is over and if it should be stopped.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="k">def</span> <span class="nf">stop_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Stop combat and do cleanup.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
</pre></div>
</div>
<p>The Combat Handler is a <a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Script</span></a>. Scripts are typeclassed entities, which means that they are persistently stored in the database. Scripts can optionally be stored “on” other objects (such as on Characters or Rooms) or be global without any such connection. While Scripts has an optional timer component, it is not active by default and Scripts are commonly used just as plain storage. Since Scripts dont have an in-game existence, they are great for storing data on systems of all kinds, including our combat.</p>
<p>Lets implement the generic methods we need.</p>
<section id="combathandler-get-or-create-combathandler">
<h3><span class="section-number">9.1.1. </span>CombatHandler.get_or_create_combathandler<a class="headerlink" href="#combathandler-get-or-create-combathandler" title="Permalink to this headline"></a></h3>
<p>A helper method for quickly getting the combathandler for an ongoing combat and combatant.</p>
<p>We expect to create the script “on” an object (which one we dont know yet, but we expect it to be a typeclassed entity).</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_script</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">DefaultScript</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">get_or_create_combathandler</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Get or create a combathandler on `obj`.</span>
<span class="sd"> </span>
<span class="sd"> Args:</span>
<span class="sd"> obj (any): The Typeclassed entity to store this Script on. </span>
<span class="sd"> Keyword Args:</span>
<span class="sd"> combathandler_key (str): Identifier for script. &#39;combathandler&#39; by</span>
<span class="sd"> default.</span>
<span class="sd"> **kwargs: Extra arguments to the Script, if it is created.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">obj</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">CombatFailure</span><span class="p">(</span><span class="s2">&quot;Cannot start combat without a place to do it!&quot;</span><span class="p">)</span>
<span class="n">combathandler_key</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">,</span> <span class="s2">&quot;combathandler&quot;</span><span class="p">)</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combathandler</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">combathandler</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">combathandler</span><span class="o">.</span><span class="n">id</span><span class="p">:</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">scripts</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">combathandler_key</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">combathandler</span><span class="p">:</span>
<span class="c1"># have to create from scratch</span>
<span class="n">persistent</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">&quot;persistent&quot;</span><span class="p">,</span> <span class="kc">True</span><span class="p">)</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="n">create_script</span><span class="p">(</span>
<span class="bp">cls</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="n">combathandler_key</span><span class="p">,</span>
<span class="n">obj</span><span class="o">=</span><span class="n">obj</span><span class="p">,</span>
<span class="n">persistent</span><span class="o">=</span><span class="n">persistent</span><span class="p">,</span>
<span class="o">**</span><span class="n">kwargs</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">obj</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combathandler</span>
<span class="k">return</span> <span class="n">combathandler</span>
<span class="c1"># ... </span>
</pre></div>
</div>
<p>This helper method uses <code class="docutils literal notranslate"><span class="pre">obj.scripts.get()</span></code> to find if the combat script already exists on the provided <code class="docutils literal notranslate"><span class="pre">obj</span></code>. If not, it will create it using Evennias <a class="reference internal" href="../../../api/evennia.utils.create.html#evennia.utils.create.create_script" title="evennia.utils.create.create_script"><span class="xref myst py py-func">create_script</span></a> function. For some extra speed we cache the handler as <code class="docutils literal notranslate"><span class="pre">obj.ndb.combathandler</span></code> The <code class="docutils literal notranslate"><span class="pre">.ndb.</span></code> (non-db) means that handler is cached only in memory.</p>
<aside class="sidebar">
<p class="sidebar-title">Checking .id (or .pk)</p>
<p>When getting it from cache, we make sure to also check if the combathandler we got has a database <code class="docutils literal notranslate"><span class="pre">.id</span></code> that is not <code class="docutils literal notranslate"><span class="pre">None</span></code> (we could also check <code class="docutils literal notranslate"><span class="pre">.pk</span></code>, stands for “primary key”) . If its <code class="docutils literal notranslate"><span class="pre">None</span></code>, this means the database entity was deleted and we just got its cached python representation from memory - we need to recreate it.</p>
</aside>
<p><code class="docutils literal notranslate"><span class="pre">get_or_create_combathandler</span></code> is decorated to be a <a class="reference external" href="https://docs.python.org/3/library/functions.html#classmethod">classmethod</a>, meaning it should be used on the handler class directly (rather than on an <em>instance</em> of said class). This makes sense because this method actually should return the new instance.</p>
<p>As a class method well need to call this directly on the class, like this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">EvAdventureCombatBaseHandler</span><span class="o">.</span><span class="n">get_or_create_combathandler</span><span class="p">(</span><span class="n">combatant</span><span class="p">)</span>
</pre></div>
</div>
<p>The result will be a new handler <em>or</em> one that was already defined.</p>
</section>
<section id="combathandler-msg">
<h3><span class="section-number">9.1.2. </span>CombatHandler.msg<a class="headerlink" href="#combathandler-msg" title="Permalink to this headline"></a></h3>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">DefaultScript</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">msg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">combatant</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Central place for sending messages to combatants. This allows</span>
<span class="sd"> for adding any combat-specific text-decoration in one place.</span>
<span class="sd"> Args:</span>
<span class="sd"> message (str): The message to send.</span>
<span class="sd"> combatant (Object): The &#39;You&#39; in the message, if any.</span>
<span class="sd"> broadcast (bool): If `False`, `combatant` must be included and</span>
<span class="sd"> will be the only one to see the message. If `True`, send to</span>
<span class="sd"> everyone in the location.</span>
<span class="sd"> location (Object, optional): If given, use this as the location to</span>
<span class="sd"> send broadcast messages to. If not, use `self.obj` as that</span>
<span class="sd"> location.</span>
<span class="sd"> Notes:</span>
<span class="sd"> If `combatant` is given, use `$You/you()` markup to create</span>
<span class="sd"> a message that looks different depending on who sees it. Use</span>
<span class="sd"> `$You(combatant_key)` to refer to other combatants.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">location</span><span class="p">:</span>
<span class="n">location</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span>
<span class="n">location_objs</span> <span class="o">=</span> <span class="n">location</span><span class="o">.</span><span class="n">contents</span>
<span class="n">exclude</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">broadcast</span> <span class="ow">and</span> <span class="n">combatant</span><span class="p">:</span>
<span class="n">exclude</span> <span class="o">=</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="n">location_objs</span> <span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">combatant</span><span class="p">]</span>
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="n">message</span><span class="p">,</span>
<span class="n">exclude</span><span class="o">=</span><span class="n">exclude</span><span class="p">,</span>
<span class="n">from_obj</span><span class="o">=</span><span class="n">combatant</span><span class="p">,</span>
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">locobj</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">locobj</span> <span class="k">for</span> <span class="n">locobj</span> <span class="ow">in</span> <span class="n">location_objs</span><span class="p">},</span>
<span class="p">)</span>
<span class="c1"># ... </span>
</pre></div>
</div>
<aside class="sidebar">
<p>The <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> property of a Script is the entity on which the Script sits. If set on a Character, <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> will be that Character. If on a room, itd be that room. For a global script, <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> is <code class="docutils literal notranslate"><span class="pre">None</span></code>.</p>
</aside>
<p>We saw the <code class="docutils literal notranslate"><span class="pre">location.msg_contents()</span></code> method before in the <a class="reference internal" href="Beginner-Tutorial-Objects.html#weapons"><span class="std std-doc">Weapon class of the Objects lesson</span></a>. Its purpose is to take a string on the form <code class="docutils literal notranslate"><span class="pre">&quot;$You()</span> <span class="pre">do</span> <span class="pre">stuff</span> <span class="pre">against</span> <span class="pre">$you(key)&quot;</span></code> and make sure all sides see a string suitable just to them. Our <code class="docutils literal notranslate"><span class="pre">msg()</span></code> method will by default broadcast the message to everyone in the room.</p>
<div style="clear: right;"></div>
<p>Youd use it like this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(throw) </span><span class="si">{</span><span class="n">item</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> at $you(</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">).&quot;</span><span class="p">,</span>
<span class="n">combatant</span><span class="o">=</span><span class="n">combatant</span><span class="p">,</span>
<span class="n">location</span><span class="o">=</span><span class="n">combatant</span><span class="o">.</span><span class="n">location</span>
<span class="p">)</span>
</pre></div>
</div>
<p>If combatant is <code class="docutils literal notranslate"><span class="pre">Trickster</span></code>, <code class="docutils literal notranslate"><span class="pre">item.key</span></code> is “a colorful ball” and <code class="docutils literal notranslate"><span class="pre">target.key</span></code> is “Goblin”, then</p>
<p>The combatant would see:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You throw a colorful ball at Goblin.
</pre></div>
</div>
<p>The Goblin sees</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Trickster throws a colorful ball at you.
</pre></div>
</div>
<p>Everyone else in the room sees</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Trickster throws a colorful ball at Goblin.
</pre></div>
</div>
</section>
<section id="combathandler-get-combat-summary">
<h3><span class="section-number">9.1.3. </span>Combathandler.get_combat_summary<a class="headerlink" href="#combathandler-get-combat-summary" title="Permalink to this headline"></a></h3>
<p>We want to be able to show a nice summary of the current combat:</p>
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span><span class="w"> </span>Goblin<span class="w"> </span>shaman<span class="w"> </span><span class="o">(</span>Perfect<span class="o">)</span>
<span class="w"> </span>Gregor<span class="w"> </span><span class="o">(</span>Hurt<span class="o">)</span><span class="w"> </span>Goblin<span class="w"> </span>brawler<span class="o">(</span>Hurt<span class="o">)</span>
<span class="w"> </span>Bob<span class="w"> </span><span class="o">(</span>Perfect<span class="o">)</span><span class="w"> </span>vs<span class="w"> </span>Goblin<span class="w"> </span>grunt<span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="o">(</span>Hurt<span class="o">)</span>
<span class="w"> </span>Goblin<span class="w"> </span>grunt<span class="w"> </span><span class="m">2</span><span class="w"> </span><span class="o">(</span>Perfect<span class="o">)</span>
<span class="w"> </span>Goblin<span class="w"> </span>grunt<span class="w"> </span><span class="m">3</span><span class="w"> </span><span class="o">(</span>Wounded<span class="o">)</span>
</pre></div>
</div>
<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></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/combat_base.py</span>
<span class="c1"># ...</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">EvTable</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">DefaultScript</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
<span class="hll"> <span class="n">allies</span><span class="p">,</span> <span class="n">enemies</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_sides</span><span class="p">(</span><span class="n">combatant</span><span class="p">)</span>
</span> <span class="c1"># we must include outselves at the top of the list (we are not returned from get_sides)</span>
<span class="hll"> <span class="n">allies</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">combatant</span><span class="p">)</span>
</span> <span class="n">nallies</span><span class="p">,</span> <span class="n">nenemies</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">allies</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">enemies</span><span class="p">)</span>
<span class="c1"># prepare colors and hurt-levels</span>
<span class="hll"> <span class="n">allies</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">ally</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">ally</span><span class="o">.</span><span class="n">hurt_level</span><span class="si">}</span><span class="s2">)&quot;</span> <span class="k">for</span> <span class="n">ally</span> <span class="ow">in</span> <span class="n">allies</span><span class="p">]</span>
</span><span class="hll"> <span class="n">enemies</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">enemy</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">enemy</span><span class="o">.</span><span class="n">hurt_level</span><span class="si">}</span><span class="s2">)&quot;</span> <span class="k">for</span> <span class="n">enemy</span> <span class="ow">in</span> <span class="n">enemies</span><span class="p">]</span>
</span>
<span class="c1"># the center column with the &#39;vs&#39;</span>
<span class="n">vs_column</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;&quot;</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="n">nallies</span><span class="p">,</span> <span class="n">nenemies</span><span class="p">))]</span>
<span class="n">vs_column</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">vs_column</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;|wvs|n&quot;</span>
<span class="hll"> <span class="c1"># the two allies / enemies columns should be centered vertically</span>
</span> <span class="n">diff</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="n">nallies</span> <span class="o">-</span> <span class="n">nenemies</span><span class="p">)</span>
<span class="n">top_empty</span> <span class="o">=</span> <span class="n">diff</span> <span class="o">//</span> <span class="mi">2</span>
<span class="n">bot_empty</span> <span class="o">=</span> <span class="n">diff</span> <span class="o">-</span> <span class="n">top_empty</span>
<span class="n">topfill</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;&quot;</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">top_empty</span><span class="p">)]</span>
<span class="n">botfill</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;&quot;</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">bot_empty</span><span class="p">)]</span>
<span class="k">if</span> <span class="n">nallies</span> <span class="o">&gt;=</span> <span class="n">nenemies</span><span class="p">:</span>
<span class="n">enemies</span> <span class="o">=</span> <span class="n">topfill</span> <span class="o">+</span> <span class="n">enemies</span> <span class="o">+</span> <span class="n">botfill</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">allies</span> <span class="o">=</span> <span class="n">topfill</span> <span class="o">+</span> <span class="n">allies</span> <span class="o">+</span> <span class="n">botfill</span>
<span class="c1"># make a table with three columns</span>
<span class="hll"> <span class="k">return</span> <span class="n">evtable</span><span class="o">.</span><span class="n">EvTable</span><span class="p">(</span>
</span> <span class="n">table</span><span class="o">=</span><span class="p">[</span>
<span class="n">evtable</span><span class="o">.</span><span class="n">EvColumn</span><span class="p">(</span><span class="o">*</span><span class="n">allies</span><span class="p">,</span> <span class="n">align</span><span class="o">=</span><span class="s2">&quot;l&quot;</span><span class="p">),</span>
<span class="n">evtable</span><span class="o">.</span><span class="n">EvColumn</span><span class="p">(</span><span class="o">*</span><span class="n">vs_column</span><span class="p">,</span> <span class="n">align</span><span class="o">=</span><span class="s2">&quot;c&quot;</span><span class="p">),</span>
<span class="n">evtable</span><span class="o">.</span><span class="n">EvColumn</span><span class="p">(</span><span class="o">*</span><span class="n">enemies</span><span class="p">,</span> <span class="n">align</span><span class="o">=</span><span class="s2">&quot;r&quot;</span><span class="p">),</span>
<span class="p">],</span>
<span class="n">border</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">maxwidth</span><span class="o">=</span><span class="mi">78</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># ... </span>
</pre></div></td></tr></table></div>
</div>
<p>This may look complex, but the complexity is only in figuring out how to organize three columns, especially how to to adjust to the two sides on each side of the <code class="docutils literal notranslate"><span class="pre">vs</span></code> are roughly vertically aligned.</p>
<ul class="simple">
<li><p><strong>Line 15</strong> : We make use of the <code class="docutils literal notranslate"><span class="pre">self.get_sides(combatant)</span></code> method which we havent actually implemented yet. This is because turn-based and twitch-based combat will need different ways to find out what the sides are. The <code class="docutils literal notranslate"><span class="pre">allies</span></code> and <code class="docutils literal notranslate"><span class="pre">enemies</span></code> are lists.</p></li>
<li><p><strong>Line 17</strong>: The <code class="docutils literal notranslate"><span class="pre">combatant</span></code> is not a part of the <code class="docutils literal notranslate"><span class="pre">allies</span></code> list (this is how we defined <code class="docutils literal notranslate"><span class="pre">get_sides</span></code> to work), so we insert it at the top of the list (so they show first on the left-hand side).</p></li>
<li><p><strong>Lines 21, 22</strong>: We make use of the <code class="docutils literal notranslate"><span class="pre">.hurt_level</span></code> values of all living things (see the <a class="reference internal" href="Beginner-Tutorial-Characters.html"><span class="doc std std-doc">LivingMixin of the Character lesson</span></a>).</p></li>
<li><p><strong>Lines 28-39</strong>: We determine how to vertically center the two sides by adding empty lines above and below the content.</p></li>
<li><p><strong>Line 41</strong>: The <a class="reference internal" href="../../../Components/EvTable.html"><span class="doc std std-doc">Evtable</span></a> is an Evennia utility for making, well, text tables. Once we are happy with the columns, we feed them to the table and let Evennia do the rest. Its worth to explore <code class="docutils literal notranslate"><span class="pre">EvTable</span></code> since it can help you create all sorts of nice layouts.</p></li>
</ul>
</section>
</section>
<section id="actions">
<h2><span class="section-number">9.2. </span>Actions<a class="headerlink" href="#actions" title="Permalink to this headline"></a></h2>
<p>In EvAdventure we will only support a few common combat actions, mapping to the equivalent rolls and checks used in <em>Knave</em>. We will design our combat framework so that its easy to expand with other actions later.</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">hold</span></code> - The simplest action. You just lean back and do nothing.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">attack</span></code> - You attack a given <code class="docutils literal notranslate"><span class="pre">target</span></code> using your currently equipped weapon. This will become a roll of STR or WIS against the targets ARMOR.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">stunt</span></code> - You make a stunt, which in roleplaying terms would mean you tripping your opponent, taunting or otherwise trying to gain the upper hand without hurting them. You can do this to give yourself (or an ally) <em>advantage</em> against a <code class="docutils literal notranslate"><span class="pre">target</span></code> on the next action. You can also give a <code class="docutils literal notranslate"><span class="pre">target</span></code> <em>disadvantage</em> against you or an ally for their next action.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">use</span> <span class="pre">item</span></code> - You make use of a <code class="docutils literal notranslate"><span class="pre">Consumable</span></code> in your inventory. When used on yourself, itd normally be something like a healing potion. If used on an enemy it could be a firebomb or a bottle of acid.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">wield</span></code> - You wield an item. Depending on what is being wielded, it will be wielded in different ways: A helmet will be placed on the head, a piece of armor on the chest. A sword will be wielded in one hand, a shield in another. A two-handed axe will use up two hands. Doing so will move whatever was there previously to the backpack.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">flee</span></code> - You run away/disengage. This action is only applicable in turn-based combat (in twitch-based combat you just move to another room to flee). We will thus wait to define this action until the <a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html"><span class="doc std std-doc">Turnbased combat lesson</span></a>.</p></li>
</ul>
</section>
<section id="action-dicts">
<h2><span class="section-number">9.3. </span>Action dicts<a class="headerlink" href="#action-dicts" title="Permalink to this headline"></a></h2>
<p>To pass around the details of an attack (the second point above), we will use a <code class="docutils literal notranslate"><span class="pre">dict</span></code>. A <code class="docutils literal notranslate"><span class="pre">dict</span></code> is simple and also easy to save in an <code class="docutils literal notranslate"><span class="pre">Attribute</span></code>. Well call this the <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> and heres what we need for each action.</p>
<blockquote>
<div><p>You dont need to type these out anywhere, its listed here for reference. We will use these dicts when calling <code class="docutils literal notranslate"><span class="pre">combathandler.queue_action(combatant,</span> <span class="pre">action_dict)</span></code>.</p>
</div></blockquote>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">hold_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span>
<span class="p">}</span>
<span class="n">attack_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;attack&quot;</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Character</span><span class="o">/</span><span class="n">NPC</span><span class="o">&gt;</span>
<span class="p">}</span>
<span class="n">stunt_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;stunt&quot;</span><span class="p">,</span>
<span class="s2">&quot;recipient&quot;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Character</span><span class="o">/</span><span class="n">NPC</span><span class="o">&gt;</span><span class="p">,</span> <span class="c1"># who gains advantage/disadvantage</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Character</span><span class="o">/</span><span class="n">NPC</span><span class="o">&gt;</span><span class="p">,</span> <span class="c1"># who the recipient gainst adv/dis against</span>
<span class="s2">&quot;advantage&quot;</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span> <span class="c1"># grant advantage or disadvantage?</span>
<span class="s2">&quot;stunt_type&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="p">,</span> <span class="c1"># Ability to use for the challenge</span>
<span class="s2">&quot;defense_type&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="p">,</span> <span class="c1"># what Ability for recipient to defend with if we</span>
<span class="c1"># are trying to give disadvantage </span>
<span class="p">}</span>
<span class="n">use_item_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;use&quot;</span><span class="p">,</span>
<span class="s2">&quot;item&quot;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Object</span><span class="o">&gt;</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Character</span><span class="o">/</span><span class="n">NPC</span><span class="o">/</span><span class="kc">None</span><span class="o">&gt;</span> <span class="c1"># if using item against someone else </span>
<span class="p">}</span>
<span class="n">wield_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;wield&quot;</span><span class="p">,</span>
<span class="s2">&quot;item&quot;</span><span class="p">:</span> <span class="o">&lt;</span><span class="n">Object</span><span class="o">&gt;</span>
<span class="p">}</span>
<span class="c1"># used only for the turnbased combat, so its Action will be defined there</span>
<span class="n">flee_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;flee&quot;</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Apart from the <code class="docutils literal notranslate"><span class="pre">stunt</span></code> action, these dicts are all pretty simple. The <code class="docutils literal notranslate"><span class="pre">key</span></code> identifes the action to perform and the other fields identifies the minimum things you need to know in order to resolve each action.</p>
<p>We have not yet written the code to set these dicts, but we will assume that we know who is performing each of these actions. So if <code class="docutils literal notranslate"><span class="pre">Beowulf</span></code> attacks <code class="docutils literal notranslate"><span class="pre">Grendel</span></code>, Beowulf is not himself included in the attack dict:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">attack_action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;attack&quot;</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="n">Grendel</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Lets explain the longest action dict, the <code class="docutils literal notranslate"><span class="pre">Stunt</span></code> action dict in more detail as well. In this example, The <code class="docutils literal notranslate"><span class="pre">Trickster</span></code> is performing a <em>Stunt</em> in order to help his friend <code class="docutils literal notranslate"><span class="pre">Paladin</span></code> to gain an INT- <em>advantage</em> against the <code class="docutils literal notranslate"><span class="pre">Goblin</span></code> (maybe the paladin is preparing to cast a spell of something). Since <code class="docutils literal notranslate"><span class="pre">Trickster</span></code> is doing the action, hes not showing up in the dict:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">stunt_action_dict</span> <span class="o">-</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;stunt&quot;</span><span class="p">,</span>
<span class="s2">&quot;recipient&quot;</span><span class="p">:</span> <span class="n">Paladin</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="n">Goblin</span><span class="p">,</span>
<span class="s2">&quot;advantage&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;stunt_type&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">INT</span><span class="p">,</span>
<span class="s2">&quot;defense_type&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">INT</span><span class="p">,</span>
<span class="p">}</span>
</pre></div>
</div>
<aside class="sidebar">
<p>In EvAdventure, well always set <code class="docutils literal notranslate"><span class="pre">stunt_type</span> <span class="pre">==</span> <span class="pre">defense_type</span></code> for simplicity. But you could also consider mixing things up so you could use DEX to confuse someone and give them INT disadvantage, for example.</p>
</aside>
<p>This should result in an INT vs INT based check between the <code class="docutils literal notranslate"><span class="pre">Trickster</span></code> and the <code class="docutils literal notranslate"><span class="pre">Goblin</span></code> (maybe the trickster is trying to confuse the goblin with some clever word play). If the <code class="docutils literal notranslate"><span class="pre">Trickster</span></code> wins, the <code class="docutils literal notranslate"><span class="pre">Paladin</span></code> gains advantage against the Goblin on the <code class="docutils literal notranslate"><span class="pre">Paladin</span></code>s next action .</p>
</section>
<section id="action-classes">
<h2><span class="section-number">9.4. </span>Action classes<a class="headerlink" href="#action-classes" title="Permalink to this headline"></a></h2>
<p>Once our <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> identifies the particular action we should use, we need something that reads those keys/values and actually <em>performs</em> the action.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="k">class</span> <span class="nc">CombatAction</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combathandler</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combathandler</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="o">=</span> <span class="n">combatant</span>
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">action_dict</span><span class="o">.</span><span class="n">items</span><span class="p">();</span>
<span class="k">if</span> <span class="n">key</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;_&quot;</span><span class="p">):</span>
<span class="nb">setattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>
</pre></div>
</div>
<p>We will create a new instance of this class <em>every time an action is happening</em>. So we store some key things every action will need - we will need a reference to the common <code class="docutils literal notranslate"><span class="pre">combathandler</span></code> (which we will design in the next section), and to the <code class="docutils literal notranslate"><span class="pre">combatant</span></code> (the one performing this action). The <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> is a dict matching the action we want to perform.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">setattr</span></code> Python standard function assigns the keys/values of the <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> to be properties “on” this action. This is very convenient to use in other methods. So for the <code class="docutils literal notranslate"><span class="pre">stunt</span></code> action, other methods could just access <code class="docutils literal notranslate"><span class="pre">self.key</span></code>, <code class="docutils literal notranslate"><span class="pre">self.recipient</span></code>, <code class="docutils literal notranslate"><span class="pre">self.target</span></code> and so on directly.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="k">class</span> <span class="nc">CombatAction</span><span class="p">:</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">msg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
<span class="s2">&quot;Send message to others in combat&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">combatant</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="n">broadcast</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">can_use</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Return False if combatant can&#39;s use this action right now&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="kc">True</span>
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Does the actional action&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">post_execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called after `execute`&quot;&quot;&quot;</span>
<span class="k">pass</span>
</pre></div>
</div>
<p>Its <em>very</em> common to want to send messages to everyone in combat - you need to tell people they are getting attacked, if they get hurt and so on. So having a <code class="docutils literal notranslate"><span class="pre">msg</span></code> helper method on the action is convenient. We offload all the complexity to the combathandler.msg() method.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">can_use</span></code>, <code class="docutils literal notranslate"><span class="pre">execute</span></code> and <code class="docutils literal notranslate"><span class="pre">post_execute</span></code> should all be called in a chain and we should make sure the <code class="docutils literal notranslate"><span class="pre">combathandler</span></code> calls them like this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">action</span><span class="o">.</span><span class="n">can_use</span><span class="p">():</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
<span class="n">action</span><span class="o">.</span><span class="n">post_execute</span><span class="p">()</span>
</pre></div>
</div>
<section id="hold-action">
<h3><span class="section-number">9.4.1. </span>Hold Action<a class="headerlink" href="#hold-action" title="Permalink to this headline"></a></h3>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">CombatActionHold</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; </span>
<span class="sd"> Action that does nothing </span>
<span class="sd"> </span>
<span class="sd"> action_dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;hold&quot;</span>
<span class="sd"> }</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>
</div>
<p>Holding does nothing but its cleaner to nevertheless have a separate class for it. We use the docstring to specify how its action-dict should look.</p>
</section>
<section id="attack-action">
<h3><span class="section-number">9.4.2. </span>Attack Action<a class="headerlink" href="#attack-action" title="Permalink to this headline"></a></h3>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">CombatActionAttack</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> A regular attack, using a wielded weapon.</span>
<span class="sd"> </span>
<span class="sd"> action-dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;attack&quot;,</span>
<span class="sd"> &quot;target&quot;: Character/Object</span>
<span class="sd"> }</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">attacker</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="n">weapon</span> <span class="o">=</span> <span class="n">attacker</span><span class="o">.</span><span class="n">weapon</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span>
<span class="k">if</span> <span class="n">weapon</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="n">weapon</span><span class="o">.</span><span class="n">use</span><span class="p">(</span>
<span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="n">advantage</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
<span class="p">)</span>
<span class="n">weapon</span><span class="o">.</span><span class="n">at_post_use</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
</pre></div>
</div>
<p>Refer to how we <a class="reference internal" href="Beginner-Tutorial-Objects.html#weapons"><span class="std std-doc">designed Evadventure weapons</span></a> to understand what happens here - most of the work is performed by the weapon class - we just plug in the relevant arguments.</p>
</section>
<section id="stunt-action">
<h3><span class="section-number">9.4.3. </span>Stunt Action<a class="headerlink" href="#stunt-action" title="Permalink to this headline"></a></h3>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">CombatActionStunt</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Perform a stunt the grants a beneficiary (can be self) advantage on their next action against a </span>
<span class="sd"> target. Whenever performing a stunt that would affect another negatively (giving them</span>
<span class="sd"> disadvantage against an ally, or granting an advantage against them, we need to make a check</span>
<span class="sd"> first. We don&#39;t do a check if giving an advantage to an ally or ourselves.</span>
<span class="sd"> action_dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;stunt&quot;,</span>
<span class="sd"> &quot;recipient&quot;: Character/NPC,</span>
<span class="sd"> &quot;target&quot;: Character/NPC,</span>
<span class="sd"> &quot;advantage&quot;: bool, # if False, it&#39;s a disadvantage</span>
<span class="sd"> &quot;stunt_type&quot;: Ability, # what ability (like STR, DEX etc) to use to perform this stunt. </span>
<span class="sd"> &quot;defense_type&quot;: Ability, # what ability to use to defend against (negative) effects of</span>
<span class="sd"> this stunt.</span>
<span class="sd"> }</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span>
<span class="n">attacker</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="n">recipient</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">recipient</span> <span class="c1"># the one to receive the effect of the stunt</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span> <span class="c1"># the affected by the stunt (can be the same as recipient/combatant)</span>
<span class="n">txt</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
<span class="k">if</span> <span class="n">recipient</span> <span class="o">==</span> <span class="n">target</span><span class="p">:</span>
<span class="c1"># grant another entity dis/advantage against themselves</span>
<span class="n">defender</span> <span class="o">=</span> <span class="n">recipient</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># recipient not same as target; who will defend depends on disadvantage or advantage</span>
<span class="c1"># to give.</span>
<span class="n">defender</span> <span class="o">=</span> <span class="n">target</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">advantage</span> <span class="k">else</span> <span class="n">recipient</span>
<span class="c1"># trying to give advantage to recipient against target. Target defends against caller</span>
<span class="n">is_success</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">txt</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">dice</span><span class="o">.</span><span class="n">opposed_saving_throw</span><span class="p">(</span>
<span class="n">attacker</span><span class="p">,</span>
<span class="n">defender</span><span class="p">,</span>
<span class="n">attack_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">stunt_type</span><span class="p">,</span>
<span class="n">defense_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">defense_type</span><span class="p">,</span>
<span class="n">advantage</span><span class="o">=</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">defender</span><span class="p">),</span>
<span class="n">disadvantage</span><span class="o">=</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_disadvantage</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">defender</span><span class="p">),</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;$You() $conj(attempt) stunt on $You(</span><span class="si">{</span><span class="n">defender</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">). </span><span class="si">{</span><span class="n">txt</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="c1"># deal with results</span>
<span class="k">if</span> <span class="n">is_success</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">advantage</span><span class="p">:</span>
<span class="n">combathandler</span><span class="o">.</span><span class="n">give_advantage</span><span class="p">(</span><span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">combathandler</span><span class="o">.</span><span class="n">give_disadvantage</span><span class="p">(</span><span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
<span class="k">if</span> <span class="n">recipient</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(gain) </span><span class="si">{</span><span class="s1">&#39;advantage&#39;</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="bp">self</span><span class="o">.</span><span class="n">advantage</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="s1">&#39;disadvantage&#39;</span><span class="si">}</span><span class="s2"> &quot;</span>
<span class="sa">f</span><span class="s2">&quot;against $You(</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">)!&quot;</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(cause) $You(</span><span class="si">{</span><span class="n">recipient</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) &quot;</span>
<span class="sa">f</span><span class="s2">&quot;to gain </span><span class="si">{</span><span class="s1">&#39;advantage&#39;</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="bp">self</span><span class="o">.</span><span class="n">advantage</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="s1">&#39;disadvantage&#39;</span><span class="si">}</span><span class="s2"> &quot;</span>
<span class="sa">f</span><span class="s2">&quot;against $You(</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">)!&quot;</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="s2">&quot;|yHaving succeeded, you hold back to plan your next move.|n [hold]&quot;</span><span class="p">,</span>
<span class="n">broadcast</span><span class="o">=</span><span class="kc">False</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">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;$You(</span><span class="si">{</span><span class="n">defender</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) $conj(resist)! $You() $conj(fail) the stunt.&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>The main action here is the call to the <code class="docutils literal notranslate"><span class="pre">rules.dice.opposed_saving_throw</span></code> to determine if the stunt succeeds. After that, most lines is about figuring out who should be given advantage/disadvantage and to communicate the result to the affected parties.</p>
<p>Note that we make heavy use of the helper methods on the <code class="docutils literal notranslate"><span class="pre">combathandler</span></code> here, even those that are not yet implemented. As long as we pass the <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> into the <code class="docutils literal notranslate"><span class="pre">combathandler</span></code>, the action doesnt actually care what happens next.</p>
<p>After we have performed a successful stunt, we queue the <code class="docutils literal notranslate"><span class="pre">combathandler.fallback_action_dict</span></code>. This is because stunts are meant to be one-off things are if we are repeating actions, it would not make sense to repeat the stunt over and over.</p>
</section>
<section id="use-item-action">
<h3><span class="section-number">9.4.4. </span>Use Item Action<a class="headerlink" href="#use-item-action" title="Permalink to this headline"></a></h3>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">CombatActionUseItem</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Use an item in combat. This is meant for one-off or limited-use items (so things like scrolls and potions, not swords and shields). If this is some sort of weapon or spell rune, we refer to the item to determine what to use for attack/defense rolls.</span>
<span class="sd"> action_dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;use&quot;,</span>
<span class="sd"> &quot;item&quot;: Object</span>
<span class="sd"> &quot;target&quot;: Character/NPC/Object/None</span>
<span class="sd"> }</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">item</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">item</span>
<span class="n">user</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</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="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="n">item</span><span class="o">.</span><span class="n">use</span><span class="p">(</span>
<span class="n">user</span><span class="p">,</span>
<span class="n">target</span><span class="p">,</span>
<span class="n">advantage</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">),</span>
<span class="n">disadvantage</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_disadvantage</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">),</span>
<span class="p">)</span>
<span class="n">item</span><span class="o">.</span><span class="n">at_post_use</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
</pre></div>
</div>
<p>See the <a class="reference internal" href="Beginner-Tutorial-Objects.html"><span class="doc std std-doc">Consumable items in the Object lesson</span></a> to see how consumables work. Like with weapons, we offload all the logic to the item we use.</p>
</section>
<section id="wield-action">
<h3><span class="section-number">9.4.5. </span>Wield Action<a class="headerlink" href="#wield-action" title="Permalink to this headline"></a></h3>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">CombatActionWield</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Wield a new weapon (or spell) from your inventory. This will </span>
<span class="sd"> swap out the one you are currently wielding, if any.</span>
<span class="sd"> action_dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;wield&quot;,</span>
<span class="sd"> &quot;item&quot;: Object</span>
<span class="sd"> }</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">execute</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">combatant</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">item</span><span class="p">)</span>
</pre></div>
</div>
<p>We rely on the <a class="reference internal" href="Beginner-Tutorial-Equipment.html"><span class="doc std std-doc">Equipment handler</span></a> we created to handle the swapping of items for us. Since it doesnt make sense to keep swapping over and over, we queue the fallback action after this one.</p>
</section>
</section>
<section id="testing">
<h2><span class="section-number">9.5. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a module <code class="docutils literal notranslate"><span class="pre">evadventure/tests/test_combat.py</span></code>.</p>
</div></blockquote>
<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 ready-made combat unit tests.</p>
</aside>
<p>Unit testing the combat base classes can seem impossible because we have not yet implemented most of it. We can however get very far by the use of <a class="reference external" href="https://docs.python.org/3/library/unittest.mock.html">Mocks</a>. The idea of a Mock is that you <em>replace</em> a piece of code with a dummy object (a mock) that can be called to return some specific value.</p>
<p>For example, consider this following test of the <code class="docutils literal notranslate"><span class="pre">CombatHandler.get_combat_summary</span></code>. We cant just call this because it internally calls <code class="docutils literal notranslate"><span class="pre">.get_sides</span></code> which would raise a <code class="docutils literal notranslate"><span class="pre">NotImplementedError</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></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/tests/test_combat.py </span>
<span class="kn">from</span> <span class="nn">unittest.mock</span> <span class="kn">import</span> <span class="n">Mock</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTestCase</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_object</span>
<span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">combat_base</span>
<span class="kn">from</span> <span class="nn">..rooms</span> <span class="kn">import</span> <span class="n">EvAdventureRoom</span>
<span class="kn">from</span> <span class="nn">..characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
<span class="k">class</span> <span class="nc">TestEvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">EvenniaTestCase</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">location</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;testroom&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;testchar&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureMob</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;testmonster&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># do the test from perspective of combatant</span>
<span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_sides</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="p">([],</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">]))</span>
</span> <span class="n">result</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span>
<span class="n">result</span><span class="p">,</span>
<span class="s2">&quot; testchar (Perfect) vs testmonster (Perfect)&quot;</span>
<span class="p">)</span>
<span class="c1"># test from the perspective of the monster </span>
<span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_sides</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="p">([],</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">]))</span>
</span> <span class="n">result</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span>
<span class="n">result</span><span class="p">,</span>
<span class="s2">&quot; testmonster (Perfect) vs testchar (Perfect)&quot;</span>
<span class="p">)</span>
</pre></div></td></tr></table></div>
</div>
<p>The interesting places are where we apply the mocks:</p>
<ul class="simple">
<li><p><strong>Line 25</strong> and <strong>Line 32</strong>: While <code class="docutils literal notranslate"><span class="pre">get_sides</span></code> is not implemented yet, we know what it is <em>supposed</em> to return - a tuple of lists. So for the sake of the test, we <em>replace</em> the <code class="docutils literal notranslate"><span class="pre">get_sides</span></code> method with a mock that when called will return something useful.</p></li>
</ul>
<p>With this kind of approach its possible to fully test a system also when its not complete yet.</p>
</section>
<section id="conclusions">
<h2><span class="section-number">9.6. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline"></a></h2>
<p>We have the core functionality we need for our combat system! In the following two lessons we will make use of these building blocks to create two styles of combat.</p>
</section>
</section>
</div>
</div>
</div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Combat-Twitch.html" title="10. Twitch Combat"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How we get there (example game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">9. </span>Combat base framework</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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>12. In-game Commands &#8212; Evennia 1.0 documentation</title>
<title>16. In-game Commands &#8212; Evennia 1.0 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>
@ -18,7 +18,7 @@
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="Part 4: Using what we created" href="../Part4/Beginner-Tutorial-Part4-Overview.html" />
<link rel="prev" title="11. Dynamically generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
<link rel="prev" title="15. In-game Shops" href="Beginner-Tutorial-Shops.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
@ -33,13 +33,13 @@
<a href="../Part4/Beginner-Tutorial-Part4-Overview.html" title="Part 4: Using what we created"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Dungeon.html" title="11. Dynamically generated Dungeon"
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</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> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">12. </span>In-game Commands</a></li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">16. </span>In-game Commands</a></li>
</ul>
</div>
@ -62,8 +62,8 @@
</div>
<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">11. </span>Dynamically generated Dungeon</a></p>
<p class="topless"><a href="Beginner-Tutorial-Shops.html"
title="previous chapter"><span class="section-number">15. </span>In-game Shops</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="../Part4/Beginner-Tutorial-Part4-Overview.html"
title="next chapter">Part 4: Using what we created</a></p>
@ -99,7 +99,7 @@
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="in-game-commands">
<h1><span class="section-number">12. </span>In-game Commands<a class="headerlink" href="#in-game-commands" title="Permalink to this headline"></a></h1>
<h1><span class="section-number">16. </span>In-game Commands<a class="headerlink" href="#in-game-commands" title="Permalink to this headline"></a></h1>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>This part of the Beginner tutorial is still being developed.</p>
@ -125,13 +125,13 @@
<a href="../Part4/Beginner-Tutorial-Part4-Overview.html" title="Part 4: Using what we created"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Dungeon.html" title="11. Dynamically generated Dungeon"
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How we get there (example game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">12. </span>In-game Commands</a></li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">16. </span>In-game Commands</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">

View file

@ -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>11. Dynamically generated Dungeon &#8212; Evennia 1.0 documentation</title>
<title>13. Dynamically generated Dungeon &#8212; Evennia 1.0 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>
@ -17,8 +17,8 @@
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="12. In-game Commands" href="Beginner-Tutorial-Commands.html" />
<link rel="prev" title="10. In-game Shops" href="Beginner-Tutorial-Shops.html" />
<link rel="next" title="14. Game Quests" href="Beginner-Tutorial-Quests.html" />
<link rel="prev" title="12. NPC and monster AI" href="Beginner-Tutorial-AI.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
@ -30,16 +30,16 @@
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Commands.html" title="12. In-game Commands"
<a href="Beginner-Tutorial-Quests.html" title="14. Game Quests"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Shops.html" title="10. In-game Shops"
<a href="Beginner-Tutorial-AI.html" title="12. NPC and monster AI"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</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> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">11. </span>Dynamically generated Dungeon</a></li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">13. </span>Dynamically generated Dungeon</a></li>
</ul>
</div>
@ -62,11 +62,11 @@
</div>
<script>$('#searchbox').show(0);</script>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Shops.html"
title="previous chapter"><span class="section-number">10. </span>In-game Shops</a></p>
<p class="topless"><a href="Beginner-Tutorial-AI.html"
title="previous chapter"><span class="section-number">12. </span>NPC and monster AI</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Commands.html"
title="next chapter"><span class="section-number">12. </span>In-game Commands</a></p>
<p class="topless"><a href="Beginner-Tutorial-Quests.html"
title="next chapter"><span class="section-number">14. </span>Game Quests</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
@ -99,7 +99,7 @@
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="dynamically-generated-dungeon">
<h1><span class="section-number">11. </span>Dynamically generated Dungeon<a class="headerlink" href="#dynamically-generated-dungeon" title="Permalink to this headline"></a></h1>
<h1><span class="section-number">13. </span>Dynamically generated Dungeon<a class="headerlink" href="#dynamically-generated-dungeon" title="Permalink to this headline"></a></h1>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>This part of the Beginner tutorial is still being developed.</p>
@ -122,16 +122,16 @@
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Commands.html" title="12. In-game Commands"
<a href="Beginner-Tutorial-Quests.html" title="14. Game Quests"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Shops.html" title="10. In-game Shops"
<a href="Beginner-Tutorial-AI.html" title="12. NPC and monster AI"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How we get there (example game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">11. </span>Dynamically generated Dungeon</a></li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">13. </span>Dynamically generated Dungeon</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">

View file

@ -123,11 +123,8 @@
<section class="tex2jax_ignore mathjax_ignore" id="handling-equipment">
<h1><span class="section-number">5. </span>Handling Equipment<a class="headerlink" href="#handling-equipment" title="Permalink to this headline"></a></h1>
<p>In <em>Knave</em>, you have a certain number of inventory “slots”. The amount of slots is given by <code class="docutils literal notranslate"><span class="pre">CON</span> <span class="pre">+</span> <span class="pre">10</span></code>.
All items (except coins) have a <code class="docutils literal notranslate"><span class="pre">size</span></code>, indicating how many slots it uses. You cant carry more items
than you have slot-space for. Also items wielded or worn count towards the slots.</p>
<p>We still need to track what the character is using however: What weapon they have readied affects the damage
they can do. The shield, helmet and armor they use affects their defense.</p>
<p>In <em>Knave</em>, you have a certain number of inventory “slots”. The amount of slots is given by <code class="docutils literal notranslate"><span class="pre">CON</span> <span class="pre">+</span> <span class="pre">10</span></code>. All items (except coins) have a <code class="docutils literal notranslate"><span class="pre">size</span></code>, indicating how many slots it uses. You cant carry more items than you have slot-space for. Also items wielded or worn count towards the slots.</p>
<p>We still need to track what the character is using however: What weapon they have readied affects the damage they can do. The shield, helmet and armor they use affects their defense.</p>
<p>We have already set up the possible wear/wield locations when we defined our Objects
<a class="reference internal" href="Beginner-Tutorial-Objects.html"><span class="doc std std-doc">in the previous lesson</span></a>. This is what we have in <code class="docutils literal notranslate"><span class="pre">enums.py</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/enums.py</span>
@ -144,8 +141,7 @@ they can do. The shield, helmet and armor they use affects their defense.</p>
<span class="n">HEAD</span> <span class="o">=</span> <span class="s2">&quot;head&quot;</span> <span class="c1"># helmets</span>
</pre></div>
</div>
<p>Basically, all the weapon/armor locations are exclusive - you can only have one item in each (or none).
The BACKPACK is special - it contains any number of items (up to the maximum slot usage).</p>
<p>Basically, all the weapon/armor locations are exclusive - you can only have one item in each (or none). The BACKPACK is special - it contains any number of items (up to the maximum slot usage).</p>
<section id="equipmenthandler-that-saves">
<h2><span class="section-number">5.1. </span>EquipmentHandler that saves<a class="headerlink" href="#equipmenthandler-that-saves" title="Permalink to this headline"></a></h2>
<blockquote>
@ -155,11 +151,8 @@ The BACKPACK is special - it contains any number of items (up to the maximum slo
<p>If you want to understand more about behind how Evennia uses handlers, there is a
<a class="reference internal" href="../../Tutorial-Persistent-Handler.html"><span class="doc std std-doc">dedicated tutorial</span></a> talking about the principle.</p>
</aside>
<p>In default Evennia, everything you pick up will end up “inside” your character object (that is, have
you as its <code class="docutils literal notranslate"><span class="pre">.location</span></code>). This is called your <em>inventory</em> and has no limit. We will keep moving items into us
when we pick them up, but we will add more functionality using an <em>Equipment handler</em>.</p>
<p>A handler is (for our purposes) an object that sits “on” another entity, containing functionality
for doing one specific thing (managing equipment, in our case).</p>
<p>In default Evennia, everything you pick up will end up “inside” your character object (that is, have you as its <code class="docutils literal notranslate"><span class="pre">.location</span></code>). This is called your <em>inventory</em> and has no limit. We will keep moving items into us when we pick them up, but we will add more functionality using an <em>Equipment handler</em>.</p>
<p>A handler is (for our purposes) an object that sits “on” another entity, containing functionality for doing one specific thing (managing equipment, in our case).</p>
<p>This is the start of our handler:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/equipment.py </span>
@ -217,30 +210,18 @@ we will add it to the Character:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>character.equipment
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">&#64;lazy_property</span></code> works such that it will not load the handler until someone actually tries to
fetch it with <code class="docutils literal notranslate"><span class="pre">character.equipment</span></code>. When that
happens, we start up the handler and feed it <code class="docutils literal notranslate"><span class="pre">self</span></code> (the <code class="docutils literal notranslate"><span class="pre">Character</span></code> instance itself). This is what
enters <code class="docutils literal notranslate"><span class="pre">__init__</span></code> as <code class="docutils literal notranslate"><span class="pre">.obj</span></code> in the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> code above.</p>
<p>So we now have a handler on the character, and the handler has a back-reference to the character it sits
on.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">&#64;lazy_property</span></code> works such that it will not load the handler until someone actually tries to fetch it with <code class="docutils literal notranslate"><span class="pre">character.equipment</span></code>. When that happens, we start up the handler and feed it <code class="docutils literal notranslate"><span class="pre">self</span></code> (the <code class="docutils literal notranslate"><span class="pre">Character</span></code> instance itself). This is what enters <code class="docutils literal notranslate"><span class="pre">__init__</span></code> as <code class="docutils literal notranslate"><span class="pre">.obj</span></code> in the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> code above.</p>
<p>So we now have a handler on the character, and the handler has a back-reference to the character it sits on.</p>
<p>Since the handler itself is just a regular Python object, we need to use the <code class="docutils literal notranslate"><span class="pre">Character</span></code> to store
our data - our <em>Knave</em> “slots”. We must save them to the database, because we want the server to remember
them even after reloading.</p>
<p>Using <code class="docutils literal notranslate"><span class="pre">self.obj.attributes.add()</span></code> and <code class="docutils literal notranslate"><span class="pre">.get()</span></code> we save the data to the Character in a specially named
<a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attribute</span></a>. Since we use a <code class="docutils literal notranslate"><span class="pre">category</span></code>, we are unlikely to collide with
our data - our <em>Knave</em> “slots”. We must save them to the database, because we want the server to remember them even after reloading.</p>
<p>Using <code class="docutils literal notranslate"><span class="pre">self.obj.attributes.add()</span></code> and <code class="docutils literal notranslate"><span class="pre">.get()</span></code> we save the data to the Character in a specially named <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attribute</span></a>. Since we use a <code class="docutils literal notranslate"><span class="pre">category</span></code>, we are unlikely to collide with
other Attributes.</p>
<p>Our storage structure is a <code class="docutils literal notranslate"><span class="pre">dict</span></code> with keys after our available <code class="docutils literal notranslate"><span class="pre">WieldLocation</span></code> enums. Each can only
have one item except <code class="docutils literal notranslate"><span class="pre">WieldLocation.BACKPACK</span></code>, which is a list.</p>
<p>Our storage structure is a <code class="docutils literal notranslate"><span class="pre">dict</span></code> with keys after our available <code class="docutils literal notranslate"><span class="pre">WieldLocation</span></code> enums. Each can only have one item except <code class="docutils literal notranslate"><span class="pre">WieldLocation.BACKPACK</span></code>, which is a list.</p>
</section>
<section id="connecting-the-equipmenthandler">
<h2><span class="section-number">5.2. </span>Connecting the EquipmentHandler<a class="headerlink" href="#connecting-the-equipmenthandler" title="Permalink to this headline"></a></h2>
<p>Whenever an object leaves from one location to the next, Evennia will call a set of <em>hooks</em> (methods) on the
object that moves, on the source-location and on its destination. This is the same for all moving things -
whether its a character moving between rooms or an item being dropping from your hand to the ground.</p>
<p>We need to tie our new <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> into this system. By reading the doc page on <a class="reference internal" href="../../../Components/Objects.html"><span class="doc std std-doc">Objects</span></a>,
or looking at the <a class="reference internal" href="../../../api/evennia.objects.objects.html#evennia.objects.objects.DefaultObject.move_to" title="evennia.objects.objects.DefaultObject.move_to"><span class="xref myst py py-meth">DefaultObject.move_to</span></a> docstring, well
find out what hooks Evennia will call. Here <code class="docutils literal notranslate"><span class="pre">self</span></code> is the object being moved from
<code class="docutils literal notranslate"><span class="pre">source_location</span></code> to <code class="docutils literal notranslate"><span class="pre">destination</span></code>:</p>
<p>Whenever an object leaves from one location to the next, Evennia will call a set of <em>hooks</em> (methods) on the object that moves, on the source-location and on its destination. This is the same for all moving things - whether its a character moving between rooms or an item being dropping from your hand to the ground.</p>
<p>We need to tie our new <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> into this system. By reading the doc page on <a class="reference internal" href="../../../Components/Objects.html"><span class="doc std std-doc">Objects</span></a>, or looking at the <a class="reference internal" href="../../../api/evennia.objects.objects.html#evennia.objects.objects.DefaultObject.move_to" title="evennia.objects.objects.DefaultObject.move_to"><span class="xref myst py py-meth">DefaultObject.move_to</span></a> docstring, well find out what hooks Evennia will call. Here <code class="docutils literal notranslate"><span class="pre">self</span></code> is the object being moved from <code class="docutils literal notranslate"><span class="pre">source_location</span></code> to <code class="docutils literal notranslate"><span class="pre">destination</span></code>:</p>
<ol class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">self.at_pre_move(destination)</span></code> (abort if return False)</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">source_location.at_pre_object_leave(self,</span> <span class="pre">destination)</span></code> (abort if return False)</p></li>
@ -252,9 +233,7 @@ find out what hooks Evennia will call. Here <code class="docutils literal notran
<li><p><code class="docutils literal notranslate"><span class="pre">destination.at_object_receive(self,</span> <span class="pre">source_location)</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">self.at_post_move(source_location)</span></code></p></li>
</ol>
<p>All of these hooks can be overridden to customize movement behavior. In this case we are interested in
controlling how items enter and leave our character - being inside the character is the same as
them carrying it. We have three good hook-candidates to use for this.</p>
<p>All of these hooks can be overridden to customize movement behavior. In this case we are interested in controlling how items enter and leave our character - being inside the character is the same as them carrying it. We have three good hook-candidates to use for this.</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">.at_pre_object_receive</span></code> - used to check if you can actually pick something up, or if your equipment-store is full.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.at_object_receive</span></code> - used to add the item to the equipmenthandler</p></li>
@ -292,9 +271,7 @@ we will skip that for this tutorial.</p>
<span class="bp">self</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">moved_object</span><span class="p">)</span>
</pre></div>
</div>
<p>Above we have assumed the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> (<code class="docutils literal notranslate"><span class="pre">.equipment</span></code>) has methods <code class="docutils literal notranslate"><span class="pre">.validate_slot_usage</span></code>,
<code class="docutils literal notranslate"><span class="pre">.add</span></code> and <code class="docutils literal notranslate"><span class="pre">.remove</span></code>. But we havent actually added them yet - we just put some reasonable names! Before
we can use this, we need to go actually adding those methods.</p>
<p>Above we have assumed the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> (<code class="docutils literal notranslate"><span class="pre">.equipment</span></code>) has methods <code class="docutils literal notranslate"><span class="pre">.validate_slot_usage</span></code>, <code class="docutils literal notranslate"><span class="pre">.add</span></code> and <code class="docutils literal notranslate"><span class="pre">.remove</span></code>. But we havent actually added them yet - we just put some reasonable names! Before we can use this, we need to go actually adding those methods.</p>
</section>
<section id="expanding-the-equipmenthandler">
<h2><span class="section-number">5.3. </span>Expanding the Equipmenthandler<a class="headerlink" href="#expanding-the-equipmenthandler" title="Permalink to this headline"></a></h2>
@ -353,14 +330,10 @@ we can use this, we need to go actually adding those methods.</p>
That is, you can access <code class="docutils literal notranslate"><span class="pre">.max_slots</span></code> instead of <code class="docutils literal notranslate"><span class="pre">.max_slots()</span></code>. In this case, its just a
little less to type.</p>
</aside>
<p>We add two helpers - the <code class="docutils literal notranslate"><span class="pre">max_slots</span></code> <em>property</em> and <code class="docutils literal notranslate"><span class="pre">count_slots</span></code>, a method that calculate the current
slots being in use. Lets figure out how they work.</p>
<p>We add two helpers - the <code class="docutils literal notranslate"><span class="pre">max_slots</span></code> <em>property</em> and <code class="docutils literal notranslate"><span class="pre">count_slots</span></code>, a method that calculate the current slots being in use. Lets figure out how they work.</p>
<section id="max-slots">
<h3><span class="section-number">5.4.1. </span><code class="docutils literal notranslate"><span class="pre">.max_slots</span></code><a class="headerlink" href="#max-slots" title="Permalink to this headline"></a></h3>
<p>For <code class="docutils literal notranslate"><span class="pre">max_slots</span></code>, remember that <code class="docutils literal notranslate"><span class="pre">.obj</span></code> on the handler is a back-reference to the <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> we
put this handler on. <code class="docutils literal notranslate"><span class="pre">getattr</span></code> is a Python method for retrieving a named property on an object.
The <code class="docutils literal notranslate"><span class="pre">Enum</span></code> <code class="docutils literal notranslate"><span class="pre">Ability.CON.value</span></code> is the string <code class="docutils literal notranslate"><span class="pre">Constitution</span></code> (check out the
<a class="reference internal" href="Beginner-Tutorial-Utilities.html"><span class="doc std std-doc">first Utility and Enums tutorial</span></a> if you dont recall).</p>
<p>For <code class="docutils literal notranslate"><span class="pre">max_slots</span></code>, remember that <code class="docutils literal notranslate"><span class="pre">.obj</span></code> on the handler is a back-reference to the <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> we put this handler on. <code class="docutils literal notranslate"><span class="pre">getattr</span></code> is a Python method for retrieving a named property on an object. The <code class="docutils literal notranslate"><span class="pre">Enum</span></code> <code class="docutils literal notranslate"><span class="pre">Ability.CON.value</span></code> is the string <code class="docutils literal notranslate"><span class="pre">Constitution</span></code> (check out the <a class="reference internal" href="Beginner-Tutorial-Utilities.html"><span class="doc std std-doc">first Utility and Enums tutorial</span></a> if you dont recall).</p>
<p>So to be clear,</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">,</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CON</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="o">+</span> <span class="mi">10</span>
</pre></div>
@ -373,23 +346,17 @@ The <code class="docutils literal notranslate"><span class="pre">Enum</span></co
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">your_character</span><span class="o">.</span><span class="n">Constitution</span> <span class="o">+</span> <span class="mi">10</span>
</pre></div>
</div>
<p>In our code we write <code class="docutils literal notranslate"><span class="pre">getattr(self.obj,</span> <span class="pre">Ability.CON.value,</span> <span class="pre">1)</span></code> - that extra <code class="docutils literal notranslate"><span class="pre">1</span></code> means that if there
should happen to <em>not</em> be a property “Constitution” on <code class="docutils literal notranslate"><span class="pre">self.obj</span></code>, we should not error out but just
return 1.</p>
<p>In our code we write <code class="docutils literal notranslate"><span class="pre">getattr(self.obj,</span> <span class="pre">Ability.CON.value,</span> <span class="pre">1)</span></code> - that extra <code class="docutils literal notranslate"><span class="pre">1</span></code> means that if there should happen to <em>not</em> be a property “Constitution” on <code class="docutils literal notranslate"><span class="pre">self.obj</span></code>, we should not error out but just return 1.</p>
</section>
<section id="count-slots">
<h3><span class="section-number">5.4.2. </span><code class="docutils literal notranslate"><span class="pre">.count_slots</span></code><a class="headerlink" href="#count-slots" title="Permalink to this headline"></a></h3>
<p>In this helper we use two Python tools - the <code class="docutils literal notranslate"><span class="pre">sum()</span></code> function and a
<a class="reference external" href="https://www.w3schools.com/python/python_lists_comprehension.asp">list comprehension</a>. The former
simply adds the values of any iterable together. The latter is a more efficient way to create a list:</p>
<p>In this helper we use two Python tools - the <code class="docutils literal notranslate"><span class="pre">sum()</span></code> function and a <a class="reference external" href="https://www.w3schools.com/python/python_lists_comprehension.asp">list comprehension</a>. The former simply adds the values of any iterable together. The latter is a more efficient way to create a list:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>new_list = [item for item in some_iterable if condition]
all_above_5 = [num for num in range(10) if num &gt; 5] # [6, 7, 8, 9]
all_below_5 = [num for num in range(10) if num &lt; 5] # [0, 1, 2, 3, 4]
</pre></div>
</div>
<p>To make it easier to understand, try reading the last line above as “for every number in the range 0-9,
pick all with a value below 5 and make a list of them”. You can also embed such comprehensions
directly in a function call like <code class="docutils literal notranslate"><span class="pre">sum()</span></code> without using <code class="docutils literal notranslate"><span class="pre">[]</span></code> around it.</p>
<p>To make it easier to understand, try reading the last line above as “for every number in the range 0-9, pick all with a value below 5 and make a list of them”. You can also embed such comprehensions directly in a function call like <code class="docutils literal notranslate"><span class="pre">sum()</span></code> without using <code class="docutils literal notranslate"><span class="pre">[]</span></code> around it.</p>
<p>In <code class="docutils literal notranslate"><span class="pre">count_slots</span></code> we have this code:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">wield_usage</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span>
<span class="nb">getattr</span><span class="p">(</span><span class="n">slotobj</span><span class="p">,</span> <span class="s2">&quot;size&quot;</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
@ -398,10 +365,7 @@ directly in a function call like <code class="docutils literal notranslate"><spa
<span class="p">)</span>
</pre></div>
</div>
<p>We should be able to follow all except <code class="docutils literal notranslate"><span class="pre">slots.items()</span></code>. Since <code class="docutils literal notranslate"><span class="pre">slots</span></code> is a <code class="docutils literal notranslate"><span class="pre">dict</span></code>, we can use <code class="docutils literal notranslate"><span class="pre">.items()</span></code>
to get a sequence of <code class="docutils literal notranslate"><span class="pre">(key,</span> <span class="pre">value)</span></code> pairs. We store these in <code class="docutils literal notranslate"><span class="pre">slot</span></code> and <code class="docutils literal notranslate"><span class="pre">slotobj</span></code>. So the above can
be understood as “for every <code class="docutils literal notranslate"><span class="pre">slot</span></code> and <code class="docutils literal notranslate"><span class="pre">slotobj</span></code>-pair in <code class="docutils literal notranslate"><span class="pre">slots</span></code>, check which slot location it is.
If it is <em>not</em> in the backpack, get its size and add it to the list. Sum over all these
<p>We should be able to follow all except <code class="docutils literal notranslate"><span class="pre">slots.items()</span></code>. Since <code class="docutils literal notranslate"><span class="pre">slots</span></code> is a <code class="docutils literal notranslate"><span class="pre">dict</span></code>, we can use <code class="docutils literal notranslate"><span class="pre">.items()</span></code> to get a sequence of <code class="docutils literal notranslate"><span class="pre">(key,</span> <span class="pre">value)</span></code> pairs. We store these in <code class="docutils literal notranslate"><span class="pre">slot</span></code> and <code class="docutils literal notranslate"><span class="pre">slotobj</span></code>. So the above can be understood as “for every <code class="docutils literal notranslate"><span class="pre">slot</span></code> and <code class="docutils literal notranslate"><span class="pre">slotobj</span></code>-pair in <code class="docutils literal notranslate"><span class="pre">slots</span></code>, check which slot location it is. If it is <em>not</em> in the backpack, get its size and add it to the list. Sum over all these
sizes”.</p>
<p>A less compact but maybe more readonable way to write this would be:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">backpack_item_sizes</span> <span class="o">=</span> <span class="p">[]</span>
@ -417,15 +381,12 @@ together.</p>
</section>
<section id="validating-slots">
<h3><span class="section-number">5.4.3. </span>Validating slots<a class="headerlink" href="#validating-slots" title="Permalink to this headline"></a></h3>
<p>With these helpers in place, <code class="docutils literal notranslate"><span class="pre">validate_slot_usage</span></code> now becomes simple. We use <code class="docutils literal notranslate"><span class="pre">max_slots</span></code> to see how much we can carry.
We then get how many slots we are already using (with <code class="docutils literal notranslate"><span class="pre">count_slots</span></code>) and see if our new <code class="docutils literal notranslate"><span class="pre">obj</span></code>s size
would be too much for us.</p>
<p>With these helpers in place, <code class="docutils literal notranslate"><span class="pre">validate_slot_usage</span></code> now becomes simple. We use <code class="docutils literal notranslate"><span class="pre">max_slots</span></code> to see how much we can carry. We then get how many slots we are already using (with <code class="docutils literal notranslate"><span class="pre">count_slots</span></code>) and see if our new <code class="docutils literal notranslate"><span class="pre">obj</span></code>s size would be too much for us.</p>
</section>
</section>
<section id="add-and-remove">
<h2><span class="section-number">5.5. </span><code class="docutils literal notranslate"><span class="pre">.add</span></code> and <code class="docutils literal notranslate"><span class="pre">.remove</span></code><a class="headerlink" href="#add-and-remove" title="Permalink to this headline"></a></h2>
<p>We will make it so <code class="docutils literal notranslate"><span class="pre">.add</span></code> puts something in the <code class="docutils literal notranslate"><span class="pre">BACKPACK</span></code> location and <code class="docutils literal notranslate"><span class="pre">remove</span></code> drops it, wherever
it is (even if it was in your hands).</p>
<p>We will make it so <code class="docutils literal notranslate"><span class="pre">.add</span></code> puts something in the <code class="docutils literal notranslate"><span class="pre">BACKPACK</span></code> location and <code class="docutils literal notranslate"><span class="pre">remove</span></code> drops it, wherever it is (even if it was in your hands).</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/equipment.py </span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">Ability</span>
@ -468,14 +429,11 @@ double-check we can actually fit the thing, then we add the item to the backpack
<p>In <code class="docutils literal notranslate"><span class="pre">.delete</span></code>, we allow emptying by <code class="docutils literal notranslate"><span class="pre">WieldLocation</span></code> - we figure out what slot it is and return
the item within (if any). If we gave <code class="docutils literal notranslate"><span class="pre">BACKPACK</span></code> as the slot, we empty the backpack and
return all items.</p>
<p>Whenever we change the equipment loadout we must make sure to <code class="docutils literal notranslate"><span class="pre">._save()</span></code> the result, or it will
be lost after a server reload.</p>
<p>Whenever we change the equipment loadout we must make sure to <code class="docutils literal notranslate"><span class="pre">._save()</span></code> the result, or it will be lost after a server reload.</p>
</section>
<section id="moving-things-around">
<h2><span class="section-number">5.6. </span>Moving things around<a class="headerlink" href="#moving-things-around" title="Permalink to this headline"></a></h2>
<p>With the help of <code class="docutils literal notranslate"><span class="pre">.remove()</span></code> and <code class="docutils literal notranslate"><span class="pre">.add()</span></code> we can get things in and out of the <code class="docutils literal notranslate"><span class="pre">BACKPACK</span></code> equipment
location. We also need to grab stuff from the backpack and wield or wear it. We add a <code class="docutils literal notranslate"><span class="pre">.move</span></code> method
on the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> to do this:</p>
<p>With the help of <code class="docutils literal notranslate"><span class="pre">.remove()</span></code> and <code class="docutils literal notranslate"><span class="pre">.add()</span></code> we can get things in and out of the <code class="docutils literal notranslate"><span class="pre">BACKPACK</span></code> equipment location. We also need to grab stuff from the backpack and wield or wear it. We add a <code class="docutils literal notranslate"><span class="pre">.move</span></code> method on the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> to do this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/equipment.py </span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">Ability</span>
@ -522,9 +480,7 @@ on the <code class="docutils literal notranslate"><span class="pre">EquipmentHan
<span class="bp">self</span><span class="o">.</span><span class="n">_save</span><span class="p">()</span>
</pre></div>
</div>
<p>Here we remember that every <code class="docutils literal notranslate"><span class="pre">EvAdventureObject</span></code> has an <code class="docutils literal notranslate"><span class="pre">inventory_use_slot</span></code> property that tells us where
it goes. So we just need to move the object to that slot, replacing whatever is in that place
from before. Anything we replace goes back to the backpack.</p>
<p>Here we remember that every <code class="docutils literal notranslate"><span class="pre">EvAdventureObject</span></code> has an <code class="docutils literal notranslate"><span class="pre">inventory_use_slot</span></code> property that tells us where it goes. So we just need to move the object to that slot, replacing whatever is in that place from before. Anything we replace goes back to the backpack.</p>
</section>
<section id="get-everything">
<h2><span class="section-number">5.7. </span>Get everything<a class="headerlink" href="#get-everything" title="Permalink to this headline"></a></h2>
@ -559,13 +515,11 @@ from before. Anything we replace goes back to the backpack.</p>
</section>
<section id="weapon-and-armor">
<h2><span class="section-number">5.8. </span>Weapon and armor<a class="headerlink" href="#weapon-and-armor" title="Permalink to this headline"></a></h2>
<p>Its convenient to have the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> easily tell you what weapon is currently wielded
and what <em>armor</em> level all worn equipment provides. Otherwise youd need to figure out what item is
in which wield-slot and to add up armor slots manually every time you need to know.</p>
<p>Its convenient to have the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> easily tell you what weapon is currently wielded and what <em>armor</em> level all worn equipment provides. Otherwise youd need to figure out what item is in which wield-slot and to add up armor slots manually every time you need to know.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/equipment.py </span>
<span class="kn">from</span> <span class="nn">.objects</span> <span class="kn">import</span> <span class="n">WeaponEmptyHand</span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">Ability</span>
<span class="kn">from</span> <span class="nn">.objects</span> <span class="kn">import</span> <span class="n">get_empty_hand</span>
<span class="c1"># ... </span>
@ -595,18 +549,15 @@ in which wield-slot and to add up armor slots manually every time you need to kn
<span class="n">weapon</span> <span class="o">=</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">weapon</span><span class="p">:</span>
<span class="n">weapon</span> <span class="o">=</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">]</span>
<span class="c1"># if we still don&#39;t have a weapon, we return None here</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">weapon</span><span class="p">:</span>
<span class="n">weapon</span> <span class="o">=</span> <span class="n">WeaponEmptyHand</span><span class="p">()</span>
<span class="o">~</span> <span class="n">weapon</span> <span class="o">=</span> <span class="n">get_bare_hands</span><span class="p">()</span>
<span class="k">return</span> <span class="n">weapon</span>
</pre></div>
</div>
<p>In the <code class="docutils literal notranslate"><span class="pre">.armor()</span></code> method we get the item (if any) out of each relevant wield-slot (body, shield, head),
and grab their <code class="docutils literal notranslate"><span class="pre">armor</span></code> Attribute. We then <code class="docutils literal notranslate"><span class="pre">sum()</span></code> them all up.</p>
<p>In <code class="docutils literal notranslate"><span class="pre">.weapon()</span></code>, we simply check which of the possible weapon slots (weapon-hand or two-hands) have
something in them. If not we fall back to the fake weapon <code class="docutils literal notranslate"><span class="pre">WeaponEmptyHand</span></code> which is just a dummy
object that represents your bare hands with damage and all.
(created in <a class="reference internal" href="Beginner-Tutorial-Objects.html#your-bare-hands"><span class="std std-doc">The Object tutorial</span></a> earlier).</p>
<p>In the <code class="docutils literal notranslate"><span class="pre">.armor()</span></code> method we get the item (if any) out of each relevant wield-slot (body, shield, head), and grab their <code class="docutils literal notranslate"><span class="pre">armor</span></code> Attribute. We then <code class="docutils literal notranslate"><span class="pre">sum()</span></code> them all up.</p>
<p>In <code class="docutils literal notranslate"><span class="pre">.weapon()</span></code>, we simply check which of the possible weapon slots (weapon-hand or two-hands) have something in them. If not we fall back to the Bare Hands object we created in the <a class="reference internal" href="Beginner-Tutorial-Objects.html#your-bare-hands"><span class="std std-doc">Object tutorial lesson</span></a> earlier.</p>
</section>
<section id="extra-credits">
<h2><span class="section-number">5.9. </span>Extra credits<a class="headerlink" href="#extra-credits" title="Permalink to this headline"></a></h2>
@ -664,12 +615,9 @@ passing these into the handlers methods.</p>
</section>
<section id="summary">
<h2><span class="section-number">5.11. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline"></a></h2>
<p><em>Handlers</em> are useful for grouping functionality together. Now that we spent our time making the
<code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code>, we shouldnt need to worry about item-slots anymore - the handler handles all
the details for us. As long as we call its methods, the details can be forgotten about.</p>
<p><em>Handlers</em> are useful for grouping functionality together. Now that we spent our time making the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code>, we shouldnt need to worry about item-slots anymore - the handler handles all the details for us. As long as we call its methods, the details can be forgotten about.</p>
<p>We also learned to use <em>hooks</em> to tie <em>Knave</em>s custom equipment handling into Evennia.</p>
<p>With <code class="docutils literal notranslate"><span class="pre">Characters</span></code>, <code class="docutils literal notranslate"><span class="pre">Objects</span></code> and now <code class="docutils literal notranslate"><span class="pre">Equipment</span></code> in place, we should be able to move on to character
generation - where players get to make their own character!</p>
<p>With <code class="docutils literal notranslate"><span class="pre">Characters</span></code>, <code class="docutils literal notranslate"><span class="pre">Objects</span></code> and now <code class="docutils literal notranslate"><span class="pre">Equipment</span></code> in place, we should be able to move on to character generation - where players get to make their own character!</p>
</section>
</section>

View file

@ -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>8. Non-Player-Characters (NPCs) &#8212; Evennia 1.0 documentation</title>
<title>8. Non-Player-Characters &#8212; Evennia 1.0 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>
@ -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="9. Game Quests" href="Beginner-Tutorial-Quests.html" />
<link rel="next" title="9. Combat base framework" href="Beginner-Tutorial-Combat-Base.html" />
<link rel="prev" title="7. In-game Rooms" href="Beginner-Tutorial-Rooms.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
@ -30,7 +30,7 @@
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Quests.html" title="9. Game Quests"
<a href="Beginner-Tutorial-Combat-Base.html" title="9. Combat base framework"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rooms.html" title="7. In-game Rooms"
@ -39,7 +39,7 @@
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</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> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">8. </span>Non-Player-Characters (NPCs)</a></li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">8. </span>Non-Player-Characters</a></li>
</ul>
</div>
@ -61,12 +61,22 @@
</div>
</div>
<script>$('#searchbox').show(0);</script>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">8. Non-Player-Characters</a><ul>
<li><a class="reference internal" href="#the-npc-base-class">8.1. The NPC base class</a></li>
<li><a class="reference internal" href="#testing">8.2. Testing</a></li>
<li><a class="reference internal" href="#conclusions">8.3. Conclusions</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Rooms.html"
title="previous chapter"><span class="section-number">7. </span>In-game Rooms</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Quests.html"
title="next chapter"><span class="section-number">9. </span>Game Quests</a></p>
<p class="topless"><a href="Beginner-Tutorial-Combat-Base.html"
title="next chapter"><span class="section-number">9. </span>Combat base framework</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
@ -98,12 +108,203 @@
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="non-player-characters-npcs">
<h1><span class="section-number">8. </span>Non-Player-Characters (NPCs)<a class="headerlink" href="#non-player-characters-npcs" title="Permalink to this headline"></a></h1>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>This part of the Beginner tutorial is still being developed.</p>
<section class="tex2jax_ignore mathjax_ignore" id="non-player-characters">
<h1><span class="section-number">8. </span>Non-Player-Characters<a class="headerlink" href="#non-player-characters" title="Permalink to this headline"></a></h1>
<p>NPCs are all active agents that are not player characters. NPCs could be anything from merchants and quest givers, to monsters and bosses. They could also be flavor - townsfolk doing their chores, farmers tending their fields - there to make the world feel “more alive”.</p>
<aside class="sidebar">
<p class="sidebar-title">vNPCs</p>
<p>You should usually avoid creating hundreds of NPC objects to populate your busy town - in a text game so many NPCs will just spam the screen and annoy your players. Since this is a text game, you can usually get away with using <em>vNPcs</em> - virtual NPCs. vNPCs are only described in text - a room could be described as a bustling street, farmers can be described shouting to each other. Using room descriptions for this works well, but the tutorial lesson about <a class="reference internal" href="Beginner-Tutorial-Rooms.html"><span class="doc std std-doc">EvAdventure Rooms</span></a> has a section called <a class="reference internal" href="Beginner-Tutorial-Rooms.html#adding-life-to-a-room"><span class="std std-doc">adding life to a room</span></a> that can be used for making vNPCs appear to do things in the background.</p>
</aside>
<p>In this lesson we will create the base class of <em>Knave</em> NPCs. According to the <em>Knave</em> rules, NPCs have some simplified stats compared to the <a class="reference internal" href="Beginner-Tutorial-Characters.html"><span class="doc std std-doc">PC characters</span></a> we designed earlier:</p>
<section id="the-npc-base-class">
<h2><span class="section-number">8.1. </span>The NPC base class<a class="headerlink" href="#the-npc-base-class" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/npcs.py</span></code>.</p>
</div></blockquote>
<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>
<span class="normal">58</span>
<span class="normal">59</span>
<span class="normal">60</span>
<span class="normal">61</span>
<span class="normal">62</span>
<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="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span><span class="p">,</span> <span class="n">AttributeProperty</span>
<span class="kn">from</span> <span class="nn">.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="hll"><span class="k">class</span> <span class="nc">EvAdventureNPC</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
</span><span class="w"> </span><span class="sd">&quot;&quot;&quot;Base class for NPCs&quot;&quot;&quot;</span>
<span class="hll"> <span class="n">is_pc</span> <span class="o">=</span> <span class="kc">False</span>
</span><span class="hll"> <span class="n">hit_dice</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span> <span class="n">armor</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># +10 to get armor defense</span>
<span class="hll"> <span class="n">hp_multiplier</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># 4 default in Knave</span>
</span> <span class="n">hp</span> <span class="o">=</span> <span class="n">AttributeProperty</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">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># internal tracking, use .hp property</span>
<span class="hll"> <span class="n">morale</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">9</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span> <span class="n">allegiance</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="n">Ability</span><span class="o">.</span><span class="n">ALLEGIANCE_HOSTILE</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="hll">
</span> <span class="n">weapon</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="n">BARE_HANDS</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># instead of inventory</span>
<span class="n">coins</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># coin loot</span>
<span class="hll"> <span class="n">is_idle</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span>
<span class="hll"> <span class="nd">@property</span>
</span> <span class="k">def</span> <span class="nf">strength</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">hit_dice</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">dexterity</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">hit_dice</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">constitution</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">hit_dice</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">intelligence</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">hit_dice</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">wisdom</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">hit_dice</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">charisma</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">hit_dice</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">hp_max</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">hit_dice</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_multiplier</span>
<span class="k">def</span> <span class="nf">at_object_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Start with max health.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</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">&quot;npcs&quot;</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">&quot;group&quot;</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">&quot;&quot;&quot; </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"> &quot;&quot;&quot;</span>
<span class="k">pass</span>
</pre></div></td></tr></table></div>
</div>
<ul class="simple">
<li><p><strong>Line 9</strong>: By use of <em>multiple inheritance</em> we use the <code class="docutils literal notranslate"><span class="pre">LinvingMixin</span></code> we created in the <a class="reference internal" href="Beginner-Tutorial-Characters.html"><span class="doc std std-doc">Character lesson</span></a>. 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.</p></li>
<li><p><strong>Line 12</strong>: The <code class="docutils literal notranslate"><span class="pre">is_pc</span></code> is a quick and convenient way to check if this is, well, a PC or not. We will use it in the upcoming <a class="reference internal" href="Beginner-Tutorial-Combat-Base.html"><span class="doc std std-doc">Combat base lesson</span></a>.</p></li>
<li><p><strong>Line 13</strong>: The NPC is simplified in that all stats are just based on the <code class="docutils literal notranslate"><span class="pre">Hit</span> <span class="pre">dice</span></code> number (see <strong>Lines 25-51</strong>). We store <code class="docutils literal notranslate"><span class="pre">armor</span></code> and a <code class="docutils literal notranslate"><span class="pre">weapon</span></code> as direct <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attributes</span></a> on the class rather than bother implementing a full equipment system.</p></li>
<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>
</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>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/tests/test_npcs.py</span></code></p>
</div></blockquote>
<p>Not so much to test yet, but we will be using the same module to test other aspects of NPCs in the future, so lets create it now.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/tests/test_npcs.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_object</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTest</span>
<span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">npcs</span>
<span class="k">class</span> <span class="nc">TestNPCBase</span><span class="p">(</span><span class="n">EvenniaTest</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Test the NPC base class&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">test_npc_base</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">npc</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span>
<span class="n">npcs</span><span class="o">.</span><span class="n">EvAdventureNPC</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;TestNPC&quot;</span><span class="p">,</span>
<span class="n">attributes</span><span class="o">=</span><span class="p">[(</span><span class="s2">&quot;hit_dice&quot;</span><span class="p">,</span> <span class="mi">4</span><span class="p">)],</span> <span class="c1"># set hit_dice to 4</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">hp_multiplier</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">hp</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">strength</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">charisma</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
</pre></div>
</div>
<p>Nothing special here. Note how the <code class="docutils literal notranslate"><span class="pre">create_object</span></code> helper function takes <code class="docutils literal notranslate"><span class="pre">attributes</span></code> as a keyword. This is a list of tuples we use to set different values than the default ones to Attributes. We then check a few of the properties to make sure they return what we expect.</p>
</section>
<section id="conclusions">
<h2><span class="section-number">8.3. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline"></a></h2>
<p>In <em>Knave</em>, an NPC is a simplified version of a Player Character. In other games and rule systems, they may be all but identical.</p>
<p>With the NPC class in place, we have enough to create a test dummy. Since it has no AI yet, it wont fight back, but it will be enough to have something to hit when we test our combat in the upcoming lessons.</p>
</section>
</section>
@ -122,7 +323,7 @@
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Quests.html" title="9. Game Quests"
<a href="Beginner-Tutorial-Combat-Base.html" title="9. Combat base framework"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Rooms.html" title="7. In-game Rooms"
@ -131,7 +332,7 @@
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How we get there (example game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">8. </span>Non-Player-Characters (NPCs)</a></li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">8. </span>Non-Player-Characters</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">

View file

@ -126,9 +126,7 @@ we also need to have a notion what an item or object is.</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">size</span></code> - this is how many slots the item uses in the characters inventory.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">value</span></code> - a base value if we want to sell or buy the item.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">inventory_use_slot</span></code> - some items can be worn or wielded. For example, a helmet needs to be
worn on the head and a shield in the shield hand. Some items cant be used this way at all, but
only belong in the backpack.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">inventory_use_slot</span></code> - some items can be worn or wielded. For example, a helmet needs to be worn on the head and a shield in the shield hand. Some items cant be used this way at all, but only belong in the backpack.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">obj_type</span></code> - Which type of item this is.</p></li>
</ul>
<section id="new-enums">
@ -173,8 +171,7 @@ Before we continue, lets expand with enums for use-slots and object types.</p
a full set of objects implemented.</p>
</aside>
<div style="clear: right;"></div>
<p>We will make a base <code class="docutils literal notranslate"><span class="pre">EvAdventureObject</span></code> class off Evennias standard <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>. We will then add
child classes to represent the relevant types:</p>
<p>We will make a base <code class="docutils literal notranslate"><span class="pre">EvAdventureObject</span></code> class off Evennias standard <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>. We will then add child classes to represent the relevant types:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">AttributeProperty</span><span class="p">,</span> <span class="n">DefaultObject</span>
@ -195,14 +192,42 @@ child classes to represent the relevant types:</p>
<span class="c1"># this can be either a single type or a list of types (for objects able to be </span>
<span class="c1"># act as multiple). This is used to tag this object during creation.</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">GEAR</span>
<span class="c1"># default evennia hooks</span>
<span class="k">def</span> <span class="nf">at_object_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called when this object is first created. We convert the .obj_type </span>
<span class="sd"> property to a database tag.&quot;&quot;&quot;</span>
<span class="k">for</span> <span class="n">obj_type</span> <span class="ow">in</span> <span class="n">make_iter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj_type</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj_type</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">&quot;obj_type&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_display_header</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The top of the description&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="s2">&quot;&quot;</span>
<span class="k">def</span> <span class="nf">get_display_desc</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;The main display - show object stats&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">get_obj_stats</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="o">=</span><span class="n">looker</span><span class="p">)</span>
<span class="c1"># custom evadventure methods</span>
<span class="k">def</span> <span class="nf">has_obj_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">objtype</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Check if object is of a certain type&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">objtype</span><span class="o">.</span><span class="n">value</span> <span class="ow">in</span> <span class="n">make_iter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj_type</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called before use. If returning False, can&#39;t be used&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="kc">True</span>
<span class="k">def</span> <span class="nf">use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Use this object, whatever that means&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">post_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Always called after use.&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">get_help</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Get any help text for this item&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="s2">&quot;No help for this item&quot;</span>
@ -218,30 +243,18 @@ property on the class:</p>
<span class="n">value</span> <span class="o">=</span> <span class="mi">0</span>
</pre></div>
</div>
<p>The problem with this is that if we want to make a new object of <code class="docutils literal notranslate"><span class="pre">size</span> <span class="pre">3</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span> <span class="pre">20</span></code>, we have to
make a new class for it. We cant change it on the fly because the change would only be in memory and
be lost on next server reload.</p>
<p>Because we use <code class="docutils literal notranslate"><span class="pre">AttributeProperties</span></code>, we can set <code class="docutils literal notranslate"><span class="pre">size</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span></code> to whatever we like when we
create the object (or later), and the Attributes will remember our changes to that object indefinitely.</p>
<p>To make this a little more efficient, we use <code class="docutils literal notranslate"><span class="pre">autocreate=False</span></code>. Normally when you create a
new object with defined <code class="docutils literal notranslate"><span class="pre">AttributeProperties</span></code>, a matching <code class="docutils literal notranslate"><span class="pre">Attribute</span></code> is immediately created at
the same time. So normally, the object would be created along with two Attributes <code class="docutils literal notranslate"><span class="pre">size</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span></code>.
With <code class="docutils literal notranslate"><span class="pre">autocreate=False</span></code>, no Attribute will be created <em>unless the default is changed</em>. That is, as
long as your object has <code class="docutils literal notranslate"><span class="pre">size=1</span></code> no database <code class="docutils literal notranslate"><span class="pre">Attribute</span></code> will be created at all. This saves time and
resources when creating large number of objects.</p>
<p>The drawback is that since no Attribute is created you cant refer to it
with <code class="docutils literal notranslate"><span class="pre">obj.db.size</span></code> or <code class="docutils literal notranslate"><span class="pre">obj.attributes.get(&quot;size&quot;)</span></code> <em>unless you change its default</em>. You also cant query
the database for all objects with <code class="docutils literal notranslate"><span class="pre">size=1</span></code>, since most objects would not yet have an in-database
<p>The problem with this is that if we want to make a new object of <code class="docutils literal notranslate"><span class="pre">size</span> <span class="pre">3</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span> <span class="pre">20</span></code>, we have to make a new class for it. We cant change it on the fly because the change would only be in memory and be lost on next server reload.</p>
<p>Because we use <code class="docutils literal notranslate"><span class="pre">AttributeProperties</span></code>, we can set <code class="docutils literal notranslate"><span class="pre">size</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span></code> to whatever we like when we create the object (or later), and the Attributes will remember our changes to that object indefinitely.</p>
<p>To make this a little more efficient, we use <code class="docutils literal notranslate"><span class="pre">autocreate=False</span></code>. Normally when you create a new object with defined <code class="docutils literal notranslate"><span class="pre">AttributeProperties</span></code>, a matching <code class="docutils literal notranslate"><span class="pre">Attribute</span></code> is immediately created at the same time. So normally, the object would be created along with two Attributes <code class="docutils literal notranslate"><span class="pre">size</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span></code>. With <code class="docutils literal notranslate"><span class="pre">autocreate=False</span></code>, no Attribute will be created <em>unless the default is changed</em>. That is, as long as your object has <code class="docutils literal notranslate"><span class="pre">size=1</span></code> no database <code class="docutils literal notranslate"><span class="pre">Attribute</span></code> will be created at all. This saves time and resources when creating large number of objects.</p>
<p>The drawback is that since no Attribute is created you cant refer to it with <code class="docutils literal notranslate"><span class="pre">obj.db.size</span></code> or <code class="docutils literal notranslate"><span class="pre">obj.attributes.get(&quot;size&quot;)</span></code> <em>unless you change its default</em>. You also cant query the database for all objects with <code class="docutils literal notranslate"><span class="pre">size=1</span></code>, since most objects would not yet have an in-database
<code class="docutils literal notranslate"><span class="pre">size</span></code> Attribute to search for.</p>
<p>In our case, well only refer to these properties as <code class="docutils literal notranslate"><span class="pre">obj.size</span></code> etc, and have no need to find
all objects of a particular size. So we should be safe.</p>
</section>
<section id="creating-tags-in-at-object-creation">
<h3><span class="section-number">4.2.2. </span>Creating tags in <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code><a class="headerlink" href="#creating-tags-in-at-object-creation" title="Permalink to this headline"></a></h3>
<p>The <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> is a method Evennia calls on every child of <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> whenever it is
first created.</p>
<p>We do a tricky thing here, converting our <code class="docutils literal notranslate"><span class="pre">.obj_type</span></code> to one or more <a class="reference internal" href="../../../Components/Tags.html"><span class="doc std std-doc">Tags</span></a>. Tagging the
object like this means you can later efficiently find all objects of a given type (or combination of
<p>The <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> is a method Evennia calls on every child of <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> whenever it is first created.</p>
<p>We do a tricky thing here, converting our <code class="docutils literal notranslate"><span class="pre">.obj_type</span></code> to one or more <a class="reference internal" href="../../../Components/Tags.html"><span class="doc std std-doc">Tags</span></a>. Tagging the object like this means you can later efficiently find all objects of a given type (or combination of
types) with Evennias search functions:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">ObjType</span>
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">search</span>
@ -250,9 +263,7 @@ types) with Evennias search functions:</p>
<span class="n">all_shields</span> <span class="o">=</span> <span class="n">search</span><span class="o">.</span><span class="n">search_object_by_tag</span><span class="p">(</span><span class="n">ObjType</span><span class="o">.</span><span class="n">SHIELD</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">&quot;obj_type&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>We allow <code class="docutils literal notranslate"><span class="pre">.obj_type</span></code> to be given as a single value or a list of values. We use <code class="docutils literal notranslate"><span class="pre">make_iter</span></code> from the
evennia utility library to make sure we dont balk at either. This means you could have a Shield that
is also Magical, for example.</p>
<p>We allow <code class="docutils literal notranslate"><span class="pre">.obj_type</span></code> to be given as a single value or a list of values. We use <code class="docutils literal notranslate"><span class="pre">make_iter</span></code> from the evennia utility library to make sure we dont balk at either. This means you could have a Shield that is also Magical, for example.</p>
</section>
</section>
<section id="other-object-types">
@ -281,8 +292,7 @@ is also Magical, for example.</p>
</section>
<section id="consumables">
<h2><span class="section-number">4.4. </span>Consumables<a class="headerlink" href="#consumables" title="Permalink to this headline"></a></h2>
<p>A consumable is an item that has a certain number of uses. Once fully consumed, it cant be used
anymore. An example would be a health potion.</p>
<p>A consumable is an item that has a certain number of uses. Once fully consumed, it cant be used anymore. An example would be a health potion.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
<span class="c1"># ... </span>
@ -294,14 +304,20 @@ anymore. An example would be a health potion.</p>
<span class="n">value</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mf">0.25</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">uses</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called before using. If returning False, abort use.&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">uses</span> <span class="o">&gt;</span> <span class="mi">0</span>
<span class="k">def</span> <span class="nf">at_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="n">target</span> <span class="ow">and</span> <span class="n">user</span><span class="o">.</span><span class="n">location</span> <span class="o">!=</span> <span class="n">target</span><span class="o">.</span><span class="n">location</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You are not close enough to the target!&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;|w</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is used up.|n&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">def</span> <span class="nf">use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called when using the item&quot;&quot;&quot;</span>
<span class="k">pass</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">at_post_use</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called after using the item&quot;&quot;&quot;</span>
<span class="c1"># detract a usage, deleting the item if used up.</span>
@ -311,12 +327,12 @@ anymore. An example would be a health potion.</p>
<span class="bp">self</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>
</pre></div>
</div>
<p>What exactly each consumable does will vary - we will need to implement children of this class
later, overriding <code class="docutils literal notranslate"><span class="pre">at_use</span></code> with different effects.</p>
<p>In <code class="docutils literal notranslate"><span class="pre">at_pre_use</span></code> we check if we have specified a target (heal someone else or throw a fire bomb at an enemy?), making sure we are in the same location. We also make sure we have <code class="docutils literal notranslate"><span class="pre">usages</span></code> left. In <code class="docutils literal notranslate"><span class="pre">at_post_use</span></code> we make sure to tick off usages.</p>
<p>What exactly each consumable does will vary - we will need to implement children of this class later, overriding <code class="docutils literal notranslate"><span class="pre">at_use</span></code> with different effects.</p>
</section>
<section id="weapons">
<h2><span class="section-number">4.5. </span>Weapons<a class="headerlink" href="#weapons" title="Permalink to this headline"></a></h2>
<p>All weapons need properties that describe how efficient they are in battle.</p>
<p>All weapons need properties that describe how efficient they are in battle. To use a weapon means to attack with it, so we can let the weapon itself handle all logic around performing an attack. Having the attack code on the weapon also means that if we in the future wanted a weapon doing something special on-attack (for example, a vampiric sword that heals the attacker when hurting the enemy), we could easily add that on the weapon subclass in question without modifying other code.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">ObjType</span><span class="p">,</span> <span class="n">Ability</span>
@ -334,17 +350,113 @@ later, overriding <code class="docutils literal notranslate"><span class="pre">a
<span class="n">defend_type</span> <span class="o">=</span> <span class="n">AttibuteProperty</span><span class="p">(</span><span class="n">Ability</span><span class="o">.</span><span class="n">ARMOR</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">damage_roll</span> <span class="o">=</span> <span class="n">AttibuteProperty</span><span class="p">(</span><span class="s2">&quot;1d6&quot;</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="n">target</span> <span class="ow">and</span> <span class="n">user</span><span class="o">.</span><span class="n">location</span> <span class="o">!=</span> <span class="n">target</span><span class="o">.</span><span class="n">location</span><span class="p">:</span>
<span class="c1"># we assume weapons can only be used in the same location</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You are not close enough to the target!&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">user</span><span class="p">)</span><span class="si">}</span><span class="s2"> is broken and can&#39;t be used!&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="n">target</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">advantage</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">disadvantage</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;When a weapon is used, it attacks an opponent&quot;&quot;&quot;</span>
<span class="n">location</span> <span class="o">=</span> <span class="n">attacker</span><span class="o">.</span><span class="n">location</span>
<span class="n">is_hit</span><span class="p">,</span> <span class="n">quality</span><span class="p">,</span> <span class="n">txt</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">dice</span><span class="o">.</span><span class="n">opposed_saving_throw</span><span class="p">(</span>
<span class="n">attacker</span><span class="p">,</span>
<span class="n">target</span><span class="p">,</span>
<span class="n">attack_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">attack_type</span><span class="p">,</span>
<span class="n">defense_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">defense_type</span><span class="p">,</span>
<span class="n">advantage</span><span class="o">=</span><span class="n">advantage</span><span class="p">,</span>
<span class="n">disadvantage</span><span class="o">=</span><span class="n">disadvantage</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(attack) $You(</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">) with </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">txt</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">,</span>
<span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span>
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">},</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">is_hit</span><span class="p">:</span>
<span class="c1"># enemy hit, calculate damage</span>
<span class="n">dmg</span> <span class="o">=</span> <span class="n">rules</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="bp">self</span><span class="o">.</span><span class="n">damage_roll</span><span class="p">)</span>
<span class="k">if</span> <span class="n">quality</span> <span class="ow">is</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CRITICAL_SUCCESS</span><span class="p">:</span>
<span class="c1"># doble damage roll for critical success</span>
<span class="n">dmg</span> <span class="o">+=</span> <span class="n">rules</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="bp">self</span><span class="o">.</span><span class="n">damage_roll</span><span class="p">)</span>
<span class="n">message</span> <span class="o">=</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot; $You() |ycritically|n $conj(hit) $You(</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">) for |r</span><span class="si">{</span><span class="n">dmg</span><span class="si">}</span><span class="s2">|n damage!&quot;</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">message</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot; $You() $conj(hit) $You(</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">) for |r</span><span class="si">{</span><span class="n">dmg</span><span class="si">}</span><span class="s2">|n damage!&quot;</span>
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span> <span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">})</span>
<span class="c1"># call hook</span>
<span class="n">target</span><span class="o">.</span><span class="n">at_damage</span><span class="p">(</span><span class="n">dmg</span><span class="p">,</span> <span class="n">attacker</span><span class="o">=</span><span class="n">attacker</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># a miss</span>
<span class="n">message</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot; $You() $conj(miss) $You(</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">).&quot;</span>
<span class="k">if</span> <span class="n">quality</span> <span class="ow">is</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CRITICAL_FAILURE</span><span class="p">:</span>
<span class="n">message</span> <span class="o">+=</span> <span class="s2">&quot;.. it&#39;s a |rcritical miss!|n, damaging the weapon.&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span> <span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">})</span>
<span class="k">def</span> <span class="nf">at_post_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;|r</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">user</span><span class="p">)</span><span class="si">}</span><span class="s2"> breaks and can no longer be used!&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">quality</span></code> is something we need to track in <em>Knave</em>. When getting critical failures on attacks,
a weapons quality will go down. When it reaches 0, it will break.</p>
<p>In EvAdventure, we will assume all weapons (including bows etc) are used in the same location as the target. Weapons also have a <code class="docutils literal notranslate"><span class="pre">quality</span></code> attribute that gets worn down if the user rolls a critical failure. Once quality is down to 0, the weapon is broken and needs to be repaired.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">quality</span></code> is something we need to track in <em>Knave</em>. When getting critical failures on attacks, a weapons quality will go down. When it reaches 0, it will break. We assume that a <code class="docutils literal notranslate"><span class="pre">quality</span></code> of <code class="docutils literal notranslate"><span class="pre">None</span></code> means that quality doesnt apply (that is, the item is unbreakable), so we must consider that when checking.</p>
<p>The attack/defend type tracks how we resolve attacks with the weapon, like <code class="docutils literal notranslate"><span class="pre">roll</span> <span class="pre">+</span> <span class="pre">STR</span> <span class="pre">vs</span> <span class="pre">ARMOR</span> <span class="pre">+</span> <span class="pre">10</span></code>.</p>
<p>In the <code class="docutils literal notranslate"><span class="pre">use</span></code> method we make use of the <code class="docutils literal notranslate"><span class="pre">rules</span></code> module we <a class="reference internal" href="Beginner-Tutorial-Rules.html"><span class="doc std std-doc">created earlier</span></a> to perform all the dice rolls needed to resolve the attack.</p>
<p>This code requires some additional explanation:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(attack) $you(</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">) with </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">txt</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">,</span>
<span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span>
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">},</span>
<span class="p">)</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">location.msg_contents</span></code> sends a message to everyone in <code class="docutils literal notranslate"><span class="pre">location</span></code>. Since people will usually notice if you swing a sword at somone, this makes sense to tell people about. This message should however look <em>different</em> depending on who sees it.</p>
<p>I should see:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You attack Grendel with sword: &lt;dice roll results&gt;
</pre></div>
</div>
<p>Others should see</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Beowulf attacks Grendel with sword: &lt;dice roll results&gt;
</pre></div>
</div>
<p>And Grendel should see</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Beowulf attacks you with sword: &lt;dice roll results&gt;
</pre></div>
</div>
<p>We provide the following string to <code class="docutils literal notranslate"><span class="pre">msg_contents</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sa">f</span><span class="s2">&quot;$You() $conj(attack) $You(</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">) with </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">txt</span><span class="si">}</span><span class="s2">&quot;</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">{...}</span></code> are normal f-string formatting markers like those we have used before. The <code class="docutils literal notranslate"><span class="pre">$func(...)</span></code> bits are <a class="reference internal" href="../../../Components/FuncParser.html"><span class="doc std std-doc">Evennnia FuncParser</span></a> function calls. FuncParser calls are executed as functions and the result replaces their position in the string. As this string is parsed by Evennia, this is what happens:</p>
<p>First the f-string markers are replaced, so that we get this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="s2">&quot;$You() $cobj(attack) $you(Grendel) with sword: </span><span class="se">\n</span><span class="s2"> rolled 8 on d20 ...&quot;</span>
</pre></div>
</div>
<p>Next the funcparser functions are run:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">$You()</span></code> becomes the name or <code class="docutils literal notranslate"><span class="pre">You</span></code> depending on if the string is to be sent to that object or not. It uses the <code class="docutils literal notranslate"><span class="pre">from_obj=</span></code> kwarg to the <code class="docutils literal notranslate"><span class="pre">msg_contents</span></code> method to know this. Since <code class="docutils literal notranslate"><span class="pre">msg_contents=attacker</span></code> , this becomes <code class="docutils literal notranslate"><span class="pre">You</span></code> or <code class="docutils literal notranslate"><span class="pre">Beowulf</span></code> in this example.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">$you(Grendel)</span></code> looks for the <code class="docutils literal notranslate"><span class="pre">mapping=</span></code> kwarg to <code class="docutils literal notranslate"><span class="pre">msg_contents</span></code> to determine who should be addressed here. If will replace this with the display name or the lowercase <code class="docutils literal notranslate"><span class="pre">you</span></code>. We have added <code class="docutils literal notranslate"><span class="pre">mapping={target.key:</span> <span class="pre">target}</span></code> - that is <code class="docutils literal notranslate"><span class="pre">{&quot;Grendel&quot;:</span> <span class="pre">&lt;grendel_obj&gt;}</span></code>. So this will become <code class="docutils literal notranslate"><span class="pre">you</span></code> or <code class="docutils literal notranslate"><span class="pre">Grendel</span></code> depending on who sees the string.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">$conj(attack)</span></code> <em>conjugates</em> the verb depending on who sees it. The result will be <code class="docutils literal notranslate"><span class="pre">You</span> <span class="pre">attack</span> <span class="pre">...</span></code> or <code class="docutils literal notranslate"><span class="pre">Beowulf</span> <span class="pre">attacks</span></code> (note the extra <code class="docutils literal notranslate"><span class="pre">s</span></code>).</p></li>
</ul>
<p>A few funcparser calls compacts all these points of view into one string!</p>
</section>
<section id="magic">
<h2><span class="section-number">4.6. </span>Magic<a class="headerlink" href="#magic" title="Permalink to this headline"></a></h2>
<p>In <em>Knave</em>, anyone can use magic if they are wielding a rune stone (our name for spell books) in both
hands. You can only use a rune stone once per rest. So a rune stone is an example of a magical weapon
that is also a consumable of sorts.</p>
<p>In <em>Knave</em>, anyone can use magic if they are wielding a rune stone (our name for spell books) in both hands. You can only use a rune stone once per rest. So a rune stone is an example of a magical weapon that is also a consumable of sorts.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
<span class="c1"># ... </span>
@ -377,22 +489,13 @@ that is also a consumable of sorts.</p>
<span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o">=</span> <span class="mi">1</span>
</pre></div>
</div>
<p>We make the rune stone a mix of weapon and consumable. Note that we dont have to add <code class="docutils literal notranslate"><span class="pre">.uses</span></code>
again, its inherited from <code class="docutils literal notranslate"><span class="pre">EvAdventureConsumable</span></code> parent. The <code class="docutils literal notranslate"><span class="pre">at_pre_use</span></code> and <code class="docutils literal notranslate"><span class="pre">at_use</span></code> methods
are also inherited; we only override <code class="docutils literal notranslate"><span class="pre">at_post_use</span></code> since we dont want the runestone to be deleted
when it runs out of uses.</p>
<p>We add a little convenience method <code class="docutils literal notranslate"><span class="pre">refresh</span></code> - we should call this when the character rests, to
make the runestone active again.</p>
<p>Exactly what rune stones <em>do</em> will be implemented in the <code class="docutils literal notranslate"><span class="pre">at_use</span></code> methods of subclasses to this
base class. Since magic in <em>Knave</em> tends to be pretty custom, it makes sense that it will lead to a lot
of custom code.</p>
<p>We make the rune stone a mix of weapon and consumable. Note that we dont have to add <code class="docutils literal notranslate"><span class="pre">.uses</span></code> again, its inherited from <code class="docutils literal notranslate"><span class="pre">EvAdventureConsumable</span></code> parent. The <code class="docutils literal notranslate"><span class="pre">at_pre_use</span></code> and <code class="docutils literal notranslate"><span class="pre">use</span></code> methods are also inherited; we only override <code class="docutils literal notranslate"><span class="pre">at_post_use</span></code> since we dont want the runestone to be deleted when it runs out of uses.</p>
<p>We add a little convenience method <code class="docutils literal notranslate"><span class="pre">refresh</span></code> - we should call this when the character rests, to make the runestone active again.</p>
<p>Exactly what rune stones <em>do</em> will be implemented in the <code class="docutils literal notranslate"><span class="pre">at_use</span></code> methods of subclasses to this base class. Since magic in <em>Knave</em> tends to be pretty custom, it makes sense that it will lead to a lot of custom code.</p>
</section>
<section id="armor">
<h2><span class="section-number">4.7. </span>Armor<a class="headerlink" href="#armor" title="Permalink to this headline"></a></h2>
<p>Armor, shields and helmets increase the <code class="docutils literal notranslate"><span class="pre">ARMOR</span></code> stat of the character. In <em>Knave</em>, what is stored is the
defense value of the armor (values 11-20). We will instead store the armor bonus (1-10). As we know,
defending is always <code class="docutils literal notranslate"><span class="pre">bonus</span> <span class="pre">+</span> <span class="pre">10</span></code>, so the result will be the same - this means
we can use <code class="docutils literal notranslate"><span class="pre">Ability.ARMOR</span></code> as any other defensive ability without worrying about a special case.</p>
<p>Armor, shields and helmets increase the <code class="docutils literal notranslate"><span class="pre">ARMOR</span></code> stat of the character. In <em>Knave</em>, what is stored is the defense value of the armor (values 11-20). We will instead store the armor bonus (1-10). As we know, defending is always <code class="docutils literal notranslate"><span class="pre">bonus</span> <span class="pre">+</span> <span class="pre">10</span></code>, so the result will be the same - this means we can use <code class="docutils literal notranslate"><span class="pre">Ability.ARMOR</span></code> as any other defensive ability without worrying about a special case.</p>
<p>``</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
@ -419,32 +522,47 @@ we can use <code class="docutils literal notranslate"><span class="pre">Ability.
</section>
<section id="your-bare-hands">
<h2><span class="section-number">4.8. </span>Your Bare hands<a class="headerlink" href="#your-bare-hands" title="Permalink to this headline"></a></h2>
<p>This is a dummy object that is not stored in the database. We will use this in the upcoming
<a class="reference internal" href="Beginner-Tutorial-Equipment.html"><span class="doc std std-doc">Equipment tutorial lesson</span></a> to represent when you have nothing
in your hands. This way we dont need to add any special case for this.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">WeaponEmptyHand</span><span class="p">:</span>
<p>When we dont have any weapons, well be using our bare fists to fight.</p>
<p>We will use this in the upcoming <a class="reference internal" href="Beginner-Tutorial-Equipment.html"><span class="doc std std-doc">Equipment tutorial lesson</span></a> to represent when you have nothing in your hands. This way we dont need to add any special case for this.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">search_object</span><span class="p">,</span> <span class="n">create_object</span>
<span class="n">_BARE_HANDS</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">WeaponBareHands</span><span class="p">(</span><span class="n">EvAdventureWeapon</span><span class="p">)</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">WEAPON</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;Empty Fists&quot;</span>
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span>
<span class="n">attack_type</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">STR</span>
<span class="n">defense_type</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">ARMOR</span>
<span class="n">damage_roll</span> <span class="o">=</span> <span class="s2">&quot;1d4&quot;</span>
<span class="n">quality</span> <span class="o">=</span> <span class="mi">100000</span> <span class="c1"># let&#39;s assume fists are always available ...</span>
<span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="s2">&quot;&lt;WeaponEmptyHand&gt;&quot;</span>
<span class="n">quality</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># let&#39;s assume fists are indestructible ...</span>
<span class="k">def</span> <span class="nf">get_bare_hands</span><span class="p">():</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Get the bare hands&quot;&quot;&quot;</span>
<span class="k">global</span> <span class="n">_BARE_HANDS</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">_BARE_HANDS</span><span class="p">:</span>
<span class="n">_BARE_HANDS</span> <span class="o">=</span> <span class="n">search_object</span><span class="p">(</span><span class="s2">&quot;Bare hands&quot;</span><span class="p">,</span> <span class="n">typeclass</span><span class="o">=</span><span class="n">WeaponBareHands</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">_BARE_HANDS</span><span class="p">:</span>
<span class="n">_BARE_HANDS</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">WeaponBareHands</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;Bare hands&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">_BARE_HANDS</span>
</pre></div>
</div>
<aside class="sidebar">
<p>Creating a single instance of something that is used everywhere is called to create a <em>Singleton</em>.</p>
</aside>
<p>Since everyones empty hands are the same (in our game), we create <em>one</em> <code class="docutils literal notranslate"><span class="pre">Bare</span> <span class="pre">hands</span></code> weapon object that everyone shares. We do this by searching for the object with <code class="docutils literal notranslate"><span class="pre">search_object</span></code> (the <code class="docutils literal notranslate"><span class="pre">.first()</span></code> means we grab the first one even if we should by accident have created multiple hands, see <a class="reference internal" href="../Part1/Beginner-Tutorial-Django-queries.html"><span class="doc std std-doc">The Django querying tutorial</span></a> for more info). If we find none, we create it.</p>
<p>By use of the <code class="docutils literal notranslate"><span class="pre">global</span></code> Python keyword, we cache the bare hands object get/create in a module level property <code class="docutils literal notranslate"><span class="pre">_BARE_HANDS</span></code>. So this acts as a cache to not have to search the database more than necessary.</p>
<p>From now on, other modules can just import and run this function to get the bare hands.</p>
</section>
<section id="testing-and-extra-credits">
<h2><span class="section-number">4.9. </span>Testing and Extra credits<a class="headerlink" href="#testing-and-extra-credits" title="Permalink to this headline"></a></h2>
<p>Remember the <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> function from the <a class="reference internal" href="Beginner-Tutorial-Utilities.html"><span class="doc std std-doc">Utility Tutorial</span></a> earlier?
We had to use dummy-values since we didnt yet know how we would store properties on Objects in the game.</p>
<p>Well, we just figured out all we need! You can go back and update <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> to properly read the data
from the object it receives.</p>
<p>When you change this function you must also update the related unit test - so your existing test becomes a
nice way to test your new Objects as well! Add more tests showing the output of feeding different object-types
to <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code>.</p>
<p>Remember the <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> function from the <a class="reference internal" href="Beginner-Tutorial-Utilities.html"><span class="doc std std-doc">Utility Tutorial</span></a> earlier? We had to use dummy-values since we didnt yet know how we would store properties on Objects in the game.</p>
<p>Well, we just figured out all we need! You can go back and update <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> to properly read the data from the object it receives.</p>
<p>When you change this function you must also update the related unit test - so your existing test becomes a nice way to test your new Objects as well! Add more tests showing the output of feeding different object-types to <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code>.</p>
<p>Try it out yourself. If you need help, a finished utility example is found in <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.utils.html#evennia.contrib.tutorials.evadventure.utils.get_obj_stats" title="evennia.contrib.tutorials.evadventure.utils.get_obj_stats"><span class="xref myst py py-func">evennia/contrib/tutorials/evadventure/utils.py</span></a>.</p>
</section>
</section>

View file

@ -109,9 +109,7 @@
<h1>Part 3: How we get there (example game)<a class="headerlink" href="#part-3-how-we-get-there-example-game" title="Permalink to this headline"></a></h1>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>The tutorial game is under development and is not yet complete, nor tested. Use the existing
lessons as inspiration and to help get you going, but dont expect out-of-the-box perfection
from it at this time.</p>
<p>The tutorial game is under development and is not yet complete, nor tested. Use the existing lessons as inspiration and to help get you going, but dont expect out-of-the-box perfection from it at this time.</p>
</div>
<aside class="sidebar">
<p class="sidebar-title">Beginner Tutorial Parts</p>
@ -131,17 +129,19 @@ from it at this time.</p>
</ul>
</aside>
<p>In part three of the Evennia Beginner tutorial we will go through the actual creation of
our tutorial game <em>EvAdventure</em>, based on the <a class="reference external" href="https://www.drivethrurpg.com/product/250888/Knave">Knave</a>
RPG ruleset.</p>
<p>This is a big part. Youll be seeing a lot of code and there are plenty of lessons to go through.
Take your time!</p>
<p>If you followed the previous parts of this tutorial you will have some notions about Python and where to
find and make use of things in Evennia. We also have a good idea of the type of game we will
create.</p>
our tutorial game <em>EvAdventure</em>, based on the <a class="reference external" href="https://www.drivethrurpg.com/product/250888/Knave">Knave</a> RPG ruleset.</p>
<p>If you followed the previous parts of this tutorial series you will have some notions about Python and where to find and make use of things in Evennia. We also have a good idea of the type of game we will create.</p>
<p>Even if this is not the game-style you are interested in, following along will give you a lot
of experience using Evennia and be really helpful for doing your own thing later!</p>
of experience using Evennia and be really helpful for doing your own thing later! The EvAdventure game code is also built to easily be expanded upon.</p>
<p>Fully coded examples of all code we make in this part can be found in the
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure</span></a> package.</p>
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure</span></a> package. There are three common ways to learn from this:</p>
<ol class="simple">
<li><p>Follow the tutorial lessons in sequence and use it to write your own code, referring to the ready-made code as extra help, context, or as a facit to check yourself.</p></li>
<li><p>Read through the code in the package and refer to the tutorial lesson for each part for more information on what you see.</p></li>
<li><p>Some mix of the two.</p></li>
</ol>
<p>Which approach you choose is individual - we all learn in different ways.</p>
<p>Either way, this is a big part. Youll be seeing a lot of code and there are plenty of lessons to go through. We are making a whole game from scratch after all. Take your time!</p>
<section id="lessons">
<h2>Lessons<a class="headerlink" href="#lessons" title="Permalink to this headline"></a></h2>
<div class="toctree-wrapper compound">
@ -211,12 +211,57 @@ 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-Chargen.html#conclusions">6.10. Conclusions</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Rooms.html">7. In-game Rooms</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-NPCs.html">8. Non-Player-Characters (NPCs)</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Quests.html">9. Game Quests</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Shops.html">10. In-game Shops</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Dungeon.html">11. Dynamically generated Dungeon</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Commands.html">12. In-game Commands</a></li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Rooms.html">7. In-game Rooms</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#the-base-room">7.1. The base room</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#pvp-room">7.2. PvP room</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#adding-a-room-map">7.3. Adding a room map</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#adding-life-to-a-room">7.4. Adding life to a room</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#testing">7.5. Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#conclusion">7.6. Conclusion</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-NPCs.html">8. Non-Player-Characters</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-NPCs.html#the-npc-base-class">8.1. The NPC base class</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-NPCs.html#testing">8.2. Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-NPCs.html#conclusions">8.3. Conclusions</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html">9. Combat base framework</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#combathandler">9.1. CombatHandler</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#actions">9.2. Actions</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#action-dicts">9.3. Action dicts</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#action-classes">9.4. Action classes</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#testing">9.5. Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#conclusions">9.6. Conclusions</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html">10. Twitch Combat</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#general-principle">10.1. General principle</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#twitch-combat-handler">10.2. Twitch combat handler</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#commands">10.3. Commands</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#grouping-commands-for-use">10.4. Grouping Commands for use</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#unit-testing">10.5. Unit Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#a-small-combat-test">10.6. A small combat test</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#conclusions">10.7. Conclusions</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html">11. Turnbased Combat</a><ul>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#general-principle">11.1. General Principle</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#turnbased-combat-handler">11.2. Turnbased combat handler</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#using-evmenu-for-the-combat-menu">11.3. Using EvMenu for the combat menu</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#menu-for-turnbased-combat">11.4. Menu for Turnbased combat</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#attack-command">11.5. Attack Command</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#making-sure-the-menu-stops">11.6. Making sure the menu stops</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#testing">11.7. Testing</a></li>
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#a-small-combat-test">11.8. A small combat test</a></li>
<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-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>
</ul>
</div>
</section>

View file

@ -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>9. Game Quests &#8212; Evennia 1.0 documentation</title>
<title>14. Game Quests &#8212; Evennia 1.0 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>
@ -17,8 +17,8 @@
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="10. In-game Shops" href="Beginner-Tutorial-Shops.html" />
<link rel="prev" title="8. Non-Player-Characters (NPCs)" href="Beginner-Tutorial-NPCs.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" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
@ -30,16 +30,16 @@
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Shops.html" title="10. In-game Shops"
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters (NPCs)"
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</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> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">9. </span>Game Quests</a></li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">14. </span>Game Quests</a></li>
</ul>
</div>
@ -62,11 +62,11 @@
</div>
<script>$('#searchbox').show(0);</script>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-NPCs.html"
title="previous chapter"><span class="section-number">8. </span>Non-Player-Characters (NPCs)</a></p>
<p class="topless"><a href="Beginner-Tutorial-Dungeon.html"
title="previous chapter"><span class="section-number">13. </span>Dynamically generated Dungeon</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Shops.html"
title="next chapter"><span class="section-number">10. </span>In-game Shops</a></p>
title="next chapter"><span class="section-number">15. </span>In-game Shops</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
@ -99,7 +99,7 @@
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="game-quests">
<h1><span class="section-number">9. </span>Game Quests<a class="headerlink" href="#game-quests" title="Permalink to this headline"></a></h1>
<h1><span class="section-number">14. </span>Game Quests<a class="headerlink" href="#game-quests" title="Permalink to this headline"></a></h1>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>This part of the Beginner tutorial is still being developed.</p>
@ -122,16 +122,16 @@
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Shops.html" title="10. In-game Shops"
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters (NPCs)"
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How we get there (example game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">9. </span>Game Quests</a></li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">14. </span>Game Quests</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">

View file

@ -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="8. Non-Player-Characters (NPCs)" href="Beginner-Tutorial-NPCs.html" />
<link rel="next" title="8. Non-Player-Characters" href="Beginner-Tutorial-NPCs.html" />
<link rel="prev" title="6. Character Generation" href="Beginner-Tutorial-Chargen.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
@ -30,7 +30,7 @@
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters (NPCs)"
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Chargen.html" title="6. Character Generation"
@ -61,12 +61,25 @@
</div>
</div>
<script>$('#searchbox').show(0);</script>
<h3><a href="../../../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">7. In-game Rooms</a><ul>
<li><a class="reference internal" href="#the-base-room">7.1. The base room</a></li>
<li><a class="reference internal" href="#pvp-room">7.2. PvP room</a></li>
<li><a class="reference internal" href="#adding-a-room-map">7.3. Adding a room map</a></li>
<li><a class="reference internal" href="#adding-life-to-a-room">7.4. Adding life to a room</a></li>
<li><a class="reference internal" href="#testing">7.5. Testing</a></li>
<li><a class="reference internal" href="#conclusion">7.6. Conclusion</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Chargen.html"
title="previous chapter"><span class="section-number">6. </span>Character Generation</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-NPCs.html"
title="next chapter"><span class="section-number">8. </span>Non-Player-Characters (NPCs)</a></p>
title="next chapter"><span class="section-number">8. </span>Non-Player-Characters</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
@ -100,10 +113,348 @@
<section class="tex2jax_ignore mathjax_ignore" id="in-game-rooms">
<h1><span class="section-number">7. </span>In-game Rooms<a class="headerlink" href="#in-game-rooms" title="Permalink to this headline"></a></h1>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>This part of the Beginner tutorial is still being developed.</p>
<p>A <em>room</em> describes a specific location in the game world. Being an abstract concept, it can represent any area of game content that is convenient to group together. In this lesson we will also create a small in-game automap.</p>
<p>In EvAdventure, we will have two main types of rooms:</p>
<ul class="simple">
<li><p>Normal, above-ground rooms. Based on a fixed map, these will be created once and then dont change. Well cover them in this lesson.</p></li>
<li><p>Dungeon rooms - these will be examples of <em>procedurally generated</em> rooms, created on the fly as the players explore the underworld. Being subclasses of the normal room, well get to them in the <a class="reference internal" href="Beginner-Tutorial-Dungeon.html"><span class="doc std std-doc">Dungeon generation lesson</span></a>.</p></li>
</ul>
<section id="the-base-room">
<h2><span class="section-number">7.1. </span>The base room<a class="headerlink" href="#the-base-room" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/rooms.py</span></code>.</p>
</div></blockquote>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/rooms.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">AttributeProperty</span><span class="p">,</span> <span class="n">DefaultRoom</span>
<span class="k">class</span> <span class="nc">EvAdventureRoom</span><span class="p">(</span><span class="n">DefaultRoom</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Simple room supporting some EvAdventure-specifics.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">allow_combat</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">allow_pvp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">allow_death</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</pre></div>
</div>
<p>Our <code class="docutils literal notranslate"><span class="pre">EvadventureRoom</span></code> is very simple. We use Evennias <code class="docutils literal notranslate"><span class="pre">DefaultRoom</span></code> as a base and just add three additional Attributes that defines</p>
<ul class="simple">
<li><p>If combat is allowed to start in the room at all.</p></li>
<li><p>If combat is allowed, if PvP (player vs player) combat is allowed.</p></li>
<li><p>If combat is allowed, if any side is allowed to die from it.</p></li>
</ul>
<p>Later on we must make sure our combat systems honors these values.</p>
</section>
<section id="pvp-room">
<h2><span class="section-number">7.2. </span>PvP room<a class="headerlink" href="#pvp-room" title="Permalink to this headline"></a></h2>
<p>Heres a room that allows non-lethal PvP (sparring):</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/rooms.py</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EvAdventurePvPRoom</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Room where PvP can happen, but noone gets killed.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">allow_combat</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">True</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">allow_pvp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">True</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_display_footer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Customize footer of description.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="s2">&quot;|yNon-lethal PvP combat is allowed here!|n&quot;</span>
</pre></div>
</div>
<p>The return of <code class="docutils literal notranslate"><span class="pre">get_display_footer</span></code> will show after the <a class="reference internal" href="../../../Components/Objects.html#changing-an-objects-appearance"><span class="std std-doc">main room description</span></a>, showing that the room is a sparring room. This means that when a player drops to 0 HP, they will lose the combat, but dont stand any risk of dying (weapons wear out normally during sparring though).</p>
</section>
<section id="adding-a-room-map">
<h2><span class="section-number">7.3. </span>Adding a room map<a class="headerlink" href="#adding-a-room-map" title="Permalink to this headline"></a></h2>
<p>We want a dynamic map that visualizes the exits you can use at any moment. Heres how our room will display:</p>
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span><span class="w"> </span>o<span class="w"> </span>o<span class="w"> </span>o
<span class="w"> </span><span class="se">\|</span>/
<span class="w"> </span>o-@-o
<span class="w"> </span><span class="p">|</span><span class="w"> </span>
<span class="w"> </span>o
The<span class="w"> </span>crossroads<span class="w"> </span>
A<span class="w"> </span>place<span class="w"> </span>where<span class="w"> </span>many<span class="w"> </span>roads<span class="w"> </span>meet.<span class="w"> </span>
Exits:<span class="w"> </span>north,<span class="w"> </span>northeast,<span class="w"> </span>south,<span class="w"> </span>west,<span class="w"> </span>and<span class="w"> </span>northwest
</pre></div>
</div>
<blockquote>
<div><p>Documentation does not show ansi colors.</p>
</div></blockquote>
<p>Lets expand the base <code class="docutils literal notranslate"><span class="pre">EvAdventureRoom</span></code> with the map.</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>
<span class="normal">58</span>
<span class="normal">59</span>
<span class="normal">60</span>
<span class="normal">61</span>
<span class="normal">62</span>
<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>
<span class="normal">68</span>
<span class="normal">69</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventyre/rooms.py</span>
<span class="c1"># ... </span>
<span class="kn">from</span> <span class="nn">copy</span> <span class="kn">import</span> <span class="n">deepcopy</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span>
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">inherits_from</span>
<span class="n">CHAR_SYMBOL</span> <span class="o">=</span> <span class="s2">&quot;|w@|n&quot;</span>
<span class="n">CHAR_ALT_SYMBOL</span> <span class="o">=</span> <span class="s2">&quot;|w&gt;|n&quot;</span>
<span class="n">ROOM_SYMBOL</span> <span class="o">=</span> <span class="s2">&quot;|bo|n&quot;</span>
<span class="hll"><span class="n">LINK_COLOR</span> <span class="o">=</span> <span class="s2">&quot;|B&quot;</span>
</span>
<span class="n">_MAP_GRID</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">[</span><span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">],</span>
<span class="p">[</span><span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">],</span>
<span class="p">[</span><span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot;@&quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">],</span>
<span class="p">[</span><span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">],</span>
<span class="hll"> <span class="p">[</span><span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot; &quot;</span><span class="p">],</span>
</span><span class="p">]</span>
<span class="n">_EXIT_GRID_SHIFT</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;north&quot;</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;||&quot;</span><span class="p">),</span>
<span class="s2">&quot;east&quot;</span><span class="p">:</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">&quot;-&quot;</span><span class="p">),</span>
<span class="s2">&quot;south&quot;</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;||&quot;</span><span class="p">),</span>
<span class="s2">&quot;west&quot;</span><span class="p">:</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">&quot;-&quot;</span><span class="p">),</span>
<span class="s2">&quot;northeast&quot;</span><span class="p">:</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;/&quot;</span><span class="p">),</span>
<span class="s2">&quot;southeast&quot;</span><span class="p">:</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;</span><span class="se">\\</span><span class="s2">&quot;</span><span class="p">),</span>
<span class="s2">&quot;southwest&quot;</span><span class="p">:</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;/&quot;</span><span class="p">),</span>
<span class="s2">&quot;northwest&quot;</span><span class="p">:</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;</span><span class="se">\\</span><span class="s2">&quot;</span><span class="p">),</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">EvAdventureRoom</span><span class="p">(</span><span class="n">DefaultRoom</span><span class="p">):</span>
<span class="c1"># ... </span>
<span class="k">def</span> <span class="nf">format_appearance</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">appearance</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Don&#39;t left-strip the appearance string&quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">appearance</span><span class="o">.</span><span class="n">rstrip</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">get_display_header</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Display the current location as a mini-map.</span>
<span class="sd"> </span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># make sure to not show make a map for users of screenreaders.</span>
<span class="c1"># for optimization we also don&#39;t show it to npcs/mobs</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">inherits_from</span><span class="p">(</span><span class="n">looker</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span>
<span class="n">looker</span><span class="o">.</span><span class="n">account</span> <span class="ow">and</span> <span class="n">looker</span><span class="o">.</span><span class="n">account</span><span class="o">.</span><span class="n">uses_screenreader</span><span class="p">()</span>
<span class="p">):</span>
<span class="k">return</span> <span class="s2">&quot;&quot;</span>
<span class="hll">
</span><span class="hll"> <span class="c1"># build a map</span>
</span> <span class="n">map_grid</span> <span class="o">=</span> <span class="n">deepcopy</span><span class="p">(</span><span class="n">_MAP_GRID</span><span class="p">)</span>
<span class="n">dx0</span><span class="p">,</span> <span class="n">dy0</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span>
<span class="n">map_grid</span><span class="p">[</span><span class="n">dy0</span><span class="p">][</span><span class="n">dx0</span><span class="p">]</span> <span class="o">=</span> <span class="n">CHAR_SYMBOL</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">exits</span><span class="p">:</span>
<span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="p">,</span> <span class="n">symbol</span> <span class="o">=</span> <span class="n">_EXIT_GRID_SHIFT</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">exi</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">))</span>
<span class="hll"> <span class="k">if</span> <span class="n">symbol</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span> <span class="c1"># we have a non-cardinal direction to go to - indicate this</span>
<span class="n">map_grid</span><span class="p">[</span><span class="n">dy0</span><span class="p">][</span><span class="n">dx0</span><span class="p">]</span> <span class="o">=</span> <span class="n">CHAR_ALT_SYMBOL</span>
<span class="k">continue</span>
<span class="n">map_grid</span><span class="p">[</span><span class="n">dy0</span> <span class="o">+</span> <span class="n">dy</span><span class="p">][</span><span class="n">dx0</span> <span class="o">+</span> <span class="n">dx</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">LINK_COLOR</span><span class="si">}{</span><span class="n">symbol</span><span class="si">}</span><span class="s2">|n&quot;</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="bp">self</span><span class="p">:</span>
<span class="n">map_grid</span><span class="p">[</span><span class="n">dy0</span> <span class="o">+</span> <span class="n">dy</span> <span class="o">+</span> <span class="n">dy</span><span class="p">][</span><span class="n">dx0</span> <span class="o">+</span> <span class="n">dx</span> <span class="o">+</span> <span class="n">dx</span><span class="p">]</span> <span class="o">=</span> <span class="n">ROOM_SYMBOL</span>
<span class="c1"># Note that on the grid, dy is really going *downwards* (origo is</span>
<span class="hll"> <span class="c1"># in the top left), so we need to reverse the order at the end to mirror it</span>
</span> <span class="c1"># vertically and have it come out right.</span>
<span class="k">return</span> <span class="s2">&quot; &quot;</span> <span class="o">+</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2"> &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="n">map_grid</span><span class="p">))</span>
</pre></div></td></tr></table></div>
</div>
<p>The string returned from <code class="docutils literal notranslate"><span class="pre">get_display_header</span></code> will end up at the top of the <span class="xref myst">room description</span>, a good place to have the map appear!</p>
<ul class="simple">
<li><p><strong>Line 12</strong>: The map itself consists of the 2D matrix <code class="docutils literal notranslate"><span class="pre">_MAP_GRID</span></code>. This is a 2D area described by a list of Python lists. To find a given place in the list, you first first need to find which of the nested lists to use, and then which element to use in that list. Indices start from 0 in Python. So to draw the <code class="docutils literal notranslate"><span class="pre">o</span></code> symbol for the southermost room, youd need to do so at <code class="docutils literal notranslate"><span class="pre">_MAP_GRID[4][2]</span></code>.</p></li>
<li><p><strong>Line 19</strong>: The <code class="docutils literal notranslate"><span class="pre">_EXIT_GRID_SHIFT</span></code> indicates the direction to go for each cardinal exit, along with the map symbol to draw at that point. So <code class="docutils literal notranslate"><span class="pre">&quot;east&quot;:</span> <span class="pre">(1,</span> <span class="pre">0,</span> <span class="pre">&quot;-&quot;)</span></code> means the east exit will be drawn one step in the positive x direction (to the right), using the “-” symbol. For symbols like <code class="docutils literal notranslate"><span class="pre">|</span></code> and “\” we need to escape with a double-symbol since these would otherwise be interpreted as part of other formatting.</p></li>
<li><p><strong>Line 51</strong>: We start by making a <code class="docutils literal notranslate"><span class="pre">deepcopy</span></code> of the <code class="docutils literal notranslate"><span class="pre">_MAP_GRID</span></code>. This is so that we dont modify the original but always have an empty template to work from.</p></li>
<li><p><strong>Line 52</strong>: We use <code class="docutils literal notranslate"><span class="pre">&#64;</span></code> to indicate the location of the player (at coordinate <code class="docutils literal notranslate"><span class="pre">(2,</span> <span class="pre">2)</span></code>). We then take the actual exits from the room use their names to figure out what symbols to draw out from the center.</p></li>
<li><p><strong>Line 58</strong>: We want to be able to get on/off the grid if so needed. So if a room has a non-cardinal exit in it (like back or up/down), well indicate this by showing the <code class="docutils literal notranslate"><span class="pre">&gt;</span></code> symbol instead of the <code class="docutils literal notranslate"><span class="pre">&#64;</span></code> in your current room.</p></li>
<li><p><strong>Line 67</strong>: Once we have placed all the exit- and room-symbols in the grid, we merge it all together into a single string. At the end we use Pythons standard <a class="reference external" href="https://www.w3schools.com/python/ref_string_join.asp">join</a> to convert the grid into a single string. In doing so we must flip the grid upside down (reverse the outermost list). Why is this? If you think about how a MUD game displays its data - by printing at the bottom and then scrolling upwards - youll realize that Evennia has to send out the top of your map <em>first</em> and the bottom of it <em>last</em> for it to show correctly to the user.</p></li>
</ul>
</section>
<section id="adding-life-to-a-room">
<h2><span class="section-number">7.4. </span>Adding life to a room<a class="headerlink" href="#adding-life-to-a-room" title="Permalink to this headline"></a></h2>
<p>Normally the room is static until you do something in it. But lets say you are in a room described to be a bustling market. Would it not be nice to occasionally get some random messages like</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&quot;You hear a merchant calling out his wares.&quot;
&quot;The sound of music drifts over the square from an open tavern door.&quot;
&quot;The sound of commerse rises and fall in a steady rythm.&quot;
</pre></div>
</div>
<p>Heres an example of how to accomplish this:</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></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/rooms.py </span>
<span class="c1"># ... </span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">choice</span><span class="p">,</span> <span class="n">random</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">TICKER_HANDLER</span>
<span class="c1"># ... </span>
<span class="k">class</span> <span class="nc">EchoingRoom</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;A room that randomly echoes messages to everyone inside it&quot;&quot;&quot;</span>
<span class="n">echoes</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">echo_rate</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">60</span> <span class="o">*</span> <span class="mi">2</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">echo_chance</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mf">0.1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">send_echo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">echoes</span> <span class="ow">and</span> <span class="n">random</span><span class="p">()</span> <span class="o">&lt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">echo_chance</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="n">choice</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">echoes</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">start_echo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="hll"> <span class="n">TICKER_HANDLER</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">echo_rate</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">send_echo</span><span class="p">)</span>
</span>
<span class="k">def</span> <span class="nf">stop_echo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="hll"> <span class="n">TICKER_HANDLER</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">echo_rate</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">send_echo</span><span class="p">)</span>
</span></pre></div></td></tr></table></div>
</div>
<p>The <a class="reference internal" href="../../../Components/TickerHandler.html"><span class="doc std std-doc">TickerHandler</span></a>. This is acts as a please tick me - subscription service. In <strong>Line 22</strong> we tell add our <code class="docutils literal notranslate"><span class="pre">.send_echo</span></code> method to the handler and tell the TickerHandler to call that method every <code class="docutils literal notranslate"><span class="pre">.echo_rate</span></code> seconds.</p>
<p>When the <code class="docutils literal notranslate"><span class="pre">.send_echo</span></code> method is called, it will use <code class="docutils literal notranslate"><span class="pre">random.random()</span></code> to check if we should <em>actually</em> do anything. In our example we only show a message 10% of the time. In that case we use Pythons <code class="docutils literal notranslate"><span class="pre">random.choice()</span></code> to grab a random text string from the <code class="docutils literal notranslate"><span class="pre">.echoes</span></code> list to send to everyone inside this room.</p>
<p>Heres how youd use this room in-game:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; dig market:evadventure.EchoingRoom = market,back
&gt; market
&gt; set here/echoes = [&quot;You hear a merchant shouting&quot;, &quot;You hear the clatter of coins&quot;]
&gt; py here.start_echo()
</pre></div>
</div>
<p>If you wait a while youll eventually see one of the two echoes show up. Use <code class="docutils literal notranslate"><span class="pre">py</span> <span class="pre">here.stop_echo()</span></code> if you want.</p>
<p>Its a good idea to be able to turn on/off the echoes at will, if nothing else because youd be surprised how annoying they can be if they show too often.</p>
<p>In this example we had to resort to <code class="docutils literal notranslate"><span class="pre">py</span></code> to activate/deactivate the echoes, but you could very easily make little utility <a class="reference internal" href="../Part1/Beginner-Tutorial-Adding-Commands.html"><span class="doc std std-doc">Commands</span></a> <code class="docutils literal notranslate"><span class="pre">startecho</span></code> and <code class="docutils literal notranslate"><span class="pre">stopecho</span></code> to do it for you. This we leave as a bonus exercise.</p>
</section>
<section id="testing">
<h2><span class="section-number">7.5. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline"></a></h2>
<blockquote>
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/tests/test_rooms.py</span></code>.</p>
</div></blockquote>
<aside class="sidebar">
<p>You can find a ready testing module <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_rooms.html#evennia-contrib-tutorials-evadventure-tests-test-rooms"><span class="std std-ref">here in the tutorial folder</span></a>.</p>
</aside>
<p>The main thing to test with our new rooms is the map. Heres the basic principle for how to do this testing:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/tests/test_rooms.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultExit</span><span class="p">,</span> <span class="n">create_object</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTestCase</span>
<span class="kn">from</span> <span class="nn">..characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
<span class="kn">from</span> <span class="nn">..rooms</span> <span class="kn">import</span> <span class="n">EvAdventureRoom</span>
<span class="k">class</span> <span class="nc">EvAdventureRoomTest</span><span class="p">(</span><span class="n">EvenniaTestCase</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">test_map</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">center_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room_center&quot;</span><span class="p">)</span>
<span class="n">n_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room_n)</span>
<span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;north&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">n_room</span><span class="p">)</span>
<span class="n">ne_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room=ne&quot;</span><span class="p">)</span>
<span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;northeast&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">ne_room</span><span class="p">)</span>
<span class="c1"># ... etc for all cardinal directions </span>
<span class="n">char</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;TestChar&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">)</span>
<span class="n">desc</span> <span class="o">=</span> <span class="n">center_room</span><span class="o">.</span><span class="n">return_appearance</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
<span class="c1"># compare the desc we got with the expected description here</span>
</pre></div>
</div>
<p>So we create a bunch of rooms, link them to one centr room and then make sure the map in that room looks like wed expect.</p>
</section>
<section id="conclusion">
<h2><span class="section-number">7.6. </span>Conclusion<a class="headerlink" href="#conclusion" title="Permalink to this headline"></a></h2>
<p>In this lesson we manipulated strings and made a map. Changing the description of an object is a big part of changing the graphics of a text-based game, so checking out the <span class="xref myst">parts making up an object description</span> is good extra reading.</p>
</section>
</section>
@ -122,7 +473,7 @@
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters (NPCs)"
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Chargen.html" title="6. Character Generation"

View file

@ -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>10. In-game Shops &#8212; Evennia 1.0 documentation</title>
<title>15. In-game Shops &#8212; Evennia 1.0 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>
@ -17,8 +17,8 @@
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="11. Dynamically generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
<link rel="prev" title="9. Game Quests" href="Beginner-Tutorial-Quests.html" />
<link rel="next" title="16. In-game Commands" href="Beginner-Tutorial-Commands.html" />
<link rel="prev" title="14. Game Quests" href="Beginner-Tutorial-Quests.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
@ -30,16 +30,16 @@
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Dungeon.html" title="11. Dynamically generated Dungeon"
<a href="Beginner-Tutorial-Commands.html" title="16. In-game Commands"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Quests.html" title="9. Game Quests"
<a href="Beginner-Tutorial-Quests.html" title="14. Game Quests"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</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> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">10. </span>In-game Shops</a></li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">15. </span>In-game Shops</a></li>
</ul>
</div>
@ -63,10 +63,10 @@
<script>$('#searchbox').show(0);</script>
<h4>Previous topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Quests.html"
title="previous chapter"><span class="section-number">9. </span>Game Quests</a></p>
title="previous chapter"><span class="section-number">14. </span>Game Quests</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Beginner-Tutorial-Dungeon.html"
title="next chapter"><span class="section-number">11. </span>Dynamically generated Dungeon</a></p>
<p class="topless"><a href="Beginner-Tutorial-Commands.html"
title="next chapter"><span class="section-number">16. </span>In-game Commands</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
@ -99,7 +99,7 @@
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="in-game-shops">
<h1><span class="section-number">10. </span>In-game Shops<a class="headerlink" href="#in-game-shops" title="Permalink to this headline"></a></h1>
<h1><span class="section-number">15. </span>In-game Shops<a class="headerlink" href="#in-game-shops" title="Permalink to this headline"></a></h1>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>This part of the Beginner tutorial is still being developed.</p>
@ -122,16 +122,16 @@
<a href="../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Dungeon.html" title="11. Dynamically generated Dungeon"
<a href="Beginner-Tutorial-Commands.html" title="16. In-game Commands"
>next</a> |</li>
<li class="right" >
<a href="Beginner-Tutorial-Quests.html" title="9. Game Quests"
<a href="Beginner-Tutorial-Quests.html" title="14. Game Quests"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How we get there (example game)</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">10. </span>In-game Shops</a></li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">15. </span>In-game Shops</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">

View file

@ -216,11 +216,24 @@ folders into packages Python understands to import from.</p>
<span class="n">ALLEGIANCE_HOSTILE</span> <span class="o">=</span> <span class="s2">&quot;hostile&quot;</span>
<span class="n">ALLEGIANCE_NEUTRAL</span> <span class="o">=</span> <span class="s2">&quot;neutral&quot;</span>
<span class="n">ALLEGIANCE_FRIENDLY</span> <span class="o">=</span> <span class="s2">&quot;friendly&quot;</span>
<span class="n">ABILITY_REVERSE_MAP</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;str&quot;</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">&quot;dex&quot;</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="s2">&quot;con&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CON</span><span class="p">,</span>
<span class="s2">&quot;int&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">INT</span><span class="p">,</span>
<span class="s2">&quot;wis&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">WIS</span><span class="p">,</span>
<span class="s2">&quot;cha&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CHA</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Here the <code class="docutils literal notranslate"><span class="pre">Ability</span></code> class holds basic properties of a character sheet.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">ABILITY_REVERSE_MAP</span></code> is a convenient map to go the other way - if you in some command were to enter the string cha, we could use this mapping to directly convert your input to the correct <code class="docutils literal notranslate"><span class="pre">Ability</span></code>:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>ability = ABILITY_REVERSE_MAP.get(your_input)
</pre></div>
</div>
</section>
<section id="utility-module">
<h2><span class="section-number">1.3. </span>Utility module<a class="headerlink" href="#utility-module" title="Permalink to this headline"></a></h2>

View file

@ -18,7 +18,7 @@
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="next" title="1. Unimplemented" href="../../../Unimplemented.html" />
<link rel="prev" title="12. In-game Commands" href="../Part3/Beginner-Tutorial-Commands.html" />
<link rel="prev" title="16. In-game Commands" href="../Part3/Beginner-Tutorial-Commands.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
@ -33,7 +33,7 @@
<a href="../../../Unimplemented.html" title="1. Unimplemented"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="../Part3/Beginner-Tutorial-Commands.html" title="12. In-game Commands"
<a href="../Part3/Beginner-Tutorial-Commands.html" title="16. In-game Commands"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
@ -70,7 +70,7 @@
<h4>Previous topic</h4>
<p class="topless"><a href="../Part3/Beginner-Tutorial-Commands.html"
title="previous chapter"><span class="section-number">12. </span>In-game Commands</a></p>
title="previous chapter"><span class="section-number">16. </span>In-game Commands</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="../../../Unimplemented.html"
title="next chapter"><span class="section-number">1. </span>Unimplemented</a></p>
@ -155,7 +155,7 @@
<a href="../../../Unimplemented.html" title="1. Unimplemented"
>next</a> |</li>
<li class="right" >
<a href="../Part3/Beginner-Tutorial-Commands.html" title="12. In-game Commands"
<a href="../Part3/Beginner-Tutorial-Commands.html" title="16. In-game Commands"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>

View file

@ -171,11 +171,15 @@ a small but full game with Evennia. Other tutorials and howtos tend to assume
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Equipment.html">5. Handling Equipment</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Chargen.html">6. Character Generation</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Rooms.html">7. In-game Rooms</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-NPCs.html">8. Non-Player-Characters (NPCs)</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Quests.html">9. Game Quests</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Shops.html">10. In-game Shops</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Dungeon.html">11. Dynamically generated Dungeon</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Commands.html">12. In-game Commands</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-NPCs.html">8. Non-Player-Characters</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Base.html">9. Combat base framework</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Twitch.html">10. Twitch Combat</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Turnbased.html">11. Turnbased Combat</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-AI.html">12. NPC and monster AI</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Dungeon.html">13. Dynamically generated Dungeon</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Quests.html">14. Game Quests</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Shops.html">15. In-game Shops</a></li>
<li class="toctree-l3"><a class="reference internal" href="Beginner-Tutorial/Part3/Beginner-Tutorial-Commands.html">16. In-game Commands</a></li>
</ul>
</li>
</ul>

View file

@ -196,7 +196,7 @@ with symlinks not being supported. Answer is to not use NTFS (seriously, why wou
<h2>Mac Troubleshooting<a class="headerlink" href="#mac-troubleshooting" title="Permalink to this headline"></a></h2>
<ul class="simple">
<li><p>Some Mac users have reported not being able to connect to <code class="docutils literal notranslate"><span class="pre">localhost</span></code> (i.e. your own computer). If so, try to connect to <code class="docutils literal notranslate"><span class="pre">127.0.0.1</span></code> instead, which is the same thing. Use port 4000 from mud clients and port 4001 from the web browser as usual.</p></li>
<li><p>If you get a <code class="docutils literal notranslate"><span class="pre">MemoryError</span></code> when starting Evennia, or when looking at the log, this may be due to an sqlite versioning issue. <a class="reference external" href="https://github.com/evennia/evennia/discussions/2638#discussioncomment-3630761">A user in our forums</a> found a working solution for this. <a class="reference external" href="https://github.com/evennia/evennia/issues/3120#issuecomment-1442540538">Here</a> is another variation to solve it.</p></li>
<li><p>If you get a <code class="docutils literal notranslate"><span class="pre">MemoryError</span></code> when starting Evennia, or when looking at the log, this may be due to an sqlite versioning issue. <a class="reference external" href="https://github.com/evennia/evennia/discussions/2637">A user in our forums</a> found a working solution for this. <a class="reference external" href="https://github.com/evennia/evennia/issues/2854">Here</a> is another variation to solve it.</p></li>
</ul>
</section>
<section id="windows-troubleshooting">

View file

@ -149,7 +149,6 @@
<span class="kn">from</span> <span class="nn">django.conf</span> <span class="kn">import</span> <span class="n">settings</span>
<span class="kn">from</span> <span class="nn">django.utils.translation</span> <span class="kn">import</span> <span class="n">gettext</span> <span class="k">as</span> <span class="n">_</span>
<span class="kn">from</span> <span class="nn">evennia.commands.cmdset</span> <span class="kn">import</span> <span class="n">CmdSet</span>
<span class="kn">from</span> <span class="nn">evennia.server.models</span> <span class="kn">import</span> <span class="n">ServerConfig</span>
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">logger</span><span class="p">,</span> <span class="n">utils</span>
@ -244,7 +243,6 @@
<span class="p">]</span>
<span class="n">errstring</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
<span class="k">for</span> <span class="n">python_path</span> <span class="ow">in</span> <span class="n">python_paths</span><span class="p">:</span>
<span class="k">if</span> <span class="s2">&quot;.&quot;</span> <span class="ow">in</span> <span class="n">path</span><span class="p">:</span>
<span class="n">modpath</span><span class="p">,</span> <span class="n">classname</span> <span class="o">=</span> <span class="n">python_path</span><span class="o">.</span><span class="n">rsplit</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>

View file

@ -1506,6 +1506,7 @@
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">&quot;Building&quot;</span>
<span class="n">new_obj_lockstring</span> <span class="o">=</span> <span class="s2">&quot;control:id(</span><span class="si">{id}</span><span class="s2">) or perm(Admin);delete:id(</span><span class="si">{id}</span><span class="s2">) or perm(Admin)&quot;</span>
<span class="c1"># a custom member method to chug out exits and do checks</span>
<div class="viewcode-block" id="CmdOpen.create_exit"><a class="viewcode-back" href="../../../../api/evennia.commands.default.building.html#evennia.commands.default.building.CmdOpen.create_exit">[docs]</a> <span class="k">def</span> <span class="nf">create_exit</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exit_name</span><span class="p">,</span> <span class="n">location</span><span class="p">,</span> <span class="n">destination</span><span class="p">,</span> <span class="n">exit_aliases</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">typeclass</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
@ -2132,7 +2133,6 @@
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">&quot;Building&quot;</span>
<span class="k">def</span> <span class="nf">_generic_search</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">query</span><span class="p">,</span> <span class="n">typeclass_path</span><span class="p">):</span>
<span class="n">caller</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span>
<span class="k">if</span> <span class="n">typeclass_path</span><span class="p">:</span>
<span class="c1"># make sure we search the right database table</span>
@ -3345,7 +3345,6 @@
<span class="p">)</span>
<span class="k">for</span> <span class="n">script</span> <span class="ow">in</span> <span class="n">scripts</span><span class="p">:</span>
<span class="n">nextrep</span> <span class="o">=</span> <span class="n">script</span><span class="o">.</span><span class="n">time_until_next_repeat</span><span class="p">()</span>
<span class="k">if</span> <span class="n">nextrep</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">nextrep</span> <span class="o">=</span> <span class="n">script</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">_paused_time</span>
@ -3816,7 +3815,6 @@
<span class="n">use_destination</span><span class="o">=</span><span class="s2">&quot;intoexit&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">switches</span><span class="p">,</span>
<span class="n">move_type</span><span class="o">=</span><span class="s2">&quot;teleport&quot;</span><span class="p">,</span>
<span class="p">):</span>
<span class="k">if</span> <span class="n">obj_to_teleport</span> <span class="o">==</span> <span class="n">caller</span><span class="p">:</span>
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Teleported to </span><span class="si">{</span><span class="n">destination</span><span class="si">}</span><span class="s2">.&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>

View file

@ -166,7 +166,6 @@
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">from</span> <span class="nn">django.conf</span> <span class="kn">import</span> <span class="n">settings</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">CmdSet</span><span class="p">,</span> <span class="n">DefaultRoom</span><span class="p">,</span> <span class="n">default_cmds</span><span class="p">,</span> <span class="n">gametime</span><span class="p">,</span> <span class="n">utils</span>
<span class="c1"># error return function, needed by Extended Look command</span>
@ -439,7 +438,9 @@
<span class="k">if</span> <span class="n">detail</span><span class="p">:</span>
<span class="c1"># we found a detail</span>
<span class="c1"># tell all the objects in the room we&#39;re looking closely at something</span>
<span class="n">caller</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;$You() $conj(look) closely at </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">.</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">from_obj</span><span class="o">=</span><span class="n">caller</span><span class="p">)</span>
<span class="n">caller</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(look) closely at </span><span class="si">{</span><span class="n">args</span><span class="si">}</span><span class="s2">.</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">from_obj</span><span class="o">=</span><span class="n">caller</span>
<span class="p">)</span>
<span class="c1"># show the detail to the player</span>
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">detail</span><span class="p">)</span>
<span class="k">return</span>

View file

@ -84,7 +84,6 @@
<span class="kn">from</span> <span class="nn">evennia.objects.objects</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span>
<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.utils.evform</span> <span class="kn">import</span> <span class="n">EvForm</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="p">,</span> <span class="n">ask_yes_no</span>
<span class="kn">from</span> <span class="nn">evennia.utils.evtable</span> <span class="kn">import</span> <span class="n">EvTable</span>
<span class="kn">from</span> <span class="nn">evennia.utils.logger</span> <span class="kn">import</span> <span class="n">log_trace</span>
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">lazy_property</span>
@ -92,7 +91,6 @@
<span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">rules</span>
<span class="kn">from</span> <span class="nn">.equipment</span> <span class="kn">import</span> <span class="n">EquipmentError</span><span class="p">,</span> <span class="n">EquipmentHandler</span>
<span class="kn">from</span> <span class="nn">.quests</span> <span class="kn">import</span> <span class="n">EvAdventureQuestHandler</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">get_obj_stats</span>
<div class="viewcode-block" id="LivingMixin"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.characters.html#evennia.contrib.tutorials.evadventure.characters.LivingMixin">[docs]</a><span class="k">class</span> <span class="nc">LivingMixin</span><span class="p">:</span>
@ -142,6 +140,13 @@
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;You are healed for </span><span class="si">{</span><span class="n">healed</span><span class="si">}</span><span class="s2"> health.&quot;</span><span class="p">)</span></div>
<div class="viewcode-block" id="LivingMixin.at_attacked"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.characters.html#evennia.contrib.tutorials.evadventure.characters.LivingMixin.at_attacked">[docs]</a> <span class="k">def</span> <span class="nf">at_attacked</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attacker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Called when being attacked / combat starts.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">pass</span></div>
<div class="viewcode-block" id="LivingMixin.at_damage"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.characters.html#evennia.contrib.tutorials.evadventure.characters.LivingMixin.at_damage">[docs]</a> <span class="k">def</span> <span class="nf">at_damage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">damage</span><span class="p">,</span> <span class="n">attacker</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Called when attacked and taking damage.</span>

View file

@ -81,10 +81,9 @@
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">django.conf</span> <span class="kn">import</span> <span class="n">settings</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_object</span>
<span class="kn">from</span> <span class="nn">evennia.objects.models</span> <span class="kn">import</span> <span class="n">ObjectDB</span>
<span class="kn">from</span> <span class="nn">evennia.prototypes.spawner</span> <span class="kn">import</span> <span class="n">spawn</span>
<span class="kn">from</span> <span class="nn">evennia.utils.create</span> <span class="kn">import</span> <span class="n">create_object</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">.characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>

View file

@ -0,0 +1,616 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>evennia.contrib.tutorials.evadventure.combat_base &#8212; Evennia 1.0 documentation</title>
<link rel="stylesheet" href="../../../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../../../" src="../../../../../_static/documentation_options.js"></script>
<script src="../../../../../_static/jquery.js"></script>
<script src="../../../../../_static/underscore.js"></script>
<script src="../../../../../_static/doctools.js"></script>
<script src="../../../../../_static/language_data.js"></script>
<link rel="shortcut icon" href="../../../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../../../genindex.html" />
<link rel="search" title="Search" href="../../../../../search.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../../../evennia.html" accesskey="U">evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.contrib.tutorials.evadventure.combat_base</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="../../../../../index.html">
<img class="logo" src="../../../../../_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../../../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="combat_base.html">1.0 (main branch)</a></li>
<ul>
<li><a href="../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<h1>Source code for evennia.contrib.tutorials.evadventure.combat_base</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd">EvAdventure Base combat utilities.</span>
<span class="sd">This establishes the basic building blocks for combat:</span>
<span class="sd">- `CombatFailure` - exception for combat-specific errors.</span>
<span class="sd">- `CombatAction` (and subclasses) - classes encompassing all the working around an action.</span>
<span class="sd"> They are initialized from &#39;action-dicts` - dictionaries with all the relevant data for the</span>
<span class="sd"> particular invocation</span>
<span class="sd">- `CombatHandler` - base class for running a combat. Exactly how this is used depends on the</span>
<span class="sd"> type of combat intended (twitch- or turn-based) so many details of this will be implemented</span>
<span class="sd"> in child classes.</span>
<span class="sd">----</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">evennia.scripts.scripts</span> <span class="kn">import</span> <span class="n">DefaultScript</span>
<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.utils</span> <span class="kn">import</span> <span class="n">evtable</span>
<span class="kn">from</span> <span class="nn">evennia.utils.create</span> <span class="kn">import</span> <span class="n">create_script</span>
<span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">rules</span>
<div class="viewcode-block" id="CombatFailure"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatFailure">[docs]</a><span class="k">class</span> <span class="nc">CombatFailure</span><span class="p">(</span><span class="ne">RuntimeError</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Some failure during combat actions.</span>
<span class="sd"> &quot;&quot;&quot;</span></div>
<div class="viewcode-block" id="CombatAction"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatAction">[docs]</a><span class="k">class</span> <span class="nc">CombatAction</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Parent class for all actions.</span>
<span class="sd"> This represents the executable code to run to perform an action. It is initialized from an</span>
<span class="sd"> &#39;action-dict&#39;, a set of properties stored in the action queue by each combatant.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<div class="viewcode-block" id="CombatAction.__init__"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatAction.__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">combathandler</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Each key-value pair in the action-dict is stored as a property on this class</span>
<span class="sd"> for later access.</span>
<span class="sd"> Args:</span>
<span class="sd"> combatant (EvAdventureCharacter, EvAdventureNPC): The combatant performing</span>
<span class="sd"> the action.</span>
<span class="sd"> action_dict (dict): A dict containing all properties to initialize on this</span>
<span class="sd"> class. This should not be any keys with `_` prefix, since these are</span>
<span class="sd"> used internally by the class.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combathandler</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="o">=</span> <span class="n">combatant</span>
<span class="c1"># store the action dicts&#39; keys as properties accessible as e.g. action.target etc</span>
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">action_dict</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">key</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;_&quot;</span><span class="p">):</span>
<span class="nb">setattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span></div>
<div class="viewcode-block" id="CombatAction.msg"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatAction.msg">[docs]</a> <span class="k">def</span> <span class="nf">msg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Convenience route to the combathandler msg-sender mechanism.</span>
<span class="sd"> Args:</span>
<span class="sd"> message (str): Message to send; use `$You()` and `$You(other.key)` to refer to</span>
<span class="sd"> the combatant doing the action and other combatants, respectively.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">combatant</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="n">broadcast</span><span class="p">)</span></div>
<div class="viewcode-block" id="CombatAction.can_use"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatAction.can_use">[docs]</a> <span class="k">def</span> <span class="nf">can_use</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Called to determine if the action is usable with the current settings. This does not</span>
<span class="sd"> actually perform the action.</span>
<span class="sd"> Returns:</span>
<span class="sd"> bool: If this action can be used at this time.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="kc">True</span></div>
<div class="viewcode-block" id="CombatAction.execute"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatAction.execute">[docs]</a> <span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Perform the action as the combatant. Should normally make use of the properties</span>
<span class="sd"> stored on the class during initialization.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">pass</span></div>
<div class="viewcode-block" id="CombatAction.post_execute"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatAction.post_execute">[docs]</a> <span class="k">def</span> <span class="nf">post_execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Called after execution.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">pass</span></div></div>
<div class="viewcode-block" id="CombatActionHold"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatActionHold">[docs]</a><span class="k">class</span> <span class="nc">CombatActionHold</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Action that does nothing.</span>
<span class="sd"> ::</span>
<span class="sd"> action_dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;hold&quot;</span>
<span class="sd"> }</span>
<span class="sd"> &quot;&quot;&quot;</span></div>
<div class="viewcode-block" id="CombatActionAttack"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatActionAttack">[docs]</a><span class="k">class</span> <span class="nc">CombatActionAttack</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> A regular attack, using a wielded weapon.</span>
<span class="sd"> ::</span>
<span class="sd"> action-dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;attack&quot;,</span>
<span class="sd"> &quot;target&quot;: Character/Object</span>
<span class="sd"> }</span>
<span class="sd"> &quot;&quot;&quot;</span>
<div class="viewcode-block" id="CombatActionAttack.execute"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatActionAttack.execute">[docs]</a> <span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">attacker</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="n">weapon</span> <span class="o">=</span> <span class="n">attacker</span><span class="o">.</span><span class="n">weapon</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span>
<span class="k">if</span> <span class="n">weapon</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="n">weapon</span><span class="o">.</span><span class="n">use</span><span class="p">(</span>
<span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="n">advantage</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
<span class="p">)</span>
<span class="n">weapon</span><span class="o">.</span><span class="n">at_post_use</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span></div></div>
<div class="viewcode-block" id="CombatActionStunt"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatActionStunt">[docs]</a><span class="k">class</span> <span class="nc">CombatActionStunt</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Perform a stunt the grants a beneficiary (can be self) advantage on their next action against a</span>
<span class="sd"> target. Whenever performing a stunt that would affect another negatively (giving them</span>
<span class="sd"> disadvantage against an ally, or granting an advantage against them, we need to make a check</span>
<span class="sd"> first. We don&#39;t do a check if giving an advantage to an ally or ourselves.</span>
<span class="sd"> ::</span>
<span class="sd"> action_dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;stunt&quot;,</span>
<span class="sd"> &quot;recipient&quot;: Character/NPC,</span>
<span class="sd"> &quot;target&quot;: Character/NPC,</span>
<span class="sd"> &quot;advantage&quot;: bool, # if False, it&#39;s a disadvantage</span>
<span class="sd"> &quot;stunt_type&quot;: Ability, # what ability (like STR, DEX etc) to use to perform this stunt.</span>
<span class="sd"> &quot;defense_type&quot;: Ability, # what ability to use to defend against (negative) effects of</span>
<span class="sd"> this stunt.</span>
<span class="sd"> }</span>
<span class="sd"> &quot;&quot;&quot;</span>
<div class="viewcode-block" id="CombatActionStunt.execute"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatActionStunt.execute">[docs]</a> <span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span>
<span class="n">attacker</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="n">recipient</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">recipient</span> <span class="c1"># the one to receive the effect of the stunt</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span> <span class="c1"># the affected by the stunt (can be the same as recipient/combatant)</span>
<span class="n">txt</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
<span class="k">if</span> <span class="n">recipient</span> <span class="o">==</span> <span class="n">target</span><span class="p">:</span>
<span class="c1"># grant another entity dis/advantage against themselves</span>
<span class="n">defender</span> <span class="o">=</span> <span class="n">recipient</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># recipient not same as target; who will defend depends on disadvantage or advantage</span>
<span class="c1"># to give.</span>
<span class="n">defender</span> <span class="o">=</span> <span class="n">target</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">advantage</span> <span class="k">else</span> <span class="n">recipient</span>
<span class="c1"># trying to give advantage to recipient against target. Target defends against caller</span>
<span class="n">is_success</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">txt</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">dice</span><span class="o">.</span><span class="n">opposed_saving_throw</span><span class="p">(</span>
<span class="n">attacker</span><span class="p">,</span>
<span class="n">defender</span><span class="p">,</span>
<span class="n">attack_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">stunt_type</span><span class="p">,</span>
<span class="n">defense_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">defense_type</span><span class="p">,</span>
<span class="n">advantage</span><span class="o">=</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">defender</span><span class="p">),</span>
<span class="n">disadvantage</span><span class="o">=</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_disadvantage</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">defender</span><span class="p">),</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;$You() $conj(attempt) stunt on $You(</span><span class="si">{</span><span class="n">defender</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">). </span><span class="si">{</span><span class="n">txt</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="c1"># deal with results</span>
<span class="k">if</span> <span class="n">is_success</span><span class="p">:</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">advantage</span><span class="p">:</span>
<span class="n">combathandler</span><span class="o">.</span><span class="n">give_advantage</span><span class="p">(</span><span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">combathandler</span><span class="o">.</span><span class="n">give_disadvantage</span><span class="p">(</span><span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
<span class="k">if</span> <span class="n">recipient</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(gain) </span><span class="si">{</span><span class="s1">&#39;advantage&#39;</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="bp">self</span><span class="o">.</span><span class="n">advantage</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="s1">&#39;disadvantage&#39;</span><span class="si">}</span><span class="s2"> &quot;</span>
<span class="sa">f</span><span class="s2">&quot;against $You(</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">)!&quot;</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(cause) $You(</span><span class="si">{</span><span class="n">recipient</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) &quot;</span>
<span class="sa">f</span><span class="s2">&quot;to gain </span><span class="si">{</span><span class="s1">&#39;advantage&#39;</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="bp">self</span><span class="o">.</span><span class="n">advantage</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="s1">&#39;disadvantage&#39;</span><span class="si">}</span><span class="s2"> &quot;</span>
<span class="sa">f</span><span class="s2">&quot;against $You(</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">)!&quot;</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;$You(</span><span class="si">{</span><span class="n">defender</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) $conj(resist)! $You() $conj(fail) the stunt.&quot;</span><span class="p">)</span></div></div>
<div class="viewcode-block" id="CombatActionUseItem"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatActionUseItem">[docs]</a><span class="k">class</span> <span class="nc">CombatActionUseItem</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Use an item in combat. This is meant for one-off or limited-use items (so things like</span>
<span class="sd"> scrolls and potions, not swords and shields). If this is some sort of weapon or spell rune,</span>
<span class="sd"> we refer to the item to determine what to use for attack/defense rolls.</span>
<span class="sd"> ::</span>
<span class="sd"> action_dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;use&quot;,</span>
<span class="sd"> &quot;item&quot;: Object</span>
<span class="sd"> &quot;target&quot;: Character/NPC/Object/None</span>
<span class="sd"> }</span>
<span class="sd"> &quot;&quot;&quot;</span>
<div class="viewcode-block" id="CombatActionUseItem.execute"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatActionUseItem.execute">[docs]</a> <span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">item</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">item</span>
<span class="n">user</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</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="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="n">item</span><span class="o">.</span><span class="n">use</span><span class="p">(</span>
<span class="n">user</span><span class="p">,</span>
<span class="n">target</span><span class="p">,</span>
<span class="n">advantage</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">),</span>
<span class="n">disadvantage</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_disadvantage</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">),</span>
<span class="p">)</span>
<span class="n">item</span><span class="o">.</span><span class="n">at_post_use</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span></div></div>
<div class="viewcode-block" id="CombatActionWield"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatActionWield">[docs]</a><span class="k">class</span> <span class="nc">CombatActionWield</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Wield a new weapon (or spell) from your inventory. This will swap out the one you are currently</span>
<span class="sd"> wielding, if any.</span>
<span class="sd"> ::</span>
<span class="sd"> action_dict = {</span>
<span class="sd"> &quot;key&quot;: &quot;wield&quot;,</span>
<span class="sd"> &quot;item&quot;: Object</span>
<span class="sd"> }</span>
<span class="sd"> &quot;&quot;&quot;</span>
<div class="viewcode-block" id="CombatActionWield.execute"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.CombatActionWield.execute">[docs]</a> <span class="k">def</span> <span class="nf">execute</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">combatant</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">item</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;$You() $conj(wield) $You(</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">item</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">).&quot;</span><span class="p">)</span></div></div>
<span class="c1"># main combathandler</span>
<div class="viewcode-block" id="EvAdventureCombatBaseHandler"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler">[docs]</a><span class="k">class</span> <span class="nc">EvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">DefaultScript</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> This script is created when a combat starts. It &#39;ticks&#39; the combat and tracks</span>
<span class="sd"> all sides of it.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># available actions in combat</span>
<span class="n">action_classes</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;hold&quot;</span><span class="p">:</span> <span class="n">CombatActionHold</span><span class="p">,</span>
<span class="s2">&quot;attack&quot;</span><span class="p">:</span> <span class="n">CombatActionAttack</span><span class="p">,</span>
<span class="s2">&quot;stunt&quot;</span><span class="p">:</span> <span class="n">CombatActionStunt</span><span class="p">,</span>
<span class="s2">&quot;use&quot;</span><span class="p">:</span> <span class="n">CombatActionUseItem</span><span class="p">,</span>
<span class="s2">&quot;wield&quot;</span><span class="p">:</span> <span class="n">CombatActionWield</span><span class="p">,</span>
<span class="p">}</span>
<span class="c1"># fallback action if not selecting anything</span>
<span class="n">fallback_action_dict</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">({</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</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="EvAdventureCombatBaseHandler.get_or_create_combathandler"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler.get_or_create_combathandler">[docs]</a> <span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">get_or_create_combathandler</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Get or create a combathandler on `obj`.</span>
<span class="sd"> Args:</span>
<span class="sd"> obj (any): The Typeclassed entity to store the CombatHandler Script on. This could be</span>
<span class="sd"> a location (for turn-based combat) or a Character (for twitch-based combat).</span>
<span class="sd"> Keyword Args:</span>
<span class="sd"> combathandler_key (str): They key name for the script. Will be &#39;combathandler&#39; by</span>
<span class="sd"> default.</span>
<span class="sd"> **kwargs: Arguments to the Script, if it is created.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">obj</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">CombatFailure</span><span class="p">(</span><span class="s2">&quot;Cannot start combat without a place to do it!&quot;</span><span class="p">)</span>
<span class="n">combathandler_key</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">,</span> <span class="s2">&quot;combathandler&quot;</span><span class="p">)</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combathandler</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">combathandler</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">combathandler</span><span class="o">.</span><span class="n">id</span><span class="p">:</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">scripts</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">combathandler_key</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">combathandler</span><span class="p">:</span>
<span class="c1"># have to create from scratch</span>
<span class="n">persistent</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">&quot;persistent&quot;</span><span class="p">,</span> <span class="kc">True</span><span class="p">)</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="n">create_script</span><span class="p">(</span>
<span class="bp">cls</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="n">combathandler_key</span><span class="p">,</span>
<span class="n">obj</span><span class="o">=</span><span class="n">obj</span><span class="p">,</span>
<span class="n">persistent</span><span class="o">=</span><span class="n">persistent</span><span class="p">,</span>
<span class="n">autostart</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="o">**</span><span class="n">kwargs</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">obj</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combathandler</span>
<span class="k">return</span> <span class="n">combathandler</span></div>
<div class="viewcode-block" id="EvAdventureCombatBaseHandler.msg"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler.msg">[docs]</a> <span class="k">def</span> <span class="nf">msg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">combatant</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Central place for sending messages to combatants. This allows</span>
<span class="sd"> for adding any combat-specific text-decoration in one place.</span>
<span class="sd"> Args:</span>
<span class="sd"> message (str): The message to send.</span>
<span class="sd"> combatant (Object): The &#39;You&#39; in the message, if any.</span>
<span class="sd"> broadcast (bool): If `False`, `combatant` must be included and</span>
<span class="sd"> will be the only one to see the message. If `True`, send to</span>
<span class="sd"> everyone in the location.</span>
<span class="sd"> location (Object, optional): If given, use this as the location to</span>
<span class="sd"> send broadcast messages to. If not, use `self.obj` as that</span>
<span class="sd"> location.</span>
<span class="sd"> Notes:</span>
<span class="sd"> If `combatant` is given, use `$You/you()` markup to create</span>
<span class="sd"> a message that looks different depending on who sees it. Use</span>
<span class="sd"> `$You(combatant_key)` to refer to other combatants.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">location</span><span class="p">:</span>
<span class="n">location</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span>
<span class="n">location_objs</span> <span class="o">=</span> <span class="n">location</span><span class="o">.</span><span class="n">contents</span>
<span class="n">exclude</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">broadcast</span> <span class="ow">and</span> <span class="n">combatant</span><span class="p">:</span>
<span class="n">exclude</span> <span class="o">=</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="n">location_objs</span> <span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">combatant</span><span class="p">]</span>
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="n">message</span><span class="p">,</span>
<span class="n">exclude</span><span class="o">=</span><span class="n">exclude</span><span class="p">,</span>
<span class="n">from_obj</span><span class="o">=</span><span class="n">combatant</span><span class="p">,</span>
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">locobj</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">locobj</span> <span class="k">for</span> <span class="n">locobj</span> <span class="ow">in</span> <span class="n">location_objs</span><span class="p">},</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureCombatBaseHandler.get_combat_summary"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler.get_combat_summary">[docs]</a> <span class="k">def</span> <span class="nf">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Get a &#39;battle report&#39; - an overview of the current state of combat from the perspective</span>
<span class="sd"> of one of the sides.</span>
<span class="sd"> Args:</span>
<span class="sd"> combatant (EvAdventureCharacter, EvAdventureNPC): The combatant to get.</span>
<span class="sd"> Returns:</span>
<span class="sd"> EvTable: A table representing the current state of combat.</span>
<span class="sd"> Example:</span>
<span class="sd"> ::</span>
<span class="sd"> Goblin shaman (Perfect)</span>
<span class="sd"> Gregor (Hurt) Goblin brawler(Hurt)</span>
<span class="sd"> Bob (Perfect) vs Goblin grunt 1 (Hurt)</span>
<span class="sd"> Goblin grunt 2 (Perfect)</span>
<span class="sd"> Goblin grunt 3 (Wounded)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">allies</span><span class="p">,</span> <span class="n">enemies</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_sides</span><span class="p">(</span><span class="n">combatant</span><span class="p">)</span>
<span class="c1"># we must include outselves at the top of the list (we are not returned from get_sides)</span>
<span class="n">allies</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">combatant</span><span class="p">)</span>
<span class="n">nallies</span><span class="p">,</span> <span class="n">nenemies</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">allies</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">enemies</span><span class="p">)</span>
<span class="c1"># prepare colors and hurt-levels</span>
<span class="n">allies</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">ally</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">ally</span><span class="o">.</span><span class="n">hurt_level</span><span class="si">}</span><span class="s2">)&quot;</span> <span class="k">for</span> <span class="n">ally</span> <span class="ow">in</span> <span class="n">allies</span><span class="p">]</span>
<span class="n">enemies</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">enemy</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">enemy</span><span class="o">.</span><span class="n">hurt_level</span><span class="si">}</span><span class="s2">)&quot;</span> <span class="k">for</span> <span class="n">enemy</span> <span class="ow">in</span> <span class="n">enemies</span><span class="p">]</span>
<span class="c1"># the center column with the &#39;vs&#39;</span>
<span class="n">vs_column</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;&quot;</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="n">nallies</span><span class="p">,</span> <span class="n">nenemies</span><span class="p">))]</span>
<span class="n">vs_column</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">vs_column</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;|wvs|n&quot;</span>
<span class="c1"># the two allies / enemies columns should be centered vertically</span>
<span class="n">diff</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="n">nallies</span> <span class="o">-</span> <span class="n">nenemies</span><span class="p">)</span>
<span class="n">top_empty</span> <span class="o">=</span> <span class="n">diff</span> <span class="o">//</span> <span class="mi">2</span>
<span class="n">bot_empty</span> <span class="o">=</span> <span class="n">diff</span> <span class="o">-</span> <span class="n">top_empty</span>
<span class="n">topfill</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;&quot;</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">top_empty</span><span class="p">)]</span>
<span class="n">botfill</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;&quot;</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">bot_empty</span><span class="p">)]</span>
<span class="k">if</span> <span class="n">nallies</span> <span class="o">&gt;=</span> <span class="n">nenemies</span><span class="p">:</span>
<span class="n">enemies</span> <span class="o">=</span> <span class="n">topfill</span> <span class="o">+</span> <span class="n">enemies</span> <span class="o">+</span> <span class="n">botfill</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">allies</span> <span class="o">=</span> <span class="n">topfill</span> <span class="o">+</span> <span class="n">allies</span> <span class="o">+</span> <span class="n">botfill</span>
<span class="c1"># make a table with three columns</span>
<span class="k">return</span> <span class="n">evtable</span><span class="o">.</span><span class="n">EvTable</span><span class="p">(</span>
<span class="n">table</span><span class="o">=</span><span class="p">[</span>
<span class="n">evtable</span><span class="o">.</span><span class="n">EvColumn</span><span class="p">(</span><span class="o">*</span><span class="n">allies</span><span class="p">,</span> <span class="n">align</span><span class="o">=</span><span class="s2">&quot;l&quot;</span><span class="p">),</span>
<span class="n">evtable</span><span class="o">.</span><span class="n">EvColumn</span><span class="p">(</span><span class="o">*</span><span class="n">vs_column</span><span class="p">,</span> <span class="n">align</span><span class="o">=</span><span class="s2">&quot;c&quot;</span><span class="p">),</span>
<span class="n">evtable</span><span class="o">.</span><span class="n">EvColumn</span><span class="p">(</span><span class="o">*</span><span class="n">enemies</span><span class="p">,</span> <span class="n">align</span><span class="o">=</span><span class="s2">&quot;r&quot;</span><span class="p">),</span>
<span class="p">],</span>
<span class="n">border</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">maxwidth</span><span class="o">=</span><span class="mi">78</span><span class="p">,</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureCombatBaseHandler.get_sides"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler.get_sides">[docs]</a> <span class="k">def</span> <span class="nf">get_sides</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Get a listing of the two &#39;sides&#39; of this combat, from the perspective of the provided</span>
<span class="sd"> combatant. The sides don&#39;t need to be balanced.</span>
<span class="sd"> Args:</span>
<span class="sd"> combatant (Character or NPC): The one whose sides are to determined.</span>
<span class="sd"> Returns:</span>
<span class="sd"> tuple: A tuple of lists `(allies, enemies)`, from the perspective of `combatant`.</span>
<span class="sd"> Note:</span>
<span class="sd"> The sides are found by checking PCs vs NPCs. PCs can normally not attack other PCs, so</span>
<span class="sd"> are naturally allies. If the current room has the `allow_pvp` Attribute set, then _all_</span>
<span class="sd"> other combatants (PCs and NPCs alike) are considered valid enemies (one could expand</span>
<span class="sd"> this with group mechanics).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span></div>
<div class="viewcode-block" id="EvAdventureCombatBaseHandler.give_advantage"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler.give_advantage">[docs]</a> <span class="k">def</span> <span class="nf">give_advantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Let a benefiter gain advantage against the target.</span>
<span class="sd"> Args:</span>
<span class="sd"> recipient (Character or NPC): The one to gain the advantage. This may or may not</span>
<span class="sd"> be the same entity that creates the advantage in the first place.</span>
<span class="sd"> target (Character or NPC): The one against which the target gains advantage. This</span>
<span class="sd"> could (in principle) be the same as the benefiter (e.g. gaining advantage on</span>
<span class="sd"> some future boost)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span></div>
<div class="viewcode-block" id="EvAdventureCombatBaseHandler.give_disadvantage"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler.give_disadvantage">[docs]</a> <span class="k">def</span> <span class="nf">give_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Let an affected party gain disadvantage against a target.</span>
<span class="sd"> Args:</span>
<span class="sd"> recipient (Character or NPC): The one to get the disadvantage.</span>
<span class="sd"> target (Character or NPC): The one against which the target gains disadvantage, usually</span>
<span class="sd"> an enemy.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span></div>
<div class="viewcode-block" id="EvAdventureCombatBaseHandler.has_advantage"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler.has_advantage">[docs]</a> <span class="k">def</span> <span class="nf">has_advantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Check if a given combatant has advantage against a target.</span>
<span class="sd"> Args:</span>
<span class="sd"> combatant (Character or NPC): The one to check if they have advantage</span>
<span class="sd"> target (Character or NPC): The target to check advantage against.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span></div>
<div class="viewcode-block" id="EvAdventureCombatBaseHandler.has_disadvantage"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler.has_disadvantage">[docs]</a> <span class="k">def</span> <span class="nf">has_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Check if a given combatant has disadvantage against a target.</span>
<span class="sd"> Args:</span>
<span class="sd"> combatant (Character or NPC): The one to check if they have disadvantage</span>
<span class="sd"> target (Character or NPC): The target to check disadvantage against.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span></div>
<div class="viewcode-block" id="EvAdventureCombatBaseHandler.queue_action"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler.queue_action">[docs]</a> <span class="k">def</span> <span class="nf">queue_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">,</span> <span class="n">combatant</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Queue an action by adding the new actiondict.</span>
<span class="sd"> Args:</span>
<span class="sd"> action_dict (dict): A dict describing the action class by name along with properties.</span>
<span class="sd"> combatant (EvAdventureCharacter, EvAdventureNPC, optional): A combatant queueing the</span>
<span class="sd"> action.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span></div>
<div class="viewcode-block" id="EvAdventureCombatBaseHandler.execute_next_action"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler.execute_next_action">[docs]</a> <span class="k">def</span> <span class="nf">execute_next_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Perform a combatant&#39;s next action.</span>
<span class="sd"> Args:</span>
<span class="sd"> combatant (EvAdventureCharacter, EvAdventureNPC): The combatant performing and action.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span></div>
<div class="viewcode-block" id="EvAdventureCombatBaseHandler.start_combat"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler.start_combat">[docs]</a> <span class="k">def</span> <span class="nf">start_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Start combat.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span></div>
<div class="viewcode-block" id="EvAdventureCombatBaseHandler.check_stop_combat"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler.check_stop_combat">[docs]</a> <span class="k">def</span> <span class="nf">check_stop_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Check if this combat should be aborted, whatever this means for the particular</span>
<span class="sd"> the particular combat type.</span>
<span class="sd"> Keyword Args:</span>
<span class="sd"> kwargs: Any extra keyword args used.</span>
<span class="sd"> Returns:</span>
<span class="sd"> bool: If `True`, the `stop_combat` method should be called.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span></div>
<div class="viewcode-block" id="EvAdventureCombatBaseHandler.stop_combat"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia.contrib.tutorials.evadventure.combat_base.EvAdventureCombatBaseHandler.stop_combat">[docs]</a> <span class="k">def</span> <span class="nf">stop_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Stop combat. This should also do all cleanup.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span></div></div>
</pre></div>
</div>
</div>
</div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../../../genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="../../../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../../../evennia.html" >evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.contrib.tutorials.evadventure.combat_base</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,677 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>evennia.contrib.tutorials.evadventure.combat_twitch &#8212; Evennia 1.0 documentation</title>
<link rel="stylesheet" href="../../../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../../../" src="../../../../../_static/documentation_options.js"></script>
<script src="../../../../../_static/jquery.js"></script>
<script src="../../../../../_static/underscore.js"></script>
<script src="../../../../../_static/doctools.js"></script>
<script src="../../../../../_static/language_data.js"></script>
<link rel="shortcut icon" href="../../../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../../../genindex.html" />
<link rel="search" title="Search" href="../../../../../search.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../../../evennia.html" accesskey="U">evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.contrib.tutorials.evadventure.combat_twitch</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="../../../../../index.html">
<img class="logo" src="../../../../../_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../../../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="combat_twitch.html">1.0 (main branch)</a></li>
<ul>
<li><a href="../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<h1>Source code for evennia.contrib.tutorials.evadventure.combat_twitch</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd">EvAdventure Twitch-based combat</span>
<span class="sd">This implements a &#39;twitch&#39; (aka DIKU or other traditional muds) style of MUD combat.</span>
<span class="sd">----</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">AttributeProperty</span><span class="p">,</span> <span class="n">CmdSet</span><span class="p">,</span> <span class="n">default_cmds</span>
<span class="kn">from</span> <span class="nn">evennia.commands.command</span> <span class="kn">import</span> <span class="n">Command</span><span class="p">,</span> <span class="n">InterruptCommand</span>
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">display_len</span><span class="p">,</span> <span class="n">inherits_from</span><span class="p">,</span> <span class="n">list_to_string</span><span class="p">,</span> <span class="n">pad</span><span class="p">,</span> <span class="n">repeat</span><span class="p">,</span> <span class="n">unrepeat</span>
<span class="kn">from</span> <span class="nn">.characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
<span class="kn">from</span> <span class="nn">.combat_base</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">CombatActionAttack</span><span class="p">,</span>
<span class="n">CombatActionHold</span><span class="p">,</span>
<span class="n">CombatActionStunt</span><span class="p">,</span>
<span class="n">CombatActionUseItem</span><span class="p">,</span>
<span class="n">CombatActionWield</span><span class="p">,</span>
<span class="n">EvAdventureCombatBaseHandler</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">ABILITY_REVERSE_MAP</span>
<div class="viewcode-block" id="EvAdventureCombatTwitchHandler"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler">[docs]</a><span class="k">class</span> <span class="nc">EvAdventureCombatTwitchHandler</span><span class="p">(</span><span class="n">EvAdventureCombatBaseHandler</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> This is created on the combatant when combat starts. It tracks only the combatants</span>
<span class="sd"> side of the combat and handles when the next action will happen.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># fixed properties</span>
<span class="n">action_classes</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;hold&quot;</span><span class="p">:</span> <span class="n">CombatActionHold</span><span class="p">,</span>
<span class="s2">&quot;attack&quot;</span><span class="p">:</span> <span class="n">CombatActionAttack</span><span class="p">,</span>
<span class="s2">&quot;stunt&quot;</span><span class="p">:</span> <span class="n">CombatActionStunt</span><span class="p">,</span>
<span class="s2">&quot;use&quot;</span><span class="p">:</span> <span class="n">CombatActionUseItem</span><span class="p">,</span>
<span class="s2">&quot;wield&quot;</span><span class="p">:</span> <span class="n">CombatActionWield</span><span class="p">,</span>
<span class="p">}</span>
<span class="c1"># dynamic properties</span>
<span class="n">advantage_against</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="nb">dict</span><span class="p">)</span>
<span class="n">disadvantage_against</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="nb">dict</span><span class="p">)</span>
<span class="n">action_dict</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="nb">dict</span><span class="p">)</span>
<span class="n">fallback_action_dict</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">({</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">,</span> <span class="s2">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">})</span>
<span class="c1"># stores the current ticker reference, so we can manipulate it later</span>
<span class="n">current_ticker_ref</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
<div class="viewcode-block" id="EvAdventureCombatTwitchHandler.msg"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler.msg">[docs]</a> <span class="k">def</span> <span class="nf">msg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Central place for sending messages to combatants. This allows</span>
<span class="sd"> for adding any combat-specific text-decoration in one place.</span>
<span class="sd"> Args:</span>
<span class="sd"> message (str): The message to send.</span>
<span class="sd"> combatant (Object): The &#39;You&#39; in the message, if any.</span>
<span class="sd"> broadcast (bool): If `False`, `combatant` must be included and</span>
<span class="sd"> will be the only one to see the message. If `True`, send to</span>
<span class="sd"> everyone in the location.</span>
<span class="sd"> location (Object, optional): If given, use this as the location to</span>
<span class="sd"> send broadcast messages to. If not, use `self.obj` as that</span>
<span class="sd"> location.</span>
<span class="sd"> Notes:</span>
<span class="sd"> If `combatant` is given, use `$You/you()` markup to create</span>
<span class="sd"> a message that looks different depending on who sees it. Use</span>
<span class="sd"> `$You(combatant_key)` to refer to other combatants.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">combatant</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="n">broadcast</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">location</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureCombatTwitchHandler.at_init"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler.at_init">[docs]</a> <span class="k">def</span> <span class="nf">at_init</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">cmdset</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">TwitchLookCmdSet</span><span class="p">,</span> <span class="n">persistent</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureCombatTwitchHandler.get_sides"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler.get_sides">[docs]</a> <span class="k">def</span> <span class="nf">get_sides</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Get a listing of the two &#39;sides&#39; of this combat, from the perspective of the provided</span>
<span class="sd"> combatant. The sides don&#39;t need to be balanced.</span>
<span class="sd"> Args:</span>
<span class="sd"> combatant (Character or NPC): The one whose sides are to determined.</span>
<span class="sd"> Returns:</span>
<span class="sd"> tuple: A tuple of lists `(allies, enemies)`, from the perspective of `combatant`.</span>
<span class="sd"> Note that combatant itself is not included in either of these.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># get all entities involved in combat by looking up their combathandlers</span>
<span class="n">combatants</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">comb</span>
<span class="k">for</span> <span class="n">comb</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">comb</span><span class="p">,</span> <span class="s2">&quot;scripts&quot;</span><span class="p">)</span> <span class="ow">and</span> <span class="n">comb</span><span class="o">.</span><span class="n">scripts</span><span class="o">.</span><span class="n">has</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="p">)</span>
<span class="p">]</span>
<span class="n">location</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">location</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">location</span><span class="p">,</span> <span class="s2">&quot;allow_pvp&quot;</span><span class="p">)</span> <span class="ow">and</span> <span class="n">location</span><span class="o">.</span><span class="n">allow_pvp</span><span class="p">:</span>
<span class="c1"># in pvp, everyone else is an enemy</span>
<span class="n">allies</span> <span class="o">=</span> <span class="p">[</span><span class="n">combatant</span><span class="p">]</span>
<span class="n">enemies</span> <span class="o">=</span> <span class="p">[</span><span class="n">comb</span> <span class="k">for</span> <span class="n">comb</span> <span class="ow">in</span> <span class="n">combatants</span> <span class="k">if</span> <span class="n">comb</span> <span class="o">!=</span> <span class="n">combatant</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># otherwise, enemies/allies depend on who combatant is</span>
<span class="n">pcs</span> <span class="o">=</span> <span class="p">[</span><span class="n">comb</span> <span class="k">for</span> <span class="n">comb</span> <span class="ow">in</span> <span class="n">combatants</span> <span class="k">if</span> <span class="n">inherits_from</span><span class="p">(</span><span class="n">comb</span><span class="p">,</span> <span class="n">EvAdventureCharacter</span><span class="p">)]</span>
<span class="n">npcs</span> <span class="o">=</span> <span class="p">[</span><span class="n">comb</span> <span class="k">for</span> <span class="n">comb</span> <span class="ow">in</span> <span class="n">combatants</span> <span class="k">if</span> <span class="n">comb</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">pcs</span><span class="p">]</span>
<span class="k">if</span> <span class="n">combatant</span> <span class="ow">in</span> <span class="n">pcs</span><span class="p">:</span>
<span class="c1"># combatant is a PC, so NPCs are all enemies</span>
<span class="n">allies</span> <span class="o">=</span> <span class="p">[</span><span class="n">comb</span> <span class="k">for</span> <span class="n">comb</span> <span class="ow">in</span> <span class="n">pcs</span> <span class="k">if</span> <span class="n">comb</span> <span class="o">!=</span> <span class="n">combatant</span><span class="p">]</span>
<span class="n">enemies</span> <span class="o">=</span> <span class="n">npcs</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># combatant is an NPC, so PCs are all enemies</span>
<span class="n">allies</span> <span class="o">=</span> <span class="p">[</span><span class="n">comb</span> <span class="k">for</span> <span class="n">comb</span> <span class="ow">in</span> <span class="n">npcs</span> <span class="k">if</span> <span class="n">comb</span> <span class="o">!=</span> <span class="n">combatant</span><span class="p">]</span>
<span class="n">enemies</span> <span class="o">=</span> <span class="n">pcs</span>
<span class="k">return</span> <span class="n">allies</span><span class="p">,</span> <span class="n">enemies</span></div>
<div class="viewcode-block" id="EvAdventureCombatTwitchHandler.give_advantage"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler.give_advantage">[docs]</a> <span class="k">def</span> <span class="nf">give_advantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Let a benefiter gain advantage against the target.</span>
<span class="sd"> Args:</span>
<span class="sd"> recipient (Character or NPC): The one to gain the advantage. This may or may not</span>
<span class="sd"> be the same entity that creates the advantage in the first place.</span>
<span class="sd"> target (Character or NPC): The one against which the target gains advantage. This</span>
<span class="sd"> could (in principle) be the same as the benefiter (e.g. gaining advantage on</span>
<span class="sd"> some future boost)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">advantage_against</span><span class="p">[</span><span class="n">target</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span></div>
<div class="viewcode-block" id="EvAdventureCombatTwitchHandler.give_disadvantage"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler.give_disadvantage">[docs]</a> <span class="k">def</span> <span class="nf">give_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Let an affected party gain disadvantage against a target.</span>
<span class="sd"> Args:</span>
<span class="sd"> recipient (Character or NPC): The one to get the disadvantage.</span>
<span class="sd"> target (Character or NPC): The one against which the target gains disadvantage, usually</span>
<span class="sd"> an enemy.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">disadvantage_against</span><span class="p">[</span><span class="n">target</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span></div>
<div class="viewcode-block" id="EvAdventureCombatTwitchHandler.has_advantage"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler.has_advantage">[docs]</a> <span class="k">def</span> <span class="nf">has_advantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Check if a given combatant has advantage against a target.</span>
<span class="sd"> Args:</span>
<span class="sd"> combatant (Character or NPC): The one to check if they have advantage</span>
<span class="sd"> target (Character or NPC): The target to check advantage against.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">advantage_against</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="kc">False</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureCombatTwitchHandler.has_disadvantage"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler.has_disadvantage">[docs]</a> <span class="k">def</span> <span class="nf">has_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Check if a given combatant has disadvantage against a target.</span>
<span class="sd"> Args:</span>
<span class="sd"> combatant (Character or NPC): The one to check if they have disadvantage</span>
<span class="sd"> target (Character or NPC): The target to check disadvantage against.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">disadvantage_against</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="kc">False</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureCombatTwitchHandler.queue_action"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler.queue_action">[docs]</a> <span class="k">def</span> <span class="nf">queue_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">,</span> <span class="n">combatant</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Schedule the next action to fire.</span>
<span class="sd"> Args:</span>
<span class="sd"> action_dict (dict): The new action-dict to initialize.</span>
<span class="sd"> combatant: Unused.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="n">action_dict</span><span class="p">[</span><span class="s2">&quot;key&quot;</span><span class="p">]</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">action_classes</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">msg</span><span class="p">(</span><span class="s2">&quot;This is an unkown action!&quot;</span><span class="p">)</span>
<span class="k">return</span>
<span class="c1"># store action dict and schedule it to run in dt time</span>
<span class="bp">self</span><span class="o">.</span><span class="n">action_dict</span> <span class="o">=</span> <span class="n">action_dict</span>
<span class="n">dt</span> <span class="o">=</span> <span class="n">action_dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;dt&quot;</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_ticker_ref</span><span class="p">:</span>
<span class="c1"># we already have a current ticker going - abort it</span>
<span class="n">unrepeat</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">current_ticker_ref</span><span class="p">)</span>
<span class="k">if</span> <span class="n">dt</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="c1"># no repeat</span>
<span class="bp">self</span><span class="o">.</span><span class="n">current_ticker_ref</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># always schedule the task to be repeating, cancel later otherwise. We store</span>
<span class="c1"># the tickerhandler&#39;s ref to make sure we can remove it later</span>
<span class="bp">self</span><span class="o">.</span><span class="n">current_ticker_ref</span> <span class="o">=</span> <span class="n">repeat</span><span class="p">(</span><span class="n">dt</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">execute_next_action</span><span class="p">,</span> <span class="n">id_string</span><span class="o">=</span><span class="s2">&quot;combat&quot;</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureCombatTwitchHandler.execute_next_action"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler.execute_next_action">[docs]</a> <span class="k">def</span> <span class="nf">execute_next_action</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Triggered after a delay by the command</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">combatant</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span>
<span class="n">action_dict</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">action_dict</span>
<span class="n">action_class</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">action_classes</span><span class="p">[</span><span class="n">action_dict</span><span class="p">[</span><span class="s2">&quot;key&quot;</span><span class="p">]]</span>
<span class="n">action</span> <span class="o">=</span> <span class="n">action_class</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">)</span>
<span class="k">if</span> <span class="n">action</span><span class="o">.</span><span class="n">can_use</span><span class="p">():</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
<span class="n">action</span><span class="o">.</span><span class="n">post_execute</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">action_dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;repeat&quot;</span><span class="p">,</span> <span class="kc">True</span><span class="p">):</span>
<span class="c1"># not a repeating action, use the fallback (normally the original attack)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">action_dict</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">fallback_action_dict</span>
<span class="bp">self</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">fallback_action_dict</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">check_stop_combat</span><span class="p">()</span></div>
<div class="viewcode-block" id="EvAdventureCombatTwitchHandler.check_stop_combat"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler.check_stop_combat">[docs]</a> <span class="k">def</span> <span class="nf">check_stop_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Check if the combat is over.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">allies</span><span class="p">,</span> <span class="n">enemies</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_sides</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">)</span>
<span class="n">allies</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">)</span>
<span class="n">location</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">location</span>
<span class="c1"># only keep combatants that are alive and still in the same room</span>
<span class="n">allies</span> <span class="o">=</span> <span class="p">[</span><span class="n">comb</span> <span class="k">for</span> <span class="n">comb</span> <span class="ow">in</span> <span class="n">allies</span> <span class="k">if</span> <span class="n">comb</span><span class="o">.</span><span class="n">hp</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">comb</span><span class="o">.</span><span class="n">location</span> <span class="o">==</span> <span class="n">location</span><span class="p">]</span>
<span class="n">enemies</span> <span class="o">=</span> <span class="p">[</span><span class="n">comb</span> <span class="k">for</span> <span class="n">comb</span> <span class="ow">in</span> <span class="n">enemies</span> <span class="k">if</span> <span class="n">comb</span><span class="o">.</span><span class="n">hp</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">comb</span><span class="o">.</span><span class="n">location</span> <span class="o">==</span> <span class="n">location</span><span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">allies</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">enemies</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;Noone stands after the dust settles.&quot;</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">stop_combat</span><span class="p">()</span>
<span class="k">return</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">allies</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">enemies</span><span class="p">:</span>
<span class="k">if</span> <span class="n">allies</span> <span class="o">+</span> <span class="n">enemies</span> <span class="o">==</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="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;The combat is over.&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">still_standing</span> <span class="o">=</span> <span class="n">list_to_string</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;$You(</span><span class="si">{</span><span class="n">comb</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">)&quot;</span> <span class="k">for</span> <span class="n">comb</span> <span class="ow">in</span> <span class="n">allies</span> <span class="o">+</span> <span class="n">enemies</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;The combat is over. Still standing: </span><span class="si">{</span><span class="n">still_standing</span><span class="si">}</span><span class="s2">.&quot;</span><span class="p">,</span>
<span class="n">broadcast</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">stop_combat</span><span class="p">()</span></div>
<div class="viewcode-block" id="EvAdventureCombatTwitchHandler.stop_combat"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.EvAdventureCombatTwitchHandler.stop_combat">[docs]</a> <span class="k">def</span> <span class="nf">stop_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Stop combat immediately.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">queue_action</span><span class="p">({</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">,</span> <span class="s2">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">})</span> <span class="c1"># make sure ticker is killed</span>
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combathandler</span>
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">cmdset</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">TwitchLookCmdSet</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span></div></div>
<span class="k">class</span> <span class="nc">_BaseTwitchCombatCommand</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Parent class for all twitch-combat commnads.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">at_pre_command</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Called before parsing.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">location</span> <span class="ow">or</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">allow_combat</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;Can&#39;t fight here!&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">InterruptCommand</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Handle parsing of most supported combat syntaxes (except stunts).</span>
<span class="sd"> &lt;action&gt; [&lt;target&gt;|&lt;item&gt;]</span>
<span class="sd"> or</span>
<span class="sd"> &lt;action&gt; &lt;item&gt; [on] &lt;target&gt;</span>
<span class="sd"> Use &#39;on&#39; to differentiate if names/items have spaces in the name.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">args</span> <span class="o">=</span> <span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">lhs</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">rhs</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">args</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">if</span> <span class="s2">&quot; on &quot;</span> <span class="ow">in</span> <span class="n">args</span><span class="p">:</span>
<span class="n">lhs</span><span class="p">,</span> <span class="n">rhs</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot; on &quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">lhs</span><span class="p">,</span> <span class="o">*</span><span class="n">rhs</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">rhs</span> <span class="o">=</span> <span class="s2">&quot; &quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">rhs</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">lhs</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">rhs</span> <span class="o">=</span> <span class="n">lhs</span><span class="o">.</span><span class="n">strip</span><span class="p">(),</span> <span class="n">rhs</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">get_or_create_combathandler</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">combathandler_key</span><span class="o">=</span><span class="s2">&quot;combathandler&quot;</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Get or create the combathandler assigned to this combatant.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="n">target</span><span class="p">:</span>
<span class="c1"># add/check combathandler to the target</span>
<span class="k">if</span> <span class="n">target</span><span class="o">.</span><span class="n">hp_max</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You can&#39;t attack that!&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">InterruptCommand</span><span class="p">()</span>
<span class="n">EvAdventureCombatTwitchHandler</span><span class="o">.</span><span class="n">get_or_create_combathandler</span><span class="p">(</span>
<span class="n">target</span><span class="p">,</span> <span class="n">combathandler_key</span><span class="o">=</span><span class="n">combathandler_key</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">EvAdventureCombatTwitchHandler</span><span class="o">.</span><span class="n">get_or_create_combathandler</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span>
<div class="viewcode-block" id="CmdAttack"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdAttack">[docs]</a><span class="k">class</span> <span class="nc">CmdAttack</span><span class="p">(</span><span class="n">_BaseTwitchCombatCommand</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Attack a target. Will keep attacking the target until</span>
<span class="sd"> combat ends or another combat action is taken.</span>
<span class="sd"> Usage:</span>
<span class="sd"> attack/hit &lt;target&gt;</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;attack&quot;</span>
<span class="n">aliases</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;hit&quot;</span><span class="p">]</span>
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">&quot;combat&quot;</span>
<div class="viewcode-block" id="CmdAttack.func"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdAttack.func">[docs]</a> <span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">lhs</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">target</span><span class="p">:</span>
<span class="k">return</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_or_create_combathandler</span><span class="p">(</span><span class="n">target</span><span class="p">)</span>
<span class="c1"># we use a fixed dt of 3 here, to mimic Diku style; one could also picture</span>
<span class="c1"># attacking at a different rate, depending on skills/weapon etc.</span>
<span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">({</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;attack&quot;</span><span class="p">,</span> <span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="n">target</span><span class="p">,</span> <span class="s2">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="s2">&quot;repeat&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">})</span>
<span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;$You() $conj(attack) $You(</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">)!&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span></div></div>
<div class="viewcode-block" id="CmdLook"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdLook">[docs]</a><span class="k">class</span> <span class="nc">CmdLook</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CmdLook</span><span class="p">,</span> <span class="n">_BaseTwitchCombatCommand</span><span class="p">):</span>
<div class="viewcode-block" id="CmdLook.func"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdLook.func">[docs]</a> <span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># get regular look, followed by a combat summary</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">func</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">:</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_or_create_combathandler</span><span class="p">()</span>
<span class="n">txt</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">))</span>
<span class="n">maxwidth</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="n">display_len</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">txt</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;|r</span><span class="si">{</span><span class="n">pad</span><span class="p">(</span><span class="s1">&#39; Combat Status &#39;</span><span class="p">,</span><span class="w"> </span><span class="n">width</span><span class="o">=</span><span class="n">maxwidth</span><span class="p">,</span><span class="w"> </span><span class="n">fillchar</span><span class="o">=</span><span class="s1">&#39;-&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2">|n</span><span class="se">\n</span><span class="si">{</span><span class="n">txt</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span></div></div>
<div class="viewcode-block" id="CmdHold"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdHold">[docs]</a><span class="k">class</span> <span class="nc">CmdHold</span><span class="p">(</span><span class="n">_BaseTwitchCombatCommand</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Hold back your blows, doing nothing.</span>
<span class="sd"> Usage:</span>
<span class="sd"> hold</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;hold&quot;</span>
<div class="viewcode-block" id="CmdHold.func"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdHold.func">[docs]</a> <span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_or_create_combathandler</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">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">})</span>
<span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;$You() $conj(hold) back, doing nothing.&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span></div></div>
<div class="viewcode-block" id="CmdStunt"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdStunt">[docs]</a><span class="k">class</span> <span class="nc">CmdStunt</span><span class="p">(</span><span class="n">_BaseTwitchCombatCommand</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Perform a combat stunt, that boosts an ally against a target, or</span>
<span class="sd"> foils an enemy, giving them disadvantage against an ally.</span>
<span class="sd"> Usage:</span>
<span class="sd"> boost [ability] &lt;recipient&gt; &lt;target&gt;</span>
<span class="sd"> foil [ability] &lt;recipient&gt; &lt;target&gt;</span>
<span class="sd"> boost [ability] &lt;target&gt; (same as boost me &lt;target&gt;)</span>
<span class="sd"> foil [ability] &lt;target&gt; (same as foil &lt;target&gt; me)</span>
<span class="sd"> Example:</span>
<span class="sd"> boost STR me Goblin</span>
<span class="sd"> boost DEX Goblin</span>
<span class="sd"> foil STR Goblin me</span>
<span class="sd"> foil INT Goblin</span>
<span class="sd"> boost INT Wizard Goblin</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;stunt&quot;</span>
<span class="n">aliases</span> <span class="o">=</span> <span class="p">(</span>
<span class="s2">&quot;boost&quot;</span><span class="p">,</span>
<span class="s2">&quot;foil&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">&quot;combat&quot;</span>
<div class="viewcode-block" id="CmdStunt.parse"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdStunt.parse">[docs]</a> <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">args</span> <span class="ow">or</span> <span class="s2">&quot; &quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">args</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;Usage: &lt;ability&gt; &lt;recipient&gt; &lt;target&gt;&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">InterruptCommand</span><span class="p">()</span>
<span class="n">advantage</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">cmdname</span> <span class="o">!=</span> <span class="s2">&quot;foil&quot;</span>
<span class="c1"># extract data from the input</span>
<span class="n">stunt_type</span><span class="p">,</span> <span class="n">recipient</span><span class="p">,</span> <span class="n">target</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span>
<span class="n">stunt_type</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">if</span> <span class="n">stunt_type</span><span class="p">:</span>
<span class="n">stunt_type</span> <span class="o">=</span> <span class="n">stunt_type</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
<span class="n">args</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">args</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span>
<span class="n">recipient</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">target</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">args</span> <span class="k">else</span> <span class="kc">None</span>
<span class="c1"># validate input and try to guess if not given</span>
<span class="c1"># ability is requried</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">stunt_type</span> <span class="ow">or</span> <span class="n">stunt_type</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">ABILITY_REVERSE_MAP</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;&#39;</span><span class="si">{</span><span class="n">stunt_type</span><span class="si">}</span><span class="s2">&#39; is not a valid ability. Pick one of&quot;</span>
<span class="sa">f</span><span class="s2">&quot; </span><span class="si">{</span><span class="s1">&#39;, &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">ABILITY_REVERSE_MAP</span><span class="o">.</span><span class="n">keys</span><span class="p">())</span><span class="si">}</span><span class="s2">.&quot;</span>
<span class="p">)</span>
<span class="k">raise</span> <span class="n">InterruptCommand</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">recipient</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;Must give at least a recipient or target.&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">InterruptCommand</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">target</span><span class="p">:</span>
<span class="c1"># something like `boost str target`</span>
<span class="n">target</span> <span class="o">=</span> <span class="n">recipient</span> <span class="k">if</span> <span class="n">advantage</span> <span class="k">else</span> <span class="s2">&quot;me&quot;</span>
<span class="n">recipient</span> <span class="o">=</span> <span class="s2">&quot;me&quot;</span> <span class="k">if</span> <span class="n">advantage</span> <span class="k">else</span> <span class="n">recipient</span>
<span class="c1"># if we still have None:s at this point, we can&#39;t continue</span>
<span class="k">if</span> <span class="kc">None</span> <span class="ow">in</span> <span class="p">(</span><span class="n">stunt_type</span><span class="p">,</span> <span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;Both ability, recipient and target of stunt must be given.&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">InterruptCommand</span><span class="p">()</span>
<span class="c1"># save what we found so it can be accessed from func()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">advantage</span> <span class="o">=</span> <span class="n">advantage</span>
<span class="bp">self</span><span class="o">.</span><span class="n">stunt_type</span> <span class="o">=</span> <span class="n">ABILITY_REVERSE_MAP</span><span class="p">[</span><span class="n">stunt_type</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">recipient</span> <span class="o">=</span> <span class="n">recipient</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span> <span class="o">=</span> <span class="n">target</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span></div>
<div class="viewcode-block" id="CmdStunt.func"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdStunt.func">[docs]</a> <span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">target</span><span class="p">:</span>
<span class="k">return</span>
<span class="n">recipient</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">recipient</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">recipient</span><span class="p">:</span>
<span class="k">return</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_or_create_combathandler</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">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;stunt&quot;</span><span class="p">,</span>
<span class="s2">&quot;recipient&quot;</span><span class="p">:</span> <span class="n">recipient</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="n">target</span><span class="p">,</span>
<span class="s2">&quot;advantage&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">advantage</span><span class="p">,</span>
<span class="s2">&quot;stunt_type&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">stunt_type</span><span class="p">,</span>
<span class="s2">&quot;defense_type&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">stunt_type</span><span class="p">,</span>
<span class="s2">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">)</span>
<span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;$You() prepare a stunt!&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span></div></div>
<div class="viewcode-block" id="CmdUseItem"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdUseItem">[docs]</a><span class="k">class</span> <span class="nc">CmdUseItem</span><span class="p">(</span><span class="n">_BaseTwitchCombatCommand</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Use an item in combat. The item must be in your inventory to use.</span>
<span class="sd"> Usage:</span>
<span class="sd"> use &lt;item&gt;</span>
<span class="sd"> use &lt;item&gt; [on] &lt;target&gt;</span>
<span class="sd"> Examples:</span>
<span class="sd"> use potion</span>
<span class="sd"> use throwing knife on goblin</span>
<span class="sd"> use bomb goblin</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;use&quot;</span>
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">&quot;combat&quot;</span>
<div class="viewcode-block" id="CmdUseItem.parse"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdUseItem.parse">[docs]</a> <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">parse</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;What do you want to use?&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">InterruptCommand</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">item</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">lhs</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rhs</span> <span class="ow">or</span> <span class="s2">&quot;me&quot;</span></div>
<div class="viewcode-block" id="CmdUseItem.func"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdUseItem.func">[docs]</a> <span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">item</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">item</span><span class="p">,</span> <span class="n">candidates</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">get_usable_objects_from_backpack</span><span class="p">()</span>
<span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">item</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;(You must carry the item to use it.)&quot;</span><span class="p">)</span>
<span class="k">return</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">:</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">target</span><span class="p">:</span>
<span class="k">return</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_or_create_combathandler</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="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;use&quot;</span><span class="p">,</span> <span class="s2">&quot;item&quot;</span><span class="p">:</span> <span class="n">item</span><span class="p">,</span> <span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="n">target</span><span class="p">,</span> <span class="s2">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">})</span>
<span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() prepare to use </span><span class="si">{</span><span class="n">item</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span><span class="si">}</span><span class="s2">!&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span>
<span class="p">)</span></div></div>
<div class="viewcode-block" id="CmdWield"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdWield">[docs]</a><span class="k">class</span> <span class="nc">CmdWield</span><span class="p">(</span><span class="n">_BaseTwitchCombatCommand</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Wield a weapon or spell-rune. You will the wield the item, swapping with any other item(s) you</span>
<span class="sd"> were wielded before.</span>
<span class="sd"> Usage:</span>
<span class="sd"> wield &lt;weapon or spell&gt;</span>
<span class="sd"> Examples:</span>
<span class="sd"> wield sword</span>
<span class="sd"> wield shield</span>
<span class="sd"> wield fireball</span>
<span class="sd"> Note that wielding a shield will not replace the sword in your hand, while wielding a two-handed</span>
<span class="sd"> weapon (or a spell-rune) will take two hands and swap out what you were carrying.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;wield&quot;</span>
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">&quot;combat&quot;</span>
<div class="viewcode-block" id="CmdWield.parse"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdWield.parse">[docs]</a> <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;What do you want to wield?&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">InterruptCommand</span><span class="p">()</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">parse</span><span class="p">()</span></div>
<div class="viewcode-block" id="CmdWield.func"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.CmdWield.func">[docs]</a> <span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">item</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">,</span> <span class="n">candidates</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">get_wieldable_objects_from_backpack</span><span class="p">()</span>
<span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">item</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;(You must carry the item to wield it.)&quot;</span><span class="p">)</span>
<span class="k">return</span>
<span class="n">combathandler</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_or_create_combathandler</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">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;wield&quot;</span><span class="p">,</span> <span class="s2">&quot;item&quot;</span><span class="p">:</span> <span class="n">item</span><span class="p">,</span> <span class="s2">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">})</span>
<span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;$You() reach for </span><span class="si">{</span><span class="n">item</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span><span class="si">}</span><span class="s2">!&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span></div></div>
<div class="viewcode-block" id="TwitchCombatCmdSet"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.TwitchCombatCmdSet">[docs]</a><span class="k">class</span> <span class="nc">TwitchCombatCmdSet</span><span class="p">(</span><span class="n">CmdSet</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Add to character, to be able to attack others in a twitch-style way.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<div class="viewcode-block" id="TwitchCombatCmdSet.at_cmdset_creation"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.TwitchCombatCmdSet.at_cmdset_creation">[docs]</a> <span class="k">def</span> <span class="nf">at_cmdset_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdAttack</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdHold</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdStunt</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdUseItem</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdWield</span><span class="p">())</span></div></div>
<div class="viewcode-block" id="TwitchLookCmdSet"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.TwitchLookCmdSet">[docs]</a><span class="k">class</span> <span class="nc">TwitchLookCmdSet</span><span class="p">(</span><span class="n">CmdSet</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> This will be added/removed dynamically when in combat.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<div class="viewcode-block" id="TwitchLookCmdSet.at_cmdset_creation"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia.contrib.tutorials.evadventure.combat_twitch.TwitchLookCmdSet.at_cmdset_creation">[docs]</a> <span class="k">def</span> <span class="nf">at_cmdset_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdLook</span><span class="p">())</span></div></div>
</pre></div>
</div>
</div>
</div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../../../genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="../../../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../../../evennia.html" >evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.contrib.tutorials.evadventure.combat_twitch</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -83,7 +83,6 @@
<span class="sd">to the CharacterCmdSet</span>
<span class="sd">New commands:</span>
<span class="sd"> attack/hit &lt;target&gt;[,...]</span>
<span class="sd"> inventory</span>
<span class="sd"> wield/wear &lt;item&gt;</span>
<span class="sd"> unwield/remove &lt;item&gt;</span>
@ -111,7 +110,6 @@
<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">inherits_from</span>
<span class="kn">from</span> <span class="nn">.combat_turnbased</span> <span class="kn">import</span> <span class="n">CombatFailure</span><span class="p">,</span> <span class="n">join_combat</span>
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span>
<span class="kn">from</span> <span class="nn">.equipment</span> <span class="kn">import</span> <span class="n">EquipmentError</span>
<span class="kn">from</span> <span class="nn">.npcs</span> <span class="kn">import</span> <span class="n">EvAdventureTalkativeNPC</span>
@ -132,51 +130,6 @@
<span class="bp">self</span><span class="o">.</span><span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span></div></div>
<div class="viewcode-block" id="CmdAttackTurnBased"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.commands.html#evennia.contrib.tutorials.evadventure.commands.CmdAttackTurnBased">[docs]</a><span class="k">class</span> <span class="nc">CmdAttackTurnBased</span><span class="p">(</span><span class="n">EvAdventureCommand</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Attack a target or join an existing combat.</span>
<span class="sd"> Usage:</span>
<span class="sd"> attack &lt;target&gt;</span>
<span class="sd"> attack &lt;target&gt;, &lt;target&gt;, ...</span>
<span class="sd"> If the target is involved in combat already, you&#39;ll join combat with</span>
<span class="sd"> the first target you specify. Attacking multiple will draw them all into</span>
<span class="sd"> combat.</span>
<span class="sd"> This will start/join turn-based, combat, where you have a limited</span>
<span class="sd"> time to decide on your next action from a menu of options.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;attack&quot;</span>
<span class="n">aliases</span> <span class="o">=</span> <span class="p">(</span><span class="s2">&quot;hit&quot;</span><span class="p">,)</span>
<div class="viewcode-block" id="CmdAttackTurnBased.parse"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.commands.html#evennia.contrib.tutorials.evadventure.commands.CmdAttackTurnBased.parse">[docs]</a> <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">parse</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">targets</span> <span class="o">=</span> <span class="p">[</span><span class="n">name</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;,&quot;</span><span class="p">)]</span></div>
<div class="viewcode-block" id="CmdAttackTurnBased.func"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.commands.html#evennia.contrib.tutorials.evadventure.commands.CmdAttackTurnBased.func">[docs]</a> <span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># find if</span>
<span class="n">target_objs</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">target</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">targets</span><span class="p">:</span>
<span class="n">target_obj</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">target</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">target_obj</span><span class="p">:</span>
<span class="c1"># show a warning but don&#39;t abort</span>
<span class="k">continue</span>
<span class="n">target_objs</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">target_obj</span><span class="p">)</span>
<span class="k">if</span> <span class="n">target_objs</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">join_combat</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">,</span> <span class="o">*</span><span class="n">target_objs</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">session</span><span class="p">)</span>
<span class="k">except</span> <span class="n">CombatFailure</span> <span class="k">as</span> <span class="n">err</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;|r</span><span class="si">{</span><span class="n">err</span><span class="si">}</span><span class="s2">|n&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;|rFound noone to attack.|n&quot;</span><span class="p">)</span></div></div>
<div class="viewcode-block" id="CmdInventory"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.commands.html#evennia.contrib.tutorials.evadventure.commands.CmdInventory">[docs]</a><span class="k">class</span> <span class="nc">CmdInventory</span><span class="p">(</span><span class="n">EvAdventureCommand</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> View your inventory</span>
@ -347,15 +300,19 @@
<span class="n">item</span><span class="o">.</span><span class="n">move_to</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">quiet</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">move_type</span><span class="o">=</span><span class="s2">&quot;give&quot;</span><span class="p">)</span>
<span class="k">except</span> <span class="n">EquipmentError</span><span class="p">:</span>
<span class="n">caller</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You(</span><span class="si">{</span><span class="n">giver</span><span class="o">.</span><span class="n">key</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) $conj(try) to give &quot;</span>
<span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">item</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> to $You(</span><span class="si">{</span><span class="n">caller</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">), but they can&#39;t accept it since their &quot;</span>
<span class="s2">&quot;inventory is full.&quot;</span><span class="p">,</span>
<span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You(</span><span class="si">{</span><span class="n">giver</span><span class="o">.</span><span class="n">key</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) $conj(try) to give &quot;</span>
<span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">item</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> to $You(</span><span class="si">{</span><span class="n">caller</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">), but they can&#39;t accept it since their &quot;</span>
<span class="s2">&quot;inventory is full.&quot;</span>
<span class="p">),</span>
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">giver</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">giver</span><span class="p">,</span> <span class="n">caller</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">caller</span><span class="p">},</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">caller</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You(</span><span class="si">{</span><span class="n">giver</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) $conj(give) </span><span class="si">{</span><span class="n">item</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> to $You(</span><span class="si">{</span><span class="n">caller</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">), &quot;</span>
<span class="s2">&quot;and they accepted the offer.&quot;</span><span class="p">,</span>
<span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You(</span><span class="si">{</span><span class="n">giver</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) $conj(give) </span><span class="si">{</span><span class="n">item</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> to $You(</span><span class="si">{</span><span class="n">caller</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">), &quot;</span>
<span class="s2">&quot;and they accepted the offer.&quot;</span>
<span class="p">),</span>
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">giver</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">giver</span><span class="p">,</span> <span class="n">caller</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">caller</span><span class="p">},</span>
<span class="p">)</span>
<span class="n">giver</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">_evmenu</span><span class="o">.</span><span class="n">close_menu</span><span class="p">()</span>
@ -533,7 +490,6 @@
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;evadventure&quot;</span>
<div class="viewcode-block" id="EvAdventureCmdSet.at_cmdset_creation"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.commands.html#evennia.contrib.tutorials.evadventure.commands.EvAdventureCmdSet.at_cmdset_creation">[docs]</a> <span class="k">def</span> <span class="nf">at_cmdset_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdAttackTurnBased</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdInventory</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdWieldOrWear</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">CmdRemove</span><span class="p">())</span>

View file

@ -166,8 +166,8 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">allow_combat</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">allow_death</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">allow_combat</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">True</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">allow_death</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">True</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="c1"># dungeon generation attributes; set when room is created</span>
<span class="n">back_exit</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>

View file

@ -82,6 +82,7 @@
<span class="sd">enum, Python will give you an error while a typo in a string may go through silently.</span>
<span class="sd">It&#39;s used as a direct reference:</span>
<span class="sd">::</span>
<span class="sd"> from enums import Ability</span>
@ -91,11 +92,13 @@
<span class="sd">To get the `value` of an enum (must always be hashable, useful for Attribute lookups), use</span>
<span class="sd">`Ability.STR.value` (which would return &#39;strength&#39; in our case).</span>
<span class="sd">----</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">enum</span> <span class="kn">import</span> <span class="n">Enum</span>
<div class="viewcode-block" id="Ability"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.enums.html#evennia.contrib.tutorials.evadventure.combat_turnbased.Ability">[docs]</a><span class="k">class</span> <span class="nc">Ability</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<div class="viewcode-block" id="Ability"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.enums.html#evennia.contrib.tutorials.evadventure.enums.Ability">[docs]</a><span class="k">class</span> <span class="nc">Ability</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> The six base abilities (defense is always bonus + 10)</span>
@ -118,7 +121,17 @@
<span class="n">ALLEGIANCE_FRIENDLY</span> <span class="o">=</span> <span class="s2">&quot;friendly&quot;</span></div>
<div class="viewcode-block" id="WieldLocation"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.enums.html#evennia.contrib.tutorials.evadventure.combat_turnbased.WieldLocation">[docs]</a><span class="k">class</span> <span class="nc">WieldLocation</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<span class="n">ABILITY_REVERSE_MAP</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;str&quot;</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">&quot;dex&quot;</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="s2">&quot;con&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CON</span><span class="p">,</span>
<span class="s2">&quot;int&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">INT</span><span class="p">,</span>
<span class="s2">&quot;wis&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">WIS</span><span class="p">,</span>
<span class="s2">&quot;cha&quot;</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CHA</span><span class="p">,</span>
<span class="p">}</span>
<div class="viewcode-block" id="WieldLocation"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.enums.html#evennia.contrib.tutorials.evadventure.enums.WieldLocation">[docs]</a><span class="k">class</span> <span class="nc">WieldLocation</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Wield (or wear) locations.</span>
@ -133,7 +146,7 @@
<span class="n">HEAD</span> <span class="o">=</span> <span class="s2">&quot;head&quot;</span> <span class="c1"># helmets</span></div>
<div class="viewcode-block" id="ObjType"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.enums.html#evennia.contrib.tutorials.evadventure.combat_turnbased.ObjType">[docs]</a><span class="k">class</span> <span class="nc">ObjType</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<div class="viewcode-block" id="ObjType"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.enums.html#evennia.contrib.tutorials.evadventure.enums.ObjType">[docs]</a><span class="k">class</span> <span class="nc">ObjType</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Object types</span>
@ -145,6 +158,7 @@
<span class="n">HELMET</span> <span class="o">=</span> <span class="s2">&quot;helmet&quot;</span>
<span class="n">CONSUMABLE</span> <span class="o">=</span> <span class="s2">&quot;consumable&quot;</span>
<span class="n">GEAR</span> <span class="o">=</span> <span class="s2">&quot;gear&quot;</span>
<span class="n">THROWABLE</span> <span class="o">=</span> <span class="s2">&quot;throwable&quot;</span>
<span class="n">MAGIC</span> <span class="o">=</span> <span class="s2">&quot;magic&quot;</span>
<span class="n">QUEST</span> <span class="o">=</span> <span class="s2">&quot;quest&quot;</span>
<span class="n">TREASURE</span> <span class="o">=</span> <span class="s2">&quot;treasure&quot;</span></div>

View file

@ -84,7 +84,7 @@
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">inherits_from</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">EvAdventureObject</span><span class="p">,</span> <span class="n">WeaponEmptyHand</span>
<span class="kn">from</span> <span class="nn">.objects</span> <span class="kn">import</span> <span class="n">EvAdventureObject</span><span class="p">,</span> <span class="n">get_bare_hands</span>
<div class="viewcode-block" id="EquipmentError"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.equipment.html#evennia.contrib.tutorials.evadventure.equipment.EquipmentError">[docs]</a><span class="k">class</span> <span class="nc">EquipmentError</span><span class="p">(</span><span class="ne">TypeError</span><span class="p">):</span>
@ -127,6 +127,9 @@
<span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">:</span> <span class="p">[],</span>
<span class="p">},</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]</span> <span class="o">=</span> <span class="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">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]</span> <span class="k">if</span> <span class="n">obj</span> <span class="ow">and</span> <span class="n">obj</span><span class="o">.</span><span class="n">id</span>
<span class="p">]</span>
<span class="k">def</span> <span class="nf">_save</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
@ -245,7 +248,7 @@
<span class="k">if</span> <span class="ow">not</span> <span class="n">weapon</span><span class="p">:</span>
<span class="n">weapon</span> <span class="o">=</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">weapon</span><span class="p">:</span>
<span class="n">weapon</span> <span class="o">=</span> <span class="n">WeaponEmptyHand</span><span class="p">()</span>
<span class="n">weapon</span> <span class="o">=</span> <span class="n">get_bare_hands</span><span class="p">()</span>
<span class="k">return</span> <span class="n">weapon</span>
<div class="viewcode-block" id="EquipmentHandler.display_loadout"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.equipment.html#evennia.contrib.tutorials.evadventure.equipment.EquipmentHandler.display_loadout">[docs]</a> <span class="k">def</span> <span class="nf">display_loadout</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
@ -435,7 +438,9 @@
<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">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]</span>
<span class="k">if</span> <span class="n">obj</span><span class="o">.</span><span class="n">inventory_use_slot</span>
<span class="k">if</span> <span class="n">obj</span>
<span class="ow">and</span> <span class="n">obj</span><span class="o">.</span><span class="n">id</span>
<span class="ow">and</span> <span class="n">obj</span><span class="o">.</span><span class="n">inventory_use_slot</span>
<span class="ow">in</span> <span class="p">(</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">,</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">,</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">)</span>
<span class="p">]</span></div>
@ -453,7 +458,7 @@
<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">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]</span>
<span class="k">if</span> <span class="n">obj</span><span class="o">.</span><span class="n">inventory_use_slot</span> <span class="ow">in</span> <span class="p">(</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BODY</span><span class="p">,</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">HEAD</span><span class="p">)</span>
<span class="k">if</span> <span class="n">obj</span> <span class="ow">and</span> <span class="n">obj</span><span class="o">.</span><span class="n">id</span> <span class="ow">and</span> <span class="n">obj</span><span class="o">.</span><span class="n">inventory_use_slot</span> <span class="ow">in</span> <span class="p">(</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BODY</span><span class="p">,</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">HEAD</span><span class="p">)</span>
<span class="p">]</span></div>
<div class="viewcode-block" id="EquipmentHandler.get_usable_objects_from_backpack"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.equipment.html#evennia.contrib.tutorials.evadventure.equipment.EquipmentHandler.get_usable_objects_from_backpack">[docs]</a> <span class="k">def</span> <span class="nf">get_usable_objects_from_backpack</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
@ -466,7 +471,9 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">character</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">obj</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">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]</span> <span class="k">if</span> <span class="n">obj</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="n">character</span><span class="p">)]</span></div>
<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">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]</span> <span class="k">if</span> <span class="n">obj</span> <span class="ow">and</span> <span class="n">obj</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="n">character</span><span class="p">)</span>
<span class="p">]</span></div>
<div class="viewcode-block" id="EquipmentHandler.all"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.equipment.html#evennia.contrib.tutorials.evadventure.equipment.EquipmentHandler.all">[docs]</a> <span class="k">def</span> <span class="nf">all</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">only_objs</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>

View file

@ -84,12 +84,13 @@
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span>
<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">.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">WeaponEmptyHand</span>
<span class="kn">from</span> <span class="nn">.objects</span> <span class="kn">import</span> <span class="n">get_bare_hands</span>
<span class="kn">from</span> <span class="nn">.rules</span> <span class="kn">import</span> <span class="n">dice</span>
@ -128,9 +129,13 @@
<span class="n">is_idle</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">weapon</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="n">WeaponEmptyHand</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># instead of inventory</span>
<span class="n">weapon</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="n">get_bare_hands</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># instead of inventory</span>
<span class="n">coins</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># coin loot</span>
<span class="c1"># if this npc is attacked, everyone with the same tag in the current location will also be</span>
<span class="c1"># pulled into combat.</span>
<span class="n">group</span> <span class="o">=</span> <span class="n">TagProperty</span><span class="p">(</span><span class="s2">&quot;npcs&quot;</span><span class="p">)</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">strength</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">hit_dice</span>
@ -164,9 +169,17 @@
<span class="sd"> Start with max health.</span>
<span class="sd"> &quot;&quot;&quot;</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></div>
<span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span>
<span class="bp">self</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">&quot;npcs&quot;</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">&quot;group&quot;</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureNPC.ai_combat_next_action"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureNPC.ai_combat_next_action">[docs]</a> <span class="k">def</span> <span class="nf">ai_combat_next_action</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<div class="viewcode-block" id="EvAdventureNPC.at_attacked"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureNPC.at_attacked">[docs]</a> <span class="k">def</span> <span class="nf">at_attacked</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attacker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Called when being attacked and combat starts.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">pass</span></div>
<div class="viewcode-block" id="EvAdventureNPC.ai_next_action"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureNPC.ai_next_action">[docs]</a> <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 class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> The combat engine should ask this method in order to</span>
<span class="sd"> get the next action the npc should perform in combat.</span>
@ -321,7 +334,7 @@
<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_combat_next_action"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_combat_next_action">[docs]</a> <span class="k">def</span> <span class="nf">ai_combat_next_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combathandler</span><span class="p">):</span>
<div class="viewcode-block" id="EvAdventureMob.ai_next_action"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob.ai_next_action">[docs]</a> <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 class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Called to get the next action in combat.</span>
@ -334,7 +347,7 @@
<span class="sd"> combatant in the current combat handler.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">.combat_turnbased</span> <span class="kn">import</span> <span class="n">CombatActionAttack</span><span class="p">,</span> <span class="n">CombatActionDoNothing</span>
<span class="kn">from</span> <span class="nn">.combat</span> <span class="kn">import</span> <span class="n">CombatActionAttack</span><span class="p">,</span> <span class="n">CombatActionDoNothing</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">is_idle</span><span class="p">:</span>
<span class="c1"># mob just stands around</span>

View file

@ -96,13 +96,16 @@
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">AttributeProperty</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">AttributeProperty</span><span class="p">,</span> <span class="n">create_object</span><span class="p">,</span> <span class="n">search_object</span>
<span class="kn">from</span> <span class="nn">evennia.objects.objects</span> <span class="kn">import</span> <span class="n">DefaultObject</span>
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">make_iter</span>
<span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">rules</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">ObjType</span><span class="p">,</span> <span class="n">WieldLocation</span>
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">get_obj_stats</span>
<span class="n">_BARE_HANDS</span> <span class="o">=</span> <span class="kc">None</span>
<div class="viewcode-block" id="EvAdventureObject"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureObject">[docs]</a><span class="k">class</span> <span class="nc">EvAdventureObject</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
@ -146,7 +149,26 @@
<span class="sd"> str: The help text, by default taken from the `.help_text` property.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="s2">&quot;No help for this item.&quot;</span></div></div>
<span class="k">return</span> <span class="s2">&quot;No help for this item.&quot;</span></div>
<div class="viewcode-block" id="EvAdventureObject.at_pre_use"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureObject.at_pre_use">[docs]</a> <span class="k">def</span> <span class="nf">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Called before use. If returning False, usage should be aborted.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="kc">True</span></div>
<div class="viewcode-block" id="EvAdventureObject.use"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureObject.use">[docs]</a> <span class="k">def</span> <span class="nf">use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Use this object, whatever that may mean.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span></div>
<div class="viewcode-block" id="EvAdventureObject.at_post_use"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureObject.at_post_use">[docs]</a> <span class="k">def</span> <span class="nf">at_post_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Called after use happened.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">pass</span></div></div>
<div class="viewcode-block" id="EvAdventureObjectFiller"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureObjectFiller">[docs]</a><span class="k">class</span> <span class="nc">EvAdventureObjectFiller</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
@ -183,7 +205,7 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">TREASURE</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span></div>
<span class="n">value</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureConsumable"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureConsumable">[docs]</a><span class="k">class</span> <span class="nc">EvAdventureConsumable</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
@ -194,20 +216,30 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">CONSUMABLE</span>
<span class="n">size</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mf">0.25</span><span class="p">)</span>
<span class="n">uses</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">size</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mf">0.25</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">uses</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<div class="viewcode-block" id="EvAdventureConsumable.at_use"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureConsumable.at_use">[docs]</a> <span class="k">def</span> <span class="nf">at_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<div class="viewcode-block" id="EvAdventureConsumable.at_pre_use"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureConsumable.at_pre_use">[docs]</a> <span class="k">def</span> <span class="nf">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="n">target</span> <span class="ow">and</span> <span class="n">user</span><span class="o">.</span><span class="n">location</span> <span class="o">!=</span> <span class="n">target</span><span class="o">.</span><span class="n">location</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You are not close enough to the target!&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;|w</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is used up.|n&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="n">target</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureConsumable.use"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureConsumable.use">[docs]</a> <span class="k">def</span> <span class="nf">use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Consume a &#39;use&#39; of this item. Once it reaches 0 uses, it should normally</span>
<span class="sd"> not be usable anymore and probably be deleted.</span>
<span class="sd"> Args:</span>
<span class="sd"> user (Object): The one using the item.</span>
<span class="sd"> *args, **kwargs: Extra arguments depending on the usage and item.</span>
<span class="sd"> Use the consumable.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">pass</span></div>
<span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">location</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(use) </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">user</span><span class="p">)</span><span class="si">}</span><span class="s2">.&quot;</span><span class="p">,</span> <span class="n">from_obj</span><span class="o">=</span><span class="n">user</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureConsumable.at_post_use"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureConsumable.at_post_use">[docs]</a> <span class="k">def</span> <span class="nf">at_post_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
@ -220,28 +252,10 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> was used up.&quot;</span><span class="p">)</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;|w</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> was used up.|n&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span></div></div>
<div class="viewcode-block" id="WeaponEmptyHand"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.WeaponEmptyHand">[docs]</a><span class="k">class</span> <span class="nc">WeaponEmptyHand</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> This is a dummy-class loaded when you wield no weapons. We won&#39;t create any db-object for it.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">WEAPON</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;Empty Fists&quot;</span>
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span>
<span class="n">attack_type</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">STR</span>
<span class="n">defense_type</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">ARMOR</span>
<span class="n">damage_roll</span> <span class="o">=</span> <span class="s2">&quot;1d4&quot;</span>
<span class="n">quality</span> <span class="o">=</span> <span class="mi">100000</span> <span class="c1"># let&#39;s assume fists are always available ...</span>
<span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="s2">&quot;&lt;WeaponEmptyHand&gt;&quot;</span></div>
<div class="viewcode-block" id="EvAdventureWeapon"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureWeapon">[docs]</a><span class="k">class</span> <span class="nc">EvAdventureWeapon</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Base weapon class for all EvAdventure weapons.</span>
@ -256,6 +270,93 @@
<span class="n">attack_type</span> <span class="o">=</span> <span class="n">AttributeProperty</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="c1"># what defense stat of the enemy it must defeat</span>
<span class="n">defense_type</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">Ability</span><span class="o">.</span><span class="n">ARMOR</span><span class="p">)</span>
<span class="n">damage_roll</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="s2">&quot;1d6&quot;</span><span class="p">)</span>
<div class="viewcode-block" id="EvAdventureWeapon.get_display_name"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureWeapon.get_display_name">[docs]</a> <span class="k">def</span> <span class="nf">get_display_name</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="n">quality</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span>
<span class="n">quality_txt</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
<span class="k">if</span> <span class="n">quality</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">quality_txt</span> <span class="o">=</span> <span class="s2">&quot;|r(broken!)|n&quot;</span>
<span class="k">elif</span> <span class="n">quality</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
<span class="n">quality_txt</span> <span class="o">=</span> <span class="s2">&quot;|y(damaged)|n&quot;</span>
<span class="k">elif</span> <span class="n">quality</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">:</span>
<span class="n">quality_txt</span> <span class="o">=</span> <span class="s2">&quot;|Y(chipped)|n&quot;</span>
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">looker</span><span class="o">=</span><span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">+</span> <span class="n">quality_txt</span></div>
<div class="viewcode-block" id="EvAdventureWeapon.at_pre_use"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureWeapon.at_pre_use">[docs]</a> <span class="k">def</span> <span class="nf">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="n">target</span> <span class="ow">and</span> <span class="n">user</span><span class="o">.</span><span class="n">location</span> <span class="o">!=</span> <span class="n">target</span><span class="o">.</span><span class="n">location</span><span class="p">:</span>
<span class="c1"># we assume weapons can only be used in the same location</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You are not close enough to the target!&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">user</span><span class="p">)</span><span class="si">}</span><span class="s2"> is broken and can&#39;t be used!&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="n">target</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureWeapon.use"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureWeapon.use">[docs]</a> <span class="k">def</span> <span class="nf">use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">advantage</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">disadvantage</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;When a weapon is used, it attacks an opponent&quot;&quot;&quot;</span>
<span class="n">location</span> <span class="o">=</span> <span class="n">attacker</span><span class="o">.</span><span class="n">location</span>
<span class="n">is_hit</span><span class="p">,</span> <span class="n">quality</span><span class="p">,</span> <span class="n">txt</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">dice</span><span class="o">.</span><span class="n">opposed_saving_throw</span><span class="p">(</span>
<span class="n">attacker</span><span class="p">,</span>
<span class="n">target</span><span class="p">,</span>
<span class="n">attack_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">attack_type</span><span class="p">,</span>
<span class="n">defense_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">defense_type</span><span class="p">,</span>
<span class="n">advantage</span><span class="o">=</span><span class="n">advantage</span><span class="p">,</span>
<span class="n">disadvantage</span><span class="o">=</span><span class="n">disadvantage</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;$You() $conj(attack) $You(</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">) with </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">txt</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">,</span>
<span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span>
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">},</span>
<span class="p">)</span>
<span class="k">if</span> <span class="n">is_hit</span><span class="p">:</span>
<span class="c1"># enemy hit, calculate damage</span>
<span class="n">dmg</span> <span class="o">=</span> <span class="n">rules</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="bp">self</span><span class="o">.</span><span class="n">damage_roll</span><span class="p">)</span>
<span class="k">if</span> <span class="n">quality</span> <span class="ow">is</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CRITICAL_SUCCESS</span><span class="p">:</span>
<span class="c1"># doble damage roll for critical success</span>
<span class="n">dmg</span> <span class="o">+=</span> <span class="n">rules</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="bp">self</span><span class="o">.</span><span class="n">damage_roll</span><span class="p">)</span>
<span class="n">message</span> <span class="o">=</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot; $You() |ycritically|n $conj(hit) $You(</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">) for |r</span><span class="si">{</span><span class="n">dmg</span><span class="si">}</span><span class="s2">|n damage!&quot;</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">message</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot; $You() $conj(hit) $You(</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">) for |r</span><span class="si">{</span><span class="n">dmg</span><span class="si">}</span><span class="s2">|n damage!&quot;</span>
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span> <span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">})</span>
<span class="c1"># call hook</span>
<span class="n">target</span><span class="o">.</span><span class="n">at_damage</span><span class="p">(</span><span class="n">dmg</span><span class="p">,</span> <span class="n">attacker</span><span class="o">=</span><span class="n">attacker</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># a miss</span>
<span class="n">message</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot; $You() $conj(miss) $You(</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">).&quot;</span>
<span class="k">if</span> <span class="n">quality</span> <span class="ow">is</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CRITICAL_FAILURE</span><span class="p">:</span>
<span class="n">message</span> <span class="o">+=</span> <span class="s2">&quot;.. it&#39;s a |rcritical miss!|n, damaging the weapon.&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span> <span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">})</span></div>
<div class="viewcode-block" id="EvAdventureWeapon.at_post_use"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureWeapon.at_post_use">[docs]</a> <span class="k">def</span> <span class="nf">at_post_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;|r</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">user</span><span class="p">)</span><span class="si">}</span><span class="s2"> breaks and can no longer be used!&quot;</span><span class="p">)</span></div></div>
<div class="viewcode-block" id="EvAdventureThrowable"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.EvAdventureThrowable">[docs]</a><span class="k">class</span> <span class="nc">EvAdventureThrowable</span><span class="p">(</span><span class="n">EvAdventureWeapon</span><span class="p">,</span> <span class="n">EvAdventureConsumable</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Something you can throw at an enemy to harm them once, like a knife or exploding potion/grenade.</span>
<span class="sd"> Note: In Knave, ranged attacks are done with WIS (representing the stillness of your mind?)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="p">(</span><span class="n">ObjType</span><span class="o">.</span><span class="n">THROWABLE</span><span class="p">,</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">WEAPON</span><span class="p">,</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">CONSUMABLE</span><span class="p">)</span>
<span class="n">attack_type</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">Ability</span><span class="o">.</span><span class="n">WIS</span><span class="p">)</span>
<span class="n">defense_type</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">Ability</span><span class="o">.</span><span class="n">DEX</span><span class="p">)</span>
<span class="n">damage_roll</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="s2">&quot;1d6&quot;</span><span class="p">)</span></div>
@ -317,6 +418,37 @@
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">HELMET</span>
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">HEAD</span></div>
<div class="viewcode-block" id="WeaponBareHands"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.WeaponBareHands">[docs]</a><span class="k">class</span> <span class="nc">WeaponBareHands</span><span class="p">(</span><span class="n">EvAdventureWeapon</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> This is a dummy-class loaded when you wield no weapons. We won&#39;t create any db-object for it.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">WEAPON</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;Bare hands&quot;</span>
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span>
<span class="n">attack_type</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">STR</span>
<span class="n">defense_type</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">ARMOR</span>
<span class="n">damage_roll</span> <span class="o">=</span> <span class="s2">&quot;1d4&quot;</span>
<span class="n">quality</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># let&#39;s assume fists are always available ...</span></div>
<div class="viewcode-block" id="get_bare_hands"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.objects.html#evennia.contrib.tutorials.evadventure.objects.get_bare_hands">[docs]</a><span class="k">def</span> <span class="nf">get_bare_hands</span><span class="p">():</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Get the bare-hands singleton object.</span>
<span class="sd"> Returns:</span>
<span class="sd"> WeaponBareHands</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">global</span> <span class="n">_BARE_HANDS</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">_BARE_HANDS</span><span class="p">:</span>
<span class="n">_BARE_HANDS</span> <span class="o">=</span> <span class="n">search_object</span><span class="p">(</span><span class="s2">&quot;Bare hands&quot;</span><span class="p">,</span> <span class="n">typeclass</span><span class="o">=</span><span class="n">WeaponBareHands</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">_BARE_HANDS</span><span class="p">:</span>
<span class="n">_BARE_HANDS</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">WeaponBareHands</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;Bare hands&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">_BARE_HANDS</span></div>
</pre></div>
</div>

View file

@ -89,7 +89,7 @@
<span class="kn">from</span> <span class="nn">copy</span> <span class="kn">import</span> <span class="n">deepcopy</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">DefaultRoom</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">AttributeProperty</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">,</span> <span class="n">DefaultRoom</span>
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">inherits_from</span>
<span class="n">CHAR_SYMBOL</span> <span class="o">=</span> <span class="s2">&quot;|w@|n&quot;</span>
@ -122,9 +122,9 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">allow_combat</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">allow_pvp</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">allow_death</span> <span class="o">=</span> <span class="kc">False</span>
<span class="n">allow_combat</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">allow_pvp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">allow_death</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">False</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="EvAdventureRoom.format_appearance"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.rooms.html#evennia.contrib.tutorials.evadventure.rooms.EvAdventureRoom.format_appearance">[docs]</a> <span class="k">def</span> <span class="nf">format_appearance</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">appearance</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Don&#39;t left-strip the appearance string&quot;&quot;&quot;</span>
@ -168,13 +168,12 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">allow_combat</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">allow_pvp</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">allow_combat</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">True</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">allow_pvp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">True</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="EvAdventurePvPRoom.get_display_footer"><a class="viewcode-back" href="../../../../../api/evennia.contrib.tutorials.evadventure.rooms.html#evennia.contrib.tutorials.evadventure.rooms.EvAdventurePvPRoom.get_display_footer">[docs]</a> <span class="k">def</span> <span class="nf">get_display_footer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Show if the room is &#39;cleared&#39; or not as part of its description.</span>
<span class="sd"> Customize footer of description.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="s2">&quot;|yNon-lethal PvP combat is allowed here!|n&quot;</span></div></div>
</pre></div>

View file

@ -79,25 +79,10 @@
<span></span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd">MUD ruleset based on the _Knave_ OSR tabletop RPG by Ben Milton (modified for MUD use).</span>
<span class="sd">The rules are divided into a set of classes. While each class (except chargen) could</span>
<span class="sd">also have been stand-alone functions, having them as classes makes it a little easier</span>
<span class="sd">to use them as the base for your own variation (tweaking values etc).</span>
<span class="sd">The center of the rule system is the &quot;RollEngine&quot;, which handles all rolling of dice</span>
<span class="sd">and determining what the outcome is.</span>
<span class="sd">- Roll-engine: Class with methods for making all dice rolls needed by the rules. Knave only</span>
<span class="sd"> has a few resolution rolls, but we define helper methods for different actions the</span>
<span class="sd"> character will be able to do in-code.</span>
<span class="sd">- Character generation - this is a container used for holding, tweaking and setting</span>
<span class="sd"> all character data during character generation. At the end it will save itself</span>
<span class="sd"> onto the Character for permanent storage.</span>
<span class="sd">- Improvement - this container holds rules used with experience to improve the</span>
<span class="sd"> character over time.</span>
<span class="sd">- Charsheet - a container with tools for visually displaying the character sheet in-game.</span>
<span class="sd">This module presents several singletons to import</span>
<span class="sd">- `dice` - the `EvAdventureRollEngine` for all random resolution and table-rolling.</span>
<span class="sd">- `character_sheet` - the `EvAdventureCharacterSheet` visualizer.</span>
<span class="sd">- `improvement` - the EvAdventureImprovement` class for handling char xp and leveling.</span>
<span class="sd">----</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span>
@ -235,13 +220,13 @@
<span class="n">bontxt</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;(+</span><span class="si">{</span><span class="n">bonus</span><span class="si">}</span><span class="s2">)&quot;</span>
<span class="n">modtxt</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
<span class="k">if</span> <span class="n">modifier</span><span class="p">:</span>
<span class="n">modtxt</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot; + </span><span class="si">{</span><span class="n">modifier</span><span class="si">}</span><span class="s2">&quot;</span> <span class="k">if</span> <span class="n">modifier</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="sa">f</span><span class="s2">&quot; - </span><span class="si">{</span><span class="nb">abs</span><span class="p">(</span><span class="n">modifier</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="n">modtxt</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;+ </span><span class="si">{</span><span class="n">modifier</span><span class="si">}</span><span class="s2">&quot;</span> <span class="k">if</span> <span class="n">modifier</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="sa">f</span><span class="s2">&quot; - </span><span class="si">{</span><span class="nb">abs</span><span class="p">(</span><span class="n">modifier</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="n">qualtxt</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot; (</span><span class="si">{</span><span class="n">quality</span><span class="o">.</span><span class="n">value</span><span class="si">}</span><span class="s2">!)&quot;</span> <span class="k">if</span> <span class="n">quality</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span>
<span class="n">txt</span> <span class="o">=</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;rolled </span><span class="si">{</span><span class="n">dice_roll</span><span class="si">}</span><span class="s2"> on </span><span class="si">{</span><span class="n">rolltxt</span><span class="si">}</span><span class="s2"> &quot;</span>
<span class="sa">f</span><span class="s2">&quot; rolled </span><span class="si">{</span><span class="n">dice_roll</span><span class="si">}</span><span class="s2"> on </span><span class="si">{</span><span class="n">rolltxt</span><span class="si">}</span><span class="s2"> &quot;</span>
<span class="sa">f</span><span class="s2">&quot;+ </span><span class="si">{</span><span class="n">bonus_type</span><span class="o">.</span><span class="n">value</span><span class="si">}{</span><span class="n">bontxt</span><span class="si">}{</span><span class="n">modtxt</span><span class="si">}</span><span class="s2"> vs &quot;</span>
<span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2"> -&gt; |w</span><span class="si">{</span><span class="n">result</span><span class="si">}{</span><span class="n">qualtxt</span><span class="si">}</span><span class="s2">|n&quot;</span>
<span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2"> -&gt; |w</span><span class="si">{</span><span class="s1">&#39;|GSuccess|w&#39;</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="s1">&#39;|RFail|w&#39;</span><span class="si">}{</span><span class="n">qualtxt</span><span class="si">}</span><span class="s2">|n&quot;</span>
<span class="p">)</span>
<span class="k">return</span> <span class="p">(</span><span class="n">dice_roll</span> <span class="o">+</span> <span class="n">bonus</span> <span class="o">+</span> <span class="n">modifier</span><span class="p">)</span> <span class="o">&gt;</span> <span class="n">target</span><span class="p">,</span> <span class="n">quality</span><span class="p">,</span> <span class="n">txt</span></div>
@ -323,8 +308,7 @@
<span class="c1"># tuple with range conditional, like (&#39;1-5&#39;, &quot;Blue&quot;) or (&#39;10&#39;, &quot;Purple&quot;)</span>
<span class="n">max_range</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
<span class="n">min_range</span> <span class="o">=</span> <span class="mi">10</span><span class="o">**</span><span class="mi">6</span>
<span class="k">for</span> <span class="p">(</span><span class="n">valrange</span><span class="p">,</span> <span class="n">choice</span><span class="p">)</span> <span class="ow">in</span> <span class="n">table_choices</span><span class="p">:</span>
<span class="k">for</span> <span class="n">valrange</span><span class="p">,</span> <span class="n">choice</span> <span class="ow">in</span> <span class="n">table_choices</span><span class="p">:</span>
<span class="n">minval</span><span class="p">,</span> <span class="o">*</span><span class="n">maxval</span> <span class="o">=</span> <span class="n">valrange</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;-&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">minval</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">minval</span><span class="p">))</span>
<span class="n">maxval</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">maxval</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">if</span> <span class="n">maxval</span> <span class="k">else</span> <span class="n">minval</span><span class="p">)</span>
@ -410,9 +394,11 @@
<span class="nb">setattr</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="n">abi</span><span class="p">,</span> <span class="n">current_abi</span><span class="p">)</span>
<span class="n">character</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="s2">&quot;~&quot;</span> <span class="o">*</span> <span class="mi">78</span> <span class="o">+</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">|yYou survive your brush with death, &quot;</span>
<span class="s2">&quot;~&quot;</span> <span class="o">*</span> <span class="mi">78</span>
<span class="o">+</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">|yYou survive your brush with death, &quot;</span>
<span class="sa">f</span><span class="s2">&quot;but are |r</span><span class="si">{</span><span class="n">result</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span><span class="si">}</span><span class="s2">|y and permanently |rlose </span><span class="si">{</span><span class="n">loss</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">abi</span><span class="si">}</span><span class="s2">|y.|n</span><span class="se">\n</span><span class="s2">&quot;</span>
<span class="sa">f</span><span class="s2">&quot;|GYou recover |g</span><span class="si">{</span><span class="n">new_hp</span><span class="si">}</span><span class="s2">|G health|.</span><span class="se">\n</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="s2">&quot;~&quot;</span> <span class="o">*</span> <span class="mi">78</span>
<span class="sa">f</span><span class="s2">&quot;|GYou recover |g</span><span class="si">{</span><span class="n">new_hp</span><span class="si">}</span><span class="s2">|G health|.</span><span class="se">\n</span><span class="s2">&quot;</span>
<span class="o">+</span> <span class="s2">&quot;~&quot;</span> <span class="o">*</span> <span class="mi">78</span>
<span class="p">)</span></div></div>

View file

@ -81,42 +81,42 @@
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">unittest.mock</span> <span class="kn">import</span> <span class="n">MagicMock</span><span class="p">,</span> <span class="n">patch</span>
<span class="kn">from</span> <span class="nn">unittest.mock</span> <span class="kn">import</span> <span class="n">Mock</span><span class="p">,</span> <span class="n">call</span><span class="p">,</span> <span class="n">patch</span>
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">create</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">BaseEvenniaTest</span>
<span class="kn">from</span> <span class="nn">evennia.utils.ansi</span> <span class="kn">import</span> <span class="n">strip_ansi</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">BaseEvenniaTest</span><span class="p">,</span> <span class="n">EvenniaCommandTestMixin</span><span class="p">,</span> <span class="n">EvenniaTestCase</span>
<span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">combat_turnbased</span>
<span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">combat_base</span><span class="p">,</span> <span class="n">combat_turnbased</span><span class="p">,</span> <span class="n">combat_twitch</span>
<span class="kn">from</span> <span class="nn">..characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
<span class="kn">from</span> <span class="nn">..enums</span> <span class="kn">import</span> <span class="n">WieldLocation</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">..npcs</span> <span class="kn">import</span> <span class="n">EvAdventureMob</span>
<span class="kn">from</span> <span class="nn">..objects</span> <span class="kn">import</span> <span class="n">EvAdventureConsumable</span><span class="p">,</span> <span class="n">EvAdventureRunestone</span><span class="p">,</span> <span class="n">EvAdventureWeapon</span>
<span class="kn">from</span> <span class="nn">.mixins</span> <span class="kn">import</span> <span class="n">EvAdventureMixin</span>
<span class="kn">from</span> <span class="nn">..rooms</span> <span class="kn">import</span> <span class="n">EvAdventureRoom</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest">[docs]</a><span class="k">class</span> <span class="nc">EvAdventureTurnbasedCombatHandlerTest</span><span class="p">(</span><span class="n">EvAdventureMixin</span><span class="p">,</span> <span class="n">BaseEvenniaTest</span><span class="p">):</span>
<span class="k">class</span> <span class="nc">_CombatTestBase</span><span class="p">(</span><span class="n">EvenniaTestCase</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Test methods on the turn-based combat handler.</span>
<span class="sd"> Set up common entities for testing combat:</span>
<span class="sd"> - `location` (key=testroom)</span>
<span class="sd"> - `combatant` (key=testchar)</span>
<span class="sd"> - `target` (key=testmonster)`</span>
<span class="sd"> We also mock the `.msg` method of both `combatant` and `target` so we can</span>
<span class="sd"> see what was sent.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">maxDiff</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># make sure to mock away all time-keeping elements</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.setUp"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.setUp">[docs]</a> <span class="nd">@patch</span><span class="p">(</span>
<span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_turnbased&quot;</span>
<span class="s2">&quot;.EvAdventureCombatHandler.interval&quot;</span><span class="p">,</span>
<span class="n">new</span><span class="o">=-</span><span class="mi">1</span><span class="p">,</span>
<span class="p">)</span>
<span class="nd">@patch</span><span class="p">(</span>
<span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_turnbased.delay&quot;</span><span class="p">,</span>
<span class="n">new</span><span class="o">=</span><span class="n">MagicMock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="kc">None</span><span class="p">),</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">location</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;testroom&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span>
<span class="n">EvAdventureCharacter</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;testchar&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">location</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">allow_combat</span> <span class="o">=</span> <span class="kc">True</span>
<span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">allow_death</span> <span class="o">=</span> <span class="kc">True</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">character</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span>
<span class="n">EvAdventureMob</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;testmonster&quot;</span><span class="p">,</span>
@ -124,180 +124,158 @@
<span class="n">attributes</span><span class="o">=</span><span class="p">((</span><span class="s2">&quot;is_idle&quot;</span><span class="p">,</span> <span class="kc">True</span><span class="p">),),</span>
<span class="p">)</span>
<span class="c1"># this already starts turn 1</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combat_turnbased</span><span class="o">.</span><span class="n">join_combat</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)</span></div>
<span class="c1"># mock the msg so we can check what they were sent later</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="o">.</span><span class="n">msg</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">msg</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.tearDown"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.tearDown">[docs]</a> <span class="k">def</span> <span class="nf">tearDown</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">combathandler</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_remove_combatant"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_remove_combatant">[docs]</a> <span class="k">def</span> <span class="nf">test_remove_combatant</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">assertTrue</span><span class="p">(</span><span class="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combathandler</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">remove_combatant</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertFalse</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">combatants</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertFalse</span><span class="p">(</span><span class="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combathandler</span><span class="p">))</span></div>
<div class="viewcode-block" id="TestEvAdventureCombatBaseHandler"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureCombatBaseHandler">[docs]</a><span class="k">class</span> <span class="nc">TestEvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">_CombatTestBase</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Test the base functionality of the base combat handler.</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_start_turn"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_start_turn">[docs]</a> <span class="k">def</span> <span class="nf">test_start_turn</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">combathandler</span><span class="o">.</span><span class="n">_start_turn</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">combathandler</span><span class="o">.</span><span class="n">turn</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">_start_turn</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">combathandler</span><span class="o">.</span><span class="n">turn</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span></div>
<span class="sd"> &quot;&quot;&quot;</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_end_of_turn__empty"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_end_of_turn__empty">[docs]</a> <span class="k">def</span> <span class="nf">test_end_of_turn__empty</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">combathandler</span><span class="o">.</span><span class="n">_end_turn</span><span class="p">()</span></div>
<div class="viewcode-block" id="TestEvAdventureCombatBaseHandler.setUp"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureCombatBaseHandler.setUp">[docs]</a> <span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;This also tests the `get_or_create_combathandler` classfunc&quot;&quot;&quot;</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">EvAdventureCombatBaseHandler</span><span class="o">.</span><span class="n">get_or_create_combathandler</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;combathandler&quot;</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_add_combatant"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_add_combatant">[docs]</a> <span class="k">def</span> <span class="nf">test_add_combatant</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">combathandler</span><span class="o">.</span><span class="n">_init_menu</span> <span class="o">=</span> <span class="n">MagicMock</span><span class="p">()</span>
<span class="n">combatant3</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;testcharacter3&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">add_combatant</span><span class="p">(</span><span class="n">combatant3</span><span class="p">)</span>
<div class="viewcode-block" id="TestEvAdventureCombatBaseHandler.test_combathandler_msg"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureCombatBaseHandler.test_combathandler_msg">[docs]</a> <span class="k">def</span> <span class="nf">test_combathandler_msg</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Test sending messages to all in handler&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="n">combatant3</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">combatants</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">_init_menu</span><span class="o">.</span><span class="n">assert_called_once</span><span class="p">()</span></div>
<span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_start_combat"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_start_combat">[docs]</a> <span class="k">def</span> <span class="nf">test_start_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">_start_turn</span> <span class="o">=</span> <span class="n">MagicMock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">start</span> <span class="o">=</span> <span class="n">MagicMock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">start_combat</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">_start_turn</span><span class="o">.</span><span class="n">assert_called_once</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">start</span><span class="o">.</span><span class="n">assert_called_once</span><span class="p">()</span></div>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;test_message&quot;</span><span class="p">)</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_combat_summary"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_combat_summary">[docs]</a> <span class="k">def</span> <span class="nf">test_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="s2">&quot;You (4 / 4 health)&quot;</span> <span class="ow">in</span> <span class="n">result</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="s2">&quot;testmonster&quot;</span> <span class="ow">in</span> <span class="n">result</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_msg"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_msg">[docs]</a> <span class="k">def</span> <span class="nf">test_msg</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">location</span><span class="o">.</span><span class="n">msg_contents</span> <span class="o">=</span> <span class="n">MagicMock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You hurt the target&quot;</span><span class="p">,</span> <span class="n">combatant</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="o">.</span><span class="n">assert_called_with</span><span class="p">(</span>
<span class="s2">&quot;You hurt the target&quot;</span><span class="p">,</span>
<span class="n">from_obj</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span>
<span class="s2">&quot;test_message&quot;</span><span class="p">,</span>
<span class="n">exclude</span><span class="o">=</span><span class="p">[],</span>
<span class="n">from_obj</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;testchar&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="s2">&quot;testmonster&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">},</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_gain_advantage"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_gain_advantage">[docs]</a> <span class="k">def</span> <span class="nf">test_gain_advantage</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">combathandler</span><span class="o">.</span><span class="n">gain_advantage</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">advantage_matrix</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">][</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">]))</span></div>
<div class="viewcode-block" id="TestEvAdventureCombatBaseHandler.test_get_combat_summary"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureCombatBaseHandler.test_get_combat_summary">[docs]</a> <span class="k">def</span> <span class="nf">test_get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Test combat summary&quot;&quot;&quot;</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_gain_disadvantage"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_gain_disadvantage">[docs]</a> <span class="k">def</span> <span class="nf">test_gain_disadvantage</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">combathandler</span><span class="o">.</span><span class="n">gain_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">disadvantage_matrix</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">][</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">]))</span></div>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_sides</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="p">([],</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">]))</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_flee"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_flee">[docs]</a> <span class="k">def</span> <span class="nf">test_flee</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">combathandler</span><span class="o">.</span><span class="n">flee</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">fleeing_combatants</span><span class="p">)</span></div>
<span class="c1"># as seen from one side</span>
<span class="n">result</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">))</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_unflee"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_unflee">[docs]</a> <span class="k">def</span> <span class="nf">test_unflee</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">combathandler</span><span class="o">.</span><span class="n">unflee</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertFalse</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">fleeing_combatants</span><span class="p">)</span></div>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span>
<span class="n">strip_ansi</span><span class="p">(</span><span class="n">result</span><span class="p">),</span>
<span class="s2">&quot; testchar (Perfect) vs testmonster (Perfect) &quot;</span><span class="p">,</span>
<span class="p">)</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_register_and_run_action"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_register_and_run_action">[docs]</a> <span class="k">def</span> <span class="nf">test_register_and_run_action</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">action_class</span> <span class="o">=</span> <span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionAttack</span>
<span class="n">action</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">combatant_actions</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">][</span><span class="n">action_class</span><span class="o">.</span><span class="n">key</span><span class="p">]</span>
<span class="c1"># as seen from other side</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_sides</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="p">([],</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">]))</span>
<span class="n">result</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">register_action</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">action</span><span class="o">.</span><span class="n">key</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">combathandler</span><span class="o">.</span><span class="n">action_queue</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">],</span> <span class="p">(</span><span class="n">action</span><span class="p">,</span> <span class="p">(),</span> <span class="p">{}))</span>
<span class="n">action</span><span class="o">.</span><span class="n">use</span> <span class="o">=</span> <span class="n">MagicMock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">_end_turn</span><span class="p">()</span>
<span class="n">action</span><span class="o">.</span><span class="n">use</span><span class="o">.</span><span class="n">assert_called_once</span><span class="p">()</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_get_available_actions"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_get_available_actions">[docs]</a> <span class="k">def</span> <span class="nf">test_get_available_actions</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_available_actions</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">result</span><span class="p">),</span> <span class="mi">7</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="n">strip_ansi</span><span class="p">(</span><span class="n">result</span><span class="p">),</span>
<span class="s2">&quot; testmonster (Perfect) vs testchar (Perfect) &quot;</span><span class="p">,</span>
<span class="p">)</span></div></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatActionTest"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatActionTest">[docs]</a><span class="k">class</span> <span class="nc">EvAdventureTurnbasedCombatActionTest</span><span class="p">(</span><span class="n">EvAdventureMixin</span><span class="p">,</span> <span class="n">BaseEvenniaTest</span><span class="p">):</span>
<div class="viewcode-block" id="TestCombatActionsBase"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestCombatActionsBase">[docs]</a><span class="k">class</span> <span class="nc">TestCombatActionsBase</span><span class="p">(</span><span class="n">_CombatTestBase</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Test actions in turn_based combat.</span>
<span class="sd"> A class for testing all subclasses of CombatAction in combat_base.py</span>
<span class="sd"> &quot;&quot;&quot;</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatActionTest.setUp"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatActionTest.setUp">[docs]</a> <span class="nd">@patch</span><span class="p">(</span>
<span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_turnbased&quot;</span>
<span class="s2">&quot;.EvAdventureCombatHandler.interval&quot;</span><span class="p">,</span>
<span class="n">new</span><span class="o">=-</span><span class="mi">1</span><span class="p">,</span>
<span class="p">)</span>
<span class="nd">@patch</span><span class="p">(</span>
<span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_turnbased.delay&quot;</span><span class="p">,</span>
<span class="n">new</span><span class="o">=</span><span class="n">MagicMock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="kc">None</span><span class="p">),</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<div class="viewcode-block" id="TestCombatActionsBase.setUp"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestCombatActionsBase.setUp">[docs]</a> <span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">allow_combat</span> <span class="o">=</span> <span class="kc">True</span>
<span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">allow_death</span> <span class="o">=</span> <span class="kc">True</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">character</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant2</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;testcharacter2&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span>
<span class="n">EvAdventureMob</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;testmonster&quot;</span><span class="p">,</span> <span class="n">attributes</span><span class="o">=</span><span class="p">((</span><span class="s2">&quot;is_idle&quot;</span><span class="p">,</span> <span class="kc">True</span><span class="p">),)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">EvAdventureCombatBaseHandler</span><span class="o">.</span><span class="n">get_or_create_combathandler</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;combathandler&quot;</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="mi">4</span>
<span class="c1"># we need to mock all NotImplemented methods</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_sides</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="p">([],</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">]))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">give_advantage</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">give_disadvantage</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">remove_advantage</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">remove_disadvantage</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_advantage</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_disadvantage</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_disadvantage</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span></div>
<span class="c1"># this already starts turn 1</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combat_turnbased</span><span class="o">.</span><span class="n">join_combat</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)</span></div>
<div class="viewcode-block" id="TestCombatActionsBase.test_base_action"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestCombatActionsBase.test_base_action">[docs]</a> <span class="k">def</span> <span class="nf">test_base_action</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="n">combat_base</span><span class="o">.</span><span class="n">CombatAction</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">,</span> <span class="s2">&quot;foo&quot;</span><span class="p">:</span> <span class="s2">&quot;bar&quot;</span><span class="p">}</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">action</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="s2">&quot;hold&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">action</span><span class="o">.</span><span class="n">foo</span><span class="p">,</span> <span class="s2">&quot;bar&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">action</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">action</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span></div>
<span class="k">def</span> <span class="nf">_run_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">action</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">register_action</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">action</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">_end_turn</span><span class="p">()</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatActionTest.test_do_nothing"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatActionTest.test_do_nothing">[docs]</a> <span class="k">def</span> <span class="nf">test_do_nothing</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">combathandler</span><span class="o">.</span><span class="n">msg</span> <span class="o">=</span> <span class="n">MagicMock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionDoNothing</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="o">.</span><span class="n">assert_called</span><span class="p">()</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatActionTest.test_attack__miss"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatActionTest.test_attack__miss">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_turnbased.rules.randint&quot;</span><span class="p">)</span>
<div class="viewcode-block" id="TestCombatActionsBase.test_attack__miss"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestCombatActionsBase.test_attack__miss">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_base.rules.randint&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_attack__miss</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
<span class="n">actiondict</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;attack&quot;</span><span class="p">,</span> <span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">}</span>
<span class="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">8</span> <span class="c1"># target has default armor 11, so 8+1 str will miss</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionAttack</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)</span>
<span class="n">action</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">CombatActionAttack</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">actiondict</span><span class="p">)</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</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">target</span><span class="o">.</span><span class="n">hp</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatActionTest.test_attack__success__still_alive"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatActionTest.test_attack__success__still_alive">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_turnbased.rules.randint&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_attack__success__still_alive</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
<div class="viewcode-block" id="TestCombatActionsBase.test_attack__success"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestCombatActionsBase.test_attack__success">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_base.rules.randint&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_attack__success</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
<span class="n">actiondict</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;attack&quot;</span><span class="p">,</span> <span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">}</span>
<span class="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">11</span> <span class="c1"># 11 + 1 str will hit beat armor 11</span>
<span class="c1"># make sure target survives</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="mi">20</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionAttack</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)</span>
<span class="n">action</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">CombatActionAttack</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">actiondict</span><span class="p">)</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</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">target</span><span class="o">.</span><span class="n">hp</span><span class="p">,</span> <span class="mi">9</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatActionTest.test_attack__success__kill"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatActionTest.test_attack__success__kill">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_turnbased.rules.randint&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_attack__success__kill</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
<span class="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">11</span> <span class="c1"># 11 + 1 str will hit beat armor 11</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionAttack</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</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">target</span><span class="o">.</span><span class="n">hp</span><span class="p">,</span> <span class="o">-</span><span class="mi">7</span><span class="p">)</span>
<span class="c1"># after this the combat is over</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertIsNone</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">pk</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatActionTest.test_stunt_fail"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatActionTest.test_stunt_fail">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_turnbased.rules.randint&quot;</span><span class="p">)</span>
<div class="viewcode-block" id="TestCombatActionsBase.test_stunt_fail"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestCombatActionsBase.test_stunt_fail">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_base.rules.randint&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_stunt_fail</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
<span class="n">action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;stunt&quot;</span><span class="p">,</span>
<span class="s2">&quot;recipient&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span>
<span class="s2">&quot;advantage&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;stunt_type&quot;</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">&quot;defense_type&quot;</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="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">8</span> <span class="c1"># fails 8+1 dex vs DEX 11 defence</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionStunt</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</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">combathandler</span><span class="o">.</span><span class="n">advantage_matrix</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">],</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">combathandler</span><span class="o">.</span><span class="n">disadvantage_matrix</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">],</span> <span class="p">{})</span></div>
<span class="n">action</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">CombatActionStunt</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">)</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">give_advantage</span><span class="o">.</span><span class="n">assert_not_called</span><span class="p">()</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatActionTest.test_stunt_advantage__success"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatActionTest.test_stunt_advantage__success">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_turnbased.rules.randint&quot;</span><span class="p">)</span>
<div class="viewcode-block" id="TestCombatActionsBase.test_stunt_advantage__success"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestCombatActionsBase.test_stunt_advantage__success">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_base.rules.randint&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_stunt_advantage__success</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
<span class="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">11</span> <span class="c1"># 11+1 dex vs DEX 11 defence is success</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionStunt</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</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="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">advantage_matrix</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">][</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">]),</span> <span class="kc">True</span>
<span class="p">)</span></div>
<span class="n">action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;stunt&quot;</span><span class="p">,</span>
<span class="s2">&quot;recipient&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span>
<span class="s2">&quot;advantage&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;stunt_type&quot;</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">&quot;defense_type&quot;</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="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">11</span> <span class="c1"># 11+1 dex vs DEX 11 defence is success</span>
<span class="n">action</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">CombatActionStunt</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">)</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">give_advantage</span><span class="o">.</span><span class="n">assert_called_with</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatActionTest.test_stunt_disadvantage__success"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatActionTest.test_stunt_disadvantage__success">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_turnbased.rules.randint&quot;</span><span class="p">)</span>
<div class="viewcode-block" id="TestCombatActionsBase.test_stunt_disadvantage__success"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestCombatActionsBase.test_stunt_disadvantage__success">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_base.rules.randint&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_stunt_disadvantage__success</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
<span class="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">11</span> <span class="c1"># 11+1 dex vs DEX 11 defence is success</span>
<span class="n">action</span> <span class="o">=</span> <span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionStunt</span>
<span class="n">action</span><span class="o">.</span><span class="n">give_advantage</span> <span class="o">=</span> <span class="kc">False</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span>
<span class="n">action</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</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="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">disadvantage_matrix</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">][</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">]),</span> <span class="kc">True</span>
<span class="p">)</span></div>
<span class="n">action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;stunt&quot;</span><span class="p">,</span>
<span class="s2">&quot;recipient&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span>
<span class="s2">&quot;advantage&quot;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span>
<span class="s2">&quot;stunt_type&quot;</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">&quot;defense_type&quot;</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="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">11</span> <span class="c1"># 11+1 dex vs DEX 11 defence is success</span>
<span class="n">action</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">CombatActionStunt</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">)</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">give_disadvantage</span><span class="o">.</span><span class="n">assert_called_with</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatActionTest.test_use_item"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatActionTest.test_use_item">[docs]</a> <span class="k">def</span> <span class="nf">test_use_item</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<div class="viewcode-block" id="TestCombatActionsBase.test_use_item"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestCombatActionsBase.test_use_item">[docs]</a> <span class="k">def</span> <span class="nf">test_use_item</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Use up a potion during combat.</span>
@ -305,13 +283,23 @@
<span class="n">item</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span>
<span class="n">EvAdventureConsumable</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;Healing potion&quot;</span><span class="p">,</span> <span class="n">attributes</span><span class="o">=</span><span class="p">[(</span><span class="s2">&quot;uses&quot;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)]</span>
<span class="p">)</span>
<span class="n">item</span><span class="o">.</span><span class="n">use</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<span class="n">action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;use&quot;</span><span class="p">,</span>
<span class="s2">&quot;item&quot;</span><span class="p">:</span> <span class="n">item</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span>
<span class="p">}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">uses</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionUseItem</span><span class="p">,</span> <span class="n">item</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span>
<span class="n">action</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">CombatActionUseItem</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">)</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">uses</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionUseItem</span><span class="p">,</span> <span class="n">item</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">pk</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span> <span class="c1"># deleted, it was used up</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatActionTest.test_swap_wielded_weapon_or_spell"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatActionTest.test_swap_wielded_weapon_or_spell">[docs]</a> <span class="k">def</span> <span class="nf">test_swap_wielded_weapon_or_spell</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<div class="viewcode-block" id="TestCombatActionsBase.test_swap_wielded_weapon_or_spell"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestCombatActionsBase.test_swap_wielded_weapon_or_spell">[docs]</a> <span class="k">def</span> <span class="nf">test_swap_wielded_weapon_or_spell</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> First draw a weapon (from empty fists), then swap that out to another weapon, then</span>
<span class="sd"> swap to a spell rune.</span>
@ -326,66 +314,498 @@
<span class="n">runestone</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRunestone</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;ice rune&quot;</span><span class="p">)</span>
<span class="c1"># check hands are empty</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">combatant</span><span class="o">.</span><span class="n">weapon</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="s2">&quot;Empty Fists&quot;</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">combatant</span><span class="o">.</span><span class="n">weapon</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="s2">&quot;Bare hands&quot;</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">combatant</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="kc">None</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">combatant</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="kc">None</span><span class="p">)</span>
<span class="c1"># swap to sword</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionSwapWieldedWeaponOrSpell</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="n">sword</span><span class="p">)</span>
<span class="n">actiondict</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;wield&quot;</span><span class="p">,</span> <span class="s2">&quot;item&quot;</span><span class="p">:</span> <span class="n">sword</span><span class="p">}</span>
<span class="n">action</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">CombatActionWield</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">actiondict</span><span class="p">)</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</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">combatant</span><span class="o">.</span><span class="n">weapon</span><span class="p">,</span> <span class="n">sword</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">combatant</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="n">sword</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">combatant</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="kc">None</span><span class="p">)</span>
<span class="c1"># swap to zweihander (two-handed sword)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionSwapWieldedWeaponOrSpell</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="n">zweihander</span><span class="p">)</span>
<span class="n">actiondict</span><span class="p">[</span><span class="s2">&quot;item&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">zweihander</span>
<span class="n">action</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">CombatActionWield</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">actiondict</span><span class="p">)</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</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">combatant</span><span class="o">.</span><span class="n">weapon</span><span class="p">,</span> <span class="n">zweihander</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">combatant</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="kc">None</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">combatant</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">zweihander</span><span class="p">)</span>
<span class="c1"># swap to runestone (also using two hands)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionSwapWieldedWeaponOrSpell</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="n">runestone</span><span class="p">)</span>
<span class="n">actiondict</span><span class="p">[</span><span class="s2">&quot;item&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">runestone</span>
<span class="n">action</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">CombatActionWield</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">actiondict</span><span class="p">)</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</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">combatant</span><span class="o">.</span><span class="n">weapon</span><span class="p">,</span> <span class="n">runestone</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">combatant</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="kc">None</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">combatant</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">runestone</span><span class="p">)</span>
<span class="c1"># swap back to normal one-handed sword</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionSwapWieldedWeaponOrSpell</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="n">sword</span><span class="p">)</span>
<span class="n">actiondict</span><span class="p">[</span><span class="s2">&quot;item&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">sword</span>
<span class="n">action</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">CombatActionWield</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">actiondict</span><span class="p">)</span>
<span class="n">action</span><span class="o">.</span><span class="n">execute</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">combatant</span><span class="o">.</span><span class="n">weapon</span><span class="p">,</span> <span class="n">sword</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">combatant</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="n">sword</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">combatant</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="kc">None</span><span class="p">)</span></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">combatant</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="kc">None</span><span class="p">)</span></div></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatActionTest.test_flee__success"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatActionTest.test_flee__success">[docs]</a> <span class="k">def</span> <span class="nf">test_flee__success</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest">[docs]</a><span class="k">class</span> <span class="nc">EvAdventureTurnbasedCombatHandlerTest</span><span class="p">(</span><span class="n">_CombatTestBase</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Test methods on the turn-based combat handler and actions</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">maxDiff</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># make sure to mock away all time-keeping elements</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.setUp"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.setUp">[docs]</a> <span class="nd">@patch</span><span class="p">(</span>
<span class="p">(</span>
<span class="s2">&quot;evennia.contrib.tutorials.evadventure.&quot;</span>
<span class="s2">&quot;combat_turnbased.EvAdventureTurnbasedCombatHandler.interval&quot;</span>
<span class="p">),</span>
<span class="n">new</span><span class="o">=-</span><span class="mi">1</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
<span class="c1"># add target to combat</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">combat_turnbased</span><span class="o">.</span><span class="n">EvAdventureTurnbasedCombatHandler</span><span class="o">.</span><span class="n">get_or_create_combathandler</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;combathandler&quot;</span>
<span class="p">)</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">add_combatant</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">add_combatant</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)</span></div>
<span class="k">def</span> <span class="nf">_get_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">action_dict</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">}):</span>
<span class="n">action_class</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">action_classes</span><span class="p">[</span><span class="n">action_dict</span><span class="p">[</span><span class="s2">&quot;key&quot;</span><span class="p">]]</span>
<span class="k">return</span> <span class="n">action_class</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_run_actions</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">,</span> <span class="n">action_dict2</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">},</span> <span class="n">combatant_msg</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">target_msg</span><span class="o">=</span><span class="kc">None</span>
<span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Helper method to run an action and check so combatant saw the expected message.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span> <span class="n">action_dict2</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">at_repeat</span><span class="p">()</span>
<span class="k">if</span> <span class="n">combatant_msg</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="c1"># this works because we mock combatant.msg in SetUp</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="o">.</span><span class="n">msg</span><span class="o">.</span><span class="n">assert_called_with</span><span class="p">(</span><span class="n">combatant_msg</span><span class="p">)</span>
<span class="k">if</span> <span class="n">target_msg</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="c1"># this works because we mock target.msg in SetUp</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="o">.</span><span class="n">msg</span><span class="o">.</span><span class="n">assert_called_with</span><span class="p">(</span><span class="n">target_msg</span><span class="p">)</span>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_combatanthandler_setup"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_combatanthandler_setup">[docs]</a> <span class="k">def</span> <span class="nf">test_combatanthandler_setup</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Testing all is set up correctly in the combathandler&quot;&quot;&quot;</span>
<span class="n">chandler</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span>
<span class="nb">dict</span><span class="p">(</span><span class="n">chandler</span><span class="o">.</span><span class="n">combatants</span><span class="p">),</span>
<span class="p">{</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">:</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">},</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">:</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">}},</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="nb">dict</span><span class="p">(</span><span class="n">chandler</span><span class="o">.</span><span class="n">action_classes</span><span class="p">),</span>
<span class="p">{</span>
<span class="s2">&quot;hold&quot;</span><span class="p">:</span> <span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionHold</span><span class="p">,</span>
<span class="s2">&quot;attack&quot;</span><span class="p">:</span> <span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionAttack</span><span class="p">,</span>
<span class="s2">&quot;stunt&quot;</span><span class="p">:</span> <span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionStunt</span><span class="p">,</span>
<span class="s2">&quot;use&quot;</span><span class="p">:</span> <span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionUseItem</span><span class="p">,</span>
<span class="s2">&quot;wield&quot;</span><span class="p">:</span> <span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionWield</span><span class="p">,</span>
<span class="s2">&quot;flee&quot;</span><span class="p">:</span> <span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionFlee</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">chandler</span><span class="o">.</span><span class="n">flee_timeout</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="nb">dict</span><span class="p">(</span><span class="n">chandler</span><span class="o">.</span><span class="n">advantage_matrix</span><span class="p">),</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="nb">dict</span><span class="p">(</span><span class="n">chandler</span><span class="o">.</span><span class="n">disadvantage_matrix</span><span class="p">),</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="nb">dict</span><span class="p">(</span><span class="n">chandler</span><span class="o">.</span><span class="n">fleeing_combatants</span><span class="p">),</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="nb">dict</span><span class="p">(</span><span class="n">chandler</span><span class="o">.</span><span class="n">defeated_combatants</span><span class="p">),</span> <span class="p">{})</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_remove_combatant"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_remove_combatant">[docs]</a> <span class="k">def</span> <span class="nf">test_remove_combatant</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Remove a combatant.&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">remove_combatant</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</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="nb">dict</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">combatants</span><span class="p">),</span> <span class="p">{</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">:</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">}})</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_stop_combat"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_stop_combat">[docs]</a> <span class="k">def</span> <span class="nf">test_stop_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Stopping combat, making sure combathandler is deleted.&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">stop_combat</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertIsNone</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">pk</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_get_sides"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_get_sides">[docs]</a> <span class="k">def</span> <span class="nf">test_get_sides</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Getting the sides of combat&quot;&quot;&quot;</span>
<span class="n">combatant2</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span>
<span class="n">EvAdventureCharacter</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;testchar2&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">location</span>
<span class="p">)</span>
<span class="n">target2</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span>
<span class="n">EvAdventureMob</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;testmonster2&quot;</span><span class="p">,</span>
<span class="n">location</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="p">,</span>
<span class="n">attributes</span><span class="o">=</span><span class="p">((</span><span class="s2">&quot;is_idle&quot;</span><span class="p">,</span> <span class="kc">True</span><span class="p">),),</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">add_combatant</span><span class="p">(</span><span class="n">combatant2</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">add_combatant</span><span class="p">(</span><span class="n">target2</span><span class="p">)</span>
<span class="c1"># allies to combatant</span>
<span class="n">allies</span><span class="p">,</span> <span class="n">enemies</span> <span class="o">=</span> <span class="bp">self</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="o">.</span><span class="n">combatant</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="n">allies</span><span class="p">,</span> <span class="n">enemies</span><span class="p">),</span> <span class="p">([</span><span class="n">combatant2</span><span class="p">],</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span> <span class="n">target2</span><span class="p">]))</span>
<span class="c1"># allies to monster</span>
<span class="n">allies</span><span class="p">,</span> <span class="n">enemies</span> <span class="o">=</span> <span class="bp">self</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="o">.</span><span class="n">target</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="n">allies</span><span class="p">,</span> <span class="n">enemies</span><span class="p">),</span> <span class="p">([</span><span class="n">target2</span><span class="p">],</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">combatant2</span><span class="p">]))</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_queue_and_execute_action"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_queue_and_execute_action">[docs]</a> <span class="k">def</span> <span class="nf">test_queue_and_execute_action</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Queue actions and execute&quot;&quot;&quot;</span>
<span class="n">hold</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">hold</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="nb">dict</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">combatants</span><span class="p">),</span>
<span class="p">{</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">:</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">},</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">:</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">}},</span>
<span class="p">)</span>
<span class="n">mock_action</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">action_classes</span><span class="p">[</span><span class="s2">&quot;hold&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="n">mock_action</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">execute_next_action</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">action_classes</span><span class="p">[</span><span class="s2">&quot;hold&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">assert_called_with</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">hold</span>
<span class="p">)</span>
<span class="n">mock_action</span><span class="o">.</span><span class="n">execute</span><span class="o">.</span><span class="n">assert_called_once</span><span class="p">()</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_execute_full_turn"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_execute_full_turn">[docs]</a> <span class="k">def</span> <span class="nf">test_execute_full_turn</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Run a full (passive) turn&quot;&quot;&quot;</span>
<span class="n">hold</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">hold</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span> <span class="n">hold</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">execute_next_action</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">at_repeat</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">execute_next_action</span><span class="o">.</span><span class="n">assert_has_calls</span><span class="p">(</span>
<span class="p">[</span><span class="n">call</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">),</span> <span class="n">call</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)],</span> <span class="n">any_order</span><span class="o">=</span><span class="kc">True</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_action__action_ticks_turn"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_action__action_ticks_turn">[docs]</a> <span class="k">def</span> <span class="nf">test_action__action_ticks_turn</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Test that action execution ticks turns&quot;&quot;&quot;</span>
<span class="n">actiondict</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_actions</span><span class="p">(</span><span class="n">actiondict</span><span class="p">,</span> <span class="n">actiondict</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">combathandler</span><span class="o">.</span><span class="n">turn</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="o">.</span><span class="n">msg</span><span class="o">.</span><span class="n">assert_not_called</span><span class="p">()</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_attack__success__kill"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_attack__success__kill">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_base.rules.randint&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_attack__success__kill</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Test that the combathandler is deleted once there are no more enemies&quot;&quot;&quot;</span>
<span class="n">actiondict</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;attack&quot;</span><span class="p">,</span> <span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">}</span>
<span class="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">11</span> <span class="c1"># 11 + 1 str will hit beat armor 11</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_actions</span><span class="p">(</span><span class="n">actiondict</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">target</span><span class="o">.</span><span class="n">hp</span><span class="p">,</span> <span class="o">-</span><span class="mi">7</span><span class="p">)</span>
<span class="c1"># after this the combat is over</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertIsNone</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">pk</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_stunt_fail"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_stunt_fail">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_base.rules.randint&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_stunt_fail</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
<span class="n">action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;stunt&quot;</span><span class="p">,</span>
<span class="s2">&quot;recipient&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span>
<span class="s2">&quot;advantage&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;stunt_type&quot;</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">&quot;defense_type&quot;</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="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">8</span> <span class="c1"># fails 8+1 dex vs DEX 11 defence</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_actions</span><span class="p">(</span><span class="n">action_dict</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">combathandler</span><span class="o">.</span><span class="n">advantage_matrix</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">],</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">combathandler</span><span class="o">.</span><span class="n">disadvantage_matrix</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">],</span> <span class="p">{})</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_stunt_advantage__success"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_stunt_advantage__success">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_base.rules.randint&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_stunt_advantage__success</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Test so the advantage matrix is updated correctly&quot;&quot;&quot;</span>
<span class="n">action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;stunt&quot;</span><span class="p">,</span>
<span class="s2">&quot;recipient&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span>
<span class="s2">&quot;advantage&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;stunt_type&quot;</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">&quot;defense_type&quot;</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="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">11</span> <span class="c1"># 11+1 dex vs DEX 11 defence is success</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_actions</span><span class="p">(</span><span class="n">action_dict</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="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">advantage_matrix</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">][</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">]),</span> <span class="kc">True</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_stunt_disadvantage__success"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_stunt_disadvantage__success">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_base.rules.randint&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_stunt_disadvantage__success</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Test so the disadvantage matrix is updated correctly&quot;&quot;&quot;</span>
<span class="n">action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;stunt&quot;</span><span class="p">,</span>
<span class="s2">&quot;recipient&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span>
<span class="s2">&quot;advantage&quot;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span>
<span class="s2">&quot;stunt_type&quot;</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">&quot;defense_type&quot;</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="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">11</span> <span class="c1"># 11+1 dex vs DEX 11 defence is success</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_actions</span><span class="p">(</span><span class="n">action_dict</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="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">disadvantage_matrix</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">][</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">]),</span> <span class="kc">True</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatHandlerTest.test_flee__success"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatHandlerTest.test_flee__success">[docs]</a> <span class="k">def</span> <span class="nf">test_flee__success</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Test fleeing twice, leading to leaving combat.</span>
<span class="sd"> &quot;&quot;&quot;</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">combathandler</span><span class="o">.</span><span class="n">turn</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">action_dict</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;flee&quot;</span><span class="p">,</span> <span class="s2">&quot;repeat&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">}</span>
<span class="c1"># first flee records the fleeing state</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionFlee</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">fleeing_combatants</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">flee_timeout</span> <span class="o">=</span> <span class="mi">2</span> <span class="c1"># to make sure</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_actions</span><span class="p">(</span><span class="n">action_dict</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">combathandler</span><span class="o">.</span><span class="n">turn</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">fleeing_combatants</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">],</span> <span class="mi">1</span><span class="p">)</span>
<span class="c1"># action_dict should still be in place due to repeat</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">combathandler</span><span class="o">.</span><span class="n">combatants</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">],</span> <span class="n">action_dict</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="o">.</span><span class="n">msg</span><span class="o">.</span><span class="n">assert_called_with</span><span class="p">(</span>
<span class="n">text</span><span class="o">=</span><span class="p">(</span>
<span class="s2">&quot;You retreat, being exposed to attack while doing so (will escape in 1 turn).&quot;</span><span class="p">,</span>
<span class="p">{},</span>
<span class="p">),</span>
<span class="n">from_obj</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Check that enemies have advantage against you now</span>
<span class="n">action</span> <span class="o">=</span> <span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatAction</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">})</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="n">action</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">))</span>
<span class="c1"># second flee should remove combatant</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionFlee</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertIsNone</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">pk</span><span class="p">)</span></div>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_actions</span><span class="p">(</span><span class="n">action_dict</span><span class="p">)</span>
<span class="c1"># this ends combat, so combathandler should be gone</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertIsNone</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">pk</span><span class="p">)</span></div></div>
<div class="viewcode-block" id="EvAdventureTurnbasedCombatActionTest.test_flee__blocked"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.EvAdventureTurnbasedCombatActionTest.test_flee__blocked">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_turnbased.rules.randint&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_flee__blocked</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot; &quot;&quot;&quot;</span>
<span class="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">11</span> <span class="c1"># means block will succeed</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_run_action</span><span class="p">(</span><span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionFlee</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">fleeing_combatants</span><span class="p">)</span>
<div class="viewcode-block" id="TestEvAdventureTwitchCombatHandler"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureTwitchCombatHandler">[docs]</a><span class="k">class</span> <span class="nc">TestEvAdventureTwitchCombatHandler</span><span class="p">(</span><span class="n">EvenniaCommandTestMixin</span><span class="p">,</span> <span class="n">_CombatTestBase</span><span class="p">):</span>
<div class="viewcode-block" id="TestEvAdventureTwitchCombatHandler.setUp"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureTwitchCombatHandler.setUp">[docs]</a> <span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
<span class="c1"># other combatant blocks in the same turn</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">register_action</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionFlee</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="kc">None</span>
<span class="c1"># in order to use the EvenniaCommandTestMixin we need these variables defined</span>
<span class="bp">self</span><span class="o">.</span><span class="n">char1</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="bp">self</span><span class="o">.</span><span class="n">account</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">combat_twitch</span><span class="o">.</span><span class="n">EvAdventureCombatTwitchHandler</span><span class="o">.</span><span class="n">get_or_create_combathandler</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;combathandler&quot;</span>
<span class="p">)</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">register_action</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span> <span class="n">combat_turnbased</span><span class="o">.</span><span class="n">CombatActionBlock</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target_combathandler</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">combat_twitch</span><span class="o">.</span><span class="n">EvAdventureCombatTwitchHandler</span><span class="o">.</span><span class="n">get_or_create_combathandler</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;combathandler&quot;</span>
<span class="p">)</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="TestEvAdventureTwitchCombatHandler.test_get_sides"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureTwitchCombatHandler.test_get_sides">[docs]</a> <span class="k">def</span> <span class="nf">test_get_sides</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">sides</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">get_sides</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">sides</span><span class="p">,</span> <span class="p">([],</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">]))</span></div>
<div class="viewcode-block" id="TestEvAdventureTwitchCombatHandler.test_give_advantage"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureTwitchCombatHandler.test_give_advantage">[docs]</a> <span class="k">def</span> <span class="nf">test_give_advantage</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">combatant_combathandler</span><span class="o">.</span><span class="n">give_advantage</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">advantage_against</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">])</span></div>
<div class="viewcode-block" id="TestEvAdventureTwitchCombatHandler.test_give_disadvantage"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureTwitchCombatHandler.test_give_disadvantage">[docs]</a> <span class="k">def</span> <span class="nf">test_give_disadvantage</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">combatant_combathandler</span><span class="o">.</span><span class="n">give_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">disadvantage_against</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">])</span></div>
<div class="viewcode-block" id="TestEvAdventureTwitchCombatHandler.test_queue_action"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureTwitchCombatHandler.test_queue_action">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.unrepeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.repeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="mi">999</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">test_queue_action</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Test so the queue action cleans up tickerhandler correctly&quot;&quot;&quot;</span>
<span class="n">actiondict</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span><span class="n">actiondict</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertIsNone</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">current_ticker_ref</span><span class="p">)</span>
<span class="n">actiondict</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">,</span> <span class="s2">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">5</span><span class="p">}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">queue_action</span><span class="p">(</span><span class="n">actiondict</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">combatant_combathandler</span><span class="o">.</span><span class="n">current_ticker_ref</span><span class="p">,</span> <span class="mi">999</span><span class="p">)</span></div>
<div class="viewcode-block" id="TestEvAdventureTwitchCombatHandler.test_execute_next_action"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureTwitchCombatHandler.test_execute_next_action">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.unrepeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.repeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="k">def</span> <span class="nf">test_execute_next_action</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">combatant_combathandler</span><span class="o">.</span><span class="n">action_dict</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">,</span>
<span class="s2">&quot;dummy&quot;</span><span class="p">:</span> <span class="s2">&quot;foo&quot;</span><span class="p">,</span>
<span class="s2">&quot;repeat&quot;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span>
<span class="p">}</span> <span class="c1"># to separate from fallback</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">execute_next_action</span><span class="p">()</span>
<span class="c1"># should now be back to fallback</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">combatant_combathandler</span><span class="o">.</span><span class="n">action_dict</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">fallback_action_dict</span><span class="p">,</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="TestEvAdventureTwitchCombatHandler.test_check_stop_combat"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureTwitchCombatHandler.test_check_stop_combat">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.unrepeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="k">def</span> <span class="nf">test_check_stop_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Test combat-stop functionality&quot;&quot;&quot;</span>
<span class="c1"># noone remains (both combatant/target &lt;0 hp</span>
<span class="c1"># get_sides does not include the caller</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">get_sides</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="p">([],</span> <span class="p">[]))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">stop_combat</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">check_stop_combat</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="o">.</span><span class="n">msg</span><span class="o">.</span><span class="n">assert_called_with</span><span class="p">(</span>
<span class="n">text</span><span class="o">=</span><span class="p">(</span><span class="s2">&quot;Noone stands after the dust settles.&quot;</span><span class="p">,</span> <span class="p">{}),</span> <span class="n">from_obj</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">_end_turn</span><span class="p">()</span>
<span class="c1"># the fleeing combatant should remain now</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">fleeing_combatants</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertTrue</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">combatants</span><span class="p">)</span></div></div>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">stop_combat</span><span class="o">.</span><span class="n">assert_called</span><span class="p">()</span>
<span class="c1"># only one side wiped out</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="mi">10</span>
<span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">get_sides</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="p">([],</span> <span class="p">[]))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant_combathandler</span><span class="o">.</span><span class="n">check_stop_combat</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="o">.</span><span class="n">msg</span><span class="o">.</span><span class="n">assert_called_with</span><span class="p">(</span>
<span class="n">text</span><span class="o">=</span><span class="p">(</span><span class="s2">&quot;The combat is over.&quot;</span><span class="p">,</span> <span class="p">{}),</span> <span class="n">from_obj</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="TestEvAdventureTwitchCombatHandler.test_hold"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureTwitchCombatHandler.test_hold">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.unrepeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.repeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="k">def</span> <span class="nf">test_hold</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">call</span><span class="p">(</span><span class="n">combat_twitch</span><span class="o">.</span><span class="n">CmdHold</span><span class="p">(),</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="s2">&quot;You hold back, doing nothing&quot;</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">combatant_combathandler</span><span class="o">.</span><span class="n">action_dict</span><span class="p">,</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;hold&quot;</span><span class="p">})</span></div>
<div class="viewcode-block" id="TestEvAdventureTwitchCombatHandler.test_attack"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureTwitchCombatHandler.test_attack">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.unrepeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.repeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="k">def</span> <span class="nf">test_attack</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Test attack action in the twitch combathandler&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">combat_twitch</span><span class="o">.</span><span class="n">CmdAttack</span><span class="p">(),</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="s2">&quot;You attack testmonster!&quot;</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">combatant_combathandler</span><span class="o">.</span><span class="n">action_dict</span><span class="p">,</span>
<span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;attack&quot;</span><span class="p">,</span> <span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span> <span class="s2">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="s2">&quot;repeat&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">},</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="TestEvAdventureTwitchCombatHandler.test_stunt"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureTwitchCombatHandler.test_stunt">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.unrepeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.repeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="k">def</span> <span class="nf">test_stunt</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">boost_result</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;stunt&quot;</span><span class="p">,</span>
<span class="s2">&quot;recipient&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span>
<span class="s2">&quot;advantage&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;stunt_type&quot;</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">&quot;defense_type&quot;</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">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
<span class="p">}</span>
<span class="n">foil_result</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;stunt&quot;</span><span class="p">,</span>
<span class="s2">&quot;recipient&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span>
<span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span>
<span class="s2">&quot;advantage&quot;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span>
<span class="s2">&quot;stunt_type&quot;</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">&quot;defense_type&quot;</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">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
<span class="p">}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">(</span>
<span class="n">combat_twitch</span><span class="o">.</span><span class="n">CmdStunt</span><span class="p">(),</span>
<span class="sa">f</span><span class="s2">&quot;STR </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">,</span>
<span class="s2">&quot;You prepare a stunt!&quot;</span><span class="p">,</span>
<span class="n">cmdstring</span><span class="o">=</span><span class="s2">&quot;boost&quot;</span><span class="p">,</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">combatant_combathandler</span><span class="o">.</span><span class="n">action_dict</span><span class="p">,</span> <span class="n">boost_result</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">(</span>
<span class="n">combat_twitch</span><span class="o">.</span><span class="n">CmdStunt</span><span class="p">(),</span>
<span class="sa">f</span><span class="s2">&quot;STR me </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">,</span>
<span class="s2">&quot;You prepare a stunt!&quot;</span><span class="p">,</span>
<span class="n">cmdstring</span><span class="o">=</span><span class="s2">&quot;boost&quot;</span><span class="p">,</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">combatant_combathandler</span><span class="o">.</span><span class="n">action_dict</span><span class="p">,</span> <span class="n">boost_result</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">(</span>
<span class="n">combat_twitch</span><span class="o">.</span><span class="n">CmdStunt</span><span class="p">(),</span>
<span class="sa">f</span><span class="s2">&quot;STR </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">,</span>
<span class="s2">&quot;You prepare a stunt!&quot;</span><span class="p">,</span>
<span class="n">cmdstring</span><span class="o">=</span><span class="s2">&quot;foil&quot;</span><span class="p">,</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">combatant_combathandler</span><span class="o">.</span><span class="n">action_dict</span><span class="p">,</span> <span class="n">foil_result</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">(</span>
<span class="n">combat_twitch</span><span class="o">.</span><span class="n">CmdStunt</span><span class="p">(),</span>
<span class="sa">f</span><span class="s2">&quot;STR </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> me&quot;</span><span class="p">,</span>
<span class="s2">&quot;You prepare a stunt!&quot;</span><span class="p">,</span>
<span class="n">cmdstring</span><span class="o">=</span><span class="s2">&quot;foil&quot;</span><span class="p">,</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">combatant_combathandler</span><span class="o">.</span><span class="n">action_dict</span><span class="p">,</span> <span class="n">foil_result</span><span class="p">)</span></div>
<div class="viewcode-block" id="TestEvAdventureTwitchCombatHandler.test_useitem"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureTwitchCombatHandler.test_useitem">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.unrepeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.repeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="k">def</span> <span class="nf">test_useitem</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">item</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span>
<span class="n">EvAdventureConsumable</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;potion&quot;</span><span class="p">,</span> <span class="n">attributes</span><span class="o">=</span><span class="p">[(</span><span class="s2">&quot;uses&quot;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)],</span> <span class="n">location</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">combat_twitch</span><span class="o">.</span><span class="n">CmdUseItem</span><span class="p">(),</span> <span class="s2">&quot;potion&quot;</span><span class="p">,</span> <span class="s2">&quot;You prepare to use potion!&quot;</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">combatant_combathandler</span><span class="o">.</span><span class="n">action_dict</span><span class="p">,</span>
<span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;use&quot;</span><span class="p">,</span> <span class="s2">&quot;item&quot;</span><span class="p">:</span> <span class="n">item</span><span class="p">,</span> <span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="s2">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">},</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">(</span>
<span class="n">combat_twitch</span><span class="o">.</span><span class="n">CmdUseItem</span><span class="p">(),</span>
<span class="sa">f</span><span class="s2">&quot;potion on </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">,</span>
<span class="s2">&quot;You prepare to use potion!&quot;</span><span class="p">,</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">combatant_combathandler</span><span class="o">.</span><span class="n">action_dict</span><span class="p">,</span>
<span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;use&quot;</span><span class="p">,</span> <span class="s2">&quot;item&quot;</span><span class="p">:</span> <span class="n">item</span><span class="p">,</span> <span class="s2">&quot;target&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">,</span> <span class="s2">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">},</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="TestEvAdventureTwitchCombatHandler.test_wield"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia.contrib.tutorials.evadventure.tests.test_combat.TestEvAdventureTwitchCombatHandler.test_wield">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.unrepeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.combat_twitch.repeat&quot;</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="n">Mock</span><span class="p">())</span>
<span class="k">def</span> <span class="nf">test_wield</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">sword</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureWeapon</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;sword&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">)</span>
<span class="n">runestone</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span>
<span class="n">EvAdventureWeapon</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;runestone&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">combat_twitch</span><span class="o">.</span><span class="n">CmdWield</span><span class="p">(),</span> <span class="s2">&quot;sword&quot;</span><span class="p">,</span> <span class="s2">&quot;You reach for sword!&quot;</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">combatant_combathandler</span><span class="o">.</span><span class="n">action_dict</span><span class="p">,</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;wield&quot;</span><span class="p">,</span> <span class="s2">&quot;item&quot;</span><span class="p">:</span> <span class="n">sword</span><span class="p">,</span> <span class="s2">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">}</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">combat_twitch</span><span class="o">.</span><span class="n">CmdWield</span><span class="p">(),</span> <span class="s2">&quot;runestone&quot;</span><span class="p">,</span> <span class="s2">&quot;You reach for runestone!&quot;</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">combatant_combathandler</span><span class="o">.</span><span class="n">action_dict</span><span class="p">,</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;wield&quot;</span><span class="p">,</span> <span class="s2">&quot;item&quot;</span><span class="p">:</span> <span class="n">runestone</span><span class="p">,</span> <span class="s2">&quot;dt&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">}</span>
<span class="p">)</span></div></div>
</pre></div>
</div>

View file

@ -84,7 +84,6 @@
<span class="kn">from</span> <span class="nn">unittest.mock</span> <span class="kn">import</span> <span class="n">call</span><span class="p">,</span> <span class="n">patch</span>
<span class="kn">from</span> <span class="nn">anything</span> <span class="kn">import</span> <span class="n">Something</span>
<span class="kn">from</span> <span class="nn">evennia.utils.create</span> <span class="kn">import</span> <span class="n">create_object</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">BaseEvenniaCommandTest</span>
@ -112,18 +111,6 @@
<span class="sd">&quot;&quot;&quot;</span><span class="o">.</span><span class="n">strip</span><span class="p">(),</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="TestEvAdventureCommands.test_attack"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_commands.html#evennia.contrib.tutorials.evadventure.tests.test_commands.TestEvAdventureCommands.test_attack">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.commands.join_combat&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_attack</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_join_combat</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">allow_combat</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">target</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureMob</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;Ogre&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">commands</span><span class="o">.</span><span class="n">CmdAttackTurnBased</span><span class="p">(),</span> <span class="s2">&quot;ogre&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">mock_join_combat</span><span class="o">.</span><span class="n">assert_called_with</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">char1</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="n">Something</span><span class="p">)</span>
<span class="n">target</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span></div>
<div class="viewcode-block" id="TestEvAdventureCommands.test_wield_or_wear"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_commands.html#evennia.contrib.tutorials.evadventure.tests.test_commands.TestEvAdventureCommands.test_wield_or_wear">[docs]</a> <span class="k">def</span> <span class="nf">test_wield_or_wear</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">char1</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">char1</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">weapon</span><span class="p">)</span>
@ -163,7 +150,6 @@
<div class="viewcode-block" id="TestEvAdventureCommands.test_give__item"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_commands.html#evennia.contrib.tutorials.evadventure.tests.test_commands.TestEvAdventureCommands.test_give__item">[docs]</a> <span class="nd">@patch</span><span class="p">(</span><span class="s2">&quot;evennia.contrib.tutorials.evadventure.commands.EvMenu&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_give__item</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_EvMenu</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">char1</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">)</span>
<span class="n">recipient</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;Friend&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="p">)</span>

View file

@ -0,0 +1,129 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>evennia.contrib.tutorials.evadventure.tests.test_npcs &#8212; Evennia 1.0 documentation</title>
<link rel="stylesheet" href="../../../../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../../../../" src="../../../../../../_static/documentation_options.js"></script>
<script src="../../../../../../_static/jquery.js"></script>
<script src="../../../../../../_static/underscore.js"></script>
<script src="../../../../../../_static/doctools.js"></script>
<script src="../../../../../../_static/language_data.js"></script>
<link rel="shortcut icon" href="../../../../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../../../../genindex.html" />
<link rel="search" title="Search" href="../../../../../../search.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../../../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../../../../evennia.html" accesskey="U">evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.contrib.tutorials.evadventure.tests.test_npcs</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="../../../../../../index.html">
<img class="logo" src="../../../../../../_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../../../../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="test_npcs.html">1.0 (main branch)</a></li>
<ul>
<li><a href="../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<h1>Source code for evennia.contrib.tutorials.evadventure.tests.test_npcs</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd">Test NPC classes.</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_object</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTest</span>
<span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">npcs</span>
<div class="viewcode-block" id="TestNPCBase"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_npcs.html#evennia.contrib.tutorials.evadventure.tests.test_npcs.TestNPCBase">[docs]</a><span class="k">class</span> <span class="nc">TestNPCBase</span><span class="p">(</span><span class="n">EvenniaTest</span><span class="p">):</span>
<div class="viewcode-block" id="TestNPCBase.test_npc_base"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_npcs.html#evennia.contrib.tutorials.evadventure.tests.test_npcs.TestNPCBase.test_npc_base">[docs]</a> <span class="k">def</span> <span class="nf">test_npc_base</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">npc</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span>
<span class="n">npcs</span><span class="o">.</span><span class="n">EvAdventureNPC</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;TestNPC&quot;</span><span class="p">,</span>
<span class="n">attributes</span><span class="o">=</span><span class="p">[(</span><span class="s2">&quot;hit_dice&quot;</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span> <span class="p">(</span><span class="s2">&quot;armor&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="s2">&quot;morale&quot;</span><span class="p">,</span> <span class="mi">9</span><span class="p">)],</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">hp_multiplier</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">hp_max</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">strength</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">charisma</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span></div></div>
</pre></div>
</div>
</div>
</div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../../../../genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="../../../../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../../../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../../../../evennia.html" >evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.contrib.tutorials.evadventure.tests.test_npcs</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -0,0 +1,160 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>evennia.contrib.tutorials.evadventure.tests.test_rooms &#8212; Evennia 1.0 documentation</title>
<link rel="stylesheet" href="../../../../../../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../../../../../../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../../../../" src="../../../../../../_static/documentation_options.js"></script>
<script src="../../../../../../_static/jquery.js"></script>
<script src="../../../../../../_static/underscore.js"></script>
<script src="../../../../../../_static/doctools.js"></script>
<script src="../../../../../../_static/language_data.js"></script>
<link rel="shortcut icon" href="../../../../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../../../../genindex.html" />
<link rel="search" title="Search" href="../../../../../../search.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../../../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../../../../evennia.html" accesskey="U">evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.contrib.tutorials.evadventure.tests.test_rooms</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="../../../../../../index.html">
<img class="logo" src="../../../../../../_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../../../../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="test_rooms.html">1.0 (main branch)</a></li>
<ul>
<li><a href="../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<h1>Source code for evennia.contrib.tutorials.evadventure.tests.test_rooms</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd">Test of EvAdventure Rooms</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultExit</span><span class="p">,</span> <span class="n">create_object</span>
<span class="kn">from</span> <span class="nn">evennia.utils.ansi</span> <span class="kn">import</span> <span class="n">strip_ansi</span>
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTestCase</span>
<span class="kn">from</span> <span class="nn">..characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
<span class="kn">from</span> <span class="nn">..rooms</span> <span class="kn">import</span> <span class="n">EvAdventureRoom</span>
<div class="viewcode-block" id="EvAdventureRoomTest"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_rooms.html#evennia.contrib.tutorials.evadventure.tests.test_rooms.EvAdventureRoomTest">[docs]</a><span class="k">class</span> <span class="nc">EvAdventureRoomTest</span><span class="p">(</span><span class="n">EvenniaTestCase</span><span class="p">):</span>
<div class="viewcode-block" id="EvAdventureRoomTest.setUp"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_rooms.html#evennia.contrib.tutorials.evadventure.tests.test_rooms.EvAdventureRoomTest.setUp">[docs]</a> <span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">char</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;TestChar&quot;</span><span class="p">)</span></div>
<div class="viewcode-block" id="EvAdventureRoomTest.test_map"><a class="viewcode-back" href="../../../../../../api/evennia.contrib.tutorials.evadventure.tests.test_rooms.html#evennia.contrib.tutorials.evadventure.tests.test_rooms.EvAdventureRoomTest.test_map">[docs]</a> <span class="k">def</span> <span class="nf">test_map</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">center_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room_center&quot;</span><span class="p">)</span>
<span class="n">n_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room_n&quot;</span><span class="p">)</span>
<span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;north&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">n_room</span><span class="p">)</span>
<span class="n">ne_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room_ne&quot;</span><span class="p">)</span>
<span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;northeast&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">ne_room</span><span class="p">)</span>
<span class="n">e_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room_e&quot;</span><span class="p">)</span>
<span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;east&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">e_room</span><span class="p">)</span>
<span class="n">se_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room_se&quot;</span><span class="p">)</span>
<span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;southeast&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">se_room</span><span class="p">)</span>
<span class="n">s_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room_&quot;</span><span class="p">)</span>
<span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;south&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">s_room</span><span class="p">)</span>
<span class="n">sw_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room_sw&quot;</span><span class="p">)</span>
<span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;southwest&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">sw_room</span><span class="p">)</span>
<span class="n">w_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room_w&quot;</span><span class="p">)</span>
<span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;west&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">w_room</span><span class="p">)</span>
<span class="n">nw_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;room_nw&quot;</span><span class="p">)</span>
<span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">&quot;northwest&quot;</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">nw_room</span><span class="p">)</span>
<span class="n">desc</span> <span class="o">=</span> <span class="n">center_room</span><span class="o">.</span><span class="n">return_appearance</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">char</span><span class="p">)</span>
<span class="n">expected</span> <span class="o">=</span> <span class="sa">r</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> o o o</span>
<span class="s2"> \|/</span>
<span class="s2"> o-@-o</span>
<span class="s2"> /|\</span>
<span class="s2"> o o o</span>
<span class="s2">room_center</span>
<span class="s2">You see nothing special.</span>
<span class="s2">Exits: north, northeast, east, southeast, south, southwest, west, and northwest&quot;&quot;&quot;</span>
<span class="n">result</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">part</span><span class="o">.</span><span class="n">rstrip</span><span class="p">()</span> <span class="k">for</span> <span class="n">part</span> <span class="ow">in</span> <span class="n">strip_ansi</span><span class="p">(</span><span class="n">desc</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">))</span>
<span class="n">expected</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">part</span><span class="o">.</span><span class="n">rstrip</span><span class="p">()</span> <span class="k">for</span> <span class="n">part</span> <span class="ow">in</span> <span class="n">expected</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">))</span>
<span class="c1"># print(result)</span>
<span class="c1"># print(expected)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">expected</span><span class="p">)</span></div></div>
</pre></div>
</div>
</div>
</div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../../../../genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="../../../../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../../../../index.html">Evennia 1.0</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../../../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../../../../evennia.html" >evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.contrib.tutorials.evadventure.tests.test_rooms</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>

View file

@ -84,7 +84,6 @@
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">django.utils.translation</span> <span class="kn">import</span> <span class="n">gettext</span> <span class="k">as</span> <span class="n">_</span>
<span class="kn">from</span> <span class="nn">evennia.scripts.models</span> <span class="kn">import</span> <span class="n">ScriptDB</span>
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">create</span><span class="p">,</span> <span class="n">logger</span>
@ -190,6 +189,19 @@
<span class="n">num</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="n">num</span></div>
<div class="viewcode-block" id="ScriptHandler.has"><a class="viewcode-back" href="../../../api/evennia.scripts.scripthandler.html#evennia.scripts.scripthandler.ScriptHandler.has">[docs]</a> <span class="k">def</span> <span class="nf">has</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Determine if a given script exists on this object.</span>
<span class="sd"> Args:</span>
<span class="sd"> key (str): Search criterion, the script&#39;s key or dbref.</span>
<span class="sd"> Returns:</span>
<span class="sd"> bool: If the script exists or not.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">ScriptDB</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get_all_scripts_on_obj</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="n">key</span><span class="p">)</span><span class="o">.</span><span class="n">exists</span><span class="p">()</span></div>
<div class="viewcode-block" id="ScriptHandler.get"><a class="viewcode-back" href="../../../api/evennia.scripts.scripthandler.html#evennia.scripts.scripthandler.ScriptHandler.get">[docs]</a> <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Search scripts on this object.</span>

View file

@ -84,13 +84,12 @@
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">django.utils.translation</span> <span class="kn">import</span> <span class="n">gettext</span> <span class="k">as</span> <span class="n">_</span>
<span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><span class="p">,</span> <span class="n">maybeDeferred</span>
<span class="kn">from</span> <span class="nn">twisted.internet.task</span> <span class="kn">import</span> <span class="n">LoopingCall</span>
<span class="kn">from</span> <span class="nn">evennia.scripts.manager</span> <span class="kn">import</span> <span class="n">ScriptManager</span>
<span class="kn">from</span> <span class="nn">evennia.scripts.models</span> <span class="kn">import</span> <span class="n">ScriptDB</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">create</span><span class="p">,</span> <span class="n">logger</span>
<span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><span class="p">,</span> <span class="n">maybeDeferred</span>
<span class="kn">from</span> <span class="nn">twisted.internet.task</span> <span class="kn">import</span> <span class="n">LoopingCall</span>
<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;DefaultScript&quot;</span><span class="p">,</span> <span class="s2">&quot;DoNothing&quot;</span><span class="p">,</span> <span class="s2">&quot;Store&quot;</span><span class="p">]</span>

View file

@ -237,18 +237,7 @@
<div class="viewcode-block" id="AttributeProperty"><a class="viewcode-back" href="../../../api/evennia.typeclasses.attributes.html#evennia.typeclasses.attributes.AttributeProperty">[docs]</a><span class="k">class</span> <span class="nc">AttributeProperty</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Attribute property descriptor. Allows for specifying Attributes as Django-like &#39;fields&#39;</span>
<span class="sd"> on the class level. Note that while one can set a lock on the Attribute,</span>
<span class="sd"> there is no way to *check* said lock when accessing via the property - use</span>
<span class="sd"> the full `AttributeHandler` if you need to do access checks. Note however that if you use the</span>
<span class="sd"> full `AttributeHandler` to access this Attribute, the `at_get/at_set` methods on this class will</span>
<span class="sd"> _not_ fire (because you are bypassing the `AttributeProperty` entirely in that case).</span>
<span class="sd"> Example:</span>
<span class="sd"> ::</span>
<span class="sd"> class Character(DefaultCharacter):</span>
<span class="sd"> foo = AttributeProperty(default=&quot;Bar&quot;)</span>
<span class="sd"> AttributeProperty.</span>
<span class="sd"> &quot;&quot;&quot;</span>
@ -256,10 +245,18 @@
<div class="viewcode-block" id="AttributeProperty.__init__"><a class="viewcode-back" href="../../../api/evennia.typeclasses.attributes.html#evennia.typeclasses.attributes.AttributeProperty.__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">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">strattr</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">lockstring</span><span class="o">=</span><span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Allows for specifying Attributes as Django-like &#39;fields&#39; on the class level. Note that while</span>
<span class="sd"> one can set a lock on the Attribute, there is no way to *check* said lock when accessing via</span>
<span class="sd"> the property - use the full `AttributeHandler` if you need to do access checks. Note however</span>
<span class="sd"> that if you use the full `AttributeHandler` to access this Attribute, the `at_get/at_set`</span>
<span class="sd"> methods on this class will _not_ fire (because you are bypassing the `AttributeProperty`</span>
<span class="sd"> entirely in that case).</span>
<span class="sd"> Initialize an Attribute as a property descriptor.</span>
<span class="sd"> Keyword Args:</span>
<span class="sd"> default (any): A default value if the attr is not set.</span>
<span class="sd"> default (any): A default value if the attr is not set. If a callable, this will be</span>
<span class="sd"> run without any arguments and is expected to return the default value.</span>
<span class="sd"> category (str): The attribute&#39;s category. If unset, use class default.</span>
<span class="sd"> strattr (bool): If set, this Attribute *must* be a simple string, and will be</span>
<span class="sd"> stored more efficiently.</span>
@ -272,6 +269,11 @@
<span class="sd"> is explicitly assigned a value. This makes it more efficient while it retains</span>
<span class="sd"> its default (there&#39;s no db access), but without an actual Attribute generated,</span>
<span class="sd"> one cannot access it via .db, the AttributeHandler or see it with `examine`.</span>
<span class="sd"> Example:</span>
<span class="sd"> ::</span>
<span class="sd"> class Character(DefaultCharacter):</span>
<span class="sd"> foo = AttributeProperty(default=&quot;Bar&quot;)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_default</span> <span class="o">=</span> <span class="n">default</span>
@ -1468,11 +1470,12 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">backend</span><span class="o">.</span><span class="n">clear_attributes</span><span class="p">(</span><span class="n">category</span><span class="p">,</span> <span class="n">accessing_obj</span><span class="p">,</span> <span class="n">default_access</span><span class="p">)</span></div>
<div class="viewcode-block" id="AttributeHandler.all"><a class="viewcode-back" href="../../../api/evennia.typeclasses.attributes.html#evennia.typeclasses.attributes.AttributeHandler.all">[docs]</a> <span class="k">def</span> <span class="nf">all</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">accessing_obj</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">default_access</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
<div class="viewcode-block" id="AttributeHandler.all"><a class="viewcode-back" href="../../../api/evennia.typeclasses.attributes.html#evennia.typeclasses.attributes.AttributeHandler.all">[docs]</a> <span class="k">def</span> <span class="nf">all</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">accessing_obj</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">default_access</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Return all Attribute objects on this object, regardless of category.</span>
<span class="sd"> Args:</span>
<span class="sd"> category (str, optional): A given category to limit results to.</span>
<span class="sd"> accessing_obj (object, optional): Check the `attrread`</span>
<span class="sd"> lock on each attribute before returning them. If not</span>
<span class="sd"> given, this check is skipped.</span>
@ -1486,6 +1489,8 @@
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">attrs</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">backend</span><span class="o">.</span><span class="n">get_all_attributes</span><span class="p">()</span>
<span class="k">if</span> <span class="n">category</span><span class="p">:</span>
<span class="n">attrs</span> <span class="o">=</span> <span class="p">[</span><span class="n">attr</span> <span class="k">for</span> <span class="n">attr</span> <span class="ow">in</span> <span class="n">attrs</span> <span class="k">if</span> <span class="n">attr</span><span class="o">.</span><span class="n">category</span> <span class="o">==</span> <span class="n">category</span><span class="p">]</span>
<span class="k">if</span> <span class="n">accessing_obj</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[</span>

View file

@ -440,6 +440,21 @@
<div class="viewcode-block" id="CmdEvMenuNode.get_help"><a class="viewcode-back" href="../../../api/evennia.utils.evmenu.html#evennia.utils.evmenu.CmdEvMenuNode.get_help">[docs]</a> <span class="k">def</span> <span class="nf">get_help</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="s2">&quot;Menu commands are explained within the menu.&quot;</span></div>
<span class="k">def</span> <span class="nf">_update_aliases</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">menu</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Add aliases to make sure to override defaults if we defined we want it.&quot;&quot;&quot;</span>
<span class="n">new_aliases</span> <span class="o">=</span> <span class="p">[</span><span class="n">_CMD_NOMATCH</span><span class="p">]</span>
<span class="k">if</span> <span class="n">menu</span><span class="o">.</span><span class="n">auto_quit</span> <span class="ow">and</span> <span class="s2">&quot;quit&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">aliases</span><span class="p">:</span>
<span class="n">new_aliases</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="s2">&quot;q&quot;</span><span class="p">,</span> <span class="s2">&quot;quit&quot;</span><span class="p">])</span>
<span class="k">if</span> <span class="n">menu</span><span class="o">.</span><span class="n">auto_look</span> <span class="ow">and</span> <span class="s2">&quot;look&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">aliases</span><span class="p">:</span>
<span class="n">new_aliases</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="s2">&quot;l&quot;</span><span class="p">,</span> <span class="s2">&quot;look&quot;</span><span class="p">])</span>
<span class="k">if</span> <span class="n">menu</span><span class="o">.</span><span class="n">auto_help</span> <span class="ow">and</span> <span class="s2">&quot;help&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">aliases</span><span class="p">:</span>
<span class="n">new_aliases</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="s2">&quot;h&quot;</span><span class="p">,</span> <span class="s2">&quot;help&quot;</span><span class="p">])</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">new_aliases</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">set_aliases</span><span class="p">(</span><span class="n">new_aliases</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;aliases: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">aliases</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<div class="viewcode-block" id="CmdEvMenuNode.func"><a class="viewcode-back" href="../../../api/evennia.utils.evmenu.html#evennia.utils.evmenu.CmdEvMenuNode.func">[docs]</a> <span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Implement all menu commands.</span>
@ -460,7 +475,8 @@
<span class="n">saved_options</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="s2">&quot;startnode_input&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">startnode_input</span>
<span class="n">MenuClass</span> <span class="o">=</span> <span class="n">saved_options</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="c1"># this will create a completely new menu call</span>
<span class="n">MenuClass</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="o">*</span><span class="n">saved_options</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="o">**</span><span class="n">saved_options</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
<span class="n">menu</span> <span class="o">=</span> <span class="n">MenuClass</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="o">*</span><span class="n">saved_options</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="o">**</span><span class="n">saved_options</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
<span class="c1"># self._update_aliases(menu)</span>
<span class="k">return</span> <span class="kc">True</span>
<span class="k">return</span> <span class="kc">None</span>
@ -486,6 +502,9 @@
<span class="n">err</span>
<span class="p">)</span> <span class="c1"># don&#39;t give the session as a kwarg here, direct to original</span>
<span class="k">raise</span> <span class="n">EvMenuError</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
<span class="c1"># self._update_aliases(menu)</span>
<span class="c1"># we must do this after the caller with the menu has been correctly identified since it</span>
<span class="c1"># can be either Account, Object or Session (in the latter case this info will be</span>
<span class="c1"># superfluous).</span>
@ -615,7 +634,7 @@
<span class="sd"> by default in all nodes of the menu. This will print out the current state of</span>
<span class="sd"> the menu. Deactivate for production use! When the debug flag is active, the</span>
<span class="sd"> `persistent` flag is deactivated.</span>
<span class="sd"> **kwargs: All kwargs will become initialization variables on `caller.ndb._menutree`,</span>
<span class="sd"> **kwargs: All kwargs will become initialization variables on `caller.ndb._evmenu`,</span>
<span class="sd"> to be available at run.</span>
<span class="sd"> Raises:</span>
@ -709,7 +728,7 @@
<span class="c1"># store ourself on the object</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">_evmenu</span> <span class="o">=</span> <span class="bp">self</span>
<span class="c1"># DEPRECATED - for backwards-compatibility. Use `.ndb._evmenu` instead</span>
<span class="c1"># TODO DEPRECATED - for backwards-compatibility. Use `.ndb._evmenu` instead</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">_menutree</span> <span class="o">=</span> <span class="bp">self</span>
<span class="k">if</span> <span class="n">persistent</span><span class="p">:</span>
@ -948,6 +967,9 @@
<span class="k">if</span> <span class="ow">not</span> <span class="n">nodename</span><span class="p">:</span>
<span class="c1"># no nodename return. Re-run current node</span>
<span class="n">nodename</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">nodename</span>
<span class="k">elif</span> <span class="n">nodename_or_callable</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="c1"># repeat current node</span>
<span class="n">nodename</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">nodename</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># the nodename given directly</span>
<span class="n">nodename</span> <span class="o">=</span> <span class="n">nodename_or_callable</span>
@ -993,7 +1015,6 @@
<span class="k">if</span> <span class="n">options</span><span class="p">:</span>
<span class="n">options</span> <span class="o">=</span> <span class="p">[</span><span class="n">options</span><span class="p">]</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">options</span><span class="p">,</span> <span class="nb">dict</span><span class="p">)</span> <span class="k">else</span> <span class="n">options</span>
<span class="k">for</span> <span class="n">inum</span><span class="p">,</span> <span class="n">dic</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">options</span><span class="p">):</span>
<span class="c1"># homogenize the options dict</span>
<span class="n">keys</span> <span class="o">=</span> <span class="n">make_iter</span><span class="p">(</span><span class="n">dic</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">))</span>
<span class="n">desc</span> <span class="o">=</span> <span class="n">dic</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;desc&quot;</span><span class="p">,</span> <span class="n">dic</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;text&quot;</span><span class="p">,</span> <span class="kc">None</span><span class="p">))</span>
@ -1031,6 +1052,7 @@
<span class="bp">self</span><span class="o">.</span><span class="n">_quitting</span> <span class="o">=</span> <span class="kc">True</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">cmdset</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">EvMenuCmdSet</span><span class="p">)</span>
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">_evmenu</span>
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">_menutree</span> <span class="c1"># TODO Deprecated</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_persistent</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="s2">&quot;_menutree_saved&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="s2">&quot;_menutree_saved_startnode&quot;</span><span class="p">)</span>
@ -1385,7 +1407,6 @@
<span class="k">return</span> <span class="kc">None</span><span class="p">,</span> <span class="n">kwargs</span>
<span class="k">def</span> <span class="nf">_list_node</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="n">option_list</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">option_generator</span><span class="p">(</span><span class="n">caller</span><span class="p">)</span> <span class="k">if</span> <span class="nb">callable</span><span class="p">(</span><span class="n">option_generator</span><span class="p">)</span> <span class="k">else</span> <span class="n">option_generator</span>
<span class="p">)</span>

View file

@ -1567,6 +1567,7 @@
<span class="s2">&quot;conj&quot;</span><span class="p">:</span> <span class="n">funcparser_callable_conjugate</span><span class="p">,</span>
<span class="s2">&quot;pron&quot;</span><span class="p">:</span> <span class="n">funcparser_callable_pronoun</span><span class="p">,</span>
<span class="s2">&quot;Pron&quot;</span><span class="p">:</span> <span class="n">funcparser_callable_pronoun_capitalize</span><span class="p">,</span>
<span class="o">**</span><span class="n">FUNCPARSER_CALLABLES</span><span class="p">,</span>
<span class="p">}</span>
</pre></div>

View file

@ -203,7 +203,9 @@
<li><a href="evennia/contrib/tutorials/bodyfunctions/tests.html">evennia.contrib.tutorials.bodyfunctions.tests</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/characters.html">evennia.contrib.tutorials.evadventure.characters</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/chargen.html">evennia.contrib.tutorials.evadventure.chargen</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/combat_base.html">evennia.contrib.tutorials.evadventure.combat_base</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/combat_turnbased.html">evennia.contrib.tutorials.evadventure.combat_turnbased</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/combat_twitch.html">evennia.contrib.tutorials.evadventure.combat_twitch</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/commands.html">evennia.contrib.tutorials.evadventure.commands</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/dungeon.html">evennia.contrib.tutorials.evadventure.dungeon</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/enums.html">evennia.contrib.tutorials.evadventure.enums</a></li>
@ -221,7 +223,9 @@
<li><a href="evennia/contrib/tutorials/evadventure/tests/test_commands.html">evennia.contrib.tutorials.evadventure.tests.test_commands</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/tests/test_dungeon.html">evennia.contrib.tutorials.evadventure.tests.test_dungeon</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/tests/test_equipment.html">evennia.contrib.tutorials.evadventure.tests.test_equipment</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/tests/test_npcs.html">evennia.contrib.tutorials.evadventure.tests.test_npcs</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/tests/test_quests.html">evennia.contrib.tutorials.evadventure.tests.test_quests</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/tests/test_rooms.html">evennia.contrib.tutorials.evadventure.tests.test_rooms</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/tests/test_rules.html">evennia.contrib.tutorials.evadventure.tests.test_rules</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/tests/test_utils.html">evennia.contrib.tutorials.evadventure.tests.test_utils</a></li>
<li><a href="evennia/contrib/tutorials/evadventure/utils.html">evennia.contrib.tutorials.evadventure.utils</a></li>

View file

@ -1,5 +1,15 @@
# Changelog
## Main branch
- Contrib: Container typeclass with new commands for storing and retrieving
things inside them (InspectorCaracal)
- Fix: The `AttributeHandler.all()` now actually accepts `category=` as
keyword arg, like our docs already claimed it should (Volund)
- Docs: New Beginner-Tutorial lessons for NPCs, Base-Combat Twitch-Combat and
Turnbased-combat (note that the Beginner tutorial is still WIP).
## Evennia 1.3.0
Apr 29, 2023

View file

@ -1,11 +1,9 @@
# Soft Code
Softcode is a very simple programming language that was created for in-game development on TinyMUD derivatives such as MUX, PennMUSH, TinyMUSH, and RhostMUSH. The idea is that by providing a stripped down, minimalistic language for in-game use, you can allow quick and easy building and game development to happen without having to learn C/C++. There is an added benefit of not having to have to hand out shell access to all developers, and permissions can be used to alleviate many security problems.
Softcode is a simple programming language that was created for in-game development on TinyMUD derivatives such as MUX, PennMUSH, TinyMUSH, and RhostMUSH. The idea was that by providing a stripped down, minimalistic language for in-game use, you could allow quick and easy building and game development to happen without builders having to learn the 'hardcode' language for those servers (C/C++). There is an added benefit of not having to have to hand out shell access to all developers. Permissions in softcode can be used to alleviate many security problems.
Writing and installing softcode is done through a MUD client. Thus it is not a formatted language.
Each softcode function is a single line of varying size. Some functions can be a half of a page long
or more which is obviously not very readable nor (easily) maintainable over time.
Writing and installing softcode is done through a MUD client. Thus it is not a formatted language. Each softcode function is a single line of varying size. Some functions can be a half of a page long or more which is obviously not very readable nor (easily) maintainable over time.
## Examples of Softcode
@ -15,27 +13,24 @@ Here is a simple 'Hello World!' command:
@set me=HELLO_WORLD.C:$hello:@pemit %#=Hello World!
```
Pasting this into a MUX/MUSH and typing 'hello' will theoretically yield 'Hello World!', assuming
certain flags are not set on your account object.
Pasting this into a MUD client, sending it to a MUX/MUSH server and typing 'hello' will theoretically yield 'Hello World!', assuming certain flags are not set on your account object.
Setting attributes is done via `@set`. Softcode also allows the use of the ampersand (`&`) symbol.
This shorter version looks like this:
Setting attributes in Softcode is done via `@set`. Softcode also allows the use of the ampersand (`&`) symbol. This shorter version looks like this:
```bash
&HELLO_WORLD.C me=$hello:@pemit %#=Hello World!
```
Perhaps I want to break the Hello World into an attribute which is retrieved when emitting:
We could also read the text from an attribute which is retrieved when emitting:
```bash
&HELLO_VALUE.D me=Hello World
&HELLO_WORLD.C me=$hello:@pemit %#=[v(HELLO_VALUE.D)]
```
The `v()` function returns the `HELLO_VALUE.D` attribute on the object that the command resides
(`me`, which is yourself in this case). This should yield the same output as the first example.
The `v()` function returns the `HELLO_VALUE.D` attribute on the object that the command resides (`me`, which is yourself in this case). This should yield the same output as the first example.
If you are still curious about how Softcode works, take a look at some external resources:
If you are curious about how MUSH/MUX Softcode works, take a look at some external resources:
- https://wiki.tinymux.org/index.php/Softcode
- https://www.duh.com/discordia/mushman/man2x1
@ -44,24 +39,26 @@ If you are still curious about how Softcode works, take a look at some external
Softcode is excellent at what it was intended for: *simple things*. It is a great tool for making an interactive object, a room with ambiance, simple global commands, simple economies and coded systems. However, once you start to try to write something like a complex combat system or a higher end economy, you're likely to find yourself buried under a mountain of functions that span multiple objects across your entire code.
Not to mention, softcode is not an inherently fast language. It is not compiled, it is parsed with each calling of a function. While MUX and MUSH parsers have jumped light years ahead of where they once were they can still stutter under the weight of more complex systems if not designed properly.
Not to mention, softcode is not an inherently fast language. It is not compiled, it is parsed with each calling of a function. While MUX and MUSH parsers have jumped light years ahead of where they once were, they can still stutter under the weight of more complex systems if those are not designed properly.
Also, Softcode is not a standardized language. Different servers each have their own slight variations. Code tools and resources are also limited to the documentation from those servers.
## Changing Times
Now that starting text-based games is easy and an option for even the most technically inarticulate, new projects are a dime a dozen. People are starting new MUDs every day with varying levels of commitment and ability. Because of this shift from fewer, larger, well-staffed games to a bunch of small, one or two developer games, some of the benefit of softcode fades.
Now that starting text-based games is easy and an option for even the most technically inarticulate, new projects are a dime a dozen. People are starting new MUDs every day with varying levels of commitment and ability. Because of this shift from fewer, larger, well-staffed games to a bunch of small, one or two developer games, the benefit of softcode fades.
Softcode is great in that it allows a mid to large sized staff all work on the same game without stepping on one another's toes. As mentioned before, shell access is not necessary to develop a MUX or a MUSH. However, now that we are seeing a lot more small, one or two-man shops, the issue of shell access and stepping on each other's toes is a lot less.
Softcode is great in that it allows a mid to large sized staff all work on the same game without stepping on one another's toes without shell access. However, the rise of modern code collaboration tools (such as private github/gitlab repos) has made it trivial to collaborate on code.
## Our Solution
Evennia shuns in-game softcode for on-disk Python modules. Python is a popular, mature and
professional programming language. You code it using the conveniences of modern text editors.
Evennia developers have access to the entire library of Python modules out there in the wild - not
to mention the vast online help resources available. Python code is not bound to one-line functions
on objects but complex systems may be organized neatly into real source code modules, sub-modules, or even broken out into entire Python packages as desired.
Evennia shuns in-game softcode for on-disk Python modules. Python is a popular, mature and professional programming language. Evennia developers have access to the entire library of Python modules out there in the wild - not to mention the vast online help resources available. Python code is not bound to one-line functions on objects; complex systems may be organized neatly into real source code modules, sub-modules, or even broken out into entire Python packages as desired.
So what is *not* included in Evennia is a MUX/MOO-like online player-coding system. Advanced coding in Evennia is primarily intended to be done outside the game, in full-fledged Python modules. Advanced building is best handled by extending Evennia's command system with your own sophisticated building commands. We feel that with a small development team you are better off using a professional source-control system (svn, git, bazaar, mercurial etc) anyway.
So what is *not* included in Evennia is a MUX/MOO-like online player-coding system (aka Softcode). Advanced coding in Evennia is primarily intended to be done outside the game, in full-fledged Python modules (what MUSH would call 'hardcode'). Advanced building is best handled by extending Evennia's command system with your own sophisticated building commands.
In Evennia you develop your MU like you would any piece of modern software - using your favorite code editor/IDE and online code sharing tools.
## Your Solution
Adding advanced and flexible building commands to your game is easy and will probably be enough to satisfy most creative builders. However, if you really, *really* want to offer online coding, there is of course nothing stopping you from adding that to Evennia, no matter our recommendations. You could even re-implement MUX' softcode in Python should you be very ambitious.
Adding advanced and flexible building commands to your game is easy and will probably be enough to satisfy most creative builders. However, if you really, *really* want to offer online coding, there is of course nothing stopping you from adding that to Evennia, no matter our recommendations. You could even re-implement MUX' softcode in Python should you be very ambitious. The [in-game-python](../Contribs/Contrib-Ingame-Python.md) is an optional pseudo-softcode plugin aimed at developers wanting to script their game from inside it.
In default Evennia, the [Funcparser](Funcparser) system allows for simple remapping of text on-demand without becomeing a full softcode language. The [contribs](Contrib-Overview) has several tools and utililities to start from when adding more complex in-game building.

View file

@ -2,13 +2,9 @@
*Note: This is considered an advanced topic.*
Evennia offers many convenient ways to store object data, such as via Attributes or Scripts. This is
sufficient for most use cases. But if you aim to build a large stand-alone system, trying to squeeze
your storage requirements into those may be more complex than you bargain for. Examples may be to
store guild data for guild members to be able to change, tracking the flow of money across a game-
wide economic system or implement other custom game systems that requires the storage of custom data
in a quickly accessible way. Whereas [Tags](../Components/Tags.md) or [Scripts](../Components/Scripts.md) can handle many situations,
sometimes things may be easier to handle by adding your own database model.
Evennia offers many convenient ways to store object data, such as via Attributes or Scripts. This is sufficient for most use cases. But if you aim to build a large stand-alone system, trying to squeeze your storage requirements into those may be more complex than you bargain for. Examples may be to store guild data for guild members to be able to change, tracking the flow of money across a game-wide economic system or implement other custom game systems that requires the storage of custom data in a quickly accessible way.
Whereas [Tags](../Components/Tags.md) or [Scripts](../Components/Scripts.md) can handle many situations, sometimes things may be easier to handle by adding your own _database model_.
## Overview of database tables
@ -22,9 +18,7 @@ retrieving text stored in tables. A table may look like this
2 | Rock | evennia.DefaultObject | None ...
```
Each line is considerably longer in your database. Each column is referred to as a "field" and every
row is a separate object. You can check this out for yourself. If you use the default sqlite3
database, go to your game folder and run
Each line is considerably longer in your database. Each column is referred to as a "field" and every row is a separate object. You can check this out for yourself. If you use the default sqlite3 database, go to your game folder and run
evennia dbshell
@ -42,34 +36,19 @@ You will drop into the database shell. While there, try:
sqlite> .exit
Evennia uses [Django](https://docs.djangoproject.com), which abstracts away the database SQL
manipulation and allows you to search and manipulate your database entirely in Python. Each database
table is in Django represented by a class commonly called a *model* since it describes the look of
the table. In Evennia, Objects, Scripts, Channels etc are examples of Django models that we then
extend and build on.
Evennia uses [Django](https://docs.djangoproject.com), which abstracts away the database SQL manipulation and allows you to search and manipulate your database entirely in Python. Each database table is in Django represented by a class commonly called a *model* since it describes the look of the table. In Evennia, Objects, Scripts, Channels etc are examples of Django models that we then extend and build on.
## Adding a new database table
Here is how you add your own database table/models:
1. In Django lingo, we will create a new "application" - a subsystem under the main Evennia program.
For this example we'll call it "myapp". Run the following (you need to have a working Evennia
running before you do this, so make sure you have run the steps in [Setup Quickstart](Getting-
Started) first):
1. In Django lingo, we will create a new "application" - a subsystem under the main Evennia program. For this example we'll call it "myapp". Run the following (you need to have a working Evennia running before you do this, so make sure you have run the steps in [Setup Quickstart](Getting- Started) first):
cd mygame/world
evennia startapp myapp
1. A new folder `myapp` is created. "myapp" will also be the name (the "app label") from now on. We
chose to put it in the `world/` subfolder here, but you could put it in the root of your `mygame` if
that makes more sense.
1. The `myapp` folder contains a few empty default files. What we are
interested in for now is `models.py`. In `models.py` you define your model(s). Each model will be a
table in the database. See the next section and don't continue until you have added the models you
want.
1. You now need to tell Evennia that the models of your app should be a part of your database
scheme. Add this line to your `mygame/server/conf/settings.py`file (make sure to use the path where
you put `myapp` and don't forget the comma at the end of the tuple):
1. A new folder `myapp` is created. "myapp" will also be the name (the "app label") from now on. We chose to put it in the `world/` subfolder here, but you could put it in the root of your `mygame` if that makes more sense. 1. The `myapp` folder contains a few empty default files. What we are interested in for now is `models.py`. In `models.py` you define your model(s). Each model will be a table in the database. See the next section and don't continue until you have added the models you want.
1. You now need to tell Evennia that the models of your app should be a part of your database scheme. Add this line to your `mygame/server/conf/settings.py`file (make sure to use the path where you put `myapp` and don't forget the comma at the end of the tuple):
```
INSTALLED_APPS = INSTALLED_APPS + ("world.myapp", )
@ -78,22 +57,16 @@ you put `myapp` and don't forget the comma at the end of the tuple):
1. From `mygame/`, run
evennia makemigrations myapp
evennia migrate
evennia migrate myapp
This will add your new database table to the database. If you have put your game under version
control (if not, [you should](../Coding/Version-Control.md)), don't forget to `git add myapp/*` to add all items
This will add your new database table to the database. If you have put your game under version control (if not, [you should](../Coding/Version-Control.md)), don't forget to `git add myapp/*` to add all items
to version control.
## Defining your models
A Django *model* is the Python representation of a database table. It can be handled like any other
Python class. It defines *fields* on itself, objects of a special type. These become the "columns"
of the database table. Finally, you create new instances of the model to add new rows to the
database.
A Django *model* is the Python representation of a database table. It can be handled like any other Python class. It defines *fields* on itself, objects of a special type. These become the "columns" of the database table. Finally, you create new instances of the model to add new rows to the database.
We won't describe all aspects of Django models here, for that we refer to the vast [Django
documentation](https://docs.djangoproject.com/en/4.1/topics/db/models/) on the subject. Here is a
(very) brief example:
We won't describe all aspects of Django models here, for that we refer to the vast [Django documentation](https://docs.djangoproject.com/en/4.1/topics/db/models/) on the subject. Here is a (very) brief example:
```python
from django.db import models
@ -112,28 +85,51 @@ class MyDataStore(models.Model):
We create four fields: two character fields of limited length and one text field which has no
maximum length. Finally we create a field containing the current time of us creating this object.
> The `db_date_created` field, with exactly this name, is *required* if you want to be able to store
instances of your custom model in an Evennia [Attribute](../Components/Attributes.md). It will automatically be set
upon creation and can after that not be changed. Having this field will allow you to do e.g.
`obj.db.myinstance = mydatastore`. If you know you'll never store your model instances in Attributes
the `db_date_created` field is optional.
> The `db_date_created` field, with exactly this name, is *required* if you want to be able to store instances of your custom model in an Evennia [Attribute](../Components/Attributes.md). It will automatically be set upon creation and can after that not be changed. Having this field will allow you to do e.g. `obj.db.myinstance = mydatastore`. If you know you'll never store your model instances in Attributes the `db_date_created` field is optional.
You don't *have* to start field names with `db_`, this is an Evennia convention. It's nevertheless
recommended that you do use `db_`, partly for clarity and consistency with Evennia (if you ever want
to share your code) and partly for the case of you later deciding to use Evennia's
You don't *have* to start field names with `db_`, this is an Evennia convention. It's nevertheless recommended that you do use `db_`, partly for clarity and consistency with Evennia (if you ever want to share your code) and partly for the case of you later deciding to use Evennia's
`SharedMemoryModel` parent down the line.
The field keyword `db_index` creates a *database index* for this field, which allows quicker
lookups, so it's recommended to put it on fields you know you'll often use in queries. The
`null=True` and `blank=True` keywords means that these fields may be left empty or set to the empty
string without the database complaining. There are many other field types and keywords to define
them, see django docs for more info.
The field keyword `db_index` creates a *database index* for this field, which allows quicker lookups, so it's recommended to put it on fields you know you'll often use in queries. The `null=True` and `blank=True` keywords means that these fields may be left empty or set to the empty string without the database complaining. There are many other field types and keywords to define them, see django docs for more info.
Similar to using [django-admin](https://docs.djangoproject.com/en/4.1/howto/legacy-databases/) you
are able to do `evennia inspectdb` to get an automated listing of model information for an existing
database. As is the case with any model generating tool you should only use this as a starting
Similar to using [django-admin](https://docs.djangoproject.com/en/4.1/howto/legacy-databases/) you are able to do `evennia inspectdb` to get an automated listing of model information for an existing database. As is the case with any model generating tool you should only use this as a starting
point for your models.
## Referencing existing models and typeclasses
You may want to use `ForeignKey` or `ManyToManyField` to relate your new model to existing ones.
To do this we need to specify the app-path for the root object type we want to store as a string (we must use a string rather than the class directly or you'll run into problems with models not having been initialized yet).
- `"objects.ObjectDB"` for all [Objects](../Components/Objects.md) (like exits, rooms, characters etc)
- `"accounts.AccountDB"` for [Accounts](../Components/Accounts.md).
- `"scripts.ScriptDB"` for [Scripts](../Components/Scripts.md).
- `"comms.ChannelDB"` for [Channels](../Components/Channels.md).
- `"comms.Msg"` for [Msg](../Components/Msg.md) objects.
- `"help.HelpEntry"` for [Help Entries](../Components/Help-System.md).
Here's an example:
```python
from django.db import models
class MySpecial(models.Model):
db_character = models.ForeignKey("objects.ObjectDB")
db_items = models.ManyToManyField("objects.ObjectDB")
db_account = modeles.ForeignKey("accounts.AccountDB")
```
It may seem counter-intuitive, but this will work correctly:
myspecial.db_character = my_character # a Character instance
my_character = myspecial.db_character # still a Character
This works because when the `.db_character` field is loaded into Python, the entity itself knows that it's supposed to be a `Character` and loads itself to that form.
The drawback of this is that the database won't _enforce_ the type of object you store in the relation. This is the price we pay for many of the other advantages of the Typeclass system.
While the `db_character` field fail if you try to store an `Account`, it will gladly accept any instance of a typeclass that inherits from `ObjectDB`, such as rooms, exits or other non-character Objects. It's up to you to validate that what you store is what you expect it to be.
## Creating a new model instance
To create a new row in your table, you instantiate the model and then call its `save()` method:
@ -149,83 +145,54 @@ To create a new row in your table, you instantiate the model and then call its `
```
Note that the `db_date_created` field of the model is not specified. Its flag `at_now_add=True`
makes sure to set it to the current date when the object is created (it can also not be changed
further after creation).
Note that the `db_date_created` field of the model is not specified. Its flag `at_now_add=True` makes sure to set it to the current date when the object is created (it can also not be changed further after creation).
When you update an existing object with some new field value, remember that you have to save the
object afterwards, otherwise the database will not update:
When you update an existing object with some new field value, remember that you have to save the object afterwards, otherwise the database will not update:
```python
my_datastore.db_key = "Larger Sword"
my_datastore.save()
```
Evennia's normal models don't need to explicitly save, since they are based on `SharedMemoryModel`
rather than the raw django model. This is covered in the next section.
Evennia's normal models don't need to explicitly save, since they are based on `SharedMemoryModel` rather than the raw django model. This is covered in the next section.
## Using the `SharedMemoryModel` parent
Evennia doesn't base most of its models on the raw `django.db.models` but on the Evennia base model
`evennia.utils.idmapper.models.SharedMemoryModel`. There are two main reasons for this:
Evennia doesn't base most of its models on the raw `django.db.models.Model` but on the Evennia base model `evennia.utils.idmapper.models.SharedMemoryModel`. There are two main reasons for this:
1. Ease of updating fields without having to explicitly call `save()`
2. On-object memory persistence and database caching
The first (and least important) point means that as long as you named your fields `db_*`, Evennia
will automatically create field wrappers for them. This happens in the model's
[Metaclass](http://en.wikibooks.org/wiki/Python_Programming/Metaclasses) so there is no speed
penalty for this. The name of the wrapper will be the same name as the field, minus the `db_`
prefix. So the `db_key` field will have a wrapper property named `key`. You can then do:
The first (and least important) point means that as long as you named your fields `db_*`, Evennia will automatically create field wrappers for them. This happens in the model's [Metaclass](http://en.wikibooks.org/wiki/Python_Programming/Metaclasses) so there is no speed penalty for this. The name of the wrapper will be the same name as the field, minus the `db_` prefix. So the `db_key` field will have a wrapper property named `key`. You can then do:
```python
my_datastore.key = "Larger Sword"
```
and don't have to explicitly call `save()` afterwards. The saving also happens in a more efficient
way under the hood, updating only the field rather than the entire model using django optimizations.
Note that if you were to manually add the property or method `key` to your model, this will be used
instead of the automatic wrapper and allows you to fully customize access as needed.
and don't have to explicitly call `save()` afterwards. The saving also happens in a more efficient way under the hood, updating only the field rather than the entire model using django optimizations. Note that if you were to manually add the property or method `key` to your model, this will be used instead of the automatic wrapper and allows you to fully customize access as needed.
To explain the second and more important point, consider the following example using the default
Django model parent:
To explain the second and more important point, consider the following example using the default Django model parent:
```python
shield = MyDataStore.objects.get(db_key="SmallShield")
shield.cracked = True # where cracked is not a database field
```
And then later:
And then in another function you do
```python
shield = MyDataStore.objects.get(db_key="SmallShield")
print(shield.cracked) # error!
```
The outcome of that last print statement is *undefined*! It could *maybe* randomly work but most
likely you will get an `AttributeError` for not finding the `cracked` property. The reason is that
`cracked` doesn't represent an actual field in the database. It was just added at run-time and thus
Django don't care about it. When you retrieve your shield-match later there is *no* guarantee you
will get back the *same Python instance* of the model where you defined `cracked`, even if you
search for the same database object.
The outcome of that last print statement is *undefined*! It could *maybe* randomly work but most likely you will get an `AttributeError` for not finding the `cracked` property. The reason is that `cracked` doesn't represent an actual field in the database. It was just added at run-time and thus Django don't care about it. When you retrieve your shield-match later there is *no* guarantee you will get back the *same Python instance* of the model where you defined `cracked`, even if you search for the same database object.
Evennia relies heavily on on-model handlers and other dynamically created properties. So rather than
using the vanilla Django models, Evennia uses `SharedMemoryModel`, which levies something called
*idmapper*. The idmapper caches model instances so that we will always get the *same* instance back
after the first lookup of a given object. Using idmapper, the above example would work fine and you
could retrieve your `cracked` property at any time - until you rebooted when all non-persistent data
goes.
Evennia relies heavily on on-model handlers and other dynamically created properties. So rather than using the vanilla Django models, Evennia uses `SharedMemoryModel`, which levies something called *idmapper*. The idmapper caches model instances so that we will always get the *same* instance back after the first lookup of a given object. Using idmapper, the above example would work fine and you could retrieve your `cracked` property at any time - until you rebooted when all non-persistent data goes.
Using the idmapper is both more intuitive and more efficient *per object*; it leads to a lot less
reading from disk. The drawback is that this system tends to be more memory hungry *overall*. So if
you know that you'll *never* need to add new properties to running instances or know that you will
create new objects all the time yet rarely access them again (like for a log system), you are
probably better off making "plain" Django models rather than using `SharedMemoryModel` and its
idmapper.
reading from disk. The drawback is that this system tends to be more memory hungry *overall*. So if you know that you'll *never* need to add new properties to running instances or know that you will create new objects all the time yet rarely access them again (like for a log system), you are probably better off making "plain" Django models rather than using `SharedMemoryModel` and its idmapper.
To use the idmapper and the field-wrapper functionality you just have to have your model classes
inherit from `evennia.utils.idmapper.models.SharedMemoryModel` instead of from the default
`django.db.models.Model`:
To use the idmapper and the field-wrapper functionality you just have to have your model classes inherit from `evennia.utils.idmapper.models.SharedMemoryModel` instead of from the default `django.db.models.Model`:
```python
from evennia.utils.idmapper.models import SharedMemoryModel
@ -242,9 +209,7 @@ class MyDataStore(SharedMemoryModel):
## Searching for your models
To search your new custom database table you need to use its database *manager* to build a *query*.
Note that even if you use `SharedMemoryModel` as described in the previous section, you have to use
the actual *field names* in the query, not the wrapper name (so `db_key` and not just `key`).
To search your new custom database table you need to use its database *manager* to build a *query*. Note that even if you use `SharedMemoryModel` as described in the previous section, you have to use the actual *field names* in the query, not the wrapper name (so `db_key` and not just `key`).
```python
from world.myapp import MyDataStore
@ -260,5 +225,4 @@ the actual *field names* in the query, not the wrapper name (so `db_key` and not
self.caller.msg(match.db_text)
```
See the [Django query documentation](https://docs.djangoproject.com/en/4.1/topics/db/queries/) for a
lot more information about querying the database.
See the [Beginner Tutorial lesson on Django querying](../Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Django-queries.md) for a lot more information about querying the database.

View file

@ -1,19 +1,18 @@
# EvAdventure
Contrib by Griatch 2022
Contrib by Griatch 2023-
```{warning}
NOTE - this tutorial is WIP and NOT complete! It was put on hold to focus on
releasing Evennia 1.0. You will still learn things from it, but don't expect
perfection.
NOTE - this tutorial is WIP and NOT complete yet! You will still learn
things from it, but don't expect perfection.
```
A complete example MUD using Evennia. This is the final result of what is
implemented if you follow the Getting-Started tutorial. It's recommended
that you follow the tutorial step by step and write your own code. But if
you prefer you can also pick apart or use this as a starting point for your
own game.
implemented if you follow [Part 3 of the Getting-Started tutorial](../Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Part3-Overview.md).
It's recommended that you follow the tutorial step by step and write your own
code. But if you prefer you can also pick apart or use this as a starting point
for your own game.
## Features

View file

@ -685,13 +685,12 @@ character make small verbal observations at irregular intervals.
### `evadventure`
_Contrib by Griatch 2022_
_Contrib by Griatch 2023-_
```{warning}
NOTE - this tutorial is WIP and NOT complete! It was put on hold to focus on
releasing Evennia 1.0. You will still learn things from it, but don't expect
perfection.
NOTE - this tutorial is WIP and NOT complete yet! You will still learn
things from it, but don't expect perfection.
```
[Read the documentation](./Contrib-Evadventure.md) - [Browse the Code](evennia.contrib.tutorials.evadventure)

View file

@ -14,29 +14,20 @@ The sources are organized into several rough categories, with only a few adminis
at the root of `evennia/docs/source/`.
- `source/Components/` are docs describing separate Evennia building blocks, that is, things
that you can import and use. This extends and elaborates on what can be found out by reading
the api docs themselves. Example are documentation for `Accounts`, `Objects` and `Commands`.
- `source/Concepts/` describes how larger-scale features of Evennia hang together - things that
can't easily be broken down into one isolated component. This can be general descriptions of
how Models and Typeclasses interact to the path a message takes from the client to the server
and back.
- `source/Setup/` holds detailed docs on installing, running and maintaining the Evennia server and
the infrastructure around it.
- `source/Coding/` has help on how to interact with, use and navigate the Evennia codebase itself.
This also has non-Evennia-specific help on general development concepts and how to set up a sane development environment.
that you can import and use. This extends and elaborates on what can be found out by reading the api docs themselves. Example are documentation for `Accounts`, `Objects` and `Commands`.
- `source/Concepts/` describes how larger-scale features of Evennia hang together - things that can't easily be broken down into one isolated component. This can be general descriptions of how Models and Typeclasses interact to the path a message takes from the client to the server and back.
- `source/Setup/` holds detailed docs on installing, running and maintaining the Evennia server and the infrastructure around it.
- `source/Coding/` has help on how to interact with, use and navigate the Evennia codebase itself. This also has non-Evennia-specific help on general development concepts and how to set up a sane development environment.
- `source/Contribs/` holds documentation specifically for packages in the `evennia/contribs/` folder. Any contrib-specific tutorials will be found here instead of in `Howtos`
- `source/Howtos/` holds docs that describe how to achieve a specific goal, effect or
result in Evennia. This is often on a tutorial or FAQ form and will refer to the rest of the
documentation for further reading.
- `source/Howtos/Beginner-Tutorial/` holds all documents part of the initial tutorial sequence.
result in Evennia. This is often on a tutorial or FAQ form and will refer to the rest of the documentation for further reading.
- `source/Howtos/Beginner-Tutorial/` holds all documents part of the initial tutorial sequence.
Other files and folders:
- `source/api/` contains the auto-generated API documentation as `.html` files. Don't edit these
files manually, they are auto-generated from sources.
- `source/api/` contains the auto-generated API documentation as `.html` files. Don't edit these files manually, they are auto-generated from sources.
- `source/_templates` and `source/_static` hold files for the doc itself. They should only be modified if wanting to change the look and structure of the documentation generation itself.
- `conf.py` holds the Sphinx configuration. It should usually not be modified except to update
the Evennia version on a new branch.
- `conf.py` holds the Sphinx configuration. It should usually not be modified except to update the Evennia version on a new branch.
## Editing syntax

View file

@ -13,7 +13,6 @@ import evennia
roses = evennia.search_object(key="rose")
accts = evennia.search_account(key="MyAccountName", email="foo@bar.com")
```
```
```{sidebar} Querysets
@ -73,7 +72,7 @@ class CmdQuickFind(Command):
result = self.caller.search(query)
if not result
return
self.caller.msg(f"Found match for {query}: {foo}")
self.caller.msg(f"Found match for {query}: {foo}")
```
Remember, `self.caller` is the one calling the command. This is usually a Character, which

View file

@ -0,0 +1,5 @@
# NPC and monster AI
```{warning}
This part of the Beginner tutorial is still being developed.
```

View file

@ -104,6 +104,29 @@ class LivingMixin:
# makes it easy for mobs to know to attack PCs
is_pc = False
@property
def hurt_level(self):
"""
String describing how hurt this character is.
"""
percent = max(0, min(100, 100 * (self.hp / self.hp_max)))
if 95 < percent <= 100:
return "|gPerfect|n"
elif 80 < percent <= 95:
return "|gScraped|n"
elif 60 < percent <= 80:
return "|GBruised|n"
elif 45 < percent <= 60:
return "|yHurt|n"
elif 30 < percent <= 45:
return "|yWounded|n"
elif 15 < percent <= 30:
return "|rBadly wounded|n"
elif 1 < percent <= 15:
return "|rBarely hanging on|n"
elif percent == 0:
return "|RCollapsed!|n"
def heal(self, hp):
"""
Heal hp amount of health, not allowing to exceed our max hp
@ -121,6 +144,10 @@ class LivingMixin:
self.coins -= amount
return amount
def at_attacked(self, attacker, **kwargs):
"""Called when being attacked and combat starts."""
pass
def at_damage(self, damage, attacker=None):
"""Called when attacked and taking damage."""
self.hp -= damage
@ -147,8 +174,9 @@ class LivingMixin:
looter.coins += stolen
```
Most of these are empty since they will behave differently for characters and npcs. But having them
in the mixin means we can expect these methods to be available for all living things.
Most of these are empty since they will behave differently for characters and npcs. But having them in the mixin means we can expect these methods to be available for all living things.
Once we create more of our game, we will need to remember to actually call these hook methods so they serve a purpose. For example, once we implement combat, we must remember to call `at_attacked` as well as the other methods involving taking damage, getting defeated or dying.
## Character class
@ -204,12 +232,10 @@ class EvAdventureCharacter(LivingMixin, DefaultCharacter):
# TODO - go back into chargen to make a new character!
```
We make an assumption about our rooms here - that they have a property `.allow_death`. We need
to make a note to actually add such a property to rooms later!
We make an assumption about our rooms here - that they have a property `.allow_death`. We need to make a note to actually add such a property to rooms later!
In our `Character` class we implement all attributes we want to simulate from the _Knave_ ruleset.
The `AttributeProperty` is one way to add an Attribute in a field-like way; these will be accessible
on every character in several ways:
The `AttributeProperty` is one way to add an Attribute in a field-like way; these will be accessible on every character in several ways:
- As `character.strength`
- As `character.db.strength`
@ -217,11 +243,9 @@ on every character in several ways:
See [Attributes](../../../Components/Attributes.md) for seeing how Attributes work.
Unlike in base _Knave_, we store `coins` as a separate Attribute rather than as items in the inventory,
this makes it easier to handle barter and trading later.
Unlike in base _Knave_, we store `coins` as a separate Attribute rather than as items in the inventory, this makes it easier to handle barter and trading later.
We implement the Player Character versions of `at_defeat` and `at_death`. We also make use of `.heal()`
from the `LivingMixin` class.
We implement the Player Character versions of `at_defeat` and `at_death`. We also make use of `.heal()` from the `LivingMixin` class.
### Funcparser inlines
@ -233,15 +257,10 @@ self.location.msg_contents(
from_obj=self)
```
Remember that `self` is the Character instance here. So `self.location.msg_contents` means "send a
message to everything inside my current location". In other words, send a message to everyone
in the same place as the character.
Remember that `self` is the Character instance here. So `self.location.msg_contents` means "send a message to everything inside my current location". In other words, send a message to everyone in the same place as the character.
The `$You() $conj(collapse)` are [FuncParser inlines](../../../Components/FuncParser.md). These are functions that
execute
in the string. The resulting string may look different for different audiences. The `$You()` inline
function will use `from_obj` to figure out who 'you' are and either show your name or 'You'.
The `$conj()` (verb conjugator) will tweak the (English) verb to match.
execute in the string. The resulting string may look different for different audiences. The `$You()` inline function will use `from_obj` to figure out who 'you' are and either show your name or 'You'. The `$conj()` (verb conjugator) will tweak the (English) verb to match.
- You will see: `"You collapse in a heap, alive but beaten."`
- Others in the room will see: `"Thomas collapses in a heap, alive but beaten."`
@ -250,9 +269,7 @@ Note how `$conj()` chose `collapse/collapses` to make the sentences grammaticall
### Backtracking
We make our first use of the `rules.dice` roller to roll on the death table! As you may recall, in the
previous lesson, we didn't know just what to do when rolling 'dead' on this table. Now we know - we
should be calling `at_death` on the character. So let's add that where we had TODOs before:
We make our first use of the `rules.dice` roller to roll on the death table! As you may recall, in the previous lesson, we didn't know just what to do when rolling 'dead' on this table. Now we know - we should be calling `at_death` on the character. So let's add that where we had TODOs before:
```python
# mygame/evadventure/rules.py

View file

@ -16,12 +16,7 @@ We do this by editing `mygame/server/conf/settings.py` and adding the line
AUTO_CREATE_CHARACTER_WITH_ACCOUNT = False
When doing this, connecting with the game with a new account will land you in "OOC" mode. The
ooc-version of `look` (sitting in the Account cmdset) will show a list of available characters
if you have any. You can also enter `charcreate` to make a new character. The `charcreate` is a
simple command coming with Evennia that just lets you make a new character with a given name and
description. We will later modify that to kick off our chargen. For now we'll just keep in mind
that's how we'll start off the menu.
When doing this, connecting with the game with a new account will land you in "OOC" mode. The ooc-version of `look` (sitting in the Account cmdset) will show a list of available characters if you have any. You can also enter `charcreate` to make a new character. The `charcreate` is a simple command coming with Evennia that just lets you make a new character with a given name and description. We will later modify that to kick off our chargen. For now we'll just keep in mind that's how we'll start off the menu.
In _Knave_, most of the character-generation is random. This means this tutorial can be pretty
compact while still showing the basic idea. What we will create is a menu looking like this:
@ -202,12 +197,9 @@ class TemporaryCharacterSheet:
]
```
Here we have followed the _Knave_ rulebook to randomize abilities, description and equipment.
The `dice.roll()` and `dice.roll_random_table` methods now become very useful! Everything here
should be easy to follow.
Here we have followed the _Knave_ rulebook to randomize abilities, description and equipment. The `dice.roll()` and `dice.roll_random_table` methods now become very useful! Everything here should be easy to follow.
The main difference from baseline _Knave_ is that we make a table of "starting weapon" (in Knave
you can pick whatever you like).
The main difference from baseline _Knave_ is that we make a table of "starting weapon" (in Knave you can pick whatever you like).
We also initialize `.ability_changes = 0`. Knave only allows us to swap the values of two
Abilities _once_. We will use this to know if it has been done or not.
@ -260,9 +252,7 @@ class TemporaryCharacterSheet:
```
The new `show_sheet` method collect the data from the temporary sheet and return it in a pretty
form. Making a 'template' string like `_TEMP_SHEET` makes it easier to change things later if you want
to change how things look.
The new `show_sheet` method collect the data from the temporary sheet and return it in a pretty form. Making a 'template' string like `_TEMP_SHEET` makes it easier to change things later if you want to change how things look.
### Apply character
@ -321,8 +311,7 @@ class TemporaryCharacterSheet:
return new_character
```
We use `create_object` to create a new `EvAdventureCharacter`. We feed it with all relevant data
from the temporary character sheet. This is when these become an actual character.
We use `create_object` to create a new `EvAdventureCharacter`. We feed it with all relevant data from the temporary character sheet. This is when these become an actual character.
```{sidebar}
A prototype is basically a `dict` describing how the object should be created. Since
@ -334,9 +323,7 @@ Each piece of equipment is an object in in its own right. We will here assume th
items are defined as [Prototypes](../../../Components/Prototypes.md) keyed to its name, such as "sword", "brigandine
armor" etc.
We haven't actually created those prototypes yet, so for now we'll need to assume they are there.
Once a piece of equipment has been spawned, we make sure to move it into the `EquipmentHandler` we
created in the [Equipment lesson](./Beginner-Tutorial-Equipment.md).
We haven't actually created those prototypes yet, so for now we'll need to assume they are there. Once a piece of equipment has been spawned, we make sure to move it into the `EquipmentHandler` we created in the [Equipment lesson](./Beginner-Tutorial-Equipment.md).
## Initializing EvMenu
@ -374,12 +361,9 @@ def start_chargen(caller, session=None):
This first function is what we will call from elsewhere (for example from a custom `charcreate`
command) to kick the menu into gear.
It takes the `caller` (the one to want to start the menu) and a `session` argument. The latter will help
track just which client-connection we are using (depending on Evennia settings, you could be
connecting with multiple clients).
It takes the `caller` (the one to want to start the menu) and a `session` argument. The latter will help track just which client-connection we are using (depending on Evennia settings, you could be connecting with multiple clients).
We create a `TemporaryCharacterSheet` and call `.generate()` to make a random character. We then
feed all this into `EvMenu`.
We create a `TemporaryCharacterSheet` and call `.generate()` to make a random character. We then feed all this into `EvMenu`.
The moment this happens, the user will be in the menu, there are no further steps needed.
@ -432,12 +416,7 @@ def node_chargen(caller, raw_string, **kwargs):
A lot to unpack here! In Evennia, it's convention to name your node-functions `node_*`. While
not required, it helps you track what is a node and not.
Every menu-node, should accept `caller, raw_string, **kwargs` as arguments. Here `caller` is the
`caller` you passed into the `EvMenu` call. `raw_string` is the input given by the user in order
to _get to this node_, so currently empty. The `**kwargs` are all extra keyword arguments passed
into `EvMenu`. They can also be passed between nodes. In this case, we passed the
keyword `tmp_character` to `EvMenu`. We now have the temporary character sheet available in the
node!
Every menu-node, should accept `caller, raw_string, **kwargs` as arguments. Here `caller` is the `caller` you passed into the `EvMenu` call. `raw_string` is the input given by the user in order to _get to this node_, so currently empty. The `**kwargs` are all extra keyword arguments passed into `EvMenu`. They can also be passed between nodes. In this case, we passed the keyword `tmp_character` to `EvMenu`. We now have the temporary character sheet available in the node!
An `EvMenu` node must always return two things - `text` and `options`. The `text` is what will
show to the user when looking at this node. The `options` are, well, what options should be
@ -465,8 +444,7 @@ In our `node_chargen` node, we point to three nodes by name: `node_change_name`,
`node_swap_abilities`, and `node_apply_character`. We also make sure to pass along `kwargs`
to each node, since that contains our temporary character sheet.
The middle of these options only appear if we haven't already switched two abilities around - to
know this, we check the `.ability_changes` property to make sure it's still 0.
The middle of these options only appear if we haven't already switched two abilities around - to know this, we check the `.ability_changes` property to make sure it's still 0.
## Node: Changing your name
@ -518,8 +496,7 @@ helper _goto_function_ (`_update_name`) to handle the user's input.
For the (single) option, we use a special `key` named `_default`. This makes this option
a catch-all: If the user enters something that does not match any other option, this is
the option that will be used.
Since we have no other options here, we will always use this option no matter what the user enters.
the option that will be used. Since we have no other options here, we will always use this option no matter what the user enters.
Also note that the `goto` part of the option points to the `_update_name` callable rather than to
the name of a node. It's important we keep passing `kwargs` along to it!
@ -528,9 +505,7 @@ When a user writes anything at this node, the `_update_name` callable will be ca
the same arguments as a node, but it is _not_ a node - we will only use it to _figure out_ which
node to go to next.
In `_update_name` we now have a use for the `raw_string` argument - this is what was written by
the user on the previous node, remember? This is now either an empty string (meaning to ignore
it) or the new name of the character.
In `_update_name` we now have a use for the `raw_string` argument - this is what was written by the user on the previous node, remember? This is now either an empty string (meaning to ignore it) or the new name of the character.
A goto-function like `_update_name` must return the name of the next node to use. It can also
optionally return the `kwargs` to pass into that node - we want to always do this, so we don't
@ -628,14 +603,9 @@ In `_swap_abilities`, we need to analyze the `raw_string` from the user to see w
want to do.
Most code in the helper is validating the user didn't enter nonsense. If they did,
we use `caller.msg()` to tell them and then return `None, kwargs`, which re-runs the same node (the
name-selection) all over again.
we use `caller.msg()` to tell them and then return `None, kwargs`, which re-runs the same node (the name-selection) all over again.
Since we want users to be able to write "CON" instead of the longer "constitution", we need a
mapping `_ABILITIES` to easily convert between the two (it's stored as `consitution` on the
temporary character sheet). Once we know which abilities they want to swap, we do so and tick up
the `.ability_changes` counter. This means this option will no longer be available from the main
node.
Since we want users to be able to write "CON" instead of the longer "constitution", we need a mapping `_ABILITIES` to easily convert between the two (it's stored as `consitution` on the temporary character sheet). Once we know which abilities they want to swap, we do so and tick up the `.ability_changes` counter. This means this option will no longer be available from the main node.
Finally, we return to `node_chargen` again.
@ -658,13 +628,9 @@ node_apply_character(caller, raw_string, **kwargs):
return text, None
```
When entering the node, we will take the Temporary character sheet and use its `.appy` method to
create a new Character with all equipment.
When entering the node, we will take the Temporary character sheet and use its `.appy` method to create a new Character with all equipment.
This is what is called an _end node_, because it returns `None` instead of options. After this,
the menu will exit. We will be back to the default character selection screen. The characters
found on that screen are the ones listed in the `_playable_characters` Attribute, so we need to
also the new character to it.
This is what is called an _end node_, because it returns `None` instead of options. After this, the menu will exit. We will be back to the default character selection screen. The characters found on that screen are the ones listed in the `_playable_characters` Attribute, so we need to also the new character to it.
## Tying the nodes together
@ -692,18 +658,13 @@ This is a start point for spinning up the chargen from a command later.
```
Now that we have all the nodes, we add them to the `menutree` we left empty before. We only add
the nodes, _not_ the goto-helpers! The keys we set in the `menutree` dictionary are the names we
should use to point to nodes from inside the menu (and we did).
Now that we have all the nodes, we add them to the `menutree` we left empty before. We only add the nodes, _not_ the goto-helpers! The keys we set in the `menutree` dictionary are the names we should use to point to nodes from inside the menu (and we did).
We also add a keyword argument `startnode` pointing to the `node_chargen` node. This tells EvMenu
to first jump into that node when the menu is starting up.
We also add a keyword argument `startnode` pointing to the `node_chargen` node. This tells EvMenu to first jump into that node when the menu is starting up.
## Conclusions
This lesson taught us how to use `EvMenu` to make an interactive character generator. In an RPG
more complex than _Knave_, the menu would be bigger and more intricate, but the same principles
apply.
This lesson taught us how to use `EvMenu` to make an interactive character generator. In an RPG more complex than _Knave_, the menu would be bigger and more intricate, but the same principles apply.
Together with the previous lessons we have now fished most of the basics around player
characters - how they store their stats, handle their equipment and how to create them.

View file

@ -0,0 +1,781 @@
# Combat base framework
Combat is core to many games. Exactly how it works is very game-dependent. In this lesson we will build a framework to implement two common flavors:
- "Twitch-based" combat ([specific lesson here](./Beginner-Tutorial-Combat-Twitch.md)) means that you perform a combat action by entering a command, and after some delay (which may depend on your skills etc), the action happens. It's called 'twitch' because actions often happen fast enough that changing your strategy may involve some element of quick thinking and a 'twitchy trigger finger'.
- "Turn-based" combat ([specific lesson here](./Beginner-Tutorial-Combat-Turnbased.md)) means that players input actions in clear turns. Timeout for entering/queuing your actions is often much longer than twitch-based style. Once everyone made their choice (or the timeout is reached), everyone's action happens all at once, after which the next turn starts. This style of combat requires less player reflexes.
We will design a base combat system that supports both styles.
- We need a `CombatHandler` to track the progress of combat. This will be a [Script](../../../Components/Scripts.md). Exactly how this works (and where it is stored) will be a bit different between Twitch- and Turnbased combat. We will create its common framework in this lesson.
- Combat are divided into _actions_. We want to be able to easily extend our combat with more possible actions. An action needs Python code to show what actually happens when the action is performed. We will define such code in `Action` classes.
- We also need a way to describe a _specific instance_ of a given action. That is, when we do an "attack" action, we need at the minimum to know who is being attacked. For this will we use Python `dicts` that we will refer to as `action_dicts`.
## CombatHandler
> Create a new module `evadventure/combat_base.py`
```{sidebar}
In [evennia/contrib/tutorials/evadventure/combat_base.py](evennia.contrib.tutorials.evadventure.combat_base) you'll find a complete implementation of the base combat module.
```
Our "Combat Handler" will handle the administration around combat. It needs to be _persistent_ (even is we reload the server your combat should keep going).
Creating the CombatHandler is a little of a catch-22 - how it works depends on how Actions and Action-dicts look. But without having the CombatHandler, it's hard to know how to design Actions and Action-dicts. So we'll start with its general structure and fill out the details later in this lesson.
Below, methods with `pass` will be filled out this lesson while those raising `NotImplementedError` will be different for Twitch/Turnbased combat and will be implemented in their respective lessons following this one.
```python
# in evadventure/combat_base.py
from evennia import DefaultScript
class CombatFailure(RuntimeError):
"""If some error happens in combat"""
pass
class EvAdventureCombatBaseHandler(DefaultSCript):
"""
This should be created when combat starts. It 'ticks' the combat
and tracks all sides of it.
"""
# common for all types of combat
action_classes = {} # to fill in later
fallback_action_dict = {}
@classmethod
def get_or_create_combathandler(cls, obj, **kwargs):
""" Get or create combathandler on `obj`."""
pass
def msg(self, message, combatant=None, broadcast=True, location=True):
"""
Send a message to all combatants.
"""
pass # TODO
def get_combat_summary(self, combatant):
"""
Get a nicely formatted 'battle report' of combat, from the
perspective of the combatant.
"""
pass # TODO
# implemented differently by Twitch- and Turnbased combat
def get_sides(self, combatant):
"""
Get who's still alive on the two sides of combat, as a
tuple `([allies], [enemies])` from the perspective of `combatant`
(who is _not_ included in the `allies` list.
"""
raise NotImplementedError
def give_advantage(self, recipient, target):
"""
Give advantage to recipient against target.
"""
raise NotImplementedError
def give_disadvantage(self, recipient, target):
"""
Give disadvantage to recipient against target.
"""
raise NotImplementedError
def has_advantage(self, combatant, target):
"""
Does combatant have advantage against target?
"""
raise NotImplementedError
def has_disadvantage(self, combatant, target):
"""
Does combatant have disadvantage against target?
"""
raise NotImplementedError
def queue_action(self, combatant, action_dict):
"""
Queue an action for the combatant by providing
action dict.
"""
raise NotImplementedError
def execute_next_action(self, combatant):
"""
Perform a combatant's next action.
"""
raise NotImplementedError
def start_combat(self):
"""
Start combat.
"""
raise NotImplementedError
def check_stop_combat(self):
"""
Check if the combat is over and if it should be stopped.
"""
raise NotImplementedError
def stop_combat(self):
"""
Stop combat and do cleanup.
"""
raise NotImplementedError
```
The Combat Handler is a [Script](../../../Components/Scripts.md). Scripts are typeclassed entities, which means that they are persistently stored in the database. Scripts can optionally be stored "on" other objects (such as on Characters or Rooms) or be 'global' without any such connection. While Scripts has an optional timer component, it is not active by default and Scripts are commonly used just as plain storage. Since Scripts don't have an in-game existence, they are great for storing data on 'systems' of all kinds, including our combat.
Let's implement the generic methods we need.
### CombatHandler.get_or_create_combathandler
A helper method for quickly getting the combathandler for an ongoing combat and combatant.
We expect to create the script "on" an object (which one we don't know yet, but we expect it to be a typeclassed entity).
```python
# in evadventure/combat_base.py
from evennia import create_script
# ...
class EvAdventureCombatBaseHandler(DefaultScript):
# ...
@classmethod
def get_or_create_combathandler(cls, obj, **kwargs):
"""
Get or create a combathandler on `obj`.
Args:
obj (any): The Typeclassed entity to store this Script on.
Keyword Args:
combathandler_key (str): Identifier for script. 'combathandler' by
default.
**kwargs: Extra arguments to the Script, if it is created.
"""
if not obj:
raise CombatFailure("Cannot start combat without a place to do it!")
combathandler_key = kwargs.pop("key", "combathandler")
combathandler = obj.ndb.combathandler
if not combathandler or not combathandler.id:
combathandler = obj.scripts.get(combathandler_key).first()
if not combathandler:
# have to create from scratch
persistent = kwargs.pop("persistent", True)
combathandler = create_script(
cls,
key=combathandler_key,
obj=obj,
persistent=persistent,
**kwargs,
)
obj.ndb.combathandler = combathandler
return combathandler
# ...
```
This helper method uses `obj.scripts.get()` to find if the combat script already exists 'on' the provided `obj`. If not, it will create it using Evennia's [create_script](evennia.utils.create.create_script) function. For some extra speed we cache the handler as `obj.ndb.combathandler` The `.ndb.` (non-db) means that handler is cached only in memory.
```{sidebar} Checking .id (or .pk)
When getting it from cache, we make sure to also check if the combathandler we got has a database `.id` that is not `None` (we could also check `.pk`, stands for "primary key") . If it's `None`, this means the database entity was deleted and we just got its cached python representation from memory - we need to recreate it.
```
`get_or_create_combathandler` is decorated to be a [classmethod](https://docs.python.org/3/library/functions.html#classmethod), meaning it should be used on the handler class directly (rather than on an _instance_ of said class). This makes sense because this method actually should return the new instance.
As a class method we'll need to call this directly on the class, like this:
```python
combathandler = EvAdventureCombatBaseHandler.get_or_create_combathandler(combatant)
```
The result will be a new handler _or_ one that was already defined.
### CombatHandler.msg
```python
# in evadventure/combat_base.py
# ...
class EvAdventureCombatBaseHandler(DefaultScript):
# ...
def msg(self, message, combatant=None, broadcast=True, location=None):
"""
Central place for sending messages to combatants. This allows
for adding any combat-specific text-decoration in one place.
Args:
message (str): The message to send.
combatant (Object): The 'You' in the message, if any.
broadcast (bool): If `False`, `combatant` must be included and
will be the only one to see the message. If `True`, send to
everyone in the location.
location (Object, optional): If given, use this as the location to
send broadcast messages to. If not, use `self.obj` as that
location.
Notes:
If `combatant` is given, use `$You/you()` markup to create
a message that looks different depending on who sees it. Use
`$You(combatant_key)` to refer to other combatants.
"""
if not location:
location = self.obj
location_objs = location.contents
exclude = []
if not broadcast and combatant:
exclude = [obj for obj in location_objs if obj is not combatant]
location.msg_contents(
message,
exclude=exclude,
from_obj=combatant,
mapping={locobj.key: locobj for locobj in location_objs},
)
# ...
```
```{sidebar}
The `self.obj` property of a Script is the entity on which the Script 'sits'. If set on a Character, `self.obj` will be that Character. If on a room, it'd be that room. For a global script, `self.obj` is `None`.
```
We saw the `location.msg_contents()` method before in the [Weapon class of the Objects lesson](./Beginner-Tutorial-Objects.md#weapons). Its purpose is to take a string on the form `"$You() do stuff against $you(key)"` and make sure all sides see a string suitable just to them. Our `msg()` method will by default broadcast the message to everyone in the room.
<div style="clear: right;"></div>
You'd use it like this:
```python
combathandler.msg(
f"$You() $conj(throw) {item.key} at $you({target.key}).",
combatant=combatant,
location=combatant.location
)
```
If combatant is `Trickster`, `item.key` is "a colorful ball" and `target.key` is "Goblin", then
The combatant would see:
You throw a colorful ball at Goblin.
The Goblin sees
Trickster throws a colorful ball at you.
Everyone else in the room sees
Trickster throws a colorful ball at Goblin.
### Combathandler.get_combat_summary
We want to be able to show a nice summary of the current combat:
```shell
Goblin shaman (Perfect)
Gregor (Hurt) Goblin brawler(Hurt)
Bob (Perfect) vs Goblin grunt 1 (Hurt)
Goblin grunt 2 (Perfect)
Goblin grunt 3 (Wounded)
```
```{code-block} python
:linenos:
:emphasize-lines: 15,17,21,22,28,41
# in evadventure/combat_base.py
# ...
from evennia import EvTable
# ...
class EvAdventureCombatBaseHandler(DefaultScript):
# ...
def get_combat_summary(self, combatant):
allies, enemies = self.get_sides(combatant)
# we must include outselves at the top of the list (we are not returned from get_sides)
allies.insert(0, combatant)
nallies, nenemies = len(allies), len(enemies)
# prepare colors and hurt-levels
allies = [f"{ally} ({ally.hurt_level})" for ally in allies]
enemies = [f"{enemy} ({enemy.hurt_level})" for enemy in enemies]
# the center column with the 'vs'
vs_column = ["" for _ in range(max(nallies, nenemies))]
vs_column[len(vs_column) // 2] = "|wvs|n"
# the two allies / enemies columns should be centered vertically
diff = abs(nallies - nenemies)
top_empty = diff // 2
bot_empty = diff - top_empty
topfill = ["" for _ in range(top_empty)]
botfill = ["" for _ in range(bot_empty)]
if nallies >= nenemies:
enemies = topfill + enemies + botfill
else:
allies = topfill + allies + botfill
# make a table with three columns
return evtable.EvTable(
table=[
evtable.EvColumn(*allies, align="l"),
evtable.EvColumn(*vs_column, align="c"),
evtable.EvColumn(*enemies, align="r"),
],
border=None,
maxwidth=78,
)
# ...
```
This may look complex, but the complexity is only in figuring out how to organize three columns, especially how to to adjust to the two sides on each side of the `vs` are roughly vertically aligned.
- **Line 15** : We make use of the `self.get_sides(combatant)` method which we haven't actually implemented yet. This is because turn-based and twitch-based combat will need different ways to find out what the sides are. The `allies` and `enemies` are lists.
- **Line 17**: The `combatant` is not a part of the `allies` list (this is how we defined `get_sides` to work), so we insert it at the top of the list (so they show first on the left-hand side).
- **Lines 21, 22**: We make use of the `.hurt_level` values of all living things (see the [LivingMixin of the Character lesson](./Beginner-Tutorial-Characters.md)).
- **Lines 28-39**: We determine how to vertically center the two sides by adding empty lines above and below the content.
- **Line 41**: The [Evtable](../../../Components/EvTable.md) is an Evennia utility for making, well, text tables. Once we are happy with the columns, we feed them to the table and let Evennia do the rest. It's worth to explore `EvTable` since it can help you create all sorts of nice layouts.
## Actions
In EvAdventure we will only support a few common combat actions, mapping to the equivalent rolls and checks used in _Knave_. We will design our combat framework so that it's easy to expand with other actions later.
- `hold` - The simplest action. You just lean back and do nothing.
- `attack` - You attack a given `target` using your currently equipped weapon. This will become a roll of STR or WIS against the targets' ARMOR.
- `stunt` - You make a 'stunt', which in roleplaying terms would mean you tripping your opponent, taunting or otherwise trying to gain the upper hand without hurting them. You can do this to give yourself (or an ally) _advantage_ against a `target` on the next action. You can also give a `target` _disadvantage_ against you or an ally for their next action.
- `use item` - You make use of a `Consumable` in your inventory. When used on yourself, it'd normally be something like a healing potion. If used on an enemy it could be a firebomb or a bottle of acid.
- `wield` - You wield an item. Depending on what is being wielded, it will be wielded in different ways: A helmet will be placed on the head, a piece of armor on the chest. A sword will be wielded in one hand, a shield in another. A two-handed axe will use up two hands. Doing so will move whatever was there previously to the backpack.
- `flee` - You run away/disengage. This action is only applicable in turn-based combat (in twitch-based combat you just move to another room to flee). We will thus wait to define this action until the [Turnbased combat lesson](./Beginner-Tutorial-Combat-Turnbased.md).
## Action dicts
To pass around the details of an attack (the second point above), we will use a `dict`. A `dict` is simple and also easy to save in an `Attribute`. We'll call this the `action_dict` and here's what we need for each action.
> You don't need to type these out anywhere, it's listed here for reference. We will use these dicts when calling `combathandler.queue_action(combatant, action_dict)`.
```python
hold_action_dict = {
"key": "hold"
}
attack_action_dict = {
"key": "attack",
"target": <Character/NPC>
}
stunt_action_dict = {
"key": "stunt",
"recipient": <Character/NPC>, # who gains advantage/disadvantage
"target": <Character/NPC>, # who the recipient gainst adv/dis against
"advantage": bool, # grant advantage or disadvantage?
"stunt_type": Ability, # Ability to use for the challenge
"defense_type": Ability, # what Ability for recipient to defend with if we
# are trying to give disadvantage
}
use_item_action_dict = {
"key": "use",
"item": <Object>
"target": <Character/NPC/None> # if using item against someone else
}
wield_action_dict = {
"key": "wield",
"item": <Object>
}
# used only for the turnbased combat, so its Action will be defined there
flee_action_dict = {
"key": "flee"
}
```
Apart from the `stunt` action, these dicts are all pretty simple. The `key` identifes the action to perform and the other fields identifies the minimum things you need to know in order to resolve each action.
We have not yet written the code to set these dicts, but we will assume that we know who is performing each of these actions. So if `Beowulf` attacks `Grendel`, Beowulf is not himself included in the attack dict:
```python
attack_action_dict = {
"key": "attack",
"target": Grendel
}
```
Let's explain the longest action dict, the `Stunt` action dict in more detail as well. In this example, The `Trickster` is performing a _Stunt_ in order to help his friend `Paladin` to gain an INT- _advantage_ against the `Goblin` (maybe the paladin is preparing to cast a spell of something). Since `Trickster` is doing the action, he's not showing up in the dict:
```python
stunt_action_dict - {
"key": "stunt",
"recipient": Paladin,
"target": Goblin,
"advantage": True,
"stunt_type": Ability.INT,
"defense_type": Ability.INT,
}
```
```{sidebar}
In EvAdventure, we'll always set `stunt_type == defense_type` for simplicity. But you could also consider mixing things up so you could use DEX to confuse someone and give them INT disadvantage, for example.
```
This should result in an INT vs INT based check between the `Trickster` and the `Goblin` (maybe the trickster is trying to confuse the goblin with some clever word play). If the `Trickster` wins, the `Paladin` gains advantage against the Goblin on the `Paladin`'s next action .
## Action classes
Once our `action_dict` identifies the particular action we should use, we need something that reads those keys/values and actually _performs_ the action.
```python
# in evadventure/combat_base.py
class CombatAction:
def __init__(self, combathandler, combatant, action_dict):
self.combathandler = combathandler
self.combatant = combatant
for key, val in action_dict.items();
if key.startswith("_"):
setattr(self, key, val)
```
We will create a new instance of this class _every time an action is happening_. So we store some key things every action will need - we will need a reference to the common `combathandler` (which we will design in the next section), and to the `combatant` (the one performing this action). The `action_dict` is a dict matching the action we want to perform.
The `setattr` Python standard function assigns the keys/values of the `action_dict` to be properties "on" this action. This is very convenient to use in other methods. So for the `stunt` action, other methods could just access `self.key`, `self.recipient`, `self.target` and so on directly.
```python
# in evadventure/combat_base.py
class CombatAction:
# ...
def msg(self, message, broadcast=True):
"Send message to others in combat"
self.combathandler.msg(message, combatant=self.combatant, broadcast=broadcast)
def can_use(self):
"""Return False if combatant can's use this action right now"""
return True
def execute(self):
"""Does the actional action"""
pass
def post_execute(self):
"""Called after `execute`"""
pass
```
It's _very_ common to want to send messages to everyone in combat - you need to tell people they are getting attacked, if they get hurt and so on. So having a `msg` helper method on the action is convenient. We offload all the complexity to the combathandler.msg() method.
The `can_use`, `execute` and `post_execute` should all be called in a chain and we should make sure the `combathandler` calls them like this:
```python
if action.can_use():
action.execute()
action.post_execute()
```
### Hold Action
```python
# in evadventure/combat_base.py
# ...
class CombatActionHold(CombatAction):
"""
Action that does nothing
action_dict = {
"key": "hold"
}
"""
```
Holding does nothing but it's cleaner to nevertheless have a separate class for it. We use the docstring to specify how its action-dict should look.
### Attack Action
```python
# in evadventure/combat_base.py
# ...
class CombatActionAttack(CombatAction):
"""
A regular attack, using a wielded weapon.
action-dict = {
"key": "attack",
"target": Character/Object
}
"""
def execute(self):
attacker = self.combatant
weapon = attacker.weapon
target = self.target
if weapon.at_pre_use(attacker, target):
weapon.use(
attacker, target, advantage=self.combathandler.has_advantage(attacker, target)
)
weapon.at_post_use(attacker, target)
```
Refer to how we [designed Evadventure weapons](./Beginner-Tutorial-Objects.md#weapons) to understand what happens here - most of the work is performed by the weapon class - we just plug in the relevant arguments.
### Stunt Action
```python
# in evadventure/combat_base.py
# ...
class CombatActionStunt(CombatAction):
"""
Perform a stunt the grants a beneficiary (can be self) advantage on their next action against a
target. Whenever performing a stunt that would affect another negatively (giving them
disadvantage against an ally, or granting an advantage against them, we need to make a check
first. We don't do a check if giving an advantage to an ally or ourselves.
action_dict = {
"key": "stunt",
"recipient": Character/NPC,
"target": Character/NPC,
"advantage": bool, # if False, it's a disadvantage
"stunt_type": Ability, # what ability (like STR, DEX etc) to use to perform this stunt.
"defense_type": Ability, # what ability to use to defend against (negative) effects of
this stunt.
}
"""
def execute(self):
combathandler = self.combathandler
attacker = self.combatant
recipient = self.recipient # the one to receive the effect of the stunt
target = self.target # the affected by the stunt (can be the same as recipient/combatant)
txt = ""
if recipient == target:
# grant another entity dis/advantage against themselves
defender = recipient
else:
# recipient not same as target; who will defend depends on disadvantage or advantage
# to give.
defender = target if self.advantage else recipient
# trying to give advantage to recipient against target. Target defends against caller
is_success, _, txt = rules.dice.opposed_saving_throw(
attacker,
defender,
attack_type=self.stunt_type,
defense_type=self.defense_type,
advantage=combathandler.has_advantage(attacker, defender),
disadvantage=combathandler.has_disadvantage(attacker, defender),
)
self.msg(f"$You() $conj(attempt) stunt on $You({defender.key}). {txt}")
# deal with results
if is_success:
if self.advantage:
combathandler.give_advantage(recipient, target)
else:
combathandler.give_disadvantage(recipient, target)
if recipient == self.combatant:
self.msg(
f"$You() $conj(gain) {'advantage' if self.advantage else 'disadvantage'} "
f"against $You({target.key})!"
)
else:
self.msg(
f"$You() $conj(cause) $You({recipient.key}) "
f"to gain {'advantage' if self.advantage else 'disadvantage'} "
f"against $You({target.key})!"
)
self.msg(
"|yHaving succeeded, you hold back to plan your next move.|n [hold]",
broadcast=False,
)
else:
self.msg(f"$You({defender.key}) $conj(resist)! $You() $conj(fail) the stunt.")
```
The main action here is the call to the `rules.dice.opposed_saving_throw` to determine if the stunt succeeds. After that, most lines is about figuring out who should be given advantage/disadvantage and to communicate the result to the affected parties.
Note that we make heavy use of the helper methods on the `combathandler` here, even those that are not yet implemented. As long as we pass the `action_dict` into the `combathandler`, the action doesn't actually care what happens next.
After we have performed a successful stunt, we queue the `combathandler.fallback_action_dict`. This is because stunts are meant to be one-off things are if we are repeating actions, it would not make sense to repeat the stunt over and over.
### Use Item Action
```python
# in evadventure/combat_base.py
# ...
class CombatActionUseItem(CombatAction):
"""
Use an item in combat. This is meant for one-off or limited-use items (so things like scrolls and potions, not swords and shields). If this is some sort of weapon or spell rune, we refer to the item to determine what to use for attack/defense rolls.
action_dict = {
"key": "use",
"item": Object
"target": Character/NPC/Object/None
}
"""
def execute(self):
item = self.item
user = self.combatant
target = self.target
if item.at_pre_use(user, target):
item.use(
user,
target,
advantage=self.combathandler.has_advantage(user, target),
disadvantage=self.combathandler.has_disadvantage(user, target),
)
item.at_post_use(user, target)
```
See the [Consumable items in the Object lesson](./Beginner-Tutorial-Objects.md) to see how consumables work. Like with weapons, we offload all the logic to the item we use.
### Wield Action
```python
# in evadventure/combat_base.py
# ...
class CombatActionWield(CombatAction):
"""
Wield a new weapon (or spell) from your inventory. This will
swap out the one you are currently wielding, if any.
action_dict = {
"key": "wield",
"item": Object
}
"""
def execute(self):
self.combatant.equipment.move(self.item)
```
We rely on the [Equipment handler](./Beginner-Tutorial-Equipment.md) we created to handle the swapping of items for us. Since it doesn't make sense to keep swapping over and over, we queue the fallback action after this one.
## Testing
> Create a module `evadventure/tests/test_combat.py`.
```{sidebar}
See [evennia/contrib/tutorials/evadventure/tests/test_combat.py](evennia.contrib.tutorials.evadventure.tests.test_combat) for ready-made combat unit tests.
```
Unit testing the combat base classes can seem impossible because we have not yet implemented most of it. We can however get very far by the use of [Mocks](https://docs.python.org/3/library/unittest.mock.html). The idea of a Mock is that you _replace_ a piece of code with a dummy object (a 'mock') that can be called to return some specific value.
For example, consider this following test of the `CombatHandler.get_combat_summary`. We can't just call this because it internally calls `.get_sides` which would raise a `NotImplementedError`.
```{code-block} python
:linenos:
:emphasize-lines: 25,32
# in evadventure/tests/test_combat.py
from unittest.mock import Mock
from evennia.utils.test_resources import EvenniaTestCase
from evennia import create_object
from .. import combat_base
from ..rooms import EvAdventureRoom
from ..characters import EvAdventureCharacter
class TestEvAdventureCombatBaseHandler(EvenniaTestCase):
def setUp(self):
self.location = create_object(EvAdventureRoom, key="testroom")
self.combatant = create_object(EvAdventureCharacter, key="testchar")
self.target = create_object(EvAdventureMob, key="testmonster")
self.combathandler = combat_base.get_combat_summary(self.location)
def test_get_combat_summary(self):
# do the test from perspective of combatant
self.combathandler.get_sides = Mock(return_value=([], [self.target]))
result = str(self.combathandler.get_combat_summary(self.combatant))
self.assertEqual(
result,
" testchar (Perfect) vs testmonster (Perfect)"
)
# test from the perspective of the monster
self.combathandler.get_sides = Mock(return_value=([], [self.combatant]))
result = str(self.combathandler.get_combat_summary(self.target))
self.assertEqual(
result,
" testmonster (Perfect) vs testchar (Perfect)"
)
```
The interesting places are where we apply the mocks:
- **Line 25** and **Line 32**: While `get_sides` is not implemented yet, we know what it is _supposed_ to return - a tuple of lists. So for the sake of the test, we _replace_ the `get_sides` method with a mock that when called will return something useful.
With this kind of approach it's possible to fully test a system also when it's not 'complete' yet.
## Conclusions
We have the core functionality we need for our combat system! In the following two lessons we will make use of these building blocks to create two styles of combat.

View file

@ -1,11 +1,8 @@
# Handling Equipment
In _Knave_, you have a certain number of inventory "slots". The amount of slots is given by `CON + 10`.
All items (except coins) have a `size`, indicating how many slots it uses. You can't carry more items
than you have slot-space for. Also items wielded or worn count towards the slots.
In _Knave_, you have a certain number of inventory "slots". The amount of slots is given by `CON + 10`. All items (except coins) have a `size`, indicating how many slots it uses. You can't carry more items than you have slot-space for. Also items wielded or worn count towards the slots.
We still need to track what the character is using however: What weapon they have readied affects the damage
they can do. The shield, helmet and armor they use affects their defense.
We still need to track what the character is using however: What weapon they have readied affects the damage they can do. The shield, helmet and armor they use affects their defense.
We have already set up the possible 'wear/wield locations' when we defined our Objects
[in the previous lesson](./Beginner-Tutorial-Objects.md). This is what we have in `enums.py`:
@ -25,8 +22,7 @@ class WieldLocation(Enum):
HEAD = "head" # helmets
```
Basically, all the weapon/armor locations are exclusive - you can only have one item in each (or none).
The BACKPACK is special - it contains any number of items (up to the maximum slot usage).
Basically, all the weapon/armor locations are exclusive - you can only have one item in each (or none). The BACKPACK is special - it contains any number of items (up to the maximum slot usage).
## EquipmentHandler that saves
@ -36,12 +32,9 @@ The BACKPACK is special - it contains any number of items (up to the maximum slo
If you want to understand more about behind how Evennia uses handlers, there is a
[dedicated tutorial](../../Tutorial-Persistent-Handler.md) talking about the principle.
```
In default Evennia, everything you pick up will end up "inside" your character object (that is, have
you as its `.location`). This is called your _inventory_ and has no limit. We will keep 'moving items into us'
when we pick them up, but we will add more functionality using an _Equipment handler_.
In default Evennia, everything you pick up will end up "inside" your character object (that is, have you as its `.location`). This is called your _inventory_ and has no limit. We will keep 'moving items into us' when we pick them up, but we will add more functionality using an _Equipment handler_.
A handler is (for our purposes) an object that sits "on" another entity, containing functionality
for doing one specific thing (managing equipment, in our case).
A handler is (for our purposes) an object that sits "on" another entity, containing functionality for doing one specific thing (managing equipment, in our case).
This is the start of our handler:
@ -104,35 +97,23 @@ After reloading the server, the equipment-handler will now be accessible on char
character.equipment
The `@lazy_property` works such that it will not load the handler until someone actually tries to
fetch it with `character.equipment`. When that
happens, we start up the handler and feed it `self` (the `Character` instance itself). This is what
enters `__init__` as `.obj` in the `EquipmentHandler` code above.
The `@lazy_property` works such that it will not load the handler until someone actually tries to fetch it with `character.equipment`. When that happens, we start up the handler and feed it `self` (the `Character` instance itself). This is what enters `__init__` as `.obj` in the `EquipmentHandler` code above.
So we now have a handler on the character, and the handler has a back-reference to the character it sits
on.
So we now have a handler on the character, and the handler has a back-reference to the character it sits on.
Since the handler itself is just a regular Python object, we need to use the `Character` to store
our data - our _Knave_ "slots". We must save them to the database, because we want the server to remember
them even after reloading.
our data - our _Knave_ "slots". We must save them to the database, because we want the server to remember them even after reloading.
Using `self.obj.attributes.add()` and `.get()` we save the data to the Character in a specially named
[Attribute](../../../Components/Attributes.md). Since we use a `category`, we are unlikely to collide with
Using `self.obj.attributes.add()` and `.get()` we save the data to the Character in a specially named [Attribute](../../../Components/Attributes.md). Since we use a `category`, we are unlikely to collide with
other Attributes.
Our storage structure is a `dict` with keys after our available `WieldLocation` enums. Each can only
have one item except `WieldLocation.BACKPACK`, which is a list.
Our storage structure is a `dict` with keys after our available `WieldLocation` enums. Each can only have one item except `WieldLocation.BACKPACK`, which is a list.
## Connecting the EquipmentHandler
Whenever an object leaves from one location to the next, Evennia will call a set of _hooks_ (methods) on the
object that moves, on the source-location and on its destination. This is the same for all moving things -
whether it's a character moving between rooms or an item being dropping from your hand to the ground.
Whenever an object leaves from one location to the next, Evennia will call a set of _hooks_ (methods) on the object that moves, on the source-location and on its destination. This is the same for all moving things - whether it's a character moving between rooms or an item being dropping from your hand to the ground.
We need to tie our new `EquipmentHandler` into this system. By reading the doc page on [Objects](../../../Components/Objects.md),
or looking at the [DefaultObject.move_to](evennia.objects.objects.DefaultObject.move_to) docstring, we'll
find out what hooks Evennia will call. Here `self` is the object being moved from
`source_location` to `destination`:
We need to tie our new `EquipmentHandler` into this system. By reading the doc page on [Objects](../../../Components/Objects.md), or looking at the [DefaultObject.move_to](evennia.objects.objects.DefaultObject.move_to) docstring, we'll find out what hooks Evennia will call. Here `self` is the object being moved from `source_location` to `destination`:
1. `self.at_pre_move(destination)` (abort if return False)
@ -145,9 +126,7 @@ find out what hooks Evennia will call. Here `self` is the object being moved fro
8. `destination.at_object_receive(self, source_location)`
9. `self.at_post_move(source_location)`
All of these hooks can be overridden to customize movement behavior. In this case we are interested in
controlling how items 'enter' and 'leave' our character - being 'inside' the character is the same as
them 'carrying' it. We have three good hook-candidates to use for this.
All of these hooks can be overridden to customize movement behavior. In this case we are interested in controlling how items 'enter' and 'leave' our character - being 'inside' the character is the same as them 'carrying' it. We have three good hook-candidates to use for this.
- `.at_pre_object_receive` - used to check if you can actually pick something up, or if your equipment-store is full.
- `.at_object_receive` - used to add the item to the equipmenthandler
@ -187,16 +166,13 @@ class EvAdventureCharacter(LivingMixin, DefaultCharacter):
self.equipment.remove(moved_object)
```
Above we have assumed the `EquipmentHandler` (`.equipment`) has methods `.validate_slot_usage`,
`.add` and `.remove`. But we haven't actually added them yet - we just put some reasonable names! Before
we can use this, we need to go actually adding those methods.
Above we have assumed the `EquipmentHandler` (`.equipment`) has methods `.validate_slot_usage`, `.add` and `.remove`. But we haven't actually added them yet - we just put some reasonable names! Before we can use this, we need to go actually adding those methods.
## Expanding the Equipmenthandler
## `.validate_slot_usage`
Let's start with implementing the first method we came up with above, `validate_slot_usage`:
```python
# mygame/evadventure/equipment.py
@ -249,15 +225,11 @@ The `@property` decorator turns a method into a property so you don't need to 'c
That is, you can access `.max_slots` instead of `.max_slots()`. In this case, it's just a
little less to type.
```
We add two helpers - the `max_slots` _property_ and `count_slots`, a method that calculate the current
slots being in use. Let's figure out how they work.
We add two helpers - the `max_slots` _property_ and `count_slots`, a method that calculate the current slots being in use. Let's figure out how they work.
### `.max_slots`
For `max_slots`, remember that `.obj` on the handler is a back-reference to the `EvAdventureCharacter` we
put this handler on. `getattr` is a Python method for retrieving a named property on an object.
The `Enum` `Ability.CON.value` is the string `Constitution` (check out the
[first Utility and Enums tutorial](./Beginner-Tutorial-Utilities.md) if you don't recall).
For `max_slots`, remember that `.obj` on the handler is a back-reference to the `EvAdventureCharacter` we put this handler on. `getattr` is a Python method for retrieving a named property on an object. The `Enum` `Ability.CON.value` is the string `Constitution` (check out the [first Utility and Enums tutorial](./Beginner-Tutorial-Utilities.md) if you don't recall).
So to be clear,
@ -276,24 +248,18 @@ which is the same as doing something like this:
your_character.Constitution + 10
```
In our code we write `getattr(self.obj, Ability.CON.value, 1)` - that extra `1` means that if there
should happen to _not_ be a property "Constitution" on `self.obj`, we should not error out but just
return 1.
In our code we write `getattr(self.obj, Ability.CON.value, 1)` - that extra `1` means that if there should happen to _not_ be a property "Constitution" on `self.obj`, we should not error out but just return 1.
### `.count_slots`
In this helper we use two Python tools - the `sum()` function and a
[list comprehension](https://www.w3schools.com/python/python_lists_comprehension.asp). The former
simply adds the values of any iterable together. The latter is a more efficient way to create a list:
In this helper we use two Python tools - the `sum()` function and a [list comprehension](https://www.w3schools.com/python/python_lists_comprehension.asp). The former simply adds the values of any iterable together. The latter is a more efficient way to create a list:
new_list = [item for item in some_iterable if condition]
all_above_5 = [num for num in range(10) if num > 5] # [6, 7, 8, 9]
all_below_5 = [num for num in range(10) if num < 5] # [0, 1, 2, 3, 4]
To make it easier to understand, try reading the last line above as "for every number in the range 0-9,
pick all with a value below 5 and make a list of them". You can also embed such comprehensions
directly in a function call like `sum()` without using `[]` around it.
To make it easier to understand, try reading the last line above as "for every number in the range 0-9, pick all with a value below 5 and make a list of them". You can also embed such comprehensions directly in a function call like `sum()` without using `[]` around it.
In `count_slots` we have this code:
@ -305,10 +271,7 @@ wield_usage = sum(
)
```
We should be able to follow all except `slots.items()`. Since `slots` is a `dict`, we can use `.items()`
to get a sequence of `(key, value)` pairs. We store these in `slot` and `slotobj`. So the above can
be understood as "for every `slot` and `slotobj`-pair in `slots`, check which slot location it is.
If it is _not_ in the backpack, get its size and add it to the list. Sum over all these
We should be able to follow all except `slots.items()`. Since `slots` is a `dict`, we can use `.items()` to get a sequence of `(key, value)` pairs. We store these in `slot` and `slotobj`. So the above can be understood as "for every `slot` and `slotobj`-pair in `slots`, check which slot location it is. If it is _not_ in the backpack, get its size and add it to the list. Sum over all these
sizes".
A less compact but maybe more readonable way to write this would be:
@ -327,14 +290,11 @@ together.
### Validating slots
With these helpers in place, `validate_slot_usage` now becomes simple. We use `max_slots` to see how much we can carry.
We then get how many slots we are already using (with `count_slots`) and see if our new `obj`'s size
would be too much for us.
With these helpers in place, `validate_slot_usage` now becomes simple. We use `max_slots` to see how much we can carry. We then get how many slots we are already using (with `count_slots`) and see if our new `obj`'s size would be too much for us.
## `.add` and `.remove`
We will make it so `.add` puts something in the `BACKPACK` location and `remove` drops it, wherever
it is (even if it was in your hands).
We will make it so `.add` puts something in the `BACKPACK` location and `remove` drops it, wherever it is (even if it was in your hands).
```python
# mygame/evadventure/equipment.py
@ -381,14 +341,11 @@ In `.delete`, we allow emptying by `WieldLocation` - we figure out what slot it
the item within (if any). If we gave `BACKPACK` as the slot, we empty the backpack and
return all items.
Whenever we change the equipment loadout we must make sure to `._save()` the result, or it will
be lost after a server reload.
Whenever we change the equipment loadout we must make sure to `._save()` the result, or it will be lost after a server reload.
## Moving things around
With the help of `.remove()` and `.add()` we can get things in and out of the `BACKPACK` equipment
location. We also need to grab stuff from the backpack and wield or wear it. We add a `.move` method
on the `EquipmentHandler` to do this:
With the help of `.remove()` and `.add()` we can get things in and out of the `BACKPACK` equipment location. We also need to grab stuff from the backpack and wield or wear it. We add a `.move` method on the `EquipmentHandler` to do this:
```python
# mygame/evadventure/equipment.py
@ -437,9 +394,7 @@ class EquipmentHandler:
self._save()
```
Here we remember that every `EvAdventureObject` has an `inventory_use_slot` property that tells us where
it goes. So we just need to move the object to that slot, replacing whatever is in that place
from before. Anything we replace goes back to the backpack.
Here we remember that every `EvAdventureObject` has an `inventory_use_slot` property that tells us where it goes. So we just need to move the object to that slot, replacing whatever is in that place from before. Anything we replace goes back to the backpack.
## Get everything
@ -477,16 +432,14 @@ Here we get all the equipment locations and add their contents together into a l
## Weapon and armor
It's convenient to have the `EquipmentHandler` easily tell you what weapon is currently wielded
and what _armor_ level all worn equipment provides. Otherwise you'd need to figure out what item is
in which wield-slot and to add up armor slots manually every time you need to know.
It's convenient to have the `EquipmentHandler` easily tell you what weapon is currently wielded and what _armor_ level all worn equipment provides. Otherwise you'd need to figure out what item is in which wield-slot and to add up armor slots manually every time you need to know.
```python
# mygame/evadventure/equipment.py
from .objects import WeaponEmptyHand
from .enums import WieldLocation, Ability
from .objects import get_empty_hand
# ...
@ -516,19 +469,16 @@ class EquipmentHandler:
weapon = slots[WieldLocation.TWO_HANDS]
if not weapon:
weapon = slots[WieldLocation.WEAPON_HAND]
# if we still don't have a weapon, we return None here
if not weapon:
weapon = WeaponEmptyHand()
~ weapon = get_bare_hands()
return weapon
```
In the `.armor()` method we get the item (if any) out of each relevant wield-slot (body, shield, head),
and grab their `armor` Attribute. We then `sum()` them all up.
In the `.armor()` method we get the item (if any) out of each relevant wield-slot (body, shield, head), and grab their `armor` Attribute. We then `sum()` them all up.
In `.weapon()`, we simply check which of the possible weapon slots (weapon-hand or two-hands) have
something in them. If not we fall back to the 'fake' weapon `WeaponEmptyHand` which is just a 'dummy'
object that represents your bare hands with damage and all.
(created in [The Object tutorial](./Beginner-Tutorial-Objects.md#your-bare-hands) earlier).
In `.weapon()`, we simply check which of the possible weapon slots (weapon-hand or two-hands) have something in them. If not we fall back to the 'Bare Hands' object we created in the [Object tutorial lesson](./Beginner-Tutorial-Objects.md#your-bare-hands) earlier.
## Extra credits
@ -589,11 +539,8 @@ class TestEquipment(BaseEvenniaTest):
## Summary
_Handlers_ are useful for grouping functionality together. Now that we spent our time making the
`EquipmentHandler`, we shouldn't need to worry about item-slots anymore - the handler 'handles' all
the details for us. As long as we call its methods, the details can be forgotten about.
_Handlers_ are useful for grouping functionality together. Now that we spent our time making the `EquipmentHandler`, we shouldn't need to worry about item-slots anymore - the handler 'handles' all the details for us. As long as we call its methods, the details can be forgotten about.
We also learned to use _hooks_ to tie _Knave_'s custom equipment handling into Evennia.
With `Characters`, `Objects` and now `Equipment` in place, we should be able to move on to character
generation - where players get to make their own character!
With `Characters`, `Objects` and now `Equipment` in place, we should be able to move on to character generation - where players get to make their own character!

View file

@ -1,5 +1,137 @@
# Non-Player-Characters (NPCs)
# Non-Player-Characters
```{warning}
This part of the Beginner tutorial is still being developed.
```
NPCs are all active agents that are not player characters. NPCs could be anything from merchants and quest givers, to monsters and bosses. They could also be 'flavor' - townsfolk doing their chores, farmers tending their fields - there to make the world feel "more alive".
```{sidebar} vNPCs
You should usually avoid creating hundreds of NPC objects to populate your 'busy town' - in a text game so many NPCs will just spam the screen and annoy your players. Since this is a text game, you can usually get away with using _vNPcs_ - virtual NPCs. vNPCs are only described in text - a room could be described as a bustling street, farmers can be described shouting to each other. Using room descriptions for this works well, but the tutorial lesson about [EvAdventure Rooms](./Beginner-Tutorial-Rooms.md) has a section called [adding life to a room](./Beginner-Tutorial-Rooms.md#adding-life-to-a-room) that can be used for making vNPCs appear to do things in the background.
```
In this lesson we will create the base class of _Knave_ NPCs. According to the _Knave_ rules, NPCs have some simplified stats compared to the [PC characters](./Beginner-Tutorial-Characters.md) we designed earlier:
## The NPC base class
> Create a new module `evadventure/npcs.py`.
```{code-block} python
:linenos:
:emphasize-lines: 9, 12, 13, 15, 17, 19, 25, 23, 59, 61
# in evadventure/npcs.py
from evennia import DefaultCharacter, AttributeProperty
from .characters import LivingMixin
from .enums import Ability
class EvAdventureNPC(LivingMixin, DefaultCharacter):
"""Base class for NPCs"""
is_pc = False
hit_dice = AttributeProperty(default=1, autocreate=False)
armor = AttributeProperty(default=1, autocreate=False) # +10 to get armor defense
hp_multiplier = AttributeProperty(default=4, autocreate=False) # 4 default in Knave
hp = AttributeProperty(default=None, autocreate=False) # internal tracking, use .hp property
morale = AttributeProperty(default=9, autocreate=False)
allegiance = AttributeProperty(default=Ability.ALLEGIANCE_HOSTILE, autocreate=False)
weapon = AttributeProperty(default=BARE_HANDS, autocreate=False) # instead of inventory
coins = AttributeProperty(default=1, autocreate=False) # coin loot
is_idle = AttributeProperty(default=False, autocreate=False)
@property
def strength(self):
return self.hit_dice
@property
def dexterity(self):
return self.hit_dice
@property
def constitution(self):
return self.hit_dice
@property
def intelligence(self):
return self.hit_dice
@property
def wisdom(self):
return self.hit_dice
@property
def charisma(self):
return self.hit_dice
@property
def hp_max(self):
return self.hit_dice * self.hp_multiplier
def at_object_creation(self):
"""
Start with max health.
"""
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
```
- **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.
- **Line 12**: The `is_pc` is a quick and convenient way to check if this is, well, a PC or not. We will use it in the upcoming [Combat base lesson](./Beginner-Tutorial-Combat-Base.md).
- **Line 13**: The NPC is simplified in that all stats are just based on the `Hit dice` number (see **Lines 25-51**). We store `armor` and a `weapon` as direct [Attributes](../../../Components/Attributes.md) on the class rather than bother implementing a full equipment system.
- **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.
## Testing
> Create a new module `evadventure/tests/test_npcs.py`
Not so much to test yet, but we will be using the same module to test other aspects of NPCs in the future, so let's create it now.
```python
# in evadventure/tests/test_npcs.py
from evennia import create_object
from evennia.utils.test_resources import EvenniaTest
from .. import npcs
class TestNPCBase(EvenniaTest):
"""Test the NPC base class"""
def test_npc_base(self):
npc = create_object(
npcs.EvAdventureNPC,
key="TestNPC",
attributes=[("hit_dice", 4)], # set hit_dice to 4
)
self.assertEqual(npc.hp_multiplier, 4)
self.assertEqual(npc.hp, 16)
self.assertEqual(npc.strength, 4)
self.assertEqual(npc.charisma, 4)
```
Nothing special here. Note how the `create_object` helper function takes `attributes` as a keyword. This is a list of tuples we use to set different values than the default ones to Attributes. We then check a few of the properties to make sure they return what we expect.
## Conclusions
In _Knave_, an NPC is a simplified version of a Player Character. In other games and rule systems, they may be all but identical.
With the NPC class in place, we have enough to create a 'test dummy'. Since it has no AI yet, it won't fight back, but it will be enough to have something to hit when we test our combat in the upcoming lessons.

View file

@ -7,9 +7,7 @@ Looking at _Knave_'s item lists, we can get some ideas of what we need to track:
- `size` - this is how many 'slots' the item uses in the character's inventory.
- `value` - a base value if we want to sell or buy the item.
- `inventory_use_slot` - some items can be worn or wielded. For example, a helmet needs to be
worn on the head and a shield in the shield hand. Some items can't be used this way at all, but
only belong in the backpack.
- `inventory_use_slot` - some items can be worn or wielded. For example, a helmet needs to be worn on the head and a shield in the shield hand. Some items can't be used this way at all, but only belong in the backpack.
- `obj_type` - Which 'type' of item this is.
@ -57,8 +55,7 @@ a full set of objects implemented.
```
<div style="clear: right;"></div>
We will make a base `EvAdventureObject` class off Evennia's standard `DefaultObject`. We will then add
child classes to represent the relevant types:
We will make a base `EvAdventureObject` class off Evennia's standard `DefaultObject`. We will then add child classes to represent the relevant types:
```python
# mygame/evadventure/objects.py
@ -81,14 +78,42 @@ class EvAdventureObject(DefaultObject):
# this can be either a single type or a list of types (for objects able to be
# act as multiple). This is used to tag this object during creation.
obj_type = ObjType.GEAR
# default evennia hooks
def at_object_creation(self):
"""Called when this object is first created. We convert the .obj_type
property to a database tag."""
for obj_type in make_iter(self.obj_type):
self.tags.add(self.obj_type.value, category="obj_type")
def get_display_header(self, looker, **kwargs):
"""The top of the description"""
return ""
def get_display_desc(self, looker, **kwargs)
"""The main display - show object stats"""
return get_obj_stats(self, owner=looker)
# custom evadventure methods
def has_obj_type(self, objtype):
"""Check if object is of a certain type"""
return objtype.value in make_iter(self.obj_type)
def at_pre_use(self, *args, **kwargs):
"""Called before use. If returning False, can't be used"""
return True
def use(self, *args, **kwargs):
"""Use this object, whatever that means"""
pass
def post_use(self, *args, **kwargs):
"""Always called after use."""
pass
def get_help(self):
"""Get any help text for this item"""
return "No help for this item"
@ -106,23 +131,13 @@ class EvAdventureObject(DefaultObject):
value = 0
```
The problem with this is that if we want to make a new object of `size 3` and `value 20`, we have to
make a new class for it. We can't change it on the fly because the change would only be in memory and
be lost on next server reload.
The problem with this is that if we want to make a new object of `size 3` and `value 20`, we have to make a new class for it. We can't change it on the fly because the change would only be in memory and be lost on next server reload.
Because we use `AttributeProperties`, we can set `size` and `value` to whatever we like when we
create the object (or later), and the Attributes will remember our changes to that object indefinitely.
Because we use `AttributeProperties`, we can set `size` and `value` to whatever we like when we create the object (or later), and the Attributes will remember our changes to that object indefinitely.
To make this a little more efficient, we use `autocreate=False`. Normally when you create a
new object with defined `AttributeProperties`, a matching `Attribute` is immediately created at
the same time. So normally, the object would be created along with two Attributes `size` and `value`.
With `autocreate=False`, no Attribute will be created _unless the default is changed_. That is, as
long as your object has `size=1` no database `Attribute` will be created at all. This saves time and
resources when creating large number of objects.
To make this a little more efficient, we use `autocreate=False`. Normally when you create a new object with defined `AttributeProperties`, a matching `Attribute` is immediately created at the same time. So normally, the object would be created along with two Attributes `size` and `value`. With `autocreate=False`, no Attribute will be created _unless the default is changed_. That is, as long as your object has `size=1` no database `Attribute` will be created at all. This saves time and resources when creating large number of objects.
The drawback is that since no Attribute is created you can't refer to it
with `obj.db.size` or `obj.attributes.get("size")` _unless you change its default_. You also can't query
the database for all objects with `size=1`, since most objects would not yet have an in-database
The drawback is that since no Attribute is created you can't refer to it with `obj.db.size` or `obj.attributes.get("size")` _unless you change its default_. You also can't query the database for all objects with `size=1`, since most objects would not yet have an in-database
`size` Attribute to search for.
In our case, we'll only refer to these properties as `obj.size` etc, and have no need to find
@ -130,11 +145,9 @@ all objects of a particular size. So we should be safe.
### Creating tags in `at_object_creation`
The `at_object_creation` is a method Evennia calls on every child of `DefaultObject` whenever it is
first created.
The `at_object_creation` is a method Evennia calls on every child of `DefaultObject` whenever it is first created.
We do a tricky thing here, converting our `.obj_type` to one or more [Tags](../../../Components/Tags.md). Tagging the
object like this means you can later efficiently find all objects of a given type (or combination of
We do a tricky thing here, converting our `.obj_type` to one or more [Tags](../../../Components/Tags.md). Tagging the object like this means you can later efficiently find all objects of a given type (or combination of
types) with Evennia's search functions:
```python
@ -145,9 +158,7 @@ types) with Evennia's search functions:
all_shields = search.search_object_by_tag(ObjType.SHIELD.value, category="obj_type")
```
We allow `.obj_type` to be given as a single value or a list of values. We use `make_iter` from the
evennia utility library to make sure we don't balk at either. This means you could have a Shield that
is also Magical, for example.
We allow `.obj_type` to be given as a single value or a list of values. We use `make_iter` from the evennia utility library to make sure we don't balk at either. This means you could have a Shield that is also Magical, for example.
## Other object types
@ -176,8 +187,7 @@ class EvAdventureTreasure(EvAdventureObject):
## Consumables
A 'consumable' is an item that has a certain number of 'uses'. Once fully consumed, it can't be used
anymore. An example would be a health potion.
A 'consumable' is an item that has a certain number of 'uses'. Once fully consumed, it can't be used anymore. An example would be a health potion.
```python
@ -192,14 +202,20 @@ class EvAdventureConsumable(EvAdventureObject):
value = AttributeProperty(0.25, autocreate=False)
uses = AttributeProperty(1, autocreate=False)
def at_pre_use(self, user, *args, **kwargs):
def at_pre_use(self, user, target=None, *args, **kwargs):
"""Called before using. If returning False, abort use."""
return uses > 0
def at_use(self, user, *args, **kwargs):
if target and user.location != target.location:
user.msg("You are not close enough to the target!")
return False
if self.uses <= 0:
user.msg(f"|w{self.key} is used up.|n")
return False
def use(self, user, *args, **kwargs):
"""Called when using the item"""
pass
pass
def at_post_use(self. user, *args, **kwargs):
"""Called after using the item"""
# detract a usage, deleting the item if used up.
@ -209,12 +225,13 @@ class EvAdventureConsumable(EvAdventureObject):
self.delete()
```
What exactly each consumable does will vary - we will need to implement children of this class
later, overriding `at_use` with different effects.
In `at_pre_use` we check if we have specified a target (heal someone else or throw a fire bomb at an enemy?), making sure we are in the same location. We also make sure we have `usages` left. In `at_post_use` we make sure to tick off usages.
What exactly each consumable does will vary - we will need to implement children of this class later, overriding `at_use` with different effects.
## Weapons
All weapons need properties that describe how efficient they are in battle.
All weapons need properties that describe how efficient they are in battle. To 'use' a weapon means to attack with it, so we can let the weapon itself handle all logic around performing an attack. Having the attack code on the weapon also means that if we in the future wanted a weapon doing something special on-attack (for example, a vampiric sword that heals the attacker when hurting the enemy), we could easily add that on the weapon subclass in question without modifying other code.
```python
# mygame/evadventure/objects.py
@ -234,18 +251,122 @@ class EvAdventureWeapon(EvAdventureObject):
defend_type = AttibuteProperty(Ability.ARMOR, autocreate=False)
damage_roll = AttibuteProperty("1d6", autocreate=False)
def at_pre_use(self, user, target=None, *args, **kwargs):
if target and user.location != target.location:
# we assume weapons can only be used in the same location
user.msg("You are not close enough to the target!")
return False
if self.quality is not None and self.quality <= 0:
user.msg(f"{self.get_display_name(user)} is broken and can't be used!")
return False
return super().at_pre_use(user, target=target, *args, **kwargs)
def use(self, attacker, target, *args, advantage=False, disadvantage=False, **kwargs):
"""When a weapon is used, it attacks an opponent"""
location = attacker.location
is_hit, quality, txt = rules.dice.opposed_saving_throw(
attacker,
target,
attack_type=self.attack_type,
defense_type=self.defense_type,
advantage=advantage,
disadvantage=disadvantage,
)
location.msg_contents(
f"$You() $conj(attack) $You({target.key}) with {self.key}: {txt}",
from_obj=attacker,
mapping={target.key: target},
)
if is_hit:
# enemy hit, calculate damage
dmg = rules.dice.roll(self.damage_roll)
if quality is Ability.CRITICAL_SUCCESS:
# doble damage roll for critical success
dmg += rules.dice.roll(self.damage_roll)
message = (
f" $You() |ycritically|n $conj(hit) $You({target.key}) for |r{dmg}|n damage!"
)
else:
message = f" $You() $conj(hit) $You({target.key}) for |r{dmg}|n damage!"
location.msg_contents(message, from_obj=attacker, mapping={target.key: target})
# call hook
target.at_damage(dmg, attacker=attacker)
else:
# a miss
message = f" $You() $conj(miss) $You({target.key})."
if quality is Ability.CRITICAL_FAILURE:
message += ".. it's a |rcritical miss!|n, damaging the weapon."
if self.quality is not None:
self.quality -= 1
location.msg_contents(message, from_obj=attacker, mapping={target.key: target})
def at_post_use(self, user, *args, **kwargs):
if self.quality is not None and self.quality <= 0:
user.msg(f"|r{self.get_display_name(user)} breaks and can no longer be used!")
```
The `quality` is something we need to track in _Knave_. When getting critical failures on attacks,
a weapon's quality will go down. When it reaches 0, it will break.
In EvAdventure, we will assume all weapons (including bows etc) are used in the same location as the target. Weapons also have a `quality` attribute that gets worn down if the user rolls a critical failure. Once quality is down to 0, the weapon is broken and needs to be repaired.
The `quality` is something we need to track in _Knave_. When getting critical failures on attacks, a weapon's quality will go down. When it reaches 0, it will break. We assume that a `quality` of `None` means that quality doesn't apply (that is, the item is unbreakable), so we must consider that when checking.
The attack/defend type tracks how we resolve attacks with the weapon, like `roll + STR vs ARMOR + 10`.
In the `use` method we make use of the `rules` module we [created earlier](./Beginner-Tutorial-Rules.md) to perform all the dice rolls needed to resolve the attack.
This code requires some additional explanation:
```python
location.msg_contents(
f"$You() $conj(attack) $you({target.key}) with {self.key}: {txt}",
from_obj=attacker,
mapping={target.key: target},
)
```
`location.msg_contents` sends a message to everyone in `location`. Since people will usually notice if you swing a sword at somone, this makes sense to tell people about. This message should however look _different_ depending on who sees it.
I should see:
You attack Grendel with sword: <dice roll results>
Others should see
Beowulf attacks Grendel with sword: <dice roll results>
And Grendel should see
Beowulf attacks you with sword: <dice roll results>
We provide the following string to `msg_contents`:
```python
f"$You() $conj(attack) $You({target.key}) with {self.key}: {txt}"
```
The `{...}` are normal f-string formatting markers like those we have used before. The `$func(...)` bits are [Evennnia FuncParser](../../../Components/FuncParser.md) function calls. FuncParser calls are executed as functions and the result replaces their position in the string. As this string is parsed by Evennia, this is what happens:
First the f-string markers are replaced, so that we get this:
```python
"$You() $cobj(attack) $you(Grendel) with sword: \n rolled 8 on d20 ..."
```
Next the funcparser functions are run:
- `$You()` becomes the name or `You` depending on if the string is to be sent to that object or not. It uses the `from_obj=` kwarg to the `msg_contents` method to know this. Since `msg_contents=attacker` , this becomes `You` or `Beowulf` in this example.
- `$you(Grendel)` looks for the `mapping=` kwarg to `msg_contents` to determine who should be addressed here. If will replace this with the display name or the lowercase `you`. We have added `mapping={target.key: target}` - that is `{"Grendel": <grendel_obj>}`. So this will become `you` or `Grendel` depending on who sees the string.
- `$conj(attack)` _conjugates_ the verb depending on who sees it. The result will be `You attack ...` or `Beowulf attacks` (note the extra `s`).
A few funcparser calls compacts all these points of view into one string!
## Magic
In _Knave_, anyone can use magic if they are wielding a rune stone (our name for spell books) in both
hands. You can only use a rune stone once per rest. So a rune stone is an example of a 'magical weapon'
that is also a 'consumable' of sorts.
In _Knave_, anyone can use magic if they are wielding a rune stone (our name for spell books) in both hands. You can only use a rune stone once per rest. So a rune stone is an example of a 'magical weapon' that is also a 'consumable' of sorts.
```python
@ -281,25 +402,16 @@ class EvAdventureRuneStone(EvAdventureWeapon, EvAdventureConsumable):
self.uses = 1
```
We make the rune stone a mix of weapon and consumable. Note that we don't have to add `.uses`
again, it's inherited from `EvAdventureConsumable` parent. The `at_pre_use` and `at_use` methods
are also inherited; we only override `at_post_use` since we don't want the runestone to be deleted
when it runs out of uses.
We make the rune stone a mix of weapon and consumable. Note that we don't have to add `.uses` again, it's inherited from `EvAdventureConsumable` parent. The `at_pre_use` and `use` methods are also inherited; we only override `at_post_use` since we don't want the runestone to be deleted when it runs out of uses.
We add a little convenience method `refresh` - we should call this when the character rests, to
make the runestone active again.
We add a little convenience method `refresh` - we should call this when the character rests, to make the runestone active again.
Exactly what rune stones _do_ will be implemented in the `at_use` methods of subclasses to this
base class. Since magic in _Knave_ tends to be pretty custom, it makes sense that it will lead to a lot
of custom code.
Exactly what rune stones _do_ will be implemented in the `at_use` methods of subclasses to this base class. Since magic in _Knave_ tends to be pretty custom, it makes sense that it will lead to a lot of custom code.
## Armor
Armor, shields and helmets increase the `ARMOR` stat of the character. In _Knave_, what is stored is the
defense value of the armor (values 11-20). We will instead store the 'armor bonus' (1-10). As we know,
defending is always `bonus + 10`, so the result will be the same - this means
we can use `Ability.ARMOR` as any other defensive ability without worrying about a special case.
Armor, shields and helmets increase the `ARMOR` stat of the character. In _Knave_, what is stored is the defense value of the armor (values 11-20). We will instead store the 'armor bonus' (1-10). As we know, defending is always `bonus + 10`, so the result will be the same - this means we can use `Ability.ARMOR` as any other defensive ability without worrying about a special case.
``
```python
@ -327,34 +439,53 @@ class EvAdventureHelmet(EvAdventureArmor):
## Your Bare hands
This is a 'dummy' object that is not stored in the database. We will use this in the upcoming
[Equipment tutorial lesson](./Beginner-Tutorial-Equipment.md) to represent when you have 'nothing'
in your hands. This way we don't need to add any special case for this.
When we don't have any weapons, we'll be using our bare fists to fight.
We will use this in the upcoming [Equipment tutorial lesson](./Beginner-Tutorial-Equipment.md) to represent when you have 'nothing' in your hands. This way we don't need to add any special case for this.
```python
class WeaponEmptyHand:
# mygame/evadventure/objects.py
from evennia import search_object, create_object
_BARE_HANDS = None
# ...
class WeaponBareHands(EvAdventureWeapon)
obj_type = ObjType.WEAPON
key = "Empty Fists"
inventory_use_slot = WieldLocation.WEAPON_HAND
attack_type = Ability.STR
defense_type = Ability.ARMOR
damage_roll = "1d4"
quality = 100000 # let's assume fists are always available ...
def __repr__(self):
return "<WeaponEmptyHand>"
quality = None # let's assume fists are indestructible ...
def get_bare_hands():
"""Get the bare hands"""
global _BARE_HANDS
if not _BARE_HANDS:
_BARE_HANDS = search_object("Bare hands", typeclass=WeaponBareHands).first()
if not _BARE_HANDS:
_BARE_HANDS = create_object(WeaponBareHands, key="Bare hands")
return _BARE_HANDS
```
```{sidebar}
Creating a single instance of something that is used everywhere is called to create a _Singleton_.
```
Since everyone's empty hands are the same (in our game), we create _one_ `Bare hands` weapon object that everyone shares. We do this by searching for the object with `search_object` (the `.first()` means we grab the first one even if we should by accident have created multiple hands, see [The Django querying tutorial](../Part1/Beginner-Tutorial-Django-queries.md) for more info). If we find none, we create it.
By use of the `global` Python keyword, we cache the bare hands object get/create in a module level property `_BARE_HANDS`. So this acts as a cache to not have to search the database more than necessary.
From now on, other modules can just import and run this function to get the bare hands.
## Testing and Extra credits
Remember the `get_obj_stats` function from the [Utility Tutorial](./Beginner-Tutorial-Utilities.md) earlier?
We had to use dummy-values since we didn't yet know how we would store properties on Objects in the game.
Remember the `get_obj_stats` function from the [Utility Tutorial](./Beginner-Tutorial-Utilities.md) earlier? We had to use dummy-values since we didn't yet know how we would store properties on Objects in the game.
Well, we just figured out all we need! You can go back and update `get_obj_stats` to properly read the data
from the object it receives.
Well, we just figured out all we need! You can go back and update `get_obj_stats` to properly read the data from the object it receives.
When you change this function you must also update the related unit test - so your existing test becomes a
nice way to test your new Objects as well! Add more tests showing the output of feeding different object-types
to `get_obj_stats`.
When you change this function you must also update the related unit test - so your existing test becomes a nice way to test your new Objects as well! Add more tests showing the output of feeding different object-types to `get_obj_stats`.
Try it out yourself. If you need help, a finished utility example is found in [evennia/contrib/tutorials/evadventure/utils.py](get_obj_stats).

View file

@ -1,9 +1,7 @@
# Part 3: How we get there (example game)
```{warning}
The tutorial game is under development and is not yet complete, nor tested. Use the existing
lessons as inspiration and to help get you going, but don't expect out-of-the-box perfection
from it at this time.
The tutorial game is under development and is not yet complete, nor tested. Use the existing lessons as inspiration and to help get you going, but don't expect out-of-the-box perfection from it at this time.
```
```{sidebar} Beginner Tutorial Parts
@ -22,21 +20,23 @@ from it at this time.
```
In part three of the Evennia Beginner tutorial we will go through the actual creation of
our tutorial game _EvAdventure_, based on the [Knave](https://www.drivethrurpg.com/product/250888/Knave)
RPG ruleset.
our tutorial game _EvAdventure_, based on the [Knave](https://www.drivethrurpg.com/product/250888/Knave) RPG ruleset.
This is a big part. You'll be seeing a lot of code and there are plenty of lessons to go through.
Take your time!
If you followed the previous parts of this tutorial you will have some notions about Python and where to
find and make use of things in Evennia. We also have a good idea of the type of game we will
create.
If you followed the previous parts of this tutorial series you will have some notions about Python and where to find and make use of things in Evennia. We also have a good idea of the type of game we will create.
Even if this is not the game-style you are interested in, following along will give you a lot
of experience using Evennia and be really helpful for doing your own thing later!
of experience using Evennia and be really helpful for doing your own thing later! The EvAdventure game code is also built to easily be expanded upon.
Fully coded examples of all code we make in this part can be found in the
[evennia/contrib/tutorials/evadventure](../../../api/evennia.contrib.tutorials.evadventure.md) package.
Fully coded examples of all code we make in this part can be found in the
[evennia/contrib/tutorials/evadventure](../../../api/evennia.contrib.tutorials.evadventure.md) package. There are three common ways to learn from this:
1. Follow the tutorial lessons in sequence and use it to write your own code, referring to the ready-made code as extra help, context, or as a 'facit' to check yourself.
2. Read through the code in the package and refer to the tutorial lesson for each part for more information on what you see.
3. Some mix of the two.
Which approach you choose is individual - we all learn in different ways.
Either way, this is a big part. You'll be seeing a lot of code and there are plenty of lessons to go through. We are making a whole game from scratch after all. Take your time!
## Lessons
@ -53,9 +53,13 @@ Beginner-Tutorial-Equipment
Beginner-Tutorial-Chargen
Beginner-Tutorial-Rooms
Beginner-Tutorial-NPCs
Beginner-Tutorial-Turnbased-Combat
Beginner-Tutorial-Combat-Base
Beginner-Tutorial-Combat-Twitch
Beginner-Tutorial-Combat-Turnbased
Beginner-Tutorial-AI
Beginner-Tutorial-Dungeon
Beginner-Tutorial-Monsters
Beginner-Tutorial-Quests
Beginner-Tutorial-Shops
Beginner-Tutorial-Dungeon
Beginner-Tutorial-Commands
```

View file

@ -1,5 +1,269 @@
# In-game Rooms
```{warning}
This part of the Beginner tutorial is still being developed.
```
A _room_ describes a specific location in the game world. Being an abstract concept, it can represent any area of game content that is convenient to group together. In this lesson we will also create a small in-game automap.
In EvAdventure, we will have two main types of rooms:
- Normal, above-ground rooms. Based on a fixed map, these will be created once and then don't change. We'll cover them in this lesson.
- Dungeon rooms - these will be examples of _procedurally generated_ rooms, created on the fly as the players explore the underworld. Being subclasses of the normal room, we'll get to them in the [Dungeon generation lesson](./Beginner-Tutorial-Dungeon.md).
## The base room
> Create a new module `evadventure/rooms.py`.
```python
# in evadventure/rooms.py
from evennia import AttributeProperty, DefaultRoom
class EvAdventureRoom(DefaultRoom):
"""
Simple room supporting some EvAdventure-specifics.
"""
allow_combat = AttributeProperty(False, autocreate=False)
allow_pvp = AttributeProperty(False, autocreate=False)
allow_death = AttributeProperty(False, autocreate=False)
```
Our `EvadventureRoom` is very simple. We use Evennia's `DefaultRoom` as a base and just add three additional Attributes that defines
- If combat is allowed to start in the room at all.
- If combat is allowed, if PvP (player vs player) combat is allowed.
- If combat is allowed, if any side is allowed to die from it.
Later on we must make sure our combat systems honors these values.
## PvP room
Here's a room that allows non-lethal PvP (sparring):
```python
# in evadventure/rooms.py
# ...
class EvAdventurePvPRoom(EvAdventureRoom):
"""
Room where PvP can happen, but noone gets killed.
"""
allow_combat = AttributeProperty(True, autocreate=False)
allow_pvp = AttributeProperty(True, autocreate=False)
def get_display_footer(self, looker, **kwargs):
"""
Customize footer of description.
"""
return "|yNon-lethal PvP combat is allowed here!|n"
```
The return of `get_display_footer` will show after the [main room description](../../../Components/Objects.md#changing-an-objects-appearance), showing that the room is a sparring room. This means that when a player drops to 0 HP, they will lose the combat, but don't stand any risk of dying (weapons wear out normally during sparring though).
## Adding a room map
We want a dynamic map that visualizes the exits you can use at any moment. Here's how our room will display:
```shell
o o o
\|/
o-@-o
|
o
The crossroads
A place where many roads meet.
Exits: north, northeast, south, west, and northwest
```
> Documentation does not show ansi colors.
Let's expand the base `EvAdventureRoom` with the map.
```{code-block} python
:linenos:
:emphasize-lines: 12,19,51,52,58,67
# in evadventyre/rooms.py
# ...
from copy import deepcopy
from evennia import DefaultCharacter
from evennia.utils.utils import inherits_from
CHAR_SYMBOL = "|w@|n"
CHAR_ALT_SYMBOL = "|w>|n"
ROOM_SYMBOL = "|bo|n"
LINK_COLOR = "|B"
_MAP_GRID = [
[" ", " ", " ", " ", " "],
[" ", " ", " ", " ", " "],
[" ", " ", "@", " ", " "],
[" ", " ", " ", " ", " "],
[" ", " ", " ", " ", " "],
]
_EXIT_GRID_SHIFT = {
"north": (0, 1, "||"),
"east": (1, 0, "-"),
"south": (0, -1, "||"),
"west": (-1, 0, "-"),
"northeast": (1, 1, "/"),
"southeast": (1, -1, "\\"),
"southwest": (-1, -1, "/"),
"northwest": (-1, 1, "\\"),
}
class EvAdventureRoom(DefaultRoom):
# ...
def format_appearance(self, appearance, looker, **kwargs):
"""Don't left-strip the appearance string"""
return appearance.rstrip()
def get_display_header(self, looker, **kwargs):
"""
Display the current location as a mini-map.
"""
# make sure to not show make a map for users of screenreaders.
# for optimization we also don't show it to npcs/mobs
if not inherits_from(looker, DefaultCharacter) or (
looker.account and looker.account.uses_screenreader()
):
return ""
# build a map
map_grid = deepcopy(_MAP_GRID)
dx0, dy0 = 2, 2
map_grid[dy0][dx0] = CHAR_SYMBOL
for exi in self.exits:
dx, dy, symbol = _EXIT_GRID_SHIFT.get(exi.key, (None, None, None))
if symbol is None:
# we have a non-cardinal direction to go to - indicate this
map_grid[dy0][dx0] = CHAR_ALT_SYMBOL
continue
map_grid[dy0 + dy][dx0 + dx] = f"{LINK_COLOR}{symbol}|n"
if exi.destination != self:
map_grid[dy0 + dy + dy][dx0 + dx + dx] = ROOM_SYMBOL
# Note that on the grid, dy is really going *downwards* (origo is
# in the top left), so we need to reverse the order at the end to mirror it
# vertically and have it come out right.
return " " + "\n ".join("".join(line) for line in reversed(map_grid))
```
The string returned from `get_display_header` will end up at the top of the [room description](../../../Components/Objects.md#changing-an-objects-description), a good place to have the map appear!
- **Line 12**: The map itself consists of the 2D matrix `_MAP_GRID`. This is a 2D area described by a list of Python lists. To find a given place in the list, you first first need to find which of the nested lists to use, and then which element to use in that list. Indices start from 0 in Python. So to draw the `o` symbol for the southermost room, you'd need to do so at `_MAP_GRID[4][2]`.
- **Line 19**: The `_EXIT_GRID_SHIFT` indicates the direction to go for each cardinal exit, along with the map symbol to draw at that point. So `"east": (1, 0, "-")` means the east exit will be drawn one step in the positive x direction (to the right), using the "-" symbol. For symbols like `|` and "\\" we need to escape with a double-symbol since these would otherwise be interpreted as part of other formatting.
- **Line 51**: We start by making a `deepcopy` of the `_MAP_GRID`. This is so that we don't modify the original but always have an empty template to work from.
- **Line 52**: We use `@` to indicate the location of the player (at coordinate `(2, 2)`). We then take the actual exits from the room use their names to figure out what symbols to draw out from the center.
- **Line 58**: We want to be able to get on/off the grid if so needed. So if a room has a non-cardinal exit in it (like 'back' or up/down), we'll indicate this by showing the `>` symbol instead of the `@` in your current room.
- **Line 67**: Once we have placed all the exit- and room-symbols in the grid, we merge it all together into a single string. At the end we use Python's standard [join](https://www.w3schools.com/python/ref_string_join.asp) to convert the grid into a single string. In doing so we must flip the grid upside down (reverse the outermost list). Why is this? If you think about how a MUD game displays its data - by printing at the bottom and then scrolling upwards - you'll realize that Evennia has to send out the top of your map _first_ and the bottom of it _last_ for it to show correctly to the user.
## Adding life to a room
Normally the room is static until you do something in it. But let's say you are in a room described to be a bustling market. Would it not be nice to occasionally get some random messages like
"You hear a merchant calling out his wares."
"The sound of music drifts over the square from an open tavern door."
"The sound of commerse rises and fall in a steady rythm."
Here's an example of how to accomplish this:
```{code-block} python
:linenos:
:emphasize-lines: 22,25
# in evadventure/rooms.py
# ...
from random import choice, random
from evennia import TICKER_HANDLER
# ...
class EchoingRoom(EvAdventureRoom):
"""A room that randomly echoes messages to everyone inside it"""
echoes = AttributeProperty(list, autocreate=False)
echo_rate = AttributeProperty(60 * 2, autocreate=False)
echo_chance = AttributeProperty(0.1, autocreate=False)
def send_echo(self):
if self.echoes and random() < self.echo_chance:
self.msg_contents(choice(self.echoes))
def start_echo(self):
TICKER_HANDLER.add(self.echo_rate, self.send_echo)
def stop_echo(self):
TICKER_HANDLER.remove(self.echo_rate, self.send_echo)
```
The [TickerHandler](../../../Components/TickerHandler.md). This is acts as a 'please tick me - subscription service'. In **Line 22** we tell add our `.send_echo` method to the handler and tell the TickerHandler to call that method every `.echo_rate` seconds.
When the `.send_echo` method is called, it will use `random.random()` to check if we should _actually_ do anything. In our example we only show a message 10% of the time. In that case we use Python's `random.choice()` to grab a random text string from the `.echoes` list to send to everyone inside this room.
Here's how you'd use this room in-game:
> dig market:evadventure.EchoingRoom = market,back
> market
> set here/echoes = ["You hear a merchant shouting", "You hear the clatter of coins"]
> py here.start_echo()
If you wait a while you'll eventually see one of the two echoes show up. Use `py here.stop_echo()` if you want.
It's a good idea to be able to turn on/off the echoes at will, if nothing else because you'd be surprised how annoying they can be if they show too often.
In this example we had to resort to `py` to activate/deactivate the echoes, but you could very easily make little utility [Commands](../Part1/Beginner-Tutorial-Adding-Commands.md) `startecho` and `stopecho` to do it for you. This we leave as a bonus exercise.
## Testing
> Create a new module `evadventure/tests/test_rooms.py`.
```{sidebar}
You can find a ready testing module [here in the tutorial folder](evennia.contrib.tutorials.evadventure.tests.test_rooms).
```
The main thing to test with our new rooms is the map. Here's the basic principle for how to do this testing:
```python
# in evadventure/tests/test_rooms.py
from evennia import DefaultExit, create_object
from evennia.utils.test_resources import EvenniaTestCase
from ..characters import EvAdventureCharacter
from ..rooms import EvAdventureRoom
class EvAdventureRoomTest(EvenniaTestCase):
def test_map(self):
center_room = create_object(EvAdventureRoom, key="room_center")
n_room = create_object(EvAdventureRoom, key="room_n)
create_object(DefaultExit,
key="north", location=center_room, destination=n_room)
ne_room = create_object(EvAdventureRoom, key="room=ne")
create_object(DefaultExit,
key="northeast", location=center_room, destination=ne_room)
# ... etc for all cardinal directions
char = create_object(EvAdventureCharacter,
key="TestChar", location=center_room)
desc = center_room.return_appearance(char)
# compare the desc we got with the expected description here
```
So we create a bunch of rooms, link them to one centr room and then make sure the map in that room looks like we'd expect.
## Conclusion
In this lesson we manipulated strings and made a map. Changing the description of an object is a big part of changing the 'graphics' of a text-based game, so checking out the [parts making up an object description](../../../Components/Objects.md#changing-an-objects-description) is good extra reading.

View file

@ -122,12 +122,25 @@ class Ability(Enum):
ALLEGIANCE_HOSTILE = "hostile"
ALLEGIANCE_NEUTRAL = "neutral"
ALLEGIANCE_FRIENDLY = "friendly"
ABILITY_REVERSE_MAP = {
"str": Ability.STR,
"dex": Ability.DEX,
"con": Ability.CON,
"int": Ability.INT,
"wis": Ability.WIS,
"cha": Ability.CHA
}
```
Here the `Ability` class holds basic properties of a character sheet.
The `ABILITY_REVERSE_MAP` is a convenient map to go the other way - if you in some command were to enter the string 'cha', we could use this mapping to directly convert your input to the correct `Ability`:
ability = ABILITY_REVERSE_MAP.get(your_input)
## Utility module

View file

@ -78,7 +78,7 @@ If `localhost` doesn't work when trying to connect to your local game, try `127.
## Mac Troubleshooting
- Some Mac users have reported not being able to connect to `localhost` (i.e. your own computer). If so, try to connect to `127.0.0.1` instead, which is the same thing. Use port 4000 from mud clients and port 4001 from the web browser as usual.
- If you get a `MemoryError` when starting Evennia, or when looking at the log, this may be due to an sqlite versioning issue. [A user in our forums](https://github.com/evennia/evennia/discussions/2638#discussioncomment-3630761) found a working solution for this. [Here](https://github.com/evennia/evennia/issues/3120#issuecomment-1442540538) is another variation to solve it.
- If you get a `MemoryError` when starting Evennia, or when looking at the log, this may be due to an sqlite versioning issue. [A user in our forums](https://github.com/evennia/evennia/discussions/2637) found a working solution for this. [Here](https://github.com/evennia/evennia/issues/2854) is another variation to solve it.
## Windows Troubleshooting

View file

@ -0,0 +1,17 @@
```{eval-rst}
evennia.contrib.tutorials.evadventure.batchscripts
==========================================================
.. automodule:: evennia.contrib.tutorials.evadventure.batchscripts
:members:
:undoc-members:
:show-inheritance:
.. toctree::
:maxdepth: 6
evennia.contrib.tutorials.evadventure.batchscripts.turnbased_combat_demo
```

View file

@ -0,0 +1,10 @@
```{eval-rst}
evennia.contrib.tutorials.evadventure.batchscripts.turnbased\_combat\_demo
=================================================================================
.. automodule:: evennia.contrib.tutorials.evadventure.batchscripts.turnbased_combat_demo
:members:
:undoc-members:
:show-inheritance:
```

View file

@ -0,0 +1,10 @@
```{eval-rst}
evennia.contrib.tutorials.evadventure.combat\_base
=========================================================
.. automodule:: evennia.contrib.tutorials.evadventure.combat_base
:members:
:undoc-members:
:show-inheritance:
```

View file

@ -0,0 +1,10 @@
```{eval-rst}
evennia.contrib.tutorials.evadventure.combat\_twitch
===========================================================
.. automodule:: evennia.contrib.tutorials.evadventure.combat_twitch
:members:
:undoc-members:
:show-inheritance:
```

View file

@ -16,7 +16,9 @@ evennia.contrib.tutorials.evadventure
evennia.contrib.tutorials.evadventure.build_world
evennia.contrib.tutorials.evadventure.characters
evennia.contrib.tutorials.evadventure.chargen
evennia.contrib.tutorials.evadventure.combat_base
evennia.contrib.tutorials.evadventure.combat_turnbased
evennia.contrib.tutorials.evadventure.combat_twitch
evennia.contrib.tutorials.evadventure.commands
evennia.contrib.tutorials.evadventure.dungeon
evennia.contrib.tutorials.evadventure.enums
@ -34,6 +36,7 @@ evennia.contrib.tutorials.evadventure
.. toctree::
:maxdepth: 6
evennia.contrib.tutorials.evadventure.batchscripts
evennia.contrib.tutorials.evadventure.tests
```

View file

@ -19,7 +19,9 @@ evennia.contrib.tutorials.evadventure.tests
evennia.contrib.tutorials.evadventure.tests.test_commands
evennia.contrib.tutorials.evadventure.tests.test_dungeon
evennia.contrib.tutorials.evadventure.tests.test_equipment
evennia.contrib.tutorials.evadventure.tests.test_npcs
evennia.contrib.tutorials.evadventure.tests.test_quests
evennia.contrib.tutorials.evadventure.tests.test_rooms
evennia.contrib.tutorials.evadventure.tests.test_rules
evennia.contrib.tutorials.evadventure.tests.test_utils

View file

@ -0,0 +1,10 @@
```{eval-rst}
evennia.contrib.tutorials.evadventure.tests.test\_npcs
=============================================================
.. automodule:: evennia.contrib.tutorials.evadventure.tests.test_npcs
:members:
:undoc-members:
:show-inheritance:
```

View file

@ -0,0 +1,10 @@
```{eval-rst}
evennia.contrib.tutorials.evadventure.tests.test\_rooms
==============================================================
.. automodule:: evennia.contrib.tutorials.evadventure.tests.test_rooms
:members:
:undoc-members:
:show-inheritance:
```

View file

@ -250,10 +250,22 @@ div.highlight {
border-radius: 5px;
}
div.highlight-shell.notranslate > div.highlight .nb {
color: #79ecff !important;
/* Shell code style (for in-game blocks) */
div.highlight-shell.notranslate > div.highlight .nb{
color: white !important;
}
div.highlight-shell.notranslate > div.highlight .m{
color: white !important;
font-weight: normal !important;
}
div.highlight-shell.notranslate > div.highlight .k{
color: white !important;
font-weight: normal !important;
}
div.highlight-shell.notranslate > div.highlight .o{
color: white !important;
font-weight: normal !important;
}
div.note {
background-color: #eee;
@ -352,7 +364,7 @@ code {
/* padding: 1px 2px; */
font-size: 0.9em;
font-family: "Courier Prime", Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
font-weight: bold;
font-weight: normal;
background-color: #f7f7f7;
}

View file

@ -428,7 +428,9 @@
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.build_world.html">evennia.contrib.tutorials.evadventure.build_world</a></li>
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.characters.html">evennia.contrib.tutorials.evadventure.characters</a></li>
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.chargen.html">evennia.contrib.tutorials.evadventure.chargen</a></li>
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.combat_base.html">evennia.contrib.tutorials.evadventure.combat_base</a></li>
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.combat_turnbased.html">evennia.contrib.tutorials.evadventure.combat_turnbased</a></li>
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.combat_twitch.html">evennia.contrib.tutorials.evadventure.combat_twitch</a></li>
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.commands.html">evennia.contrib.tutorials.evadventure.commands</a></li>
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.dungeon.html">evennia.contrib.tutorials.evadventure.dungeon</a></li>
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.enums.html">evennia.contrib.tutorials.evadventure.enums</a></li>
@ -441,6 +443,10 @@
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.rules.html">evennia.contrib.tutorials.evadventure.rules</a></li>
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.shops.html">evennia.contrib.tutorials.evadventure.shops</a></li>
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.utils.html">evennia.contrib.tutorials.evadventure.utils</a></li>
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.batchscripts.html">evennia.contrib.tutorials.evadventure.batchscripts</a><ul>
<li class="toctree-l6"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.batchscripts.turnbased_combat_demo.html">evennia.contrib.tutorials.evadventure.batchscripts.turnbased_combat_demo</a></li>
</ul>
</li>
<li class="toctree-l5"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.tests.html">evennia.contrib.tutorials.evadventure.tests</a><ul>
<li class="toctree-l6"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.tests.mixins.html">evennia.contrib.tutorials.evadventure.tests.mixins</a></li>
<li class="toctree-l6"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.tests.test_characters.html">evennia.contrib.tutorials.evadventure.tests.test_characters</a></li>
@ -449,7 +455,9 @@
<li class="toctree-l6"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.tests.test_commands.html">evennia.contrib.tutorials.evadventure.tests.test_commands</a></li>
<li class="toctree-l6"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.tests.test_dungeon.html">evennia.contrib.tutorials.evadventure.tests.test_dungeon</a></li>
<li class="toctree-l6"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.tests.test_equipment.html">evennia.contrib.tutorials.evadventure.tests.test_equipment</a></li>
<li class="toctree-l6"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.tests.test_npcs.html">evennia.contrib.tutorials.evadventure.tests.test_npcs</a></li>
<li class="toctree-l6"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.tests.test_quests.html">evennia.contrib.tutorials.evadventure.tests.test_quests</a></li>
<li class="toctree-l6"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.tests.test_rooms.html">evennia.contrib.tutorials.evadventure.tests.test_rooms</a></li>
<li class="toctree-l6"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.tests.test_rules.html">evennia.contrib.tutorials.evadventure.tests.test_rules</a></li>
<li class="toctree-l6"><a class="reference internal" href="evennia.contrib.tutorials.evadventure.tests.test_utils.html">evennia.contrib.tutorials.evadventure.tests.test_utils</a></li>
</ul>

View file

@ -1345,7 +1345,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"> = ['&#64;swap', '&#64;typeclasses', '&#64;parent', '&#64;type', '&#64;update']</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"> = ['&#64;update', '&#64;typeclasses', '&#64;parent', '&#64;swap', '&#64;type']</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">
@ -1376,7 +1376,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': '&#64;swap &#64;typeclasses &#64;parent &#64;type &#64;update', 'category': 'building', 'key': '&#64;typeclass', 'no_prefix': 'typeclass swap typeclasses parent type update', 'tags': '', 'text': &quot;\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] &lt;object&gt; [= typeclass.path]\n typeclass/prototype &lt;object&gt; = 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 &quot;}</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': '&#64;update &#64;typeclasses &#64;parent &#64;swap &#64;type', 'category': 'building', 'key': '&#64;typeclass', 'no_prefix': 'typeclass update typeclasses parent swap type', 'tags': '', 'text': &quot;\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] &lt;object&gt; [= typeclass.path]\n typeclass/prototype &lt;object&gt; = 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 &quot;}</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>

File diff suppressed because one or more lines are too long

View file

@ -323,7 +323,7 @@ inv</p>
<dl class="py attribute">
<dt id="evennia.commands.default.general.CmdInventory.aliases">
<code class="sig-name descname">aliases</code><em class="property"> = ['i', 'inv']</em><a class="headerlink" href="#evennia.commands.default.general.CmdInventory.aliases" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">aliases</code><em class="property"> = ['inv', 'i']</em><a class="headerlink" href="#evennia.commands.default.general.CmdInventory.aliases" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
@ -354,7 +354,7 @@ inv</p>
<dl class="py attribute">
<dt id="evennia.commands.default.general.CmdInventory.search_index_entry">
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'i inv', 'category': 'general', 'key': 'inventory', 'no_prefix': ' i inv', 'tags': '', 'text': '\n view inventory\n\n Usage:\n inventory\n inv\n\n Shows your inventory.\n '}</em><a class="headerlink" href="#evennia.commands.default.general.CmdInventory.search_index_entry" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'inv i', 'category': 'general', 'key': 'inventory', 'no_prefix': ' inv i', 'tags': '', 'text': '\n view inventory\n\n Usage:\n inventory\n inv\n\n Shows your inventory.\n '}</em><a class="headerlink" href="#evennia.commands.default.general.CmdInventory.search_index_entry" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
@ -598,7 +598,7 @@ placing it in their inventory.</p>
<dl class="py attribute">
<dt id="evennia.commands.default.general.CmdSay.aliases">
<code class="sig-name descname">aliases</code><em class="property"> = [&quot;'&quot;, '&quot;']</em><a class="headerlink" href="#evennia.commands.default.general.CmdSay.aliases" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">aliases</code><em class="property"> = ['&quot;', &quot;'&quot;]</em><a class="headerlink" href="#evennia.commands.default.general.CmdSay.aliases" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
@ -629,7 +629,7 @@ placing it in their inventory.</p>
<dl class="py attribute">
<dt id="evennia.commands.default.general.CmdSay.search_index_entry">
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '\' &quot;', 'category': 'general', 'key': 'say', 'no_prefix': ' \' &quot;', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say &lt;message&gt;\n\n Talk to those in your current location.\n '}</em><a class="headerlink" href="#evennia.commands.default.general.CmdSay.search_index_entry" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '&quot; \'', 'category': 'general', 'key': 'say', 'no_prefix': ' &quot; \'', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say &lt;message&gt;\n\n Talk to those in your current location.\n '}</em><a class="headerlink" href="#evennia.commands.default.general.CmdSay.search_index_entry" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
@ -773,7 +773,7 @@ which permission groups you are a member of.</p>
<dl class="py attribute">
<dt id="evennia.commands.default.general.CmdAccess.aliases">
<code class="sig-name descname">aliases</code><em class="property"> = ['hierarchy', 'groups']</em><a class="headerlink" href="#evennia.commands.default.general.CmdAccess.aliases" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">aliases</code><em class="property"> = ['groups', 'hierarchy']</em><a class="headerlink" href="#evennia.commands.default.general.CmdAccess.aliases" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
@ -804,7 +804,7 @@ which permission groups you are a member of.</p>
<dl class="py attribute">
<dt id="evennia.commands.default.general.CmdAccess.search_index_entry">
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'hierarchy groups', 'category': 'general', 'key': 'access', 'no_prefix': ' hierarchy groups', 'tags': '', 'text': '\n show your current game access\n\n Usage:\n access\n\n This command shows you the permission hierarchy and\n which permission groups you are a member of.\n '}</em><a class="headerlink" href="#evennia.commands.default.general.CmdAccess.search_index_entry" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'groups hierarchy', 'category': 'general', 'key': 'access', 'no_prefix': ' groups hierarchy', 'tags': '', 'text': '\n show your current game access\n\n Usage:\n access\n\n This command shows you the permission hierarchy and\n which permission groups you are a member of.\n '}</em><a class="headerlink" href="#evennia.commands.default.general.CmdAccess.search_index_entry" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>

View file

@ -955,7 +955,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"> = &lt;module 'evennia.contrib.tutorials.red_button.red_button' from '/tmp/tmppm6u46wp/efc5209b9fc10bd5a57a6e4a4a9bf539653c6480/evennia/contrib/tutorials/red_button/red_button.py'&gt;</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"> = &lt;module 'evennia.contrib.tutorials.red_button.red_button' from '/tmp/tmp5zayxg7_/bbf90ea790ccdc4632988fe16ebef1275f2e829c/evennia/contrib/tutorials/red_button/red_button.py'&gt;</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">

View file

@ -122,7 +122,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"> = ['con', 'co', 'conn']</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"> = ['co', 'con', 'conn']</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">
@ -157,7 +157,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': 'con co conn', 'category': 'general', 'key': 'connect', 'no_prefix': ' con co conn', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect &quot;account name&quot; &quot;pass word&quot;\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': 'co con conn', 'category': 'general', 'key': 'connect', 'no_prefix': ' co con conn', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect &quot;account name&quot; &quot;pass word&quot;\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>
@ -292,7 +292,7 @@ All it does is display the connect screen.</p>
<dl class="py attribute">
<dt id="evennia.commands.default.unloggedin.CmdUnconnectedLook.aliases">
<code class="sig-name descname">aliases</code><em class="property"> = ['look', 'l']</em><a class="headerlink" href="#evennia.commands.default.unloggedin.CmdUnconnectedLook.aliases" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">aliases</code><em class="property"> = ['l', 'look']</em><a class="headerlink" href="#evennia.commands.default.unloggedin.CmdUnconnectedLook.aliases" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
@ -318,7 +318,7 @@ All it does is display the connect screen.</p>
<dl class="py attribute">
<dt id="evennia.commands.default.unloggedin.CmdUnconnectedLook.search_index_entry">
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'look l', 'category': 'general', 'key': '__unloggedin_look_command', 'no_prefix': ' look l', 'tags': '', 'text': '\n look when in unlogged-in state\n\n Usage:\n look\n\n This is an unconnected version of the look command for simplicity.\n\n This is called by the server and kicks everything in gear.\n All it does is display the connect screen.\n '}</em><a class="headerlink" href="#evennia.commands.default.unloggedin.CmdUnconnectedLook.search_index_entry" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'l look', 'category': 'general', 'key': '__unloggedin_look_command', 'no_prefix': ' l look', 'tags': '', 'text': '\n look when in unlogged-in state\n\n Usage:\n look\n\n This is an unconnected version of the look command for simplicity.\n\n This is called by the server and kicks everything in gear.\n All it does is display the connect screen.\n '}</em><a class="headerlink" href="#evennia.commands.default.unloggedin.CmdUnconnectedLook.search_index_entry" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>

View file

@ -139,7 +139,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"> = ['con', 'co', 'conn']</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"> = ['co', 'con', 'conn']</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">
@ -169,7 +169,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': 'con co conn', 'category': 'general', 'key': 'connect', 'no_prefix': ' con co conn', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect &lt;email&gt; &lt;password&gt;\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': 'co con conn', 'category': 'general', 'key': 'connect', 'no_prefix': ' co con conn', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect &lt;email&gt; &lt;password&gt;\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>
@ -291,7 +291,7 @@ All it does is display the connect screen.</p>
<dl class="py attribute">
<dt id="evennia.contrib.base_systems.email_login.email_login.CmdUnconnectedLook.aliases">
<code class="sig-name descname">aliases</code><em class="property"> = ['look', 'l']</em><a class="headerlink" href="#evennia.contrib.base_systems.email_login.email_login.CmdUnconnectedLook.aliases" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">aliases</code><em class="property"> = ['l', 'look']</em><a class="headerlink" href="#evennia.contrib.base_systems.email_login.email_login.CmdUnconnectedLook.aliases" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
@ -317,7 +317,7 @@ All it does is display the connect screen.</p>
<dl class="py attribute">
<dt id="evennia.contrib.base_systems.email_login.email_login.CmdUnconnectedLook.search_index_entry">
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'look l', 'category': 'general', 'key': '__unloggedin_look_command', 'no_prefix': ' look l', 'tags': '', 'text': '\n This is an unconnected version of the `look` command for simplicity.\n\n This is called by the server and kicks everything in gear.\n All it does is display the connect screen.\n '}</em><a class="headerlink" href="#evennia.contrib.base_systems.email_login.email_login.CmdUnconnectedLook.search_index_entry" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'l look', 'category': 'general', 'key': '__unloggedin_look_command', 'no_prefix': ' l look', 'tags': '', 'text': '\n This is an unconnected version of the `look` command for simplicity.\n\n This is called by the server and kicks everything in gear.\n All it does is display the connect screen.\n '}</em><a class="headerlink" href="#evennia.contrib.base_systems.email_login.email_login.CmdUnconnectedLook.search_index_entry" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>

View file

@ -116,7 +116,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"> = ['&#64;callbacks', '&#64;callback', '&#64;calls']</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"> = ['&#64;calls', '&#64;callbacks', '&#64;callback']</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">
@ -197,7 +197,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': '&#64;callbacks &#64;callback &#64;calls', 'category': 'building', 'key': '&#64;call', 'no_prefix': 'call callbacks callback 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>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': '&#64;calls &#64;callbacks &#64;callback', 'category': 'building', 'key': '&#64;call', 'no_prefix': 'call calls callbacks 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>
<dd></dd></dl>
</dd></dl>

View file

@ -217,7 +217,7 @@ for that channel.</p>
<dl class="py attribute">
<dt id="evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.CmdDelCom.aliases">
<code class="sig-name descname">aliases</code><em class="property"> = ['delaliaschan', 'delchanalias']</em><a class="headerlink" href="#evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.CmdDelCom.aliases" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">aliases</code><em class="property"> = ['delchanalias', 'delaliaschan']</em><a class="headerlink" href="#evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.CmdDelCom.aliases" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
@ -248,7 +248,7 @@ for that channel.</p>
<dl class="py attribute">
<dt id="evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.CmdDelCom.search_index_entry">
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'delaliaschan delchanalias', 'category': 'comms', 'key': 'delcom', 'no_prefix': ' delaliaschan delchanalias', 'tags': '', 'text': &quot;\n remove a channel alias and/or unsubscribe from channel\n\n Usage:\n delcom &lt;alias or channel&gt;\n delcom/all &lt;channel&gt;\n\n If the full channel name is given, unsubscribe from the\n channel. If an alias is given, remove the alias but don't\n unsubscribe. If the 'all' switch is used, remove all aliases\n for that channel.\n &quot;}</em><a class="headerlink" href="#evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.CmdDelCom.search_index_entry" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'delchanalias delaliaschan', 'category': 'comms', 'key': 'delcom', 'no_prefix': ' delchanalias delaliaschan', 'tags': '', 'text': &quot;\n remove a channel alias and/or unsubscribe from channel\n\n Usage:\n delcom &lt;alias or channel&gt;\n delcom/all &lt;channel&gt;\n\n If the full channel name is given, unsubscribe from the\n channel. If an alias is given, remove the alias but don't\n unsubscribe. If the 'all' switch is used, remove all aliases\n for that channel.\n &quot;}</em><a class="headerlink" href="#evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.CmdDelCom.search_index_entry" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>

View file

@ -211,7 +211,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"> = ['quit', 'q', 'abort', 'chicken out']</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"> = ['chicken out', 'quit', 'abort', 'q']</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">
@ -235,7 +235,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': 'quit q abort chicken out', 'category': 'evscaperoom', 'key': 'give up', 'no_prefix': ' quit q abort 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>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'chicken out quit abort q', 'category': 'evscaperoom', 'key': 'give up', 'no_prefix': ' chicken out quit abort q', '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>
@ -371,7 +371,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"> = ['whisper', ';', 'shout']</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"> = ['shout', 'whisper', ';']</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">
@ -400,7 +400,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': 'whisper ; shout', 'category': 'general', 'key': 'say', 'no_prefix': ' whisper ; shout', 'tags': '', 'text': '\n Perform an communication action.\n\n Usage:\n say &lt;text&gt;\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': 'shout whisper ;', 'category': 'general', 'key': 'say', 'no_prefix': ' shout whisper ;', 'tags': '', 'text': '\n Perform an communication action.\n\n Usage:\n say &lt;text&gt;\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>
@ -428,7 +428,7 @@ emote /me points to /box and /lever.</p>
<dl class="py attribute">
<dt id="evennia.contrib.full_systems.evscaperoom.commands.CmdEmote.aliases">
<code class="sig-name descname">aliases</code><em class="property"> = ['pose', ':']</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdEmote.aliases" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">aliases</code><em class="property"> = [':', 'pose']</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdEmote.aliases" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
@ -467,7 +467,7 @@ set in self.parse())</p>
<dl class="py attribute">
<dt id="evennia.contrib.full_systems.evscaperoom.commands.CmdEmote.search_index_entry">
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'pose :', 'category': 'general', 'key': 'emote', 'no_prefix': ' pose :', 'tags': '', 'text': '\n Perform a free-form emote. Use /me to\n include yourself in the emote and /name\n to include other objects or characters.\n Use &quot;...&quot; to enact speech.\n\n Usage:\n emote &lt;emote&gt;\n :&lt;emote\n\n Example:\n emote /me smiles at /peter\n emote /me points to /box and /lever.\n\n '}</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdEmote.search_index_entry" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': ': pose', 'category': 'general', 'key': 'emote', 'no_prefix': ' : pose', 'tags': '', 'text': '\n Perform a free-form emote. Use /me to\n include yourself in the emote and /name\n to include other objects or characters.\n Use &quot;...&quot; to enact speech.\n\n Usage:\n emote &lt;emote&gt;\n :&lt;emote\n\n Example:\n emote /me smiles at /peter\n emote /me points to /box and /lever.\n\n '}</em><a class="headerlink" href="#evennia.contrib.full_systems.evscaperoom.commands.CmdEmote.search_index_entry" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
@ -490,7 +490,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"> = ['e', 'ex', 'unfocus', 'examine']</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"> = ['examine', 'unfocus', 'ex', 'e']</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">
@ -519,7 +519,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': 'e ex unfocus examine', 'category': 'evscaperoom', 'key': 'focus', 'no_prefix': ' e ex unfocus examine', 'tags': '', 'text': '\n Focus your attention on a target.\n\n Usage:\n focus &lt;obj&gt;\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': 'examine unfocus ex e', 'category': 'evscaperoom', 'key': 'focus', 'no_prefix': ' examine unfocus ex e', 'tags': '', 'text': '\n Focus your attention on a target.\n\n Usage:\n focus &lt;obj&gt;\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>
@ -581,7 +581,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"> = ['inventory', 'give', 'i', '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"> = ['inv', 'inventory', 'i', '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">
@ -605,7 +605,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': 'inventory give i inv', 'category': 'evscaperoom', 'key': 'get', 'no_prefix': ' inventory give i 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': 'inv inventory i give', 'category': 'evscaperoom', 'key': 'get', 'no_prefix': ' inv inventory i 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>
@ -626,7 +626,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"> = ['&#64;open', '&#64;dig']</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"> = ['&#64;dig', '&#64;open']</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">
@ -649,7 +649,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': '&#64;open &#64;dig', 'category': 'general', 'key': 'open', 'no_prefix': ' open dig', 'tags': '', 'text': '\n Interact with an object in focus.\n\n Usage:\n &lt;action&gt; [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': '&#64;dig &#64;open', 'category': 'general', 'key': 'open', 'no_prefix': ' dig open', 'tags': '', 'text': '\n Interact with an object in focus.\n\n Usage:\n &lt;action&gt; [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>

View file

@ -622,7 +622,7 @@ inv</p>
<dl class="py attribute">
<dt id="evennia.contrib.game_systems.clothing.clothing.CmdInventory.aliases">
<code class="sig-name descname">aliases</code><em class="property"> = ['i', 'inv']</em><a class="headerlink" href="#evennia.contrib.game_systems.clothing.clothing.CmdInventory.aliases" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">aliases</code><em class="property"> = ['inv', 'i']</em><a class="headerlink" href="#evennia.contrib.game_systems.clothing.clothing.CmdInventory.aliases" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
@ -653,7 +653,7 @@ inv</p>
<dl class="py attribute">
<dt id="evennia.contrib.game_systems.clothing.clothing.CmdInventory.search_index_entry">
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'i inv', 'category': 'general', 'key': 'inventory', 'no_prefix': ' i inv', 'tags': '', 'text': '\n view inventory\n\n Usage:\n inventory\n inv\n\n Shows your inventory.\n '}</em><a class="headerlink" href="#evennia.contrib.game_systems.clothing.clothing.CmdInventory.search_index_entry" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'inv i', 'category': 'general', 'key': 'inventory', 'no_prefix': ' inv i', 'tags': '', 'text': '\n view inventory\n\n Usage:\n inventory\n inv\n\n Shows your inventory.\n '}</em><a class="headerlink" href="#evennia.contrib.game_systems.clothing.clothing.CmdInventory.search_index_entry" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>

View file

@ -672,7 +672,7 @@ if there are still any actions you can take.</p>
<dl class="py attribute">
<dt id="evennia.contrib.game_systems.turnbattle.tb_basic.CmdPass.aliases">
<code class="sig-name descname">aliases</code><em class="property"> = ['wait', 'hold']</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_basic.CmdPass.aliases" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">aliases</code><em class="property"> = ['hold', 'wait']</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_basic.CmdPass.aliases" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
@ -698,7 +698,7 @@ if there are still any actions you can take.</p>
<dl class="py attribute">
<dt id="evennia.contrib.game_systems.turnbattle.tb_basic.CmdPass.search_index_entry">
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'no_prefix': ' wait hold', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_basic.CmdPass.search_index_entry" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'no_prefix': ' hold wait', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_basic.CmdPass.search_index_entry" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>

View file

@ -567,7 +567,7 @@ if there are still any actions you can take.</p>
<dl class="py attribute">
<dt id="evennia.contrib.game_systems.turnbattle.tb_equip.CmdPass.aliases">
<code class="sig-name descname">aliases</code><em class="property"> = ['wait', 'hold']</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_equip.CmdPass.aliases" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">aliases</code><em class="property"> = ['hold', 'wait']</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_equip.CmdPass.aliases" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
@ -587,7 +587,7 @@ if there are still any actions you can take.</p>
<dl class="py attribute">
<dt id="evennia.contrib.game_systems.turnbattle.tb_equip.CmdPass.search_index_entry">
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'no_prefix': ' wait hold', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_equip.CmdPass.search_index_entry" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'no_prefix': ' hold wait', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_equip.CmdPass.search_index_entry" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>

View file

@ -690,7 +690,7 @@ if there are still any actions you can take.</p>
<dl class="py attribute">
<dt id="evennia.contrib.game_systems.turnbattle.tb_items.CmdPass.aliases">
<code class="sig-name descname">aliases</code><em class="property"> = ['wait', 'hold']</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_items.CmdPass.aliases" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">aliases</code><em class="property"> = ['hold', 'wait']</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_items.CmdPass.aliases" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
@ -710,7 +710,7 @@ if there are still any actions you can take.</p>
<dl class="py attribute">
<dt id="evennia.contrib.game_systems.turnbattle.tb_items.CmdPass.search_index_entry">
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'no_prefix': ' wait hold', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_items.CmdPass.search_index_entry" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'no_prefix': ' hold wait', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_items.CmdPass.search_index_entry" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>

View file

@ -469,7 +469,7 @@ if there are still any actions you can take.</p>
<dl class="py attribute">
<dt id="evennia.contrib.game_systems.turnbattle.tb_magic.CmdPass.aliases">
<code class="sig-name descname">aliases</code><em class="property"> = ['wait', 'hold']</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_magic.CmdPass.aliases" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">aliases</code><em class="property"> = ['hold', 'wait']</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_magic.CmdPass.aliases" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
@ -489,7 +489,7 @@ if there are still any actions you can take.</p>
<dl class="py attribute">
<dt id="evennia.contrib.game_systems.turnbattle.tb_magic.CmdPass.search_index_entry">
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'no_prefix': ' wait hold', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_magic.CmdPass.search_index_entry" title="Permalink to this definition"></a></dt>
<code class="sig-name descname">search_index_entry</code><em class="property"> = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'no_prefix': ' hold wait', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}</em><a class="headerlink" href="#evennia.contrib.game_systems.turnbattle.tb_magic.CmdPass.search_index_entry" title="Permalink to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>

Some files were not shown because too many files have changed in this diff Show more