<h1><spanclass="section-number">10. </span>Twitch Combat<aclass="headerlink"href="#twitch-combat"title="Permalink to this headline">¶</a></h1>
<p>In this lesson we will build upon the basic combat framework we devised <aclass="reference internal"href="Beginner-Tutorial-Combat-Base.html"><spanclass="doc std std-doc">in the previous lesson</span></a> to create a ‘twitch-like’ combat system.</p>
<div><p>Note that this documentation doesn’t show in-game colors. If you are interested in an alternative, see the <aclass="reference internal"href="Beginner-Tutorial-Combat-Turnbased.html"><spanclass="doc std std-doc">next lesson</span></a>, where we’ll make a turnbased, menu-based system instead.</p>
</div></blockquote>
<p>With “Twitch” combat, we refer to a type of combat system that runs without any clear divisions of ‘turns’ (the opposite of <aclass="reference internal"href="Beginner-Tutorial-Combat-Turnbased.html"><spanclass="doc std std-doc">Turn-based combat</span></a>). It is inspired by the way combat worked in the old <aclass="reference external"href="https://en.wikipedia.org/wiki/DikuMUD">DikuMUD</a> codebase, but is more flexible.</p>
<asideclass="sidebar">
<pclass="sidebar-title">Differences to DIKU combat</p>
<p>In DIKU, all actions in combat happen on a <em>global</em>‘tick’ of, say 3 seconds. In our system, each combatant have their own ‘tick’ which is completely independent of each other. Now, in Evadventure, each combatant will tick at the same rate and thus mimic DIKU … but they don’t <em>have</em> to.</p>
</aside>
<p>Basically, a user enters an action and after a certain time that action will execute (normally an attack). If they don’t do anything, the attack will repeat over and over (with a random result) until the enemy or you is defeated.</p>
<p>You can change up your strategy by performing other actions (like drinking a potion or cast a spell). You can also simply move to another room to ‘flee’ the combat (but the enemy may of course follow you)</p>
<sectionid="general-principle">
<h2><spanclass="section-number">10.1. </span>General principle<aclass="headerlink"href="#general-principle"title="Permalink to this headline">¶</a></h2>
<p>An example of an implemented Twitch combat system can be found in <codeclass="docutils literal notranslate"><spanclass="pre">evennia/contrib/tutorials</span></code>, in <aclass="reference internal"href="../../../api/evennia.contrib.tutorials.evadventure.combat_twitch.html#evennia-contrib-tutorials-evadventure-combat-twitch"><spanclass="std std-ref">evadventure/combat_twitch.py</span></a>.</p>
<p>Here is the general design of the Twitch-based combat handler:</p>
<ulclass="simple">
<li><p>The twitch-version of the CombatHandler will be stored on each combatant whenever combat starts. When combat is over, or they leave the room with combat, the handler will be deleted.</p></li>
<li><p>The handler will queue each action independently, starting a timer until they fire.</p></li>
<li><p>All input are handled via Evennia <aclass="reference internal"href="../../../Components/Commands.html"><spanclass="doc std std-doc">Commands</span></a>.</p></li>
</ul>
</section>
<sectionid="twitch-combat-handler">
<h2><spanclass="section-number">10.2. </span>Twitch combat handler<aclass="headerlink"href="#twitch-combat-handler"title="Permalink to this headline">¶</a></h2>
<blockquote>
<div><p>Create a new module <codeclass="docutils literal notranslate"><spanclass="pre">evadventure/combat_twitch.py</span></code>.</p>
</div></blockquote>
<p>We will make use of the <em>Combat Actions</em>, <em>Action dicts</em> and the parent <codeclass="docutils literal notranslate"><spanclass="pre">EvAdventureCombatBaseHandler</span></code><aclass="reference internal"href="Beginner-Tutorial-Combat-Base.html"><spanclass="doc std std-doc">we created previously</span></a>.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_twitch.py</span>
<p>We make a child class of <codeclass="docutils literal notranslate"><spanclass="pre">EvAdventureCombatBaseHandler</span></code> for our Twitch combat. The parent class is a <aclass="reference internal"href="../../../Components/Scripts.html"><spanclass="doc std std-doc">Script</span></a>, and when a Script sits ‘on’ an Object, that Object is available on the script as <codeclass="docutils literal notranslate"><spanclass="pre">self.obj</span></code>. Since this handler is meant to sit ‘on’ the combatant, then <codeclass="docutils literal notranslate"><spanclass="pre">self.obj</span></code> is thus the combatant and <codeclass="docutils literal notranslate"><spanclass="pre">self.obj.location</span></code> is the current room the combatant is in. By using <codeclass="docutils literal notranslate"><spanclass="pre">super()</span></code> we can reuse the parent class’<codeclass="docutils literal notranslate"><spanclass="pre">msg()</span></code> method with these Twitch-specific details.</p>
<sectionid="getting-the-sides-of-combat">
<h3><spanclass="section-number">10.2.1. </span>Getting the sides of combat<aclass="headerlink"href="#getting-the-sides-of-combat"title="Permalink to this headline">¶</a></h3>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_twitch.py </span>
<p>Next we add our own implementation of the <codeclass="docutils literal notranslate"><spanclass="pre">get_sides()</span></code> method. This presents the sides of combat from the perspective of the provided <codeclass="docutils literal notranslate"><spanclass="pre">combatant</span></code>. In Twitch combat, there are a few things that identifies a combatant:</p>
<ulclass="simple">
<li><p>That they are in the same location</p></li>
<li><p>That they each have a <codeclass="docutils literal notranslate"><spanclass="pre">EvAdventureCombatTwitchHandler</span></code> script running on themselves</p></li>
</ul>
<asideclass="sidebar">
<pclass="sidebar-title">inherits_from</p>
<p>Since <codeclass="docutils literal notranslate"><spanclass="pre">inherits_from</span></code> is True if your class inherits from the parent at <em>any</em> distance, this particular check would not work if you were to change the NPC class to inherit from our Character class as well. In that case we’d have to come up with some other way to compare the two types of entities.</p>
</aside>
<p>In a PvP-open room, it’s all for themselves - everyone else is considered an ‘enemy’. Otherwise we separate PCs from NPCs by seeing if they inherit from <codeclass="docutils literal notranslate"><spanclass="pre">EvAdventureCharacter</span></code> (our PC class) or not - if you are a PC, then the NPCs are your enemies and vice versa. The <aclass="reference internal"href="../../../api/evennia.utils.utils.html#evennia.utils.utils.inherits_from"title="evennia.utils.utils.inherits_from"><spanclass="xref myst py py-func">inherits_from</span></a> is very useful for doing these checks - it will pass also if you inherit from <codeclass="docutils literal notranslate"><spanclass="pre">EvAdventureCharacter</span></code> at <em>any</em> distance.</p>
<p>Note that <codeclass="docutils literal notranslate"><spanclass="pre">allies</span></code> does not include the <codeclass="docutils literal notranslate"><spanclass="pre">combatant</span></code> itself, so if you are fighting a lone enemy, the return from this method will be <codeclass="docutils literal notranslate"><spanclass="pre">([],</span><spanclass="pre">[enemy_obj])</span></code>.</p>
</section>
<sectionid="tracking-advantage-disadvantage">
<h3><spanclass="section-number">10.2.2. </span>Tracking Advantage / Disadvantage<aclass="headerlink"href="#tracking-advantage-disadvantage"title="Permalink to this headline">¶</a></h3>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_twitch.py </span>
<p>As seen in the previous lesson, the Actions call these methods to store the fact that
a given combatant has advantage.</p>
<p>In this Twitch-combat case, the one getting the advantage is always one on which the combathandler is defined, so we don’t actually need to use the <codeclass="docutils literal notranslate"><spanclass="pre">recipient/combatant</span></code> argument (it’s always going to be <codeclass="docutils literal notranslate"><spanclass="pre">self.obj</span></code>) - only <codeclass="docutils literal notranslate"><spanclass="pre">target</span></code> is important.</p>
<p>We create two new Attributes to store the relation as dicts.</p>
</section>
<sectionid="queue-action">
<h3><spanclass="section-number">10.2.3. </span>Queue action<aclass="headerlink"href="#queue-action"title="Permalink to this headline">¶</a></h3>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">obj</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"This is an unkown action!"</span><spanclass="p">)</span>
<spanclass="k">return</span>
<spanclass="hll"><spanclass="c1"># store action dict and schedule it to run in dt time</span>
<li><p><strong>Line 30</strong>: The <codeclass="docutils literal notranslate"><spanclass="pre">queue_action</span></code> method takes an “Action dict” representing an action the combatant wants to perform next. It must be one of the keyed Actions added to the handler in the <codeclass="docutils literal notranslate"><spanclass="pre">action_classes</span></code> property (<strong>Line 17</strong>). We make no use of the <codeclass="docutils literal notranslate"><spanclass="pre">combatant</span></code> keyword argument since we already know that the combatant is <codeclass="docutils literal notranslate"><spanclass="pre">self.obj</span></code>.</p></li>
<li><p><strong>Line 43</strong>: We simply store the given action dict in the Attribute <codeclass="docutils literal notranslate"><spanclass="pre">action_dict</span></code> on the handler. Simple and effective!</p></li>
<li><p><strong>Line 44</strong>: When you enter e.g. <codeclass="docutils literal notranslate"><spanclass="pre">attack</span></code>, you expect in this type of combat to see the <codeclass="docutils literal notranslate"><spanclass="pre">attack</span></code> command repeat automatically even if you don’t enter anything more. To this end we are looking for a new key in action dicts, indicating that this action should <em>repeat</em> with a certain rate (<codeclass="docutils literal notranslate"><spanclass="pre">dt</span></code>, given in seconds). We make this compatible with all action dicts by simply assuming it’s zero if not specified.</p></li>
</ul>
<p><aclass="reference internal"href="../../../api/evennia.utils.utils.html#evennia.utils.utils.repeat"title="evennia.utils.utils.repeat"><spanclass="xref myst py py-func">evennia.utils.utils.repeat</span></a> and <aclass="reference internal"href="../../../api/evennia.utils.utils.html#evennia.utils.utils.unrepeat"title="evennia.utils.utils.unrepeat"><spanclass="xref myst py py-func">evennia.utils.utils.unrepeat</span></a> are convenient shortcuts to the <aclass="reference internal"href="../../../Components/TickerHandler.html"><spanclass="doc std std-doc">TickerHandler</span></a>. You tell <codeclass="docutils literal notranslate"><spanclass="pre">repeat</span></code> to call a given method/function at a certain rate. What you get back is a reference that you can then later use to ‘un-repeat’ (stop the repeating) later. We make sure to store this reference (we don’t care exactly how it looks, just that we need to store it) in <codeclass="docutils literal notranslate"><spanclass="pre">the</span><spanclass="pre">current_ticket_ref</span></code> Attribute (<strong>Line 26</strong>).</p>
<ulclass="simple">
<li><p><strong>Line 48</strong>: Whenever we queue a new action (it may replace an existing one) we must make sure to kill (un-repeat) any old repeats that are ongoing. Otherwise we would get old actions firing over and over and new ones starting alongside them.</p></li>
<li><p><strong>Line 49</strong>: If <codeclass="docutils literal notranslate"><spanclass="pre">dt</span></code> is set, we call <codeclass="docutils literal notranslate"><spanclass="pre">repeat</span></code> to set up a new repeat action at the given rate. We store this new reference. After <codeclass="docutils literal notranslate"><spanclass="pre">dt</span></code> seconds, the <codeclass="docutils literal notranslate"><spanclass="pre">.execute_next_action</span></code> method will fire (we’ll create that in the next section).</p></li>
</ul>
</section>
<sectionid="execute-an-action">
<h3><spanclass="section-number">10.2.4. </span>Execute an action<aclass="headerlink"href="#execute-an-action"title="Permalink to this headline">¶</a></h3>
<p>This is the method called after <codeclass="docutils literal notranslate"><spanclass="pre">dt</span></code> seconds in <codeclass="docutils literal notranslate"><spanclass="pre">queue_action</span></code>.</p>
<ulclass="simple">
<li><p><strong>Line 5</strong>: We defined a ‘fallback action’. This is used after a one-time action (one that should not repeat) has completed.</p></li>
<li><p><strong>Line 15</strong>: We take the <codeclass="docutils literal notranslate"><spanclass="pre">'key'</span></code> from the <codeclass="docutils literal notranslate"><spanclass="pre">action-dict</span></code> and use the <codeclass="docutils literal notranslate"><spanclass="pre">action_classes</span></code> mapping to get an action class (e.g. <codeclass="docutils literal notranslate"><spanclass="pre">ACtionAttack</span></code> we defined <aclass="reference internal"href="Beginner-Tutorial-Combat-Base.html#attack-action"><spanclass="std std-doc">here</span></a>).</p></li>
<li><p><strong>Line 16</strong>: Here we initialize the action class with the actual current data - the combatant and the <codeclass="docutils literal notranslate"><spanclass="pre">action_dict</span></code>. This calls the <codeclass="docutils literal notranslate"><spanclass="pre">__init__</span></code> method on the class and makes the action ready to use.</p></li>
</ul>
<asideclass="sidebar">
<pclass="sidebar-title">New action-dict keys </p>
<p>To summarize, for twitch-combat use we have now introduced two new keys to action-dicts:</p>
<ulclass="simple">
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">dt</span></code>: How long to wait (in seconds) from queueing the action until it fires.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">repeat</span></code>: Boolean determining if action should automatically be queued again after it fires.</p></li>
</ul>
</aside>
<ulclass="simple">
<li><p><strong>Line 18</strong>: Here we run through the usage methods of the action - where we perform the action. We let the action itself handle all the logics.</p></li>
<li><p><strong>Line 22</strong>: We check for another optional flag on the action-dict: <codeclass="docutils literal notranslate"><spanclass="pre">repeat</span></code>. Unless it’s set, we use the fallback-action defined on <strong>Line 5</strong>. Many actions should not repeat - for example, it would not make sense to do <codeclass="docutils literal notranslate"><spanclass="pre">wield</span></code> for the same weapon over and over.</p></li>
<li><p><strong>Line 27</strong>: It’s important that we know how to stop combat. We will write this method next.</p></li>
</ul>
</section>
<sectionid="checking-and-stopping-combat">
<h3><spanclass="section-number">10.2.5. </span>Checking and stopping combat<aclass="headerlink"href="#checking-and-stopping-combat"title="Permalink to this headline">¶</a></h3>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"The combat is over. You lost."</span><spanclass="p">,</span><spanclass="n">broadcast</span><spanclass="o">=</span><spanclass="kc">False</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"The combat is over. You won!"</span><spanclass="p">,</span><spanclass="n">broadcast</span><spanclass="o">=</span><spanclass="kc">False</span><spanclass="p">)</span>
<spanclass="k">pass</span><spanclass="c1"># We'll finish this last</span>
</pre></div></td></tr></table></div>
</div>
<p>We must make sure to check if combat is over.</p>
<ulclass="simple">
<li><p><strong>Line 12</strong>: With our <codeclass="docutils literal notranslate"><spanclass="pre">.get_sides()</span></code> method we can easily get the two sides of the conflict.</p></li>
<li><p><strong>Lines 18, 19</strong>: We get everyone still alive <em>and still in the same room</em>. The latter condition is important in case we move away from the battle - you can’t hit your enemy from another room.</p></li>
</ul>
<p>In the <codeclass="docutils literal notranslate"><spanclass="pre">stop_method</span></code> we’ll need to do a bunch of cleanup. We’ll hold off on implementing this until we have the Commands written out. Read on.</p>
</section>
</section>
<sectionid="commands">
<h2><spanclass="section-number">10.3. </span>Commands<aclass="headerlink"href="#commands"title="Permalink to this headline">¶</a></h2>
<p>We want each action to map to a <aclass="reference internal"href="../../../Components/Commands.html"><spanclass="doc std std-doc">Command</span></a> - an actual input the player can pass to the game.</p>
<sectionid="base-combat-class">
<h3><spanclass="section-number">10.3.1. </span>Base Combat class<aclass="headerlink"href="#base-combat-class"title="Permalink to this headline">¶</a></h3>
<p>We should try to find the similarities between the commands we’ll need and group them into one parent class. When a Command fires, it will fire the following methods on itself, in sequence:</p>
<spanclass="k">if</span><spanclass="s2">" on "</span><spanclass="ow">in</span><spanclass="n">args</span><spanclass="p">:</span>
<spanclass="n">lhs</span><spanclass="p">,</span><spanclass="n">rhs</span><spanclass="o">=</span><spanclass="n">args</span><spanclass="o">.</span><spanclass="n">split</span><spanclass="p">(</span><spanclass="s2">" on "</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">)</span>
<li><p><strong>Line 23</strong>: If the current location doesn’t allow combat, all combat commands should exit immediately. To stop the command before it reaches the <codeclass="docutils literal notranslate"><spanclass="pre">.func()</span></code>, we must raise the <codeclass="docutils literal notranslate"><spanclass="pre">InterruptCommand()</span></code>.</p></li>
<li><p><strong>Line 49</strong>: It’s convenient to add a helper method for getting the command handler because all our commands will be using it. It in turn calls the class method <codeclass="docutils literal notranslate"><spanclass="pre">get_or_create_combathandler</span></code> we inherit from the parent of <codeclass="docutils literal notranslate"><spanclass="pre">EvAdventureCombatTwitchHandler</span></code>.</p></li>
</ul>
</section>
<sectionid="in-combat-look-command">
<h3><spanclass="section-number">10.3.2. </span>In-combat look command<aclass="headerlink"href="#in-combat-look-command"title="Permalink to this headline">¶</a></h3>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_twitch.py </span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"|r</span><spanclass="si">{</span><spanclass="n">pad</span><spanclass="p">(</span><spanclass="s1">' Combat Status '</span><spanclass="p">,</span><spanclass="w"></span><spanclass="n">width</span><spanclass="o">=</span><spanclass="n">maxwidth</span><spanclass="p">,</span><spanclass="w"></span><spanclass="n">fillchar</span><spanclass="o">=</span><spanclass="s1">'-'</span><spanclass="p">)</span><spanclass="si">}</span><spanclass="s2">|n</span><spanclass="se">\n</span><spanclass="si">{</span><spanclass="n">txt</span><spanclass="si">}</span><spanclass="s2">"</span><spanclass="p">)</span>
</pre></div>
</div>
<p>When in combat we want to be able to do <codeclass="docutils literal notranslate"><spanclass="pre">look</span></code> and get the normal look but with the extra <codeclass="docutils literal notranslate"><spanclass="pre">combat</span><spanclass="pre">summary</span></code> at the end (on the form <codeclass="docutils literal notranslate"><spanclass="pre">Me</span><spanclass="pre">(Hurt)</span> <spanclass="pre">vs</span> <spanclass="pre">Troll</span><spanclass="pre">(Perfect)</span></code>). So</p>
<p>The last line uses Evennia’s <codeclass="docutils literal notranslate"><spanclass="pre">utils.pad</span></code> function to put the text “Combat Status” surrounded by a line on both sides.</p>
<p>The result will be the look command output followed directly by</p>
<p>The ‘do nothing’ command showcases the basic principle of how all following commands work:</p>
<olclass="simple">
<li><p>Get the combathandler (will be created or loaded if it already existed).</p></li>
<li><p>Queue the action by passing its action-dict to the <codeclass="docutils literal notranslate"><spanclass="pre">combathandler.queue_action</span></code> method.</p></li>
<li><p>Confirm to the caller that they now queued this action.</p></li>
</ol>
</section>
<sectionid="attack-command">
<h3><spanclass="section-number">10.3.4. </span>Attack command<aclass="headerlink"href="#attack-command"title="Permalink to this headline">¶</a></h3>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_twitch.py </span>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">attack</span></code> command becomes quite simple because we do all the heavy lifting in the combathandler and in the <codeclass="docutils literal notranslate"><spanclass="pre">ActionAttack</span></code> class. Note that we set <codeclass="docutils literal notranslate"><spanclass="pre">dt</span></code> to a fixed <codeclass="docutils literal notranslate"><spanclass="pre">3</span></code> here, but in a more complex system one could imagine your skills, weapon and circumstance affecting how long your attack will take.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_twitch.py </span>
<spanclass="sa">f</span><spanclass="s2">"'</span><spanclass="si">{</span><spanclass="n">stunt_type</span><spanclass="si">}</span><spanclass="s2">' is not a valid ability. Pick one of"</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"Must give at least a recipient or target."</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"Both ability, recipient and target of stunt must be given."</span><spanclass="p">)</span>
<spanclass="n">combathandler</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"$You() prepare a stunt!"</span><spanclass="p">,</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span><spanclass="p">)</span>
</pre></div>
</div>
<p>This looks much longer, but that is only because the stunt command should understand many different input structures depending on if you are trying to create a advantage or disadvantage, and if an ally or enemy should receive the effect of the stunt.</p>
<p>Note the <codeclass="docutils literal notranslate"><spanclass="pre">enums.ABILITY_REVERSE_MAP</span></code> (created in the <aclass="reference internal"href="Beginner-Tutorial-Utilities.html"><spanclass="doc std std-doc">Utilities lesson</span></a>) being useful to convert your input of ‘str’ into <codeclass="docutils literal notranslate"><spanclass="pre">Ability.STR</span></code> needed by the action dict.</p>
<p>Once we’ve sorted out the string parsing, the <codeclass="docutils literal notranslate"><spanclass="pre">func</span></code> is simple - we find the target and recipient and use them to build the needed action-dict to queue.</p>
</section>
<sectionid="using-items">
<h3><spanclass="section-number">10.3.5. </span>Using items<aclass="headerlink"href="#using-items"title="Permalink to this headline">¶</a></h3>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_twitch.py </span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"What do you want to use?"</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"(You must carry the item to use it.)"</span><spanclass="p">)</span>
<spanclass="sa">f</span><spanclass="s2">"$You() prepare to use </span><spanclass="si">{</span><spanclass="n">item</span><spanclass="o">.</span><spanclass="n">get_display_name</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span><spanclass="p">)</span><spanclass="si">}</span><spanclass="s2">!"</span><spanclass="p">,</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span>
<spanclass="p">)</span>
</pre></div>
</div>
<p>To use an item, we need to make sure we are carrying it. Luckily our work in the <aclass="reference internal"href="Beginner-Tutorial-Equipment.html"><spanclass="doc std std-doc">Equipment lesson</span></a> gives us easy methods we can use to search for suitable objects.</p>
</section>
<sectionid="wielding-new-weapons-and-equipment">
<h3><spanclass="section-number">10.3.6. </span>Wielding new weapons and equipment<aclass="headerlink"href="#wielding-new-weapons-and-equipment"title="Permalink to this headline">¶</a></h3>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_twitch.py </span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"What do you want to wield?"</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"(You must carry the item to wield it.)"</span><spanclass="p">)</span>
<spanclass="n">combathandler</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"$You() reach for </span><spanclass="si">{</span><spanclass="n">item</span><spanclass="o">.</span><spanclass="n">get_display_name</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span><spanclass="p">)</span><spanclass="si">}</span><spanclass="s2">!"</span><spanclass="p">,</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span><spanclass="p">)</span>
</pre></div>
</div>
<p>The Wield command follows the same pattern as other commands.</p>
</section>
</section>
<sectionid="grouping-commands-for-use">
<h2><spanclass="section-number">10.4. </span>Grouping Commands for use<aclass="headerlink"href="#grouping-commands-for-use"title="Permalink to this headline">¶</a></h2>
<p>To make these commands available to use we must add them to a <aclass="reference internal"href="../../../Components/Command-Sets.html"><spanclass="doc std std-doc">Command Set</span></a>.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_twitch.py </span>
<p>The first cmdset, <codeclass="docutils literal notranslate"><spanclass="pre">TwitchCombatCmdSet</span></code> is intended to be added to the Character. We can do so permanently by adding the cmdset to the default character cmdset (as outlined in the <aclass="reference internal"href="../Part1/Beginner-Tutorial-Adding-Commands.html"><spanclass="doc std std-doc">Beginner Command lesson</span></a>). In the testing section below, we’ll do this in another way.</p>
<p>What about that <codeclass="docutils literal notranslate"><spanclass="pre">TwitchLookCmdSet</span></code>? We can’t add it to our character permanently, because we only want this particular version of <codeclass="docutils literal notranslate"><spanclass="pre">look</span></code> to operate while we are in combat.</p>
<p>We must make sure to add and clean this up when combat starts and ends.</p>
<sectionid="combat-startup-and-cleanup">
<h3><spanclass="section-number">10.4.1. </span>Combat startup and cleanup<aclass="headerlink"href="#combat-startup-and-cleanup"title="Permalink to this headline">¶</a></h3>
<spanclass="hll"><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">queue_action</span><spanclass="p">({</span><spanclass="s2">"key"</span><spanclass="p">:</span><spanclass="s2">"hold"</span><spanclass="p">,</span><spanclass="s2">"dt"</span><spanclass="p">:</span><spanclass="mi">0</span><spanclass="p">})</span><spanclass="c1"># make sure ticker is killed</span>
<p>Now that we have the Look command set, we can finish the Twitch combat handler.</p>
<ulclass="simple">
<li><p><strong>Line 9</strong>: The <codeclass="docutils literal notranslate"><spanclass="pre">at_init</span></code> method is a standard Evennia method available on all typeclassed entities (including <codeclass="docutils literal notranslate"><spanclass="pre">Scripts</span></code>, which is what our combat handler is). Unlike <codeclass="docutils literal notranslate"><spanclass="pre">at_object_creation</span></code> (which only fires once, when the object is first created), <codeclass="docutils literal notranslate"><spanclass="pre">at_init</span></code> will be called every time the object is loaded into memory (normally after you do a server <codeclass="docutils literal notranslate"><spanclass="pre">reload</span></code>). So we add the <codeclass="docutils literal notranslate"><spanclass="pre">TwitchLookCmdSet</span></code> here. We do so non-persistently, since we don’t want to get an ever growing number of cmdsets added every time we reload.</p></li>
<li><p><strong>Line 13</strong>: By queuing a hold action with <codeclass="docutils literal notranslate"><spanclass="pre">dt</span></code> of <codeclass="docutils literal notranslate"><spanclass="pre">0</span></code>, we make sure to kill the <codeclass="docutils literal notranslate"><spanclass="pre">repeat</span></code> action that is going on. If not, it would still fire later - and find that the combat handler is gone.</p></li>
<li><p><strong>Line 14</strong>: If looking at how we defined the <codeclass="docutils literal notranslate"><spanclass="pre">get_or_create_combathandler</span></code> classmethod (the one we have been using to get/create the combathandler during the combat), you’ll see that it caches the handler as <codeclass="docutils literal notranslate"><spanclass="pre">.ndb.combathandler</span></code> on the object we send to it. So we delete that cached reference here to make sure it’s gone.</p></li>
<li><p><strong>Line 15</strong>: We remove the look-cmdset from ourselves (remember <codeclass="docutils literal notranslate"><spanclass="pre">self.obj</span></code> is you, the combatant that now just finished combat).</p></li>
<li><p><strong>Line 16</strong>: We delete the combat handler itself.</p></li>
</ul>
</section>
</section>
<sectionid="unit-testing">
<h2><spanclass="section-number">10.5. </span>Unit Testing<aclass="headerlink"href="#unit-testing"title="Permalink to this headline">¶</a></h2>
<p>For examples of unit tests, see <codeclass="docutils literal notranslate"><spanclass="pre">evennia/contrib/tutorials</span></code>, in <aclass="reference internal"href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia-contrib-tutorials-evadventure-tests-test-combat"><spanclass="std std-ref">evadventure/tests/test_combat.py</span></a> for an example of a full suite of combat tests.</p>
<div><p>Create <codeclass="docutils literal notranslate"><spanclass="pre">evadventure/tests/test_combat.py</span></code> (if you don’t already have it).</p>
</div></blockquote>
<p>Both the Twitch command handler and commands can and should be unit tested. Testing of commands are made easier by Evennia’s special <codeclass="docutils literal notranslate"><spanclass="pre">EvenniaCommandTestMixin</span></code> class. This makes the <codeclass="docutils literal notranslate"><spanclass="pre">.call</span></code> method available and makes it easy to check if a command returns what you expect.</p>
<p>Here’s an example:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/tests/test_combat.py </span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">call</span><spanclass="p">(</span><spanclass="n">combat_twitch</span><spanclass="p">,</span><spanclass="n">CmdHold</span><spanclass="p">(),</span><spanclass="s2">""</span><spanclass="p">,</span><spanclass="s2">"You hold back, doing nothing"</span><spanclass="p">)</span>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">EvenniaCommandTestMixin</span></code> as a few default objects, including <codeclass="docutils literal notranslate"><spanclass="pre">self.char1</span></code>, which we make use of here.</p>
<p>The two <codeclass="docutils literal notranslate"><spanclass="pre">@patch</span></code> lines are Python <aclass="reference external"href="https://realpython.com/primer-on-python-decorators/">decorators</a> that ‘patch’ the <codeclass="docutils literal notranslate"><spanclass="pre">test_hold_command</span></code> method. What they do is basically saying “in the following method, whenever any code tries to access <codeclass="docutils literal notranslate"><spanclass="pre">evadventure.combat_twitch.un/repeat</span></code>, just return a Mocked object instead”.</p>
<p>We do this patching as an easy way to avoid creating timers in the unit test - these timers would finish after the test finished (which includes deleting its objects) and thus fail.</p>
<p>Inside the test, we use the <codeclass="docutils literal notranslate"><spanclass="pre">self.call()</span></code> method to explicitly fire the Command (with no argument) and check that the output is what we expect. Lastly we check that the combathandler is set up correctly, having stored the action-dict on itself.</p>
</section>
<sectionid="a-small-combat-test">
<h2><spanclass="section-number">10.6. </span>A small combat test<aclass="headerlink"href="#a-small-combat-test"title="Permalink to this headline">¶</a></h2>
<p>You can find an example batch-command script at <codeclass="docutils literal notranslate"><spanclass="pre">evennia/contrib/tutorials/evadventure</span></code>, in <aclass="reference external"href="https://github.com/evennia/evennia/blob/main/evennia/contrib/tutorials/evadventure/batchscripts/twitch_combat_demo.ev">batchscripts/twitch_combat_demo.ev</a></p>
<p>Showing that the individual pieces of code works (unit testing) is not enough to be sure that your combat system is actually working. We need to test all the pieces <em>together</em>. This is often called <em>functional testing</em>. While functional testing can also be automated, wouldn’t it be fun to be able to actually see our code in action?</p>
<p>This is what we need for a minimal test:</p>
<ulclass="simple">
<li><p>A room with combat enabled.</p></li>
<li><p>An NPC to attack (it won’t do anything back yet since we haven’t added any AI)</p></li>
<li><p>A weapon we can <codeclass="docutils literal notranslate"><spanclass="pre">wield</span></code></p></li>
<li><p>An item (like a potion) we can <codeclass="docutils literal notranslate"><spanclass="pre">use</span></code>.</p></li>
</ul>
<p>While you can create these manually in-game, it can be convenient to create a <aclass="reference internal"href="../../../Components/Batch-Command-Processor.html"><spanclass="doc std std-doc">batch-command script</span></a> to set up your testing environment.</p>
<blockquote>
<div><p>create a new subfolder <codeclass="docutils literal notranslate"><spanclass="pre">evadventure/batchscripts/</span></code> (if it doesn’t already exist)</p>
</div></blockquote>
<blockquote>
<div><p>create a new file <codeclass="docutils literal notranslate"><spanclass="pre">evadventure/combat_demo.ev</span></code> (note, it’s <codeclass="docutils literal notranslate"><spanclass="pre">.ev</span></code> not <codeclass="docutils literal notranslate"><spanclass="pre">.py</span></code>!)</p>
</div></blockquote>
<p>A batch-command file is a text file with normal in-game commands, one per line, separated by lines starting with <codeclass="docutils literal notranslate"><spanclass="pre">#</span></code> (these are required between all command lines). Here’s how it looks:</p>
<p>This should place you in the arena with the dummy (if not, check for errors in the output! Use <codeclass="docutils literal notranslate"><spanclass="pre">objects</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">delete</span></code> commands to list and delete objects if you need to start over. )</p>
<p>You can now try <codeclass="docutils literal notranslate"><spanclass="pre">attack</span><spanclass="pre">dummy</span></code> and should be able to pound away at the dummy (lower its health to test destroying it). Use <codeclass="docutils literal notranslate"><spanclass="pre">back</span></code> to ‘flee’ the combat.</p>
</section>
<sectionid="conclusions">
<h2><spanclass="section-number">10.7. </span>Conclusions<aclass="headerlink"href="#conclusions"title="Permalink to this headline">¶</a></h2>
<p>This was a big lesson! Even though our combat system is not very complex, there are still many moving parts to keep in mind.</p>
<p>Also, while pretty simple, there is also a lot of growth possible with this system. You could easily expand from this or use it as inspiration for your own game.</p>
<p>Next we’ll try to achieve the same thing within a turn-based framework!</p>