evennia/docs/0.x/Implementing-a-game-rule-system.html
2023-12-20 19:10:09 +01:00

426 lines
No EOL
39 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>Implementing a game rule system &#8212; Evennia 0.9.5 documentation</title>
<link rel="stylesheet" href="_static/nature.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/language_data.js"></script>
<script async="async" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"processClass": "tex2jax_process|mathjax_process|math|output_area"}})</script>
<link rel="shortcut icon" href="_static/favicon.ico"/>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Evennia 0.9.5</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Implementing a game rule system</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="implementing-a-game-rule-system">
<h1>Implementing a game rule system<a class="headerlink" href="#implementing-a-game-rule-system" title="Permalink to this headline"></a></h1>
<p>The simplest way to create an online roleplaying game (at least from a code perspective) is to
simply grab a paperback RPG rule book, get a staff of game masters together and start to run scenes
with whomever logs in. Game masters can roll their dice in front of their computers and tell the
players the results. This is only one step away from a traditional tabletop game and puts heavy
demands on the staff - it is unlikely staff will be able to keep up around the clock even if they
are very dedicated.</p>
<p>Many games, even the most roleplay-dedicated, thus tend to allow for players to mediate themselves
to some extent. A common way to do this is to introduce <em>coded systems</em> - that is, to let the
computer do some of the heavy lifting. A basic thing is to add an online dice-roller so everyone can
make rolls and make sure noone is cheating. Somewhere at this level you find the most bare-bones
roleplaying MUSHes.</p>
<p>The advantage of a coded system is that as long as the rules are fair the computer is too - it makes
no judgement calls and holds no personal grudges (and cannot be accused of holding any). Also, the
computer doesnt need to sleep and can always be online regardless of when a player logs on. The
drawback is that a coded system is not flexible and wont adapt to the unprogrammed actions human
players may come up with in role play. For this reason many roleplay-heavy MUDs do a hybrid
variation - they use coded systems for things like combat and skill progression but leave role play
to be mostly freeform, overseen by staff game masters.</p>
<p>Finally, on the other end of the scale are less- or no-roleplay games, where game mechanics (and
thus player fairness) is the most important aspect. In such games the only events with in-game value
are those resulting from code. Such games are very common and include everything from hack-and-slash
MUDs to various tactical simulations.</p>
<p>So your first decision needs to be just what type of system you are aiming for. This page will try
to give some ideas for how to organize the “coded” part of your system, however big that may be.</p>
<section id="overall-system-infrastructure">
<h2>Overall system infrastructure<a class="headerlink" href="#overall-system-infrastructure" title="Permalink to this headline"></a></h2>
<p>We strongly recommend that you code your rule system as stand-alone as possible. That is, dont
spread your skill check code, race bonus calculation, die modifiers or what have you all over your
game.</p>
<ul>
<li><p>Put everything you would need to look up in a rule book into a module in <code class="docutils literal notranslate"><span class="pre">mygame/world</span></code>. Hide away
as much as you can. Think of it as a black box (or maybe the code representation of an all-knowing
game master). The rest of your game will ask this black box questions and get answers back. Exactly
how it arrives at those results should not need to be known outside the box. Doing it this way
makes it easier to change and update things in one place later.</p></li>
<li><p>Store only the minimum stuff you need with each game object. That is, if your Characters need
values for Health, a list of skills etc, store those things on the Character - dont store how to
roll or change them.</p></li>
<li><p>Next is to determine just how you want to store things on your Objects and Characters. You can
choose to either store things as individual <a class="reference internal" href="Attributes.html"><span class="doc std std-doc">Attributes</span></a>, like <code class="docutils literal notranslate"><span class="pre">character.db.STR=34</span></code> and
<code class="docutils literal notranslate"><span class="pre">character.db.Hunting_skill=20</span></code>. But you could also use some custom storage method, like a
dictionary <code class="docutils literal notranslate"><span class="pre">character.db.skills</span> <span class="pre">=</span> <span class="pre">{&quot;Hunting&quot;:34,</span> <span class="pre">&quot;Fishing&quot;:20,</span> <span class="pre">...}</span></code>. A much more fancy solution is
to look at the Ainneve <a class="reference external" href="https://github.com/evennia/ainneve/blob/master/world/traits.py">Trait
handler</a>. Finally you could even go
with a <a class="reference internal" href="New-Models.html"><span class="doc std std-doc">custom django model</span></a>. Which is the better depends on your game and the
complexity of your system.</p></li>
<li><p>Make a clear <a class="reference external" href="http://en.wikipedia.org/wiki/Application_programming_interface">API</a> into your
rules. That is, make methods/functions that you feed with, say, your Character and which skill you
want to check. That is, you want something similar to this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="kn">from</span> <span class="nn">world</span> <span class="kn">import</span> <span class="n">rules</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">roll_skill</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="s2">&quot;hunting&quot;</span><span class="p">)</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">roll_challenge</span><span class="p">(</span><span class="n">character1</span><span class="p">,</span> <span class="n">character2</span><span class="p">,</span> <span class="s2">&quot;swords&quot;</span><span class="p">)</span>
</pre></div>
</div>
</li>
</ul>
<p>You might need to make these functions more or less complex depending on your game. For example the
properties of the room might matter to the outcome of a roll (if the room is dark, burning etc).
Establishing just what you need to send into your game mechanic module is a great way to also get a
feel for what you need to add to your engine.</p>
</section>
<section id="coded-systems">
<h2>Coded systems<a class="headerlink" href="#coded-systems" title="Permalink to this headline"></a></h2>
<p>Inspired by tabletop role playing games, most game systems mimic some sort of die mechanic. To this
end Evennia offers a full <a class="reference external" href="https://github.com/evennia/evennia/blob/master/evennia/contrib/dice.py">dice
roller</a> in its <code class="docutils literal notranslate"><span class="pre">contrib</span></code>
folder. For custom implementations, Python offers many ways to randomize a result using its in-built
<code class="docutils literal notranslate"><span class="pre">random</span></code> module. No matter how its implemented, we will in this text refer to the action of
determining an outcome as a “roll”.</p>
<p>In a freeform system, the result of the roll is just compared with values and people (or the game
master) just agree on what it means. In a coded system the result now needs to be processed somehow.
There are many things that may happen as a result of rule enforcement:</p>
<ul class="simple">
<li><p>Health may be added or deducted. This can effect the character in various ways.</p></li>
<li><p>Experience may need to be added, and if a level-based system is used, the player might need to be
informed they have increased a level.</p></li>
<li><p>Room-wide effects need to be reported to the room, possibly affecting everyone in the room.</p></li>
</ul>
<p>There are also a slew of other things that fall under “Coded systems”, including things like
weather, NPC artificial intelligence and game economy. Basically everything about the world that a
Game master would control in a tabletop role playing game can be mimicked to some level by coded
systems.</p>
</section>
<section id="example-of-rule-module">
<h2>Example of Rule module<a class="headerlink" href="#example-of-rule-module" title="Permalink to this headline"></a></h2>
<p>Here is a simple example of a rule module. This is what we assume about our simple example game:</p>
<ul class="simple">
<li><p>Characters have only four numerical values:</p>
<ul>
<li><p>Their <code class="docutils literal notranslate"><span class="pre">level</span></code>, which starts at 1.</p></li>
<li><p>A skill <code class="docutils literal notranslate"><span class="pre">combat</span></code>, which determines how good they are at hitting things. Starts between 5 and</p></li>
</ul>
</li>
</ul>
<ol class="simple">
<li><ul class="simple">
<li><p>Their Strength, <code class="docutils literal notranslate"><span class="pre">STR</span></code>, which determine how much damage they do. Starts between 1 and 10.</p></li>
<li><p>Their Health points, <code class="docutils literal notranslate"><span class="pre">HP</span></code>, which starts at 100.</p></li>
</ul>
</li>
</ol>
<ul class="simple">
<li><p>When a Character reaches <code class="docutils literal notranslate"><span class="pre">HP</span> <span class="pre">=</span> <span class="pre">0</span></code>, they are presumed “defeated”. Their HP is reset and they get a
failure message (as a stand-in for death code).</p></li>
<li><p>Abilities are stored as simple Attributes on the Character.</p></li>
<li><p>“Rolls” are done by rolling a 100-sided die. If the result plus the <code class="docutils literal notranslate"><span class="pre">combat</span></code>value is greater than
the other character, its a success and damage is rolled. Damage is rolled as a six-sided die + the
value of <code class="docutils literal notranslate"><span class="pre">STR</span></code> (for this example we ignore weapons and assume <code class="docutils literal notranslate"><span class="pre">STR</span></code> is all that matters).</p></li>
<li><p>Every successful <code class="docutils literal notranslate"><span class="pre">attack</span></code> roll gives 1-3 experience points (<code class="docutils literal notranslate"><span class="pre">XP</span></code>). Every time the number of <code class="docutils literal notranslate"><span class="pre">XP</span></code>
reaches <code class="docutils literal notranslate"><span class="pre">(level</span> <span class="pre">+</span> <span class="pre">1)</span> <span class="pre">**</span> <span class="pre">2</span></code>, the Character levels up. When leveling up, the Characters <code class="docutils literal notranslate"><span class="pre">combat</span></code>
value goes up by 2 points and <code class="docutils literal notranslate"><span class="pre">STR</span></code> by one (this is a stand-in for a real progression system).</p></li>
<li><p>Characters with the name <code class="docutils literal notranslate"><span class="pre">dummy</span></code> will gain no XP. Allowing us to make a dummy to train with.</p></li>
</ul>
<section id="character">
<h3>Character<a class="headerlink" href="#character" title="Permalink to this headline"></a></h3>
<p>The Character typeclass is simple. It goes in <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/characters.py</span></code>. There is already
an empty <code class="docutils literal notranslate"><span class="pre">Character</span></code> class there that Evennia will look to and use.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span>
<span class="k">class</span> <span class="nc">Character</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Custom rule-restricted character. We randomize</span>
<span class="sd"> the initial skill and ability values bettween 1-10.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">at_object_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;Called only when first created&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">level</span> <span class="o">=</span> <span class="mi">1</span>
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">HP</span> <span class="o">=</span> <span class="mi">100</span>
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">XP</span> <span class="o">=</span> <span class="mi">0</span>
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">STR</span> <span class="o">=</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span> <span class="o">=</span> <span class="n">randint</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">&#64;reload</span></code> the server to load up the new code. Doing <code class="docutils literal notranslate"><span class="pre">examine</span> <span class="pre">self</span></code> will however <em>not</em> show the new
Attributes on yourself. This is because the <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> hook is only called on <em>new</em>
Characters. Your Character was already created and will thus not have them. To force a reload, use
the following command:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@typeclass</span><span class="o">/</span><span class="n">force</span><span class="o">/</span><span class="n">reset</span> <span class="bp">self</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">examine</span> <span class="pre">self</span></code> command will now show the new Attributes.</p>
</section>
<section id="rule-module">
<h3>Rule module<a class="headerlink" href="#rule-module" title="Permalink to this headline"></a></h3>
<p>This is a module <code class="docutils literal notranslate"><span class="pre">mygame/world/rules.py</span></code>.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd">mygame/world/rules.py</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span>
<span class="k">def</span> <span class="nf">roll_hit</span><span class="p">():</span>
<span class="s2">&quot;Roll 1d100&quot;</span>
<span class="k">return</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">roll_dmg</span><span class="p">():</span>
<span class="s2">&quot;Roll 1d6&quot;</span>
<span class="k">return</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">check_defeat</span><span class="p">(</span><span class="n">character</span><span class="p">):</span>
<span class="s2">&quot;Checks if a character is &#39;defeated&#39;.&quot;</span>
<span class="k">if</span> <span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">HP</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">character</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You fall down, defeated!&quot;</span><span class="p">)</span>
<span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">HP</span> <span class="o">=</span> <span class="mi">100</span> <span class="c1"># reset</span>
<span class="k">def</span> <span class="nf">add_XP</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="n">amount</span><span class="p">):</span>
<span class="s2">&quot;Add XP to character, tracking level increases.&quot;</span>
<span class="k">if</span> <span class="s2">&quot;training_dummy&quot;</span> <span class="ow">in</span> <span class="n">character</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">all</span><span class="p">():</span> <span class="c1"># don&#39;t allow the training dummy to level</span>
<span class="n">character</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="s2">&quot;Training Dummies can not gain XP.&quot;</span><span class="p">)</span>
<span class="k">return</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">XP</span> <span class="o">+=</span> <span class="n">amount</span>
<span class="k">if</span> <span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">XP</span> <span class="o">&gt;=</span> <span class="p">(</span><span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">level</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span><span class="p">:</span>
<span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">level</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">STR</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span> <span class="o">+=</span> <span class="mi">2</span>
<span class="n">character</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You are now level </span><span class="si">%i</span><span class="s2">!&quot;</span> <span class="o">%</span> <span class="n">character</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">level</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">skill_combat</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> This determines outcome of combat. The one who</span>
<span class="sd"> rolls under their combat skill AND higher than</span>
<span class="sd"> their opponent&#39;s roll hits.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">char1</span><span class="p">,</span> <span class="n">char2</span> <span class="o">=</span> <span class="n">args</span>
<span class="n">roll1</span><span class="p">,</span> <span class="n">roll2</span> <span class="o">=</span> <span class="n">roll_hit</span><span class="p">(),</span> <span class="n">roll_hit</span><span class="p">()</span>
<span class="n">failtext</span> <span class="o">=</span> <span class="s2">&quot;You are hit by </span><span class="si">%s</span><span class="s2"> for </span><span class="si">%i</span><span class="s2"> damage!&quot;</span>
<span class="n">wintext</span> <span class="o">=</span> <span class="s2">&quot;You hit </span><span class="si">%s</span><span class="s2"> for </span><span class="si">%i</span><span class="s2"> damage!&quot;</span>
<span class="n">xp_gain</span> <span class="o">=</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
<span class="c1"># display messages showing attack numbers</span>
<span class="n">attack_message</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">char1</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2"> rolls </span><span class="si">{</span><span class="n">roll1</span><span class="si">}</span><span class="s2"> + combat </span><span class="si">{</span><span class="n">char1</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span><span class="si">}</span><span class="s2"> &quot;</span> \
<span class="sa">f</span><span class="s2">&quot;= </span><span class="si">{</span><span class="n">char1</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span><span class="o">+</span><span class="n">roll1</span><span class="si">}</span><span class="s2"> | </span><span class="si">{</span><span class="n">char2</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2"> rolls </span><span class="si">{</span><span class="n">roll2</span><span class="si">}</span><span class="s2"> + combat &quot;</span> \
<span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">char2</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span><span class="si">}</span><span class="s2"> = </span><span class="si">{</span><span class="n">char2</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span><span class="o">+</span><span class="n">roll2</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="n">char1</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="n">attack_message</span><span class="p">)</span>
<span class="n">attack_summary</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">char1</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">char1</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span><span class="o">+</span><span class="n">roll1</span><span class="si">}</span><span class="s2"> &quot;</span> \
<span class="sa">f</span><span class="s2">&quot;vs </span><span class="si">{</span><span class="n">char2</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">char2</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span><span class="o">+</span><span class="n">roll2</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="n">char1</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="n">attack_summary</span><span class="p">)</span>
<span class="k">if</span> <span class="n">char1</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span><span class="o">+</span><span class="n">roll1</span> <span class="o">&gt;</span> <span class="n">char2</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span><span class="o">+</span><span class="n">roll2</span><span class="p">:</span>
<span class="c1"># char 1 hits</span>
<span class="n">dmg</span> <span class="o">=</span> <span class="n">roll_dmg</span><span class="p">()</span> <span class="o">+</span> <span class="n">char1</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">STR</span>
<span class="n">char1</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">wintext</span> <span class="o">%</span> <span class="p">(</span><span class="n">char2</span><span class="p">,</span> <span class="n">dmg</span><span class="p">))</span>
<span class="n">add_XP</span><span class="p">(</span><span class="n">char1</span><span class="p">,</span> <span class="n">xp_gain</span><span class="p">)</span>
<span class="n">char2</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">failtext</span> <span class="o">%</span> <span class="p">(</span><span class="n">char1</span><span class="p">,</span> <span class="n">dmg</span><span class="p">))</span>
<span class="n">char2</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">HP</span> <span class="o">-=</span> <span class="n">dmg</span>
<span class="n">check_defeat</span><span class="p">(</span><span class="n">char2</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">char2</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span><span class="o">+</span><span class="n">roll2</span> <span class="o">&gt;</span> <span class="n">char1</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">combat</span><span class="o">+</span><span class="n">roll1</span><span class="p">:</span>
<span class="c1"># char 2 hits</span>
<span class="n">dmg</span> <span class="o">=</span> <span class="n">roll_dmg</span><span class="p">()</span> <span class="o">+</span> <span class="n">char2</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">STR</span>
<span class="n">char1</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">failtext</span> <span class="o">%</span> <span class="p">(</span><span class="n">char2</span><span class="p">,</span> <span class="n">dmg</span><span class="p">))</span>
<span class="n">char1</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">HP</span> <span class="o">-=</span> <span class="n">dmg</span>
<span class="n">check_defeat</span><span class="p">(</span><span class="n">char1</span><span class="p">)</span>
<span class="n">char2</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">wintext</span> <span class="o">%</span> <span class="p">(</span><span class="n">char1</span><span class="p">,</span> <span class="n">dmg</span><span class="p">))</span>
<span class="n">add_XP</span><span class="p">(</span><span class="n">char2</span><span class="p">,</span> <span class="n">xp_gain</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># a draw</span>
<span class="n">drawtext</span> <span class="o">=</span> <span class="s2">&quot;Neither of you can find an opening.&quot;</span>
<span class="n">char1</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">drawtext</span><span class="p">)</span>
<span class="n">char2</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">drawtext</span><span class="p">)</span>
<span class="n">SKILLS</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;combat&quot;</span><span class="p">:</span> <span class="n">skill_combat</span><span class="p">}</span>
<span class="k">def</span> <span class="nf">roll_challenge</span><span class="p">(</span><span class="n">character1</span><span class="p">,</span> <span class="n">character2</span><span class="p">,</span> <span class="n">skillname</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Determine the outcome of a skill challenge between</span>
<span class="sd"> two characters based on the skillname given.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="n">skillname</span> <span class="ow">in</span> <span class="n">SKILLS</span><span class="p">:</span>
<span class="n">SKILLS</span><span class="p">[</span><span class="n">skillname</span><span class="p">](</span><span class="n">character1</span><span class="p">,</span> <span class="n">character2</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">RunTimeError</span><span class="p">(</span><span class="s2">&quot;Skillname </span><span class="si">%s</span><span class="s2"> not found.&quot;</span> <span class="o">%</span> <span class="n">skillname</span><span class="p">)</span>
</pre></div>
</div>
<p>These few functions implement the entirety of our simple rule system. We have a function to check
the “defeat” condition and reset the <code class="docutils literal notranslate"><span class="pre">HP</span></code> back to 100 again. We define a generic “skill” function.
Multiple skills could all be added with the same signature; our <code class="docutils literal notranslate"><span class="pre">SKILLS</span></code> dictionary makes it easy to
look up the skills regardless of what their actual functions are called. Finally, the access
function <code class="docutils literal notranslate"><span class="pre">roll_challenge</span></code> just picks the skill and gets the result.</p>
<p>In this example, the skill function actually does a lot - it not only rolls results, it also informs
everyone of their results via <code class="docutils literal notranslate"><span class="pre">character.msg()</span></code> calls.</p>
</section>
<section id="attack-command">
<h3>Attack Command<a class="headerlink" href="#attack-command" title="Permalink to this headline"></a></h3>
<p>Here is an example of usage in a game command:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span>
<span class="kn">from</span> <span class="nn">world</span> <span class="kn">import</span> <span class="n">rules</span>
<span class="k">class</span> <span class="nc">CmdAttack</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> attack an opponent</span>
<span class="sd"> Usage:</span>
<span class="sd"> attack &lt;target&gt;</span>
<span class="sd"> This will attack a target in the same room, dealing</span>
<span class="sd"> damage with your bare hands.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;Implementing combat&quot;</span>
<span class="n">caller</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">:</span>
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;You need to pick a target to attack.&quot;</span><span class="p">)</span>
<span class="k">return</span>
<span class="n">target</span> <span class="o">=</span> <span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">)</span>
<span class="k">if</span> <span class="n">target</span><span class="p">:</span>
<span class="n">rules</span><span class="o">.</span><span class="n">roll_challenge</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="s2">&quot;combat&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Note how simple the command becomes and how generic you can make it. It becomes simple to offer any
number of Combat commands by just extending this functionality - you can easily roll challenges and
pick different skills to check. And if you ever decided to, say, change how to determine hit chance,
you dont have to change every command, but need only change the single <code class="docutils literal notranslate"><span class="pre">roll_hit</span></code> function inside
your <code class="docutils literal notranslate"><span class="pre">rules</span></code> module.</p>
</section>
<section id="training-dummy">
<h3>Training dummy<a class="headerlink" href="#training-dummy" title="Permalink to this headline"></a></h3>
<p>Create a dummy to test the attack command. Give it a <code class="docutils literal notranslate"><span class="pre">training_dummy</span></code> tag anything with the tag
<code class="docutils literal notranslate"><span class="pre">training_dummy</span></code> will not gain xp.<br></p>
<blockquote>
<div><p>create/drop dummy:characters.Character<br>
tag dummy=training_dummy</p>
</div></blockquote>
<p>Attack it with your new command</p>
<blockquote>
<div><p>attack dummy</p>
</div></blockquote>
</section>
</section>
</section>
<div class="clearer"></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="index.html">
<img class="logo" src="_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<p><h3><a href="index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Implementing a game rule system</a><ul>
<li><a class="reference internal" href="#overall-system-infrastructure">Overall system infrastructure</a></li>
<li><a class="reference internal" href="#coded-systems">Coded systems</a></li>
<li><a class="reference internal" href="#example-of-rule-module">Example of Rule module</a><ul>
<li><a class="reference internal" href="#character">Character</a></li>
<li><a class="reference internal" href="#rule-module">Rule module</a></li>
<li><a class="reference internal" href="#attack-command">Attack Command</a></li>
<li><a class="reference internal" href="#training-dummy">Training dummy</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="_sources/Implementing-a-game-rule-system.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li><a href="http://webchat.freenode.net/?channels=evennia&uio=MT1mYWxzZSY5PXRydWUmMTE9MTk1JjEyPXRydWUbb">IRC</a> -
<a href="https://discord.gg/NecFePw">Discord</a> -
<a href="https://groups.google.com/forum/#%21forum/evennia">Forums</a>
</li>
<li><a href="http://evennia.blogspot.com/">Evennia Dev blog</a> </li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="../1.0-dev/index.html">1.0-dev (develop branch)</a></li>
<li><a href="Implementing-a-game-rule-system.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Evennia 0.9.5</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Implementing a game rule system</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>