mirror of
https://github.com/evennia/evennia.git
synced 2026-03-18 22:06:30 +01:00
442 lines
No EOL
40 KiB
HTML
442 lines
No EOL
40 KiB
HTML
|
||
<!DOCTYPE html>
|
||
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
||
|
||
<title>NPC shop Tutorial — Evennia 0.9.5 documentation</title>
|
||
<link rel="stylesheet" href="_static/nature.css" type="text/css" />
|
||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||
<script src="_static/jquery.js"></script>
|
||
<script src="_static/underscore.js"></script>
|
||
<script src="_static/doctools.js"></script>
|
||
<script src="_static/language_data.js"></script>
|
||
<script async="async" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"processClass": "tex2jax_process|mathjax_process|math|output_area"}})</script>
|
||
<link rel="shortcut icon" href="_static/favicon.ico"/>
|
||
<link rel="index" title="Index" href="genindex.html" />
|
||
<link rel="search" title="Search" href="search.html" />
|
||
</head><body>
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="genindex.html" title="General Index"
|
||
accesskey="I">index</a></li>
|
||
<li class="right" >
|
||
<a href="py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="index.html">Evennia 0.9.5</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">NPC shop Tutorial</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="document">
|
||
<div class="documentwrapper">
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<section class="tex2jax_ignore mathjax_ignore" id="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="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 store’s stock.</p>
|
||
<p>Our shop extends over two rooms - a “front” room open to the shop’s 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 can’t 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. Evennia’s <em>EvMenu</em> utility will manage
|
||
the menu for us. It’s a good idea to <a class="reference internal" href="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 menu’s 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 won’t 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">"This is the top-menu screen."</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'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">"door"</span><span class="p">]</span>
|
||
|
||
<span class="n">text</span> <span class="o">=</span> <span class="s2">"*** Welcome to </span><span class="si">%s</span><span class="s2">! ***</span><span class="se">\n</span><span class="s2">"</span> <span class="o">%</span> <span class="n">shopname</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="s2">" Things for sale (choose 1-</span><span class="si">%i</span><span class="s2"> to inspect);"</span> \
|
||
<span class="s2">" quit to exit:"</span> <span class="o">%</span> <span class="nb">len</span><span class="p">(</span><span class="n">wares</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">text</span> <span class="o">+=</span> <span class="s2">" There is nothing for sale; quit to exit."</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">options</span><span class="o">.</span><span class="n">append</span><span class="p">({</span><span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"</span><span class="si">%s</span><span class="s2"> (</span><span class="si">%s</span><span class="s2"> gold)"</span> <span class="o">%</span>
|
||
<span class="p">(</span><span class="n">ware</span><span class="o">.</span><span class="n">key</span><span class="p">,</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="p">),</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="s2">"menunode_inspect_and_buy"</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 shop’s 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 menu’s 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 can’t know
|
||
which goods will be available to sale so we rely on this node to modify itself depending on the
|
||
circumstances. Let’s 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">"Sets up the buy menu screen."</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'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">"door"</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="s2">"You inspect </span><span class="si">%s</span><span class="s2">:</span><span class="se">\n\n</span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">ware</span><span class="o">.</span><span class="n">key</span><span class="p">,</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="p">)</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">"This will be executed first when choosing to buy."</span>
|
||
<span class="k">if</span> <span class="n">wealth</span> <span class="o">>=</span> <span class="n">value</span><span class="p">:</span>
|
||
<span class="n">rtext</span> <span class="o">=</span> <span class="s2">"You pay </span><span class="si">%i</span><span class="s2"> gold and purchase </span><span class="si">%s</span><span class="s2">!"</span> <span class="o">%</span> \
|
||
<span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">ware</span><span class="o">.</span><span class="n">key</span><span class="p">)</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="k">else</span><span class="p">:</span>
|
||
<span class="n">rtext</span> <span class="o">=</span> <span class="s2">"You cannot afford </span><span class="si">%i</span><span class="s2"> gold for </span><span class="si">%s</span><span class="s2">!"</span> <span class="o">%</span> \
|
||
<span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">ware</span><span class="o">.</span><span class="n">key</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">rtext</span><span class="p">)</span>
|
||
|
||
<span class="n">options</span> <span class="o">=</span> <span class="p">({</span><span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Buy </span><span class="si">%s</span><span class="s2"> for </span><span class="si">%s</span><span class="s2"> gold"</span> <span class="o">%</span> \
|
||
<span class="p">(</span><span class="n">ware</span><span class="o">.</span><span class="n">key</span><span class="p">,</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="p">),</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="s2">"menunode_shopfront"</span><span class="p">,</span>
|
||
<span class="s2">"exec"</span><span class="p">:</span> <span class="n">buy_ware_result</span><span class="p">},</span>
|
||
<span class="p">{</span><span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Look for something else"</span><span class="p">,</span>
|
||
<span class="s2">"goto"</span><span class="p">:</span> <span class="s2">"menunode_shopfront"</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. It’s better to create a <a class="reference internal" href="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">"""</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"> """</span>
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"buy"</span>
|
||
<span class="n">aliases</span> <span class="o">=</span> <span class="p">(</span><span class="s2">"shop"</span><span class="p">,</span> <span class="s2">"browse"</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">"Starts the shop EvMenu instance"</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">"typeclasses.npcshop"</span><span class="p">,</span>
|
||
<span class="n">startnode</span><span class="o">=</span><span class="s2">"menunode_shopfront"</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="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">@py</span></code> or other admin
|
||
commands. Just to show how it can be done we’ll instead make a custom <a class="reference internal" href="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, permanent=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">"""</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 <storename>-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"> """</span>
|
||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"@buildshop"</span>
|
||
<span class="n">locks</span> <span class="o">=</span> <span class="s2">"cmd:perm(Builders)"</span>
|
||
<span class="n">help_category</span> <span class="o">=</span> <span class="s2">"Builders"</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">"Create the shop rooms"</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">"Usage: @buildshop <storename>"</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="s2">"</span><span class="si">%s</span><span class="s2">-storage"</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">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">"back door"</span><span class="p">,</span>
|
||
<span class="n">aliases</span><span class="o">=</span><span class="p">[</span><span class="s2">"storage"</span><span class="p">,</span> <span class="s2">"store room"</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">"door"</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="s2">"</span><span class="si">%s</span><span class="s2">-storekey"</span> <span class="o">%</span> <span class="n">shopname</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="s2">"traverse:holds(</span><span class="si">%s</span><span class="s2">)"</span> <span class="o">%</span> <span class="n">storeroom_key_name</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="s2">"The shop </span><span class="si">%s</span><span class="s2"> was created!"</span> <span class="o">%</span> <span class="n">shop</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">"<shopname>-storage"</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](./Adding-Command-Tutorial.md#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">@teleport</span></code> to it or
|
||
<code class="docutils literal notranslate"><span class="pre">@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="Locks.html"><span class="doc std std-doc">Lock</span></a> on the storage
|
||
door. It’s a simple lock that requires the one entering to carry an object named
|
||
<code class="docutils literal notranslate"><span class="pre"><shopname>-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 don’t 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">@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">@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 we’re 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="Scripts.html"><span class="doc std std-doc">Script</span></a> to restock the shop’s
|
||
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 class="clearer"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||
<div class="sphinxsidebarwrapper">
|
||
<p class="logo"><a href="index.html">
|
||
<img class="logo" src="_static/evennia_logo.png" alt="Logo"/>
|
||
</a></p>
|
||
<div id="searchbox" style="display: none" role="search">
|
||
<h3 id="searchlabel">Quick search</h3>
|
||
<div class="searchformwrapper">
|
||
<form class="search" action="search.html" method="get">
|
||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||
<input type="submit" value="Go" />
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<script>$('#searchbox').show(0);</script>
|
||
<p><h3><a href="index.html">Table of Contents</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">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>
|
||
|
||
<div role="note" aria-label="source link">
|
||
<!--h3>This Page</h3-->
|
||
<ul class="this-page-menu">
|
||
<li><a href="_sources/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="http://webchat.freenode.net/?channels=evennia&uio=MT1mYWxzZSY5PXRydWUmMTE9MTk1JjEyPXRydWUbb">IRC</a> -
|
||
<a href="https://discord.gg/NecFePw">Discord</a> -
|
||
<a href="https://groups.google.com/forum/#%21forum/evennia">Forums</a>
|
||
</li>
|
||
<li><a href="http://evennia.blogspot.com/">Evennia Dev blog</a> </li>
|
||
</ul>
|
||
<h3>Versions</h3>
|
||
<ul>
|
||
<li><a href="../1.0-dev/index.html">1.0-dev (develop branch)</a></li>
|
||
<li><a href="NPC-shop-Tutorial.html">0.9.5 (v0.9.5 branch)</a></li>
|
||
</ul>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
<div class="clearer"></div>
|
||
</div>
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="genindex.html" title="General Index"
|
||
>index</a></li>
|
||
<li class="right" >
|
||
<a href="py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="index.html">Evennia 0.9.5</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">NPC shop Tutorial</a></li>
|
||
</ul>
|
||
</div>
|
||
<div class="footer" role="contentinfo">
|
||
© Copyright 2020, The Evennia developer community.
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||
</div>
|
||
</body>
|
||
</html> |