evennia/docs/1.0-dev/_modules/evennia/contrib/crafting/crafting.html
2021-05-15 00:35:21 +02:00

1166 lines
No EOL
110 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>evennia.contrib.crafting.crafting &#8212; Evennia 1.0-dev 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" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../../evennia.html" accesskey="U">evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.contrib.crafting.crafting</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1>Source code for evennia.contrib.crafting.crafting</h1><div class="highlight"><pre>
<span></span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd">Crafting - Griatch 2020</span>
<span class="sd">This is a general crafting engine. The basic functionality of crafting is to</span>
<span class="sd">combine any number of of items or tools in a &#39;recipe&#39; to produce a new result.</span>
<span class="sd"> item + item + item + tool + tool -&gt; recipe -&gt; new result</span>
<span class="sd">This is useful not only for traditional crafting but the engine is flexible</span>
<span class="sd">enough to also be useful for puzzles or similar.</span>
<span class="sd">## Installation</span>
<span class="sd">- Add the `CmdCraft` Command from this module to your default cmdset. This</span>
<span class="sd"> allows for crafting from in-game using a simple syntax.</span>
<span class="sd">- Create a new module and add it to a new list in your settings file</span>
<span class="sd"> (`server/conf/settings.py`) named `CRAFT_RECIPES_MODULES`, such as</span>
<span class="sd"> `CRAFT_RECIPE_MODULES = [&quot;world.recipes_weapons&quot;]`.</span>
<span class="sd">- In the new module(s), create one or more classes, each a child of</span>
<span class="sd"> `CraftingRecipe` from this module. Each such class must have a unique `.name`</span>
<span class="sd"> property. It also defines what inputs are required and what is created using</span>
<span class="sd"> this recipe.</span>
<span class="sd">- Objects to use for crafting should (by default) be tagged with tags using the</span>
<span class="sd"> tag-category `crafting_material` or `crafting_tool`. The name of the object</span>
<span class="sd"> doesn&#39;t matter, only its tag.</span>
<span class="sd">## Crafting in game</span>
<span class="sd">The default `craft` command handles all crafting needs.</span>
<span class="sd">::</span>
<span class="sd"> &gt; craft spiked club from club, nails</span>
<span class="sd">Here, `spiked club` specifies the recipe while `club` and `nails` are objects</span>
<span class="sd">the crafter must have in their inventory. These will be consumed during</span>
<span class="sd">crafting (by default only if crafting was successful).</span>
<span class="sd">A recipe can also require *tools* (like the `hammer` above). These must be</span>
<span class="sd">either in inventory *or* be in the current location. Tools are *not* consumed</span>
<span class="sd">during the crafting process.</span>
<span class="sd">::</span>
<span class="sd"> &gt; craft wooden doll from wood with knife</span>
<span class="sd">## Crafting in code</span>
<span class="sd">In code, you should use the helper function `craft` from this module. This</span>
<span class="sd">specifies the name of the recipe to use and expects all suitable</span>
<span class="sd">ingredients/tools as arguments (consumables and tools should be added together,</span>
<span class="sd">tools will be identified before consumables).</span>
<span class="sd">```python</span>
<span class="sd"> from evennia.contrib.crafting import crafting</span>
<span class="sd"> spiked_club = crafting.craft(crafter, &quot;spiked club&quot;, club, nails)</span>
<span class="sd">```</span>
<span class="sd">The result is always a list with zero or more objects. A fail leads to an empty</span>
<span class="sd">list. The crafter should already have been notified of any error in this case</span>
<span class="sd">(this should be handle by the recipe itself).</span>
<span class="sd">## Recipes</span>
<span class="sd">A *recipe* is a class that works like an input/output blackbox: you initialize</span>
<span class="sd">it with consumables (and/or tools) if they match the recipe, a new</span>
<span class="sd">result is spit out. Consumables are consumed in the process while tools are not.</span>
<span class="sd">This module contains a base class for making new ingredient types</span>
<span class="sd">(`CraftingRecipeBase`) and an implementation of the most common form of</span>
<span class="sd">crafting (`CraftingRecipe`) using objects and prototypes.</span>
<span class="sd">Recipes are put in one or more modules added as a list to the</span>
<span class="sd">`CRAFT_RECIPE_MODULES` setting, for example:</span>
<span class="sd">```python</span>
<span class="sd"> CRAFT_RECIPE_MODULES = [&#39;world.recipes_weapons&#39;, &#39;world.recipes_potions&#39;]</span>
<span class="sd">```</span>
<span class="sd">Below is an example of a crafting recipe and how `craft` calls it under the</span>
<span class="sd">hood. See the `CraftingRecipe` class for details of which properties and</span>
<span class="sd">methods are available to override - the craft behavior can be modified</span>
<span class="sd">substantially this way.</span>
<span class="sd">```python</span>
<span class="sd"> from evennia.contrib.crafting.crafting import CraftingRecipe</span>
<span class="sd"> class PigIronRecipe(CraftingRecipe):</span>
<span class="sd"> # Pig iron is a high-carbon result of melting iron in a blast furnace.</span>
<span class="sd"> name = &quot;pig iron&quot; # this is what crafting.craft and CmdCraft uses</span>
<span class="sd"> tool_tags = [&quot;blast furnace&quot;]</span>
<span class="sd"> consumable_tags = [&quot;iron ore&quot;, &quot;coal&quot;, &quot;coal&quot;]</span>
<span class="sd"> output_prototypes = [</span>
<span class="sd"> {&quot;key&quot;: &quot;Pig Iron ingot&quot;,</span>
<span class="sd"> &quot;desc&quot;: &quot;An ingot of crude pig iron.&quot;,</span>
<span class="sd"> &quot;tags&quot;: [(&quot;pig iron&quot;, &quot;crafting_material&quot;)]}</span>
<span class="sd"> ]</span>
<span class="sd"> # for testing, conveniently spawn all we need based on the tags on the class</span>
<span class="sd"> tools, consumables = PigIronRecipe.seed()</span>
<span class="sd"> recipe = PigIronRecipe(caller, *(tools + consumables))</span>
<span class="sd"> result = recipe.craft()</span>
<span class="sd">```</span>
<span class="sd">If the above class was added to a module in `CRAFT_RECIPE_MODULES`, it could be</span>
<span class="sd">called using its `.name` property, as &quot;pig iron&quot;.</span>
<span class="sd">The [example_recipies](api:evennia.contrib.crafting.example_recipes) module has</span>
<span class="sd">a full example of the components for creating a sword from base components.</span>
<span class="sd">----</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">copy</span> <span class="kn">import</span> <span class="n">copy</span>
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">iter_to_str</span><span class="p">,</span> <span class="n">callables_from_module</span><span class="p">,</span> <span class="n">inherits_from</span><span class="p">,</span> <span class="n">make_iter</span>
<span class="kn">from</span> <span class="nn">evennia.commands.cmdset</span> <span class="kn">import</span> <span class="n">CmdSet</span>
<span class="kn">from</span> <span class="nn">evennia.commands.command</span> <span class="kn">import</span> <span class="n">Command</span>
<span class="kn">from</span> <span class="nn">evennia.prototypes.spawner</span> <span class="kn">import</span> <span class="n">spawn</span>
<span class="kn">from</span> <span class="nn">evennia.utils.create</span> <span class="kn">import</span> <span class="n">create_object</span>
<span class="n">_RECIPE_CLASSES</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">def</span> <span class="nf">_load_recipes</span><span class="p">():</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Delayed loading of recipe classes. This parses</span>
<span class="sd"> `settings.CRAFT_RECIPE_MODULES`.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="kn">from</span> <span class="nn">django.conf</span> <span class="kn">import</span> <span class="n">settings</span>
<span class="k">global</span> <span class="n">_RECIPE_CLASSES</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">_RECIPE_CLASSES</span><span class="p">:</span>
<span class="n">paths</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;evennia.contrib.crafting.example_recipes&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">settings</span><span class="p">,</span> <span class="s2">&quot;CRAFT_RECIPE_MODULES&quot;</span><span class="p">):</span>
<span class="n">paths</span> <span class="o">+=</span> <span class="n">make_iter</span><span class="p">(</span><span class="n">settings</span><span class="o">.</span><span class="n">CRAFT_RECIPE_MODULES</span><span class="p">)</span>
<span class="k">for</span> <span class="n">path</span> <span class="ow">in</span> <span class="n">paths</span><span class="p">:</span>
<span class="k">for</span> <span class="bp">cls</span> <span class="ow">in</span> <span class="n">callables_from_module</span><span class="p">(</span><span class="n">path</span><span class="p">)</span><span class="o">.</span><span class="n">values</span><span class="p">():</span>
<span class="k">if</span> <span class="n">inherits_from</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">CraftingRecipeBase</span><span class="p">):</span>
<span class="n">_RECIPE_CLASSES</span><span class="p">[</span><span class="bp">cls</span><span class="o">.</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="bp">cls</span>
<div class="viewcode-block" id="CraftingError"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingError">[docs]</a><span class="k">class</span> <span class="nc">CraftingError</span><span class="p">(</span><span class="ne">RuntimeError</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Crafting error.</span>
<span class="sd"> &quot;&quot;&quot;</span></div>
<div class="viewcode-block" id="CraftingValidationError"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingValidationError">[docs]</a><span class="k">class</span> <span class="nc">CraftingValidationError</span><span class="p">(</span><span class="n">CraftingError</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Error if crafting validation failed.</span>
<span class="sd"> &quot;&quot;&quot;</span></div>
<div class="viewcode-block" id="CraftingRecipeBase"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipeBase">[docs]</a><span class="k">class</span> <span class="nc">CraftingRecipeBase</span><span class="p">:</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> The recipe handles all aspects of performing a &#39;craft&#39; operation. This is</span>
<span class="sd"> the base of the crafting system, intended to be replace if you want to</span>
<span class="sd"> adapt it for very different functionality - see the `CraftingRecipe` child</span>
<span class="sd"> class for an implementation of the most common type of crafting using</span>
<span class="sd"> objects.</span>
<span class="sd"> Example of usage:</span>
<span class="sd"> ::</span>
<span class="sd"> recipe = CraftRecipe(crafter, obj1, obj2, obj3)</span>
<span class="sd"> result = recipe.craft()</span>
<span class="sd"> Note that the most common crafting operation is that the inputs are</span>
<span class="sd"> consumed - so in that case the recipe cannot be used a second time (doing so</span>
<span class="sd"> will raise a `CraftingError`)</span>
<span class="sd"> Process:</span>
<span class="sd"> 1. `.craft(**kwargs)` - this starts the process on the initialized recipe. The kwargs</span>
<span class="sd"> are optional but will be passed into all of the following hooks.</span>
<span class="sd"> 2. `.pre_craft(**kwargs)` - this normally validates inputs and stores them in</span>
<span class="sd"> `.validated_inputs.`. Raises `CraftingValidationError` otherwise.</span>
<span class="sd"> 4. `.do_craft(**kwargs)` - should return the crafted item(s) or the empty list. Any</span>
<span class="sd"> crafting errors should be immediately reported to user.</span>
<span class="sd"> 5. `.post_craft(crafted_result, **kwargs)`- always called, even if `pre_craft`</span>
<span class="sd"> raised a `CraftingError` or `CraftingValidationError`.</span>
<span class="sd"> Should return `crafted_result` (modified or not).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;recipe base&quot;</span>
<span class="c1"># if set, allow running `.craft` more than once on the same instance.</span>
<span class="c1"># don&#39;t set this unless crafting inputs are *not* consumed by the crafting</span>
<span class="c1"># process (otherwise subsequent calls will fail).</span>
<span class="n">allow_reuse</span> <span class="o">=</span> <span class="kc">False</span>
<div class="viewcode-block" id="CraftingRecipeBase.__init__"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipeBase.__init__">[docs]</a> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">crafter</span><span class="p">,</span> <span class="o">*</span><span class="n">inputs</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Initialize the recipe.</span>
<span class="sd"> Args:</span>
<span class="sd"> crafter (Object): The one doing the crafting.</span>
<span class="sd"> *inputs (any): The ingredients of the recipe to use.</span>
<span class="sd"> **kwargs (any): Any other parameters that are relevant for</span>
<span class="sd"> this recipe.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">crafter</span> <span class="o">=</span> <span class="n">crafter</span>
<span class="bp">self</span><span class="o">.</span><span class="n">inputs</span> <span class="o">=</span> <span class="n">inputs</span>
<span class="bp">self</span><span class="o">.</span><span class="n">craft_kwargs</span> <span class="o">=</span> <span class="n">kwargs</span>
<span class="bp">self</span><span class="o">.</span><span class="n">allow_craft</span> <span class="o">=</span> <span class="kc">True</span>
<span class="bp">self</span><span class="o">.</span><span class="n">validated_inputs</span> <span class="o">=</span> <span class="p">[]</span></div>
<div class="viewcode-block" id="CraftingRecipeBase.msg"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipeBase.msg">[docs]</a> <span class="k">def</span> <span class="nf">msg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Send message to crafter. This is a central point to override if wanting</span>
<span class="sd"> to change crafting return style in some way.</span>
<span class="sd"> Args:</span>
<span class="sd"> message(str): The message to send.</span>
<span class="sd"> **kwargs: Any optional properties relevant to this send.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">crafter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="p">{</span><span class="s2">&quot;type&quot;</span><span class="p">:</span> <span class="s2">&quot;crafting&quot;</span><span class="p">})</span></div>
<div class="viewcode-block" id="CraftingRecipeBase.pre_craft"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipeBase.pre_craft">[docs]</a> <span class="k">def</span> <span class="nf">pre_craft</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Hook to override.</span>
<span class="sd"> This is called just before crafting operation and is normally</span>
<span class="sd"> responsible for validating the inputs, storing data on</span>
<span class="sd"> `self.validated_inputs`.</span>
<span class="sd"> Args:</span>
<span class="sd"> **kwargs: Optional extra flags passed during initialization or</span>
<span class="sd"> `.craft(**kwargs)`.</span>
<span class="sd"> Raises:</span>
<span class="sd"> CraftingValidationError: If validation fails.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">allow_craft</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">validated_inputs</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">inputs</span><span class="p">[:]</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">CraftingValidationError</span></div>
<div class="viewcode-block" id="CraftingRecipeBase.do_craft"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipeBase.do_craft">[docs]</a> <span class="k">def</span> <span class="nf">do_craft</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Hook to override.</span>
<span class="sd"> This performs the actual crafting. At this point the inputs are</span>
<span class="sd"> expected to have been verified already. If needed, the validated</span>
<span class="sd"> inputs are available on this recipe instance.</span>
<span class="sd"> Args:</span>
<span class="sd"> **kwargs: Any extra flags passed at initialization.</span>
<span class="sd"> Returns:</span>
<span class="sd"> any: The result of crafting.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="kc">None</span></div>
<div class="viewcode-block" id="CraftingRecipeBase.post_craft"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipeBase.post_craft">[docs]</a> <span class="k">def</span> <span class="nf">post_craft</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">crafting_result</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Hook to override.</span>
<span class="sd"> This is called just after crafting has finished. A common use of this</span>
<span class="sd"> method is to delete the inputs.</span>
<span class="sd"> Args:</span>
<span class="sd"> crafting_result (any): The outcome of crafting, as returned by `do_craft`.</span>
<span class="sd"> **kwargs: Any extra flags passed at initialization.</span>
<span class="sd"> Returns:</span>
<span class="sd"> any: The final crafting result.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">crafting_result</span></div>
<div class="viewcode-block" id="CraftingRecipeBase.craft"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipeBase.craft">[docs]</a> <span class="k">def</span> <span class="nf">craft</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">raise_exception</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Main crafting call method. Call this to produce a result and make</span>
<span class="sd"> sure all hooks run correctly.</span>
<span class="sd"> Args:</span>
<span class="sd"> raise_exception (bool): If crafting would return `None`, raise</span>
<span class="sd"> exception instead.</span>
<span class="sd"> **kwargs (any): Any other parameters that is relevant</span>
<span class="sd"> for this particular craft operation. This will temporarily</span>
<span class="sd"> override same-named kwargs given at the creation of this recipe</span>
<span class="sd"> and be passed into all of the crafting hooks.</span>
<span class="sd"> Returns:</span>
<span class="sd"> any: The result of the craft, or `None` if crafting failed.</span>
<span class="sd"> Raises:</span>
<span class="sd"> CraftingValidationError: If recipe validation failed and</span>
<span class="sd"> `raise_exception` is True.</span>
<span class="sd"> CraftingError: On If trying to rerun a no-rerun recipe, or if crafting</span>
<span class="sd"> would return `None` and raise_exception` is set.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">craft_result</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">allow_craft</span><span class="p">:</span>
<span class="c1"># override/extend craft_kwargs from initialization.</span>
<span class="n">craft_kwargs</span> <span class="o">=</span> <span class="n">copy</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">craft_kwargs</span><span class="p">)</span>
<span class="n">craft_kwargs</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="c1"># this assigns to self.validated_inputs</span>
<span class="bp">self</span><span class="o">.</span><span class="n">pre_craft</span><span class="p">(</span><span class="o">**</span><span class="n">craft_kwargs</span><span class="p">)</span>
<span class="k">except</span> <span class="p">(</span><span class="n">CraftingError</span><span class="p">,</span> <span class="n">CraftingValidationError</span><span class="p">):</span>
<span class="k">if</span> <span class="n">raise_exception</span><span class="p">:</span>
<span class="k">raise</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">craft_result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">do_craft</span><span class="p">(</span><span class="o">**</span><span class="n">craft_kwargs</span><span class="p">)</span>
<span class="k">finally</span><span class="p">:</span>
<span class="n">craft_result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">post_craft</span><span class="p">(</span><span class="n">craft_result</span><span class="p">,</span> <span class="o">**</span><span class="n">craft_kwargs</span><span class="p">)</span>
<span class="k">except</span> <span class="p">(</span><span class="n">CraftingError</span><span class="p">,</span> <span class="n">CraftingValidationError</span><span class="p">):</span>
<span class="k">if</span> <span class="n">raise_exception</span><span class="p">:</span>
<span class="k">raise</span>
<span class="c1"># possibly turn off re-use depending on class setting</span>
<span class="bp">self</span><span class="o">.</span><span class="n">allow_craft</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">allow_reuse</span>
<span class="k">elif</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">allow_reuse</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">CraftingError</span><span class="p">(</span><span class="s2">&quot;Cannot re-run crafting without re-initializing recipe first.&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="n">craft_result</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">raise_exception</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">CraftingError</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Crafting of </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2"> failed.&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">craft_result</span></div></div>
<div class="viewcode-block" id="CraftingRecipe"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipe">[docs]</a><span class="k">class</span> <span class="nc">CraftingRecipe</span><span class="p">(</span><span class="n">CraftingRecipeBase</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> The CraftRecipe implements the most common form of crafting: Combining (and</span>
<span class="sd"> consuming) inputs to produce a new result. This type of recipe only works</span>
<span class="sd"> with typeclassed entities as inputs and outputs, since it&#39;s based on Tags</span>
<span class="sd"> and Prototypes.</span>
<span class="sd"> There are two types of crafting ingredients: &#39;tools&#39; and &#39;consumables&#39;. The</span>
<span class="sd"> difference between them is that the former is not consumed in the crafting</span>
<span class="sd"> process. So if you need a hammer and anvil to craft a sword, they are</span>
<span class="sd"> &#39;tools&#39; whereas the materials of the sword are &#39;consumables&#39;.</span>
<span class="sd"> Examples:</span>
<span class="sd"> ::</span>
<span class="sd"> class FlourRecipe(CraftRecipe):</span>
<span class="sd"> name = &quot;flour&quot;</span>
<span class="sd"> tool_tags = [&#39;windmill&#39;]</span>
<span class="sd"> consumable_tags = [&quot;wheat&quot;]</span>
<span class="sd"> output_prototypes = [</span>
<span class="sd"> {&quot;key&quot;: &quot;Bag of flour&quot;,</span>
<span class="sd"> &quot;typeclass&quot;: &quot;typeclasses.food.Flour&quot;,</span>
<span class="sd"> &quot;desc&quot;: &quot;A small bag of flour.&quot;</span>
<span class="sd"> &quot;tags&quot;: [(&quot;flour&quot;, &quot;crafting_material&quot;),</span>
<span class="sd"> }</span>
<span class="sd"> class BreadRecipe(CraftRecipe):</span>
<span class="sd"> name = &quot;bread&quot;</span>
<span class="sd"> tool_tags = [&quot;roller&quot;, &quot;owen&quot;]</span>
<span class="sd"> consumable_tags = [&quot;flour&quot;, &quot;egg&quot;, &quot;egg&quot;, &quot;salt&quot;, &quot;water&quot;, &quot;yeast&quot;]</span>
<span class="sd"> output_prototypes = [</span>
<span class="sd"> {&quot;key&quot;: &quot;bread&quot;,</span>
<span class="sd"> &quot;desc&quot;: &quot;A tasty bread.&quot;</span>
<span class="sd"> }</span>
<span class="sd"> ## Properties on the class level:</span>
<span class="sd"> - `name` (str): The name of this recipe. This should be globally unique.</span>
<span class="sd"> ### tools</span>
<span class="sd"> - `tool_tag_category` (str): What tag-category tools must use. Default is</span>
<span class="sd"> &#39;crafting_tool&#39;.</span>
<span class="sd"> - `tool_tags` (list): Object-tags to use for tooling. If more than one instace</span>
<span class="sd"> of a tool is needed, add multiple entries here.</span>
<span class="sd"> - `tool_names` (list): Human-readable names for tools. These are used for informative</span>
<span class="sd"> messages/errors. If not given, the tags will be used. If given, this list should</span>
<span class="sd"> match the length of `tool_tags`.:</span>
<span class="sd"> - `exact_tools` (bool, default True): Must have exactly the right tools, any extra</span>
<span class="sd"> leads to failure.</span>
<span class="sd"> - `exact_tool_order` (bool, default False): Tools must be added in exactly the</span>
<span class="sd"> right order for crafting to pass.</span>
<span class="sd"> ### consumables</span>
<span class="sd"> - `consumable_tag_category` (str): What tag-category consumables must use.</span>
<span class="sd"> Default is &#39;crafting_material&#39;.</span>
<span class="sd"> - `consumable_tags` (list): Tags for objects that will be consumed as part of</span>
<span class="sd"> running the recipe.</span>
<span class="sd"> - `consumable_names` (list): Human-readable names for consumables. Same as for tools.</span>
<span class="sd"> - `exact_consumables` (bool, default True): Normally, adding more consumables</span>
<span class="sd"> than needed leads to a a crafting error. If this is False, the craft will</span>
<span class="sd"> still succeed (only the needed ingredients will be consumed).</span>
<span class="sd"> - `exact_consumable_order` (bool, default False): Normally, the order in which</span>
<span class="sd"> ingredients are added does not matter. With this set, trying to add consumables in</span>
<span class="sd"> another order than given will lead to failing crafting.</span>
<span class="sd"> - `consume_on_fail` (bool, default False): Normally, consumables remain if</span>
<span class="sd"> crafting fails. With this flag, a failed crafting will still consume</span>
<span class="sd"> consumables. Note that this will also consume any &#39;extra&#39; consumables</span>
<span class="sd"> added not part of the recipe!</span>
<span class="sd"> ### outputs (result of crafting)</span>
<span class="sd"> - `output_prototypes` (list): One or more prototypes (`prototype_keys` or</span>
<span class="sd"> full dicts) describing how to create the result(s) of this recipe.</span>
<span class="sd"> - `output_names` (list): Human-readable names for (prospective) prototypes.</span>
<span class="sd"> This is used in error messages. If not given, this is extracted from the</span>
<span class="sd"> prototypes&#39; `key` if possible.</span>
<span class="sd"> ### custom error messages</span>
<span class="sd"> custom messages all have custom formatting markers. Many are empty strings</span>
<span class="sd"> when not applicable.</span>
<span class="sd"> ::</span>
<span class="sd"> {missing}: Comma-separated list of tool/consumable missing for missing/out of order errors.</span>
<span class="sd"> {excess}: Comma-separated list of tool/consumable added in excess of recipe</span>
<span class="sd"> {inputs}: Comma-separated list of any inputs (tools + consumables) involved in error.</span>
<span class="sd"> {tools}: Comma-sepatated list of tools involved in error.</span>
<span class="sd"> {consumables}: Comma-separated list of consumables involved in error.</span>
<span class="sd"> {outputs}: Comma-separated list of (expected) outputs</span>
<span class="sd"> {t0}..{tN-1}: Individual tools, same order as `.tool_names`.</span>
<span class="sd"> {c0}..{cN-1}: Individual consumables, same order as `.consumable_names`.</span>
<span class="sd"> {o0}..{oN-1}: Individual outputs, same order as `.output_names`.</span>
<span class="sd"> - `error_tool_missing_message`: &quot;Could not craft {outputs} without {missing}.&quot;</span>
<span class="sd"> - `error_tool_order_message`:</span>
<span class="sd"> &quot;Could not craft {outputs} since {missing} was added in the wrong order.&quot;</span>
<span class="sd"> - `error_tool_excess_message`: &quot;Could not craft {outputs} (extra {excess}).&quot;</span>
<span class="sd"> - `error_consumable_missing_message`: &quot;Could not craft {outputs} without {missing}.&quot;</span>
<span class="sd"> - `error_consumable_order_message`:</span>
<span class="sd"> &quot;Could not craft {outputs} since {missing} was added in the wrong order.&quot;</span>
<span class="sd"> - `error_consumable_excess_message`: &quot;Could not craft {outputs} (excess {excess}).&quot;</span>
<span class="sd"> - `success_message`: &quot;You successfuly craft {outputs}!&quot;</span>
<span class="sd"> - `failure_message`: &quot;&quot; (this is handled by the other error messages by default)</span>
<span class="sd"> ## Hooks</span>
<span class="sd"> 1. Crafting starts by calling `.craft(**kwargs)` on the parent class. The</span>
<span class="sd"> `**kwargs` are optional, extends any `**kwargs` passed to the class</span>
<span class="sd"> constructor and will be passed into all the following hooks.</span>
<span class="sd"> 3. `.pre_craft(**kwargs)` should handle validation of inputs. Results should</span>
<span class="sd"> be stored in `validated_consumables/tools` respectively. Raises `CraftingValidationError`</span>
<span class="sd"> otherwise.</span>
<span class="sd"> 4. `.do_craft(**kwargs)` will not be called if validation failed. Should return</span>
<span class="sd"> a list of the things crafted.</span>
<span class="sd"> 5. `.post_craft(crafting_result, **kwargs)` is always called, also if validation</span>
<span class="sd"> failed (`crafting_result` will then be falsy). It does any cleanup. By default</span>
<span class="sd"> this deletes consumables.</span>
<span class="sd"> Use `.msg` to conveniently send messages to the crafter. Raise</span>
<span class="sd"> `evennia.contrib.crafting.crafting.CraftingError` exception to abort</span>
<span class="sd"> crafting at any time in the sequence. If raising with a text, this will be</span>
<span class="sd"> shown to the crafter automatically</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;crafting recipe&quot;</span>
<span class="c1"># this define the overall category all material tags must have</span>
<span class="n">consumable_tag_category</span> <span class="o">=</span> <span class="s2">&quot;crafting_material&quot;</span>
<span class="c1"># tag category for tool objects</span>
<span class="n">tool_tag_category</span> <span class="o">=</span> <span class="s2">&quot;crafting_tool&quot;</span>
<span class="c1"># the tools needed to perform this crafting. Tools are never consumed (if they were,</span>
<span class="c1"># they&#39;d need to be a consumable). If more than one instance of a tool is needed,</span>
<span class="c1"># there should be multiple entries in this list.</span>
<span class="n">tool_tags</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># human-readable names for the tools. This will be used for informative messages</span>
<span class="c1"># or when usage fails. If empty</span>
<span class="n">tool_names</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># if we must have exactly the right tools, no more</span>
<span class="n">exact_tools</span> <span class="o">=</span> <span class="kc">True</span>
<span class="c1"># if the order of the tools matters</span>
<span class="n">exact_tool_order</span> <span class="o">=</span> <span class="kc">False</span>
<span class="c1"># error to show if missing tools</span>
<span class="n">error_tool_missing_message</span> <span class="o">=</span> <span class="s2">&quot;Could not craft </span><span class="si">{outputs}</span><span class="s2"> without </span><span class="si">{missing}</span><span class="s2">.&quot;</span>
<span class="c1"># error to show if tool-order matters and it was wrong. Missing is the first</span>
<span class="c1"># tool out of order</span>
<span class="n">error_tool_order_message</span> <span class="o">=</span> <span class="p">(</span>
<span class="s2">&quot;Could not craft </span><span class="si">{outputs}</span><span class="s2"> since </span><span class="si">{missing}</span><span class="s2"> was added in the wrong order.&quot;</span>
<span class="p">)</span>
<span class="c1"># if .exact_tools is set and there are more than needed</span>
<span class="n">error_tool_excess_message</span> <span class="o">=</span> <span class="p">(</span>
<span class="s2">&quot;Could not craft </span><span class="si">{outputs}</span><span class="s2"> without the exact tools (extra </span><span class="si">{excess}</span><span class="s2">).&quot;</span>
<span class="p">)</span>
<span class="c1"># a list of tag-keys (of the `tag_category`). If more than one of each type</span>
<span class="c1"># is needed, there should be multiple same-named entries in this list.</span>
<span class="n">consumable_tags</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># these are human-readable names for the items to use. This is used for informative</span>
<span class="c1"># messages or when usage fails. If empty, the tag-names will be used. If given, this</span>
<span class="c1"># must have the same length as `consumable_tags`.</span>
<span class="n">consumable_names</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># if True, consume valid inputs also if crafting failed (returned None)</span>
<span class="n">consume_on_fail</span> <span class="o">=</span> <span class="kc">False</span>
<span class="c1"># if True, having any wrong input result in failing the crafting. If False,</span>
<span class="c1"># extra components beyond the recipe are ignored.</span>
<span class="n">exact_consumables</span> <span class="o">=</span> <span class="kc">True</span>
<span class="c1"># if True, the exact order in which inputs are provided matters and must match</span>
<span class="c1"># the order of `consumable_tags`. If False, order doesn&#39;t matter.</span>
<span class="n">exact_consumable_order</span> <span class="o">=</span> <span class="kc">False</span>
<span class="c1"># error to show if missing consumables</span>
<span class="n">error_consumable_missing_message</span> <span class="o">=</span> <span class="s2">&quot;Could not craft </span><span class="si">{outputs}</span><span class="s2"> without </span><span class="si">{missing}</span><span class="s2">.&quot;</span>
<span class="c1"># error to show if consumable order matters and it was wrong. Missing is the first</span>
<span class="c1"># consumable out of order</span>
<span class="n">error_consumable_order_message</span> <span class="o">=</span> <span class="p">(</span>
<span class="s2">&quot;Could not craft </span><span class="si">{outputs}</span><span class="s2"> since </span><span class="si">{missing}</span><span class="s2"> was added in the wrong order.&quot;</span>
<span class="p">)</span>
<span class="c1"># if .exact_consumables is set and there are more than needed</span>
<span class="n">error_consumable_excess_message</span> <span class="o">=</span> <span class="p">(</span>
<span class="s2">&quot;Could not craft </span><span class="si">{outputs}</span><span class="s2"> without the exact ingredients (extra </span><span class="si">{excess}</span><span class="s2">).&quot;</span>
<span class="p">)</span>
<span class="c1"># this is a list of one or more prototypes (prototype_keys to existing</span>
<span class="c1"># prototypes or full prototype-dicts) to use to build the result. All of</span>
<span class="c1"># these will be returned (as a list) if crafting succeeded.</span>
<span class="n">output_prototypes</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># human-readable name(s) for the (expected) result of this crafting. This will usually only</span>
<span class="c1"># be used for error messages (to report what would have been). If not given, the</span>
<span class="c1"># prototype&#39;s key or typeclass will be used. If given, this must have the same length</span>
<span class="c1"># as `output_prototypes`.</span>
<span class="n">output_names</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># general craft-failure msg to show after other error-messages.</span>
<span class="n">failure_message</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
<span class="c1"># show after a successful craft</span>
<span class="n">success_message</span> <span class="o">=</span> <span class="s2">&quot;You successfully craft </span><span class="si">{outputs}</span><span class="s2">!&quot;</span>
<div class="viewcode-block" id="CraftingRecipe.__init__"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipe.__init__">[docs]</a> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">crafter</span><span class="p">,</span> <span class="o">*</span><span class="n">inputs</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Args:</span>
<span class="sd"> crafter (Object): The one doing the crafting.</span>
<span class="sd"> *inputs (Object): The ingredients (+tools) of the recipe to use. The</span>
<span class="sd"> The recipe will itself figure out (from tags) which is a tool and</span>
<span class="sd"> which is a consumable.</span>
<span class="sd"> **kwargs (any): Any other parameters that are relevant for</span>
<span class="sd"> this recipe. These will be passed into the crafting hooks.</span>
<span class="sd"> Notes:</span>
<span class="sd"> Internally, this class stores validated data in</span>
<span class="sd"> `.validated_consumables` and `.validated_tools` respectively. The</span>
<span class="sd"> `.validated_inputs` property (from parent) holds a list of everything</span>
<span class="sd"> types in the order inserted to the class constructor.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">crafter</span><span class="p">,</span> <span class="o">*</span><span class="n">inputs</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">validated_consumables</span> <span class="o">=</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">validated_tools</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># validate class properties</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">consumable_names</span><span class="p">:</span>
<span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">consumable_names</span><span class="p">)</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">consumable_tags</span><span class="p">),</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;Crafting </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="si">}</span><span class="s2">.consumable_names list must &quot;</span>
<span class="s2">&quot;have the same length as .consumable_tags.&quot;</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">consumable_names</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">consumable_tags</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">tool_names</span><span class="p">:</span>
<span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">tool_names</span><span class="p">)</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">tool_tags</span><span class="p">),</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;Crafting </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="si">}</span><span class="s2">.tool_names list must &quot;</span>
<span class="s2">&quot;have the same length as .tool_tags.&quot;</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">tool_names</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">tool_tags</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">output_names</span><span class="p">:</span>
<span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">consumable_names</span><span class="p">)</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">consumable_tags</span><span class="p">),</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;Crafting </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="si">}</span><span class="s2">.output_names list must &quot;</span>
<span class="s2">&quot;have the same length as .output_prototypes.&quot;</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">output_names</span> <span class="o">=</span> <span class="p">[</span>
<span class="n">prot</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">,</span> <span class="n">prot</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;typeclass&quot;</span><span class="p">,</span> <span class="s2">&quot;unnamed&quot;</span><span class="p">))</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">prot</span><span class="p">,</span> <span class="nb">dict</span><span class="p">)</span>
<span class="k">else</span> <span class="nb">str</span><span class="p">(</span><span class="n">prot</span><span class="p">)</span>
<span class="k">for</span> <span class="n">prot</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">output_prototypes</span>
<span class="p">]</span>
<span class="k">assert</span> <span class="nb">isinstance</span><span class="p">(</span>
<span class="bp">self</span><span class="o">.</span><span class="n">output_prototypes</span><span class="p">,</span> <span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">)</span>
<span class="p">),</span> <span class="s2">&quot;Crafting </span><span class="si">{self.__class__}</span><span class="s2">.output_prototypes must be a list or tuple.&quot;</span>
<span class="c1"># don&#39;t allow reuse if we have consumables. If only tools we can reuse</span>
<span class="c1"># over and over since nothing changes.</span>
<span class="bp">self</span><span class="o">.</span><span class="n">allow_reuse</span> <span class="o">=</span> <span class="ow">not</span> <span class="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">consumable_tags</span><span class="p">)</span></div>
<span class="k">def</span> <span class="nf">_format_message</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="n">missing</span> <span class="o">=</span> <span class="n">iter_to_str</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;missing&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">))</span>
<span class="n">excess</span> <span class="o">=</span> <span class="n">iter_to_str</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;excess&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">))</span>
<span class="n">involved_tools</span> <span class="o">=</span> <span class="n">iter_to_str</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;tools&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">))</span>
<span class="n">involved_cons</span> <span class="o">=</span> <span class="n">iter_to_str</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;consumables&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">))</span>
<span class="c1"># build template context</span>
<span class="n">mapping</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;missing&quot;</span><span class="p">:</span> <span class="n">missing</span><span class="p">,</span> <span class="s2">&quot;excess&quot;</span><span class="p">:</span> <span class="n">excess</span><span class="p">}</span>
<span class="n">mapping</span><span class="o">.</span><span class="n">update</span><span class="p">(</span>
<span class="p">{</span>
<span class="sa">f</span><span class="s2">&quot;i</span><span class="si">{</span><span class="n">ind</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">consumable_names</span><span class="p">[</span><span class="n">ind</span><span class="p">]</span>
<span class="k">for</span> <span class="n">ind</span><span class="p">,</span> <span class="n">name</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">consumable_names</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">consumable_tags</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">)</span>
<span class="n">mapping</span><span class="o">.</span><span class="n">update</span><span class="p">(</span>
<span class="p">{</span><span class="sa">f</span><span class="s2">&quot;o</span><span class="si">{</span><span class="n">ind</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">output_names</span><span class="p">[</span><span class="n">ind</span><span class="p">]</span> <span class="k">for</span> <span class="n">ind</span><span class="p">,</span> <span class="n">name</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">output_names</span><span class="p">)}</span>
<span class="p">)</span>
<span class="n">mapping</span><span class="p">[</span><span class="s2">&quot;tools&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">involved_tools</span>
<span class="n">mapping</span><span class="p">[</span><span class="s2">&quot;consumables&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">involved_cons</span>
<span class="n">mapping</span><span class="p">[</span><span class="s2">&quot;inputs&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">iter_to_str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">consumable_names</span><span class="p">)</span>
<span class="n">mapping</span><span class="p">[</span><span class="s2">&quot;outputs&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">iter_to_str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">output_names</span><span class="p">)</span>
<span class="c1"># populate template and return</span>
<span class="k">return</span> <span class="n">message</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="o">**</span><span class="n">mapping</span><span class="p">)</span>
<div class="viewcode-block" id="CraftingRecipe.seed"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipe.seed">[docs]</a> <span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">seed</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">tool_kwargs</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">consumable_kwargs</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> This is a helper class-method for easy testing and application of this</span>
<span class="sd"> recipe. When called, it will create simple dummy ingredients with names</span>
<span class="sd"> and tags needed by this recipe.</span>
<span class="sd"> Args:</span>
<span class="sd"> consumable_kwargs (dict, optional): This will be passed as</span>
<span class="sd"> `**consumable_kwargs` into the `create_object` call for each consumable.</span>
<span class="sd"> If not given, matching `consumable_name` or `consumable_tag`</span>
<span class="sd"> will be used for key.</span>
<span class="sd"> tool_kwargs (dict, optional): Will be passed as `**tool_kwargs` into the `create_object`</span>
<span class="sd"> call for each tool. If not given, the matching</span>
<span class="sd"> `tool_name` or `tool_tag` will be used for key.</span>
<span class="sd"> Returns:</span>
<span class="sd"> tuple: A tuple `(tools, consumables)` with newly created dummy</span>
<span class="sd"> objects matching the recipe ingredient list.</span>
<span class="sd"> Example:</span>
<span class="sd"> ::</span>
<span class="sd"> tools, consumables = SwordRecipe.seed()</span>
<span class="sd"> recipe = SwordRecipe(caller, *(tools + consumables))</span>
<span class="sd"> result = recipe.craft()</span>
<span class="sd"> Notes:</span>
<span class="sd"> If `key` is given in `consumable/tool_kwargs` then _every_ created item</span>
<span class="sd"> of each type will have the same key.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">tool_kwargs</span><span class="p">:</span>
<span class="n">tool_kwargs</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">consumable_kwargs</span><span class="p">:</span>
<span class="n">consumable_kwargs</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">tool_key</span> <span class="o">=</span> <span class="n">tool_kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="n">cons_key</span> <span class="o">=</span> <span class="n">consumable_kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">&quot;key&quot;</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="n">tool_tags</span> <span class="o">=</span> <span class="n">tool_kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">&quot;tags&quot;</span><span class="p">,</span> <span class="p">[])</span>
<span class="n">cons_tags</span> <span class="o">=</span> <span class="n">consumable_kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">&quot;tags&quot;</span><span class="p">,</span> <span class="p">[])</span>
<span class="n">tools</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">itag</span><span class="p">,</span> <span class="n">tag</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="bp">cls</span><span class="o">.</span><span class="n">tool_tags</span><span class="p">):</span>
<span class="n">tools</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
<span class="n">create_object</span><span class="p">(</span>
<span class="n">key</span><span class="o">=</span><span class="n">tool_key</span> <span class="ow">or</span> <span class="p">(</span><span class="bp">cls</span><span class="o">.</span><span class="n">tool_names</span><span class="p">[</span><span class="n">itag</span><span class="p">]</span> <span class="k">if</span> <span class="bp">cls</span><span class="o">.</span><span class="n">tool_names</span> <span class="k">else</span> <span class="n">tag</span><span class="o">.</span><span class="n">capitalize</span><span class="p">()),</span>
<span class="n">tags</span><span class="o">=</span><span class="p">[(</span><span class="n">tag</span><span class="p">,</span> <span class="bp">cls</span><span class="o">.</span><span class="n">tool_tag_category</span><span class="p">),</span> <span class="o">*</span><span class="n">tool_tags</span><span class="p">],</span>
<span class="o">**</span><span class="n">tool_kwargs</span><span class="p">,</span>
<span class="p">)</span>
<span class="p">)</span>
<span class="n">consumables</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">itag</span><span class="p">,</span> <span class="n">tag</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="bp">cls</span><span class="o">.</span><span class="n">consumable_tags</span><span class="p">):</span>
<span class="n">consumables</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
<span class="n">create_object</span><span class="p">(</span>
<span class="n">key</span><span class="o">=</span><span class="n">cons_key</span>
<span class="ow">or</span> <span class="p">(</span><span class="bp">cls</span><span class="o">.</span><span class="n">consumable_names</span><span class="p">[</span><span class="n">itag</span><span class="p">]</span> <span class="k">if</span> <span class="bp">cls</span><span class="o">.</span><span class="n">consumable_names</span> <span class="k">else</span> <span class="n">tag</span><span class="o">.</span><span class="n">capitalize</span><span class="p">()),</span>
<span class="n">tags</span><span class="o">=</span><span class="p">[(</span><span class="n">tag</span><span class="p">,</span> <span class="bp">cls</span><span class="o">.</span><span class="n">consumable_tag_category</span><span class="p">),</span> <span class="o">*</span><span class="n">cons_tags</span><span class="p">],</span>
<span class="o">**</span><span class="n">consumable_kwargs</span><span class="p">,</span>
<span class="p">)</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">tools</span><span class="p">,</span> <span class="n">consumables</span></div>
<div class="viewcode-block" id="CraftingRecipe.pre_craft"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipe.pre_craft">[docs]</a> <span class="k">def</span> <span class="nf">pre_craft</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Do pre-craft checks, including input validation.</span>
<span class="sd"> Check so the given inputs are what is needed. This operates on</span>
<span class="sd"> `self.inputs` which is set to the inputs added to the class</span>
<span class="sd"> constructor. Validated data is stored as lists on `.validated_tools`</span>
<span class="sd"> and `.validated_consumables` respectively.</span>
<span class="sd"> Args:</span>
<span class="sd"> **kwargs: Any optional extra kwargs passed during initialization of</span>
<span class="sd"> the recipe class.</span>
<span class="sd"> Raises:</span>
<span class="sd"> CraftingValidationError: If validation fails. At this point the crafter</span>
<span class="sd"> is expected to have been informed of the problem already.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">_check_completeness</span><span class="p">(</span>
<span class="n">tagmap</span><span class="p">,</span>
<span class="n">taglist</span><span class="p">,</span>
<span class="n">namelist</span><span class="p">,</span>
<span class="n">exact_match</span><span class="p">,</span>
<span class="n">exact_order</span><span class="p">,</span>
<span class="n">error_missing_message</span><span class="p">,</span>
<span class="n">error_order_message</span><span class="p">,</span>
<span class="n">error_excess_message</span><span class="p">,</span>
<span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Compare tagmap (inputs) to taglist (required)&quot;&quot;&quot;</span>
<span class="n">valids</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">itag</span><span class="p">,</span> <span class="n">tagkey</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">taglist</span><span class="p">):</span>
<span class="n">found_obj</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">for</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtags</span> <span class="ow">in</span> <span class="n">tagmap</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="n">tagkey</span> <span class="ow">in</span> <span class="n">objtags</span><span class="p">:</span>
<span class="n">found_obj</span> <span class="o">=</span> <span class="n">obj</span>
<span class="k">break</span>
<span class="k">if</span> <span class="n">exact_order</span><span class="p">:</span>
<span class="c1"># if we get here order is wrong</span>
<span class="n">err</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_format_message</span><span class="p">(</span>
<span class="n">error_order_message</span><span class="p">,</span> <span class="n">missing</span><span class="o">=</span><span class="n">obj</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">looker</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">crafter</span><span class="p">)</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">CraftingValidationError</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
<span class="c1"># since we pop from the mapping, it gets ever shorter</span>
<span class="n">match</span> <span class="o">=</span> <span class="n">tagmap</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="n">found_obj</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="k">if</span> <span class="n">match</span><span class="p">:</span>
<span class="n">valids</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">found_obj</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">exact_match</span><span class="p">:</span>
<span class="n">err</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_format_message</span><span class="p">(</span>
<span class="n">error_missing_message</span><span class="p">,</span>
<span class="n">missing</span><span class="o">=</span><span class="n">namelist</span><span class="p">[</span><span class="n">itag</span><span class="p">]</span> <span class="k">if</span> <span class="n">namelist</span> <span class="k">else</span> <span class="n">tagkey</span><span class="o">.</span><span class="n">capitalize</span><span class="p">(),</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">CraftingValidationError</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
<span class="k">if</span> <span class="n">exact_match</span> <span class="ow">and</span> <span class="n">tagmap</span><span class="p">:</span>
<span class="c1"># something is left in tagmap, that means it was never popped and</span>
<span class="c1"># thus this is not an exact match</span>
<span class="n">err</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_format_message</span><span class="p">(</span>
<span class="n">error_excess_message</span><span class="p">,</span>
<span class="n">excess</span><span class="o">=</span><span class="p">[</span><span class="n">obj</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">looker</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">crafter</span><span class="p">)</span> <span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">tagmap</span><span class="p">],</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">CraftingValidationError</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
<span class="k">return</span> <span class="n">valids</span>
<span class="c1"># get tools and consumables from self.inputs</span>
<span class="n">tool_map</span> <span class="o">=</span> <span class="p">{</span>
<span class="n">obj</span><span class="p">:</span> <span class="n">obj</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">category</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">tool_tag_category</span><span class="p">,</span> <span class="n">return_list</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">inputs</span>
<span class="k">if</span> <span class="n">obj</span>
<span class="ow">and</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="s2">&quot;tags&quot;</span><span class="p">)</span>
<span class="ow">and</span> <span class="n">inherits_from</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="s2">&quot;evennia.objects.models.ObjectDB&quot;</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">tool_map</span> <span class="o">=</span> <span class="p">{</span><span class="n">obj</span><span class="p">:</span> <span class="n">tags</span> <span class="k">for</span> <span class="n">obj</span><span class="p">,</span> <span class="n">tags</span> <span class="ow">in</span> <span class="n">tool_map</span><span class="o">.</span><span class="n">items</span><span class="p">()</span> <span class="k">if</span> <span class="n">tags</span><span class="p">}</span>
<span class="n">consumable_map</span> <span class="o">=</span> <span class="p">{</span>
<span class="n">obj</span><span class="p">:</span> <span class="n">obj</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">category</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">consumable_tag_category</span><span class="p">,</span> <span class="n">return_list</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">inputs</span>
<span class="k">if</span> <span class="n">obj</span>
<span class="ow">and</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="s2">&quot;tags&quot;</span><span class="p">)</span>
<span class="ow">and</span> <span class="n">obj</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">tool_map</span>
<span class="ow">and</span> <span class="n">inherits_from</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="s2">&quot;evennia.objects.models.ObjectDB&quot;</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">consumable_map</span> <span class="o">=</span> <span class="p">{</span><span class="n">obj</span><span class="p">:</span> <span class="n">tags</span> <span class="k">for</span> <span class="n">obj</span><span class="p">,</span> <span class="n">tags</span> <span class="ow">in</span> <span class="n">consumable_map</span><span class="o">.</span><span class="n">items</span><span class="p">()</span> <span class="k">if</span> <span class="n">tags</span><span class="p">}</span>
<span class="c1"># we set these so they are available for error management at all times,</span>
<span class="c1"># they will be updated with the actual values at the end</span>
<span class="bp">self</span><span class="o">.</span><span class="n">validated_tools</span> <span class="o">=</span> <span class="p">[</span><span class="n">obj</span> <span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">tool_map</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">validated_consumables</span> <span class="o">=</span> <span class="p">[</span><span class="n">obj</span> <span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">consumable_map</span><span class="p">]</span>
<span class="n">tools</span> <span class="o">=</span> <span class="n">_check_completeness</span><span class="p">(</span>
<span class="n">tool_map</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">tool_tags</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">tool_names</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exact_tools</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exact_tool_order</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">error_tool_missing_message</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">error_tool_order_message</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">error_tool_excess_message</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">consumables</span> <span class="o">=</span> <span class="n">_check_completeness</span><span class="p">(</span>
<span class="n">consumable_map</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">consumable_tags</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">consumable_names</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exact_consumables</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">exact_consumable_order</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">error_consumable_missing_message</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">error_consumable_order_message</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">error_consumable_excess_message</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># regardless of flags, the tools/consumable lists much contain exactly</span>
<span class="c1"># all the recipe needs now.</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">tools</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">tool_tags</span><span class="p">):</span>
<span class="k">raise</span> <span class="n">CraftingValidationError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;Tools </span><span class="si">{</span><span class="n">tools</span><span class="si">}</span><span class="s2">&#39;s tags do not match expected tags </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">tool_tags</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">consumables</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">consumable_tags</span><span class="p">):</span>
<span class="k">raise</span> <span class="n">CraftingValidationError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;Consumables </span><span class="si">{</span><span class="n">consumables</span><span class="si">}</span><span class="s2">&#39;s tags do not match &quot;</span>
<span class="sa">f</span><span class="s2">&quot;expected tags </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">consumable_tags</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">validated_tools</span> <span class="o">=</span> <span class="n">tools</span>
<span class="bp">self</span><span class="o">.</span><span class="n">validated_consumables</span> <span class="o">=</span> <span class="n">consumables</span></div>
<div class="viewcode-block" id="CraftingRecipe.do_craft"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipe.do_craft">[docs]</a> <span class="k">def</span> <span class="nf">do_craft</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Hook to override. This will not be called if validation in `pre_craft`</span>
<span class="sd"> fails.</span>
<span class="sd"> This performs the actual crafting. At this point the inputs are</span>
<span class="sd"> expected to have been verified already.</span>
<span class="sd"> Returns:</span>
<span class="sd"> list: A list of spawned objects created from the inputs, or None</span>
<span class="sd"> on a failure.</span>
<span class="sd"> Notes:</span>
<span class="sd"> This method should use `self.msg` to inform the user about the</span>
<span class="sd"> specific reason of failure immediately.</span>
<span class="sd"> We may want to analyze the tools in some way here to affect the</span>
<span class="sd"> crafting process.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">return</span> <span class="n">spawn</span><span class="p">(</span><span class="o">*</span><span class="bp">self</span><span class="o">.</span><span class="n">output_prototypes</span><span class="p">)</span></div>
<div class="viewcode-block" id="CraftingRecipe.post_craft"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipe.post_craft">[docs]</a> <span class="k">def</span> <span class="nf">post_craft</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">craft_result</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Hook to override.</span>
<span class="sd"> This is called just after crafting has finished. A common use of</span>
<span class="sd"> this method is to delete the inputs.</span>
<span class="sd"> Args:</span>
<span class="sd"> craft_result (list): The crafted result, provided by `self.do_craft`.</span>
<span class="sd"> **kwargs (any): Passed from `self.craft`.</span>
<span class="sd"> Returns:</span>
<span class="sd"> list: The return(s) of the craft, possibly modified in this method.</span>
<span class="sd"> Notes:</span>
<span class="sd"> This is _always_ called, also if validation in `pre_craft` fails</span>
<span class="sd"> (`craft_result` will then be `None`).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="n">craft_result</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_message</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">success_message</span><span class="p">))</span>
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">failure_message</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_format_message</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">failure_message</span><span class="p">))</span>
<span class="k">if</span> <span class="n">craft_result</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">consume_on_fail</span><span class="p">:</span>
<span class="c1"># consume the inputs</span>
<span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">validated_consumables</span><span class="p">:</span>
<span class="n">obj</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>
<span class="k">return</span> <span class="n">craft_result</span></div></div>
<span class="c1"># access function</span>
<div class="viewcode-block" id="craft"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.craft">[docs]</a><span class="k">def</span> <span class="nf">craft</span><span class="p">(</span><span class="n">crafter</span><span class="p">,</span> <span class="n">recipe_name</span><span class="p">,</span> <span class="o">*</span><span class="n">inputs</span><span class="p">,</span> <span class="n">raise_exception</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Access function. Craft a given recipe from a source recipe module. A</span>
<span class="sd"> recipe module is a Python module containing recipe classes. Note that this</span>
<span class="sd"> requires `settings.CRAFT_RECIPE_MODULES` to be added to a list of one or</span>
<span class="sd"> more python-paths to modules holding Recipe-classes.</span>
<span class="sd"> Args:</span>
<span class="sd"> crafter (Object): The one doing the crafting.</span>
<span class="sd"> recipe_name (str): The `CraftRecipe.name` to use. This uses fuzzy-matching</span>
<span class="sd"> if the result is unique.</span>
<span class="sd"> *inputs: Suitable ingredients and/or tools (Objects) to use in the crafting.</span>
<span class="sd"> raise_exception (bool, optional): If crafting failed for whatever</span>
<span class="sd"> reason, raise `CraftingError`. The user will still be informed by the</span>
<span class="sd"> recipe.</span>
<span class="sd"> **kwargs: Optional kwargs to pass into the recipe (will passed into</span>
<span class="sd"> recipe.craft).</span>
<span class="sd"> Returns:</span>
<span class="sd"> list: Crafted objects, if any.</span>
<span class="sd"> Raises:</span>
<span class="sd"> CraftingError: If `raise_exception` is True and crafting failed to</span>
<span class="sd"> produce an output. KeyError: If `recipe_name` failed to find a</span>
<span class="sd"> matching recipe class (or the hit was not precise enough.)</span>
<span class="sd"> Notes:</span>
<span class="sd"> If no recipe_module is given, will look for a list `settings.CRAFT_RECIPE_MODULES` and</span>
<span class="sd"> lastly fall back to the example module `&quot;evennia.contrib.&quot;`</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># delayed loading/caching of recipes</span>
<span class="n">_load_recipes</span><span class="p">()</span>
<span class="n">RecipeClass</span> <span class="o">=</span> <span class="n">_RECIPE_CLASSES</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">recipe_name</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">RecipeClass</span><span class="p">:</span>
<span class="c1"># try a startswith fuzzy match</span>
<span class="n">matches</span> <span class="o">=</span> <span class="p">[</span><span class="n">key</span> <span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">_RECIPE_CLASSES</span> <span class="k">if</span> <span class="n">key</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">recipe_name</span><span class="p">)]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">matches</span><span class="p">:</span>
<span class="c1"># try in-match</span>
<span class="n">matches</span> <span class="o">=</span> <span class="p">[</span><span class="n">key</span> <span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">_RECIPE_CLASSES</span> <span class="k">if</span> <span class="n">recipe_name</span> <span class="ow">in</span> <span class="n">key</span><span class="p">]</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">matches</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">RecipeClass</span> <span class="o">=</span> <span class="n">matches</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">RecipeClass</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">KeyError</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;No recipe in settings.CRAFT_RECIPE_MODULES has a name matching </span><span class="si">{</span><span class="n">recipe_name</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="n">recipe</span> <span class="o">=</span> <span class="n">RecipeClass</span><span class="p">(</span><span class="n">crafter</span><span class="p">,</span> <span class="o">*</span><span class="n">inputs</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">return</span> <span class="n">recipe</span><span class="o">.</span><span class="n">craft</span><span class="p">(</span><span class="n">raise_exception</span><span class="o">=</span><span class="n">raise_exception</span><span class="p">)</span></div>
<span class="c1"># craft command/cmdset</span>
<div class="viewcode-block" id="CraftingCmdSet"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingCmdSet">[docs]</a><span class="k">class</span> <span class="nc">CraftingCmdSet</span><span class="p">(</span><span class="n">CmdSet</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Store crafting command.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;Crafting cmdset&quot;</span>
<div class="viewcode-block" id="CraftingCmdSet.at_cmdset_creation"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingCmdSet.at_cmdset_creation">[docs]</a> <span class="k">def</span> <span class="nf">at_cmdset_creation</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">add</span><span class="p">(</span><span class="n">CmdCraft</span><span class="p">())</span></div></div>
<div class="viewcode-block" id="CmdCraft"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CmdCraft">[docs]</a><span class="k">class</span> <span class="nc">CmdCraft</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Craft an item using ingredients and tools</span>
<span class="sd"> Usage:</span>
<span class="sd"> craft &lt;recipe&gt; [from &lt;ingredient&gt;,...] [using &lt;tool&gt;, ...]</span>
<span class="sd"> Examples:</span>
<span class="sd"> craft snowball from snow</span>
<span class="sd"> craft puppet from piece of wood using knife</span>
<span class="sd"> craft bread from flour, butter, water, yeast using owen, bowl, roller</span>
<span class="sd"> craft fireball using wand, spellbook</span>
<span class="sd"> Notes:</span>
<span class="sd"> Ingredients must be in the crafter&#39;s inventory. Tools can also be</span>
<span class="sd"> things in the current location, like a furnace, windmill or anvil.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;craft&quot;</span>
<span class="n">locks</span> <span class="o">=</span> <span class="s2">&quot;cmd:all()&quot;</span>
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">&quot;General&quot;</span>
<span class="n">arg_regex</span> <span class="o">=</span> <span class="sa">r</span><span class="s2">&quot;\s|$&quot;</span>
<div class="viewcode-block" id="CmdCraft.parse"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CmdCraft.parse">[docs]</a> <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Handle parsing of:</span>
<span class="sd"> ::</span>
<span class="sd"> &lt;recipe&gt; [FROM &lt;ingredients&gt;] [USING &lt;tools&gt;]</span>
<span class="sd"> Examples:</span>
<span class="sd"> ::</span>
<span class="sd"> craft snowball from snow</span>
<span class="sd"> craft puppet from piece of wood using knife</span>
<span class="sd"> craft bread from flour, butter, water, yeast using owen, bowl, roller</span>
<span class="sd"> craft fireball using wand, spellbook</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">args</span> <span class="o">=</span> <span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
<span class="n">recipe</span><span class="p">,</span> <span class="n">ingredients</span><span class="p">,</span> <span class="n">tools</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span>
<span class="k">if</span> <span class="s2">&quot;from&quot;</span> <span class="ow">in</span> <span class="n">args</span><span class="p">:</span>
<span class="n">recipe</span><span class="p">,</span> <span class="o">*</span><span class="n">rest</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot; from &quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">rest</span> <span class="o">=</span> <span class="n">rest</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">rest</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span>
<span class="n">ingredients</span><span class="p">,</span> <span class="o">*</span><span class="n">tools</span> <span class="o">=</span> <span class="n">rest</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot; using &quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">elif</span> <span class="s2">&quot;using&quot;</span> <span class="ow">in</span> <span class="n">args</span><span class="p">:</span>
<span class="n">recipe</span><span class="p">,</span> <span class="o">*</span><span class="n">tools</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot; using &quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">tools</span> <span class="o">=</span> <span class="n">tools</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">tools</span> <span class="k">else</span> <span class="s2">&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">recipe</span> <span class="o">=</span> <span class="n">recipe</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">ingredients</span> <span class="o">=</span> <span class="p">[</span><span class="n">ingr</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">ingr</span> <span class="ow">in</span> <span class="n">ingredients</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;,&quot;</span><span class="p">)]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">tools</span> <span class="o">=</span> <span class="p">[</span><span class="n">tool</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">tool</span> <span class="ow">in</span> <span class="n">tools</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;,&quot;</span><span class="p">)]</span></div>
<div class="viewcode-block" id="CmdCraft.func"><a class="viewcode-back" href="../../../../api/evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CmdCraft.func">[docs]</a> <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="sd">&quot;&quot;&quot;</span>
<span class="sd"> Perform crafting.</span>
<span class="sd"> Will check the `craft` locktype. If a consumable/ingredient does not pass</span>
<span class="sd"> this check, we will check for the &#39;crafting_consumable_err_msg&#39;</span>
<span class="sd"> Attribute, otherwise will use a default. If failing on a tool, will use</span>
<span class="sd"> the `crafting_tool_err_msg` if available.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">caller</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span> <span class="ow">or</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">recipe</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;Usage: craft &lt;recipe&gt; from &lt;ingredient&gt;, ... [using &lt;tool&gt;,...]&quot;</span><span class="p">)</span>
<span class="k">return</span>
<span class="n">ingredients</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">ingr_key</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">ingredients</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">ingr_key</span><span class="p">:</span>
<span class="k">continue</span>
<span class="n">obj</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="n">ingr_key</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span>
<span class="c1"># since ingredients are consumed we need extra check so we don&#39;t</span>
<span class="c1"># try to include characters or accounts etc.</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">obj</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">if</span> <span class="p">(</span>
<span class="ow">not</span> <span class="n">inherits_from</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="s2">&quot;evennia.objects.models.ObjectDB&quot;</span><span class="p">)</span>
<span class="ow">or</span> <span class="n">obj</span><span class="o">.</span><span class="n">sessions</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
<span class="ow">or</span> <span class="ow">not</span> <span class="n">obj</span><span class="o">.</span><span class="n">access</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="s2">&quot;craft&quot;</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="p">):</span>
<span class="c1"># We don&#39;t allow to include puppeted objects nor those with the</span>
<span class="c1"># &#39;negative&#39; permission &#39;nocraft&#39;.</span>
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
<span class="n">obj</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">get</span><span class="p">(</span>
<span class="s2">&quot;crafting_consumable_err_msg&quot;</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">obj</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">looker</span><span class="o">=</span><span class="n">caller</span><span class="p">)</span><span class="si">}</span><span class="s2"> can&#39;t be used for this.&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="p">)</span>
<span class="k">return</span>
<span class="n">ingredients</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="n">tools</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">tool_key</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">tools</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">tool_key</span><span class="p">:</span>
<span class="k">continue</span>
<span class="c1"># tools are not consumed, can also exist in the current room</span>
<span class="n">obj</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="n">tool_key</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">obj</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">obj</span><span class="o">.</span><span class="n">access</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="s2">&quot;craft&quot;</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">True</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="n">obj</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">get</span><span class="p">(</span>
<span class="s2">&quot;crafting_tool_err_msg&quot;</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">obj</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">looker</span><span class="o">=</span><span class="n">caller</span><span class="p">)</span><span class="si">}</span><span class="s2"> can&#39;t be used for this.&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="p">)</span>
<span class="k">return</span>
<span class="n">tools</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="c1"># perform craft and make sure result is in inventory</span>
<span class="c1"># (the recipe handles all returns to caller)</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">craft</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">recipe</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="n">tools</span> <span class="o">+</span> <span class="n">ingredients</span><span class="p">))</span>
<span class="k">if</span> <span class="n">result</span><span class="p">:</span>
<span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">result</span><span class="p">:</span>
<span class="n">obj</span><span class="o">.</span><span class="n">location</span> <span class="o">=</span> <span class="n">caller</span></div></div>
</pre></div>
<div class="clearer"></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="../../../../index.html">
<img class="logo" src="../../../../_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li><a href="http://webchat.freenode.net/?channels=evennia&uio=MT1mYWxzZSY5PXRydWUmMTE9MTk1JjEyPXRydWUbb">IRC</a> -
<a href="https://discord.gg/NecFePw">Discord</a> -
<a href="https://groups.google.com/forum/#%21forum/evennia">Forums</a>
</li>
<li><a href="http://evennia.blogspot.com/">Evennia Dev blog</a> </li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="crafting.html">1.0-dev (develop 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="clearer"></div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../../../../genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="../../../../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="../../../../index.html">Evennia 1.0-dev</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="../../../index.html" >Module code</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../../../evennia.html" >evennia</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">evennia.contrib.crafting.crafting</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>