mirror of
https://github.com/evennia/evennia.git
synced 2026-03-18 13:56:30 +01:00
382 lines
No EOL
36 KiB
HTML
382 lines
No EOL
36 KiB
HTML
|
||
<!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 — Evennia 3.x documentation</title>
|
||
<link rel="stylesheet" href="../_static/nature.css" type="text/css" />
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
||
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
|
||
<script src="../_static/jquery.js"></script>
|
||
<script src="../_static/underscore.js"></script>
|
||
<script src="../_static/doctools.js"></script>
|
||
<script src="../_static/language_data.js"></script>
|
||
<link rel="shortcut icon" href="../_static/favicon.ico"/>
|
||
<link rel="index" title="Index" href="../genindex.html" />
|
||
<link rel="search" title="Search" href="../search.html" />
|
||
<link rel="next" title="Turn based Combat System" href="Turn-based-Combat-System.html" />
|
||
<link rel="prev" title="Using the Arxcode game dir" href="Tutorial-Using-Arxcode.html" />
|
||
</head><body>
|
||
|
||
|
||
<div class="admonition important">
|
||
<p class="first admonition-title">Note</p>
|
||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||
</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"
|
||
accesskey="I">index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="right" >
|
||
<a href="Turn-based-Combat-System.html" title="Turn based Combat System"
|
||
accesskey="N">next</a> |</li>
|
||
<li class="right" >
|
||
<a href="Tutorial-Using-Arxcode.html" title="Using the Arxcode game dir"
|
||
accesskey="P">previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia 3.x</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="Howtos-Overview.html" accesskey="U">Tutorials and How-To’s</a> »</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="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||
<div class="sphinxsidebarwrapper">
|
||
<p class="logo"><a href="../index.html">
|
||
<img class="logo" src="../_static/evennia_logo.png" alt="Logo"/>
|
||
</a></p>
|
||
<div id="searchbox" style="display: none" role="search">
|
||
<h3 id="searchlabel">Quick search</h3>
|
||
<div class="searchformwrapper">
|
||
<form class="search" action="../search.html" method="get">
|
||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||
<input type="submit" value="Go" />
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<script>$('#searchbox').show(0);</script>
|
||
<h3><a href="../index.html">Table of Contents</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">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>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<h4>Previous topic</h4>
|
||
<p class="topless"><a href="Tutorial-Using-Arxcode.html"
|
||
title="previous chapter">Using the Arxcode game dir</a></p>
|
||
<h4>Next topic</h4>
|
||
<p class="topless"><a href="Turn-based-Combat-System.html"
|
||
title="next chapter">Turn based Combat System</a></p>
|
||
<div role="note" aria-label="source link">
|
||
<!--h3>This Page</h3-->
|
||
<ul class="this-page-menu">
|
||
<li><a href="../_sources/Howtos/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/docs/latest/index.html">Documentation Top</a> </li>
|
||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||
<li>
|
||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<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 no one 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 doesn’t 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 won’t 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, don’t
|
||
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 - don’t 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="../Components/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">{"Hunting":34,</span> <span class="pre">"Fishing":20,</span> <span class="pre">...}</span></code>. A much more fancy solution is to look at the <a class="reference internal" href="../Contribs/Contrib-Traits.html"><span class="doc std std-doc">Trait handler contrib</span></a>. Finally you could even go with a <a class="reference internal" href="../Concepts/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="https://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">"hunting"</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">"swords"</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 internal" href="../Contribs/Contrib-Dice.html"><span class="doc std std-doc">dice roller contribution</span></a>. 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 it’s 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 10.</p></li>
|
||
<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>
|
||
<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 is below the <code class="docutils literal notranslate"><span class="pre">combat</span></code> value, it’s 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 Character’s <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>
|
||
</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="w"> </span><span class="sd">"""</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"> """</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">"Called only when first created"</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">@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="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">"Roll 1d100"</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">"Roll 1d6"</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">"Checks if a character is 'defeated'."</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"><=</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">"You fall down, defeated!"</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">"Add XP to character, tracking level increases."</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">>=</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="sa">f</span><span class="s2">"You are now level </span><span class="si">{</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="si">}</span><span class="s2">!"</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="w"> </span><span class="sd">"""</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's roll hits.</span>
|
||
<span class="sd"> """</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_template</span> <span class="o">=</span> <span class="s2">"You are hit by </span><span class="si">{attacker}</span><span class="s2"> for </span><span class="si">{dmg}</span><span class="s2"> damage!"</span>
|
||
<span class="n">wintext_template</span> <span class="o">=</span> <span class="s2">"You hit </span><span class="si">{target}</span><span class="s2"> for </span><span class="si">{dmg}</span><span class="s2"> damage!"</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="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">></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_template</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">char2</span><span class="p">,</span> <span class="n">dmg</span><span class="o">=</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_template</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">attacker</span><span class="o">=</span><span class="n">char1</span><span class="p">,</span> <span class="n">dmg</span><span class="o">=</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">></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_template</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">attacker</span><span class="o">=</span><span class="n">char2</span><span class="p">,</span> <span class="n">dmg</span><span class="o">=</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_template</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">char1</span><span class="p">,</span> <span class="n">dmg</span><span class="o">=</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">"Neither of you can find an opening."</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">"combat"</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="w"> </span><span class="sd">"""</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"> """</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="sa">f</span><span class="s2">"Skillname </span><span class="si">{</span><span class="n">skillname</span><span class="si">}</span><span class="s2"> not found."</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>
|
||
<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="w"> </span><span class="sd">"""</span>
|
||
<span class="sd"> attack an opponent</span>
|
||
|
||
<span class="sd"> Usage:</span>
|
||
<span class="sd"> attack <target></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"> """</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">"Implementing combat"</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">"You need to pick a target to attack."</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">"combat"</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 don’t 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>
|
||
</section>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="General Index"
|
||
>index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="right" >
|
||
<a href="Turn-based-Combat-System.html" title="Turn based Combat System"
|
||
>next</a> |</li>
|
||
<li class="right" >
|
||
<a href="Tutorial-Using-Arxcode.html" title="Using the Arxcode game dir"
|
||
>previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia 3.x</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">Implementing a game rule system</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
|
||
<div class="admonition important">
|
||
<p class="first admonition-title">Note</p>
|
||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||
</div>
|
||
|
||
|
||
<div class="footer" role="contentinfo">
|
||
© Copyright 2023, The Evennia developer community.
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||
</div>
|
||
</body>
|
||
</html> |