<h1><spanclass="section-number">11. </span>Turnbased Combat<aclass="headerlink"href="#turnbased-combat"title="Permalink to this headline">¶</a></h1>
<p>In this lesson we will be building on the <aclass="reference internal"href="Beginner-Tutorial-Combat-Base.html"><spanclass="doc std std-doc">combat base</span></a> to implement a combat system that works in turns and where you select your actions in a menu, like this:</p>
<div><p>Note that this documentation doesn’t show in-game colors. Also, if you interested in an alternative, see the <aclass="reference internal"href="Beginner-Tutorial-Combat-Twitch.html"><spanclass="doc std std-doc">previous lesson</span></a> where we implemented a ‘twitch’-like combat system based on entering direct commands for every action.</p>
</div></blockquote>
<p>With ‘turnbased’ combat, we mean combat that ‘ticks’ along at a slower pace, slow enough to allow the participants to select their options in a menu (the menu is not strictly necessary, but it’s a good way to learn how to make menus as well). Their actions are queued and will be executed when the turn timer runs out. To avoid waiting unnecessarily, we will also move on to the next round whenever everyone has made their choices.</p>
<p>The advantage of a turnbased system is that it removes player speed from the equation; your prowess in combat does not depend on how quickly you can enter a command. For RPG-heavy games you could also allow players time to make RP emotes during the rounds of combat to flesh out the action.</p>
<p>The advantage of using a menu is that you have all possible actions directly available to you, making it beginner friendly and easy to know what you can do. It also means a lot less writing which can be an advantage to some players.</p>
<sectionid="general-principle">
<h2><spanclass="section-number">11.1. </span>General Principle<aclass="headerlink"href="#general-principle"title="Permalink to this headline">¶</a></h2>
<asideclass="sidebar">
<p>An example of an implemented Turnbased combat system can be found in <aclass="reference internal"href="../../../api/evennia.contrib.tutorials.evadventure.combat_turnbased.html#evennia-contrib-tutorials-evadventure-combat-turnbased"><spanclass="std std-ref">evennia/contrib/tutorials/evadventure/combat_turnbased.py</span></a>.</p>
</aside>
<p>Here is the general principle of the Turnbased combat handler:</p>
<ulclass="simple">
<li><p>The turnbased version of the CombatHandler will be stored on the <em>current location</em>. That means that there will only be one combat per location. Anyone else starting combat will join the same handler and be assigned a side to fight on.</p></li>
<li><p>The handler will run a central timer of 30s (in this example). When it fires, all queued actions will be executed. If everyone has submitted their actions, this will happen immediately when the last one submits.</p></li>
<li><p>While in combat you will not be able to move around - you are stuck in the room. Fleeing combat is a separate action that takes a few turns to complete (we will need to create this).</p></li>
<li><p>Starting the combat is done via the <codeclass="docutils literal notranslate"><spanclass="pre">attack</span><spanclass="pre"><target></span></code> command. After that you are in the combat menu and will use the menu for all subsequent actions.</p></li>
</ul>
</section>
<sectionid="turnbased-combat-handler">
<h2><spanclass="section-number">11.2. </span>Turnbased combat handler<aclass="headerlink"href="#turnbased-combat-handler"title="Permalink to this headline">¶</a></h2>
<blockquote>
<div><p>Create a new module <codeclass="docutils literal notranslate"><spanclass="pre">evadventure/combat_turnbased.py</span></code>.</p>
</div></blockquote>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<p>We leave a placeholder for the <codeclass="docutils literal notranslate"><spanclass="pre">"flee"</span></code> action since we haven’t created it yet.</p>
<p>Since the turnbased combat handler is shared between all combatants, we need to store references to those combatants on the handler, in the <codeclass="docutils literal notranslate"><spanclass="pre">combatants</span></code><aclass="reference internal"href="../../../api/evennia.typeclasses.attributes.html#evennia.typeclasses.attributes.Attribute"title="evennia.typeclasses.attributes.Attribute"><spanclass="xref myst py py-class">Attribute</span></a>. In the same way we must store a <em>matrix</em> of who has advantage/disadvantage against whom. We must also track who is <em>fleeing</em>, in particular how long they have been fleeing, since they will be leaving combat after that time.</p>
<sectionid="getting-the-sides-of-combat">
<h3><spanclass="section-number">11.2.1. </span>Getting the sides of combat<aclass="headerlink"href="#getting-the-sides-of-combat"title="Permalink to this headline">¶</a></h3>
<p>The two sides are different depending on if we are in an <aclass="reference internal"href="Beginner-Tutorial-Rooms.html"><spanclass="doc std std-doc">PvP room</span></a> or not: In a PvP room everyone else is your enemy. Otherwise only NPCs in combat is your enemy (you are assumed to be teaming up with your fellow players).</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<p>Note that since the <codeclass="docutils literal notranslate"><spanclass="pre">EvadventureCombatBaseHandler</span></code> (which our turnbased handler is based on) is a <aclass="reference internal"href="../../../Components/Scripts.html"><spanclass="doc std std-doc">Script</span></a>, it provides many useful features. For example <codeclass="docutils literal notranslate"><spanclass="pre">self.obj</span></code> is the entity on which this Script ‘sits’. Since we are planning to put this handler on the current location, then <codeclass="docutils literal notranslate"><spanclass="pre">self.obj</span></code> will be that Room.</p>
<p>All we do here is check if it’s a PvP room or not and use this to figure out who would be an ally or an enemy. Note that the <codeclass="docutils literal notranslate"><spanclass="pre">combatant</span></code> is <em>not</em> included in the <codeclass="docutils literal notranslate"><spanclass="pre">allies</span></code> return - we’ll need to remember this.</p>
</section>
<sectionid="tracking-advantage-disadvantage">
<h3><spanclass="section-number">11.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_turnbased.py</span>
<p>We use the <codeclass="docutils literal notranslate"><spanclass="pre">advantage/disadvantage_matrix</span></code> Attributes to track who has advantage against whom.</p>
<asideclass="sidebar">
<pclass="sidebar-title">.pop()</p>
<p>The Python <codeclass="docutils literal notranslate"><spanclass="pre">.pop()</span></code> method exists on lists and dicts as well as some other iterables. It ‘pops’ and returns an element from the container. For a list, it’s either popped by index or by popping the last element. For a dict (like here), a specific key must be given to pop. If you don’t provide a default value as a second element, an error will be raised if the key you try to pop is not found.</p>
</aside>
<p>In the <codeclass="docutils literal notranslate"><spanclass="pre">has</span><spanclass="pre">dis/advantage</span></code> methods we <codeclass="docutils literal notranslate"><spanclass="pre">pop</span></code> the target from the matrix which will result either in the value <codeclass="docutils literal notranslate"><spanclass="pre">True</span></code> or <codeclass="docutils literal notranslate"><spanclass="pre">False</span></code> (the default value we give to <codeclass="docutils literal notranslate"><spanclass="pre">pop</span></code> if the target is not in the matrix). This means that the advantage, once gained, can only be used once.</p>
<p>We also consider everyone to have advantage against fleeing combatants.</p>
</section>
<sectionid="adding-and-removing-combatants">
<h3><spanclass="section-number">11.2.3. </span>Adding and removing combatants<aclass="headerlink"href="#adding-and-removing-combatants"title="Permalink to this headline">¶</a></h3>
<p>Since the combat handler is shared we must be able to add- and remove combatants easily.
This is new compared to the base handler.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<spanclass="c1"># clean up menu if it exists</span>
<spanclass="c1"># TODO!</span>
</pre></div>
</div>
<p>We simply add the the combatant with the fallback action-dict to start with. We return a <codeclass="docutils literal notranslate"><spanclass="pre">bool</span></code> from <codeclass="docutils literal notranslate"><spanclass="pre">add_combatant</span></code> so that the calling function will know if they were actually added anew or not (we may want to do some extra setup if they are new).</p>
<p>For now we just <codeclass="docutils literal notranslate"><spanclass="pre">pop</span></code> the combatant, but in the future we’ll need to do some extra cleanup of the menu when combat ends (we’ll get to that).</p>
</section>
<sectionid="flee-action">
<h3><spanclass="section-number">11.2.4. </span>Flee Action<aclass="headerlink"href="#flee-action"title="Permalink to this headline">¶</a></h3>
<p>Since you can’t just move away from the room to flee turnbased combat, we need to add a new <codeclass="docutils literal notranslate"><spanclass="pre">CombatAction</span></code> subclass like the ones we created in the <aclass="reference internal"href="Beginner-Tutorial-Combat-Base.html#actions"><spanclass="std std-doc">base combat lesson</span></a>.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<p>We create the action to make use of the <codeclass="docutils literal notranslate"><spanclass="pre">fleeing_combatants</span></code> dict we set up in the combat handler. This dict stores the fleeing combatant along with the <codeclass="docutils literal notranslate"><spanclass="pre">turn</span></code> its fleeing started. If performing the <codeclass="docutils literal notranslate"><spanclass="pre">flee</span></code> action multiple times, we will just display how many turns are remaining.</p>
<p>Finally, we make sure to add our new <codeclass="docutils literal notranslate"><spanclass="pre">CombatActionFlee</span></code> to the <codeclass="docutils literal notranslate"><spanclass="pre">action_classes</span></code> registry on the combat handler.</p>
</section>
<sectionid="queue-action">
<h3><spanclass="section-number">11.2.5. </span>Queue action<aclass="headerlink"href="#queue-action"title="Permalink to this headline">¶</a></h3>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<p>To queue an action, we simply store its <codeclass="docutils literal notranslate"><spanclass="pre">action_dict</span></code> with the combatant in the <codeclass="docutils literal notranslate"><spanclass="pre">combatants</span></code> Attribute.</p>
<p>We use a Python <codeclass="docutils literal notranslate"><spanclass="pre">set()</span></code> to track who has queued an action this turn. If all combatants have entered a new (or renewed) action this turn, we use the <codeclass="docutils literal notranslate"><spanclass="pre">.force_repeat()</span></code> method, which is available on all <aclass="reference internal"href="../../../Components/Scripts.html"><spanclass="doc std std-doc">Scripts</span></a>. When this is called, the next round will fire immediately instead of waiting until it times out.</p>
<h3><spanclass="section-number">11.2.6. </span>Execute an action and tick the round<aclass="headerlink"href="#execute-an-action-and-tick-the-round"title="Permalink to this headline">¶</a></h3>
<spanclass="hll"><spanclass="n">random</span><spanclass="o">.</span><spanclass="n">shuffle</span><spanclass="p">(</span><spanclass="n">combatants</span><spanclass="p">)</span><spanclass="c1"># shuffles in place</span>
</span>
<spanclass="c1"># do everyone's next queued combat action</span>
<p>Our action-execution consists of two parts - the <codeclass="docutils literal notranslate"><spanclass="pre">execute_next_action</span></code> (which was defined in the parent class for us to implement) and the <codeclass="docutils literal notranslate"><spanclass="pre">at_repeat</span></code> method which is a part of the <aclass="reference internal"href="../../../Components/Scripts.html"><spanclass="doc std std-doc">Script</span></a></p>
<li><p><strong>Line 13</strong>: We get the <codeclass="docutils literal notranslate"><spanclass="pre">action_dict</span></code> from the <codeclass="docutils literal notranslate"><spanclass="pre">combatants</span></code> Attribute. We return the <codeclass="docutils literal notranslate"><spanclass="pre">fallback_action_dict</span></code> if nothing was queued (this defaults to <codeclass="docutils literal notranslate"><spanclass="pre">hold</span></code>).</p></li>
<li><p><strong>Line 16</strong>: We use the <codeclass="docutils literal notranslate"><spanclass="pre">key</span></code> of the <codeclass="docutils literal notranslate"><spanclass="pre">action_dict</span></code> (which would be something like “attack”, “use”, “wield” etc) to get the class of the matching Action from the <codeclass="docutils literal notranslate"><spanclass="pre">action_classes</span></code> dictionary.</p></li>
<li><p><strong>Line 17</strong>: Here the action class is instantiated with the combatant and action dict, making it ready to execute. This is then executed on the following lines.</p></li>
<li><p><strong>Line 22</strong>: We introduce a new optional <codeclass="docutils literal notranslate"><spanclass="pre">action-dict</span></code> here, the boolean <codeclass="docutils literal notranslate"><spanclass="pre">repeat</span></code> key. This allows us to re-queue the action. If not the fallback action will be used.</p></li>
</ul>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">at_repeat</span></code> is called repeatedly every <codeclass="docutils literal notranslate"><spanclass="pre">interval</span></code> seconds that the Script fires. This is what we use to track when each round ends.</p>
<ulclass="simple">
<li><p><strong>Lines 43</strong>: In this example, we have no internal order between actions. So we simply randomize in which order they fire.</p></li>
<li><p><strong>Line 49</strong>: This <codeclass="docutils literal notranslate"><spanclass="pre">set</span></code> was assigned to in the <codeclass="docutils literal notranslate"><spanclass="pre">queue_action</span></code> method to know when everyone submitted a new action. We must make sure to unset it here before the next round.</p></li>
</ul>
</section>
<sectionid="check-and-stop-combat">
<h3><spanclass="section-number">11.2.7. </span>Check and stop combat<aclass="headerlink"href="#check-and-stop-combat"title="Permalink to this headline">¶</a></h3>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"|r$You() $conj(fall) to the ground, defeated.|n"</span><spanclass="p">,</span><spanclass="n">combatant</span><spanclass="o">=</span><spanclass="n">combatant</span><spanclass="p">)</span>
</span><spanclass="c1"># if they are still alive/fleeing and have been fleeing long enough, escape</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"|y$You() successfully $conj(flee) from combat.|n"</span><spanclass="p">,</span><spanclass="n">combatant</span><spanclass="o">=</span><spanclass="n">combatant</span><spanclass="p">)</span>
<spanclass="n">txt</span><spanclass="o">=</span><spanclass="p">[</span><spanclass="sa">f</span><spanclass="s2">"The combat is over. </span><spanclass="si">{</span><spanclass="n">still_standing</span><spanclass="si">}</span><spanclass="s2"> are still standing."</span><spanclass="p">]</span>
<spanclass="k">else</span><spanclass="p">:</span>
<spanclass="n">txt</span><spanclass="o">=</span><spanclass="p">[</span><spanclass="s2">"The combat is over. No-one stands as the victor."</span><spanclass="p">]</span>
<spanclass="n">txt</span><spanclass="o">.</span><spanclass="n">append</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="n">knocked_out</span><spanclass="si">}</span><spanclass="s2"> were taken down, but will live."</span><spanclass="p">)</span>
<spanclass="n">txt</span><spanclass="o">.</span><spanclass="n">append</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="n">killed</span><spanclass="si">}</span><spanclass="s2"> were killed."</span><spanclass="p">)</span>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">check_stop_combat</span></code> is called at the end of the round. We want to figure out who is dead and if one of the ‘sides’ won.</p>
<ulclass="simple">
<li><p><strong>Lines 28-38</strong>: We go over all combatants and determine if they are out of HP. If so we fire the relevant hooks and add them to the <codeclass="docutils literal notranslate"><spanclass="pre">defeated_combatants</span></code> Attribute.</p></li>
<li><p><strong>Line 38</strong>: For all surviving combatants, we make sure give them the <codeclass="docutils literal notranslate"><spanclass="pre">fallback_action_dict</span></code>.</p></li>
<li><p><strong>Lines 41-46</strong>: The <codeclass="docutils literal notranslate"><spanclass="pre">fleeing_combatant</span></code> Attribute is a dict on the form <codeclass="docutils literal notranslate"><spanclass="pre">{fleeing_combatant:</span><spanclass="pre">turn_number}</span></code>, tracking when they first started fleeing. We compare this with the current turn number and the <codeclass="docutils literal notranslate"><spanclass="pre">flee_timeout</span></code> to see if they now flee and should be allowed to be removed from combat.</p></li>
<li><p><strong>Lines 49-56</strong>: Here on we are determining if one ‘side’ of the conflict has defeated the other side.</p></li>
<li><p><strong>Line 60</strong>: The <codeclass="docutils literal notranslate"><spanclass="pre">list_to_string</span></code> Evennia utility converts a list of entries, like <codeclass="docutils literal notranslate"><spanclass="pre">["a",</span><spanclass="pre">"b",</span><spanclass="pre">"c"</span></code> to a nice string <codeclass="docutils literal notranslate"><spanclass="pre">"a,</span><spanclass="pre">b</span><spanclass="pre">and</span><spanclass="pre">c"</span></code>. We use this to be able to present some nice ending messages to the combatants.</p></li>
</ul>
</section>
<sectionid="start-combat">
<h3><spanclass="section-number">11.2.8. </span>Start combat<aclass="headerlink"href="#start-combat"title="Permalink to this headline">¶</a></h3>
<p>Since we are using the timer-component of the <aclass="reference internal"href="../../../Components/Scripts.html"><spanclass="doc std std-doc">Script</span></a> to tick our combat, we also need a helper method to ‘start’ that.</p>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">start(**kwargs)</span></code> method is a method on the Script, and will make it start to call <codeclass="docutils literal notranslate"><spanclass="pre">at_repeat</span></code> every <codeclass="docutils literal notranslate"><spanclass="pre">interval</span></code> seconds. We will pass that <codeclass="docutils literal notranslate"><spanclass="pre">interval</span></code> inside <codeclass="docutils literal notranslate"><spanclass="pre">kwargs</span></code> (so for example, we’ll do <codeclass="docutils literal notranslate"><spanclass="pre">combathandler.start_combat(interval=30)</span></code> later).</p>
</section>
</section>
<sectionid="using-evmenu-for-the-combat-menu">
<h2><spanclass="section-number">11.3. </span>Using EvMenu for the combat menu<aclass="headerlink"href="#using-evmenu-for-the-combat-menu"title="Permalink to this headline">¶</a></h2>
<p>The <em>EvMenu</em> used to create in-game menues in Evennia. We used a simple EvMenu already in the <aclass="reference internal"href="Beginner-Tutorial-Chargen.html"><spanclass="doc std std-doc">Character Generation Lesson</span></a>. This time we’ll need to be a bit more advanced. While <aclass="reference internal"href="../../../Components/EvMenu.html"><spanclass="doc std std-doc">The EvMenu documentation</span></a> describe its functionality in more detail, we will give a quick overview of how it works here.</p>
<p>An EvMenu is made up of <em>nodes</em>, which are regular functions on this form (somewhat simplified here, there are more options):</p>
<spanclass="s2">"key"</span><spanclass="p">:</span><spanclass="s2">"Option 1"</span><spanclass="p">,</span><spanclass="c1"># skip this to get a number</span>
<spanclass="s2">"desc"</span><spanclass="p">:</span><spanclass="s2">"Describing what happens when choosing this option."</span>
<spanclass="s2">"goto"</span><spanclass="p">:</span><spanclass="s2">"name of the node to go to"</span><spanclass="c1"># OR (callable, {kwargs}}) returning said name</span>
<p>So basically each node takes the arguments of <codeclass="docutils literal notranslate"><spanclass="pre">caller</span></code> (the one using the menu), <codeclass="docutils literal notranslate"><spanclass="pre">raw_string</span></code> (the empty string or what the user input on the <em>previous node</em>) and <codeclass="docutils literal notranslate"><spanclass="pre">**kwargs</span></code> which can be used to pass data from node to node. It returns <codeclass="docutils literal notranslate"><spanclass="pre">text</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">options</span></code>.</p>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">text</span></code> is what the user will see when entering this part of the menu, such as “Choose who you want to attack!”. The <codeclass="docutils literal notranslate"><spanclass="pre">options</span></code> is a list of dicts describing each option. They will appear as a multi-choice list below the node text (see the example at the top of this lesson page).</p>
<p>When we create the EvMenu later, we will create a <em>node index</em> - a mapping between a unique name and these “node functions”. So something like this:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># example of a EvMenu node index</span>
<p>Each <codeclass="docutils literal notranslate"><spanclass="pre">option</span></code> dict has a key <codeclass="docutils literal notranslate"><spanclass="pre">"goto"</span></code> that determines which node the player should jump to if they choose that option. Inside the menu, each node needs to be referenced with these names (like <codeclass="docutils literal notranslate"><spanclass="pre">"start"</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">"node1"</span></code> etc).</p>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">"goto"</span></code> value of each option can either specify the name directly (like <codeclass="docutils literal notranslate"><spanclass="pre">"node1"</span></code>) <em>or</em> it can be given as a tuple <codeclass="docutils literal notranslate"><spanclass="pre">(callable,</span><spanclass="pre">{keywords})</span></code>. This <codeclass="docutils literal notranslate"><spanclass="pre">callable</span></code> is <em>called</em> and is expected to in turn return the next node-name to use (like <codeclass="docutils literal notranslate"><spanclass="pre">"node1"</span></code>).</p>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">callable</span></code> (often called a “goto callable”) looks very similar to a node function:</p>
<spanclass="c1"># do whatever is needed to determine the next node</span>
<spanclass="k">return</span><spanclass="n">nodename</span><spanclass="c1"># also nodename, dict works</span>
</pre></div>
</div>
<asideclass="sidebar">
<pclass="sidebar-title">Separating node-functions from goto callables</p>
<p>To make node-functions clearly separate from goto-callables, Evennia docs always prefix node-functions with <codeclass="docutils literal notranslate"><spanclass="pre">node_</span></code> and menu goto-functions with an underscore <codeclass="docutils literal notranslate"><spanclass="pre">_</span></code> (which is also making goto-functions ‘private’ in Python lingo).</p>
</aside>
<p>Here, <codeclass="docutils literal notranslate"><spanclass="pre">caller</span></code> is still the one using the menu and <codeclass="docutils literal notranslate"><spanclass="pre">raw_string</span></code> is the actual string you entered to choose this option. <codeclass="docutils literal notranslate"><spanclass="pre">**kwargs</span></code> is the keywords you added to the <codeclass="docutils literal notranslate"><spanclass="pre">(callable,</span><spanclass="pre">{keywords})</span></code> tuple.</p>
<p>The goto-callable must return the name of the next node. Optionally, you can return both <codeclass="docutils literal notranslate"><spanclass="pre">nodename,</span><spanclass="pre">{kwargs}</span></code>. If you do the next node will get those kwargs as ingoing <codeclass="docutils literal notranslate"><spanclass="pre">**kwargs</span></code>. This way you can pass information from one node to the next. A special feature is that if <codeclass="docutils literal notranslate"><spanclass="pre">nodename</span></code> is returned as <codeclass="docutils literal notranslate"><spanclass="pre">None</span></code>, then the <em>current</em> node will be <em>rerun</em> again.</p>
<p>Here’s a (somewhat contrived) example of how the goto-callable and node-function hang together:</p>
<spanclass="k">return</span><spanclass="s2">"node2"</span><spanclass="p">,</span><spanclass="p">{</span><spanclass="s2">"info_number"</span><spanclass="p">:</span><spanclass="n">info_number</span><spanclass="p">}</span><spanclass="c1"># will be **kwargs when "node2" runs next</span>
<h2><spanclass="section-number">11.4. </span>Menu for Turnbased combat<aclass="headerlink"href="#menu-for-turnbased-combat"title="Permalink to this headline">¶</a></h2>
<p>Our combat menu will be pretty simple. We will have one central menu node with options indicating all the different actions of combat. When choosing an action in the menu, the player should be asked a series of question, each specifying one piece of information needed for that action. The last step will be the build this information into an <codeclass="docutils literal notranslate"><spanclass="pre">action-dict</span></code> we can queue with the combathandler.</p>
<p>To understand the process, here’s how the action selection will work (read left to right):</p>
<p>Looking at the above table we can see that we have <em>a lot</em> of re-use. The selection of allied/enemy/target/recipient/item represent nodes that can be shared by different actions.</p>
<p>Each of these actions also follow a linear sequence, like the step-by step ‘wizard’ you see in some software. We want to be able to step back and forth in each sequence, and also abort the action if you change your mind along the way.</p>
<p>After queueing the action, we should always go back to the base node where we will wait until the round ends and all actions are executed.</p>
<p>We will create a few helpers to make our particular menu easy to work with.</p>
<sectionid="the-node-index">
<h3><spanclass="section-number">11.4.1. </span>The node index<aclass="headerlink"href="#the-node-index"title="Permalink to this headline">¶</a></h3>
<p>These are the nodes we need for our menu:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># not coded anywhere yet, just noting for reference</span>
<p>All callables are left as <codeclass="docutils literal notranslate"><spanclass="pre">None</span></code> since we haven’t created them yet. But it’s good to note down the expected names because we need them in order to jump from node to node. The important one to note is that <codeclass="docutils literal notranslate"><spanclass="pre">node_combat</span></code> will be the base node we should get back to over and over.</p>
<h3><spanclass="section-number">11.4.2. </span>Getting or setting the combathandler<aclass="headerlink"href="#getting-or-setting-the-combathandler"title="Permalink to this headline">¶</a></h3>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<p>We only add this to not have to write as much when calling this later. We pass <codeclass="docutils literal notranslate"><spanclass="pre">caller.location</span></code>, which is what retrieves/creates the combathandler on the current location. The <codeclass="docutils literal notranslate"><spanclass="pre">interval</span></code> is how often the combathandler (which is a <aclass="reference internal"href="../../../Components/Scripts.html"><spanclass="doc std std-doc">Script</span></a>) will call its <codeclass="docutils literal notranslate"><spanclass="pre">at_repeat</span></code> method. We set the <codeclass="docutils literal notranslate"><spanclass="pre">flee_time</span></code> Attribute at the same time.</p>
</section>
<sectionid="queue-an-action">
<h3><spanclass="section-number">11.4.3. </span>Queue an action<aclass="headerlink"href="#queue-an-action"title="Permalink to this headline">¶</a></h3>
<p>This is our first “goto function”. This will be called to actually queue our finished action-dict with the combat handler. After doing that, it should return us to the base <codeclass="docutils literal notranslate"><spanclass="pre">node_combat</span></code>.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<p>We make one assumption here - that <codeclass="docutils literal notranslate"><spanclass="pre">kwargs</span></code> contains the <codeclass="docutils literal notranslate"><spanclass="pre">action_dict</span></code> key with the action-dict ready to go.</p>
<p>Since this is a goto-callable, we must return the next node to go to. Since this is the last step, we will always go back to the <codeclass="docutils literal notranslate"><spanclass="pre">node_combat</span></code> base node, so that’s what we return.</p>
</section>
<sectionid="rerun-a-node">
<h3><spanclass="section-number">11.4.4. </span>Rerun a node<aclass="headerlink"href="#rerun-a-node"title="Permalink to this headline">¶</a></h3>
<p>A special feature of goto callables is the ability to rerun the same node by returning <codeclass="docutils literal notranslate"><spanclass="pre">None</span></code>.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<p>Using this in an option will rerun the current node, but will preserve the <codeclass="docutils literal notranslate"><spanclass="pre">kwargs</span></code> that were sent in.</p>
</section>
<sectionid="stepping-through-the-wizard">
<h3><spanclass="section-number">11.4.5. </span>Stepping through the wizard<aclass="headerlink"href="#stepping-through-the-wizard"title="Permalink to this headline">¶</a></h3>
<p>Our particualr menu is very symmetric - you select an option and then you will just select a series of option before you come back. So we will make another goto-function to help us easily do this. To understand, let’s first show how we plan to use this:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in the base combat-node function (just shown as an example)</span>
<p>When the user chooses to use an item on an enemy, we will call <codeclass="docutils literal notranslate"><spanclass="pre">_step_wizard</span></code> with two keywords <codeclass="docutils literal notranslate"><spanclass="pre">steps</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">action_dict</span></code>. The first is the <em>sequence</em> of menu nodes we need to guide the player through in order to build up our action-dict.</p>
<p>The latter is the <codeclass="docutils literal notranslate"><spanclass="pre">action_dict</span></code> itself. Each node will gradually fill in the <codeclass="docutils literal notranslate"><spanclass="pre">None</span></code> places in this dict until we have a complete dict and can send it to the <aclass="reference internal"href="#queue-an-action"><spanclass="std std-doc"><codeclass="docutils literal notranslate"><spanclass="pre">_queue_action</span></code></span></a> goto function we defined earlier.</p>
<p>Furthermore, we want the ability to go “back” to the previous node like this:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in some other node (shown only as an example)</span>
<p>Note the use of <codeclass="docutils literal notranslate"><spanclass="pre">**</span></code> here. <codeclass="docutils literal notranslate"><spanclass="pre">{**dict1,</span><spanclass="pre">**dict2}</span></code> is a powerful one-liner syntax to combine two dicts into one. This preserves (and passes on) the incoming <codeclass="docutils literal notranslate"><spanclass="pre">kwargs</span></code> and just adds a new key “step” to it. The end effect is similar to if we had done <codeclass="docutils literal notranslate"><spanclass="pre">kwargs["step"]</span><spanclass="pre">=</span><spanclass="pre">"back"</span></code> on a separate line (except we end up with a <em>new</em><codeclass="docutils literal notranslate"><spanclass="pre">dict</span></code> when using the <codeclass="docutils literal notranslate"><spanclass="pre">**</span></code>-approach).</p>
<p>So let’s implement a <codeclass="docutils literal notranslate"><spanclass="pre">_step_wizard</span></code> goto-function to handle this!</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<p>This depends on passing around <codeclass="docutils literal notranslate"><spanclass="pre">steps</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">step</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">istep</span></code> with the <codeclass="docutils literal notranslate"><spanclass="pre">**kwargs</span></code>. If <codeclass="docutils literal notranslate"><spanclass="pre">step</span></code> is “back” then we will go back in the sequence of <codeclass="docutils literal notranslate"><spanclass="pre">steps</span></code> otherwise forward. We increase/decrease the <codeclass="docutils literal notranslate"><spanclass="pre">istep</span></code> key value to track just where we are.</p>
<p>If we reach the end we call our <codeclass="docutils literal notranslate"><spanclass="pre">_queue_action</span></code> helper function directly. If we back up to the beginning we return to the base node.</p>
<p>We will make one final helper function, to quickly add the <codeclass="docutils literal notranslate"><spanclass="pre">back</span></code> (and <codeclass="docutils literal notranslate"><spanclass="pre">abort</span></code>) options to the nodes that need it:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<p>This is not a goto-function, it’s just a helper that we will call to quickly add these extra options a node’s option list and not have to type it out over and over.</p>
<p>As we’ve seen before, the <codeclass="docutils literal notranslate"><spanclass="pre">back</span></code> option will use the <codeclass="docutils literal notranslate"><spanclass="pre">_step_wizard</span></code> to step back in the wizard. The <codeclass="docutils literal notranslate"><spanclass="pre">abort</span></code> option will simply jump back to the main node, aborting the wizard.</p>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">_default</span></code> option is special. This option key tells EvMenu: “use this option if none of the other match”. That is, if they enter an empty input or garbage, we will just re-display the node. We make sure pass along the <codeclass="docutils literal notranslate"><spanclass="pre">kwargs</span></code> though, so we don’t lose any information of where we were in the wizard.</p>
<p>Finally we are ready to write our menu nodes!</p>
</section>
<sectionid="choosing-targets-and-recipients">
<h3><spanclass="section-number">11.4.6. </span>Choosing targets and recipients<aclass="headerlink"href="#choosing-targets-and-recipients"title="Permalink to this headline">¶</a></h3>
<p>These nodes all work the same: They should present a list of suitable targets/recipients to choose from and then put that result in the action-dict as either <codeclass="docutils literal notranslate"><spanclass="pre">target</span></code> or <codeclass="docutils literal notranslate"><spanclass="pre">recipient</span></code> key.</p>
<spanclass="c1"># almost the same, except using allies + yourself and storing "recipient"</span>
</pre></div></td></tr></table></div>
</div>
<ulclass="simple">
<li><p><strong>Line 11</strong>: Here we use <codeclass="docutils literal notranslate"><spanclass="pre">combathandler.get_sides(caller)</span></code> to get the ‘enemies’ from the perspective of <codeclass="docutils literal notranslate"><spanclass="pre">caller</span></code> (the one using the menu).</p></li>
<li><p><strong>Line 13-31</strong>: This is a loop over all enemies we found.</p>
<ul>
<li><p><strong>Line 15</strong>: We use <codeclass="docutils literal notranslate"><spanclass="pre">target.get_display_name(caller)</span></code>. This method (a default method on all Evennia <codeclass="docutils literal notranslate"><spanclass="pre">Objects</span></code>) allows the target to return a name while being aware of who’s asking. It’s what makes an admin see <codeclass="docutils literal notranslate"><spanclass="pre">Name</span><spanclass="pre">(#5)</span></code> while a regular user just sees <codeclass="docutils literal notranslate"><spanclass="pre">Name</span></code>. If you didn’t care about that, you could just do <codeclass="docutils literal notranslate"><spanclass="pre">target.key</span></code> here.</p></li>
<li><p><strong>Line 18</strong>: This line looks complex, but remember that <codeclass="docutils literal notranslate"><spanclass="pre">{**dict1,</span><spanclass="pre">**dict2}</span></code> is a one-line way to merge two dicts together. What this does is to do this in three steps:</p>
<ul>
<li><p>First we add <codeclass="docutils literal notranslate"><spanclass="pre">action_dict</span></code> together with a dict <codeclass="docutils literal notranslate"><spanclass="pre">{"target":</span><spanclass="pre">target}</span></code>. This has the same effect as doing <codeclass="docutils literal notranslate"><spanclass="pre">action_dict["target"]</span><spanclass="pre">=</span><spanclass="pre">target</span></code>, except we create a new dict out of the merger.</p></li>
<li><p>Next we take this new merger and creates a new dict <codeclass="docutils literal notranslate"><spanclass="pre">{"action_dict":</span><spanclass="pre">new_action_dict}</span></code>.</p></li>
<li><p>Finally we merge this with the existing <codeclass="docutils literal notranslate"><spanclass="pre">kwargs</span></code> dict. The result is a new dict that now has the updated <codeclass="docutils literal notranslate"><spanclass="pre">"action_dict"</span></code> key pointing to an action-dict where <codeclass="docutils literal notranslate"><spanclass="pre">target</span></code> is set.</p></li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Line 23</strong>: We extend the <codeclass="docutils literal notranslate"><spanclass="pre">options</span></code> list with the default wizard options (<codeclass="docutils literal notranslate"><spanclass="pre">back</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">abort</span></code>). Since we made a helper function for this, this is only one line.</p></li>
</ul>
<p>Creating the three other needed nodes <codeclass="docutils literal notranslate"><spanclass="pre">node_choose_enemy_recipient</span></code>, <codeclass="docutils literal notranslate"><spanclass="pre">node_choose_allied_target</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">node_choose_allied_recipient</span></code> are following the same pattern; they just use either the <codeclass="docutils literal notranslate"><spanclass="pre">allies</span></code> or <codeclass="docutils literal notranslate"><spanclass="pre">enemies</span></code> return from <codeclass="docutils literal notranslate"><spanclass="pre">combathandler.get_sides().</span><spanclass="pre">It</span><spanclass="pre">then</span><spanclass="pre">sets</span><spanclass="pre">either</span><spanclass="pre">the</span></code>target<codeclass="docutils literal notranslate"><spanclass="pre">or</span></code>recipient<codeclass="docutils literal notranslate"><spanclass="pre">field</span><spanclass="pre">in</span><spanclass="pre">the</span></code>action_dict`. We leave these up to the reader to implement.</p>
</section>
<sectionid="choose-an-ability">
<h3><spanclass="section-number">11.4.7. </span>Choose an Ability<aclass="headerlink"href="#choose-an-ability"title="Permalink to this headline">¶</a></h3>
<p>For Stunts, we need to be able to select which <em>Knave</em> Ability (STR, DEX etc) you want to boost/foil.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<p>The principle is the same as for the target/recipient-setter nodes, except that we just provide a list of the abilities to choose from. We update the <codeclass="docutils literal notranslate"><spanclass="pre">stunt_type</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">defense_type</span></code> keys in the <codeclass="docutils literal notranslate"><spanclass="pre">action_dict</span></code>, as needed by the Stunt action.</p>
</section>
<sectionid="choose-an-item-to-use-or-wield">
<h3><spanclass="section-number">11.4.8. </span>Choose an item to use or wield<aclass="headerlink"href="#choose-an-item-to-use-or-wield"title="Permalink to this headline">¶</a></h3>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<spanclass="c1"># same except using caller.equipment.get_wieldable_objects_from_backpack()</span>
</pre></div>
</div>
<p>Our <aclass="reference internal"href="Beginner-Tutorial-Equipment.html"><spanclass="doc std std-doc">equipment handler</span></a> has the very useful help method <codeclass="docutils literal notranslate"><spanclass="pre">.get_usable_objects_from_backpack</span></code>. We just call this to get a list of all the items we want to choose. Otherwise this node should look pretty familiar by now.</p>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">node_choose_wiqld_item</span></code> is very similar, except it uses <codeclass="docutils literal notranslate"><spanclass="pre">caller.equipment.get_wieldable_objects_from_backpack()</span></code> instead. We’ll leave the implementation of this up to the reader.</p>
</section>
<sectionid="the-main-menu-node">
<h3><spanclass="section-number">11.4.9. </span>The main menu node<aclass="headerlink"href="#the-main-menu-node"title="Permalink to this headline">¶</a></h3>
<p>This ties it all together.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<spanclass="s2">"desc"</span><spanclass="p">:</span><spanclass="s2">"Stunt - gain a later advantage against a target"</span><spanclass="p">,</span>
<spanclass="s2">"desc"</span><spanclass="p">:</span><spanclass="s2">"Stunt - give an enemy disadvantage against yourself or an ally"</span><spanclass="p">,</span>
<spanclass="s2">"desc"</span><spanclass="p">:</span><spanclass="s2">"Use an item on yourself or an ally"</span><spanclass="p">,</span>
<spanclass="s2">"desc"</span><spanclass="p">:</span><spanclass="s2">"Wield/swap with an item from inventory"</span><spanclass="p">,</span>
<p>This starts off the <codeclass="docutils literal notranslate"><spanclass="pre">_step_wizard</span></code> for each action choice. It also lays out the <codeclass="docutils literal notranslate"><spanclass="pre">action_dict</span></code> for every action, leaving <codeclass="docutils literal notranslate"><spanclass="pre">None</span></code> values for the fields that will be set by the following nodes.</p>
<p>Note how we add the <codeclass="docutils literal notranslate"><spanclass="pre">"repeat"</span></code> key to some actions. Having them automatically repeat means the player don’t have to insert the same action every time.</p>
</section>
</section>
<sectionid="attack-command">
<h2><spanclass="section-number">11.5. </span>Attack Command<aclass="headerlink"href="#attack-command"title="Permalink to this headline">¶</a></h2>
<p>We will only need one single Command to run the Turnbased combat system. This is the <codeclass="docutils literal notranslate"><spanclass="pre">attack</span></code> command. Once you use it once, you will be in the menu.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># in evadventure/combat_turnbased.py</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"What are you attacking?"</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="n">target</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"> is already down."</span><spanclass="p">)</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"PvP combat is not allowed here!"</span><spanclass="p">)</span>
<spanclass="n">target</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"|rYou are attacked by {self.caller.get_display_name(self.caller)}!|n"</span><spanclass="p">)</span>
<p>The <codeclass="docutils literal notranslate"><spanclass="pre">attack</span><spanclass="pre">target</span></code> Command will determine if the target has health (only things with health can be attacked) and that the room allows fighting. If the target is a pc, it will check so PvP is allowed.</p>
<p>It then proceeds to either start up a new command handler or reuse a new one, while adding the attacker and target to it. If the target was already in combat, this does nothing (same with the <codeclass="docutils literal notranslate"><spanclass="pre">.start_combat()</span></code> call).</p>
<p>As we create the <codeclass="docutils literal notranslate"><spanclass="pre">EvMenu</span></code>, we pass it the “menu index” we talked to about earlier, now with the actual node functions in every slot. We make the menu persistent so it survives a reload.</p>
<p>To make the command available, add the <codeclass="docutils literal notranslate"><spanclass="pre">TurnCombatCmdSet</span></code> to the Character’s default cmdset.</p>
</section>
<sectionid="making-sure-the-menu-stops">
<h2><spanclass="section-number">11.6. </span>Making sure the menu stops<aclass="headerlink"href="#making-sure-the-menu-stops"title="Permalink to this headline">¶</a></h2>
<p>The combat can end for a bunch of reasons. When that happens, we must make sure to clean up the menu so we go back normal operation. We will add this to the <codeclass="docutils literal notranslate"><spanclass="pre">remove_combatant</span></code> method on the combat handler (we left a TODO there before):</p>
<p>When the evmenu is active, it is avaiable on its user as <codeclass="docutils literal notranslate"><spanclass="pre">.ndb._evmenu</span></code> (see the EvMenu docs). When we are removed from combat, we use this to get the evmenu and call its <codeclass="docutils literal notranslate"><spanclass="pre">close_menu()</span></code> method to shut down the menu.</p>
<p>Our turnbased combat system is complete!</p>
</section>
<sectionid="testing">
<h2><spanclass="section-number">11.7. </span>Testing<aclass="headerlink"href="#testing"title="Permalink to this headline">¶</a></h2>
<p>See an example tests in <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></p>
<p>Unit testing of the Turnbased combat handler is straight forward, you follow the process of earlier lessons to test that each method on the handler returns what you expect with mocked inputs.</p>
<p>Unit-testing the menu is more complex. You will find examples of doing this in <aclass="reference external"href="https://github.com/evennia/evennia/blob/main/evennia/utils/testss/test_evmenu.py">evennia.utils.tests.test_evmenu</a>.</p>
</section>
<sectionid="a-small-combat-test">
<h2><spanclass="section-number">11.8. </span>A small combat test<aclass="headerlink"href="#a-small-combat-test"title="Permalink to this headline">¶</a></h2>
<p>Unit testing the code is not enough to see that combat works. We need to also make a little ‘functional’ test to see how it works in practice.</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>
<p>You can find an example combat batch-code script in <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/turnbased_combat_demo.py">batchscripts/turnbased_combat_demo.py</a></p>
<p>In <aclass="reference internal"href="Beginner-Tutorial-Combat-Twitch.html"><spanclass="doc std std-doc">The Twitch combat lesson</span></a> we used a <aclass="reference internal"href="../../../Components/Batch-Command-Processor.html"><spanclass="doc std std-doc">batch-command script</span></a> to create the testing environment in game. This runs in-game Evennia commands in sequence. For demonstration purposes we’ll instead use a <aclass="reference internal"href="../../../Components/Batch-Code-Processor.html"><spanclass="doc std std-doc">batch-code script</span></a>, which runs raw Python code in a repeatable way. A batch-code script is much more flexible than a batch-command script.</p>
<blockquote>
<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 Python module <codeclass="docutils literal notranslate"><spanclass="pre">evadventure/batchscripts/combat_demo.py</span></code></p>
</div></blockquote>
<p>A batchcode file is a valid Python module. The only difference is that it has a <codeclass="docutils literal notranslate"><spanclass="pre">#</span><spanclass="pre">HEADER</span></code> block and one or more <codeclass="docutils literal notranslate"><spanclass="pre">#</span><spanclass="pre">CODE</span></code> sections. When the processor runs, the <codeclass="docutils literal notranslate"><spanclass="pre">#</span><spanclass="pre">HEADER</span></code> part will be added on top of each <codeclass="docutils literal notranslate"><spanclass="pre">#</span><spanclass="pre">CODE</span></code> part before executing that code block in isolation. Since you can run the file from in-game (including refresh it without reloading the server), this gives the ability to run longer Python codes on-demand.</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="c1"># Evadventure (Turnbased) combat demo - using a batch-code file.</span>
<spanclass="c1">#</span>
<spanclass="c1"># Sets up a combat area for testing turnbased combat.</span>
<spanclass="c1">#</span>
<spanclass="c1"># First add mygame/server/conf/settings.py:</span>
<spanclass="c1"># Make the player an EvAdventureCharacter</span>
<spanclass="n">player</span><spanclass="o">=</span><spanclass="n">caller</span><spanclass="c1"># caller is injected by the batchcode runner, it's the one running this script # E: undefined name 'caller'</span>
<spanclass="n">arena</span><spanclass="o">=</span><spanclass="n">create_object</span><spanclass="p">(</span><spanclass="n">EvAdventureRoom</span><spanclass="p">,</span><spanclass="n">key</span><spanclass="o">=</span><spanclass="s2">"Arena"</span><spanclass="p">,</span><spanclass="n">attributes</span><spanclass="o">=</span><spanclass="p">[(</span><spanclass="s2">"desc"</span><spanclass="p">,</span><spanclass="s2">"A large arena."</span><spanclass="p">)])</span>
<spanclass="n">attributes</span><spanclass="o">=</span><spanclass="p">[(</span><spanclass="s2">"desc"</span><spanclass="p">,</span><spanclass="s2">"A training dummy."</span><spanclass="p">),</span><spanclass="p">(</span><spanclass="s2">"hp"</span><spanclass="p">,</span><spanclass="mi">1000</span><spanclass="p">),</span><spanclass="p">(</span><spanclass="s2">"hp_max"</span><spanclass="p">,</span><spanclass="mi">1000</span><spanclass="p">)],</span>
<spanclass="p">)</span>
</pre></div>
</div>
<p>If editing this in an IDE, you may get errors on the <codeclass="docutils literal notranslate"><spanclass="pre">player</span><spanclass="pre">=</span><spanclass="pre">caller</span></code> line. This is because <codeclass="docutils literal notranslate"><spanclass="pre">caller</span></code> is not defined anywhere in this file. Instead <codeclass="docutils literal notranslate"><spanclass="pre">caller</span></code> (the one running the script) is injected by the <codeclass="docutils literal notranslate"><spanclass="pre">batchcode</span></code> runner.</p>
<p>But apart from the <codeclass="docutils literal notranslate"><spanclass="pre">#</span><spanclass="pre">HEADER</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">#</span><spanclass="pre">CODE</span></code> specials, this just a series of normal Evennia api calls.</p>
<p>Log into the game with a developer/superuser account and run</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). If you need to fix something, use <codeclass="docutils literal notranslate"><spanclass="pre">q</span></code> to exit the menu and get access to the <codeclass="docutils literal notranslate"><spanclass="pre">reload</span></code> command (for the final combat, you can disable this ability by passing <codeclass="docutils literal notranslate"><spanclass="pre">auto_quit=False</span></code> when you create the <codeclass="docutils literal notranslate"><spanclass="pre">EvMenu</span></code>).</p>
</section>
<sectionid="conclusions">
<h2><spanclass="section-number">11.9. </span>Conclusions<aclass="headerlink"href="#conclusions"title="Permalink to this headline">¶</a></h2>
<p>At this point we have coverered some ideas on how to implement both twitch- and turnbased combat systems. Along the way you have been exposed to many concepts such as classes, scripts and handlers, Commands, EvMenus and more.</p>
<p>Before our combat system is actually usable, we need our enemies to actually fight back. We’ll get to that next.</p>