evennia/docs/1.0-dev/Howtos/NPC-shop-Tutorial.html
Evennia docbuilder action d339a9deb3 Updated HTML docs.
2022-11-15 20:00:58 +00:00

464 lines
No EOL
42 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>NPC shop Tutorial &#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" />
<link rel="next" title="Building a mech tutorial" href="Building-a-mech-tutorial.html" />
<link rel="prev" title="Tutorial Aggressive NPCs" href="Tutorial-Aggressive-NPCs.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Building-a-mech-tutorial.html" title="Building a mech tutorial"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Tutorial-Aggressive-NPCs.html" title="Tutorial Aggressive NPCs"
accesskey="P">previous</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="Howtos-Overview.html" accesskey="U">Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">NPC shop Tutorial</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="document">
<div class="documentwrapper">
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="../index.html">
<img class="logo" src="../_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<h3><a href="../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">NPC shop Tutorial</a><ul>
<li><a class="reference internal" href="#the-shop-menu">The shop menu</a><ul>
<li><a class="reference internal" href="#designing-the-menu">Designing the menu</a></li>
<li><a class="reference internal" href="#coding-the-menu">Coding the menu</a></li>
<li><a class="reference internal" href="#the-command-to-start-the-menu">The command to start the menu</a></li>
</ul>
</li>
<li><a class="reference internal" href="#building-the-shop">Building the shop</a></li>
<li><a class="reference internal" href="#the-shop-is-open-for-business">The shop is open for business!</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Tutorial-Aggressive-NPCs.html"
title="previous chapter">Tutorial Aggressive NPCs</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Building-a-mech-tutorial.html"
title="next chapter">Building a mech tutorial</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../_sources/Howtos/NPC-shop-Tutorial.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="NPC-shop-Tutorial.html">1.0-dev (develop branch)</a></li>
<ul>
<li><a href="../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="npc-shop-tutorial">
<h1>NPC shop Tutorial<a class="headerlink" href="#npc-shop-tutorial" title="Permalink to this headline"></a></h1>
<p>This tutorial will describe how to make an NPC-run shop. We will make use of the <a class="reference internal" href="../Components/EvMenu.html"><span class="doc std std-doc">EvMenu</span></a>
system to present shoppers with a menu where they can buy things from the stores stock.</p>
<p>Our shop extends over two rooms - a “front” room open to the shops customers and a locked “store
room” holding the wares the shop should be able to sell. We aim for the following features:</p>
<ul class="simple">
<li><p>The front room should have an Attribute <code class="docutils literal notranslate"><span class="pre">storeroom</span></code> that points to the store room.</p></li>
<li><p>Inside the front room, the customer should have a command <code class="docutils literal notranslate"><span class="pre">buy</span></code> or <code class="docutils literal notranslate"><span class="pre">browse</span></code>. This will open a
menu listing all items available to buy from the store room.</p></li>
<li><p>A customer should be able to look at individual items before buying.</p></li>
<li><p>We use “gold” as an example currency. To determine cost, the system will look for an Attribute
<code class="docutils literal notranslate"><span class="pre">gold_value</span></code> on the items in the store room. If not found, a fixed base value of 1 will be assumed.
The wealth of the customer should be set as an Attribute <code class="docutils literal notranslate"><span class="pre">gold</span></code> on the Character. If not set, they
have no gold and cant buy anything.</p></li>
<li><p>When the customer makes a purchase, the system will check the <code class="docutils literal notranslate"><span class="pre">gold_value</span></code> of the goods and
compare it to the <code class="docutils literal notranslate"><span class="pre">gold</span></code> Attribute of the customer. If enough gold is available, this will be
deducted and the goods transferred from the store room to the inventory of the customer.</p></li>
<li><p>We will lock the store room so that only people with the right key can get in there.</p></li>
</ul>
<section id="the-shop-menu">
<h2>The shop menu<a class="headerlink" href="#the-shop-menu" title="Permalink to this headline"></a></h2>
<p>We want to show a menu to the customer where they can list, examine and buy items in the store. This
menu should change depending on what is currently for sale. Evennias <em>EvMenu</em> utility will manage
the menu for us. Its a good idea to <a class="reference internal" href="../Components/EvMenu.html"><span class="doc std std-doc">read up on EvMenu</span></a> if you are not familiar with it.</p>
<section id="designing-the-menu">
<h3>Designing the menu<a class="headerlink" href="#designing-the-menu" title="Permalink to this headline"></a></h3>
<p>The shopping menus design is straightforward. First we want the main screen. You get this when you
enter a shop and use the <code class="docutils literal notranslate"><span class="pre">browse</span></code> or <code class="docutils literal notranslate"><span class="pre">buy</span></code> command:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>*** Welcome to ye Old Sword shop! ***
Things for sale (choose 1-3 to inspect, quit to exit):
_________________________________________________________
1. A rusty sword (5 gold)
2. A sword with a leather handle (10 gold)
3. Excalibur (100 gold)
</pre></div>
</div>
<p>There are only three items to buy in this example but the menu should expand to however many items
are needed. When you make a selection you will get a new screen showing the options for that
particular item:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">You</span> <span class="n">inspect</span> <span class="n">A</span> <span class="n">rusty</span> <span class="n">sword</span><span class="p">:</span>
<span class="n">This</span> <span class="ow">is</span> <span class="n">an</span> <span class="n">old</span> <span class="n">weapon</span> <span class="n">maybe</span> <span class="n">once</span> <span class="n">used</span> <span class="n">by</span> <span class="n">soldiers</span> <span class="ow">in</span> <span class="n">some</span>
<span class="n">long</span> <span class="n">forgotten</span> <span class="n">army</span><span class="o">.</span> <span class="n">It</span> <span class="ow">is</span> <span class="n">rusty</span> <span class="ow">and</span> <span class="ow">in</span> <span class="n">bad</span> <span class="n">condition</span><span class="o">.</span>
<span class="n">__________________________________________________________</span>
<span class="mf">1.</span> <span class="n">Buy</span> <span class="n">A</span> <span class="n">rusty</span> <span class="n">sword</span> <span class="p">(</span><span class="mi">5</span> <span class="n">gold</span><span class="p">)</span>
<span class="mf">2.</span> <span class="n">Look</span> <span class="k">for</span> <span class="n">something</span> <span class="k">else</span><span class="o">.</span>
</pre></div>
</div>
<p>Finally, when you buy something, a brief message should pop up:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>You pay 5 gold and purchase A rusty sword!
</pre></div>
</div>
<p>or</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>You cannot afford 5 gold for A rusty sword!
</pre></div>
</div>
<p>After this you should be back to the top level of the shopping menu again and can continue browsing.</p>
</section>
<section id="coding-the-menu">
<h3>Coding the menu<a class="headerlink" href="#coding-the-menu" title="Permalink to this headline"></a></h3>
<p>EvMenu defines the <em>nodes</em> (each menu screen with options) as normal Python functions. Each node
must be able to change on the fly depending on what items are currently for sale. EvMenu will
automatically make the <code class="docutils literal notranslate"><span class="pre">quit</span></code> command available to us so we wont add that manually. For compactness
we will put everything needed for our shop in one module, <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/npcshop.py</span></code>.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/typeclasses/npcshop.py</span>
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">evmenu</span>
<span class="k">def</span> <span class="nf">menunode_shopfront</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
<span class="s2">&quot;This is the top-menu screen.&quot;</span>
<span class="n">shopname</span> <span class="o">=</span> <span class="n">caller</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">key</span>
<span class="n">wares</span> <span class="o">=</span> <span class="n">caller</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">storeroom</span><span class="o">.</span><span class="n">contents</span>
<span class="c1"># Wares includes all items inside the storeroom, including the</span>
<span class="c1"># door! Let&#39;s remove that from our for sale list.</span>
<span class="n">wares</span> <span class="o">=</span> <span class="p">[</span><span class="n">ware</span> <span class="k">for</span> <span class="n">ware</span> <span class="ow">in</span> <span class="n">wares</span> <span class="k">if</span> <span class="n">ware</span><span class="o">.</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">!=</span> <span class="s2">&quot;door&quot;</span><span class="p">]</span>
<span class="n">text</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;*** Welcome to </span><span class="si">{</span><span class="n">shopname</span><span class="si">}</span><span class="s2">! ***</span><span class="se">\n</span><span class="s2">&quot;</span>
<span class="k">if</span> <span class="n">wares</span><span class="p">:</span>
<span class="n">text</span> <span class="o">+=</span> <span class="sa">f</span><span class="s2">&quot; Things for sale (choose 1-</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">wares</span><span class="p">)</span><span class="si">}</span><span class="s2"> to inspect); quit to exit:&quot;</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">text</span> <span class="o">+=</span> <span class="s2">&quot; There is nothing for sale; quit to exit.&quot;</span>
<span class="n">options</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">ware</span> <span class="ow">in</span> <span class="n">wares</span><span class="p">:</span>
<span class="c1"># add an option for every ware in store</span>
<span class="n">gold_val</span> <span class="o">=</span> <span class="n">ware</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">gold_value</span> <span class="ow">or</span> <span class="mi">1</span>
<span class="n">options</span><span class="o">.</span><span class="n">append</span><span class="p">({</span><span class="s2">&quot;desc&quot;</span><span class="p">:</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">ware</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">gold_val</span><span class="si">}</span><span class="s2"> gold)&quot;</span><span class="p">,</span>
<span class="s2">&quot;goto&quot;</span><span class="p">:</span> <span class="s2">&quot;menunode_inspect_and_buy&quot;</span><span class="p">})</span>
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
</pre></div>
</div>
<p>In this code we assume the caller to be <em>inside</em> the shop when accessing the menu. This means we can
access the shop room via <code class="docutils literal notranslate"><span class="pre">caller.location</span></code> and get its <code class="docutils literal notranslate"><span class="pre">key</span></code> to display as the shops name. We also
assume the shop has an Attribute <code class="docutils literal notranslate"><span class="pre">storeroom</span></code> we can use to get to our stock. We loop over our goods
to build up the menus options.</p>
<p>Note that <em>all options point to the same menu node</em> called <code class="docutils literal notranslate"><span class="pre">menunode_inspect_and_buy</span></code>! We cant know
which goods will be available to sale so we rely on this node to modify itself depending on the
circumstances. Lets create it now.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># further down in mygame/typeclasses/npcshop.py</span>
<span class="k">def</span> <span class="nf">menunode_inspect_and_buy</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">raw_string</span><span class="p">):</span>
<span class="s2">&quot;Sets up the buy menu screen.&quot;</span>
<span class="n">wares</span> <span class="o">=</span> <span class="n">caller</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">storeroom</span><span class="o">.</span><span class="n">contents</span>
<span class="c1"># Don&#39;t forget, we will need to remove that pesky door again!</span>
<span class="n">wares</span> <span class="o">=</span> <span class="p">[</span><span class="n">ware</span> <span class="k">for</span> <span class="n">ware</span> <span class="ow">in</span> <span class="n">wares</span> <span class="k">if</span> <span class="n">ware</span><span class="o">.</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">!=</span> <span class="s2">&quot;door&quot;</span><span class="p">]</span>
<span class="n">iware</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">raw_string</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
<span class="n">ware</span> <span class="o">=</span> <span class="n">wares</span><span class="p">[</span><span class="n">iware</span><span class="p">]</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">ware</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">gold_value</span> <span class="ow">or</span> <span class="mi">1</span>
<span class="n">wealth</span> <span class="o">=</span> <span class="n">caller</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">gold</span> <span class="ow">or</span> <span class="mi">0</span>
<span class="n">text</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;You inspect </span><span class="si">{</span><span class="n">ware</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">:</span><span class="se">\n\n</span><span class="si">{</span><span class="n">ware</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">desc</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="k">def</span> <span class="nf">buy_ware_result</span><span class="p">(</span><span class="n">caller</span><span class="p">):</span>
<span class="s2">&quot;This will be executed first when choosing to buy.&quot;</span>
<span class="k">if</span> <span class="n">wealth</span> <span class="o">&gt;=</span> <span class="n">value</span><span class="p">:</span>
<span class="n">rtext</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;You pay </span><span class="si">{</span><span class="n">value</span><span class="si">}</span><span class="s2"> gold and purchase </span><span class="si">{</span><span class="n">ware</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">!&quot;</span>
<span class="n">caller</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">gold</span> <span class="o">-=</span> <span class="n">value</span>
<span class="n">ware</span><span class="o">.</span><span class="n">move_to</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">quiet</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">move_type</span><span class="o">=</span><span class="s2">&quot;buy&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">rtext</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;You cannot afford </span><span class="si">{</span><span class="n">value</span><span class="si">}</span><span class="s2"> gold for </span><span class="si">{</span><span class="n">ware</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">!&quot;</span>
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="n">rtext</span><span class="p">)</span>
<span class="n">gold_val</span> <span class="o">=</span> <span class="n">ware</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">gold_value</span> <span class="ow">or</span> <span class="mi">1</span>
<span class="n">options</span> <span class="o">=</span> <span class="p">({</span>
<span class="s2">&quot;desc&quot;</span><span class="p">:</span> <span class="sa">f</span><span class="s2">&quot;Buy </span><span class="si">{</span><span class="n">ware</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> for </span><span class="si">{</span><span class="n">gold_val</span><span class="si">}</span><span class="s2"> gold&quot;</span><span class="p">,</span>
<span class="s2">&quot;goto&quot;</span><span class="p">:</span> <span class="s2">&quot;menunode_shopfront&quot;</span><span class="p">,</span>
<span class="s2">&quot;exec&quot;</span><span class="p">:</span> <span class="n">buy_ware_result</span><span class="p">,</span>
<span class="p">},</span> <span class="p">{</span>
<span class="s2">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Look for something else&quot;</span><span class="p">,</span>
<span class="s2">&quot;goto&quot;</span><span class="p">:</span> <span class="s2">&quot;menunode_shopfront&quot;</span><span class="p">,</span>
<span class="p">})</span>
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="n">options</span>
</pre></div>
</div>
<p>In this menu node we make use of the <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> argument to the node. This is the text the menu
user entered on the <em>previous</em> node to get here. Since we only allow numbered options in our menu,
<code class="docutils literal notranslate"><span class="pre">raw_input</span></code> must be an number for the player to get to this point. So we convert it to an integer
index (menu lists start from 1, whereas Python indices always starts at 0, so we need to subtract
1). We then use the index to get the corresponding item from storage.</p>
<p>We just show the customer the <code class="docutils literal notranslate"><span class="pre">desc</span></code> of the item. In a more elaborate setup you might want to show
things like weapon damage and special stats here as well.</p>
<p>When the user choose the “buy” option, EvMenu will execute the <code class="docutils literal notranslate"><span class="pre">exec</span></code> instruction <em>before</em> we go
back to the top node (the <code class="docutils literal notranslate"><span class="pre">goto</span></code> instruction). For this we make a little inline function
<code class="docutils literal notranslate"><span class="pre">buy_ware_result</span></code>. EvMenu will call the function given to <code class="docutils literal notranslate"><span class="pre">exec</span></code> like any menu node but it does not
need to return anything. In <code class="docutils literal notranslate"><span class="pre">buy_ware_result</span></code> we determine if the customer can afford the cost and
give proper return messages. This is also where we actually move the bought item into the inventory
of the customer.</p>
</section>
<section id="the-command-to-start-the-menu">
<h3>The command to start the menu<a class="headerlink" href="#the-command-to-start-the-menu" title="Permalink to this headline"></a></h3>
<p>We could <em>in principle</em> launch the shopping menu the moment a customer steps into our shop room, but
this would probably be considered pretty annoying. Its better to create a <a class="reference internal" href="../Components/Commands.html"><span class="doc std std-doc">Command</span></a> for
customers to explicitly wanting to shop around.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/typeclasses/npcshop.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span>
<span class="k">class</span> <span class="nc">CmdBuy</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Start to do some shopping</span>
<span class="sd"> Usage:</span>
<span class="sd"> buy</span>
<span class="sd"> shop</span>
<span class="sd"> browse</span>
<span class="sd"> This will allow you to browse the wares of the</span>
<span class="sd"> current shop and buy items you want.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;buy&quot;</span>
<span class="n">aliases</span> <span class="o">=</span> <span class="p">(</span><span class="s2">&quot;shop&quot;</span><span class="p">,</span> <span class="s2">&quot;browse&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;Starts the shop EvMenu instance&quot;</span>
<span class="n">evmenu</span><span class="o">.</span><span class="n">EvMenu</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">,</span>
<span class="s2">&quot;typeclasses.npcshop&quot;</span><span class="p">,</span>
<span class="n">startnode</span><span class="o">=</span><span class="s2">&quot;menunode_shopfront&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>This will launch the menu. The <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> instance is initialized with the path to this very module -
since the only global functions available in this module are our menu nodes, this will work fine
(you could also have put those in a separate module). We now just need to put this command in a
<a class="reference internal" href="../Components/Command-Sets.html"><span class="doc std std-doc">CmdSet</span></a> so we can add it correctly to the game:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">CmdSet</span>
<span class="k">class</span> <span class="nc">ShopCmdSet</span><span class="p">(</span><span class="n">CmdSet</span><span class="p">):</span>
<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">CmdBuy</span><span class="p">())</span>
</pre></div>
</div>
</section>
</section>
<section id="building-the-shop">
<h2>Building the shop<a class="headerlink" href="#building-the-shop" title="Permalink to this headline"></a></h2>
<p>There are really only two things that separate our shop from any other Room:</p>
<ul class="simple">
<li><p>The shop has the <code class="docutils literal notranslate"><span class="pre">storeroom</span></code> Attribute set on it, pointing to a second (completely normal) room.</p></li>
<li><p>It has the <code class="docutils literal notranslate"><span class="pre">ShopCmdSet</span></code> stored on itself. This makes the <code class="docutils literal notranslate"><span class="pre">buy</span></code> command available to users entering
the shop.</p></li>
</ul>
<p>For testing we could easily add these features manually to a room using <code class="docutils literal notranslate"><span class="pre">&#64;py</span></code> or other admin
commands. Just to show how it can be done well instead make a custom <a class="reference internal" href="../Components/Typeclasses.html"><span class="doc std std-doc">Typeclass</span></a> for
the shop room and make a small command that builders can use to build both the shop and the
storeroom at once.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># bottom of mygame/typeclasses/npcshop.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultRoom</span><span class="p">,</span> <span class="n">DefaultExit</span><span class="p">,</span> <span class="n">DefaultObject</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="c1"># class for our front shop room</span>
<span class="k">class</span> <span class="nc">NPCShop</span><span class="p">(</span><span class="n">DefaultRoom</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">at_object_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># we could also use add(ShopCmdSet, persistent=True)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">cmdset</span><span class="o">.</span><span class="n">add_default</span><span class="p">(</span><span class="n">ShopCmdSet</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">storeroom</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># command to build a complete shop (the Command base class</span>
<span class="c1"># should already have been imported earlier in this file)</span>
<span class="k">class</span> <span class="nc">CmdBuildShop</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Build a new shop</span>
<span class="sd"> Usage:</span>
<span class="sd"> @buildshop shopname</span>
<span class="sd"> This will create a new NPCshop room</span>
<span class="sd"> as well as a linked store room (named</span>
<span class="sd"> simply &lt;storename&gt;-storage) for the</span>
<span class="sd"> wares on sale. The store room will be</span>
<span class="sd"> accessed through a locked door in</span>
<span class="sd"> the shop.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;@buildshop&quot;</span>
<span class="n">locks</span> <span class="o">=</span> <span class="s2">&quot;cmd:perm(Builders)&quot;</span>
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">&quot;Builders&quot;</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;Create the shop rooms&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">&quot;Usage: @buildshop &lt;storename&gt;&quot;</span><span class="p">)</span>
<span class="k">return</span>
<span class="c1"># create the shop and storeroom</span>
<span class="n">shopname</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="n">shop</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">NPCShop</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="n">shopname</span><span class="p">,</span>
<span class="n">location</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
<span class="n">storeroom</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">DefaultRoom</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">shopname</span><span class="si">}</span><span class="s2">-storage&quot;</span><span class="p">,</span>
<span class="n">location</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
<span class="n">shop</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">storeroom</span> <span class="o">=</span> <span class="n">storeroom</span>
<span class="c1"># create a door between the two</span>
<span class="n">shop_exit</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;back door&quot;</span><span class="p">,</span>
<span class="n">aliases</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;storage&quot;</span><span class="p">,</span> <span class="s2">&quot;store room&quot;</span><span class="p">],</span>
<span class="n">location</span><span class="o">=</span><span class="n">shop</span><span class="p">,</span>
<span class="n">destination</span><span class="o">=</span><span class="n">storeroom</span><span class="p">)</span>
<span class="n">storeroom_exit</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">DefaultExit</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="s2">&quot;door&quot;</span><span class="p">,</span>
<span class="n">location</span><span class="o">=</span><span class="n">storeroom</span><span class="p">,</span>
<span class="n">destination</span><span class="o">=</span><span class="n">shop</span><span class="p">)</span>
<span class="c1"># make a key for accessing the store room</span>
<span class="n">storeroom_key_name</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">shopname</span><span class="si">}</span><span class="s2">-storekey&quot;</span>
<span class="n">storeroom_key</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="n">storeroom_key_name</span><span class="p">,</span>
<span class="n">location</span><span class="o">=</span><span class="n">shop</span><span class="p">)</span>
<span class="c1"># only allow chars with this key to enter the store room</span>
<span class="n">shop_exit</span><span class="o">.</span><span class="n">locks</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;traverse:holds(</span><span class="si">{</span><span class="n">storeroom_key_name</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">)</span>
<span class="c1"># inform the builder about progress</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="sa">f</span><span class="s2">&quot;The shop </span><span class="si">{</span><span class="n">shop</span><span class="si">}</span><span class="s2"> was created!&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Our typeclass is simple and so is our <code class="docutils literal notranslate"><span class="pre">buildshop</span></code> command. The command (which is for Builders only)
just takes the name of the shop and builds the front room and a store room to go with it (always
named <code class="docutils literal notranslate"><span class="pre">&quot;&lt;shopname&gt;-storage&quot;</span></code>. It connects the rooms with a two-way exit. You need to add
<code class="docutils literal notranslate"><span class="pre">CmdBuildShop</span></code> [to the default cmdset](Starting/Adding-Command-Tutorial#step-2-adding-the-command-to-a-
default-cmdset) before you can use it. Once having created the shop you can now <code class="docutils literal notranslate"><span class="pre">&#64;teleport</span></code> to it or
<code class="docutils literal notranslate"><span class="pre">&#64;open</span></code> a new exit to it. You could also easily expand the above command to automatically create
exits to and from the new shop from your current location.</p>
<p>To avoid customers walking in and stealing everything, we create a <a class="reference internal" href="../Components/Locks.html"><span class="doc std std-doc">Lock</span></a> on the storage
door. Its a simple lock that requires the one entering to carry an object named
<code class="docutils literal notranslate"><span class="pre">&lt;shopname&gt;-storekey</span></code>. We even create such a key object and drop it in the shop for the new shop
keeper to pick up.</p>
<blockquote>
<div><p>If players are given the right to name their own objects, this simple lock is not very secure and
you need to come up with a more robust lock-key solution.</p>
</div></blockquote>
<blockquote>
<div><p>We dont add any descriptions to all these objects so looking “at” them will not be too thrilling.
You could add better default descriptions as part of the <code class="docutils literal notranslate"><span class="pre">&#64;buildshop</span></code> command or leave descriptions
this up to the Builder.</p>
</div></blockquote>
</section>
<section id="the-shop-is-open-for-business">
<h2>The shop is open for business!<a class="headerlink" href="#the-shop-is-open-for-business" title="Permalink to this headline"></a></h2>
<p>We now have a functioning shop and an easy way for Builders to create it. All you need now is to
<code class="docutils literal notranslate"><span class="pre">&#64;open</span></code> a new exit from the rest of the game into the shop and put some sell-able items in the store
room. Our shop does have some shortcomings:</p>
<ul class="simple">
<li><p>For Characters to be able to buy stuff they need to also have the <code class="docutils literal notranslate"><span class="pre">gold</span></code> Attribute set on
themselves.</p></li>
<li><p>We manually remove the “door” exit from our items for sale. But what if there are other unsellable
items in the store room? What if the shop owner walks in there for example - anyone in the store
could then buy them for 1 gold.</p></li>
<li><p>What if someone else were to buy the item were looking at just before we decide to buy it? It
would then be gone and the counter be wrong - the shop would pass us the next item in the list.</p></li>
</ul>
<p>Fixing these issues are left as an exercise.</p>
<p>If you want to keep the shop fully NPC-run you could add a <a class="reference internal" href="../Components/Scripts.html"><span class="doc std std-doc">Script</span></a> to restock the shops
store room regularly. This shop example could also easily be owned by a human Player (run for them
by a hired NPC) - the shop owner would get the key to the store room and be responsible for keeping
it well stocked.</p>
</section>
</section>
</div>
</div>
</div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Building-a-mech-tutorial.html" title="Building a mech tutorial"
>next</a> |</li>
<li class="right" >
<a href="Tutorial-Aggressive-NPCs.html" title="Tutorial Aggressive NPCs"
>previous</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="Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">NPC shop Tutorial</a></li>
</ul>
<div class="develop">develop branch</div>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2022, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>