mirror of
https://github.com/evennia/evennia.git
synced 2026-03-18 13:56:30 +01:00
606 lines
No EOL
63 KiB
HTML
606 lines
No EOL
63 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>Buffs — Evennia 2.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="Character Creator" href="Contrib-Character-Creator.html" />
|
||
<link rel="prev" title="XYZgrid" href="Contrib-XYZGrid.html" />
|
||
</head><body>
|
||
|
||
|
||
|
||
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="General Index"
|
||
accesskey="I">index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="right" >
|
||
<a href="Contrib-Character-Creator.html" title="Character Creator"
|
||
accesskey="N">next</a> |</li>
|
||
<li class="right" >
|
||
<a href="Contrib-XYZGrid.html" title="XYZgrid"
|
||
accesskey="P">previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia 2.x</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="Contribs-Overview.html" accesskey="U">Contribs</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">Buffs</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="#">Buffs</a><ul>
|
||
<li><a class="reference internal" href="#quick-start">Quick Start</a><ul>
|
||
<li><a class="reference internal" href="#customization">Customization</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#using-the-handler">Using the Handler</a><ul>
|
||
<li><a class="reference internal" href="#apply-a-buff">Apply a Buff</a></li>
|
||
<li><a class="reference internal" href="#get-buffs">Get Buffs</a></li>
|
||
<li><a class="reference internal" href="#remove-buffs">Remove Buffs</a></li>
|
||
<li><a class="reference internal" href="#check-modifiers">Check Modifiers</a><ul>
|
||
<li><a class="reference internal" href="#multiplicative-buffs-advanced">Multiplicative Buffs (Advanced)</a></li>
|
||
<li><a class="reference internal" href="#buff-strength-priority-advanced">Buff Strength Priority (Advanced)</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#trigger-buffs">Trigger Buffs</a></li>
|
||
<li><a class="reference internal" href="#ticking">Ticking</a></li>
|
||
<li><a class="reference internal" href="#context">Context</a></li>
|
||
<li><a class="reference internal" href="#viewing">Viewing</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#creating-new-buffs">Creating New Buffs</a><ul>
|
||
<li><a class="reference internal" href="#basics">Basics</a><ul>
|
||
<li><a class="reference internal" href="#buff-cache-advanced">Buff Cache (Advanced)</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#modifiers">Modifiers</a><ul>
|
||
<li><a class="reference internal" href="#generating-mods-advanced">Generating Mods (Advanced)</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#triggers">Triggers</a></li>
|
||
<li><a class="reference internal" href="#id1">Ticking</a></li>
|
||
<li><a class="reference internal" href="#extras">Extras</a><ul>
|
||
<li><a class="reference internal" href="#conditionals">Conditionals</a></li>
|
||
<li><a class="reference internal" href="#helper-methods">Helper Methods</a></li>
|
||
<li><a class="reference internal" href="#playtime-duration">Playtime Duration</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<h4>Previous topic</h4>
|
||
<p class="topless"><a href="Contrib-XYZGrid.html"
|
||
title="previous chapter">XYZgrid</a></p>
|
||
<h4>Next topic</h4>
|
||
<p class="topless"><a href="Contrib-Character-Creator.html"
|
||
title="next chapter">Character Creator</a></p>
|
||
<div role="note" aria-label="source link">
|
||
<!--h3>This Page</h3-->
|
||
<ul class="this-page-menu">
|
||
<li><a href="../_sources/Contribs/Contrib-Buffs.md.txt"
|
||
rel="nofollow">Show Page Source</a></li>
|
||
</ul>
|
||
</div><h3>Links</h3>
|
||
<ul>
|
||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||
<li>
|
||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||
</li>
|
||
</ul>
|
||
<h3>Doc Versions</h3>
|
||
<ul>
|
||
|
||
<li><a href="Contrib-Buffs.html">2.x (main branch)</a></li>
|
||
<ul>
|
||
<li><a href="../1.3.0/index.html">1.3.0 (v1.3.0 branch)</a></li>
|
||
|
||
<li><a href="../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
|
||
|
||
|
||
</ul>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<section class="tex2jax_ignore mathjax_ignore" id="buffs">
|
||
<h1>Buffs<a class="headerlink" href="#buffs" title="Permalink to this headline">¶</a></h1>
|
||
<p>Contribution by Tegiminis 2022</p>
|
||
<p>A buff is a timed object, attached to a game entity. It is capable of modifying values, triggering code, or both.
|
||
It is a common design pattern in RPGs, particularly action games.</p>
|
||
<p>Features:</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">BuffHandler</span></code>: A buff handler to apply to your objects.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">BaseBuff</span></code>: A buff class to extend from to create your own buffs.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">BuffableProperty</span></code>: A sample property class to show how to automatically check modifiers.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">CmdBuff</span></code>: A command which applies buffs.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">samplebuffs.py</span></code>: Some sample buffs to learn from.</p></li>
|
||
</ul>
|
||
<section id="quick-start">
|
||
<h2>Quick Start<a class="headerlink" href="#quick-start" title="Permalink to this headline">¶</a></h2>
|
||
<p>Assign the handler to a property on the object, like so.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@lazy_property</span>
|
||
<span class="k">def</span> <span class="nf">buffs</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">BuffHandler</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">BuffHandler</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>You may then call the handler to add or manipulate buffs like so: <code class="docutils literal notranslate"><span class="pre">object.buffs</span></code>. See <strong>Using the Handler</strong>.</p>
|
||
<section id="customization">
|
||
<h3>Customization<a class="headerlink" href="#customization" title="Permalink to this headline">¶</a></h3>
|
||
<p>If you want to customize the handler, you can feed the constructor two arguments:</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">dbkey</span></code>: The string you wish to use as the attribute key for the buff database. Defaults to “buffs”. This allows you to keep separate buff pools - for example, “buffs” and “perks”.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">autopause</span></code>: If you want this handler to automatically pause playtime buffs when its owning object is unpuppeted.</p></li>
|
||
</ul>
|
||
<blockquote>
|
||
<div><p><strong>Note</strong>: If you enable autopausing, you MUST initialize the property in your owning object’s
|
||
<code class="docutils literal notranslate"><span class="pre">at_init</span></code> hook. Otherwise, a hot reload can cause playtime buffs to not update properly
|
||
on puppet/unpuppet. You have been warned!</p>
|
||
</div></blockquote>
|
||
<p>Let’s say you want another handler for an object, <code class="docutils literal notranslate"><span class="pre">perks</span></code>, which has a separate database and
|
||
respects playtime buffs. You’d assign this new property as so:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">BuffableObject</span><span class="p">(</span><span class="n">Object</span><span class="p">):</span>
|
||
<span class="nd">@lazy_property</span>
|
||
<span class="k">def</span> <span class="nf">perks</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">BuffHandler</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">BuffHandler</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dbkey</span><span class="o">=</span><span class="s1">'perks'</span><span class="p">,</span> <span class="n">autopause</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">at_init</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">perks</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="using-the-handler">
|
||
<h2>Using the Handler<a class="headerlink" href="#using-the-handler" title="Permalink to this headline">¶</a></h2>
|
||
<p>Here’s how to make use of your new handler.</p>
|
||
<section id="apply-a-buff">
|
||
<h3>Apply a Buff<a class="headerlink" href="#apply-a-buff" title="Permalink to this headline">¶</a></h3>
|
||
<p>Call the handler’s <code class="docutils literal notranslate"><span class="pre">add</span></code> method. This requires a class reference, and also contains a number of
|
||
optional arguments to customize the buff’s duration, stacks, and so on. You can also store any arbitrary value
|
||
in the buff’s cache by passing a dictionary through the <code class="docutils literal notranslate"><span class="pre">to_cache</span></code> optional argument. This will not overwrite the normal
|
||
values on the cache.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">buffs</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">StrengthBuff</span><span class="p">)</span> <span class="c1"># A single stack of StrengthBuff with normal duration</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">buffs</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">DexBuff</span><span class="p">,</span> <span class="n">stacks</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">duration</span><span class="o">=</span><span class="mi">60</span><span class="p">)</span> <span class="c1"># Three stacks of DexBuff, with a duration of 60 seconds</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">buffs</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">ReflectBuff</span><span class="p">,</span> <span class="n">to_cache</span><span class="o">=</span><span class="p">{</span><span class="s1">'reflect'</span><span class="p">:</span> <span class="mf">0.5</span><span class="p">})</span> <span class="c1"># A single stack of ReflectBuff, with an extra cache value</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Two important attributes on the buff are checked when the buff is applied: <code class="docutils literal notranslate"><span class="pre">refresh</span></code> and <code class="docutils literal notranslate"><span class="pre">unique</span></code>.</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">refresh</span></code> (default: True) determines if a buff’s timer is refreshed when it is reapplied.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">unique</span></code> (default: True) determines if this buff is unique; that is, only one of it exists on the object.</p></li>
|
||
</ul>
|
||
<p>The combination of these two booleans creates one of three kinds of keys:</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">Unique</span> <span class="pre">is</span> <span class="pre">True,</span> <span class="pre">Refresh</span> <span class="pre">is</span> <span class="pre">True/False</span></code>: The buff’s default key.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">Unique</span> <span class="pre">is</span> <span class="pre">False,</span> <span class="pre">Refresh</span> <span class="pre">is</span> <span class="pre">True</span></code>: The default key mixed with the applier’s dbref. This makes the buff “unique-per-player”, so you can refresh through reapplication.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">Unique</span> <span class="pre">is</span> <span class="pre">False,</span> <span class="pre">Refresh</span> <span class="pre">is</span> <span class="pre">False</span></code>: The default key mixed with a randomized number.</p></li>
|
||
</ul>
|
||
</section>
|
||
<section id="get-buffs">
|
||
<h3>Get Buffs<a class="headerlink" href="#get-buffs" title="Permalink to this headline">¶</a></h3>
|
||
<p>The handler has several getter methods which return instanced buffs. You won’t need to use these for basic functionality, but if you want to manipulate
|
||
buffs after application, they are very useful. The handler’s <code class="docutils literal notranslate"><span class="pre">check</span></code>/<code class="docutils literal notranslate"><span class="pre">trigger</span></code> methods utilize some of these getters, while others are just for developer convenience.</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">get(key)</span></code> is the most basic getter. It returns a single buff instance, or <code class="docutils literal notranslate"><span class="pre">None</span></code> if the buff doesn’t exist on the handler. It is also the only getter
|
||
that returns a single buff instance, rather than a dictionary.</p>
|
||
<blockquote>
|
||
<div><p><strong>Note</strong>: The handler method <code class="docutils literal notranslate"><span class="pre">has(buff)</span></code> allows you to check if a matching key (if a string) or buff class (if a class) is present on the handler cache, without actually instantiating the buff. You should use this method for basic “is this buff present?” checks.</p>
|
||
</div></blockquote>
|
||
<p>Group getters, listed below, return a dictionary of values in the format <code class="docutils literal notranslate"><span class="pre">{buffkey:</span> <span class="pre">instance}</span></code>. If you want to iterate over all of these buffs,
|
||
you should do so via the <code class="docutils literal notranslate"><span class="pre">dict.values()</span></code> method.</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">get_all()</span></code> returns all buffs on this handler. You can also use the <code class="docutils literal notranslate"><span class="pre">handler.all</span></code> property.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">get_by_type(BuffClass)</span></code> returns buffs of the specified type.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">get_by_stat(stat)</span></code> returns buffs with a <code class="docutils literal notranslate"><span class="pre">Mod</span></code> object of the specified <code class="docutils literal notranslate"><span class="pre">stat</span></code> string in their <code class="docutils literal notranslate"><span class="pre">mods</span></code> list.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">get_by_trigger(string)</span></code> returns buffs with the specified string in their <code class="docutils literal notranslate"><span class="pre">triggers</span></code> list.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">get_by_source(Object)</span></code> returns buffs applied by the specified <code class="docutils literal notranslate"><span class="pre">source</span></code> object.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">get_by_cachevalue(key,</span> <span class="pre">value)</span></code> returns buffs with the matching <code class="docutils literal notranslate"><span class="pre">key:</span> <span class="pre">value</span></code> pair in their cache. <code class="docutils literal notranslate"><span class="pre">value</span></code> is optional.</p></li>
|
||
</ul>
|
||
<p>All group getters besides <code class="docutils literal notranslate"><span class="pre">get_all()</span></code> can “slice” an existing dictionary through the optional <code class="docutils literal notranslate"><span class="pre">to_filter</span></code> argument.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">dict1</span> <span class="o">=</span> <span class="n">handler</span><span class="o">.</span><span class="n">get_by_type</span><span class="p">(</span><span class="n">Burned</span><span class="p">)</span> <span class="c1"># This finds all "Burned" buffs on the handler</span>
|
||
<span class="n">dict2</span> <span class="o">=</span> <span class="n">handler</span><span class="o">.</span><span class="n">get_by_source</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">to_filter</span><span class="o">=</span><span class="n">dict1</span><span class="p">)</span> <span class="c1"># This filters dict1 to find buffs with the matching source</span>
|
||
</pre></div>
|
||
</div>
|
||
<blockquote>
|
||
<div><p><strong>Note</strong>: Most of these getters also have an associated handler property. For example, <code class="docutils literal notranslate"><span class="pre">handler.effects</span></code> returns all buffs that can be triggered, which
|
||
is then iterated over by the <code class="docutils literal notranslate"><span class="pre">get_by_trigger</span></code> method.</p>
|
||
</div></blockquote>
|
||
</section>
|
||
<section id="remove-buffs">
|
||
<h3>Remove Buffs<a class="headerlink" href="#remove-buffs" title="Permalink to this headline">¶</a></h3>
|
||
<p>There are also a number of remover methods. Generally speaking, these follow the same format as the getters.</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">remove(key)</span></code> removes the buff with the specified key.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">clear()</span></code> removes all buffs.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">remove_by_type(BuffClass)</span></code> removes buffs of the specified type.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">remove_by_stat(stat)</span></code> removes buffs with a <code class="docutils literal notranslate"><span class="pre">Mod</span></code> object of the specified <code class="docutils literal notranslate"><span class="pre">stat</span></code> string in their <code class="docutils literal notranslate"><span class="pre">mods</span></code> list.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">remove_by_trigger(string)</span></code> removes buffs with the specified string in their <code class="docutils literal notranslate"><span class="pre">triggers</span></code> list.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">remove_by_source(Object)</span></code> removes buffs applied by the specified source</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">remove_by_cachevalue(key,</span> <span class="pre">value)</span></code> removes buffs with the matching <code class="docutils literal notranslate"><span class="pre">key:</span> <span class="pre">value</span></code> pair in their cache. <code class="docutils literal notranslate"><span class="pre">value</span></code> is optional.</p></li>
|
||
</ul>
|
||
<p>You can also remove a buff by calling the instance’s <code class="docutils literal notranslate"><span class="pre">remove</span></code> helper method. You can do this on the dictionaries returned by the
|
||
getters listed above.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">to_remove</span> <span class="o">=</span> <span class="n">handler</span><span class="o">.</span><span class="n">get_by_trigger</span><span class="p">(</span><span class="n">trigger</span><span class="p">)</span> <span class="c1"># Finds all buffs with the specified trigger</span>
|
||
<span class="k">for</span> <span class="n">buff</span> <span class="ow">in</span> <span class="n">to_remove</span><span class="o">.</span><span class="n">values</span><span class="p">():</span> <span class="c1"># Removes all buffs in the to_remove dictionary via helper methods</span>
|
||
<span class="n">buff</span><span class="o">.</span><span class="n">remove</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="check-modifiers">
|
||
<h3>Check Modifiers<a class="headerlink" href="#check-modifiers" title="Permalink to this headline">¶</a></h3>
|
||
<p>Call the handler <code class="docutils literal notranslate"><span class="pre">check(value,</span> <span class="pre">stat)</span></code> method when you want to see the modified value.
|
||
This will return the <code class="docutils literal notranslate"><span class="pre">value</span></code>, modified by any relevant buffs on the handler’s owner (identified by
|
||
the <code class="docutils literal notranslate"><span class="pre">stat</span></code> string).</p>
|
||
<p>For example, let’s say you want to modify how much damage you take. That might look something like this:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># The method we call to damage ourselves</span>
|
||
<span class="k">def</span> <span class="nf">take_damage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">source</span><span class="p">,</span> <span class="n">damage</span><span class="p">):</span>
|
||
<span class="n">_damage</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">buffs</span><span class="o">.</span><span class="n">check</span><span class="p">(</span><span class="n">damage</span><span class="p">,</span> <span class="s1">'taken_damage'</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">health</span> <span class="o">-=</span> <span class="n">_damage</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This method calls the <code class="docutils literal notranslate"><span class="pre">at_pre_check</span></code> and <code class="docutils literal notranslate"><span class="pre">at_post_check</span></code> methods at the relevant points in the process. You can use to this make
|
||
buffs that are reactive to being checked; for example, removing themselves, altering their values, or interacting with the game state.</p>
|
||
<blockquote>
|
||
<div><p><strong>Note</strong>: You can also trigger relevant buffs at the same time as you check them by ensuring the optional argument <code class="docutils literal notranslate"><span class="pre">trigger</span></code> is True in the <code class="docutils literal notranslate"><span class="pre">check</span></code> method.</p>
|
||
</div></blockquote>
|
||
<p>Modifiers are calculated additively - that is, all modifiers of the same type are added together before being applied. They are then
|
||
applied through the following formula.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="n">base</span> <span class="o">+</span> <span class="n">total_add</span><span class="p">)</span> <span class="o">/</span> <span class="nb">max</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mf">1.0</span> <span class="o">+</span> <span class="n">total_div</span><span class="p">)</span> <span class="o">*</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mf">1.0</span> <span class="o">+</span> <span class="n">total_mult</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<section id="multiplicative-buffs-advanced">
|
||
<h4>Multiplicative Buffs (Advanced)<a class="headerlink" href="#multiplicative-buffs-advanced" title="Permalink to this headline">¶</a></h4>
|
||
<p>Multiply/divide modifiers in this buff system are additive by default. This means that two +50% modifiers will equal a +100% modifier. But what if you want to apply mods multiplicatively?</p>
|
||
<p>First, you should carefully consider if you truly want multiplicative modifiers. Here’s some things to consider.</p>
|
||
<ul class="simple">
|
||
<li><p>They are unintuitive to the average user, as two +50% damage buffs equal +125% instead of +100%.</p></li>
|
||
<li><p>They lead to “power explosion”, where stacking buffs in the right way can turn characters into unstoppable forces</p></li>
|
||
</ul>
|
||
<p>Doing purely-additive multipliers allows you to better control the balance of your game. Conversely, doing multiplicative multipliers enables very fun build-crafting where smart usage of buffs and skills can turn you into a one-shot powerhouse. Each has its place.</p>
|
||
<p>The best design practice for multiplicative buffs is to divide your multipliers into “tiers”, where each tier is applied separately. You can easily do this with multiple <code class="docutils literal notranslate"><span class="pre">check</span></code> calls.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">damage</span> <span class="o">=</span> <span class="n">damage</span>
|
||
<span class="n">damage</span> <span class="o">=</span> <span class="n">handler</span><span class="o">.</span><span class="n">check</span><span class="p">(</span><span class="n">damage</span><span class="p">,</span> <span class="s1">'damage'</span><span class="p">)</span>
|
||
<span class="n">damage</span> <span class="o">=</span> <span class="n">handler</span><span class="o">.</span><span class="n">check</span><span class="p">(</span><span class="n">damage</span><span class="p">,</span> <span class="s1">'empower'</span><span class="p">)</span>
|
||
<span class="n">damage</span> <span class="o">=</span> <span class="n">handler</span><span class="o">.</span><span class="n">check</span><span class="p">(</span><span class="n">damage</span><span class="p">,</span> <span class="s1">'radiant'</span><span class="p">)</span>
|
||
<span class="n">damage</span> <span class="o">=</span> <span class="n">handler</span><span class="o">.</span><span class="n">check</span><span class="p">(</span><span class="n">damage</span><span class="p">,</span> <span class="s1">'overpower'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="buff-strength-priority-advanced">
|
||
<h4>Buff Strength Priority (Advanced)<a class="headerlink" href="#buff-strength-priority-advanced" title="Permalink to this headline">¶</a></h4>
|
||
<p>Sometimes you only want to apply the strongest modifier to a stat. This is supported by the optional <code class="docutils literal notranslate"><span class="pre">strongest</span></code> bool arg in the handler’s check method</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">take_damage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">source</span><span class="p">,</span> <span class="n">damage</span><span class="p">):</span>
|
||
<span class="n">_damage</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">buffs</span><span class="o">.</span><span class="n">check</span><span class="p">(</span><span class="n">damage</span><span class="p">,</span> <span class="s1">'taken_damage'</span><span class="p">,</span> <span class="n">strongest</span><span class="o">=</span><span class="kc">True</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">health</span> <span class="o">-=</span> <span class="n">_damage</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="trigger-buffs">
|
||
<h3>Trigger Buffs<a class="headerlink" href="#trigger-buffs" title="Permalink to this headline">¶</a></h3>
|
||
<p>Call the handler’s <code class="docutils literal notranslate"><span class="pre">trigger(string)</span></code> method when you want an event call. This will call the <code class="docutils literal notranslate"><span class="pre">at_trigger</span></code> hook method on all buffs with the relevant trigger <code class="docutils literal notranslate"><span class="pre">string</span></code>.</p>
|
||
<p>For example, let’s say you want to trigger a buff to “detonate” when you hit your target with an attack.
|
||
You’d write a buff that might look like this:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Detonate</span><span class="p">(</span><span class="n">BaseBuff</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
<span class="n">triggers</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'take_damage'</span><span class="p">]</span>
|
||
<span class="k">def</span> <span class="nf">at_trigger</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">trigger</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">owner</span><span class="o">.</span><span class="n">take_damage</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">remove</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>And then call <code class="docutils literal notranslate"><span class="pre">handler.trigger('take_damage')</span></code> in the method you use to take damage.</p>
|
||
<blockquote>
|
||
<div><p><strong>Note</strong> You could also do this through mods and <code class="docutils literal notranslate"><span class="pre">at_post_check</span></code> if you like, depending on how to want to add the damage.</p>
|
||
</div></blockquote>
|
||
</section>
|
||
<section id="ticking">
|
||
<h3>Ticking<a class="headerlink" href="#ticking" title="Permalink to this headline">¶</a></h3>
|
||
<p>Ticking buffs are slightly special. They are similar to trigger buffs in that they run code, but instead of
|
||
doing so on an event trigger, they do so on a periodic tick. A common use case for a buff like this is a poison,
|
||
or a heal over time.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Poison</span><span class="p">(</span><span class="n">BaseBuff</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
<span class="n">tickrate</span> <span class="o">=</span> <span class="mi">5</span>
|
||
<span class="k">def</span> <span class="nf">at_tick</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">initial</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="n">_dmg</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">dmg</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">stacks</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">initial</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">owner</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">"Poison courses through </span><span class="si">{actor}</span><span class="s2">'s body, dealing </span><span class="si">{damage}</span><span class="s2"> damage."</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
|
||
<span class="n">actor</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">owner</span><span class="o">.</span><span class="n">named</span><span class="p">,</span> <span class="n">damage</span><span class="o">=</span><span class="n">_dmg</span>
|
||
<span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>To make a buff ticking, ensure the <code class="docutils literal notranslate"><span class="pre">tickrate</span></code> is 1 or higher, and it has code in its <code class="docutils literal notranslate"><span class="pre">at_tick</span></code>
|
||
method. Once you add it to the handler, it starts ticking!</p>
|
||
<blockquote>
|
||
<div><p><strong>Note</strong>: Ticking buffs always tick on initial application, when <code class="docutils literal notranslate"><span class="pre">initial</span></code> is <code class="docutils literal notranslate"><span class="pre">True</span></code>. If you don’t want your hook to fire at that time,
|
||
make sure to check the value of <code class="docutils literal notranslate"><span class="pre">initial</span></code> in your <code class="docutils literal notranslate"><span class="pre">at_tick</span></code> method.</p>
|
||
</div></blockquote>
|
||
</section>
|
||
<section id="context">
|
||
<h3>Context<a class="headerlink" href="#context" title="Permalink to this headline">¶</a></h3>
|
||
<p>Every important handler method optionally accepts a <code class="docutils literal notranslate"><span class="pre">context</span></code> dictionary.</p>
|
||
<p>Context is an important concept for this handler. Every method which checks, triggers, or ticks a buff passes this
|
||
dictionary (default: empty) to the buff hook methods as keyword arguments (<code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>). It is used for nothing else. This allows you to make those
|
||
methods “event-aware” by storing relevant data in the dictionary you feed to the method.</p>
|
||
<p>For example, let’s say you want a “thorns” buff which damages enemies that attack you. Let’s take our <code class="docutils literal notranslate"><span class="pre">take_damage</span></code> method
|
||
and add a context to the mix.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">take_damage</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">damage</span><span class="p">):</span>
|
||
<span class="n">context</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'attacker'</span><span class="p">:</span> <span class="n">attacker</span><span class="p">,</span> <span class="s1">'damage'</span><span class="p">:</span> <span class="n">damage</span><span class="p">}</span>
|
||
<span class="n">_damage</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">buffs</span><span class="o">.</span><span class="n">check</span><span class="p">(</span><span class="n">damage</span><span class="p">,</span> <span class="s1">'taken_damage'</span><span class="p">,</span> <span class="n">context</span><span class="o">=</span><span class="n">context</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">buffs</span><span class="o">.</span><span class="n">trigger</span><span class="p">(</span><span class="s1">'taken_damage'</span><span class="p">,</span> <span class="n">context</span><span class="o">=</span><span class="n">context</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">health</span> <span class="o">-=</span> <span class="n">_damage</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Now we use the values that context passes to the buff kwargs to customize our logic.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ThornsBuff</span><span class="p">(</span><span class="n">BaseBuff</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
<span class="n">triggers</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'taken_damage'</span><span class="p">]</span>
|
||
<span class="c1"># This is the hook method on our thorns buff</span>
|
||
<span class="k">def</span> <span class="nf">at_trigger</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">trigger</span><span class="p">,</span> <span class="n">attacker</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">damage</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">attacker</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
<span class="n">attacker</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">health</span> <span class="o">-=</span> <span class="n">damage</span> <span class="o">*</span> <span class="mf">0.2</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Apply the buff, take damage, and watch the thorns buff do its work!</p>
|
||
</section>
|
||
<section id="viewing">
|
||
<h3>Viewing<a class="headerlink" href="#viewing" title="Permalink to this headline">¶</a></h3>
|
||
<p>There are two helper methods on the handler that allow you to get useful buff information back.</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">view</span></code>: Returns a dictionary of tuples in the format <code class="docutils literal notranslate"><span class="pre">{buffkey:</span> <span class="pre">(buff.name,</span> <span class="pre">buff.flavor)}</span></code>. Finds all buffs by default, but optionally accepts a dictionary of buffs to filter as well. Useful for basic buff readouts.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">view_modifiers(stat)</span></code>: Returns a nested dictionary of information on modifiers that affect the specified stat. The first layer is the modifier type (<code class="docutils literal notranslate"><span class="pre">add/mult/div</span></code>) and the second layer is the value type (<code class="docutils literal notranslate"><span class="pre">total/strongest</span></code>). Does not return the buffs that cause these modifiers, just the modifiers themselves (akin to using <code class="docutils literal notranslate"><span class="pre">handler.check</span></code> but without actually modifying a value). Useful for stat sheets.</p></li>
|
||
</ul>
|
||
<p>You can also create your own custom viewing methods through the various handler getters, which will always return the entire buff object.</p>
|
||
</section>
|
||
</section>
|
||
<section id="creating-new-buffs">
|
||
<h2>Creating New Buffs<a class="headerlink" href="#creating-new-buffs" title="Permalink to this headline">¶</a></h2>
|
||
<p>Creating a new buff is very easy: extend <code class="docutils literal notranslate"><span class="pre">BaseBuff</span></code> into a new class, and fill in all the relevant buff details.
|
||
However, there are a lot of individual moving parts to a buff. Here’s a step-through of the important stuff.</p>
|
||
<section id="basics">
|
||
<h3>Basics<a class="headerlink" href="#basics" title="Permalink to this headline">¶</a></h3>
|
||
<p>Regardless of any other functionality, all buffs have the following class attributes:</p>
|
||
<ul class="simple">
|
||
<li><p>They have customizable <code class="docutils literal notranslate"><span class="pre">key</span></code>, <code class="docutils literal notranslate"><span class="pre">name</span></code>, and <code class="docutils literal notranslate"><span class="pre">flavor</span></code> strings.</p></li>
|
||
<li><p>They have a <code class="docutils literal notranslate"><span class="pre">duration</span></code> (float), and automatically clean-up at the end. Use -1 for infinite duration, and 0 to clean-up immediately. (default: -1)</p></li>
|
||
<li><p>They have a <code class="docutils literal notranslate"><span class="pre">tickrate</span></code> (float), and automatically tick if it is greater than 1 (default: 0)</p></li>
|
||
<li><p>They can stack, if <code class="docutils literal notranslate"><span class="pre">maxstacks</span></code> (int) is not equal to 1. If it’s 0, the buff stacks forever. (default: 1)</p></li>
|
||
<li><p>They can be <code class="docutils literal notranslate"><span class="pre">unique</span></code> (bool), which determines if they have a unique namespace or not. (default: True)</p></li>
|
||
<li><p>They can <code class="docutils literal notranslate"><span class="pre">refresh</span></code> (bool), which resets the duration when stacked or reapplied. (default: True)</p></li>
|
||
<li><p>They can be <code class="docutils literal notranslate"><span class="pre">playtime</span></code> (bool) buffs, where duration only counts down during active play. (default: False)</p></li>
|
||
</ul>
|
||
<p>Buffs also have a few useful properties:</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">owner</span></code>: The object this buff is attached to</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">ticknum</span></code>: How many ticks the buff has gone through</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">timeleft</span></code>: How much time is remaining on the buff</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">ticking</span></code>/<code class="docutils literal notranslate"><span class="pre">stacking</span></code>: If this buff ticks/stacks (checks <code class="docutils literal notranslate"><span class="pre">tickrate</span></code> and <code class="docutils literal notranslate"><span class="pre">maxstacks</span></code>)</p></li>
|
||
</ul>
|
||
<section id="buff-cache-advanced">
|
||
<h4>Buff Cache (Advanced)<a class="headerlink" href="#buff-cache-advanced" title="Permalink to this headline">¶</a></h4>
|
||
<p>Buffs always store some useful mutable information about themselves in the cache (what is stored on the owning object’s database attribute). A buff’s cache corresponds to <code class="docutils literal notranslate"><span class="pre">{buffkey:</span> <span class="pre">buffcache}</span></code>, where <code class="docutils literal notranslate"><span class="pre">buffcache</span></code> is a dictionary containing <strong>at least</strong> the information below:</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">ref</span></code> (class): The buff class path we use to construct the buff.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">start</span></code> (float): The timestamp of when the buff was applied.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">source</span></code> (Object): If specified; this allows you to track who or what applied the buff.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">prevtick</span></code> (float): The timestamp of the previous tick.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">duration</span></code> (float): The cached duration. This can vary from the class duration, depending on if the duration has been modified (paused, extended, shortened, etc).</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">tickrate</span></code> (float): The buff’s tick rate. Cannot go below 0. Altering the tickrate on an applied buff will not cause it to start ticking if it wasn’t ticking before. (<code class="docutils literal notranslate"><span class="pre">pause</span></code> and <code class="docutils literal notranslate"><span class="pre">unpause</span></code> to start/stop ticking on existing buffs)</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">stacks</span></code> (int): How many stacks they have.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">paused</span></code> (bool): Paused buffs do not clean up, modify values, tick, or fire any hook methods.</p></li>
|
||
</ul>
|
||
<p>Sometimes you will want to dynamically update a buff’s cache at runtime, such as changing a tickrate in a hook method, or altering a buff’s duration.
|
||
You can do so by using the interface <code class="docutils literal notranslate"><span class="pre">buff.cachekey</span></code>. As long as the attribute name matches a key in the cache dictionary, it will update the stored
|
||
cache with the new value.</p>
|
||
<p>If there is no matching key, it will do nothing. If you wish to add a new key to the cache, you must use the <code class="docutils literal notranslate"><span class="pre">buff.update_cache(dict)</span></code> method,
|
||
which will properly update the cache (including adding new keys) using the dictionary provided.</p>
|
||
<blockquote>
|
||
<div><p><strong>Example</strong>: You want to increase a buff’s duration by 30 seconds. You use <code class="docutils literal notranslate"><span class="pre">buff.duration</span> <span class="pre">+=</span> <span class="pre">30</span></code>. This new duration is now reflected on both the instance and the cache.</p>
|
||
</div></blockquote>
|
||
<p>The buff cache can also store arbitrary information. To do so, pass a dictionary through the handler <code class="docutils literal notranslate"><span class="pre">add</span></code> method (<code class="docutils literal notranslate"><span class="pre">handler.add(BuffClass,</span> <span class="pre">to_cache=dict)</span></code>),
|
||
set the <code class="docutils literal notranslate"><span class="pre">cache</span></code> dictionary attribute on your buff class, or use the aforementioned <code class="docutils literal notranslate"><span class="pre">buff.update_cache(dict)</span></code> method.</p>
|
||
<blockquote>
|
||
<div><p><strong>Example</strong>: You store <code class="docutils literal notranslate"><span class="pre">damage</span></code> as a value in the buff cache and use it for your poison buff. You want to increase it over time, so you use <code class="docutils literal notranslate"><span class="pre">buff.damage</span> <span class="pre">+=</span> <span class="pre">1</span></code> in the tick method.</p>
|
||
</div></blockquote>
|
||
</section>
|
||
</section>
|
||
<section id="modifiers">
|
||
<h3>Modifiers<a class="headerlink" href="#modifiers" title="Permalink to this headline">¶</a></h3>
|
||
<p>Mods are stored in the <code class="docutils literal notranslate"><span class="pre">mods</span></code> list attribute. Buffs which have one or more Mod objects in them can modify stats. You can use the handler method to check all
|
||
mods of a specific stat string and apply their modifications to the value; however, you are encouraged to use <code class="docutils literal notranslate"><span class="pre">check</span></code> in a getter/setter, for easy access.</p>
|
||
<p>Mod objects consist of only four values, assigned by the constructor in this order:</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">stat</span></code>: The stat you want to modify. When <code class="docutils literal notranslate"><span class="pre">check</span></code> is called, this string is used to find all the mods that are to be collected.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">mod</span></code>: The modifier. Defaults are <code class="docutils literal notranslate"><span class="pre">add</span></code> (addition/subtraction), <code class="docutils literal notranslate"><span class="pre">mult</span></code> (multiply), and <code class="docutils literal notranslate"><span class="pre">div</span></code> (divide). Modifiers are calculated additively (see <code class="docutils literal notranslate"><span class="pre">_calculate_mods</span></code> for more)</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">value</span></code>: How much value the modifier gives regardless of stacks</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">perstack</span></code>: How much value the modifier grants per stack, <strong>INCLUDING</strong> the first. (default: 0)</p></li>
|
||
</ul>
|
||
<p>The most basic way to add a Mod to a buff is to do so in the buff class definition, like this:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">DamageBuff</span><span class="p">(</span><span class="n">BaseBuff</span><span class="p">):</span>
|
||
<span class="n">mods</span> <span class="o">=</span> <span class="p">[</span><span class="n">Mod</span><span class="p">(</span><span class="s1">'damage'</span><span class="p">,</span> <span class="s1">'add'</span><span class="p">,</span> <span class="mi">10</span><span class="p">)]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>No mods applied to the value are permanent in any way. All calculations are done at runtime, and the mod values are never stored
|
||
anywhere except on the buff in question. In other words: you don’t need to track the origin of particular stat mods, and you will
|
||
never permanently change a stat modified by a buff. To remove the modification, simply remove the buff from the object.</p>
|
||
<blockquote>
|
||
<div><p><strong>Note</strong>: You can add your own modifier types by overloading the <code class="docutils literal notranslate"><span class="pre">_calculate_mods</span></code> method, which contains the basic modifier application logic.</p>
|
||
</div></blockquote>
|
||
<section id="generating-mods-advanced">
|
||
<h4>Generating Mods (Advanced)<a class="headerlink" href="#generating-mods-advanced" title="Permalink to this headline">¶</a></h4>
|
||
<p>An advanced way to do mods is to generate them when the buff is initialized. This lets you create mods on the fly that are reactive to the game state.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">GeneratedStatBuff</span><span class="p">(</span><span class="n">BaseBuff</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
<span class="k">def</span> <span class="nf">at_init</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="c1"># Finds our "modgen" cache value, and generates a mod from it</span>
|
||
<span class="n">modgen</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">cache</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"modgen"</span><span class="p">))</span>
|
||
<span class="k">if</span> <span class="n">modgen</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">mods</span> <span class="o">=</span> <span class="p">[</span><span class="n">Mod</span><span class="p">(</span><span class="o">*</span><span class="n">modgen</span><span class="p">)]</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="triggers">
|
||
<h3>Triggers<a class="headerlink" href="#triggers" title="Permalink to this headline">¶</a></h3>
|
||
<p>Buffs which have one or more strings in the <code class="docutils literal notranslate"><span class="pre">triggers</span></code> attribute can be triggered by events.</p>
|
||
<p>When the handler’s <code class="docutils literal notranslate"><span class="pre">trigger</span></code> method is called, it searches all buffs on the handler for any with a matchingtrigger,
|
||
then calls their <code class="docutils literal notranslate"><span class="pre">at_trigger</span></code> hooks. Buffs can have multiple triggers, and you can tell which trigger was used by
|
||
the <code class="docutils literal notranslate"><span class="pre">trigger</span></code> argument in the hook.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">AmplifyBuff</span><span class="p">(</span><span class="n">BaseBuff</span><span class="p">):</span>
|
||
<span class="n">triggers</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'damage'</span><span class="p">,</span> <span class="s1">'heal'</span><span class="p">]</span>
|
||
|
||
<span class="k">def</span> <span class="nf">at_trigger</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">trigger</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">trigger</span> <span class="o">==</span> <span class="s1">'damage'</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="s1">'Damage trigger called!'</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">trigger</span> <span class="o">==</span> <span class="s1">'heal'</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="s1">'Heal trigger called!'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="id1">
|
||
<h3>Ticking<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
|
||
<p>A buff which ticks isn’t much different than one which triggers. You’re still executing arbitrary hooks on
|
||
the buff class. To tick, the buff must have a <code class="docutils literal notranslate"><span class="pre">tickrate</span></code> of 1 or higher.</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Poison</span><span class="p">(</span><span class="n">BaseBuff</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
<span class="c1"># this buff will tick 6 times between application and cleanup.</span>
|
||
<span class="n">duration</span> <span class="o">=</span> <span class="mi">30</span>
|
||
<span class="n">tickrate</span> <span class="o">=</span> <span class="mi">5</span>
|
||
<span class="k">def</span> <span class="nf">at_tick</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">initial</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">owner</span><span class="o">.</span><span class="n">take_damage</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<blockquote>
|
||
<div><p><strong>Note</strong>: The buff always ticks once when applied. For this <strong>first tick only</strong>, <code class="docutils literal notranslate"><span class="pre">initial</span></code> will be True in the <code class="docutils literal notranslate"><span class="pre">at_tick</span></code> hook method. <code class="docutils literal notranslate"><span class="pre">initial</span></code> will be False on subsequent ticks.</p>
|
||
</div></blockquote>
|
||
<p>Ticks utilize a persistent delay, so they should be pickleable. As long as you are not adding new properties to your buff class, this shouldn’t be a concern.
|
||
If you <strong>are</strong> adding new properties, try to ensure they do not end up with a circular code path to their object or handler, as this will cause pickling errors.</p>
|
||
</section>
|
||
<section id="extras">
|
||
<h3>Extras<a class="headerlink" href="#extras" title="Permalink to this headline">¶</a></h3>
|
||
<p>Buffs have a grab-bag of extra functionality to let you add complexity to your designs.</p>
|
||
<section id="conditionals">
|
||
<h4>Conditionals<a class="headerlink" href="#conditionals" title="Permalink to this headline">¶</a></h4>
|
||
<p>You can restrict whether or not the buff will <code class="docutils literal notranslate"><span class="pre">check</span></code>, <code class="docutils literal notranslate"><span class="pre">trigger</span></code>, or <code class="docutils literal notranslate"><span class="pre">tick</span></code> through defining the <code class="docutils literal notranslate"><span class="pre">conditional</span></code> hook. As long
|
||
as it returns a “truthy” value, the buff will apply itself. This is useful for making buffs dependent on game state - for
|
||
example, if you want a buff that makes the player take more damage when they are on fire:</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">FireSick</span><span class="p">(</span><span class="n">BaseBuff</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
<span class="k">def</span> <span class="nf">conditional</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">owner</span><span class="o">.</span><span class="n">buffs</span><span class="o">.</span><span class="n">has</span><span class="p">(</span><span class="n">FireBuff</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="kc">True</span>
|
||
<span class="k">return</span> <span class="kc">False</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Conditionals for <code class="docutils literal notranslate"><span class="pre">check</span></code>/<code class="docutils literal notranslate"><span class="pre">trigger</span></code> are checked when the buffs are gathered by the handler methods for the respective operations. <code class="docutils literal notranslate"><span class="pre">Tick</span></code>
|
||
conditionals are checked each tick.</p>
|
||
</section>
|
||
<section id="helper-methods">
|
||
<h4>Helper Methods<a class="headerlink" href="#helper-methods" title="Permalink to this headline">¶</a></h4>
|
||
<p>Buff instances have a number of helper methods.</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">remove</span></code>/<code class="docutils literal notranslate"><span class="pre">dispel</span></code>: Allows you to remove or dispel the buff. Calls <code class="docutils literal notranslate"><span class="pre">at_remove</span></code>/<code class="docutils literal notranslate"><span class="pre">at_dispel</span></code>, depending on optional arguments.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">pause</span></code>/<code class="docutils literal notranslate"><span class="pre">unpause</span></code>: Pauses and unpauses the buff. Calls <code class="docutils literal notranslate"><span class="pre">at_pause</span></code>/<code class="docutils literal notranslate"><span class="pre">at_unpause</span></code>.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">reset</span></code>: Resets the buff’s start to the current time; same as “refreshing” it.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">alter_cache</span></code>: Updates the buff’s cache with the <code class="docutils literal notranslate"><span class="pre">{key:value}</span></code> pairs in the provided dictionary. Can overwrite default values, so be careful!</p></li>
|
||
</ul>
|
||
</section>
|
||
<section id="playtime-duration">
|
||
<h4>Playtime Duration<a class="headerlink" href="#playtime-duration" title="Permalink to this headline">¶</a></h4>
|
||
<p>If your handler has <code class="docutils literal notranslate"><span class="pre">autopause</span></code> enabled, any buffs with truthy <code class="docutils literal notranslate"><span class="pre">playtime</span></code> value will automatically pause
|
||
and unpause when the object the handler is attached to is puppetted or unpuppetted. This even works with ticking buffs,
|
||
although if you have less than 1 second of tick duration remaining, it will round up to 1s.</p>
|
||
<blockquote>
|
||
<div><p><strong>Note</strong>: If you want more control over this process, you can comment out the signal subscriptions on the handler and move the autopause logic
|
||
to your object’s <code class="docutils literal notranslate"><span class="pre">at_pre/post_puppet/unpuppet</span></code> hooks.</p>
|
||
</div></blockquote>
|
||
<hr class="docutils" />
|
||
<p><small>This document page is generated from <code class="docutils literal notranslate"><span class="pre">evennia/contrib/rpg/buffs/README.md</span></code>. Changes to this
|
||
file will be overwritten, so edit that file rather than this one.</small></p>
|
||
</section>
|
||
</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="Contrib-Character-Creator.html" title="Character Creator"
|
||
>next</a> |</li>
|
||
<li class="right" >
|
||
<a href="Contrib-XYZGrid.html" title="XYZgrid"
|
||
>previous</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia 2.x</a> »</li>
|
||
<li class="nav-item nav-item-1"><a href="Contribs-Overview.html" >Contribs</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">Buffs</a></li>
|
||
</ul>
|
||
</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> |