mirror of
https://github.com/evennia/evennia.git
synced 2026-03-27 02:06:32 +01:00
Add legacy 2.x docs
This commit is contained in:
parent
df0a1a4f59
commit
07cfac0bfa
2288 changed files with 531353 additions and 0 deletions
|
|
@ -0,0 +1,731 @@
|
|||
|
||||
<!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>Beginner Tutorial — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../" src="../../_static/documentation_options.js"></script>
|
||||
<script src="../../_static/jquery.js"></script>
|
||||
<script src="../../_static/underscore.js"></script>
|
||||
<script src="../../_static/doctools.js"></script>
|
||||
<script src="../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../search.html" />
|
||||
<link rel="next" title="Part 1: What We Have" href="Part1/Beginner-Tutorial-Part1-Overview.html" />
|
||||
<link rel="prev" title="Tutorials and How-To’s" href="../Howtos-Overview.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Part1/Beginner-Tutorial-Part1-Overview.html" title="Part 1: What We Have"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Howtos-Overview.html" title="Tutorials and How-To’s"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../Howtos-Overview.html" accesskey="U">Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Beginner Tutorial</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../index.html">
|
||||
<img class="logo" src="../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Beginner Tutorial</a><ul>
|
||||
<li><a class="reference internal" href="#things-you-need">Things You Need</a><ul>
|
||||
<li><a class="reference internal" href="#a-command-line-interface">A Command Line Interface</a></li>
|
||||
<li><a class="reference internal" href="#a-fresh-game-dir">A Fresh Game-Dir?</a></li>
|
||||
<li><a class="reference internal" href="#a-mud-client">A MUD Client</a></li>
|
||||
<li><a class="reference internal" href="#a-text-editor-or-ide">A Text Editor or IDE</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="../Howtos-Overview.html"
|
||||
title="previous chapter">Tutorials and How-To’s</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Part1/Beginner-Tutorial-Part1-Overview.html"
|
||||
title="next chapter">Part 1: What We Have</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../_sources/Howtos/Beginner-Tutorial/Beginner-Tutorial-Overview.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="beginner-tutorial">
|
||||
<h1>Beginner Tutorial<a class="headerlink" href="#beginner-tutorial" title="Permalink to this headline">¶</a></h1>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Beginner Tutorial Parts</p>
|
||||
<ul class="simple">
|
||||
<li><p><strong><a class="reference internal" href="#"><span class="doc std std-doc">Introduction</span></a></strong>
|
||||
<br>Getting set up.</p></li>
|
||||
<li><p>Part 1: <a class="reference internal" href="Part1/Beginner-Tutorial-Part1-Overview.html"><span class="doc std std-doc">What We Have</span></a>
|
||||
<br>A tour of Evennia and how to use the tools, including an introduction to Python.</p></li>
|
||||
<li><p>Part 2: <a class="reference internal" href="Part2/Beginner-Tutorial-Part2-Overview.html"><span class="doc std std-doc">What We Want</span></a>
|
||||
<br>Planning our tutorial game and what to consider when planning your own.</p></li>
|
||||
<li><p>Part 3: <a class="reference internal" href="Part3/Beginner-Tutorial-Part3-Overview.html"><span class="doc std std-doc">How We Get There</span></a>
|
||||
<br>Getting down to the meat of extending Evennia to make your game.</p></li>
|
||||
<li><p>Part 4: <a class="reference internal" href="Part4/Beginner-Tutorial-Part4-Overview.html"><span class="doc std std-doc">Using What We Created</span></a>
|
||||
<br>Building a tech-demo and world content to go with our code.</p></li>
|
||||
<li><p>Part 5: <a class="reference internal" href="Part5/Beginner-Tutorial-Part5-Overview.html"><span class="doc std std-doc">Showing the World</span></a>
|
||||
<br>Taking our new game online and letting players try it out.</p></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<p>Welcome to Evennia! This multi-part Beginner Tutorial will help get you off the ground and running.</p>
|
||||
<p>You may choose topics that seem interesting but, if you follow this tutorial through to the end, you will have created your own small online game to play and share with others!</p>
|
||||
<p>Use the menu on the right to navigate the index of each of the tutorial’s parts. Use the <a class="reference internal" href="Part1/Beginner-Tutorial-Part1-Overview.html"><span class="doc std std-doc">next</span></a> and <a class="reference internal" href="../Howtos-Overview.html"><span class="doc std std-doc">previous</span></a> links at the top/bottom right of each page to jump between lessons.</p>
|
||||
<section id="things-you-need">
|
||||
<h2>Things You Need<a class="headerlink" href="#things-you-need" title="Permalink to this headline">¶</a></h2>
|
||||
<ul class="simple">
|
||||
<li><p>A command line interface</p></li>
|
||||
<li><p>A MUD client (or web browser)</p></li>
|
||||
<li><p>A text-editor/IDE</p></li>
|
||||
<li><p>Evennia installed and a game-dir initialized</p></li>
|
||||
</ul>
|
||||
<section id="a-command-line-interface">
|
||||
<h3>A Command Line Interface<a class="headerlink" href="#a-command-line-interface" title="Permalink to this headline">¶</a></h3>
|
||||
<p>You need to know how to find the terminal/console in your OS. The Evennia server can be controlled from in-game, but you <em>will</em> realistically need to use the command-line interface to get anywhere. Here are some starters:</p>
|
||||
<ul class="simple">
|
||||
<li><p><a class="reference external" href="https://tutorial.djangogirls.org/en/intro_to_command_line/">Online Intro to the Command line for different OS:es</a></p></li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<div><p>Note that the documentation typically uses forward-slashes (<code class="docutils literal notranslate"><span class="pre">/</span></code>) for file system paths. Windows users should convert these to back-slashes (<code class="docutils literal notranslate"><span class="pre">\</span></code>) instead.</p>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
<section id="a-fresh-game-dir">
|
||||
<h3>A Fresh Game-Dir?<a class="headerlink" href="#a-fresh-game-dir" title="Permalink to this headline">¶</a></h3>
|
||||
<p>You should make sure that you have successfully <a class="reference internal" href="../../Setup/Installation.html"><span class="doc std std-doc">installed Evennia</span></a>. If you followed the instructions, you will have already created a game-dir. The documentation will continue to refer to this game-dir as <code class="docutils literal notranslate"><span class="pre">mygame</span></code>, so you may want to re-use it or make a new one specific to this tutorial only – it’s up to you.</p>
|
||||
<p>If you already have a game-dir and want a new one specific to this tutorial, use the <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">stop</span></code> command to halt the running server. Then, <a class="reference internal" href="../../Setup/Installation.html#initialize-a-new-game"><span class="std std-doc">initialize a new game-dir</span></a> somewhere else (<em>not</em> inside the previous game-dir!).</p>
|
||||
</section>
|
||||
<section id="a-mud-client">
|
||||
<h3>A MUD Client<a class="headerlink" href="#a-mud-client" title="Permalink to this headline">¶</a></h3>
|
||||
<p>You may already have a preferred MUD client. Check out the <a class="reference internal" href="../../Setup/Client-Support-Grid.html"><span class="doc std std-doc">grid of supported clients</span></a>. Or, if telnet’s not your thing, you may also simply use Evennia’s web-client in your preferred browser.</p>
|
||||
<p>Make sure you know how to connect and log in to your locally running Evennia server.</p>
|
||||
<blockquote>
|
||||
<div><p>In this documentation we often interchangeably use the terms ‘MUD’, ‘MU’, and ‘MU*’ to represent all the historically different forms of text-based multiplayer game-styles (i.e., MUD, MUX, MUSH, MUCK, MOO, etc.). Evennia can be used to create any of these game-styles… and more!</p>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
<section id="a-text-editor-or-ide">
|
||||
<h3>A Text Editor or IDE<a class="headerlink" href="#a-text-editor-or-ide" title="Permalink to this headline">¶</a></h3>
|
||||
<p>You need a text editor application to edit Python source files. Most anything that can edit and output raw text should work (…so not Microsoft Word).</p>
|
||||
<ul class="simple">
|
||||
<li><p><a class="reference external" href="https://www.elegantthemes.com/blog/resources/best-code-editors">Here’s a blog post summing up a variety of text editor options</a> - these things don’t change much from year to year. Popular choices for Python are PyCharm, VSCode, Atom, Sublime Text, and Notepad++. Evennia is – to a very large degree – coded in VIM, but it is not suitable for beginners.</p></li>
|
||||
</ul>
|
||||
<div class="admonition important">
|
||||
<p class="admonition-title">Important</p>
|
||||
<p>Use Spaces, Not Tabs< br/>
|
||||
Make sure to configure your text editor so that pressing the ‘Tab’ key inserts <em>4 spaces</em> rather than a tab-character. Because Python is whitespace-aware, this simple practice will make your life much easier.</p>
|
||||
</div>
|
||||
<p>You should now be ready to move on to the <a class="reference internal" href="Part1/Beginner-Tutorial-Part1-Overview.html"><span class="doc std std-doc">first part of the Beginner Tutorial</span></a>! (In the future, use the <code class="docutils literal notranslate"><span class="pre">previous</span> <span class="pre">|</span> <span class="pre">next</span></code> buttons on the top/bottom of the page to progress.)</p>
|
||||
<details>
|
||||
<summary>
|
||||
Click here to see the full index of all parts and lessons of the Beginner-Tutorial.
|
||||
</summary>
|
||||
<div class="toctree-wrapper compound">
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Part1/Beginner-Tutorial-Part1-Overview.html">Part 1: What We Have</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Part1/Beginner-Tutorial-Part1-Overview.html#lessons">Lessons</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part1/Beginner-Tutorial-Building-Quickstart.html">1. Using Commands and Building Stuff</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Building-Quickstart.html#getting-help">1.1. Getting Help</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Building-Quickstart.html#looking-around">1.2. Looking Around</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Building-Quickstart.html#stepping-down-from-godhood">1.3. Stepping Down from Godhood</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Building-Quickstart.html#creating-an-object">1.4. Creating an Object</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Building-Quickstart.html#get-a-personality">1.5. Get a Personality</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Building-Quickstart.html#pushing-your-buttons">1.6. Pushing Your Buttons</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Building-Quickstart.html#making-yourself-a-house">1.7. Making Yourself a House</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Building-Quickstart.html#reshuffling-the-world">1.8. Reshuffling the World</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Building-Quickstart.html#adding-a-help-entry">1.9. Adding a Help Entry</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Building-Quickstart.html#adding-a-world">1.10. Adding a World</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part1/Beginner-Tutorial-Tutorial-World.html">2. The Tutorial World</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Tutorial-World.html#gameplay">2.1. Gameplay</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Tutorial-World.html#gameplay-hints">2.1.1. Gameplay hints</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Tutorial-World.html#once-you-are-done-or-had-enough">2.2. Once you are done (or had enough)</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Tutorial-World.html#uninstall-the-tutorial-world">2.3. Uninstall the tutorial world</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html">3. Intro to using Python with Evennia</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#evennia-hello-world">3.1. Evennia Hello world</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#making-some-text-graphics">3.2. Making some text ‘graphics’</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#format">3.2.1. .format()</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#f-strings">3.2.2. f-strings</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#colored-text">3.2.3. Colored text</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#importing-code-from-other-modules">3.3. Importing code from other modules</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#our-first-own-function">3.3.1. Our first own function</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#sending-text-to-others">3.4. Sending text to others</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#parsing-python-errors">3.5. Parsing Python errors</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#passing-arguments-to-functions">3.6. Passing arguments to functions</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#finding-others-to-send-to">3.7. Finding others to send to</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#multi-line-py">3.8. Multi-line py</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#other-ways-to-test-python-code">3.9. Other ways to test Python code</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#ipython">3.10. ipython</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-basic-introduction.html#conclusions">3.11. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part1/Beginner-Tutorial-Gamedir-Overview.html">4. Overview of your new Game Dir</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Gamedir-Overview.html#commands">4.1. commands/</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Gamedir-Overview.html#server">4.2. server/</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Gamedir-Overview.html#server-logs">4.2.1. server/logs/</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Gamedir-Overview.html#server-conf">4.2.2. server/conf/</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Gamedir-Overview.html#typeclasses">4.2.3. typeclasses/</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Gamedir-Overview.html#web">4.2.4. web/</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Gamedir-Overview.html#world">4.2.5. world/</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-classes-and-objects.html">5. Introduction to Python classes and objects</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-classes-and-objects.html#importing-things">5.1. Importing things</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-classes-and-objects.html#on-classes-and-objects">5.2. On classes and objects</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-classes-and-objects.html#classes-and-instances">5.2.1. Classes and instances</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-classes-and-objects.html#whats-so-good-about-objects">5.2.2. What’s so good about objects?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-classes-and-objects.html#classes-can-have-children">5.2.3. Classes can have children</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-classes-and-objects.html#a-look-at-multiple-inheritance">5.2.4. A look at multiple inheritance</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Python-classes-and-objects.html#summary">5.3. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part1/Beginner-Tutorial-Evennia-Library-Overview.html">6. Overview of the Evennia library</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Evennia-Library-Overview.html#where-is-it">6.1. Where is it?</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Evennia-Library-Overview.html#an-example-of-exploring-the-library">6.2. An example of exploring the library</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Evennia-Library-Overview.html#conclusions">6.3. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html">7. Making objects persistent</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#our-first-persistent-object">7.1. Our first persistent object</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#making-a-new-object-by-calling-the-class">7.1.1. Making a new object by calling the class</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#creating-using-create-object">7.1.2. Creating using create_object</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#creating-using-create-command">7.1.3. Creating using create-command</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#typeclasses">7.2. Typeclasses</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#examining-objects">7.2.1. Examining objects</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#default-typeclasses">7.2.2. Default typeclasses</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#modifying-ourselves">7.3. Modifying ourselves</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#a-method-on-ourselves">7.3.1. A method on ourselves</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#attributes">7.3.2. Attributes</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#setting-things-on-new-characters">7.3.3. Setting things on new Characters</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#updating-all-characters-in-a-loop">7.3.4. Updating all Characters in a loop</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#extra-credits">7.4. Extra Credits</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Learning-Typeclasses.html#conclusions">7.5. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part1/Beginner-Tutorial-Adding-Commands.html">8. Adding custom commands</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Adding-Commands.html#creating-a-custom-command">8.1. Creating a custom command</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Adding-Commands.html#making-our-cmdset-persistent">8.1.1. Making our cmdset persistent</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Adding-Commands.html#add-the-echo-command-to-the-default-cmdset">8.1.2. Add the echo command to the default cmdset</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Adding-Commands.html#figuring-out-who-to-hit">8.1.3. Figuring out who to hit</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Adding-Commands.html#summary">8.2. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part1/Beginner-Tutorial-More-on-Commands.html">9. Parsing Command input</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-More-on-Commands.html#more-advanced-parsing">9.1. More advanced parsing</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-More-on-Commands.html#adding-a-command-to-an-object">9.2. Adding a Command to an object</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-More-on-Commands.html#you-need-to-hold-the-sword">9.2.1. You need to hold the sword!</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-More-on-Commands.html#adding-the-command-to-a-default-cmdset">9.3. Adding the Command to a default Cmdset</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-More-on-Commands.html#removing-commands">9.3.1. Removing Commands</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-More-on-Commands.html#replace-a-default-command">9.4. Replace a default command</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-More-on-Commands.html#summary">9.5. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part1/Beginner-Tutorial-Creating-Things.html">10. Creating things</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Creating-Things.html#creating-objects">10.1. Creating Objects</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Creating-Things.html#creating-rooms-characters-and-exits">10.2. Creating Rooms, Characters and Exits</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Creating-Things.html#linking-exits-and-rooms-in-code">10.2.1. Linking Exits and Rooms in code</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Creating-Things.html#creating-accounts">10.3. Creating Accounts</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Creating-Things.html#creating-channels">10.4. Creating Channels</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Creating-Things.html#creating-scripts">10.5. Creating Scripts</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Creating-Things.html#conclusion">10.6. Conclusion</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part1/Beginner-Tutorial-Searching-Things.html">11. Searching for things</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Searching-Things.html#main-search-functions">11.1. Main search functions</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Searching-Things.html#searching-using-object-search">11.2. Searching using Object.search</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Searching-Things.html#what-can-be-searched-for">11.3. What can be searched for</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Searching-Things.html#search-by-key">11.3.1. Search by key</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Searching-Things.html#search-by-aliases">11.3.2. Search by aliases</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Searching-Things.html#search-by-location">11.3.3. Search by location</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Searching-Things.html#search-by-tags">11.3.4. Search by Tags</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Searching-Things.html#search-by-attribute">11.3.5. Search by Attribute</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Searching-Things.html#search-by-typeclass">11.3.6. Search by Typeclass</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Searching-Things.html#search-by-dbref">11.3.7. Search by dbref</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Searching-Things.html#finding-objects-relative-each-other">11.4. Finding objects relative each other</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Searching-Things.html#summary">11.5. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part1/Beginner-Tutorial-Django-queries.html">12. Advanced searching - Django Database queries</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Django-queries.html#queryset-field-lookups">12.1. Queryset field lookups</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Django-queries.html#lets-get-that-werewolf">12.2. Let’s get that werewolf …</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Django-queries.html#queries-with-or-or-not">12.3. Queries with OR or NOT</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Django-queries.html#annotations">12.4. Annotations</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Django-queries.html#f-objects">12.5. F-objects</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Django-queries.html#grouping-and-returning-only-certain-properties">12.6. Grouping and returning only certain properties</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Django-queries.html#conclusions">12.7. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part1/Beginner-Tutorial-Making-A-Sittable-Object.html">13. Building a chair you can sit on</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Making-A-Sittable-Object.html#make-us-not-able-to-move-while-sitting">13.1. Make us not able to move while sitting</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Making-A-Sittable-Object.html#making-the-chair-itself">13.2. Making the Chair itself</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Making-A-Sittable-Object.html#sitting-on-or-in">13.2.1. Sitting on or in?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Making-A-Sittable-Object.html#extra-credits">13.2.2. Extra credits</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Making-A-Sittable-Object.html#adding-commands">13.3. Adding commands</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Making-A-Sittable-Object.html#command-variant-1-commands-on-the-chair">13.3.1. Command variant 1: Commands on the chair</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part1/Beginner-Tutorial-Making-A-Sittable-Object.html#command-variant-2-command-on-character">13.3.2. Command variant 2: Command on Character</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part1/Beginner-Tutorial-Making-A-Sittable-Object.html#conclusions">13.4. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Part2/Beginner-Tutorial-Part2-Overview.html">Part 2: What We Want</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Part2/Beginner-Tutorial-Part2-Overview.html#lessons">Lessons</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-Where-Do-I-Begin.html">1. Where do I begin?</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-Where-Do-I-Begin.html#what-is-your-motivation-for-doing-this">1.1. What is your motivation for doing this?</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-Where-Do-I-Begin.html#what-are-your-skills">1.2. What are your skills?</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-Where-Do-I-Begin.html#the-game-engine">1.2.1. The game engine</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-Where-Do-I-Begin.html#asset-creation">1.2.2. Asset creation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-Where-Do-I-Begin.html#so-where-do-i-begin-then">1.3. So, where do I begin, then?</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-Where-Do-I-Begin.html#conclusions">1.4. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html">2. On Planning a Game</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#the-steps">2.1. The steps</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#planning">2.2. Planning</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#administration">2.2.1. Administration</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#building">2.2.2. Building</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#systems">2.2.3. Systems</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#rooms">2.2.4. Rooms</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#objects-items">2.2.5. Objects / items</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#characters">2.2.6. Characters</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#coding-and-tech-demo">2.3. Coding and Tech demo</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#world-building">2.4. World Building</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#alpha-release">2.5. Alpha Release</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#beta-release-perpetual-beta">2.6. Beta Release/Perpetual Beta</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#congratulate-yourself">2.7. Congratulate yourself!</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Game-Planning.html#planning-our-tutorial-game">2.8. Planning our tutorial game</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html">3. Planning our tutorial game</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#game-concept">3.1. Game concept</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#administration">3.2. Administration</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#should-your-game-rules-be-enforced-by-coded-systems-by-human-game-masters">3.2.1. Should your game rules be enforced by coded systems by human game masters?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#what-is-the-staff-hierarchy-in-your-game-is-vanilla-evennia-roles-enough-or-do-you-need-something-else">3.2.2. What is the staff hierarchy in your game? Is vanilla Evennia roles enough or do you need something else?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#should-players-be-able-to-post-out-of-characters-on-channels-and-via-other-means-like-bulletin-boards">3.2.3. Should players be able to post out-of-characters on channels and via other means like bulletin-boards?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#building">3.3. Building</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#how-will-the-world-be-built">3.3.1. How will the world be built?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#can-only-privileged-builders-create-things-or-should-regular-players-also-have-limited-build-capability">3.3.2. Can only privileged Builders create things or should regular players also have limited build-capability?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#systems">3.4. Systems</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#do-you-base-your-game-off-an-existing-rpg-system-or-make-up-your-own">3.4.1. Do you base your game off an existing RPG system or make up your own?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#what-are-the-game-mechanics-how-do-you-decide-if-an-action-succeeds-or-fails">3.4.2. What are the game mechanics? How do you decide if an action succeeds or fails?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#does-the-flow-of-time-matter-in-your-game-does-night-and-day-change-what-about-seasons">3.4.3. Does the flow of time matter in your game - does night and day change? What about seasons?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#do-you-want-changing-global-weather-or-should-weather-just-be-set-manually-in-roleplay">3.4.4. Do you want changing, global weather or should weather just be set manually in roleplay?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#do-you-want-a-coded-world-economy-or-just-a-simple-barter-system-or-no-formal-economy-at-all">3.4.5. Do you want a coded world-economy or just a simple barter system? Or no formal economy at all?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#do-you-have-concepts-like-reputation-and-influence">3.4.6. Do you have concepts like reputation and influence?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#will-your-characters-be-known-by-their-name-or-only-by-their-physical-appearance">3.4.7. Will your characters be known by their name or only by their physical appearance?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#rooms">3.5. Rooms</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#is-a-simple-room-description-enough-or-should-the-description-be-able-to-change">3.5.1. Is a simple room description enough or should the description be able to change?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#should-the-room-have-different-statuses">3.5.2. Should the room have different statuses?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#can-objects-be-hidden-in-the-room-can-a-person-hide-in-the-room">3.5.3. Can objects be hidden in the room? Can a person hide in the room?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#objects">3.6. Objects</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#how-numerous-are-your-objects-do-you-want-large-loot-lists-or-are-objects-just-role-playing-props">3.6.1. How numerous are your objects? Do you want large loot-lists or are objects just role playing props?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#is-each-coin-a-separate-object-or-do-you-just-store-a-bank-account-value">3.6.2. Is each coin a separate object or do you just store a bank account value?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#do-multiple-similar-objects-form-stack-and-how-are-those-stacks-handled-in-that-case">3.6.3. Do multiple similar objects form stack and how are those stacks handled in that case?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#does-an-object-have-weight-or-volume-so-you-cannot-carry-an-infinite-amount-of-them">3.6.4. Does an object have weight or volume (so you cannot carry an infinite amount of them)?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#can-objects-be-broken-can-they-be-repaired">3.6.5. Can objects be broken? Can they be repaired?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#can-you-fight-with-a-chair-or-a-flower-or-must-you-use-a-special-weapon-kind-of-thing">3.6.6. Can you fight with a chair or a flower or must you use a special ‘weapon’ kind of thing?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#will-characters-be-able-to-craft-new-objects">3.6.7. Will characters be able to craft new objects?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#should-mobs-npcs-have-some-sort-of-ai">3.6.8. Should mobs/NPCs have some sort of AI?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#are-npcs-and-mobs-different-entities-how-do-they-differ">3.6.9. Are NPCs and mobs different entities? How do they differ?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#should-there-be-npcs-giving-quests-if-so-how-do-you-track-quest-status">3.6.10. _Should there be NPCs giving quests? If so, how do you track Quest status?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#characters">3.7. Characters</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#can-players-have-more-than-one-character-active-at-a-time-or-are-they-allowed-to-multi-play">3.7.1. Can players have more than one Character active at a time or are they allowed to multi-play?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#how-does-the-character-generation-work">3.7.2. How does the character-generation work?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#how-do-you-implement-different-classes-or-races">3.7.3. How do you implement different “classes” or “races”?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#if-a-character-can-hide-in-a-room-what-skill-will-decide-if-they-are-detected">3.7.4. If a Character can hide in a room, what skill will decide if they are detected?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#what-does-the-skill-tree-look-like-can-a-character-gain-experience-to-improve-by-killing-enemies-solving-quests-by-roleplaying">3.7.5. What does the skill tree look like? Can a Character gain experience to improve? By killing enemies? Solving quests? By roleplaying?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#may-player-characters-attack-each-other-pvp">3.7.6. May player-characters attack each other (PvP)?</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#what-are-the-penalties-of-defeat-permanent-death-quick-respawn-time-in-prison">3.7.7. What are the penalties of defeat? Permanent death? Quick respawn? Time in prison?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html#conclusions">3.8. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Part3/Beginner-Tutorial-Part3-Overview.html">Part 3: How We Get There (Example Game)</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Part3/Beginner-Tutorial-Part3-Overview.html#lessons">Lessons</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Utilities.html">1. Code structure and Utilities</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Utilities.html#folder-structure">1.1. Folder structure</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Utilities.html#enums">1.2. Enums</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Utilities.html#utility-module">1.3. Utility module</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Utilities.html#testing">1.4. Testing</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Utilities.html#running-your-test">1.4.1. Running your test</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Utilities.html#summary">1.5. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html">2. Rules and dice rolling</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#summary-of-knave-rules">2.1. Summary of <em>Knave</em> rules</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#making-a-rule-module">2.2. Making a rule module</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#rolling-dice">2.3. Rolling dice</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#generic-dice-roller">2.3.1. Generic dice roller</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#rolling-with-advantage">2.3.2. Rolling with advantage</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#saving-throws">2.3.3. Saving throws</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#opposed-saving-throw">2.3.4. Opposed saving throw</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#morale-check">2.3.5. Morale check</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#roll-for-healing">2.3.6. Roll for Healing</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#rolling-on-a-table">2.3.7. Rolling on a table</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#roll-for-death">2.3.8. Roll for death</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#testing">2.4. Testing</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#mocking-and-patching">2.4.1. Mocking and patching</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rules.html#summary">2.5. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Characters.html">3. Player Characters</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Characters.html#inheritance-structure">3.1. Inheritance structure</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Characters.html#living-mixin-class">3.2. Living mixin class</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Characters.html#character-class">3.3. Character class</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Characters.html#funcparser-inlines">3.3.1. Funcparser inlines</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Characters.html#backtracking">3.3.2. Backtracking</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Characters.html#connecting-the-character-with-evennia">3.4. Connecting the Character with Evennia</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Characters.html#unit-testing">3.5. Unit Testing</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Characters.html#about-races-and-classes">3.6. About races and classes</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Characters.html#summary">3.7. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Objects.html">4. In-game Objects and items</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Objects.html#new-enums">4.1. New Enums</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Objects.html#the-base-object">4.2. The base object</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Objects.html#using-attributes-or-not">4.2.1. Using Attributes or not</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Objects.html#creating-tags-in-at-object-creation">4.2.2. Creating tags in <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Objects.html#other-object-types">4.3. Other object types</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Objects.html#consumables">4.4. Consumables</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Objects.html#weapons">4.5. Weapons</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Objects.html#magic">4.6. Magic</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Objects.html#armor">4.7. Armor</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Objects.html#your-bare-hands">4.8. Your Bare hands</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Objects.html#testing-and-extra-credits">4.9. Testing and Extra credits</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html">5. Handling Equipment</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#equipmenthandler-that-saves">5.1. EquipmentHandler that saves</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#connecting-the-equipmenthandler">5.2. Connecting the EquipmentHandler</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#expanding-the-equipmenthandler">5.3. Expanding the Equipmenthandler</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#validate-slot-usage">5.4. <code class="docutils literal notranslate"><span class="pre">.validate_slot_usage</span></code></a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#max-slots">5.4.1. <code class="docutils literal notranslate"><span class="pre">.max_slots</span></code></a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#count-slots">5.4.2. <code class="docutils literal notranslate"><span class="pre">.count_slots</span></code></a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#validating-slots">5.4.3. Validating slots</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#add-and-remove">5.5. <code class="docutils literal notranslate"><span class="pre">.add</span></code> and <code class="docutils literal notranslate"><span class="pre">.remove</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#moving-things-around">5.6. Moving things around</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#get-everything">5.7. Get everything</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#weapon-and-armor">5.8. Weapon and armor</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#fixing-the-character-class">5.8.1. Fixing the Character class</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#extra-credits">5.9. Extra credits</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#unit-testing">5.10. Unit Testing</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Equipment.html#summary">5.11. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Chargen.html">6. Character Generation</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Chargen.html#how-it-will-work">6.1. How it will work</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Chargen.html#random-tables">6.2. Random tables</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Chargen.html#storing-state-of-the-menu">6.3. Storing state of the menu</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Chargen.html#showing-the-sheet">6.3.1. Showing the sheet</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Chargen.html#apply-character">6.3.2. Apply character</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Chargen.html#initializing-evmenu">6.4. Initializing EvMenu</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Chargen.html#main-node-choosing-what-to-do">6.5. Main Node: Choosing what to do</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Chargen.html#node-changing-your-name">6.6. Node: Changing your name</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Chargen.html#node-swapping-abilities-around">6.7. Node: Swapping Abilities around</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Chargen.html#node-creating-the-character">6.8. Node: Creating the Character</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Chargen.html#tying-the-nodes-together">6.9. Tying the nodes together</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Chargen.html#conclusions">6.10. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html">7. In-game Rooms</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html#the-base-room">7.1. The base room</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html#pvp-room">7.2. PvP room</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html#adding-a-room-map">7.3. Adding a room map</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html#adding-life-to-a-room">7.4. Adding life to a room</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html#testing">7.5. Testing</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Rooms.html#conclusion">7.6. Conclusion</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-NPCs.html">8. Non-Player-Characters</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-NPCs.html#the-npc-base-class">8.1. The NPC base class</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-NPCs.html#testing">8.2. Testing</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-NPCs.html#conclusions">8.3. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html">9. Combat base framework</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#combathandler">9.1. CombatHandler</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#combathandler-get-or-create-combathandler">9.1.1. CombatHandler.get_or_create_combathandler</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#combathandler-msg">9.1.2. CombatHandler.msg</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#combathandler-get-combat-summary">9.1.3. Combathandler.get_combat_summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#actions">9.2. Actions</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#action-dicts">9.3. Action dicts</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#action-classes">9.4. Action classes</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#hold-action">9.4.1. Hold Action</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#attack-action">9.4.2. Attack Action</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#stunt-action">9.4.3. Stunt Action</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#use-item-action">9.4.4. Use Item Action</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#wield-action">9.4.5. Wield Action</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#testing">9.5. Testing</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Base.html#conclusions">9.6. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html">10. Twitch Combat</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#general-principle">10.1. General principle</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#twitch-combat-handler">10.2. Twitch combat handler</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#getting-the-sides-of-combat">10.2.1. Getting the sides of combat</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#tracking-advantage-disadvantage">10.2.2. Tracking Advantage / Disadvantage</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#queue-action">10.2.3. Queue action</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#execute-an-action">10.2.4. Execute an action</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#checking-and-stopping-combat">10.2.5. Checking and stopping combat</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#commands">10.3. Commands</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#base-combat-class">10.3.1. Base Combat class</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#in-combat-look-command">10.3.2. In-combat look command</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#hold-command">10.3.3. Hold command</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#attack-command">10.3.4. Attack command</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#using-items">10.3.5. Using items</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#wielding-new-weapons-and-equipment">10.3.6. Wielding new weapons and equipment</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#grouping-commands-for-use">10.4. Grouping Commands for use</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#combat-startup-and-cleanup">10.4.1. Combat startup and cleanup</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#unit-testing">10.5. Unit Testing</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#a-small-combat-test">10.6. A small combat test</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Twitch.html#conclusions">10.7. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html">11. Turnbased Combat</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#general-principle">11.1. General Principle</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#turnbased-combat-handler">11.2. Turnbased combat handler</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#getting-the-sides-of-combat">11.2.1. Getting the sides of combat</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#tracking-advantage-disadvantage">11.2.2. Tracking Advantage/Disadvantage</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#adding-and-removing-combatants">11.2.3. Adding and removing combatants</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#flee-action">11.2.4. Flee Action</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#queue-action">11.2.5. Queue action</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#execute-an-action-and-tick-the-round">11.2.6. Execute an action and tick the round</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#check-and-stop-combat">11.2.7. Check and stop combat</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#start-combat">11.2.8. Start combat</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#using-evmenu-for-the-combat-menu">11.3. Using EvMenu for the combat menu</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#menu-for-turnbased-combat">11.4. Menu for Turnbased combat</a><ul>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#the-node-index">11.4.1. The node index</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#getting-or-setting-the-combathandler">11.4.2. Getting or setting the combathandler</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#queue-an-action">11.4.3. Queue an action</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#rerun-a-node">11.4.4. Rerun a node</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#stepping-through-the-wizard">11.4.5. Stepping through the wizard</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#choosing-targets-and-recipients">11.4.6. Choosing targets and recipients</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#choose-an-ability">11.4.7. Choose an Ability</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#choose-an-item-to-use-or-wield">11.4.8. Choose an item to use or wield</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#the-main-menu-node">11.4.9. The main menu node</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#attack-command">11.5. Attack Command</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#making-sure-the-menu-stops">11.6. Making sure the menu stops</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#testing">11.7. Testing</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#a-small-combat-test">11.8. A small combat test</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part3/Beginner-Tutorial-Combat-Turnbased.html#conclusions">11.9. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-AI.html">12. NPC and monster AI</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Dungeon.html">13. Dynamically generated Dungeon</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Quests.html">14. Game Quests</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Shops.html">15. In-game Shops</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part3/Beginner-Tutorial-Commands.html">16. In-game Commands</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Part4/Beginner-Tutorial-Part4-Overview.html">Part 4: Using What We Created</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Part4/Beginner-Tutorial-Part4-Overview.html#lessons">Lessons</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="../../Unimplemented.html">1. Unimplemented</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Part5/Beginner-Tutorial-Part5-Overview.html">Part 5: Showing the World</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Part5/Beginner-Tutorial-Part5-Overview.html#lessons">Lessons</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="Part5/Add-a-simple-new-web-page.html">1. Add a simple new web page</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part5/Add-a-simple-new-web-page.html#create-the-view">1.1. Create the view</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part5/Add-a-simple-new-web-page.html#the-html-page">1.2. The HTML page</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="Part5/Add-a-simple-new-web-page.html#the-url">1.3. The URL</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Part1/Beginner-Tutorial-Part1-Overview.html" title="Part 1: What We Have"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Howtos-Overview.html" title="Tutorials and How-To’s"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Beginner Tutorial</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,573 @@
|
|||
|
||||
<!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>8. Adding custom commands — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="9. Parsing Command input" href="Beginner-Tutorial-More-on-Commands.html" />
|
||||
<link rel="prev" title="7. Making objects persistent" href="Beginner-Tutorial-Learning-Typeclasses.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-More-on-Commands.html" title="9. Parsing Command input"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Learning-Typeclasses.html" title="7. Making objects persistent"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">8. </span>Adding custom commands</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">8. Adding custom commands</a><ul>
|
||||
<li><a class="reference internal" href="#creating-a-custom-command">8.1. Creating a custom command</a><ul>
|
||||
<li><a class="reference internal" href="#making-our-cmdset-persistent">8.1.1. Making our cmdset persistent</a></li>
|
||||
<li><a class="reference internal" href="#add-the-echo-command-to-the-default-cmdset">8.1.2. Add the echo command to the default cmdset</a></li>
|
||||
<li><a class="reference internal" href="#figuring-out-who-to-hit">8.1.3. Figuring out who to hit</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#summary">8.2. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Learning-Typeclasses.html"
|
||||
title="previous chapter"><span class="section-number">7. </span>Making objects persistent</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-More-on-Commands.html"
|
||||
title="next chapter"><span class="section-number">9. </span>Parsing Command input</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Adding-Commands.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="adding-custom-commands">
|
||||
<h1><span class="section-number">8. </span>Adding custom commands<a class="headerlink" href="#adding-custom-commands" title="Permalink to this headline">¶</a></h1>
|
||||
<p>In this lesson we’ll learn how to create our own Evennia <a class="reference internal" href="../../../Components/Commands.html"><span class="doc std std-doc">Commands</span></a> If you are new to Python you’ll also learn some more basics about how to manipulate strings and get information out of Evennia.</p>
|
||||
<p>A Command is something that handles the input from a user and causes a result to happen.
|
||||
An example is <code class="docutils literal notranslate"><span class="pre">look</span></code>, which examines your current location and tells you what it looks like and
|
||||
what is in it.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Commands are not typeclassed</p>
|
||||
<p>If you just came from the previous lesson, you might want to know that Commands and
|
||||
CommandSets are not <code class="docutils literal notranslate"><span class="pre">typeclassed</span></code>. That is, instances of them are not saved to the
|
||||
database. They are “just” normal Python classes.</p>
|
||||
</aside>
|
||||
<p>In Evennia, a Command is a Python <em>class</em>. If you are unsure about what a class is, review the
|
||||
<a class="reference internal" href="Beginner-Tutorial-Python-classes-and-objects.html"><span class="doc std std-doc">previous lesson about it</span></a>! A Command inherits from <code class="docutils literal notranslate"><span class="pre">evennia.Command</span></code> or from one of the alternative command- classes, such as <code class="docutils literal notranslate"><span class="pre">MuxCommand</span></code> which is what most default commands use.</p>
|
||||
<p>All Commands are grouped in another class called a <em>Command Set</em>. Think of a Command Set as a bag holding many different commands. One CmdSet could for example hold all commands for combat, another for building etc.</p>
|
||||
<p>Command-Sets are then associated with objects, for example with your Character. Doing so makes the commands in that cmdset available to the object. By default, Evennia groups all character-commands into one big cmdset called the <code class="docutils literal notranslate"><span class="pre">CharacterCmdSet</span></code>. It sits on <code class="docutils literal notranslate"><span class="pre">DefaultCharacter</span></code> (and thus, through inheritance, on <code class="docutils literal notranslate"><span class="pre">typeclasses.characters.Character</span></code>).</p>
|
||||
<section id="creating-a-custom-command">
|
||||
<h2><span class="section-number">8.1. </span>Creating a custom command<a class="headerlink" href="#creating-a-custom-command" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Open <code class="docutils literal notranslate"><span class="pre">mygame/commands/command.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">"""</span>
|
||||
<span class="sd">(module docstring)</span>
|
||||
<span class="sd">"""</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span> <span class="k">as</span> <span class="n">BaseCommand</span>
|
||||
<span class="c1"># from evennia import default_cmds</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Command</span><span class="p">(</span><span class="n">BaseCommand</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> (class docstring)</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="c1"># (lots of commented-out stuff)</span>
|
||||
<span class="c1"># ...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Ignoring the docstrings (which you can read if you want), this is the only really active code in the module.</p>
|
||||
<p>We can see that we import <code class="docutils literal notranslate"><span class="pre">Command</span></code> from <code class="docutils literal notranslate"><span class="pre">evennia</span></code> and use the <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">...</span> <span class="pre">import</span> <span class="pre">...</span> <span class="pre">as</span> <span class="pre">...</span></code> form to rename it to <code class="docutils literal notranslate"><span class="pre">BaseCommand</span></code>. This is so we can let our child class also be named <code class="docutils literal notranslate"><span class="pre">Command</span></code> to make it easier to reference. The class itself doesn’t do anything, it just has <code class="docutils literal notranslate"><span class="pre">pass</span></code>. So in the same way as <code class="docutils literal notranslate"><span class="pre">Object</span></code> and <code class="docutils literal notranslate"><span class="pre">Character</span></code> in the previous lessons, this class is identical to its parent.</p>
|
||||
<blockquote>
|
||||
<div><p>The commented out <code class="docutils literal notranslate"><span class="pre">default_cmds</span></code> gives us access to Evennia’s default commands for easy overriding. We’ll try that a little later.</p>
|
||||
</div></blockquote>
|
||||
<p>We could modify this module directly, but let’s work in a separate module just for the heck of it. Open a new file <code class="docutils literal notranslate"><span class="pre">mygame/commands/mycommands.py</span></code> and add the following code:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/mycommands.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">commands.command</span> <span class="kn">import</span> <span class="n">Command</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdEcho</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"echo"</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This is the simplest form of command you can imagine. It just gives itself a name, “echo”. This is what you will use to call this command later.</p>
|
||||
<p>Next we need to put this in a CmdSet. It will be a one-command CmdSet for now! Change your file as such:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/mycommands.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">commands.command</span> <span class="kn">import</span> <span class="n">Command</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">CmdEcho</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"echo"</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">MyCmdSet</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">CmdEcho</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Our <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code> class must have an <code class="docutils literal notranslate"><span class="pre">at_cmdset_creation</span></code> method, named exactly like this - this is what Evennia will be looking for when setting up the cmdset later, so if you didn’t set it up, it will use the parent’s version, which is empty. Inside we add the command class to the cmdset by <code class="docutils literal notranslate"><span class="pre">self.add()</span></code>. If you wanted to add more commands to this CmdSet you could just add more lines of <code class="docutils literal notranslate"><span class="pre">self.add</span></code> after this.</p>
|
||||
<p>Finally, let’s add this command to ourselves so we can try it out. In-game you can experiment with <code class="docutils literal notranslate"><span class="pre">py</span></code> again:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py me.cmdset.add("commands.mycommands.MyCmdSet")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">me.cmdset</span></code> is the store of all cmdsets stored on us. By giving the path to our CmdSet class, it will be added.</p>
|
||||
<p>Now try</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> echo
|
||||
Command echo has no defined `func()` - showing on-command variables:
|
||||
...
|
||||
...
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">echo</span></code> works! You should be getting a long list of outputs. The reason for this is that your <code class="docutils literal notranslate"><span class="pre">echo</span></code> function is not really “doing” anything yet and the default function is then to show all useful resources available to you when you use your Command. Let’s look at some of those listed:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Command echo has no defined `func()` - showing on-command variables:
|
||||
obj (<class 'typeclasses.characters.Character'>): YourName
|
||||
lockhandler (<class 'evennia.locks.lockhandler.LockHandler'>): cmd:all()
|
||||
caller (<class 'typeclasses.characters.Character'>): YourName
|
||||
cmdname (<class 'str'>): echo
|
||||
raw_cmdname (<class 'str'>): echo
|
||||
cmdstring (<class 'str'>): echo
|
||||
args (<class 'str'>):
|
||||
cmdset (<class 'evennia.commands.cmdset.CmdSet'>): @mail, about, access, accounts, addcom, alias, allcom, ban, batchcode, batchcommands, boot, cboot, ccreate,
|
||||
cdesc, cdestroy, cemit, channels, charcreate, chardelete, checklockstring, clientwidth, clock, cmdbare, cmdsets, color, copy, cpattr, create, cwho, delcom,
|
||||
desc, destroy, dig, dolphin, drop, echo, emit, examine, find, force, get, give, grapevine2chan, help, home, ic, inventory, irc2chan, ircstatus, link, lock,
|
||||
look, menutest, mudinfo, mvattr, name, nick, objects, ooc, open, option, page, password, perm, pose, public, py, quell, quit, reload, reset, rss2chan, say,
|
||||
script, scripts, server, service, sessions, set, setdesc, sethelp, sethome, shutdown, spawn, style, tag, tel, test2010, test2028, testrename, testtable,
|
||||
tickers, time, tunnel, typeclass, unban, unlink, up, up, userpassword, wall, whisper, who, wipe
|
||||
session (<class 'evennia.server.serversession.ServerSession'>): Griatch(#1)@1:2:7:.:0:.:0:.:1
|
||||
account (<class 'typeclasses.accounts.Account'>): Griatch(account 1)
|
||||
raw_string (<class 'str'>): echo
|
||||
|
||||
--------------------------------------------------
|
||||
echo - Command variables from evennia:
|
||||
--------------------------------------------------
|
||||
name of cmd (self.key): echo
|
||||
cmd aliases (self.aliases): []
|
||||
cmd locks (self.locks): cmd:all();
|
||||
help category (self.help_category): General
|
||||
object calling (self.caller): Griatch
|
||||
object storing cmdset (self.obj): Griatch
|
||||
command string given (self.cmdstring): echo
|
||||
current cmdset (self.cmdset): ChannelCmdSet
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>These are all properties you can access with <code class="docutils literal notranslate"><span class="pre">.</span></code> on the Command instance, such as <code class="docutils literal notranslate"><span class="pre">.key</span></code>, <code class="docutils literal notranslate"><span class="pre">.args</span></code> and so on. Evennia makes these available to you and they will be different every time a command is run. The most important ones we will make use of now are:</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">caller</span></code> - this is ‘you’, the person calling the command.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">args</span></code> - this is all arguments to the command. Now it’s empty, but if you tried <code class="docutils literal notranslate"><span class="pre">echo</span> <span class="pre">foo</span> <span class="pre">bar</span></code> you’d find that this would be <code class="docutils literal notranslate"><span class="pre">"</span> <span class="pre">foo</span> <span class="pre">bar"</span></code>.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">obj</span></code> - this is object on which this Command (and CmdSet) “sits”. So you, in this case.</p></li>
|
||||
</ul>
|
||||
<p>The reason our command doesn’t do anything yet is because it’s missing a <code class="docutils literal notranslate"><span class="pre">func</span></code> method. This is what Evennia looks for to figure out what a Command actually does. Modify your <code class="docutils literal notranslate"><span class="pre">CmdEcho</span></code> class:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/mycommands.py</span>
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdEcho</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> A simple echo command</span>
|
||||
|
||||
<span class="sd"> Usage:</span>
|
||||
<span class="sd"> echo <something></span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"echo"</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="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">"Echo: '</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="si">}</span><span class="s2">'"</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>First we added a docstring. This is always a good thing to do in general, but for a Command class, it will also automatically become the in-game help entry!</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Use Command.msg </p>
|
||||
<p>In a Command class, the <code class="docutils literal notranslate"><span class="pre">self.msg()</span></code> acts as a convenient shortcut for <code class="docutils literal notranslate"><span class="pre">self.caller.msg()</span></code>. Not only is it shorter, it also has some advantages because the command can include more metadata with the message. So using <code class="docutils literal notranslate"><span class="pre">self.msg()</span></code> is usually better. For this tutorial though, <code class="docutils literal notranslate"><span class="pre">self.caller.msg()</span></code> is more explicit in showing what is going on.</p>
|
||||
</aside>
|
||||
<p>Next we add the <code class="docutils literal notranslate"><span class="pre">func</span></code> method. It has one active line where it makes use of some of those variables the Command class offers to us. If you did the <a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html"><span class="doc std std-doc">basic Python tutorial</span></a>, you will recognize <code class="docutils literal notranslate"><span class="pre">.msg</span></code> - this will send a message to the object it is attached to us - in this case <code class="docutils literal notranslate"><span class="pre">self.caller</span></code>, that is, us. We grab <code class="docutils literal notranslate"><span class="pre">self.args</span></code> and includes that in the message.</p>
|
||||
<p>Since we haven’t changed <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code>, that will work as before. Reload and re-add this command to ourselves to try out the new version:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> py self.cmdset.add("commands.mycommands.MyCmdSet")
|
||||
> echo
|
||||
Echo: ''
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Try to pass an argument:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> echo Woo Tang!
|
||||
Echo: ' Woo Tang!'
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Note that there is an extra space before <code class="docutils literal notranslate"><span class="pre">Woo</span></code>. That is because self.args contains <em>everything</em> after the command name, including spaces. Let’s remove that extra space with a small tweak:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/mycommands.py</span>
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdEcho</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> A simple echo command</span>
|
||||
|
||||
<span class="sd"> Usage:</span>
|
||||
<span class="sd"> echo <something></span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"echo"</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="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">"Echo: '</span><span class="si">{</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="si">}</span><span class="s2">'"</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The only difference is that we called <code class="docutils literal notranslate"><span class="pre">.strip()</span></code> on <code class="docutils literal notranslate"><span class="pre">self.args</span></code>. This is a helper method available on all strings - it strips out all whitespace before and after the string. Now the Command-argument will no longer have any space in front of it.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> py self.cmdset.add("commands.mycommands.MyCmdSet")
|
||||
> echo Woo Tang!
|
||||
Echo: 'Woo Tang!'
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Don’t forget to look at the help for the echo command:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> help echo
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You will get the docstring you put in your Command-class!</p>
|
||||
<section id="making-our-cmdset-persistent">
|
||||
<h3><span class="section-number">8.1.1. </span>Making our cmdset persistent<a class="headerlink" href="#making-our-cmdset-persistent" title="Permalink to this headline">¶</a></h3>
|
||||
<p>It’s getting a little annoying to have to re-add our cmdset every time we reload, right? It’s simple
|
||||
enough to make <code class="docutils literal notranslate"><span class="pre">echo</span></code> a <em>persistent</em> change though:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py self.cmdset.add("commands.mycommands.MyCmdSet", persistent=True)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now you can <code class="docutils literal notranslate"><span class="pre">reload</span></code> as much as you want and your code changes will be available directly without needing to re-add the MyCmdSet again.</p>
|
||||
<p>We will add this cmdset in another way, so remove it manually:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py self.cmdset.remove("commands.mycommands.MyCmdSet")
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="add-the-echo-command-to-the-default-cmdset">
|
||||
<h3><span class="section-number">8.1.2. </span>Add the echo command to the default cmdset<a class="headerlink" href="#add-the-echo-command-to-the-default-cmdset" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Above we added the <code class="docutils literal notranslate"><span class="pre">echo</span></code> command to ourselves. It will <em>only</em> be available to us and noone else in the game. But all commands in Evennia are part of command-sets, including the normal <code class="docutils literal notranslate"><span class="pre">look</span></code> and <code class="docutils literal notranslate"><span class="pre">py</span></code> commands we have been using all the while. You can easily extend the default command set with your <code class="docutils literal notranslate"><span class="pre">echo</span></code> command - this way <em>everyone</em> in the game will have access to it!</p>
|
||||
<p>In <code class="docutils literal notranslate"><span class="pre">mygame/commands/</span></code> you’ll find an existing module named <code class="docutils literal notranslate"><span class="pre">default_cmdsets.py</span></code> Open it and you’ll find four empty cmdset-classes:</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">CharacterCmdSet</span></code> - this sits on all Characters (this is the one we usually want to modify)</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">AccountCmdSet</span></code> - this sits on all Accounts (shared between Characters, like <code class="docutils literal notranslate"><span class="pre">logout</span></code> etc)</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">UnloggedCmdSet</span></code> - commands available <em>before</em> you login, like the commands for creating your password and connecting to the game.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">SessionCmdSet</span></code> - commands unique to your Session (your particular client connection). This is unused by default.</p></li>
|
||||
</ul>
|
||||
<p>Tweak this file as follows:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/default_cmdsets.py </span>
|
||||
|
||||
<span class="c1"># ,.. </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">mycommands</span> <span class="c1"># <------- </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CharacterCmdSet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CharacterCmdSet</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> The `CharacterCmdSet` contains general in-game commands like `look`,</span>
|
||||
<span class="sd"> `get`, etc available on in-game Character objects. It is merged with</span>
|
||||
<span class="sd"> the `AccountCmdSet` when an Account puppets a Character.</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"DefaultCharacter"</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="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Populates the cmdset</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_cmdset_creation</span><span class="p">()</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="c1"># any commands you add below will overload the default ones.</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">mycommands</span><span class="o">.</span><span class="n">CmdEcho</span><span class="p">)</span> <span class="c1"># <-----------</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">super() and overriding defaults</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">super()</span></code> Python keyword means that the <em>parent</em> is called. In this case, the parent adds all default commands to this cmdset.</p>
|
||||
<p>Coincidentally, this is also how you replace default commands in Evennia!jj To replace e.g. the command <code class="docutils literal notranslate"><span class="pre">get</span></code>, you just give your replacement command the <code class="docutils literal notranslate"><span class="pre">key</span></code> ‘get’ and add it here - since it’s added after <code class="docutils literal notranslate"><span class="pre">super()</span></code>, it will replace the default version of <code class="docutils literal notranslate"><span class="pre">get</span></code>.</p>
|
||||
</aside>
|
||||
<p>This works the same way as when you added <code class="docutils literal notranslate"><span class="pre">CmdEcho</span></code> to your <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code>. The only difference cmdsets are automatically added to all Characters/Accounts etc so you don’t have to do so manually. We must also make sure to import the <code class="docutils literal notranslate"><span class="pre">CmdEcho</span></code> from your <code class="docutils literal notranslate"><span class="pre">mycommands</span></code> module in order for this module to know about it. The period ‘’<code class="docutils literal notranslate"><span class="pre">.</span></code>‘’ in <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">.</span> <span class="pre">import</span> <span class="pre">mycommands</span></code> means that we are telling Python that <code class="docutils literal notranslate"><span class="pre">mycommands.py</span></code> sits in the same directory as this current module. We want to import the entire module. Further down we access <code class="docutils literal notranslate"><span class="pre">mycommands.CmdEcho</span></code> to add it to the character cmdset.</p>
|
||||
<p>Just <code class="docutils literal notranslate"><span class="pre">reload</span></code> the server and your <code class="docutils literal notranslate"><span class="pre">echo</span></code> command will be available again. There is no limit to how many cmdsets a given Command can be a part of.</p>
|
||||
<p>To remove, you just comment out or delete the <code class="docutils literal notranslate"><span class="pre">self.add()</span></code> line. Keep it like this for now though - we’ll expand on it below.</p>
|
||||
</section>
|
||||
<section id="figuring-out-who-to-hit">
|
||||
<h3><span class="section-number">8.1.3. </span>Figuring out who to hit<a class="headerlink" href="#figuring-out-who-to-hit" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Let’s try something a little more exciting than just echo. Let’s make a <code class="docutils literal notranslate"><span class="pre">hit</span></code> command, for punching someone in the face! This is how we want it to work:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> hit <target>
|
||||
You hit <target> with full force!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Not only that, we want the <code class="docutils literal notranslate"><span class="pre"><target></span></code> to see</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You got hit by <hitter> with full force!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here, <code class="docutils literal notranslate"><span class="pre"><hitter></span></code> would be the one using the <code class="docutils literal notranslate"><span class="pre">hit</span></code> command and <code class="docutils literal notranslate"><span class="pre"><target></span></code> is the one doing the punching; so if your name was <code class="docutils literal notranslate"><span class="pre">Anna</span></code>, and you hit someone named <code class="docutils literal notranslate"><span class="pre">Bob</span></code>, this would look like this:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> hit bob
|
||||
You hit Bob with full force!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>And Bob would see</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You got hit by by Anna with full force!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Still in <code class="docutils literal notranslate"><span class="pre">mygame/commands/mycommands.py</span></code>, add a new class, between <code class="docutils literal notranslate"><span class="pre">CmdEcho</span></code> and <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/mycommands.py</span>
|
||||
|
||||
<span class="p">:</span><span class="n">linenos</span><span class="p">:</span>
|
||||
<span class="p">:</span><span class="n">emphasize</span><span class="o">-</span><span class="n">lines</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">11</span><span class="p">,</span><span class="mi">14</span><span class="p">,</span><span class="mi">15</span><span class="p">,</span><span class="mi">17</span><span class="p">,</span><span class="mi">18</span><span class="p">,</span><span class="mi">19</span><span class="p">,</span><span class="mi">21</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdHit</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Hit a target.</span>
|
||||
|
||||
<span class="sd"> Usage:</span>
|
||||
<span class="sd"> hit <target></span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"hit"</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="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="k">if</span> <span class="ow">not</span> <span class="n">args</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">"Who do you want to hit?"</span><span class="p">)</span>
|
||||
<span class="k">return</span>
|
||||
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</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">args</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">target</span><span class="p">:</span>
|
||||
<span class="k">return</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">"You hit </span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> with full force!"</span><span class="p">)</span>
|
||||
<span class="n">target</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You got hit by </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> with full force!"</span><span class="p">)</span>
|
||||
<span class="c1"># ...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>A lot of things to dissect here:</p>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 3</strong>: The normal <code class="docutils literal notranslate"><span class="pre">class</span></code> header. We inherit from <code class="docutils literal notranslate"><span class="pre">Command</span></code> which we imported at the top of this file.</p></li>
|
||||
<li><p><strong>Lines 4-10</strong>: The docstring and help-entry for the command. You could expand on this as much as you wanted.</p></li>
|
||||
<li><p><strong>Line 11</strong>: We want to write <code class="docutils literal notranslate"><span class="pre">hit</span></code> to use this command.</p></li>
|
||||
<li><p><strong>Line 14</strong>: We strip the whitespace from the argument like before. Since we don’t want to have to do
|
||||
<code class="docutils literal notranslate"><span class="pre">self.args.strip()</span></code> over and over, we store the stripped version
|
||||
in a <em>local variable</em> <code class="docutils literal notranslate"><span class="pre">args</span></code>. Note that we don’t modify <code class="docutils literal notranslate"><span class="pre">self.args</span></code> by doing this, <code class="docutils literal notranslate"><span class="pre">self.args</span></code> will still
|
||||
have the whitespace and is not the same as <code class="docutils literal notranslate"><span class="pre">args</span></code> in this example.</p></li>
|
||||
</ul>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">if-statements</p>
|
||||
<p>The full form of the if statement is</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>if condition:
|
||||
...
|
||||
elif othercondition:
|
||||
...
|
||||
else:
|
||||
...
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>There can be any number of <code class="docutils literal notranslate"><span class="pre">elifs</span></code> to mark when different branches of the code should run. If <code class="docutils literal notranslate"><span class="pre">else</span></code> is provided, it will run if none of the other conditions were truthy.</p>
|
||||
</aside>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 15</strong> has our first <em>conditional</em>, an <code class="docutils literal notranslate"><span class="pre">if</span></code> statement. This is written on the form <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre"><condition>:</span></code> and only if that condition is ‘truthy’ will the indented code block under the <code class="docutils literal notranslate"><span class="pre">if</span></code> statement run. To learn what is truthy in Python it’s usually easier to learn what is “falsy”:</p>
|
||||
<ul>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">False</span></code> - this is a reserved boolean word in Python. The opposite is <code class="docutils literal notranslate"><span class="pre">True</span></code>.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">None</span></code> - another reserved word. This represents nothing, a null-result or value.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">0</span></code> or <code class="docutils literal notranslate"><span class="pre">0.0</span></code></p></li>
|
||||
<li><p>The empty strings <code class="docutils literal notranslate"><span class="pre">""</span></code>, <code class="docutils literal notranslate"><span class="pre">''</span></code>, or empty triple-strings like <code class="docutils literal notranslate"><span class="pre">""""""</span></code>, <code class="docutils literal notranslate"><span class="pre">''''''</span></code></p></li>
|
||||
<li><p>Empty <em>iterables</em> we haven’t used yet, like empty lists <code class="docutils literal notranslate"><span class="pre">[]</span></code>, empty tuples <code class="docutils literal notranslate"><span class="pre">()</span></code> and empty dicts <code class="docutils literal notranslate"><span class="pre">{}</span></code>.</p></li>
|
||||
<li><p>Everything else is “truthy”.</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><strong>Line 16</strong>’s condition is <code class="docutils literal notranslate"><span class="pre">not</span> <span class="pre">args</span></code>. The <code class="docutils literal notranslate"><span class="pre">not</span></code> <em>inverses</em> the result, so if <code class="docutils literal notranslate"><span class="pre">args</span></code> is the empty string (falsy), the whole conditional becomes truthy. Let’s continue in the code:</p></li>
|
||||
<li><p><strong>Lines 16-17</strong>: This code will only run if the <code class="docutils literal notranslate"><span class="pre">if</span></code> statement is truthy, in this case if <code class="docutils literal notranslate"><span class="pre">args</span></code> is the empty string.</p></li>
|
||||
<li><p><strong>Line 17</strong>: <code class="docutils literal notranslate"><span class="pre">return</span></code> is a reserved Python word that exits <code class="docutils literal notranslate"><span class="pre">func</span></code> immediately.</p></li>
|
||||
<li><p><strong>Line 18</strong>: We use <code class="docutils literal notranslate"><span class="pre">self.caller.search</span></code> to look for the target in the current location.</p></li>
|
||||
<li><p><strong>Lines 19-20</strong>: A feature of <code class="docutils literal notranslate"><span class="pre">.search</span></code> is that it will already inform <code class="docutils literal notranslate"><span class="pre">self.caller</span></code> if it couldn’t find the target. In that case, <code class="docutils literal notranslate"><span class="pre">target</span></code> will be <code class="docutils literal notranslate"><span class="pre">None</span></code> and we should just directly <code class="docutils literal notranslate"><span class="pre">return</span></code>.</p></li>
|
||||
<li><p><strong>Lines 21-22</strong>: At this point we have a suitable target and can send our punching strings to each.</p></li>
|
||||
</ul>
|
||||
<p>Finally we must also add this to a CmdSet. Let’s add it to <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/mycommands.py</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
<span class="k">class</span> <span class="nc">MyCmdSet</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">CmdEcho</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">CmdHit</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Errors in your code</p>
|
||||
<p>With longer code snippets to try, it gets more and more likely you’ll
|
||||
make an error and get a <code class="docutils literal notranslate"><span class="pre">traceback</span></code> when you reload. This will either appear
|
||||
directly in-game or in your log (view it with <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">-l</span></code> in a terminal).
|
||||
Don’t panic; tracebacks are your friends - they are to be read bottom-up and usually describe exactly where your problem is. Refer to <a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html"><span class="doc std std-doc">The Python introduction lesson</span></a> for more hints. If you get stuck, reach out to the Evennia community for help.</p>
|
||||
</aside>
|
||||
<p>Note that since we did <code class="docutils literal notranslate"><span class="pre">py</span> <span class="pre">self.cmdset.remove("commands.mycommands.MyCmdSet")</span></code> earlier, this cmdset is no longer available on our Character. Instead we will add these commands directly to our default cmdset.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/default_cmdsets.py </span>
|
||||
|
||||
<span class="c1"># ,.. </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">mycommands</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CharacterCmdSet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CharacterCmdSet</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> The `CharacterCmdSet` contains general in-game commands like `look`,</span>
|
||||
<span class="sd"> `get`, etc available on in-game Character objects. It is merged with</span>
|
||||
<span class="sd"> the `AccountCmdSet` when an Account puppets a Character.</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"DefaultCharacter"</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="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Populates the cmdset</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_cmdset_creation</span><span class="p">()</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="c1"># any commands you add below will overload the default ones.</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">mycommands</span><span class="o">.</span><span class="n">MyCmdSet</span><span class="p">)</span> <span class="c1"># <-----------</span>
|
||||
<span class="c1"># ... </span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We changed from adding the individual <code class="docutils literal notranslate"><span class="pre">echo</span></code> command to adding the entire <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code> in one go! This will add all commands in that cmdset to the <code class="docutils literal notranslate"><span class="pre">CharacterCmdSet</span></code> and is a practical way to add a lot of command in one go. Once you explore Evennia further, you’ll find that <a class="reference internal" href="../../../Contribs/Contribs-Overview.html"><span class="doc std std-doc">Evennia contribs</span></a> all distribute their new commands in cmdsets, so you can easily add them to your game like this.</p>
|
||||
<p>Next we reload to let Evennia know of these code changes and try it out:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
hit
|
||||
Who do you want to hit?
|
||||
hit me
|
||||
You hit YourName with full force!
|
||||
You got hit by YourName with full force!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Lacking a target, we hit ourselves. If you have one of the dragons still around from the previous lesson you could try to hit it (if you dare):</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>hit smaug
|
||||
You hit Smaug with full force!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You won’t see the second string. Only Smaug sees that (and is not amused).</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="summary">
|
||||
<h2><span class="section-number">8.2. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In this lesson we learned how to create our own Command, add it to a CmdSet and then to ourselves. We also upset a dragon.</p>
|
||||
<p>In the next lesson we’ll learn how to hit Smaug with different weapons. We’ll also
|
||||
get into how we replace and extend Evennia’s default Commands.</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="Beginner-Tutorial-More-on-Commands.html" title="9. Parsing Command input"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Learning-Typeclasses.html" title="7. Making objects persistent"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">8. </span>Adding custom commands</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,385 @@
|
|||
|
||||
<!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>1. Using Commands and Building Stuff — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="2. The Tutorial World" href="Beginner-Tutorial-Tutorial-World.html" />
|
||||
<link rel="prev" title="Part 1: What We Have" href="Beginner-Tutorial-Part1-Overview.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Tutorial-World.html" title="2. The Tutorial World"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Part1-Overview.html" title="Part 1: What We Have"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">1. </span>Using Commands and Building Stuff</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">1. Using Commands and Building Stuff</a><ul>
|
||||
<li><a class="reference internal" href="#getting-help">1.1. Getting Help</a></li>
|
||||
<li><a class="reference internal" href="#looking-around">1.2. Looking Around</a></li>
|
||||
<li><a class="reference internal" href="#stepping-down-from-godhood">1.3. Stepping Down from Godhood</a></li>
|
||||
<li><a class="reference internal" href="#creating-an-object">1.4. Creating an Object</a></li>
|
||||
<li><a class="reference internal" href="#get-a-personality">1.5. Get a Personality</a></li>
|
||||
<li><a class="reference internal" href="#pushing-your-buttons">1.6. Pushing Your Buttons</a></li>
|
||||
<li><a class="reference internal" href="#making-yourself-a-house">1.7. Making Yourself a House</a></li>
|
||||
<li><a class="reference internal" href="#reshuffling-the-world">1.8. Reshuffling the World</a></li>
|
||||
<li><a class="reference internal" href="#adding-a-help-entry">1.9. Adding a Help Entry</a></li>
|
||||
<li><a class="reference internal" href="#adding-a-world">1.10. Adding a World</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Part1-Overview.html"
|
||||
title="previous chapter">Part 1: What We Have</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Tutorial-World.html"
|
||||
title="next chapter"><span class="section-number">2. </span>The Tutorial World</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Building-Quickstart.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="using-commands-and-building-stuff">
|
||||
<h1><span class="section-number">1. </span>Using Commands and Building Stuff<a class="headerlink" href="#using-commands-and-building-stuff" title="Permalink to this headline">¶</a></h1>
|
||||
<p>In this lesson, we will test out what we can do in-game out-of-the-box. Evennia ships with
|
||||
<a class="reference internal" href="../../../Components/Default-Commands.html"><span class="doc std std-doc">around 90 default commands</span></a> and, while you can override those as you please, the defaults can be quite useful.</p>
|
||||
<p>Connect and login to your new game. You will find yourself in the “Limbo” location. This
|
||||
is the only room in the game at this point. Let’s explore the default commands a little.</p>
|
||||
<p>The default commands have syntax <a class="reference internal" href="../../../Coding/Default-Command-Syntax.html"><span class="doc std std-doc">similar to MUX</span></a>:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> command[/switch/switch...] [arguments ...]
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>An example would be:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> create/drop box
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>A <em>/switch</em> is a special, optional flag to make a command behave differently. A switch is always put directly after the command name, and begins with a forward slash (<code class="docutils literal notranslate"><span class="pre">/</span></code>). The <em>arguments</em> are one or more inputs to the commands. It’s common to use an equal sign (<code class="docutils literal notranslate"><span class="pre">=</span></code>) when assigning something to an object.</p>
|
||||
<blockquote>
|
||||
<div><p>Are you used to commands starting with @, like @create? That will work, too. Evennia simply ignores the preceeding @.</p>
|
||||
</div></blockquote>
|
||||
<section id="getting-help">
|
||||
<h2><span class="section-number">1.1. </span>Getting Help<a class="headerlink" href="#getting-help" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>help
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Will give you a list of all commands available to you. Use</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>help <commandname>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>to see the in-game help for that command.</p>
|
||||
</section>
|
||||
<section id="looking-around">
|
||||
<h2><span class="section-number">1.2. </span>Looking Around<a class="headerlink" href="#looking-around" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The most common command is</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>look
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This will show you the description of the current location. <code class="docutils literal notranslate"><span class="pre">l</span></code> is an alias for the look command.</p>
|
||||
<p>When targeting objects in commands, you have two special labels you can use: <code class="docutils literal notranslate"><span class="pre">here</span></code> for the current room, or <code class="docutils literal notranslate"><span class="pre">me</span></code>/<code class="docutils literal notranslate"><span class="pre">self</span></code> to point back to yourself. Thus,</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>look me
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>will give you your own description. <code class="docutils literal notranslate"><span class="pre">look</span> <span class="pre">here</span></code> is, in this case, the same as just plain <code class="docutils literal notranslate"><span class="pre">look</span></code>.</p>
|
||||
</section>
|
||||
<section id="stepping-down-from-godhood">
|
||||
<h2><span class="section-number">1.3. </span>Stepping Down from Godhood<a class="headerlink" href="#stepping-down-from-godhood" title="Permalink to this headline">¶</a></h2>
|
||||
<p>If you just installed Evennia, your very first player account is called user #1 — also known as the <em>superuser</em> or <em>god user</em>. This user is very powerful — so powerful that it will override many game restrictions (such as locks). This can be useful, but it also hides some functionality that you might want to test.</p>
|
||||
<p>To step down temporarily from your superuser position, you may use the <code class="docutils literal notranslate"><span class="pre">quell</span></code> command in-game:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>quell
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This will make you start using the permission of your current character’s level instead of your superuser level. If you didn’t change any settings, your initial game Character should have <em>Developer</em> level permission — as high as can be without bypassing locks like the superuser does. This will work fine for the examples on this page. Use</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>unquell
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>to get superuser status again when you are done.</p>
|
||||
</section>
|
||||
<section id="creating-an-object">
|
||||
<h2><span class="section-number">1.4. </span>Creating an Object<a class="headerlink" href="#creating-an-object" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Basic objects can be anything — swords, flowers, and non-player characters. They are created using the <code class="docutils literal notranslate"><span class="pre">create</span></code> command. For example:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>create box
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This creates a new ‘box’ (of the default object type) in your inventory. Use the command <code class="docutils literal notranslate"><span class="pre">inventory</span></code> (or <code class="docutils literal notranslate"><span class="pre">i</span></code>) to see it. Now, ‘box’ is a rather short name, so let’s rename it and tack on a few aliases:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>name box = very large box;box;very;crate
|
||||
</pre></div>
|
||||
</div>
|
||||
<div class="admonition warning">
|
||||
<p class="admonition-title">Warning</p>
|
||||
<p>MUD Clients and Semi-Colons:
|
||||
Some traditional MUD clients use the semi-colon <code class="docutils literal notranslate"><span class="pre">;</span></code> to separate client inputs. If so, the above line will give an error and you’ll need to change your client to use another command-separator or put it in ‘verbatim’ mode. If you still have trouble, use the Evennia web client instead.</p>
|
||||
</div>
|
||||
<p>We have now renamed the box as <em>very large box</em> — and this is what we will see when looking at it. However, we will also recognize it by any of the other names we have offered as arguments to the name command above (i.e., <em>crate</em> or simply <em>box</em> as before). We also could have given these aliases directly after the name in the initial <code class="docutils literal notranslate"><span class="pre">create</span></code> object command. This is true for all creation commands — you can always provide a list of <code class="docutils literal notranslate"><span class="pre">;</span></code>-separated aliases to the name of your new object. In our example, if you had not wanted to change the box object’s name itself, but to add aliases only, you could use the <code class="docutils literal notranslate"><span class="pre">alias</span></code> command.</p>
|
||||
<p>At this point in the building tutorial, our Character is currently carrying the box. Let’s drop it:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>drop box
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Hey presto, — there it is on the ground, in all its normality. There is also a shortcut to both create and drop an object in one go by using the <code class="docutils literal notranslate"><span class="pre">/drop</span></code> switch (e.g, <code class="docutils literal notranslate"><span class="pre">create/drop</span> <span class="pre">box</span></code>).</p>
|
||||
<p>Let us take a closer look at our new box:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>examine box
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The examine command will show some technical details about the box object. For now, we will ignore what this
|
||||
information means.</p>
|
||||
<p>Try to <code class="docutils literal notranslate"><span class="pre">look</span></code> at the box to see the (default) description:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> look box
|
||||
You see nothing special.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The default description is not very exciting. Let’s add some flavor:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>desc box = This is a large and very heavy box.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you try the <code class="docutils literal notranslate"><span class="pre">get</span></code> command, we will pick up the box. So far so good. But, if we really want this to be a large and heavy box, people should <em>not</em> be able to run off with it so easily. To prevent this, we must lock it down. This is done by assigning a <em>lock</em> to it. TO do so, first make sure the box was dropped in the room, then use the lock command:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>lock box = get:false()
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Locks represent a rather <a class="reference internal" href="../../../Components/Locks.html"><span class="doc std std-doc">big topic</span></a> but, for now, this will do what we want. The above command will lock the box so no one can lift it — with one exception. Remember that superusers override all locks and will pick it up anyway. Make sure you are quelling your superuser powers, and try to get it again:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> get box
|
||||
You can't get that.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Think this default error message looks dull? The <code class="docutils literal notranslate"><span class="pre">get</span></code> command looks for an <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attribute</span></a> named <code class="docutils literal notranslate"><span class="pre">get_err_msg</span></code> to return as a custom error message. We set attributes using the <code class="docutils literal notranslate"><span class="pre">set</span></code> command:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>set box/get_err_msg = It's way too heavy for you to lift.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now try to get the box and you should see a more pertinent error message echoed back to you. To see what this message string is in the future, you can use ‘examine’.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>examine box/get_err_msg
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">Examine</span></code> will return the value of attributes, including color codes. For instance, <code class="docutils literal notranslate"><span class="pre">examine</span> <span class="pre">here/desc</span></code> would return the raw description of the current room (including color codes), so that you can copy-and-paste to set its description to something else.</p>
|
||||
<p>You create new Commands — or modify existing ones — in Python code outside the game. We explore doing so later in the <a class="reference internal" href="Beginner-Tutorial-Adding-Commands.html"><span class="doc std std-doc">Commands tutorial</span></a>.</p>
|
||||
</section>
|
||||
<section id="get-a-personality">
|
||||
<h2><span class="section-number">1.5. </span>Get a Personality<a class="headerlink" href="#get-a-personality" title="Permalink to this headline">¶</a></h2>
|
||||
<p><a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Scripts</span></a> are powerful out-of-character objects useful for many “under the hood” things. One of their optional abilities is to do things on a timer. To try out our first script, let’s apply one to ourselves. There is an example script in <code class="docutils literal notranslate"><span class="pre">evennia/contrib/tutorials/bodyfunctions/bodyfunctions.py</span></code> that is called <code class="docutils literal notranslate"><span class="pre">BodyFunctions</span></code>. To add this to our self, we may use the <code class="docutils literal notranslate"><span class="pre">script</span></code> command:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>script self = tutorials.bodyfunctions.BodyFunctions
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The above string tells Evennia to dig up the Python code at the place we indicate. It already knows to look in the <code class="docutils literal notranslate"><span class="pre">contrib/</span></code> folder, so we don’t have to give the full path.</p>
|
||||
<blockquote>
|
||||
<div><p>Note also how we use <code class="docutils literal notranslate"><span class="pre">.</span></code> instead of <code class="docutils literal notranslate"><span class="pre">/</span></code> (or <code class="docutils literal notranslate"><span class="pre">\</span></code> on Windows). This convention is a so-called “Python-path.” In a Python-path, you separate the parts of the path with <code class="docutils literal notranslate"><span class="pre">.</span></code> and skip the <code class="docutils literal notranslate"><span class="pre">.py</span></code> file-ending. Importantly, it also allows you to point to Python code <em>inside</em> files as in our example where the <code class="docutils literal notranslate"><span class="pre">BodyFunctions</span></code> class is inside the <code class="docutils literal notranslate"><span class="pre">bodyfunctions.py</span></code> file. We’ll get to classes later. These “Python-paths” are used extensively throughout Evennia.</p>
|
||||
</div></blockquote>
|
||||
<p>Wait a while and you will notice yourself starting to make random observations…</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>script self =
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The above command will show details about scripts on the given object, in this case your self. The <code class="docutils literal notranslate"><span class="pre">examine</span></code> command also includes such details.</p>
|
||||
<p>You will see how long it is until it “fires” next. Don’t be alarmed if nothing happens when the countdown reaches zero — this particular script has a randomizer to determine if it will say something or not. So you will not see output every time it fires.</p>
|
||||
<p>When you are tired of your character’s “insights,” stop the script with:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>script/stop self = tutorials.bodyfunctions.BodyFunctions
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You may create your own scripts in Python, outside the game; the path you give to <code class="docutils literal notranslate"><span class="pre">script</span></code> is literally the Python path to your script file. The <a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Scripts</span></a> page explains more details.</p>
|
||||
</section>
|
||||
<section id="pushing-your-buttons">
|
||||
<h2><span class="section-number">1.6. </span>Pushing Your Buttons<a class="headerlink" href="#pushing-your-buttons" title="Permalink to this headline">¶</a></h2>
|
||||
<p>If we get back to the box we made, there is only so much fun you can have with it at this point. It’s just a dumb generic object. If you renamed it to <code class="docutils literal notranslate"><span class="pre">stone</span></code> and changed its description, no one would be the wiser. However, with the combined use of custom <a class="reference internal" href="../../../Components/Typeclasses.html"><span class="doc std std-doc">Typeclasses</span></a>, <a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Scripts</span></a> and object-based <a class="reference internal" href="../../../Components/Commands.html"><span class="doc std std-doc">Commands</span></a>, you can expand it — and other items — to be as unique, complex, and interactive as you want.</p>
|
||||
<p>So, let’s work though just such an example. So far, we have only created objects that use the default object typeclass named simply <code class="docutils literal notranslate"><span class="pre">Object</span></code>. Let’s create an object that is a little more interesting. Under
|
||||
<code class="docutils literal notranslate"><span class="pre">evennia/contrib/tutorials</span></code> there is a module <code class="docutils literal notranslate"><span class="pre">red_button.py</span></code>. It contains the enigmatic <code class="docutils literal notranslate"><span class="pre">RedButton</span></code> class.</p>
|
||||
<p>Let’s make us one of <em>those</em>!</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>create/drop button:tutorials.red_button.RedButton
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Enter the above command with Python-path and there you go — one red button! Just as in the Script example earlier, we have specified a Python-path to the Python code that we want Evennia to use for creating the object.</p>
|
||||
<p>The RedButton is an example object intended to show off a few of Evennia’s features. You will find that the <a class="reference internal" href="../../../Components/Typeclasses.html"><span class="doc std std-doc">Typeclass</span></a> and <a class="reference internal" href="../../../Components/Commands.html"><span class="doc std std-doc">Commands</span></a> controlling it are inside <a class="reference internal" href="../../../api/evennia.contrib.tutorials.red_button.html"><span class="doc std std-doc">evennia/contrib/tutorials/red_button</span></a>.</p>
|
||||
<p>If you wait for a while (make sure you dropped it!) the button will blink invitingly.</p>
|
||||
<p>Why don’t you try to push it…?</p>
|
||||
<p>Surely a big red button is meant to be pushed.</p>
|
||||
<p>You know you want to.</p>
|
||||
<div class="admonition warning">
|
||||
<p class="admonition-title">Warning</p>
|
||||
<p>Don’t press the invitingly blinking red button.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section id="making-yourself-a-house">
|
||||
<h2><span class="section-number">1.7. </span>Making Yourself a House<a class="headerlink" href="#making-yourself-a-house" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The main command for shaping your game world is <code class="docutils literal notranslate"><span class="pre">dig</span></code>. For example, if you are standing in Limbo, you can dig a route to your new house location like this:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>dig house = large red door;door;in,to the outside;out
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The above command will create a new room named “house.” It will also create an exit from your current location named ‘large red door’ and a corresponding exit named ‘to the outside’ in the new house room leading back to Limbo. In above, we also define a few aliases to those exits so that players don’t need to type the full exit name.</p>
|
||||
<p>If you wanted to use regular compass directions (north, west, southwest, etc.), you could do that with <code class="docutils literal notranslate"><span class="pre">dig</span></code>, too. However, Evennia also has a specialized version of <code class="docutils literal notranslate"><span class="pre">dig</span></code> that helps with cardinal directions (as well as up/down and in/out). It’s called <code class="docutils literal notranslate"><span class="pre">tunnel</span></code>:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>tunnel sw = cliff
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This will create a new room named “cliff” with a “southwest” exit leading there, and a “northeast” path leading back from the cliff to your current location.</p>
|
||||
<p>You can create new exits from where you are standing, using the <code class="docutils literal notranslate"><span class="pre">open</span></code> command:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>open north;n = house
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This opens an exit <code class="docutils literal notranslate"><span class="pre">north</span></code> (with an alias <code class="docutils literal notranslate"><span class="pre">n</span></code>) to the previously created room <code class="docutils literal notranslate"><span class="pre">house</span></code>.</p>
|
||||
<p>If you have many rooms named <code class="docutils literal notranslate"><span class="pre">house</span></code>, you will get a list of matches and must select to which specific one you want to link.</p>
|
||||
<p>Next, follow the northern exit to your “house” by walking north. You can also <code class="docutils literal notranslate"><span class="pre">teleport</span></code> to it:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>north
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>or:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>teleport house
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>To open an exit back to Limbo manually (in case you didn’t do so automatically by using the <code class="docutils literal notranslate"><span class="pre">dig</span></code> command):</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>open door = limbo
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>(You can also use the <code class="docutils literal notranslate"><span class="pre">#dbref</span></code> of Limbo, which you can find by using <code class="docutils literal notranslate"><span class="pre">examine</span> <span class="pre">here</span></code> when standing in Limbo.)</p>
|
||||
</section>
|
||||
<section id="reshuffling-the-world">
|
||||
<h2><span class="section-number">1.8. </span>Reshuffling the World<a class="headerlink" href="#reshuffling-the-world" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Assuming you are back at <code class="docutils literal notranslate"><span class="pre">Limbo</span></code>, let’s teleport the <em>large box</em> to our <code class="docutils literal notranslate"><span class="pre">house</span></code>:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>teleport box = house
|
||||
very large box is leaving Limbo, heading for house.
|
||||
Teleported very large box -> house.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can find things in the game world, such as our <code class="docutils literal notranslate"><span class="pre">box</span></code>, by using the <code class="docutils literal notranslate"><span class="pre">find</span></code> command:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>find box
|
||||
One Match(#1-#8):
|
||||
very large box(#8) - src.objects.objects.Object
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Knowing the <code class="docutils literal notranslate"><span class="pre">#dbref</span></code> of the box (#8 in this example), you can grab the box and get it back here without actually going to the <code class="docutils literal notranslate"><span class="pre">house</span></code> first:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>teleport #8 = here
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>As mentioned, <code class="docutils literal notranslate"><span class="pre">here</span></code> is an alias for “your current location.” The box should now be back in Limbo with you.</p>
|
||||
<p>We are getting tired of the box. Let’s destroy it:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>destroy box
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Issuing the `destroy`` command will ask you for confirmation. Once you confirm, the box will be gone.</p>
|
||||
<p>You can <code class="docutils literal notranslate"><span class="pre">destroy</span></code> many objects in one go by providing a comma-separated list of objects (or a range of <code class="docutils literal notranslate"><span class="pre">#dbrefs</span></code>, if they are not in the same location) to the command.</p>
|
||||
</section>
|
||||
<section id="adding-a-help-entry">
|
||||
<h2><span class="section-number">1.9. </span>Adding a Help Entry<a class="headerlink" href="#adding-a-help-entry" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Command-related <code class="docutils literal notranslate"><span class="pre">help</span></code> entries are something that you modify in Python code — we’ll cover that when we explain how to add Commands — but you may also add non-command-related help entries. For example, to explain something about the history of your game world:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>sethelp History = At the dawn of time ...
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You will now find your new <code class="docutils literal notranslate"><span class="pre">History</span></code> entry in the <code class="docutils literal notranslate"><span class="pre">help</span></code> list, and can read your help-text with <code class="docutils literal notranslate"><span class="pre">help</span> <span class="pre">History</span></code>.</p>
|
||||
</section>
|
||||
<section id="adding-a-world">
|
||||
<h2><span class="section-number">1.10. </span>Adding a World<a class="headerlink" href="#adding-a-world" title="Permalink to this headline">¶</a></h2>
|
||||
<p>After this brief introduction to building and using in-game commands, you may be ready to see a more fleshed-out example. Fortunately, Evennia comes with an tutorial world for you to explore — which we will try in the next lesson.</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="Beginner-Tutorial-Tutorial-World.html" title="2. The Tutorial World"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Part1-Overview.html" title="Part 1: What We Have"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">1. </span>Using Commands and Building Stuff</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,347 @@
|
|||
|
||||
<!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>10. Creating things — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="11. Searching for things" href="Beginner-Tutorial-Searching-Things.html" />
|
||||
<link rel="prev" title="9. Parsing Command input" href="Beginner-Tutorial-More-on-Commands.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Searching-Things.html" title="11. Searching for things"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-More-on-Commands.html" title="9. Parsing Command input"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">10. </span>Creating things</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">10. Creating things</a><ul>
|
||||
<li><a class="reference internal" href="#creating-objects">10.1. Creating Objects</a></li>
|
||||
<li><a class="reference internal" href="#creating-rooms-characters-and-exits">10.2. Creating Rooms, Characters and Exits</a><ul>
|
||||
<li><a class="reference internal" href="#linking-exits-and-rooms-in-code">10.2.1. Linking Exits and Rooms in code</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#creating-accounts">10.3. Creating Accounts</a></li>
|
||||
<li><a class="reference internal" href="#creating-channels">10.4. Creating Channels</a></li>
|
||||
<li><a class="reference internal" href="#creating-scripts">10.5. Creating Scripts</a></li>
|
||||
<li><a class="reference internal" href="#conclusion">10.6. Conclusion</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-More-on-Commands.html"
|
||||
title="previous chapter"><span class="section-number">9. </span>Parsing Command input</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Searching-Things.html"
|
||||
title="next chapter"><span class="section-number">11. </span>Searching for things</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Creating-Things.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="creating-things">
|
||||
<h1><span class="section-number">10. </span>Creating things<a class="headerlink" href="#creating-things" title="Permalink to this headline">¶</a></h1>
|
||||
<p>We have already created some things - dragons for example. There are many different things to create in Evennia though. In the <a class="reference internal" href="Beginner-Tutorial-Learning-Typeclasses.html"><span class="doc std std-doc">Typeclasses tutorial</span></a>, we noted that there are 7 default Typeclasses coming with Evennia out of the box:</p>
|
||||
<table class="colwidths-auto docutils align-default">
|
||||
<thead>
|
||||
<tr class="row-odd"><th class="head"><p>Evennia base typeclass</p></th>
|
||||
<th class="head"><p>mygame.typeclasses child</p></th>
|
||||
<th class="head"><p>description</p></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultObject</span></code></p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.objects.Object</span></code></p></td>
|
||||
<td><p>Everything with a location</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultCharacter</span></code> (child of <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>)</p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.characters.Character</span></code></p></td>
|
||||
<td><p>Player avatars</p></td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultRoom</span></code> (child of <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>)</p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.rooms.Room</span></code></p></td>
|
||||
<td><p>In-game locations</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultExit</span></code> (chld of <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>)</p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.exits.Exit</span></code></p></td>
|
||||
<td><p>Links between rooms</p></td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultAccount</span></code></p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.accounts.Account</span></code></p></td>
|
||||
<td><p>A player account</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultChannel</span></code></p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.channels.Channel</span></code></p></td>
|
||||
<td><p>In-game comms</p></td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultScript</span></code></p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.scripts.Script</span></code></p></td>
|
||||
<td><p>Entities with no location</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Given you have an imported Typeclass, there are four ways to create an instance of it:</p>
|
||||
<ul>
|
||||
<li><p>Firstly, you can call the class directly, and then <code class="docutils literal notranslate"><span class="pre">.save()</span></code> it:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> obj = SomeTypeClass(db_key=...)
|
||||
obj.save()
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This has the drawback of being two operations; you must also import the class and have to pass
|
||||
the actual database field names, such as <code class="docutils literal notranslate"><span class="pre">db_key</span></code> instead of <code class="docutils literal notranslate"><span class="pre">key</span></code> as keyword arguments. This is closest to how a ‘normal’ Python class works, but is not recommended.</p>
|
||||
</li>
|
||||
<li><p>Secondly you can use the Evennia creation helpers:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> obj = evennia.create_object(SomeTypeClass, key=...)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This is the recommended way if you are trying to create things in Python. The first argument can either be the class <em>or</em> the python-path to the typeclass, like <code class="docutils literal notranslate"><span class="pre">"path.to.SomeTypeClass"</span></code>. It can also be <code class="docutils literal notranslate"><span class="pre">None</span></code> in which case the Evennia default will be used. While all the creation methods
|
||||
are available on <code class="docutils literal notranslate"><span class="pre">evennia</span></code>, they are actually implemented in <a class="reference internal" href="../../../api/evennia.utils.create.html"><span class="doc std std-doc">evennia/utils/create.py</span></a>. Each of the different base classes have their own creation function, like <code class="docutils literal notranslate"><span class="pre">create_account</span></code> and <code class="docutils literal notranslate"><span class="pre">create_script</span></code> etc.</p>
|
||||
</li>
|
||||
<li><p>Thirdly, you can use the <code class="docutils literal notranslate"><span class="pre">.create</span></code> method on the Typeclass itself:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">SomeTypeClass</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">key</span><span class="o">=...</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Since <code class="docutils literal notranslate"><span class="pre">.create</span></code> is a method on the typeclass, this form is useful if you want to customize how the creation process works for your custom typeclasses. Note that it returns <em>two</em> values - the <code class="docutils literal notranslate"><span class="pre">obj</span></code> is either the new object or <code class="docutils literal notranslate"><span class="pre">None</span></code>, in which case <code class="docutils literal notranslate"><span class="pre">err</span></code> should be a list of error-strings detailing what went wrong.</p>
|
||||
</li>
|
||||
<li><p>Finally, you can create objects using an in-game command, such as</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> create obj:path.to.SomeTypeClass
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>As a developer you are usually best off using the other methods, but a command is usually the only way to let regular players or builders without Python-access help build the game world.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<section id="creating-objects">
|
||||
<h2><span class="section-number">10.1. </span>Creating Objects<a class="headerlink" href="#creating-objects" title="Permalink to this headline">¶</a></h2>
|
||||
<p>An <a class="reference internal" href="../../../Components/Objects.html"><span class="doc std std-doc">Object</span></a> is one of the most common creation-types. These are entities that inherits from <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> at any distance. They have an existence in the game world and includes rooms, characters, exits, weapons, flower pots and castles.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py
|
||||
> import evennia
|
||||
> rose = evennia.create_object(key="rose")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Since we didn’t specify the <code class="docutils literal notranslate"><span class="pre">typeclass</span></code> as the first argument, the default given by <code class="docutils literal notranslate"><span class="pre">settings.BASE_OBJECT_TYPECLASS</span></code> (<code class="docutils literal notranslate"><span class="pre">typeclasses.objects.Object</span></code> out of the box) will be used.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">create_object</span></code> has <a class="reference internal" href="../../../api/evennia.utils.create.html#evennia.utils.create.create_object" title="evennia.utils.create.create_object"><span class="xref myst py py-func">a lot of options</span></a>. A more detailed example in code:</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">create_object</span><span class="p">,</span> <span class="n">search_object</span>
|
||||
|
||||
<span class="n">meadow</span> <span class="o">=</span> <span class="n">search_object</span><span class="p">(</span><span class="s2">"Meadow"</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
|
||||
|
||||
<span class="n">lasgun</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="s2">"typeclasses.objects.guns.LasGun"</span><span class="p">,</span>
|
||||
<span class="n">key</span><span class="o">=</span><span class="s2">"lasgun"</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="n">meadow</span><span class="p">,</span>
|
||||
<span class="n">attributes</span><span class="o">=</span><span class="p">[(</span><span class="s2">"desc"</span><span class="p">,</span> <span class="s2">"A fearsome Lasgun."</span><span class="p">)])</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here we set the location of a weapon as well as gave it an <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attribute</span></a> <code class="docutils literal notranslate"><span class="pre">desc</span></code>, which is what the <code class="docutils literal notranslate"><span class="pre">look</span></code> command will use when looking this and other things.</p>
|
||||
</section>
|
||||
<section id="creating-rooms-characters-and-exits">
|
||||
<h2><span class="section-number">10.2. </span>Creating Rooms, Characters and Exits<a class="headerlink" href="#creating-rooms-characters-and-exits" title="Permalink to this headline">¶</a></h2>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">Characters</span></code>, <code class="docutils literal notranslate"><span class="pre">Rooms</span></code> and <code class="docutils literal notranslate"><span class="pre">Exits</span></code> are all subclasses of <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>. So there is for example no separate <code class="docutils literal notranslate"><span class="pre">create_character</span></code>, you just create characters with <code class="docutils literal notranslate"><span class="pre">create_object</span></code> pointing to the <code class="docutils literal notranslate"><span class="pre">Character</span></code> typeclass.</p>
|
||||
<section id="linking-exits-and-rooms-in-code">
|
||||
<h3><span class="section-number">10.2.1. </span>Linking Exits and Rooms in code<a class="headerlink" href="#linking-exits-and-rooms-in-code" title="Permalink to this headline">¶</a></h3>
|
||||
<p>An <code class="docutils literal notranslate"><span class="pre">Exit</span></code> is a one-way link between rooms. For example, <code class="docutils literal notranslate"><span class="pre">east</span></code> could be an <code class="docutils literal notranslate"><span class="pre">Exit</span></code> between the <code class="docutils literal notranslate"><span class="pre">Forest</span></code> room and the <code class="docutils literal notranslate"><span class="pre">Meadow</span></code> room.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Meadow -> east -> Forest
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">east</span></code> exit has a <code class="docutils literal notranslate"><span class="pre">key</span></code> of <code class="docutils literal notranslate"><span class="pre">east</span></code>, a <code class="docutils literal notranslate"><span class="pre">location</span></code> of <code class="docutils literal notranslate"><span class="pre">Meadow</span></code> and a <code class="docutils literal notranslate"><span class="pre">destination</span></code> of <code class="docutils literal notranslate"><span class="pre">Forest</span></code>. If you wanted to be able to go back from Forest to Meadow, you’d need to create a new <code class="docutils literal notranslate"><span class="pre">Exit</span></code>, say, <code class="docutils literal notranslate"><span class="pre">west</span></code>, where <code class="docutils literal notranslate"><span class="pre">location</span></code> is <code class="docutils literal notranslate"><span class="pre">Forest</span></code> and <code class="docutils literal notranslate"><span class="pre">destination</span></code> is <code class="docutils literal notranslate"><span class="pre">Meadow</span></code>.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Meadow -> east -> Forest
|
||||
Forest -> west -> Meadow
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In-game you do this with <code class="docutils literal notranslate"><span class="pre">tunnel</span></code> and <code class="docutils literal notranslate"><span class="pre">dig</span></code> commands, bit if you want to ever set up these links in code, you can do it like this:</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">create_object</span>
|
||||
<span class="kn">from</span> <span class="nn">mygame.typeclasses</span> <span class="kn">import</span> <span class="n">rooms</span><span class="p">,</span> <span class="n">exits</span>
|
||||
|
||||
<span class="c1"># rooms</span>
|
||||
<span class="n">meadow</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">rooms</span><span class="o">.</span><span class="n">Room</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"Meadow"</span><span class="p">)</span>
|
||||
<span class="n">forest</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">rooms</span><span class="o">.</span><span class="n">Room</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"Forest"</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># exits </span>
|
||||
<span class="n">create_object</span><span class="p">(</span><span class="n">exits</span><span class="o">.</span><span class="n">Exit</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"east"</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">meadow</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">forest</span><span class="p">)</span>
|
||||
<span class="n">create_object</span><span class="p">(</span><span class="n">exits</span><span class="o">.</span><span class="n">Exit</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"west"</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">forest</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">meadow</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="creating-accounts">
|
||||
<h2><span class="section-number">10.3. </span>Creating Accounts<a class="headerlink" href="#creating-accounts" title="Permalink to this headline">¶</a></h2>
|
||||
<p>An <a class="reference internal" href="../../../Components/Accounts.html"><span class="doc std std-doc">Account</span></a> is an out-of-character (OOC) entity, with no existence in the game world.
|
||||
You can find the parent class for Accounts in <code class="docutils literal notranslate"><span class="pre">typeclasses/accounts.py</span></code>.</p>
|
||||
<p>Normally, you want to create the Account when a user authenticates. By default, this happens in the <code class="docutils literal notranslate"><span class="pre">create</span> <span class="pre">account</span></code> and <code class="docutils literal notranslate"><span class="pre">login</span></code> default commands in the <code class="docutils literal notranslate"><span class="pre">UnloggedInCmdSet</span></code>. This means that customizing this just means replacing those commands!</p>
|
||||
<p>So normally you’d modify those commands rather than make something from scratch. But here’s the principle:</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">create_account</span>
|
||||
|
||||
<span class="n">new_account</span> <span class="o">=</span> <span class="n">create_account</span><span class="p">(</span>
|
||||
<span class="n">accountname</span><span class="p">,</span> <span class="n">email</span><span class="p">,</span> <span class="n">password</span><span class="p">,</span>
|
||||
<span class="n">permissions</span><span class="o">=</span><span class="p">[</span><span class="s2">"Player"</span><span class="p">],</span>
|
||||
<span class="n">typeclass</span><span class="o">=</span><span class="s2">"typeclasses.accounts.MyAccount"</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The inputs are usually taken from the player via the command. The <code class="docutils literal notranslate"><span class="pre">email</span></code> must be given, but can be <code class="docutils literal notranslate"><span class="pre">None</span></code> if you are not using it. The <code class="docutils literal notranslate"><span class="pre">accountname</span></code> must be globally unique on the server. The <code class="docutils literal notranslate"><span class="pre">password</span></code> is stored encrypted in the database. If <code class="docutils literal notranslate"><span class="pre">typeclass</span></code> is not given, the <code class="docutils literal notranslate"><span class="pre">settings.BASE_ACCOUNT_TYPECLASS</span></code> will be used (<code class="docutils literal notranslate"><span class="pre">typeclasses.accounts.Account</span></code>).</p>
|
||||
</section>
|
||||
<section id="creating-channels">
|
||||
<h2><span class="section-number">10.4. </span>Creating Channels<a class="headerlink" href="#creating-channels" title="Permalink to this headline">¶</a></h2>
|
||||
<p>A <a class="reference internal" href="../../../Components/Channels.html"><span class="doc std std-doc">Channel</span></a> acts like a switchboard for sending in-game messages between users; like an IRC- or discord channel but inside the game.</p>
|
||||
<p>Users interact with channels via the <code class="docutils literal notranslate"><span class="pre">channel</span></code> command:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>channel/all
|
||||
channel/create channelname
|
||||
channel/who channelname
|
||||
channel/sub channel name
|
||||
...
|
||||
(see 'help channel')
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If a channel named, say, <code class="docutils literal notranslate"><span class="pre">myguild</span></code> exists, a user can send a message to it just by writing the channel name:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> myguild Hello! I have some questions ...
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Creating channels follows a familiar syntax:</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">create_channel</span>
|
||||
|
||||
<span class="n">new_channel</span> <span class="o">=</span> <span class="n">create_channel</span><span class="p">(</span><span class="n">channelname</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Channels can also be auto-created by the server by setting the <code class="docutils literal notranslate"><span class="pre">DEFAULT_CHANNELS</span></code> setting. See <a class="reference internal" href="../../../Components/Channels.html"><span class="doc std std-doc">Channels documentation</span></a> for details.</p>
|
||||
</section>
|
||||
<section id="creating-scripts">
|
||||
<h2><span class="section-number">10.5. </span>Creating Scripts<a class="headerlink" href="#creating-scripts" title="Permalink to this headline">¶</a></h2>
|
||||
<p>A <a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Script</span></a> is an entity that has no in-game location. It can be used to store arbitrary data and is often used for game systems that need persistent storage but which you can’t ‘look’ at in-game. Examples are economic systems, weather and combat handlers.</p>
|
||||
<p>Scripts are multi-use and depending on what they do, a given script can either be ‘global’ or be attached “to” another object (like a Room or Character).</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">create_script</span><span class="p">,</span> <span class="n">search_object</span>
|
||||
<span class="c1"># global script </span>
|
||||
<span class="n">new_script</span> <span class="o">=</span> <span class="n">create_script</span><span class="p">(</span><span class="s2">"typeclasses.scripts.MyScript"</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"myscript"</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># on-object script </span>
|
||||
<span class="n">meadow</span> <span class="o">=</span> <span class="n">search_object</span><span class="p">(</span><span class="s2">"Meadow"</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
|
||||
<span class="n">new_script</span> <span class="o">=</span> <span class="n">create_script</span><span class="p">(</span><span class="s2">"typeclasses.scripts.MyScripts"</span><span class="p">,</span>
|
||||
<span class="n">key</span><span class="s2">"myscript2"</span><span class="p">,</span> <span class="n">obj</span><span class="o">=</span><span class="n">meadow</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>A convenient way to create global scripts is define them in the <code class="docutils literal notranslate"><span class="pre">GLOBAL_SCRIPTS</span></code> setting; Evennia will then make sure to initialize them. Scripts also have an optional ‘timer’ component. See the dedicated <a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Script</span></a> documentation for more info.</p>
|
||||
</section>
|
||||
<section id="conclusion">
|
||||
<h2><span class="section-number">10.6. </span>Conclusion<a class="headerlink" href="#conclusion" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Any game will need peristent storage of data. This was a quick run-down of how to create each default type of typeclassed entity. If you make your own typeclasses (as children of the default ones), you create them in the same way.</p>
|
||||
<p>Next we’ll learn how to find them again by <em>searching</em> for them in the database.</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="Beginner-Tutorial-Searching-Things.html" title="11. Searching for things"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-More-on-Commands.html" title="9. Parsing Command input"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">10. </span>Creating things</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,550 @@
|
|||
|
||||
<!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>12. Advanced searching - Django Database queries — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="13. Building a chair you can sit on" href="Beginner-Tutorial-Making-A-Sittable-Object.html" />
|
||||
<link rel="prev" title="11. Searching for things" href="Beginner-Tutorial-Searching-Things.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Making-A-Sittable-Object.html" title="13. Building a chair you can sit on"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Searching-Things.html" title="11. Searching for things"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">12. </span>Advanced searching - Django Database queries</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">12. Advanced searching - Django Database queries</a><ul>
|
||||
<li><a class="reference internal" href="#queryset-field-lookups">12.1. Queryset field lookups</a></li>
|
||||
<li><a class="reference internal" href="#lets-get-that-werewolf">12.2. Let’s get that werewolf …</a></li>
|
||||
<li><a class="reference internal" href="#queries-with-or-or-not">12.3. Queries with OR or NOT</a></li>
|
||||
<li><a class="reference internal" href="#annotations">12.4. Annotations</a></li>
|
||||
<li><a class="reference internal" href="#f-objects">12.5. F-objects</a></li>
|
||||
<li><a class="reference internal" href="#grouping-and-returning-only-certain-properties">12.6. Grouping and returning only certain properties</a></li>
|
||||
<li><a class="reference internal" href="#conclusions">12.7. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Searching-Things.html"
|
||||
title="previous chapter"><span class="section-number">11. </span>Searching for things</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Making-A-Sittable-Object.html"
|
||||
title="next chapter"><span class="section-number">13. </span>Building a chair you can sit on</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Django-queries.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="advanced-searching-django-database-queries">
|
||||
<h1><span class="section-number">12. </span>Advanced searching - Django Database queries<a class="headerlink" href="#advanced-searching-django-database-queries" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="admonition important">
|
||||
<p class="admonition-title">Important</p>
|
||||
<p>More advanced lesson!</p>
|
||||
<p>Learning about Django’s query language is very useful once you start doing more
|
||||
advanced things in Evennia. But it’s not strictly needed out the box and can be
|
||||
a little overwhelming for a first reading. So if you are new to Python and
|
||||
Evennia, feel free to just skim this lesson and refer back to it later when
|
||||
you’ve gained more experience.</p>
|
||||
</div>
|
||||
<p>The search functions and methods we used in the previous lesson are enough for most cases.
|
||||
But sometimes you need to be more specific:</p>
|
||||
<ul class="simple">
|
||||
<li><p>You want to find all <code class="docutils literal notranslate"><span class="pre">Characters</span></code> …</p></li>
|
||||
<li><p>… who are in Rooms tagged as <code class="docutils literal notranslate"><span class="pre">moonlit</span></code> …</p></li>
|
||||
<li><p>… <em>and</em> who has the Attribute <code class="docutils literal notranslate"><span class="pre">lycantrophy</span></code> with a level higher than 2 …</p></li>
|
||||
<li><p>… because they should immediately transform to werewolves!</p></li>
|
||||
</ul>
|
||||
<p>In principle you could achieve this with the existing search functions combined with a lot of loops
|
||||
and if statements. But for something non-standard like this, querying the database directly will be
|
||||
much more efficient.</p>
|
||||
<p>Evennia uses <a class="reference external" href="https://www.djangoproject.com/">Django</a> to handle its connection to the database.
|
||||
A <a class="reference external" href="https://docs.djangoproject.com/en/3.0/ref/models/querysets/">django queryset</a> represents a database query. One can add querysets together to build ever-more complicated queries. Only when you are trying to use the results of the queryset will it actually call the database.</p>
|
||||
<p>The normal way to build a queryset is to define what class of entity you want to search by getting its <code class="docutils literal notranslate"><span class="pre">.objects</span></code> resource, and then call various methods on that. We’ve seen variants of this before:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>all_weapons = Weapon.objects.all()
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This is now a queryset representing all instances of <code class="docutils literal notranslate"><span class="pre">Weapon</span></code>. If <code class="docutils literal notranslate"><span class="pre">Weapon</span></code> had a subclass <code class="docutils literal notranslate"><span class="pre">Cannon</span></code> and we only wanted the cannons, we would do</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>all_cannons = Cannon.objects.all()
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Note that <code class="docutils literal notranslate"><span class="pre">Weapon</span></code> and <code class="docutils literal notranslate"><span class="pre">Cannon</span></code> are <em>different</em> typeclasses. This means that you won’t find any <code class="docutils literal notranslate"><span class="pre">Weapon</span></code>-typeclassed results in <code class="docutils literal notranslate"><span class="pre">all_cannons</span></code>. Vice-versa, you won’t find any <code class="docutils literal notranslate"><span class="pre">Cannon</span></code>-typeclassed results in <code class="docutils literal notranslate"><span class="pre">all_weapons</span></code>. This may not be what you expect.</p>
|
||||
<p>If you want to get all entities with typeclass <code class="docutils literal notranslate"><span class="pre">Weapon</span></code> <em>as well</em> as all the subclasses of <code class="docutils literal notranslate"><span class="pre">Weapon</span></code>, such as <code class="docutils literal notranslate"><span class="pre">Cannon</span></code>, you need to use the <code class="docutils literal notranslate"><span class="pre">_family</span></code> type of query:</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">_family</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">all_family</span></code> and <code class="docutils literal notranslate"><span class="pre">filter_family</span></code> (as well as <code class="docutils literal notranslate"><span class="pre">get_family</span></code> for getting exactly one result) are Evennia-specific. They are not part of regular Django.</p>
|
||||
</aside>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>really_all_weapons = Weapon.objects.all_family()
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This result now contains both <code class="docutils literal notranslate"><span class="pre">Weapon</span></code> and <code class="docutils literal notranslate"><span class="pre">Cannon</span></code> instances (and any other
|
||||
entities whose typeclasses inherit at any distance from <code class="docutils literal notranslate"><span class="pre">Weapon</span></code>, like <code class="docutils literal notranslate"><span class="pre">Musket</span></code> or
|
||||
<code class="docutils literal notranslate"><span class="pre">Sword</span></code>).</p>
|
||||
<p>To limit your search by other criteria than the Typeclass you need to use <code class="docutils literal notranslate"><span class="pre">.filter</span></code>
|
||||
(or <code class="docutils literal notranslate"><span class="pre">.filter_family</span></code>) instead:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>roses = Flower.objects.filter(db_key="rose")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This is a queryset representing all flowers having a <code class="docutils literal notranslate"><span class="pre">db_key</span></code> equal to <code class="docutils literal notranslate"><span class="pre">"rose"</span></code>.
|
||||
Since this is a queryset you can keep adding to it; this will act as an <code class="docutils literal notranslate"><span class="pre">AND</span></code> condition.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>local_roses = roses.filter(db_location=myroom)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We could also have written this in one statement:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>local_roses = Flower.objects.filter(db_key="rose", db_location=myroom)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We can also <code class="docutils literal notranslate"><span class="pre">.exclude</span></code> something from results</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>local_non_red_roses = local_roses.exclude(db_key="red_rose")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>It’s important to note that we haven’t called the database yet! Not until we
|
||||
actually try to examine the result will the database be called. Here the
|
||||
database is called when we try to loop over it (because now we need to actually
|
||||
get results out of it to be able to loop):</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>for rose in local_non_red_roses:
|
||||
print(rose)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>From now on, the queryset is <em>evaluated</em> and we can’t keep adding more queries to it - we’d need to create a new queryset if we wanted to find some other result. Other ways to evaluate the queryset is to print it, convert it to a list with <code class="docutils literal notranslate"><span class="pre">list()</span></code> and otherwise try to access its results.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">database fields</p>
|
||||
<p>Each database table have only a few fields. For <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>, the most common ones are <code class="docutils literal notranslate"><span class="pre">db_key</span></code>, <code class="docutils literal notranslate"><span class="pre">db_location</span></code> and <code class="docutils literal notranslate"><span class="pre">db_destination</span></code>. When accessing them they are normally accessed just as <code class="docutils literal notranslate"><span class="pre">obj.key</span></code>, <code class="docutils literal notranslate"><span class="pre">obj.location</span></code> and <code class="docutils literal notranslate"><span class="pre">obj.destination</span></code>. You only need to remember the <code class="docutils literal notranslate"><span class="pre">db_</span></code> when using them in database queries. The object description, <code class="docutils literal notranslate"><span class="pre">obj.db.desc</span></code> is not such a hard-coded field, but one of many
|
||||
Attributes attached to the Object.</p>
|
||||
</aside>
|
||||
<p>Note how we use <code class="docutils literal notranslate"><span class="pre">db_key</span></code> and <code class="docutils literal notranslate"><span class="pre">db_location</span></code>. This is the actual names of these database fields. By convention Evennia uses <code class="docutils literal notranslate"><span class="pre">db_</span></code> in front of every database field. When you use the normal Evennia search helpers and objects you can skip the <code class="docutils literal notranslate"><span class="pre">db_</span></code> but here we are calling the database directly and need to use the ‘real’ names.</p>
|
||||
<p>Here are the most commonly used methods to use with the <code class="docutils literal notranslate"><span class="pre">objects</span></code> managers:</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">filter</span></code> - query for a listing of objects based on search criteria. Gives empty queryset if none
|
||||
were found.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">get</span></code> - query for a single match - raises exception if none were found, or more than one was
|
||||
found.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">all</span></code> - get all instances of the particular type.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">filter_family</span></code> - like <code class="docutils literal notranslate"><span class="pre">filter</span></code>, but search all subclasses as well.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">get_family</span></code> - like <code class="docutils literal notranslate"><span class="pre">get</span></code>, but search all subclasses as well.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">all_family</span></code> - like <code class="docutils literal notranslate"><span class="pre">all</span></code>, but return entities of all subclasses as well.</p></li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<div><p>All of Evennia search functions use querysets under the hood. The <code class="docutils literal notranslate"><span class="pre">evennia.search_*</span></code> functions actually return querysets (we have just been treating them as lists so far). This means you could in principle add a <code class="docutils literal notranslate"><span class="pre">.filter</span></code> query to the result of <code class="docutils literal notranslate"><span class="pre">evennia.search_object</span></code> to further refine the search.</p>
|
||||
</div></blockquote>
|
||||
<section id="queryset-field-lookups">
|
||||
<h2><span class="section-number">12.1. </span>Queryset field lookups<a class="headerlink" href="#queryset-field-lookups" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Above we found roses with exactly the <code class="docutils literal notranslate"><span class="pre">db_key</span></code> <code class="docutils literal notranslate"><span class="pre">"rose"</span></code>. This is an <em>exact</em> match that is <em>case sensitive</em>,
|
||||
so it would not find <code class="docutils literal notranslate"><span class="pre">"Rose"</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># this is case-sensitive and the same as =</span>
|
||||
<span class="n">roses</span> <span class="o">=</span> <span class="n">Flower</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">db_key__exact</span><span class="o">=</span><span class="s2">"rose"</span>
|
||||
|
||||
<span class="c1"># the i means it's case-insensitive</span>
|
||||
<span class="n">roses</span> <span class="o">=</span> <span class="n">Flower</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">db_key__iexact</span><span class="o">=</span><span class="s2">"rose"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The Django field query language uses <code class="docutils literal notranslate"><span class="pre">__</span></code> similarly to how Python uses <code class="docutils literal notranslate"><span class="pre">.</span></code> to access resources. This
|
||||
is because <code class="docutils literal notranslate"><span class="pre">.</span></code> is not allowed in a function keyword.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">roses</span> <span class="o">=</span> <span class="n">Flower</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">db_key__icontains</span><span class="o">=</span><span class="s2">"rose"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This will find all flowers whose name contains the string <code class="docutils literal notranslate"><span class="pre">"rose"</span></code>, like <code class="docutils literal notranslate"><span class="pre">"roses"</span></code>, <code class="docutils literal notranslate"><span class="pre">"wild</span> <span class="pre">rose"</span></code> etc. The <code class="docutils literal notranslate"><span class="pre">i</span></code> in the beginning makes the search case-insensitive. Other useful variations to use
|
||||
are <code class="docutils literal notranslate"><span class="pre">__istartswith</span></code> and <code class="docutils literal notranslate"><span class="pre">__iendswith</span></code>. You can also use <code class="docutils literal notranslate"><span class="pre">__gt</span></code>, <code class="docutils literal notranslate"><span class="pre">__ge</span></code> for “greater-than”/“greater-or-equal-than” comparisons (same for <code class="docutils literal notranslate"><span class="pre">__lt</span></code> and <code class="docutils literal notranslate"><span class="pre">__le</span></code>). There is also <code class="docutils literal notranslate"><span class="pre">__in</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">swords</span> <span class="o">=</span> <span class="n">Weapons</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">db_key__in</span><span class="o">=</span><span class="p">(</span><span class="s2">"rapier"</span><span class="p">,</span> <span class="s2">"two-hander"</span><span class="p">,</span> <span class="s2">"shortsword"</span><span class="p">))</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>One also uses <code class="docutils literal notranslate"><span class="pre">__</span></code> to access foreign objects like Tags. Let’s for example assume
|
||||
this is how we have identified mages:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">char</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"mage"</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"profession"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now, in this case we already have an Evennia helper to do this search:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">mages</span> <span class="o">=</span> <span class="n">evennia</span><span class="o">.</span><span class="n">search_tags</span><span class="p">(</span><span class="s2">"mage"</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"profession"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here is what it would look as a query if you were only looking for Vampire mages:</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Breaking lines of code</p>
|
||||
<p>In Python you can wrap code in <code class="docutils literal notranslate"><span class="pre">(...)</span></code> to break it over multiple lines. Doing this doesn’t affect functionality, but can make it easier to read.</p>
|
||||
</aside>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">sparkly_mages</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="n">Vampire</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span>
|
||||
<span class="n">db_tags__db_key</span><span class="o">=</span><span class="s2">"mage"</span><span class="p">,</span>
|
||||
<span class="n">db_tags__db_category</span><span class="o">=</span><span class="s2">"profession"</span><span class="p">)</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This looks at the <code class="docutils literal notranslate"><span class="pre">db_tags</span></code> field on the <code class="docutils literal notranslate"><span class="pre">Vampire</span></code> and filters on the values of each tag’s
|
||||
<code class="docutils literal notranslate"><span class="pre">db_key</span></code> and <code class="docutils literal notranslate"><span class="pre">db_category</span></code> together.</p>
|
||||
<p>For more field lookups, see the <a class="reference external" href="https://docs.djangoproject.com/en/3.0/ref/models/querysets/#field-lookups">django docs</a> on the subject.</p>
|
||||
</section>
|
||||
<section id="lets-get-that-werewolf">
|
||||
<h2><span class="section-number">12.2. </span>Let’s get that werewolf …<a class="headerlink" href="#lets-get-that-werewolf" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Let’s see if we can make a query for the werewolves in the moonlight we mentioned at the beginning
|
||||
of this lesson.</p>
|
||||
<p>Firstly, we make ourselves and our current location match the criteria, so we can test:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py here.tags.add("moonlit")
|
||||
> py me.db.lycantrophy = 3
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This is an example of a more complex query. We’ll consider it an example of what is
|
||||
possible.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span></pre></div></td><td class="code"><div><pre><span></span><span class="kn">from</span> <span class="nn">typeclasses.characters</span> <span class="kn">import</span> <span class="n">Character</span>
|
||||
|
||||
<span class="n">will_transform</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="hll"> <span class="n">Character</span><span class="o">.</span><span class="n">objects</span>
|
||||
</span> <span class="o">.</span><span class="n">filter</span><span class="p">(</span>
|
||||
<span class="hll"> <span class="n">db_location__db_tags__db_key__iexact</span><span class="o">=</span><span class="s2">"moonlit"</span><span class="p">,</span>
|
||||
</span><span class="hll"> <span class="n">db_attributes__db_key</span><span class="o">=</span><span class="s2">"lycantrophy"</span><span class="p">,</span>
|
||||
</span><span class="hll"> <span class="n">db_attributes__db_value__gt</span><span class="o">=</span><span class="mi">2</span>
|
||||
</span> <span class="p">)</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Attributes vs database fields</p>
|
||||
<p>Don’t confuse database fields with <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attributes</span></a> you set via <code class="docutils literal notranslate"><span class="pre">obj.db.attr</span> <span class="pre">=</span> <span class="pre">'foo'</span></code> or <code class="docutils literal notranslate"><span class="pre">obj.attributes.add()</span></code>. Attributes are custom database entities <em>linked</em> to an object. They are not separate fields <em>on</em> that object like <code class="docutils literal notranslate"><span class="pre">db_key</span></code> or <code class="docutils literal notranslate"><span class="pre">db_location</span></code> are.</p>
|
||||
</aside>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 4</strong> We want to find <code class="docutils literal notranslate"><span class="pre">Character</span></code>s, so we access <code class="docutils literal notranslate"><span class="pre">.objects</span></code> on the <code class="docutils literal notranslate"><span class="pre">Character</span></code> typeclass.</p></li>
|
||||
<li><p>We start to filter …</p>
|
||||
<ul>
|
||||
<li><p><strong>Line 6</strong>: … by accessing the <code class="docutils literal notranslate"><span class="pre">db_location</span></code> field (usually this is a Room)</p>
|
||||
<ul>
|
||||
<li><p>… and on that location, we get the value of <code class="docutils literal notranslate"><span class="pre">db_tags</span></code> (this is a <em>many-to-many</em> database field
|
||||
that we can treat like an object for this purpose; it references all Tags on the location)</p></li>
|
||||
<li><p>… and from those <code class="docutils literal notranslate"><span class="pre">Tags</span></code>, we looking for <code class="docutils literal notranslate"><span class="pre">Tags</span></code> whose <code class="docutils literal notranslate"><span class="pre">db_key</span></code> is “monlit” (non-case sensitive).</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><strong>Line 7</strong>: … We also want only Characters with <code class="docutils literal notranslate"><span class="pre">Attributes</span></code> whose <code class="docutils literal notranslate"><span class="pre">db_key</span></code> is exactly <code class="docutils literal notranslate"><span class="pre">"lycantrophy"</span></code></p></li>
|
||||
<li><p><strong>Line 8</strong> :… at the same time as the <code class="docutils literal notranslate"><span class="pre">Attribute</span></code>’s <code class="docutils literal notranslate"><span class="pre">db_value</span></code> is greater-than 2.</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Running this query makes our newly lycantrophic Character appear in <code class="docutils literal notranslate"><span class="pre">will_transform</span></code> so we
|
||||
know to transform it. Success!</p>
|
||||
</section>
|
||||
<section id="queries-with-or-or-not">
|
||||
<h2><span class="section-number">12.3. </span>Queries with OR or NOT<a class="headerlink" href="#queries-with-or-or-not" title="Permalink to this headline">¶</a></h2>
|
||||
<p>All examples so far used <code class="docutils literal notranslate"><span class="pre">AND</span></code> relations. The arguments to <code class="docutils literal notranslate"><span class="pre">.filter</span></code> are added together with <code class="docutils literal notranslate"><span class="pre">AND</span></code>
|
||||
(“we want tag room to be “monlit” <em>and</em> lycantrhopy be > 2”).</p>
|
||||
<p>For queries using <code class="docutils literal notranslate"><span class="pre">OR</span></code> and <code class="docutils literal notranslate"><span class="pre">NOT</span></code> we need Django’s <a class="reference external" href="https://docs.djangoproject.com/en/4.1/topics/db/queries/#complex-lookups-with-q-objects">Q object</a>. It is imported from Django directly:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>from django.db.models import Q
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">Q</span></code> is an object that is created with the same arguments as <code class="docutils literal notranslate"><span class="pre">.filter</span></code>, for example</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Q(db_key="foo")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can then use this <code class="docutils literal notranslate"><span class="pre">Q</span></code> instance as argument in a <code class="docutils literal notranslate"><span class="pre">filter</span></code>:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>q1 = Q(db_key="foo")
|
||||
Character.objects.filter(q1)
|
||||
# this is the same as
|
||||
Character.objects.filter(db_key="foo")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The useful thing about <code class="docutils literal notranslate"><span class="pre">Q</span></code> is that these objects can be chained together with special symbols (bit operators): <code class="docutils literal notranslate"><span class="pre">|</span></code> for <code class="docutils literal notranslate"><span class="pre">OR</span></code> and <code class="docutils literal notranslate"><span class="pre">&</span></code> for <code class="docutils literal notranslate"><span class="pre">AND</span></code>. A tilde <code class="docutils literal notranslate"><span class="pre">~</span></code> in front negates the expression inside the <code class="docutils literal notranslate"><span class="pre">Q</span></code> and thus
|
||||
works like <code class="docutils literal notranslate"><span class="pre">NOT</span></code>.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>q1 = Q(db_key="Dalton")
|
||||
q2 = Q(db_location=prison)
|
||||
Character.objects.filter(q1 | ~q2)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Would get all Characters that are either named “Dalton” <em>or</em> which is <em>not</em> in prison. The result is a mix
|
||||
of Daltons and non-prisoners.</p>
|
||||
<p>Let us expand our original werewolf query. Not only do we want to find all Characters in a moonlit room with a certain level of <code class="docutils literal notranslate"><span class="pre">lycanthrophy</span></code> - we decide that if they have been <em>newly bitten</em>, they should also turn, <em>regardless</em> of their lycantrophy level (more dramatic that way!).</p>
|
||||
<p>Let’s say that getting bitten means that you’ll get assigned a Tag <code class="docutils literal notranslate"><span class="pre">recently_bitten</span></code>.</p>
|
||||
<p>This is how we’d change our query:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db.models</span> <span class="kn">import</span> <span class="n">Q</span>
|
||||
|
||||
<span class="n">will_transform</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="n">Character</span><span class="o">.</span><span class="n">objects</span>
|
||||
<span class="o">.</span><span class="n">filter</span><span class="p">(</span>
|
||||
<span class="n">Q</span><span class="p">(</span><span class="n">db_location__db_tags__db_key__iexact</span><span class="o">=</span><span class="s2">"moonlit"</span><span class="p">)</span>
|
||||
<span class="o">&</span> <span class="p">(</span>
|
||||
<span class="n">Q</span><span class="p">(</span><span class="n">db_attributes__db_key</span><span class="o">=</span><span class="s2">"lycantrophy"</span><span class="p">,</span>
|
||||
<span class="n">db_attributes__db_value__gt</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
|
||||
<span class="o">|</span> <span class="n">Q</span><span class="p">(</span><span class="n">db_tags__db_key__iexact</span><span class="o">=</span><span class="s2">"recently_bitten"</span><span class="p">)</span>
|
||||
<span class="p">))</span>
|
||||
<span class="o">.</span><span class="n">distinct</span><span class="p">()</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>That’s quite compact. It may be easier to see what’s going on if written this way:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db.models</span> <span class="kn">import</span> <span class="n">Q</span>
|
||||
|
||||
<span class="n">q_moonlit</span> <span class="o">=</span> <span class="n">Q</span><span class="p">(</span><span class="n">db_location__db_tags__db_key__iexact</span><span class="o">=</span><span class="s2">"moonlit"</span><span class="p">)</span>
|
||||
<span class="n">q_lycantropic</span> <span class="o">=</span> <span class="n">Q</span><span class="p">(</span><span class="n">db_attributes__db_key</span><span class="o">=</span><span class="s2">"lycantrophy"</span><span class="p">,</span> <span class="n">db_attributes__db_value__gt</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
|
||||
<span class="n">q_recently_bitten</span> <span class="o">=</span> <span class="n">Q</span><span class="p">(</span><span class="n">db_tags__db_key__iexact</span><span class="o">=</span><span class="s2">"recently_bitten"</span><span class="p">)</span>
|
||||
|
||||
<span class="n">will_transform</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="n">Character</span><span class="o">.</span><span class="n">objects</span>
|
||||
<span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">q_moonlit</span> <span class="o">&</span> <span class="p">(</span><span class="n">q_lycantropic</span> <span class="o">|</span> <span class="n">q_recently_bitten</span><span class="p">))</span>
|
||||
<span class="o">.</span><span class="n">distinct</span><span class="p">()</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">SQL</p>
|
||||
<p>These Python structures are internally converted to SQL, the native language of
|
||||
the database. If you are familiar with SQL, these are many-to-many tables
|
||||
joined with <code class="docutils literal notranslate"><span class="pre">LEFT</span> <span class="pre">OUTER</span> <span class="pre">JOIN</span></code>, which may lead to multiple merged rows combining
|
||||
the same object with different relations.</p>
|
||||
</aside>
|
||||
<p>This reads as “Find all Characters in a moonlit room that either has the
|
||||
Attribute <code class="docutils literal notranslate"><span class="pre">lycantrophy</span></code> higher than two, <em>or</em> which has the Tag
|
||||
<code class="docutils literal notranslate"><span class="pre">recently_bitten</span></code>”. With an OR-query like this it’s possible to find the same
|
||||
Character via different paths, so we add <code class="docutils literal notranslate"><span class="pre">.distinct()</span></code> at the end. This makes
|
||||
sure that there is only one instance of each Character in the result.</p>
|
||||
</section>
|
||||
<section id="annotations">
|
||||
<h2><span class="section-number">12.4. </span>Annotations<a class="headerlink" href="#annotations" title="Permalink to this headline">¶</a></h2>
|
||||
<p>What if we wanted to filter on some condition that isn’t represented easily by a
|
||||
field on the object? An example would wanting to find rooms only containing <em>five or more objects</em>.</p>
|
||||
<p>We <em>could</em> do it like this (don’t actually do it this way!):</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typeclasses.rooms</span> <span class="kn">import</span> <span class="n">Room</span>
|
||||
|
||||
<span class="n">all_rooms</span> <span class="o">=</span> <span class="n">Rooms</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
|
||||
|
||||
<span class="n">rooms_with_five_objects</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">for</span> <span class="n">room</span> <span class="ow">in</span> <span class="n">all_rooms</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">room</span><span class="o">.</span><span class="n">contents</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">5</span><span class="p">:</span>
|
||||
<span class="n">rooms_with_five_objects</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">room</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">list.append, extend and .pop</p>
|
||||
<p>Use <code class="docutils literal notranslate"><span class="pre">mylist.append(obj)</span></code> to add new items to a list. Use <code class="docutils literal notranslate"><span class="pre">mylist.extend(another_list))</span></code> or <code class="docutils literal notranslate"><span class="pre">list1</span> <span class="pre">+</span> <span class="pre">list2</span></code> to merge two lists together. Use <code class="docutils literal notranslate"><span class="pre">mylist.pop()</span></code> to remove an item from the end or <code class="docutils literal notranslate"><span class="pre">.pop(0)</span></code> to remove from the beginning of the list. Remember all indices start from <code class="docutils literal notranslate"><span class="pre">0</span></code> in Python.</p>
|
||||
</aside>
|
||||
<p>Above we get <em>all</em> rooms and then use <code class="docutils literal notranslate"><span class="pre">list.append()</span></code> to keep adding the right
|
||||
rooms to an ever-growing list. This is <em>not</em> a good idea, once your database
|
||||
grows this will be unnecessarily compute-intensive. It’s much better to query the
|
||||
database directly</p>
|
||||
<p><em>Annotations</em> allow you to set a ‘variable’ inside the query that you can then
|
||||
access from other parts of the query. Let’s do the same example as before
|
||||
directly in the database:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
|
||||
<span class="normal">2</span>
|
||||
<span class="normal">3</span>
|
||||
<span class="normal">4</span>
|
||||
<span class="normal">5</span>
|
||||
<span class="normal">6</span>
|
||||
<span class="normal">7</span>
|
||||
<span class="normal">8</span>
|
||||
<span class="normal">9</span></pre></div></td><td class="code"><div><pre><span></span><span class="kn">from</span> <span class="nn">typeclasses.rooms</span> <span class="kn">import</span> <span class="n">Room</span>
|
||||
<span class="kn">from</span> <span class="nn">django.db.models</span> <span class="kn">import</span> <span class="n">Count</span>
|
||||
|
||||
<span class="n">rooms</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="n">Room</span><span class="o">.</span><span class="n">objects</span>
|
||||
<span class="hll"> <span class="o">.</span><span class="n">annotate</span><span class="p">(</span>
|
||||
</span> <span class="n">num_objects</span><span class="o">=</span><span class="n">Count</span><span class="p">(</span><span class="s1">'locations_set'</span><span class="p">))</span>
|
||||
<span class="hll"> <span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">num_objects__gte</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>
|
||||
</span><span class="p">)</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">locations_set</p>
|
||||
<p>Note the use of <code class="docutils literal notranslate"><span class="pre">locations_set</span></code> in that <code class="docutils literal notranslate"><span class="pre">Count</span></code>. The <code class="docutils literal notranslate"><span class="pre">*s_set</span></code> is a back-reference automatically created by Django. In this case it allows you to find all objects that <em>has the current object as location</em>.</p>
|
||||
</aside>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">Count</span></code> is a Django class for counting the number of things in the database.</p>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 6-7</strong>: Here we first create an annotation <code class="docutils literal notranslate"><span class="pre">num_objects</span></code> of type <code class="docutils literal notranslate"><span class="pre">Count</span></code>. It creates an in-database function that will count the number of results inside the database. The fact annotation means that now <code class="docutils literal notranslate"><span class="pre">num_objects</span></code> is avaiable to be used in other parts of the query.</p></li>
|
||||
<li><p><strong>Line 8</strong> We filter on this annotation, using the name <code class="docutils literal notranslate"><span class="pre">num_objects</span></code> as something we
|
||||
can filter for. We use <code class="docutils literal notranslate"><span class="pre">num_objects__gte=5</span></code> which means that <code class="docutils literal notranslate"><span class="pre">num_objects</span></code>
|
||||
should be greater than or equal to 5.</p></li>
|
||||
</ul>
|
||||
<p>Annotations can be a little harder to get one’s head around but much more efficient than lopping over all objects in Python.</p>
|
||||
</section>
|
||||
<section id="f-objects">
|
||||
<h2><span class="section-number">12.5. </span>F-objects<a class="headerlink" href="#f-objects" title="Permalink to this headline">¶</a></h2>
|
||||
<p>What if we wanted to compare two dynamic parameters against one another in a
|
||||
query? For example, what if instead of having 5 or more objects, we only wanted
|
||||
objects that had a bigger inventory than they had tags (silly example, but …)?</p>
|
||||
<p>This can be with Django’s <a class="reference external" href="https://docs.djangoproject.com/en/4.1/ref/models/expressions/#f-expressions">F objects</a>. So-called F expressions allow you to do a query that looks at a value of each object in the database.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db.models</span> <span class="kn">import</span> <span class="n">Count</span><span class="p">,</span> <span class="n">F</span>
|
||||
<span class="kn">from</span> <span class="nn">typeclasses.rooms</span> <span class="kn">import</span> <span class="n">Room</span>
|
||||
|
||||
<span class="n">result</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="n">Room</span><span class="o">.</span><span class="n">objects</span>
|
||||
<span class="o">.</span><span class="n">annotate</span><span class="p">(</span>
|
||||
<span class="n">num_objects</span><span class="o">=</span><span class="n">Count</span><span class="p">(</span><span class="s1">'locations_set'</span><span class="p">),</span>
|
||||
<span class="n">num_tags</span><span class="o">=</span><span class="n">Count</span><span class="p">(</span><span class="s1">'db_tags'</span><span class="p">))</span>
|
||||
<span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">num_objects__gt</span><span class="o">=</span><span class="n">F</span><span class="p">(</span><span class="s1">'num_tags'</span><span class="p">))</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here we used <code class="docutils literal notranslate"><span class="pre">.annotate</span></code> to create two in-query ‘variables’ <code class="docutils literal notranslate"><span class="pre">num_objects</span></code> and <code class="docutils literal notranslate"><span class="pre">num_tags</span></code>. We then
|
||||
directly use these results in the filter. Using <code class="docutils literal notranslate"><span class="pre">F()</span></code> allows for also the right-hand-side of the filter
|
||||
condition to be calculated on the fly, completely within the database.</p>
|
||||
</section>
|
||||
<section id="grouping-and-returning-only-certain-properties">
|
||||
<h2><span class="section-number">12.6. </span>Grouping and returning only certain properties<a class="headerlink" href="#grouping-and-returning-only-certain-properties" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Suppose you used tags to mark someone belonging to an organization. Now you want to make a list and need to get the membership count of every organization all at once.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">.annotate</span></code>, <code class="docutils literal notranslate"><span class="pre">.values_list</span></code>, and <code class="docutils literal notranslate"><span class="pre">.order_by</span></code> queryset methods are useful for this. Normally when you run a <code class="docutils literal notranslate"><span class="pre">.filter</span></code>, what you get back is a bunch of full typeclass instances, like roses or swords. Using <code class="docutils literal notranslate"><span class="pre">.values_list</span></code> you can instead choose to only get back certain properties on objects. The <code class="docutils literal notranslate"><span class="pre">.order_by</span></code> method finally allows for sorting the results according to some criterion:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
|
||||
<span class="normal">2</span>
|
||||
<span class="normal">3</span>
|
||||
<span class="normal">4</span>
|
||||
<span class="normal">5</span>
|
||||
<span class="normal">6</span>
|
||||
<span class="normal">7</span>
|
||||
<span class="normal">8</span>
|
||||
<span class="normal">9</span></pre></div></td><td class="code"><div><pre><span></span><span class="kn">from</span> <span class="nn">django.db.models</span> <span class="kn">import</span> <span class="n">Count</span>
|
||||
<span class="kn">from</span> <span class="nn">typeclasses.rooms</span> <span class="kn">import</span> <span class="n">Room</span>
|
||||
|
||||
<span class="n">result</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="n">Character</span><span class="o">.</span><span class="n">objects</span>
|
||||
<span class="hll"> <span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">db_tags__db_category</span><span class="o">=</span><span class="s2">"organization"</span><span class="p">)</span>
|
||||
</span><span class="hll"> <span class="o">.</span><span class="n">annotate</span><span class="p">(</span><span class="n">tagcount</span><span class="o">=</span><span class="n">Count</span><span class="p">(</span><span class="s1">'id'</span><span class="p">))</span>
|
||||
</span><span class="hll"> <span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="s1">'-tagcount'</span><span class="p">))</span>
|
||||
</span><span class="hll"> <span class="o">.</span><span class="n">values_list</span><span class="p">(</span><span class="s1">'db_tags__db_key'</span><span class="p">,</span> <span class="s2">"tagcount"</span><span class="p">)</span>
|
||||
</span></pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<p>Here we fetch all Characters who …</p>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 6</strong>: … has a tag of category “organization” on them</p></li>
|
||||
<li><p><strong>Line 7</strong>:… along the way we count how many different Characters (each <code class="docutils literal notranslate"><span class="pre">id</span></code> is unique) we find for each organization and store it in a ‘variable’ <code class="docutils literal notranslate"><span class="pre">tagcount</span></code> using <code class="docutils literal notranslate"><span class="pre">.annotate</span></code> and <code class="docutils literal notranslate"><span class="pre">Count</span></code></p></li>
|
||||
<li><p><strong>Line 8</strong>: … we use this count to sort the result in descending order of <code class="docutils literal notranslate"><span class="pre">tagcount</span></code> (descending because there is a minus sign, default is increasing order but we want the most popular organization to be first).</p></li>
|
||||
<li><p><strong>Line 9</strong>: … and finally we make sure to only return exactly the properties we want, namely the name of the organization tag and how many matches we found for that organization. For this we use the <code class="docutils literal notranslate"><span class="pre">values_list</span></code> method on the queryset. This will evaluate the queryset immediately.</p></li>
|
||||
</ul>
|
||||
<p>The result will be a list of tuples ordered in descending order by the number of matches,
|
||||
in a format like the following:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[</span>
|
||||
<span class="p">(</span><span class="s1">'Griatch'</span><span class="n">s</span> <span class="n">poets</span> <span class="n">society</span><span class="s1">', 3872),</span>
|
||||
<span class="p">(</span><span class="s2">"Chainsol's Ainneve Testers"</span><span class="p">,</span> <span class="mi">2076</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"Blaufeuer's Whitespace Fixers"</span><span class="p">,</span> <span class="mi">1903</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"Volund's Bikeshed Design Crew"</span><span class="p">,</span> <span class="mi">1764</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"Tehom's Glorious Misanthropes"</span><span class="p">,</span> <span class="mi">1763</span><span class="p">)</span>
|
||||
<span class="p">]</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="conclusions">
|
||||
<h2><span class="section-number">12.7. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>We have covered a lot of ground in this lesson and covered several more complex topics. Knowing how to query using Django is a powerful skill to have.</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="Beginner-Tutorial-Making-A-Sittable-Object.html" title="13. Building a chair you can sit on"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Searching-Things.html" title="11. Searching for things"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">12. </span>Advanced searching - Django Database queries</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
|
||||
<!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>6. Overview of the Evennia library — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="7. Making objects persistent" href="Beginner-Tutorial-Learning-Typeclasses.html" />
|
||||
<link rel="prev" title="5. Introduction to Python classes and objects" href="Beginner-Tutorial-Python-classes-and-objects.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Learning-Typeclasses.html" title="7. Making objects persistent"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Python-classes-and-objects.html" title="5. Introduction to Python classes and objects"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">6. </span>Overview of the Evennia library</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">6. Overview of the Evennia library</a><ul>
|
||||
<li><a class="reference internal" href="#where-is-it">6.1. Where is it?</a></li>
|
||||
<li><a class="reference internal" href="#an-example-of-exploring-the-library">6.2. An example of exploring the library</a></li>
|
||||
<li><a class="reference internal" href="#conclusions">6.3. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Python-classes-and-objects.html"
|
||||
title="previous chapter"><span class="section-number">5. </span>Introduction to Python classes and objects</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Learning-Typeclasses.html"
|
||||
title="next chapter"><span class="section-number">7. </span>Making objects persistent</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Evennia-Library-Overview.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="overview-of-the-evennia-library">
|
||||
<h1><span class="section-number">6. </span>Overview of the Evennia library<a class="headerlink" href="#overview-of-the-evennia-library" title="Permalink to this headline">¶</a></h1>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">API</p>
|
||||
<p>API stands for <code class="docutils literal notranslate"><span class="pre">Application</span> <span class="pre">Programming</span> <span class="pre">Interface</span></code>, a description for how to access the resources of a program or library.</p>
|
||||
</aside>
|
||||
<p>There are several good ways to explore the Evennia library.</p>
|
||||
<ul class="simple">
|
||||
<li><p>This documentation contains the <a class="reference internal" href="../../../Evennia-API.html"><span class="doc std std-doc">Evennia-API docs</span></a>, generated automatically from sources. Try clicking through to a few entries - once you get deep enough you’ll see full descriptions of each component along with their documentation. You can also click <code class="docutils literal notranslate"><span class="pre">[source]</span></code> to see the full Python source code for each thing.</p></li>
|
||||
<li><p>There are <a class="reference internal" href="../../../Components/Components-Overview.html"><span class="doc std std-doc">separate doc pages for each component</span></a> if you want more detailed explanations.</p></li>
|
||||
<li><p>You can browse <a class="reference external" href="https://github.com/evennia/evennia">the evennia repository on github</a>. This is exactly what you can download from us.</p></li>
|
||||
<li><p>Finally, you can clone the evennia repo to your own computer and read the sources. This is necessary if you want to <em>really</em> understand what’s going on, or help with Evennia’s development. See the <a class="reference internal" href="../../../Setup/Installation-Git.html"><span class="doc std std-doc">extended install instructions</span></a> if you want to do this.</p></li>
|
||||
</ul>
|
||||
<section id="where-is-it">
|
||||
<h2><span class="section-number">6.1. </span>Where is it?<a class="headerlink" href="#where-is-it" title="Permalink to this headline">¶</a></h2>
|
||||
<p>If Evennia is installed, you can import from it simply with</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>import evennia
|
||||
from evennia import some_module
|
||||
from evennia.some_module.other_module import SomeClass
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>and so on.</p>
|
||||
<p>If you installed Evennia with <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span></code>, the library folder will be installed deep inside your Python installation; you are better off <a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia">looking at it on github</a>. If you cloned it, you should have an <code class="docutils literal notranslate"><span class="pre">evennia</span></code> folder to look into.</p>
|
||||
<p>You’ll find this being the outermost structure:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia/
|
||||
bin/
|
||||
CHANGELOG.md
|
||||
...
|
||||
...
|
||||
docs/
|
||||
evennia/
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This outer layer is for Evennia’s installation and package distribution. That internal folder <code class="docutils literal notranslate"><span class="pre">evennia/evennia/</span></code> is the <em>actual</em> library, the thing covered by the API auto-docs and what you get when you do <code class="docutils literal notranslate"><span class="pre">import</span> <span class="pre">evennia</span></code>.</p>
|
||||
<blockquote>
|
||||
<div><p>The <code class="docutils literal notranslate"><span class="pre">evennia/docs/</span></code> folder contains the sources for this documentation. See
|
||||
<a class="reference internal" href="../../../Contributing-Docs.html"><span class="doc std std-doc">contributing to the docs</span></a> if you want to learn more about how this works.</p>
|
||||
</div></blockquote>
|
||||
<p>This is the structure of the Evennia library:</p>
|
||||
<ul class="simple">
|
||||
<li><p>evennia</p>
|
||||
<ul>
|
||||
<li><p><a class="reference internal" href="../../../Evennia-API.html#shortcuts"><span class="std std-doc"><code class="docutils literal notranslate"><span class="pre">__init__.py</span></code></span></a> - The “flat API” of Evennia resides here.</p></li>
|
||||
<li><p><a class="reference internal" href="../../../Setup/Settings.html#settings-file"><span class="std std-doc"><code class="docutils literal notranslate"><span class="pre">settings_default.py</span></code></span></a> - Root settings of Evennia. Copy settings from here to <code class="docutils literal notranslate"><span class="pre">mygame/server/settings.py</span></code> file.</p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Commands.html"><span class="doc std std-doc"><code class="docutils literal notranslate"><span class="pre">commands/</span></code></span></a> - The command parser and handler.</p>
|
||||
<ul>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">default/</span></code> - The <a class="reference internal" href="../../../Components/Default-Commands.html"><span class="doc std std-doc">default commands</span></a> and cmdsets.</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Channels.html"><span class="doc std std-doc"><code class="docutils literal notranslate"><span class="pre">comms/</span></code></span></a> - Systems for communicating in-game.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">contrib/</span></code> - Optional plugins too game-specific for core Evennia.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">game_template/</span></code> - Copied to become the “game directory” when using <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">--init</span></code>.</p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Help-System.html"><span class="doc std std-doc"><code class="docutils literal notranslate"><span class="pre">help/</span></code></span></a> - Handles the storage and creation of help entries.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">locale/</span></code> - Language files (<a class="reference internal" href="../../../Concepts/Internationalization.html"><span class="doc std std-doc">i18n</span></a>).</p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Locks.html"><span class="doc std std-doc"><code class="docutils literal notranslate"><span class="pre">locks/</span></code></span></a> - Lock system for restricting access to in-game entities.</p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Objects.html"><span class="doc std std-doc"><code class="docutils literal notranslate"><span class="pre">objects/</span></code></span></a> - In-game entities (all types of items and Characters).</p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Prototypes.html"><span class="doc std std-doc"><code class="docutils literal notranslate"><span class="pre">prototypes/</span></code></span></a> - Object Prototype/spawning system and OLC menu</p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Accounts.html"><span class="doc std std-doc"><code class="docutils literal notranslate"><span class="pre">accounts/</span></code></span></a> - Out-of-game Session-controlled entities (accounts, bots etc)</p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc"><code class="docutils literal notranslate"><span class="pre">scripts/</span></code></span></a> - Out-of-game entities equivalence to Objects, also with timer support.</p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Portal-And-Server.html"><span class="doc std std-doc"><code class="docutils literal notranslate"><span class="pre">server/</span></code></span></a> - Core server code and Session handling.</p>
|
||||
<ul>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">portal/</span></code> - Portal proxy and connection protocols.</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Typeclasses.html"><span class="doc std std-doc"><code class="docutils literal notranslate"><span class="pre">typeclasses/</span></code></span></a> - Abstract classes for the typeclass storage and database system.</p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Coding-Utils.html"><span class="doc std std-doc"><code class="docutils literal notranslate"><span class="pre">utils/</span></code></span></a> - Various miscellaneous useful coding resources.</p></li>
|
||||
<li><p><span class="xref myst"><code class="docutils literal notranslate"><span class="pre">web/</span></code></span> - Web resources and webserver. Partly copied into game directory on initialization.</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title"><strong>init</strong>.py</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> file is a special Python filename used to represent a Python ‘package’. When you import <code class="docutils literal notranslate"><span class="pre">evennia</span></code> on its own, you import this file. When you do <code class="docutils literal notranslate"><span class="pre">evennia.foo</span></code> Python will first look for a property <code class="docutils literal notranslate"><span class="pre">.foo</span></code> in <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> and then for a module or folder of that name in the same location.</p>
|
||||
</aside>
|
||||
<p>While all the actual Evennia code is found in the various folders, the <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> represents the entire package <code class="docutils literal notranslate"><span class="pre">evennia</span></code>. It contains “shortcuts” to code that is actually located elsewhere. Most of these shortcuts are listed if you <a class="reference internal" href="../../../Evennia-API.html"><span class="doc std std-doc">scroll down a bit</span></a> on the Evennia-API page.</p>
|
||||
</section>
|
||||
<section id="an-example-of-exploring-the-library">
|
||||
<h2><span class="section-number">6.2. </span>An example of exploring the library<a class="headerlink" href="#an-example-of-exploring-the-library" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In the <a class="reference internal" href="Beginner-Tutorial-Python-classes-and-objects.html#on-classes-and-objects"><span class="std std-doc">previous lesson</span></a> we took a brief look at <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/objects</span></code> as an example of a Python module. Let’s open it again.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">"""</span>
|
||||
<span class="sd">module docstring</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultObject</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Object</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> class docstring</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We have the <code class="docutils literal notranslate"><span class="pre">Object</span></code> class, which inherits from <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>. Near the top of the module is this line:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>from evennia import DefaultObject
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We want to figure out just what this DefaultObject offers. Since this is imported directly from <code class="docutils literal notranslate"><span class="pre">evennia</span></code>, we are actually importing from <code class="docutils literal notranslate"><span class="pre">evennia/__init__.py</span></code>.</p>
|
||||
<p><a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/__init__.py#L160">Look at Line 160</a> of <code class="docutils literal notranslate"><span class="pre">evennia/__init__.py</span></code> and you’ll find this line:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>from .objects.objects import DefaultObject
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Relative and absolute imports</p>
|
||||
<p>The first full-stop in <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">.objects.objects</span> <span class="pre">...</span></code> means that we are importing from the current location. This is called a <code class="docutils literal notranslate"><span class="pre">relative</span> <span class="pre">import</span></code>. By comparison, <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">evennia.objects.objects</span></code> is an <code class="docutils literal notranslate"><span class="pre">absolute</span> <span class="pre">import</span></code>. In this particular case, the two would give the same result.</p>
|
||||
</aside>
|
||||
<blockquote>
|
||||
<div><p>You can also look at <a class="reference internal" href="../../../Evennia-API.html#typeclasses"><span class="std std-doc">the right section of the API frontpage</span></a> and click through to the code that way.</p>
|
||||
</div></blockquote>
|
||||
<p>The fact that <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> is imported into <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> here is what makes it possible to also import it as <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">evennia</span> <span class="pre">import</span> <span class="pre">DefaultObject</span></code> even though the code for the class is not actually here.</p>
|
||||
<p>So to find the code for <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> we need to look in <code class="docutils literal notranslate"><span class="pre">evennia/objects/objects.py</span></code>. Here’s how to look it up in the docs:</p>
|
||||
<ol class="simple">
|
||||
<li><p>Open the <a class="reference internal" href="../../../Evennia-API.html"><span class="doc std std-doc">API frontpage</span></a></p></li>
|
||||
<li><p>Locate the link to <a class="reference internal" href="../../../api/evennia.objects.objects.html"><span class="doc std std-doc">evennia.objects.objects</span></a> and click on it.</p></li>
|
||||
<li><p>You are now in the python module. Scroll down (or search in your web browser) to find the <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> class.</p></li>
|
||||
<li><p>You can now read what this does and what methods are on it. If you want to see the full source, click the [source] link next to it.</p></li>
|
||||
</ol>
|
||||
</section>
|
||||
<section id="conclusions">
|
||||
<h2><span class="section-number">6.3. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This is an important lesson. It teaches you how to find information for yourself. Knowing how to follow the class inheritance tree and navigate to things you need is a big part in learning a new library like Evennia.</p>
|
||||
<p>Next we’ll start to make use of what we have learned so far and combine it with the building blocks provided by Evennia.</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="Beginner-Tutorial-Learning-Typeclasses.html" title="7. Making objects persistent"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Python-classes-and-objects.html" title="5. Introduction to Python classes and objects"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">6. </span>Overview of the Evennia library</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
|
||||
<!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>4. Overview of your new Game Dir — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="5. Introduction to Python classes and objects" href="Beginner-Tutorial-Python-classes-and-objects.html" />
|
||||
<link rel="prev" title="3. Intro to using Python with Evennia" href="Beginner-Tutorial-Python-basic-introduction.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Python-classes-and-objects.html" title="5. Introduction to Python classes and objects"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Python-basic-introduction.html" title="3. Intro to using Python with Evennia"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">4. </span>Overview of your new Game Dir</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">4. Overview of your new Game Dir</a><ul>
|
||||
<li><a class="reference internal" href="#commands">4.1. commands/</a></li>
|
||||
<li><a class="reference internal" href="#server">4.2. server/</a><ul>
|
||||
<li><a class="reference internal" href="#server-logs">4.2.1. server/logs/</a></li>
|
||||
<li><a class="reference internal" href="#server-conf">4.2.2. server/conf/</a></li>
|
||||
<li><a class="reference internal" href="#typeclasses">4.2.3. typeclasses/</a></li>
|
||||
<li><a class="reference internal" href="#web">4.2.4. web/</a></li>
|
||||
<li><a class="reference internal" href="#world">4.2.5. world/</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Python-basic-introduction.html"
|
||||
title="previous chapter"><span class="section-number">3. </span>Intro to using Python with Evennia</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Python-classes-and-objects.html"
|
||||
title="next chapter"><span class="section-number">5. </span>Introduction to Python classes and objects</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Gamedir-Overview.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="overview-of-your-new-game-dir">
|
||||
<h1><span class="section-number">4. </span>Overview of your new Game Dir<a class="headerlink" href="#overview-of-your-new-game-dir" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Until now we have ‘run the game’ a bit and started playing with Python inside Evennia.
|
||||
It is time to start to look at how things look ‘outside of the game’.</p>
|
||||
<p>Let’s do a tour of your game-dir (we assume it’s called <code class="docutils literal notranslate"><span class="pre">mygame</span></code>).</p>
|
||||
<blockquote>
|
||||
<div><p>When looking through files, ignore files ending with <code class="docutils literal notranslate"><span class="pre">.pyc</span></code> and the <code class="docutils literal notranslate"><span class="pre">__pycache__</span></code> folder if it exists. This is internal Python compilation files that you should never need to touch. Files <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> is also often empty and can be ignored (they have to do with Python package management).</p>
|
||||
</div></blockquote>
|
||||
<p>You may have noticed when we were building things in-game that we would often refer to code through “python paths”, such as</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>create/drop button:tutorial_examples.red_button.RedButton
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This is a fundamental aspect of coding Evennia - <em>you create code and then you tell Evennia where that code is and when it should be used</em>. Above we told it to create a red button by pulling from specific code in the <code class="docutils literal notranslate"><span class="pre">contrib/</span></code> folder. The same principle is true everywhere. So it’s important to know where code is and how you point to it correctly.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Python-paths</p>
|
||||
<p>A ‘python path’ uses ‘.’ instead of ‘/’ or ‘<code class="docutils literal notranslate"><span class="pre">\\</span></code>’ and skips the <code class="docutils literal notranslate"><span class="pre">.py</span></code> ending of files. It can also point to the code contents of python files. Since Evennia is already looking for code in your game dir, your python paths can start from there. So a path <code class="docutils literal notranslate"><span class="pre">/home/foo/devel/mygame/commands/command.py</span></code> would translate to a Python-path <code class="docutils literal notranslate"><span class="pre">commands.command</span></code>.</p>
|
||||
</aside>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">mygame/</span></code></p>
|
||||
<ul>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">commands/</span></code> - This holds all your custom commands (user-input handlers). You both add your own and override Evennia’s defaults from here.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">server</span></code>/ - The structure of this folder should not change since Evennia expects it.</p>
|
||||
<ul>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">conf/</span></code> - All server configuration files sits here. The most important file is <code class="docutils literal notranslate"><span class="pre">settings.py</span></code>.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">logs/</span></code> - Server log files are stored here. When you use <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">--log</span></code> you are actually
|
||||
tailing the files in this directory.</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">typeclasses/</span></code> - this holds empty templates describing all database-bound entities in the game, like Characters, Scripts, Accounts etc. Adding code here allows to customize and extend the defaults.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">web/</span></code> - This is where you override and extend the default templates, views and static files used for Evennia’s web-presence, like the website and the HTML5 webclient.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">world/</span></code> - this is a “miscellaneous” folder holding everything related to the world you are building, such as build scripts and rules modules that don’t fit with one of the other folders.</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<div><p>The <code class="docutils literal notranslate"><span class="pre">server/</span></code> subfolder should remain the way it is - Evennia expects this. But you can change the structure of the rest of your game dir as best fits your preferences.
|
||||
Maybe you don’t want a single world/ folder but prefer many folders with different aspects of your world? A new folder ‘rules’ for your RPG rules? Group your commands with your objects instead of having them separate? This is fine. If you move things around you just need to update Evennia’s default settings to point to the right places in the new structure.</p>
|
||||
</div></blockquote>
|
||||
<section id="commands">
|
||||
<h2><span class="section-number">4.1. </span>commands/<a class="headerlink" href="#commands" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">commands/</span></code> folder holds Python modules related to creating and extending the <a class="reference internal" href="../../../Components/Commands.html"><span class="doc std std-doc">Commands</span></a>
|
||||
of Evennia. These manifest in game like the server understanding input like <code class="docutils literal notranslate"><span class="pre">look</span></code> or <code class="docutils literal notranslate"><span class="pre">dig</span></code>.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Classes</p>
|
||||
<p>A <code class="docutils literal notranslate"><span class="pre">class</span></code> is template for creating object-instances of a particular type in Python. We will explain classes in more detail in the next lesson.</p>
|
||||
</aside>
|
||||
<ul class="simple">
|
||||
<li><p><a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/game_template/commands/command.py">command.py</a> (Python-path: <code class="docutils literal notranslate"><span class="pre">commands.command</span></code>) - this contain the
|
||||
base <em>classes</em> for designing new input commands, or override the defaults.</p></li>
|
||||
<li><p><a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/game_template/commands/default_cmdsets.py">default_cmdsets.py</a> (Python path: <code class="docutils literal notranslate"><span class="pre">commands.default_commands</span></code>) -
|
||||
a cmdset (Command-Set) groups Commands together. Command-sets can be added and removed from objects on the fly,
|
||||
meaning a user could have a different set of commands (or versions of commands) available depending on their circumstance
|
||||
in the game. In order to add a new command to the game, it’s common to import the new command-class
|
||||
from <code class="docutils literal notranslate"><span class="pre">command.py</span></code> and add it to one of the default cmdsets in this module.</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="server">
|
||||
<h2><span class="section-number">4.2. </span>server/<a class="headerlink" href="#server" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This folder contains resource necessary for running Evennia. Contrary to the other folders, the structure of this should be kept the way it is.</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">evennia.db3</span></code> - you will only have this file if you are using the default SQLite3 database. This file contains the entire database. Just copy it to make a backup. For development you could also just make a copy once you have set up everything you need and just copy that back to ‘reset’ the state. If you delete this file you can easily recreate it by running <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">migrate</span></code>.</p></li>
|
||||
</ul>
|
||||
<section id="server-logs">
|
||||
<h3><span class="section-number">4.2.1. </span>server/logs/<a class="headerlink" href="#server-logs" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This holds the server logs. When you do <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">--log</span></code>, the evennia program is in fact tailing and concatenating the <code class="docutils literal notranslate"><span class="pre">server.log</span></code> and <code class="docutils literal notranslate"><span class="pre">portal.log</span></code> files in this directory. The logs are rotated every week. Depending on your settings, other logs, like the webserver HTTP request log can also be found here.</p>
|
||||
</section>
|
||||
<section id="server-conf">
|
||||
<h3><span class="section-number">4.2.2. </span>server/conf/<a class="headerlink" href="#server-conf" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This contains all configuration files of the Evennia server. These are regular Python modules which means that they must be extended with valid Python. You can also add logic to them if you wanted to.</p>
|
||||
<p>Common for the settings is that you generally will never them directly via their python-path; instead Evennia knows where they are and will read them to configure itself at startup.</p>
|
||||
<ul>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">settings.py</span></code> - this is by far the most important file. It’s nearly empty by default, rather you
|
||||
are expected to copy&paste the changes you need from <a class="reference internal" href="../../../Setup/Settings-Default.html"><span class="doc std std-doc">evennia/default_settings.py</span></a>. The default settings file is extensively documented. Importing/accessing the values in the settings file is done in a special way, like this:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> from django.conf import settings
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>To get to the setting <code class="docutils literal notranslate"><span class="pre">TELNET_PORT</span></code> in the settings file you’d then do</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> telnet_port = settings.TELNET_PORT
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You cannot assign to the settings file dynamically; you must change the <code class="docutils literal notranslate"><span class="pre">settings.py</span></code> file directly to change a setting. See <a class="reference internal" href="../../../Setup/Settings.html"><span class="doc std std-doc">Settings</span></a> documentation for more details.</p>
|
||||
</li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">secret_settings.py</span></code> - If you are making your code effort public, you may not want to share all settings online. There may be server-specific secrets or just fine-tuning for your game systems that you prefer be kept secret from the players. Put such settings in here, it will override values in <code class="docutils literal notranslate"><span class="pre">settings.py</span></code> and not be included in version control.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">at_initial_setup.py</span></code> - When Evennia starts up for the very first time, it does some basic tasks, like creating the superuser and Limbo room. Adding to this file allows to add more actions for it to for first-startup.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">at_search.py</span></code> - When searching for objects and either finding no match or more than one match, it will respond by giving a warning or offering the user to differentiate between the multiple matches. Modifying the code here will change this behavior to your liking.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">at_server_startstop.py</span></code> - This allows to inject code to execute every time the server starts, stops or reloads in different ways.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">connection_screens.py</span></code> - This allows for changing the connection screen you see when you first connect to your game.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">inlinefuncs.py</span></code> - <a class="reference internal" href="../../../Concepts/Inline-Functions.html"><span class="doc std std-doc">Inlinefuncs</span></a> are optional and limited ‘functions’ that can be embedded in any strings being sent to a player. They are written as <code class="docutils literal notranslate"><span class="pre">$funcname(args)</span></code> and are used to customize the output depending on the user receiving it. For example sending people the text <code class="docutils literal notranslate"><span class="pre">"Let's</span> <span class="pre">meet</span> <span class="pre">at</span> <span class="pre">$realtime(13:00,</span> <span class="pre">GMT)!</span></code> would show every player seeing that string the time given in their own time zone. The functions added to this module will become new inlinefuncs in the game. See also the <a class="reference internal" href="../../../Components/FuncParser.html"><span class="doc std std-doc">FuncParser</span></a>.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">inputfucs.py</span></code> - When a command like <code class="docutils literal notranslate"><span class="pre">look</span></code> is received by the server, it is handled by an <span class="xref myst">Inputfunc</span> that redirects it to the cmdhandler system. But there could be other inputs coming from the clients, like button-presses or the request to update a health-bar. While most common cases are already covered, this is where one adds new functions to process new types of input.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">lockfuncs.py</span></code> - <a class="reference internal" href="../../../Components/Locks.html"><span class="doc std std-doc">Locks</span></a> and their component <em>LockFuncs</em> restrict access to things in-game. Lock funcs are used in a mini-language to defined more complex locks. For example you could have a lockfunc that checks if the user is carrying a given item, is bleeding or has a certain skill value. New functions added in this modules will become available for use in lock definitions.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">mssp.py</span></code> - Mud Server Status Protocol is a way for online MUD archives/listings (which you usually have to sign up for) to track which MUDs are currently online, how many players they have etc. While Evennia handles the dynamic information automatically, this is were you set up the meta-info about your game, such as its theme, if player-killing is allowed and so on. This is a more generic form of the Evennia Game directory.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">portal_services_plugins.py</span></code> - If you want to add new external connection protocols to Evennia, this is the place to add them.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">server_services_plugins.py</span></code> - This allows to override internal server connection protocols.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">web_plugins.py</span></code> - This allows to add plugins to the Evennia webserver as it starts.</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="typeclasses">
|
||||
<h3><span class="section-number">4.2.3. </span>typeclasses/<a class="headerlink" href="#typeclasses" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The <a class="reference internal" href="../../../Components/Typeclasses.html"><span class="doc std std-doc">Typeclasses</span></a> of Evennia are Evennia-specific Python classes whose instances save themselves to the database. This allows a Character to remain in the same place and your updated strength stat to still be the same after a server reboot.</p>
|
||||
<ul class="simple">
|
||||
<li><p><a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/game_template/typeclasses/accounts.py">accounts.py</a> (Python-path: <code class="docutils literal notranslate"><span class="pre">typeclasses.accounts</span></code>) - An <a class="reference internal" href="../../../Components/Accounts.html"><span class="doc std std-doc">Account</span></a> represents the player connecting to the game. It holds information like email, password and other out-of-character details.</p></li>
|
||||
<li><p><a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/game_template/typeclasses/channels.py">channels.py</a> (Python-path: <code class="docutils literal notranslate"><span class="pre">typeclasses.channels</span></code>) - <a class="reference internal" href="../../../Components/Channels.html"><span class="doc std std-doc">Channels</span></a> are used to manage in-game communication between players.</p></li>
|
||||
<li><p><a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/game_template/typeclasses/objects.py">objects.py</a> (Python-path: <code class="docutils literal notranslate"><span class="pre">typeclasses.objects</span></code>) - <a class="reference internal" href="../../../Components/Objects.html"><span class="doc std std-doc">Objects</span></a> represent all things having a location within the game world.</p></li>
|
||||
<li><p><a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/game_template/typeclasses/characters.py">characters.py</a> (Python-path: <code class="docutils literal notranslate"><span class="pre">typeclasses.characters</span></code>) - The <span class="xref myst">Character</span> is a subclass of Objects, controlled by Accounts - they are the player’s avatars in the game world.</p></li>
|
||||
<li><p><a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/game_template/typeclasses/rooms.py">rooms.py</a> (Python-path: <code class="docutils literal notranslate"><span class="pre">typeclasses.rooms</span></code>) - A <span class="xref myst">Room</span> is also a subclass of Object; describing discrete locations. While the traditional term is ‘room’, such a location can be anything and on any scale that fits your game, from a forest glade, an entire planet or an actual dungeon room.</p></li>
|
||||
<li><p><a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/game_template/typeclasses/exits.py">exits.py</a> (Python-path: <code class="docutils literal notranslate"><span class="pre">typeclasses.exits</span></code>) - <span class="xref myst">Exits</span> is another subclass of Object. Exits link one Room to another.</p></li>
|
||||
<li><p><a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/game_template/typeclasses/scripts.py">scripts.py</a> (Python-path: <code class="docutils literal notranslate"><span class="pre">typeclasses.scripts</span></code>) - <a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Scripts</span></a> are ‘out-of-character’ objects. They have no location in-game and can serve as basis for anything that needs database persistence, such as combat, weather, or economic systems. They also have the ability to execute code repeatedly, on a timer.</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="web">
|
||||
<h3><span class="section-number">4.2.4. </span>web/<a class="headerlink" href="#web" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This folder contains subfolders for overriding the default web-presence of Evennia with your own designs. Most of these folders are empty except for a README file or a subset of other empty folders. See <a class="reference internal" href="../../../Components/Components-Overview.html#web-components"><span class="std std-doc">the Web overview</span></a> for more details (we’ll also get back to the web later in this beginner tutorial).</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">media/</span></code> - this empty folder is where you can place your own images or other media files you want the web server to serve. If you are releasing your game with a lot of media (especially if you want videos) you should consider re-pointing Evennia to use some external service to serve your media instead.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">static_overrides/</span></code> - ‘static’ files include fonts, CSS and JS. Within this folder you’ll find sub-folders for overriding the static files for the <code class="docutils literal notranslate"><span class="pre">admin</span></code> (this is the Django web-admin), the <code class="docutils literal notranslate"><span class="pre">webclient</span></code> (this is thet HTML5 webclient) and the <code class="docutils literal notranslate"><span class="pre">website</span></code>. Adding files to this folder will replace same-named files in the default web presence.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">template_overrides/</span></code> - these are HTML files, for the <code class="docutils literal notranslate"><span class="pre">webclient</span></code> and the <code class="docutils literal notranslate"><span class="pre">website</span></code>. HTML files are written using <a class="reference external" href="https://jinja.palletsprojects.com/en/2.11.x/">Jinja</a> templating, which means that one can override
|
||||
only particular parts of a default template without touching others.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">static/</span></code> - this is a work-directory for the web system and should <em>not</em> be manually modified. Basically, Evennia will copy static data from <code class="docutils literal notranslate"><span class="pre">static_overrides</span></code> here when the server starts.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">urls.py</span></code> - this module links up the Python code to the URLs you go to in the browser.</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="world">
|
||||
<h3><span class="section-number">4.2.5. </span>world/<a class="headerlink" href="#world" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This folder only contains some example files. It’s meant to hold ‘the rest’ of your game implementation. Many people change and re-structure this in various ways to better fit their ideas.</p>
|
||||
<ul class="simple">
|
||||
<li><p><a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/game_template/world/batch_cmds.ev">batch_cmds.ev</a> - This is an <code class="docutils literal notranslate"><span class="pre">.ev</span></code> file, which is essentially just a list of Evennia commands to execute in sequence. This one is empty and ready to expand on. The <a class="reference internal" href="Beginner-Tutorial-Tutorial-World.html"><span class="doc std std-doc">Tutorial World</span></a> was built with such a batch-file.</p></li>
|
||||
<li><p><a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/game_template/world/prototypes.py">prototypes.py</a> - A <a class="reference internal" href="../../../Components/Prototypes.html"><span class="doc std std-doc">prototype</span></a> is a way to easily vary objects without changing their base typeclass. For example, one could use prototypes to tell that Two goblins, while both of the class ‘Goblin’ (so they follow the same code logic), should have different equipment, stats and looks.</p></li>
|
||||
<li><p><a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/game_template/world/help_entries.py">help_entries.py</a> - You can add new in-game <a class="reference internal" href="../../../Components/Help-System.html"><span class="doc std std-doc">Help entries</span></a> in several ways, such as adding them in the database using the <code class="docutils literal notranslate"><span class="pre">sethelp</span></code> command, or (for Commands) read the help directly from the source code. You can also add them through python modules. This module is an example on how to do so.</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Python-classes-and-objects.html" title="5. Introduction to Python classes and objects"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Python-basic-introduction.html" title="3. Intro to using Python with Evennia"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">4. </span>Overview of your new Game Dir</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,739 @@
|
|||
|
||||
<!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>7. Making objects persistent — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="8. Adding custom commands" href="Beginner-Tutorial-Adding-Commands.html" />
|
||||
<link rel="prev" title="6. Overview of the Evennia library" href="Beginner-Tutorial-Evennia-Library-Overview.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Adding-Commands.html" title="8. Adding custom commands"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Evennia-Library-Overview.html" title="6. Overview of the Evennia library"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">7. </span>Making objects persistent</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">7. Making objects persistent</a><ul>
|
||||
<li><a class="reference internal" href="#our-first-persistent-object">7.1. Our first persistent object</a><ul>
|
||||
<li><a class="reference internal" href="#making-a-new-object-by-calling-the-class">7.1.1. Making a new object by calling the class</a></li>
|
||||
<li><a class="reference internal" href="#creating-using-create-object">7.1.2. Creating using create_object</a></li>
|
||||
<li><a class="reference internal" href="#creating-using-create-command">7.1.3. Creating using create-command</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#typeclasses">7.2. Typeclasses</a><ul>
|
||||
<li><a class="reference internal" href="#examining-objects">7.2.1. Examining objects</a></li>
|
||||
<li><a class="reference internal" href="#default-typeclasses">7.2.2. Default typeclasses</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#modifying-ourselves">7.3. Modifying ourselves</a><ul>
|
||||
<li><a class="reference internal" href="#a-method-on-ourselves">7.3.1. A method on ourselves</a></li>
|
||||
<li><a class="reference internal" href="#attributes">7.3.2. Attributes</a></li>
|
||||
<li><a class="reference internal" href="#setting-things-on-new-characters">7.3.3. Setting things on new Characters</a></li>
|
||||
<li><a class="reference internal" href="#updating-all-characters-in-a-loop">7.3.4. Updating all Characters in a loop</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#extra-credits">7.4. Extra Credits</a></li>
|
||||
<li><a class="reference internal" href="#conclusions">7.5. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Evennia-Library-Overview.html"
|
||||
title="previous chapter"><span class="section-number">6. </span>Overview of the Evennia library</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Adding-Commands.html"
|
||||
title="next chapter"><span class="section-number">8. </span>Adding custom commands</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Learning-Typeclasses.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="making-objects-persistent">
|
||||
<h1><span class="section-number">7. </span>Making objects persistent<a class="headerlink" href="#making-objects-persistent" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Now that we have learned a little about how to find things in the Evennia library, let’s use it.</p>
|
||||
<p>In the <a class="reference internal" href="Beginner-Tutorial-Python-classes-and-objects.html"><span class="doc std std-doc">Python classes and objects</span></a> lesson we created the dragons Fluffy, Cuddly
|
||||
and Smaug and made them fly and breathe fire. So far our dragons are short-lived - whenever we <code class="docutils literal notranslate"><span class="pre">restart</span></code> the server or <code class="docutils literal notranslate"><span class="pre">quit()</span></code> out of python mode they are gone.</p>
|
||||
<p>This is what you should have in <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/monsters.py</span></code> so far:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||||
<span class="k">class</span> <span class="nc">Monster</span><span class="p">:</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> This is a base class for Monsters.</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<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">key</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">key</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">move_around</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is moving!"</span><span class="p">)</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">Dragon</span><span class="p">(</span><span class="n">Monster</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> This is a dragon-specific monster.</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">move_around</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">move_around</span><span class="p">()</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="s2">"The world trembles."</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">firebreath</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Let our dragon breathe fire.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> breathes fire!"</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<section id="our-first-persistent-object">
|
||||
<h2><span class="section-number">7.1. </span>Our first persistent object<a class="headerlink" href="#our-first-persistent-object" title="Permalink to this headline">¶</a></h2>
|
||||
<p>At this point we should know enough to understand what is happening in <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/objects.py</span></code>. Let’s
|
||||
open it:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">"""</span>
|
||||
<span class="sd">module docstring</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultObject</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">ObjectParent</span><span class="p">:</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> class docstring </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Object</span><span class="p">(</span><span class="n">ObjectParent</span><span class="p">,</span> <span class="n">DefaultObject</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> class docstring</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>So we have a class <code class="docutils literal notranslate"><span class="pre">Object</span></code> that <em>inherits</em> from <code class="docutils literal notranslate"><span class="pre">ObjectParent</span></code> (which is empty) and <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>, which we have imported from Evennia. The <code class="docutils literal notranslate"><span class="pre">ObjectParent</span></code> acts as a place to put code you want all
|
||||
of your <code class="docutils literal notranslate"><span class="pre">Objects</span></code> to have. We’ll focus on <code class="docutils literal notranslate"><span class="pre">Object</span></code> and <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> for now.</p>
|
||||
<p>The class itself doesn’t do anything (it just <code class="docutils literal notranslate"><span class="pre">pass</span></code>es) but that doesn’t mean it’s useless. As we’ve seen, it inherits all the functionality of its parent. It’s in fact an <em>exact replica</em> of <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> right now. Once we know what kind of methods and resources are available on <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> we could add our own and change the way it works!</p>
|
||||
<p>One thing that Evennia classes offers and which you don’t get with vanilla Python classes is <em>persistence</em> - they survive a server reload since they are stored in the database.</p>
|
||||
<p>Go back to <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/monsters.py</span></code>. Change it as follows:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||||
<span class="kn">from</span> <span class="nn">typeclasses.objects</span> <span class="kn">import</span> <span class="n">Object</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Monster</span><span class="p">(</span><span class="n">Object</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> This is a base class for Monsters.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">def</span> <span class="nf">move_around</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is moving!"</span><span class="p">)</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">Dragon</span><span class="p">(</span><span class="n">Monster</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> This is a dragon-specific Monster.</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">move_around</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">move_around</span><span class="p">()</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="s2">"The world trembles."</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">firebreath</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Let our dragon breathe fire.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> breathes fire!"</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Don’t forget to save. We removed <code class="docutils literal notranslate"><span class="pre">Monster.__init__</span></code> and made <code class="docutils literal notranslate"><span class="pre">Monster</span></code> inherit from Evennia’s <code class="docutils literal notranslate"><span class="pre">Object</span></code> (which in turn inherits from Evennia’s <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>, as we saw). By extension, this means that <code class="docutils literal notranslate"><span class="pre">Dragon</span></code> also inherits from <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>, just from further away!</p>
|
||||
<section id="making-a-new-object-by-calling-the-class">
|
||||
<h3><span class="section-number">7.1.1. </span>Making a new object by calling the class<a class="headerlink" href="#making-a-new-object-by-calling-the-class" title="Permalink to this headline">¶</a></h3>
|
||||
<p>First reload the server as usual. We will need to create the dragon a little differently this time:</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Keyword arguments</p>
|
||||
<p><em>Keyword arguments</em> (like <code class="docutils literal notranslate"><span class="pre">db_key="Smaug"</span></code>) is a way to name the input arguments to a function or method. They make things easier to read but also allows for conveniently setting defaults for values not given explicitly. We saw them previously in use for <code class="docutils literal notranslate"><span class="pre">.format()</span></code>.</p>
|
||||
</aside>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py
|
||||
> from typeclasses.monsters import Dragon
|
||||
> smaug = Dragon(db_key="Smaug", db_location=here)
|
||||
> smaug.save()
|
||||
> smaug.move_around()
|
||||
Smaug is moving!
|
||||
The world trembles.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Smaug works the same as before, but we created him differently: first we used
|
||||
<code class="docutils literal notranslate"><span class="pre">Dragon(db_key="Smaug",</span> <span class="pre">db_location=here)</span></code> to create the object, and then we used <code class="docutils literal notranslate"><span class="pre">smaug.save()</span></code> afterwards.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">here</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">here</span></code> used in <code class="docutils literal notranslate"><span class="pre">db_location=here</span></code> is a shortcut for your current location. This <code class="docutils literal notranslate"><span class="pre">here</span></code> (similar to <code class="docutils literal notranslate"><span class="pre">me</span></code>) is <em>only</em> available to use in the <code class="docutils literal notranslate"><span class="pre">py</span></code> command; you can’t use it in other Python code you write unless you define it yourself.</p>
|
||||
</aside>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> quit()
|
||||
Python Console is closing.
|
||||
> look
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You should now see that Smaug <em>is in the room with you</em>. Woah!</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> look
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><em>He’s still there</em>… What we just did was to create a new entry in the database for Smaug. We gave the object its name (key) and set its location to our current location.</p>
|
||||
<p>To make use of Smaug in code we must first find him in the database. For an object in the current
|
||||
location we can easily do this in <code class="docutils literal notranslate"><span class="pre">py</span></code> by using <code class="docutils literal notranslate"><span class="pre">me.search()</span></code>:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py smaug = me.search("Smaug") ; smaug.firebreath()
|
||||
Smaug breathes fire!
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="creating-using-create-object">
|
||||
<h3><span class="section-number">7.1.2. </span>Creating using create_object<a class="headerlink" href="#creating-using-create-object" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Creating Smaug like we did above is nice because it’s similar to how we created non-database
|
||||
bound Python instances before. But you need to use <code class="docutils literal notranslate"><span class="pre">db_key</span></code> instead of <code class="docutils literal notranslate"><span class="pre">key</span></code> and you also have to
|
||||
remember to call <code class="docutils literal notranslate"><span class="pre">.save()</span></code> afterwards. Evennia has a helper function that is more common to use,
|
||||
called <code class="docutils literal notranslate"><span class="pre">create_object</span></code>. Let’s recreate Cuddly this time:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py evennia.create_object('typeclasses.monsters.Monster', key="Cuddly", location=here)
|
||||
> look
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Boom, Cuddly should now be in the room with you, a little less scary than Smaug. You specify the
|
||||
python-path to the code you want and then set the key and location (if you had the <code class="docutils literal notranslate"><span class="pre">Monster</span></code> class already imported, you could have passed that too). Evennia sets things up and saves for you.</p>
|
||||
<p>If you want to find Cuddly from anywhere (not just in the same room), you can use Evennia’s <code class="docutils literal notranslate"><span class="pre">search_object</span></code> function:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py cuddly = evennia.search_object("Cuddly")[0] ; cuddly.move_around()
|
||||
Cuddly is moving!
|
||||
</pre></div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div><p>The <code class="docutils literal notranslate"><span class="pre">[0]</span></code> is because <code class="docutils literal notranslate"><span class="pre">search_object</span></code> always returns a <em>list</em> of zero, one or more found objects. The <code class="docutils literal notranslate"><span class="pre">[0]</span></code> means that we want the first element of this list (counting in Python always starts from 0). If there were multiple Cuddlies we could get the second one with <code class="docutils literal notranslate"><span class="pre">[1]</span></code>.</p>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
<section id="creating-using-create-command">
|
||||
<h3><span class="section-number">7.1.3. </span>Creating using create-command<a class="headerlink" href="#creating-using-create-command" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Finally, you can also create a new dragon using the familiar builder-commands we explored a few lessons ago:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> create/drop Fluffy:typeclasses.monsters.Dragon
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Fluffy is now in the room. After learning about how objects are created you’ll realize that all this command really does is to parse your input, figure out that <code class="docutils literal notranslate"><span class="pre">/drop</span></code> means to “give the object the same location as the caller”, and then do a call very similar to</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia.create_object("typeclasses.monsters.Dragon", key="Cuddly", location=here)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>That’s pretty much all there is to the mighty <code class="docutils literal notranslate"><span class="pre">create</span></code> command! The rest is just parsing for the command to understand just what the user wants to create.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="typeclasses">
|
||||
<h2><span class="section-number">7.2. </span>Typeclasses<a class="headerlink" href="#typeclasses" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">Object</span></code> (and <code class="docutils literal notranslate"><span class="pre">DefafultObject</span></code> class we inherited from above is what we refer to as a <em>Typeclass</em>. This is an Evennia thing. The instance of a typeclass saves itself to the database when it is created, and after that you can just search for it to get it back.</p>
|
||||
<p>We use the term <em>typeclass</em> or <em>typeclassed</em> to differentiate these types of classes and objects from the normal Python classes, whose instances go away on a reload.</p>
|
||||
<p>The number of typeclasses in Evennia are so few they can be learned by heart:</p>
|
||||
<table class="colwidths-auto docutils align-default">
|
||||
<thead>
|
||||
<tr class="row-odd"><th class="head"><p>Evennia base typeclass</p></th>
|
||||
<th class="head"><p>mygame.typeclasses child</p></th>
|
||||
<th class="head"><p>description</p></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultObject</span></code></p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.objects.Object</span></code></p></td>
|
||||
<td><p>Everything with a location</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultCharacter</span></code> (child of <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>)</p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.characters.Character</span></code></p></td>
|
||||
<td><p>Player avatars</p></td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultRoom</span></code> (child of <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>)</p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.rooms.Room</span></code></p></td>
|
||||
<td><p>In-game locations</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultExit</span></code> (chld of <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>)</p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.exits.Exit</span></code></p></td>
|
||||
<td><p>Links between rooms</p></td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultAccount</span></code></p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.accounts.Account</span></code></p></td>
|
||||
<td><p>A player account</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultChannel</span></code></p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.channels.Channel</span></code></p></td>
|
||||
<td><p>In-game comms</p></td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">evennia.DefaultScript</span></code></p></td>
|
||||
<td><p><code class="docutils literal notranslate"><span class="pre">typeclasses.scripts.Script</span></code></p></td>
|
||||
<td><p>Entities with no location</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>The child classes under <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/</span></code> are meant for you to conveniently modify and
|
||||
work with. Every class inheriting (at any distance) from a Evennia base typeclass is also considered a typeclass.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">somewhere</span> <span class="kn">import</span> <span class="n">Something</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultScript</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">MyOwnClass</span><span class="p">(</span><span class="n">Something</span><span class="p">):</span>
|
||||
<span class="c1"># not inheriting from an Evennia core typeclass, so this </span>
|
||||
<span class="c1"># is just a 'normal' Python class inheriting from somewhere</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">MyOwnClass2</span><span class="p">(</span><span class="n">DefaultScript</span><span class="p">):</span>
|
||||
<span class="c1"># inherits from one of the core Evennia typeclasses, so </span>
|
||||
<span class="c1"># this is also considered a 'typeclass'.</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Why invent the name ‘typeclass’?</p>
|
||||
<p>We separate ‘regular classes’ from ‘typeclasses’ because while typeclasses act <em>almost</em> like normal Python classes, <a class="reference internal" href="../../../Components/Typeclasses.html"><span class="doc std std-doc">there are some differences</span></a>. We will gloss over those differences for now, but they are worth to read up on when you want to do more advanced things later.</p>
|
||||
</aside>
|
||||
<p>Notice that the classes in <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/</span></code> are <em>not inheriting from each other</em>. For example, <code class="docutils literal notranslate"><span class="pre">Character</span></code> is inheriting from <code class="docutils literal notranslate"><span class="pre">evennia.DefaultCharacter</span></code> and not from <code class="docutils literal notranslate"><span class="pre">typeclasses.objects.Object</span></code>. So if you change <code class="docutils literal notranslate"><span class="pre">Object</span></code> you will not cause any change in the <code class="docutils literal notranslate"><span class="pre">Character</span></code> class. If you want that you can easily just change the child classes to inherit in that way instead; Evennia doesn’t care.</p>
|
||||
<p>As seen with our <code class="docutils literal notranslate"><span class="pre">Dragon</span></code> example, you don’t <em>have</em> to modify these modules directly. You can just make your own modules and import the base class.</p>
|
||||
<section id="examining-objects">
|
||||
<h3><span class="section-number">7.2.1. </span>Examining objects<a class="headerlink" href="#examining-objects" title="Permalink to this headline">¶</a></h3>
|
||||
<p>When you do</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> create/drop giantess:typeclasses.monsters.Monster
|
||||
You create a new Monster: giantess.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>or</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py evennia.create_object("typeclasses.monsters.Monster", key="Giantess", location=here)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You are specifying exactly which typeclass you want to use to build the Giantess. Let’s examine the result:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> examine giantess
|
||||
-------------------------------------------------------------------------------
|
||||
Name/key: Giantess (#14)
|
||||
Typeclass: Monster (typeclasses.monsters.Monster)
|
||||
Location: Limbo (#2)
|
||||
Home: Limbo (#2)
|
||||
Permissions: <None>
|
||||
Locks: call:true(); control:id(1) or perm(Admin); delete:id(1) or perm(Admin);
|
||||
drop:holds(); edit:perm(Admin); examine:perm(Builder); get:all();
|
||||
puppet:pperm(Developer); tell:perm(Admin); view:all()
|
||||
Persistent attributes:
|
||||
desc = You see nothing special.
|
||||
-------------------------------------------------------------------------------
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We used the <code class="docutils literal notranslate"><span class="pre">examine</span></code> command briefly in the <a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html"><span class="doc std std-doc">lesson about building in-game</span></a>. Now these lines
|
||||
may be more useful to us:</p>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Name/key</strong> - The name of this thing. The value <code class="docutils literal notranslate"><span class="pre">(#14)</span></code> is probably different for you. This is the
|
||||
unique ‘primary key’ or <em>dbref</em> for this entity in the database.</p></li>
|
||||
<li><p><strong>Typeclass</strong>: This show the typeclass we specified, and the path to it.</p></li>
|
||||
<li><p><strong>Location</strong>: We are in Limbo. If you moved elsewhere you’ll see that instead. Also the <code class="docutils literal notranslate"><span class="pre">#dbref</span></code> of Limbo is shown.</p></li>
|
||||
<li><p><strong>Home</strong>: All objects with a location (inheriting from <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>) must have a home location. This is a backup to move the object to if its current location is deleted.</p></li>
|
||||
<li><p><strong>Permissions</strong>: <em>Permissions</em> are like the inverse to <em>Locks</em> - they are like keys to unlock access to other things. The giantess have no such keys (maybe fortunately). The <a class="reference internal" href="../../../Components/Permissions.html"><span class="doc std std-doc">Permissions</span></a> has more info.</p></li>
|
||||
<li><p><strong>Locks</strong>: Locks are the inverse of <em>Permissions</em> - specify what criterion <em>other</em> objects must fulfill in order to access the <code class="docutils literal notranslate"><span class="pre">giantess</span></code> object. This uses a very flexible mini-language. For examine, the line <code class="docutils literal notranslate"><span class="pre">examine:perm(Builders)</span></code> is read as “Only those with permission <em>Builder</em> or higher can <em>examine</em> this object”. Since we are the superuser we pass (even bypass) such locks with ease. See the <a class="reference internal" href="../../../Components/Locks.html"><span class="doc std std-doc">Locks</span></a> documentation for more info.</p></li>
|
||||
<li><p><strong>Persistent attributes</strong>: This allows for storing arbitrary, persistent data on the typeclassed entity. We’ll get to those in the next section.</p></li>
|
||||
</ul>
|
||||
<p>Note how the <strong>Typeclass</strong> line describes exactly where to find the code of this object? This is very useful for understanding how any object in Evennia works.</p>
|
||||
</section>
|
||||
<section id="default-typeclasses">
|
||||
<h3><span class="section-number">7.2.2. </span>Default typeclasses<a class="headerlink" href="#default-typeclasses" title="Permalink to this headline">¶</a></h3>
|
||||
<p>What happens if we create an object and <em>don’t</em> specify its typeclass though?</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> create/drop box
|
||||
You create a new Object: box.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>or</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py create.create_object(None, key="box", location=here)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now check it out:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> examine box
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You will find that the <strong>Typeclass</strong> line now reads</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Typeclass: Object (typeclasses.objects.Object)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>So when you didn’t specify a typeclass, Evennia used a default, more specifically the (so far) empty <code class="docutils literal notranslate"><span class="pre">Object</span></code> class in <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/objects.py</span></code>. This is usually what you want, especially since you can tweak that class as much as you like.</p>
|
||||
<p>But the reason Evennia knows to fall back to this class is not hard-coded - it’s a setting. The default is in <a class="reference internal" href="../../../Setup/Settings-Default.html"><span class="doc std std-doc">evennia/settings_default.py</span></a>, with the name <code class="docutils literal notranslate"><span class="pre">BASE_OBJECT_TYPECLASS</span></code>, which is set to <code class="docutils literal notranslate"><span class="pre">typeclasses.objects.Object</span></code>.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Changing things</p>
|
||||
<p>While it’s tempting to change folders around to your liking, this can make it harder to follow tutorials and may confuse if you are asking others for help. So don’t overdo it unless you really know what you are doing.</p>
|
||||
</aside>
|
||||
<p>So if you wanted the creation commands and methods to default to some other class you could
|
||||
add your own <code class="docutils literal notranslate"><span class="pre">BASE_OBJECT_TYPECLASS</span></code> line to <code class="docutils literal notranslate"><span class="pre">mygame/server/conf/settings.py</span></code>. The same is true for all the other typeclasseses, like characters, rooms and accounts. This way you can change the layout of your game dir considerably if you wanted. You just need to tell Evennia where everything is.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="modifying-ourselves">
|
||||
<h2><span class="section-number">7.3. </span>Modifying ourselves<a class="headerlink" href="#modifying-ourselves" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Let’s try to modify ourselves a little. Open up <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/characters.py</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">"""</span>
|
||||
<span class="sd">(module docstring)</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Character</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> (class docstring)</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This looks quite familiar now - an empty class inheriting from the Evennia base typeclass (it’s even easier than <code class="docutils literal notranslate"><span class="pre">Object</span></code> since there is no equvalent <code class="docutils literal notranslate"><span class="pre">ParentObject</span></code> mixin class here). As you would expect, this is also the default typeclass used for creating Characters if you don’t specify it. You can verify it:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> examine me
|
||||
------------------------------------------------------------------------------
|
||||
Name/key: YourName (#1)
|
||||
Session id(s): #1
|
||||
Account: YourName
|
||||
Account Perms: <Superuser> (quelled)
|
||||
Typeclass: Character (typeclasses.characters.Character)
|
||||
Location: Limbo (#2)
|
||||
Home: Limbo (#2)
|
||||
Permissions: developer, player
|
||||
Locks: boot:false(); call:false(); control:perm(Developer); delete:false();
|
||||
drop:holds(); edit:false(); examine:perm(Developer); get:false();
|
||||
msg:all(); puppet:false(); tell:perm(Admin); view:all()
|
||||
Stored Cmdset(s):
|
||||
commands.default_cmdsets.CharacterCmdSet [DefaultCharacter] (Union, prio 0)
|
||||
Merged Cmdset(s):
|
||||
...
|
||||
Commands available to YourName (result of Merged CmdSets):
|
||||
...
|
||||
Persistent attributes:
|
||||
desc = This is User #1.
|
||||
prelogout_location = Limbo
|
||||
Non-Persistent attributes:
|
||||
last_cmd = None
|
||||
------------------------------------------------------------------------------
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Yes, the <code class="docutils literal notranslate"><span class="pre">examine</span></code> command understands <code class="docutils literal notranslate"><span class="pre">me</span></code>. You got a lot longer output this time. You have a lot more going on than a simple Object. Here are some new fields of note:</p>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Session id(s)</strong>: This identifies the <em>Session</em> (that is, the individual connection to a player’s game client).</p></li>
|
||||
<li><p><strong>Account</strong> shows, well the <code class="docutils literal notranslate"><span class="pre">Account</span></code> object associated with this Character and Session.</p></li>
|
||||
<li><p><strong>Stored/Merged Cmdsets</strong> and <strong>Commands available</strong> is related to which <em>Commands</em> are stored on you. We will get to them in the <a class="reference internal" href="Beginner-Tutorial-Adding-Commands.html"><span class="doc std std-doc">next lesson</span></a>. For now it’s enough to know these consitute all the
|
||||
commands available to you at a given moment.</p></li>
|
||||
<li><p><strong>Non-Persistent attributes</strong> are Attributes that are only stored temporarily and will go away on next reload.</p></li>
|
||||
</ul>
|
||||
<p>Look at the <strong>Typeclass</strong> field and you’ll find that it points to <code class="docutils literal notranslate"><span class="pre">typeclasses.character.Character</span></code> as expected. So if we modify this class we’ll also modify ourselves.</p>
|
||||
<section id="a-method-on-ourselves">
|
||||
<h3><span class="section-number">7.3.1. </span>A method on ourselves<a class="headerlink" href="#a-method-on-ourselves" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Let’s try something simple first. Back in <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/characters.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||||
<span class="k">class</span> <span class="nc">Character</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> (class docstring)</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="n">strength</span> <span class="o">=</span> <span class="mi">10</span>
|
||||
<span class="n">dexterity</span> <span class="o">=</span> <span class="mi">12</span>
|
||||
<span class="n">intelligence</span> <span class="o">=</span> <span class="mi">15</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_stats</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Get the main stats of this character</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">strength</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">dexterity</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">intelligence</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> py self.get_stats()
|
||||
(10, 12, 15)
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Tuples and lists</p>
|
||||
<ul class="simple">
|
||||
<li><p>A <code class="docutils literal notranslate"><span class="pre">list</span></code> is written <code class="docutils literal notranslate"><span class="pre">[a,</span> <span class="pre">b,</span> <span class="pre">c,</span> <span class="pre">d,</span> <span class="pre">...]</span></code>. It can be modified after creation.</p></li>
|
||||
<li><p>A <code class="docutils literal notranslate"><span class="pre">tuple</span></code> is written <code class="docutils literal notranslate"><span class="pre">(a,</span> <span class="pre">b,</span> <span class="pre">c,</span> <span class="pre">...)</span></code>. It cannot be modified once created.</p></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<p>We made a new method, gave it a docstring and had it <code class="docutils literal notranslate"><span class="pre">return</span></code> the RP-esque values we set. It comes back as a <em>tuple</em> <code class="docutils literal notranslate"><span class="pre">(10,</span> <span class="pre">12,</span> <span class="pre">15)</span></code>. To get a specific value you could specify the <em>index</em> of the value you want, starting from zero:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py stats = self.get_stats() ; print(f"Strength is {stats[0]}.")
|
||||
Strength is 10.
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="attributes">
|
||||
<h3><span class="section-number">7.3.2. </span>Attributes<a class="headerlink" href="#attributes" title="Permalink to this headline">¶</a></h3>
|
||||
<p>So what happens when we increase our strength? This would be one way:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py self.strength = self.strength + 1
|
||||
> py self.strength
|
||||
11
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here we set the strength equal to its previous value + 1. A shorter way to write this is to use Python’s <code class="docutils literal notranslate"><span class="pre">+=</span></code> operator:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py self.strength += 1
|
||||
> py self.strength
|
||||
12
|
||||
> py self.get_stats()
|
||||
(12, 12, 15)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This looks correct! Try to change the values for dex and int too; it works fine. However:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> py self.get_stats()
|
||||
(10, 12, 15)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>After a reload all our changes were forgotten. When we change properties like this, it only changes in memory, not in the database (nor do we modify the python module’s code). So when we reloaded, the ‘fresh’ <code class="docutils literal notranslate"><span class="pre">Character</span></code> class was loaded, and it still has the original stats we wrote in it.</p>
|
||||
<p>In principle we could change the python code. But we don’t want to do that manually every time. And more importantly since we have the stats hardcoded in the class, <em>every</em> character instance in the game will have exactly the same <code class="docutils literal notranslate"><span class="pre">str</span></code>, <code class="docutils literal notranslate"><span class="pre">dex</span></code> and <code class="docutils literal notranslate"><span class="pre">int</span></code> now! This is clearly not what we want.</p>
|
||||
<p>Evennia offers a special, persistent type of property for this, called an <code class="docutils literal notranslate"><span class="pre">Attribute</span></code>. Rework your <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/characters.py</span></code> like this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||||
<span class="k">class</span> <span class="nc">Character</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> (class docstring)</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_stats</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Get the main stats of this character</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">strength</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">dexterity</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">intelligence</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Spaces in Attribute name?</p>
|
||||
<p>What if you want spaces in your Attribute name? Or you want to assign the name of the Attribute on-the fly? Then you can use <code class="docutils literal notranslate"><span class="pre">.attributes.add(name,</span> <span class="pre">value)</span></code> instead, for example <code class="docutils literal notranslate"><span class="pre">self.attributes.add("emotional</span> <span class="pre">intelligence",</span> <span class="pre">10)</span></code>. You read it out again with <code class="docutils literal notranslate"><span class="pre">self.attributes.get("emotional</span> <span class="pre">intelligence"</span></code>.</p>
|
||||
</aside>
|
||||
<p>We removed the hard-coded stats and added added <code class="docutils literal notranslate"><span class="pre">.db</span></code> for every stat. The <code class="docutils literal notranslate"><span class="pre">.db</span></code> handler makes the stat into an an Evennia <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attribute</span></a>.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> py self.get_stats()
|
||||
(None, None, None)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Since we removed the hard-coded values, Evennia don’t know what they should be (yet). So all we get back is <code class="docutils literal notranslate"><span class="pre">None</span></code>, which is a Python reserved word to represent nothing, a no-value. This is different from a normal python property:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py me.strength
|
||||
AttributeError: 'Character' object has no attribute 'strength'
|
||||
> py me.db.strength
|
||||
(nothing will be displayed, because it's None)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Trying to get an unknown normal Python property will give an error. Getting an unknown Evennia <code class="docutils literal notranslate"><span class="pre">Attribute</span></code> will never give an error, but only result in <code class="docutils literal notranslate"><span class="pre">None</span></code> being returned. This is often very practical.</p>
|
||||
<p>Next, let us test out assigning those Attributes</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py me.db.strength, me.db.dexterity, me.db.intelligence = 10, 12, 15
|
||||
> py me.get_stats()
|
||||
(10, 12, 15)
|
||||
> reload
|
||||
> py me.get_stats()
|
||||
(10, 12, 15)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now we set the Attributes to the right values, and they survive a server reload! Let’s modify the strength:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py self.db.strength += 2
|
||||
> py self.get_stats()
|
||||
(12, 12, 15)
|
||||
> reload
|
||||
> py self.get_stats()
|
||||
(12, 12, 15)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Also our change now survives a reload since Evennia automatically saves the Attribute to the database for us.</p>
|
||||
</section>
|
||||
<section id="setting-things-on-new-characters">
|
||||
<h3><span class="section-number">7.3.3. </span>Setting things on new Characters<a class="headerlink" href="#setting-things-on-new-characters" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Things are looking better, but one thing remains strange - the stats start out with a value <code class="docutils literal notranslate"><span class="pre">None</span></code> and we have to manually set them to something reasonable. In a later lesson we will investigate character-creation in more detail. For now, let’s give every new character some random stats to start with.</p>
|
||||
<p>We want those stats to be set only once, when the object is first created. For the Character, this method is called <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># up by the other imports</span>
|
||||
<span class="kn">import</span> <span class="nn">random</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Character</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> (class docstring)</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_object_creation</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">strength</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">18</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">dexterity</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">18</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">intelligence</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">18</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_stats</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Get the main stats of this character</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">strength</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">dexterity</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">intelligence</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We imported a new module, <code class="docutils literal notranslate"><span class="pre">random</span></code>. This is part of Python’s standard library. We used <code class="docutils literal notranslate"><span class="pre">random.randint</span></code> to
|
||||
set a random value from 3 to 18 to each stat. Simple, but for some classical RPGs this is all you need!</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> py self.get_stats()
|
||||
(12, 12, 15)
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title"><strong>init</strong> vs at_object_creation</p>
|
||||
<p>For the <code class="docutils literal notranslate"><span class="pre">Monster</span></code> class we used <code class="docutils literal notranslate"><span class="pre">__init__</span></code> to set up the class. We can’t use this for a typeclass because it will be called more than once, at the very least after every reload and maybe more depending on caching. Even if you are familiar with Python, avoid touching <code class="docutils literal notranslate"><span class="pre">__init__</span></code> for typeclasses, the results will not be what you expect.</p>
|
||||
</aside>
|
||||
<p>Hm, this is the same values we set before. They are not random. The reason for this is of course that, as said, <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> only runs <em>once</em>, the very first time a character is created. Our character object was already created long before, so it will not be called again.</p>
|
||||
<p>It’s simple enough to run it manually though:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py self.at_object_creation()
|
||||
> py self.get_stats()
|
||||
(5, 4, 8)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Lady luck didn’t smile on us for this example; maybe you’ll fare better. Evennia has a helper command
|
||||
<code class="docutils literal notranslate"><span class="pre">update</span></code> that re-runs the creation hook and also cleans up any other Attributes not re-created by <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code>:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> update self
|
||||
> py self.get_stats()
|
||||
(8, 16, 14)
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="updating-all-characters-in-a-loop">
|
||||
<h3><span class="section-number">7.3.4. </span>Updating all Characters in a loop<a class="headerlink" href="#updating-all-characters-in-a-loop" title="Permalink to this headline">¶</a></h3>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">AttributeProperties</p>
|
||||
<p>There is another way to define Attributes on a class, known as <a class="reference internal" href="../../../Components/Attributes.html#using-attributeproperty"><span class="std std-doc">AttributeProperties</span></a>. They can make it easier to maintain static default Attribute values on a typeclass. We will show them off when we make our game later in this tutorial series.</p>
|
||||
</aside>
|
||||
<p>Needless to say, you are wise to have a feel for what you want to go into the <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> hook <em>before</em> you create a lot of objects (characters in this case).</p>
|
||||
<p>Luckily you only need to update objects once, and you don’t have to go around and re-run the <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> method on everyone manually. For this we’ll try out a Python <em>loop</em>. Let’s go into multi-line Python mode:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py
|
||||
> for a in [1, 2, "foo"]:
|
||||
> print(a)
|
||||
1
|
||||
2
|
||||
foo
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>A python <em>for-loop</em> allows us to loop over something. Above, we made a <em>list</em> of two numbers and a string. In every iteration of the loop, the variable <code class="docutils literal notranslate"><span class="pre">a</span></code> becomes one element in turn, and we print that.</p>
|
||||
<p>For our list, we want to loop over all Characters, and want to call <code class="docutils literal notranslate"><span class="pre">.at_object_creation</span></code> on each. This is how this is done (still in python multi-line mode):</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> from typeclasses.characters import Character
|
||||
> for char in Character.objects.all():
|
||||
> char.at_object_creation()
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Database queries</p>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">Character.objects.all()</span></code> is an example of a database query expressed in Python. This will be converted into a database query under the hood. This syntax is part of <a class="reference external" href="https://docs.djangoproject.com/en/4.1/topics/db/queries/">Django’s query language</a>. You don’t need to know Django to use Evennia, but if you ever need more specific database queries, this is always available when you need it. We’ll get back to database queries in a later lesson.</p>
|
||||
</aside>
|
||||
<p>We import the <code class="docutils literal notranslate"><span class="pre">Character</span></code> class and then we use <code class="docutils literal notranslate"><span class="pre">.objects.all()</span></code> to get all <code class="docutils literal notranslate"><span class="pre">Character</span></code> instances. Simplified,
|
||||
<code class="docutils literal notranslate"><span class="pre">.objects</span></code> is a resource from which one can <em>query</em> for all <code class="docutils literal notranslate"><span class="pre">Characters</span></code>. Using <code class="docutils literal notranslate"><span class="pre">.all()</span></code> gets us a listing
|
||||
of all of them that we then immediately loop over. Boom, we just updated all Characters, including ourselves:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> quit()
|
||||
Closing the Python console.
|
||||
> py self.get_stats()
|
||||
(3, 18, 10)
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="extra-credits">
|
||||
<h2><span class="section-number">7.4. </span>Extra Credits<a class="headerlink" href="#extra-credits" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This principle is the same for other typeclasses. So using the tools explored in this lesson, try to expand the default room with an <code class="docutils literal notranslate"><span class="pre">is_dark</span></code> flag. It can be either <code class="docutils literal notranslate"><span class="pre">True</span></code> or <code class="docutils literal notranslate"><span class="pre">False</span></code>. Have all new rooms start with <code class="docutils literal notranslate"><span class="pre">is_dark</span> <span class="pre">=</span> <span class="pre">False</span></code> and make it so that once you change it, it survives a reload.
|
||||
Oh, and if you created any other rooms before, make sure they get the new flag too!</p>
|
||||
</section>
|
||||
<section id="conclusions">
|
||||
<h2><span class="section-number">7.5. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In this lesson we created database-persistent dragons by having their classes inherit from one <code class="docutils literal notranslate"><span class="pre">Object</span></code>, one of Evennia’s <em>typeclasses</em>. We explored where Evennia looks for typeclasses if we don’t specify the path explicitly. We then modified ourselves - via the <code class="docutils literal notranslate"><span class="pre">Character</span></code> class - to give us some simple RPG stats. This led to the need to use Evennia’s <em>Attributes</em>, settable via <code class="docutils literal notranslate"><span class="pre">.db</span></code> and to use a for-loop to update ourselves.</p>
|
||||
<p>Typeclasses are a fundamental part of Evennia and we will see a lot of more uses of them in the course of this tutorial. But that’s enough of them for now. It’s time to take some action. Let’s learn about <em>Commands</em>.</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="Beginner-Tutorial-Adding-Commands.html" title="8. Adding custom commands"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Evennia-Library-Overview.html" title="6. Overview of the Evennia library"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">7. </span>Making objects persistent</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,952 @@
|
|||
|
||||
<!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>13. Building a chair you can sit on — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="Part 2: What We Want" href="../Part2/Beginner-Tutorial-Part2-Overview.html" />
|
||||
<link rel="prev" title="12. Advanced searching - Django Database queries" href="Beginner-Tutorial-Django-queries.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Part2/Beginner-Tutorial-Part2-Overview.html" title="Part 2: What We Want"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Django-queries.html" title="12. Advanced searching - Django Database queries"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">13. </span>Building a chair you can sit on</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">13. Building a chair you can sit on</a><ul>
|
||||
<li><a class="reference internal" href="#make-us-not-able-to-move-while-sitting">13.1. Make us not able to move while sitting</a></li>
|
||||
<li><a class="reference internal" href="#making-the-chair-itself">13.2. Making the Chair itself</a><ul>
|
||||
<li><a class="reference internal" href="#sitting-on-or-in">13.2.1. Sitting on or in?</a></li>
|
||||
<li><a class="reference internal" href="#extra-credits">13.2.2. Extra credits</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#adding-commands">13.3. Adding commands</a><ul>
|
||||
<li><a class="reference internal" href="#command-variant-1-commands-on-the-chair">13.3.1. Command variant 1: Commands on the chair</a></li>
|
||||
<li><a class="reference internal" href="#command-variant-2-command-on-character">13.3.2. Command variant 2: Command on Character</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#conclusions">13.4. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Django-queries.html"
|
||||
title="previous chapter"><span class="section-number">12. </span>Advanced searching - Django Database queries</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="../Part2/Beginner-Tutorial-Part2-Overview.html"
|
||||
title="next chapter">Part 2: What We Want</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Making-A-Sittable-Object.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="building-a-chair-you-can-sit-on">
|
||||
<h1><span class="section-number">13. </span>Building a chair you can sit on<a class="headerlink" href="#building-a-chair-you-can-sit-on" title="Permalink to this headline">¶</a></h1>
|
||||
<p>In this lesson we will make use of what we have learned to create a new game object: a chair you can sit on.</p>
|
||||
<p>Our goals are:</p>
|
||||
<ul class="simple">
|
||||
<li><p>We want a new ‘sittable’ object, a <code class="docutils literal notranslate"><span class="pre">Chair</span></code> in particular.</p></li>
|
||||
<li><p>We want to be able to use a command to sit in the chair.</p></li>
|
||||
<li><p>Once we are sitting in the chair it should affect us somehow. To demonstrate this store
|
||||
the current chair in an attribute <code class="docutils literal notranslate"><span class="pre">is_sitting</span></code>. Other systems could check this to affect us in different ways.</p></li>
|
||||
<li><p>A character should be able to stand up and move away from the chair.</p></li>
|
||||
<li><p>When you sit down you should not be able to walk to another room without first standing up.</p></li>
|
||||
</ul>
|
||||
<section id="make-us-not-able-to-move-while-sitting">
|
||||
<h2><span class="section-number">13.1. </span>Make us not able to move while sitting<a class="headerlink" href="#make-us-not-able-to-move-while-sitting" title="Permalink to this headline">¶</a></h2>
|
||||
<p>When you are sitting in a chair you can’t just walk off without first standing up.
|
||||
This requires a change to our Character typeclass. Open <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/characters.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/typeclasses/characters.py</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Character</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_pre_move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">destination</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Called by self.move_to when trying to move somewhere. If this returns</span>
|
||||
<span class="sd"> False, the move is immediately cancelled.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">is_sitting</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">"You need to stand up first."</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="kc">False</span>
|
||||
<span class="k">return</span> <span class="kc">True</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>When moving somewhere, <a class="reference internal" href="../../../api/evennia.objects.objects.html#evennia.objects.objects.DefaultObject.move_to" title="evennia.objects.objects.DefaultObject.move_to"><span class="xref myst py py-meth">character.move_to</span></a> is called. This in turn
|
||||
will call <code class="docutils literal notranslate"><span class="pre">character.at_pre_move</span></code>. If this returns <code class="docutils literal notranslate"><span class="pre">False</span></code>, the move is aborted.</p>
|
||||
<p>Here we look for an Attribute <code class="docutils literal notranslate"><span class="pre">is_sitting</span></code> (which we will assign below) to determine if we are stuck on the chair or not.</p>
|
||||
</section>
|
||||
<section id="making-the-chair-itself">
|
||||
<h2><span class="section-number">13.2. </span>Making the Chair itself<a class="headerlink" href="#making-the-chair-itself" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Next we need the Chair itself, or rather a whole family of “things you can sit on” that we will call <em>sittables</em>. We can’t just use a default Object since we want a sittable to contain some custom code. We need a new, custom Typeclass. Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/sittables.py</span></code> with the following content:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span>
|
||||
<span class="normal">22</span>
|
||||
<span class="normal">23</span>
|
||||
<span class="normal">24</span>
|
||||
<span class="normal">25</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in mygame/typeclasses/sittables.py</span>
|
||||
|
||||
<span class="hll"><span class="kn">from</span> <span class="nn">typeclasses.objects</span> <span class="kn">import</span> <span class="n">Object</span>
|
||||
</span>
|
||||
<span class="k">class</span> <span class="nc">Sittable</span><span class="p">(</span><span class="n">Object</span><span class="p">):</span>
|
||||
|
||||
<span class="hll"> <span class="k">def</span> <span class="nf">do_sit</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sitter</span><span class="p">):</span>
|
||||
</span><span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Called when trying to sit on/in this object.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> sitter (Object): The one trying to sit down.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="hll"> <span class="n">current</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span>
|
||||
</span><span class="hll"> <span class="k">if</span> <span class="n">current</span><span class="p">:</span>
|
||||
</span> <span class="k">if</span> <span class="n">current</span> <span class="o">==</span> <span class="n">sitter</span><span class="p">:</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You are already sitting on </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You can't sit on </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> "</span>
|
||||
<span class="sa">f</span><span class="s2">"- </span><span class="si">{</span><span class="n">current</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is already sitting there!"</span><span class="p">)</span>
|
||||
<span class="k">return</span>
|
||||
<span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span> <span class="o">=</span> <span class="n">sitter</span>
|
||||
</span><span class="hll"> <span class="n">sitter</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">is_sitting</span> <span class="o">=</span> <span class="bp">self</span>
|
||||
</span><span class="hll"> <span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You sit on </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
</span></pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<p>This handles the logic of someone sitting down on the chair.</p>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 3</strong>: We inherit from the empty <code class="docutils literal notranslate"><span class="pre">Object</span></code> class in <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/objects.py</span></code>. This means we can theoretically modify that in the future and have those changes affect sittables too.</p></li>
|
||||
<li><p><strong>Line 7</strong>: The <code class="docutils literal notranslate"><span class="pre">do_sit</span></code> method expects to be called with the argument <code class="docutils literal notranslate"><span class="pre">sitter</span></code>, which is to be an <code class="docutils literal notranslate"><span class="pre">Object</span></code> (most likely a <code class="docutils literal notranslate"><span class="pre">Character</span></code>). This is the one wanting to sit down.</p></li>
|
||||
<li><p><strong>Line 15</strong>: Note that, if the <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attribute</span></a> <code class="docutils literal notranslate"><span class="pre">sitter</span></code> is not defined on the chair (because this is the first time someone sits in it), this will simply return <code class="docutils literal notranslate"><span class="pre">None</span></code>, which is fine.</p></li>
|
||||
<li><p><strong>Lines 16-22</strong> We check if someone is already sitting on the chair and returns appropriate error messages depending on if it’s you or someone else. We use <code class="docutils literal notranslate"><span class="pre">return</span></code> to abort the sit-action.</p></li>
|
||||
<li><p><strong>Line 23</strong>: If we get to this point, <code class="docutils literal notranslate"><span class="pre">sitter</span></code> gets to, well, sit down. We store them in the <code class="docutils literal notranslate"><span class="pre">sitter</span></code> Attribute on the chair.</p></li>
|
||||
<li><p><strong>Line 24</strong>: <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> is the chair this command is attachd to. We store that in the <code class="docutils literal notranslate"><span class="pre">is_sitting</span></code> Attribute on the <code class="docutils literal notranslate"><span class="pre">sitter</span></code> itself.</p></li>
|
||||
<li><p><strong>Line 25</strong>: Finally we tell the sitter that they could sit down.</p></li>
|
||||
</ul>
|
||||
<p>Let’s continue:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># add this right after the `do_sit method` in the same class </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">do_stand</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">stander</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Called when trying to stand from this object.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> stander (Object): The one trying to stand up.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">current</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span>
|
||||
<span class="hll"> <span class="k">if</span> <span class="ow">not</span> <span class="n">stander</span> <span class="o">==</span> <span class="n">current</span><span class="p">:</span>
|
||||
</span> <span class="n">stander</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You are not sitting on </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
</span><span class="hll"> <span class="k">del</span> <span class="n">stander</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">is_sitting</span>
|
||||
</span><span class="hll"> <span class="n">stander</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You stand up from </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
</span></pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<p>This is the inverse of sitting down; we need to do some cleanup.</p>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 12</strong>: If we are not sitting on the chair, it makes no sense to stand up from it.</p></li>
|
||||
<li><p><strong>Line 15</strong>: If we get here, we could stand up. We make sure to un-set the <code class="docutils literal notranslate"><span class="pre">sitter</span></code> Attribute so someone else could use the chair later.</p></li>
|
||||
<li><p><strong>Line 16</strong>: The character is no longer sitting, so we delete their <code class="docutils literal notranslate"><span class="pre">is_sitting</span></code> Attribute. We could also have done <code class="docutils literal notranslate"><span class="pre">stander.db.is_sitting</span> <span class="pre">=</span> <span class="pre">None</span></code> here, but deleting the Attribute feels cleaner.</p></li>
|
||||
<li><p><strong>Line 17</strong>: Finally, we inform them that they stood up successfully.</p></li>
|
||||
</ul>
|
||||
<p>One could imagine that one could have the future <code class="docutils literal notranslate"><span class="pre">sit</span></code> command (which we haven’t created yet) check if someone is already sitting in the chair instead. This would work too, but letting the <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> class handle the logic around who can sit on it makes sense.</p>
|
||||
<p>We let the typeclass handle the logic, and also let it do all the return messaging. This makes it easy to churn out a bunch of chairs for people to sit on.</p>
|
||||
<section id="sitting-on-or-in">
|
||||
<h3><span class="section-number">13.2.1. </span>Sitting on or in?<a class="headerlink" href="#sitting-on-or-in" title="Permalink to this headline">¶</a></h3>
|
||||
<p>It’s fine to sit ‘on’ a chair. But what if our Sittable is an armchair?</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">></span> <span class="n">py</span> <span class="n">armchair</span> <span class="o">=</span> <span class="n">evennia</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="s2">"typeclasses.sittables.Sittable"</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"armchair"</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">here</span><span class="p">)</span>
|
||||
<span class="o">></span> <span class="n">py</span> <span class="n">armchair</span><span class="o">.</span><span class="n">do_sit</span><span class="p">(</span><span class="n">me</span><span class="p">)</span>
|
||||
<span class="n">You</span> <span class="n">sit</span> <span class="n">on</span> <span class="n">armchair</span><span class="o">.</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This is not grammatically correct, you actually sit “in” an armchair rather than “on” it. It’s also possible to both sit ‘in’ or ‘on’ a chair depending on the type of chair (English is weird). We want to be able to control this.</p>
|
||||
<p>We <em>could</em> make a child class of <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> named <code class="docutils literal notranslate"><span class="pre">SittableIn</span></code> that makes this change, but that feels excessive. Instead we will modify what we have:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span>
|
||||
<span class="normal">22</span>
|
||||
<span class="normal">23</span>
|
||||
<span class="normal">24</span>
|
||||
<span class="normal">25</span>
|
||||
<span class="normal">26</span>
|
||||
<span class="normal">27</span>
|
||||
<span class="normal">28</span>
|
||||
<span class="normal">29</span>
|
||||
<span class="normal">30</span>
|
||||
<span class="normal">31</span>
|
||||
<span class="normal">32</span>
|
||||
<span class="normal">33</span>
|
||||
<span class="normal">34</span>
|
||||
<span class="normal">35</span>
|
||||
<span class="normal">36</span>
|
||||
<span class="normal">37</span>
|
||||
<span class="normal">38</span>
|
||||
<span class="normal">39</span>
|
||||
<span class="normal">40</span>
|
||||
<span class="normal">41</span>
|
||||
<span class="normal">42</span>
|
||||
<span class="normal">43</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in mygame/typeclasses/sittables.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">typeclasses.objects</span> <span class="kn">import</span> <span class="n">Object</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Sittable</span><span class="p">(</span><span class="n">Object</span><span class="p">):</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">do_sit</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sitter</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Called when trying to sit on/in this object.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> sitter (Object): The one trying to sit down.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="hll"> <span class="n">adjective</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">adjective</span> <span class="ow">or</span> <span class="s2">"on"</span>
|
||||
</span> <span class="n">current</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span>
|
||||
<span class="k">if</span> <span class="n">current</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="n">current</span> <span class="o">==</span> <span class="n">sitter</span><span class="p">:</span>
|
||||
<span class="hll"> <span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You are already sitting </span><span class="si">{</span><span class="n">adjective</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
</span> <span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
|
||||
<span class="hll"> <span class="sa">f</span><span class="s2">"You can't sit </span><span class="si">{</span><span class="n">adjective</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> "</span>
|
||||
</span> <span class="sa">f</span><span class="s2">"- </span><span class="si">{</span><span class="n">current</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is already sitting there!"</span><span class="p">)</span>
|
||||
<span class="k">return</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span> <span class="o">=</span> <span class="n">sitter</span>
|
||||
<span class="n">sitter</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">is_sitting</span> <span class="o">=</span> <span class="bp">self</span>
|
||||
<span class="hll"> <span class="n">sitter</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You sit </span><span class="si">{</span><span class="n">adjective</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
</span>
|
||||
<span class="k">def</span> <span class="nf">do_stand</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">stander</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Called when trying to stand from this object.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> stander (Object): The one trying to stand up.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">current</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">stander</span> <span class="o">==</span> <span class="n">current</span><span class="p">:</span>
|
||||
<span class="hll"> <span class="n">stander</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You are not sitting </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">adjective</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
</span> <span class="k">else</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">sitter</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="k">del</span> <span class="n">stander</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">is_sitting</span>
|
||||
<span class="hll"> <span class="n">stander</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You stand up from </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">."</span><span class="p">)</span>
|
||||
</span></pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 15</strong>: We grab the <code class="docutils literal notranslate"><span class="pre">adjective</span></code> Attribute. Using <code class="docutils literal notranslate"><span class="pre">self.db.adjective</span> <span class="pre">or</span> <span class="pre">"on"</span></code> here means that if the Attribute is not set (is <code class="docutils literal notranslate"><span class="pre">None</span></code>/falsy) the default “on” string will be assumed.</p></li>
|
||||
<li><p><strong>Lines 19,22,27,39, and 43</strong>: We use this adjective to modify the return text we see.</p></li>
|
||||
</ul>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">reload</span></code> the server. An advantage of using Attributes like this is that they can be modified on the fly, in-game. Let’s look at a builder could use this by normal building commands (no need for <code class="docutils literal notranslate"><span class="pre">py</span></code>):</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">></span> <span class="nb">set</span> <span class="n">armchair</span><span class="o">/</span><span class="n">adjective</span> <span class="o">=</span> <span class="ow">in</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Since we haven’t added the <code class="docutils literal notranslate"><span class="pre">sit</span></code> command yet, we must still use <code class="docutils literal notranslate"><span class="pre">py</span></code> to test:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">></span> <span class="n">py</span> <span class="n">armchair</span> <span class="o">=</span> <span class="n">evennia</span><span class="o">.</span><span class="n">search_object</span><span class="p">(</span><span class="s2">"armchair"</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span><span class="n">armchair</span><span class="o">.</span><span class="n">do_sit</span><span class="p">(</span><span class="n">me</span><span class="p">)</span>
|
||||
<span class="n">You</span> <span class="n">sit</span> <span class="ow">in</span> <span class="n">armchair</span><span class="o">.</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="extra-credits">
|
||||
<h3><span class="section-number">13.2.2. </span>Extra credits<a class="headerlink" href="#extra-credits" title="Permalink to this headline">¶</a></h3>
|
||||
<p>What if we want some more dramatic flair when you sit down in certain chairs?</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You sit down and a whoopie cushion makes a loud fart noise!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can make this happen by tweaking your <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> class having the return messages be replaceable by <code class="docutils literal notranslate"><span class="pre">Attributes</span></code> that you can set on the object you create. You want something like this:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>> py
|
||||
> chair = evennia.create_object("typeclasses.sittables.Sittable", key="pallet")
|
||||
> chair.do_sit(me)
|
||||
You sit down on pallet.
|
||||
> chair.do_stand(me)
|
||||
You stand up from pallet.
|
||||
> chair.db.msg_sitting_down = "You sit down and a whoopie cushion makes a loud fart noise!"
|
||||
> chair.do_sit(me)
|
||||
You sit down and a whoopie cushion makes a loud fart noise!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>That is, if you are not setting the Attribute, you should get a default value. We leave this implementation up to the reader.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="adding-commands">
|
||||
<h2><span class="section-number">13.3. </span>Adding commands<a class="headerlink" href="#adding-commands" title="Permalink to this headline">¶</a></h2>
|
||||
<p>As we discussed in the <a class="reference internal" href="Beginner-Tutorial-More-on-Commands.html"><span class="doc std std-doc">lesson about adding Commands</span></a>, there are two main ways to design the commands for sitting and standing up:</p>
|
||||
<ul class="simple">
|
||||
<li><p>You can store the commands on the chair so they are only available when a chair is in the room</p></li>
|
||||
<li><p>You can store the commands on the Character so they are always available and you must always specify which chair to sit on.</p></li>
|
||||
</ul>
|
||||
<p>Both of these are very useful to know about, so in this lesson we’ll try both.</p>
|
||||
<section id="command-variant-1-commands-on-the-chair">
|
||||
<h3><span class="section-number">13.3.1. </span>Command variant 1: Commands on the chair<a class="headerlink" href="#command-variant-1-commands-on-the-chair" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This way to implement <code class="docutils literal notranslate"><span class="pre">sit</span></code> and <code class="docutils literal notranslate"><span class="pre">stand</span></code> puts new cmdsets on the Sittable itself.
|
||||
As we’ve learned before, commands on objects are made available to others in the room.
|
||||
This makes the command easy but instead adds some complexity in the management of the CmdSet.</p>
|
||||
<p>This is how it could look if <code class="docutils literal notranslate"><span class="pre">armchair</span></code> is in the room (Extra credits: Change the sit message on the armchair to match this output instead of getting the default <code class="docutils literal notranslate"><span class="pre">You</span> <span class="pre">sit</span> <span class="pre">in</span> <span class="pre">armchair</span></code>!):</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> sit
|
||||
As you sit down in armchair, life feels easier.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>What happens if there are sittables <code class="docutils literal notranslate"><span class="pre">sofa</span></code> and <code class="docutils literal notranslate"><span class="pre">barstool</span></code> also in the room? Evennia will
|
||||
automatically handle this for us and allow us to specify which one we want:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> sit
|
||||
More than one match for 'sit' (please narrow target):
|
||||
sit-1 (armchair)
|
||||
sit-2 (sofa)
|
||||
sit-3 (barstool)
|
||||
> sit-1
|
||||
As you sit down in armchair, life feels easier.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>To keep things separate we’ll make a new module <code class="docutils literal notranslate"><span class="pre">mygame/commands/sittables.py</span></code>:</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Separate Commands and Typeclasses?</p>
|
||||
<p>You can organize these things as you like. If you wanted you could put the sit-command + cmdset together with the <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> typeclass in <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/sittables.py</span></code>. That has the advantage of keeping everything related to sitting in one place. But there is also some organizational merit to keeping all Commands in one place as we do here.</p>
|
||||
</aside>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span>
|
||||
<span class="normal">22</span>
|
||||
<span class="normal">23</span>
|
||||
<span class="normal">24</span>
|
||||
<span class="normal">25</span>
|
||||
<span class="normal">26</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in mygame/commands/sittables.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span><span class="p">,</span> <span class="n">CmdSet</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdSit</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Sit down.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"sit"</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="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">do_sit</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span>
|
||||
</span>
|
||||
<span class="k">class</span> <span class="nc">CmdStand</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Stand up.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"stand"</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="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">do_stand</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="p">)</span>
|
||||
</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdSetSit</span><span class="p">(</span><span class="n">CmdSet</span><span class="p">):</span>
|
||||
<span class="hll"> <span class="n">priority</span> <span class="o">=</span> <span class="mi">1</span>
|
||||
</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">CmdSit</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">CmdStand</span><span class="p">)</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<p>As seen, the commands are nearly trivial.</p>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Lines 11 and 19</strong>: The <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> is the object to which we added the cmdset with this Command (so the chair). We just call the <code class="docutils literal notranslate"><span class="pre">do_sit/stand</span></code> on that object and pass the <code class="docutils literal notranslate"><span class="pre">caller</span></code> (the person sitting down). The <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> will do the rest.</p></li>
|
||||
<li><p><strong>Line 23</strong>: The <code class="docutils literal notranslate"><span class="pre">priority</span> <span class="pre">=</span> <span class="pre">1</span></code> on <code class="docutils literal notranslate"><span class="pre">CmdSetSit</span></code> means that same-named Commands from this cmdset merge with a bit higher priority than Commands from the on-Character-cmdset (which has <code class="docutils literal notranslate"><span class="pre">priority</span> <span class="pre">=</span> <span class="pre">0</span></code>). This means that if you have a <code class="docutils literal notranslate"><span class="pre">sit</span></code> command on your Character and comes into a room with a chair, the <code class="docutils literal notranslate"><span class="pre">sit</span></code> command on the chair will take precedence.</p></li>
|
||||
</ul>
|
||||
<p>We also need to make a change to our <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> typeclass. Open <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/sittables.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in mygame/typeclasses/sittables.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">typeclasses.objects</span> <span class="kn">import</span> <span class="n">Object</span>
|
||||
<span class="hll"><span class="kn">from</span> <span class="nn">commands.sittables</span> <span class="kn">import</span> <span class="n">CmdSetSit</span>
|
||||
</span>
|
||||
<span class="k">class</span> <span class="nc">Sittable</span><span class="p">(</span><span class="n">Object</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> (docstring)</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="hll"> <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><span class="hll"> <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">CmdSetSit</span><span class="p">)</span>
|
||||
</span> <span class="c1"># ... </span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 4</strong>: We must install the <code class="docutils literal notranslate"><span class="pre">CmdSetSit</span></code> .</p></li>
|
||||
<li><p><strong>Line 10</strong>: The <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> method will only be called once, when the object is first created.</p></li>
|
||||
<li><p><strong>Line 11</strong>: We add the command-set as a ‘default’ cmdset with <code class="docutils literal notranslate"><span class="pre">add_default</span></code>. This makes it persistent also protects it from being deleted should another cmdset be added. See <a class="reference internal" href="../../../Components/Command-Sets.html"><span class="doc std std-doc">Command Sets</span></a> for more info.</p></li>
|
||||
</ul>
|
||||
<p>Make sure to <code class="docutils literal notranslate"><span class="pre">reload</span></code> to make the code changes available.</p>
|
||||
<p>All <em>new</em> Sittables will now have your <code class="docutils literal notranslate"><span class="pre">sit</span></code> Command. Your existing <code class="docutils literal notranslate"><span class="pre">armchair</span></code> will not though. This is because <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> will not re-run for already existing objects. We can update it manually:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> update armchair
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We could also update all existing sittables (all on one line):</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">List comprehensions </p>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">[obj</span> <span class="pre">for</span> <span class="pre">obj</span> <span class="pre">in</span> <span class="pre">iterator]</span></code> is an example of a <em>list comprehension</em>. Think of it as an efficient way to construct a new list all in one line. You can read more about list comprehensions <a class="reference external" href="https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions">here in the Python docs</a>.</p>
|
||||
</aside>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py from typeclasses.sittables import Sittable ;
|
||||
[sittable.at_object_creation() for sittable in Sittable.objects.all()]
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We should now be able to use <code class="docutils literal notranslate"><span class="pre">sit</span></code> while in the room with the armchair.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> sit
|
||||
As you sit down in armchair, life feels easier.
|
||||
> stand
|
||||
You stand up from armchair.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>One issue with placing the <code class="docutils literal notranslate"><span class="pre">sit</span></code> (or <code class="docutils literal notranslate"><span class="pre">stand</span></code>) Command “on” the chair is that it will not be available when in a room without a Sittable object:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> sit
|
||||
Command 'sit' is not available. ...
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This is practical but not so good-looking; it makes it harder for the user to know a <code class="docutils literal notranslate"><span class="pre">sit</span></code> action is at all possible. Here is a trick for fixing this. Let’s add <em>another</em> Command to the bottom
|
||||
of <code class="docutils literal notranslate"><span class="pre">mygame/commands/sittables.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># after the other commands in mygame/commands/sittables.py</span>
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdNoSitStand</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Sit down or Stand up</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"sit"</span>
|
||||
<span class="hll"> <span class="n">aliases</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"stand"</span><span class="p">]</span>
|
||||
</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="hll"> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">cmdname</span> <span class="o">==</span> <span class="s2">"sit"</span><span class="p">:</span>
|
||||
</span> <span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You have nothing to sit on."</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">msg</span><span class="p">(</span><span class="s2">"You are not sitting down."</span><span class="p">)</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 9</strong>: This command responds both to <code class="docutils literal notranslate"><span class="pre">sit</span></code> and <code class="docutils literal notranslate"><span class="pre">stand</span></code> because we added <code class="docutils literal notranslate"><span class="pre">stand</span></code> to its <code class="docutils literal notranslate"><span class="pre">aliases</span></code> list. Command aliases have the same ‘weight’ as the <code class="docutils literal notranslate"><span class="pre">key</span></code> of the command, both equally identify the Command.</p></li>
|
||||
<li><p><strong>Line 12</strong>: The <code class="docutils literal notranslate"><span class="pre">.cmdname</span></code> of a <code class="docutils literal notranslate"><span class="pre">Command</span></code> holds the name actually used to call it. This will be one of <code class="docutils literal notranslate"><span class="pre">"sit"</span></code> or <code class="docutils literal notranslate"><span class="pre">"stand"</span></code>. This leads to different return messages.</p></li>
|
||||
</ul>
|
||||
<p>We don’t need a new CmdSet for this, instead we will add this to the default Character cmdset. Open <code class="docutils literal notranslate"><span class="pre">mygame/commands/default_cmdsets.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/default_cmdsets.py</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
<span class="kn">from</span> <span class="nn">commands</span> <span class="kn">import</span> <span class="n">sittables</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CharacterCmdSet</span><span class="p">(</span><span class="n">CmdSet</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> (docstring)</span>
|
||||
<span class="sd"> """</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="c1"># ...</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">sittables</span><span class="o">.</span><span class="n">CmdNoSitStand</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>As usual, make sure to <code class="docutils literal notranslate"><span class="pre">reload</span></code> the server to have the new code recognized.</p>
|
||||
<p>To test we’ll build a new location without any comfy armchairs and go there:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> tunnel n = kitchen
|
||||
north
|
||||
> sit
|
||||
You have nothing to sit on.
|
||||
> south
|
||||
sit
|
||||
As you sit down in armchair, life feels easier.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We now have a fully functioning <code class="docutils literal notranslate"><span class="pre">sit</span></code> action that is contained with the chair itself. When no chair is around, a default error message is shown.</p>
|
||||
<p>How does this work? There are two cmdsets at play, both of which have a <code class="docutils literal notranslate"><span class="pre">sit/stand</span></code> Command - one on the <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> (armchair) and the other on us (via the <code class="docutils literal notranslate"><span class="pre">CharacterCmdSet</span></code>). Since we set a <code class="docutils literal notranslate"><span class="pre">priority=1</span></code> on the chair’s cmdset (and <code class="docutils literal notranslate"><span class="pre">CharacterCmdSet</span></code> has <code class="docutils literal notranslate"><span class="pre">priority=0</span></code>), there will be no command-collision: the chair’s <code class="docutils literal notranslate"><span class="pre">sit</span></code> takes precedence over the <code class="docutils literal notranslate"><span class="pre">sit</span></code> defined on us … until there is no chair around.</p>
|
||||
<p>So this handles <code class="docutils literal notranslate"><span class="pre">sit</span></code>. What about <code class="docutils literal notranslate"><span class="pre">stand</span></code>? That will work just fine:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> stand
|
||||
You stand up from armchair.
|
||||
> north
|
||||
> stand
|
||||
You are not sitting down.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We have one remaining problem with <code class="docutils literal notranslate"><span class="pre">stand</span></code> though - what happens when you are sitting down and try to <code class="docutils literal notranslate"><span class="pre">stand</span></code> in a room with more than one <code class="docutils literal notranslate"><span class="pre">Sittable</span></code>:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> stand
|
||||
More than one match for 'stand' (please narrow target):
|
||||
stand-1 (armchair)
|
||||
stand-2 (sofa)
|
||||
stand-3 (barstool)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Since all the sittables have the <code class="docutils literal notranslate"><span class="pre">stand</span></code> Command on them, you’ll get a multi-match error. This <em>works</em> … but you could pick <em>any</em> of those sittables to “stand up from”. That’s really weird.</p>
|
||||
<p>With <code class="docutils literal notranslate"><span class="pre">sit</span></code> it was okay to get a choice - Evennia can’t know which chair we intended to sit on. But once we sit we sure know from which chair we should stand up from! We must make sure that we only get the command from the chair we are actually sitting on.</p>
|
||||
<p>We will fix this with a <a class="reference internal" href="../../../Components/Locks.html"><span class="doc std std-doc">Lock</span></a> and a custom <code class="docutils literal notranslate"><span class="pre">lock</span> <span class="pre">function</span></code>. We want a lock on the <code class="docutils literal notranslate"><span class="pre">stand</span></code> Command that only makes it available when the caller is actually sitting on the chair that particular <code class="docutils literal notranslate"><span class="pre">stand</span></code> command is attached to.</p>
|
||||
<p>First let’s add the lock so we see what we want. Open <code class="docutils literal notranslate"><span class="pre">mygame/commands/sittables.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in mygame/commands/sittables.py</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdStand</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Stand up.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"stand"</span>
|
||||
<span class="hll"> <span class="n">locks</span> <span class="o">=</span> <span class="s2">"cmd:sitsonthis()"</span>
|
||||
</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="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">do_stand</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="c1"># ...</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 10</strong>: This is the lock definition. It’s on the form <code class="docutils literal notranslate"><span class="pre">condition:lockfunc</span></code>. The <code class="docutils literal notranslate"><span class="pre">cmd:</span></code> type lock is checked by Evennia when determining if a user has access to a Command at all. We want the lock-function to only return <code class="docutils literal notranslate"><span class="pre">True</span></code> if this command is on a chair which the caller is sitting on.
|
||||
What will be checked is the <code class="docutils literal notranslate"><span class="pre">sitsonthis</span></code> <em>lock function</em> which doesn’t exist yet.</p></li>
|
||||
</ul>
|
||||
<p>Open <code class="docutils literal notranslate"><span class="pre">mygame/server/conf/lockfuncs.py</span></code> to add it!</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/server/conf/lockfuncs.py</span>
|
||||
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd">(module lockstring)</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">sitsonthis</span><span class="p">(</span><span class="n">accessing_obj</span><span class="p">,</span> <span class="n">accessed_obj</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> True if accessing_obj is sitting on/in the accessed_obj.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">return</span> <span class="n">accessed_obj</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">sitter</span> <span class="o">==</span> <span class="n">accessing_obj</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Evennia knows that <em>all</em> functions in <code class="docutils literal notranslate"><span class="pre">mygame/server/conf/lockfuncs</span></code> should be possible to use in a lock definition.</p>
|
||||
<p>All lock functions must acccept the same arguments. The arguments are required and Evennia will pass all relevant objects as needed.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Lockfuncs</p>
|
||||
<p>Evennia provides a large number of default lockfuncs, such as checking permission-levels, if you are carrying or are inside the accessed object etc. There is no concept of ‘sitting’ in default Evennia however, so this we need to specify ourselves.</p>
|
||||
</aside>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">accessing_obj</span></code> is the one trying to access the lock. So us, in this case.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">accessed_obj</span></code> is the entity we are trying to gain a particular type of access to. Since we define the lock on the <code class="docutils literal notranslate"><span class="pre">CmdStand</span></code> class, this is <em>the command instance</em>. We are however not interested in that, but the object the command is assigned to (the chair). The object is available on the Command as <code class="docutils literal notranslate"><span class="pre">.obj</span></code>. So here, <code class="docutils literal notranslate"><span class="pre">accessed_obj.obj</span></code> is the chair.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">args</span></code> is a tuple holding any arguments passed to the lockfunc. Since we use <code class="docutils literal notranslate"><span class="pre">sitsondthis()</span></code> this will be empty (and if we add anything, it will be ignored).</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">kwargs</span></code> is a tuple of keyword arguments passed to the lockfuncs. This will be empty as well in our example.</p></li>
|
||||
</ul>
|
||||
<p>Make sure you <code class="docutils literal notranslate"><span class="pre">reload</span></code>.</p>
|
||||
<p>If you are superuser, it’s important that you <code class="docutils literal notranslate"><span class="pre">quell</span></code> yourself before trying this out. This is because the superuser bypasses all locks - it can never get locked out, but it means it will also not see the effects of a lock like this.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> quell
|
||||
> stand
|
||||
You stand up from armchair
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>None of the other sittables’ <code class="docutils literal notranslate"><span class="pre">stand</span></code> commands passed the lock and only the one we are actually sitting on did! This is a fully functional chair now!</p>
|
||||
<p>Adding a Command to the chair object like this is powerful and is a good technique to know. It does come with some caveats though, as we’ve seen.</p>
|
||||
<p>We’ll now try another way to add the <code class="docutils literal notranslate"><span class="pre">sit/stand</span></code> commands.</p>
|
||||
</section>
|
||||
<section id="command-variant-2-command-on-character">
|
||||
<h3><span class="section-number">13.3.2. </span>Command variant 2: Command on Character<a class="headerlink" href="#command-variant-2-command-on-character" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Before we start with this, delete the chairs you’ve created:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> del armchair
|
||||
> del sofa
|
||||
> (etc)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The make the following changes:</p>
|
||||
<ul class="simple">
|
||||
<li><p>In <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/sittables.py</span></code>, comment out the entire <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> method.</p></li>
|
||||
<li><p>In <code class="docutils literal notranslate"><span class="pre">mygame/commands/default_cmdsets.py</span></code>, comment out the line <code class="docutils literal notranslate"><span class="pre">self.add(sittables.CmdNoSitStand)</span></code>.</p></li>
|
||||
</ul>
|
||||
<p>This disables the on-object command solution so we can try an alternative. Make sure to <code class="docutils literal notranslate"><span class="pre">reload</span></code> so the changes are known to Evennia.</p>
|
||||
<p>In this variation we will put the <code class="docutils literal notranslate"><span class="pre">sit</span></code> and <code class="docutils literal notranslate"><span class="pre">stand</span></code> commands on the <code class="docutils literal notranslate"><span class="pre">Character</span></code> instead of on the chair. This makes some things easier, but makes the Commands themselves more complex because they will not know which chair to sit on. We can’t just do <code class="docutils literal notranslate"><span class="pre">sit</span></code> anymore. This is how it will work:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> sit <chair>
|
||||
You sit on chair.
|
||||
> stand
|
||||
You stand up from chair.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Open <code class="docutils literal notranslate"><span class="pre">mygame/commands/sittables.py</span></code> again. We’ll add a new sit-command. We name the class <code class="docutils literal notranslate"><span class="pre">CmdSit2</span></code> since we already have <code class="docutils literal notranslate"><span class="pre">CmdSit</span></code> from the previous example. We put everything at the end of the module to keep it separate.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span>
|
||||
<span class="normal">22</span>
|
||||
<span class="normal">23</span>
|
||||
<span class="normal">24</span>
|
||||
<span class="normal">25</span>
|
||||
<span class="normal">26</span>
|
||||
<span class="normal">27</span>
|
||||
<span class="normal">28</span>
|
||||
<span class="normal">29</span>
|
||||
<span class="normal">30</span>
|
||||
<span class="normal">31</span>
|
||||
<span class="normal">32</span>
|
||||
<span class="normal">33</span>
|
||||
<span class="normal">34</span>
|
||||
<span class="normal">35</span>
|
||||
<span class="normal">36</span>
|
||||
<span class="normal">37</span>
|
||||
<span class="normal">38</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in mygame/commands/sittables.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span><span class="p">,</span> <span class="n">CmdSet</span>
|
||||
<span class="hll"><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">InterruptCommand</span>
|
||||
</span>
|
||||
<span class="k">class</span> <span class="nc">CmdSit</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="c1"># new from here</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdSit2</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Sit down.</span>
|
||||
|
||||
<span class="sd"> Usage:</span>
|
||||
<span class="sd"> sit <sittable></span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"sit"</span>
|
||||
|
||||
<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="bp">self</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="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">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Sit on what?"</span><span class="p">)</span>
|
||||
<span class="hll"> <span class="k">raise</span> <span class="n">InterruptCommand</span>
|
||||
</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="c1"># self.search handles all error messages etc.</span>
|
||||
<span class="hll"> <span class="n">sittable</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="p">)</span>
|
||||
</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">sittable</span><span class="p">:</span>
|
||||
<span class="k">return</span>
|
||||
<span class="hll"> <span class="k">try</span><span class="p">:</span>
|
||||
</span> <span class="n">sittable</span><span class="o">.</span><span class="n">do_sit</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="k">except</span> <span class="ne">AttributeError</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">"You can't sit on that!"</span><span class="p">)</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Raising exceptions</p>
|
||||
<p>Raising an exception allows for immediately interrupting the current program flow. Python automatically raises error-exceptions when detecting problems with the code. It will be raised up through the sequence of called code (the ‘stack’) until it’s either <code class="docutils literal notranslate"><span class="pre">caught</span></code> with a <code class="docutils literal notranslate"><span class="pre">try</span> <span class="pre">...</span> <span class="pre">except</span></code> or reaches the outermost scope where it’ll be logged or displayed. In this case Evennia knows to catch the <code class="docutils literal notranslate"><span class="pre">InterruptCommand</span></code> exception and stop the command execution early.</p>
|
||||
</aside>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 4</strong>: We need the <code class="docutils literal notranslate"><span class="pre">InterruptCommand</span></code> to be able to abort command parsing early (see below).</p></li>
|
||||
<li><p><strong>Line 27</strong>: The <code class="docutils literal notranslate"><span class="pre">parse</span></code> method runs before the <code class="docutils literal notranslate"><span class="pre">func</span></code> method on a <code class="docutils literal notranslate"><span class="pre">Command</span></code>. If no argument is provided to the command, we want to fail early, already in <code class="docutils literal notranslate"><span class="pre">parse</span></code>, so <code class="docutils literal notranslate"><span class="pre">func</span></code> never fires. Just <code class="docutils literal notranslate"><span class="pre">return</span></code> is not enough to do that, we need to <code class="docutils literal notranslate"><span class="pre">raise</span> <span class="pre">InterruptCommand</span></code>. Evennia will see a raised <code class="docutils literal notranslate"><span class="pre">InterruptCommand</span></code> as a sign it should immediately abort the command execution.</p></li>
|
||||
<li><p><strong>Line 32</strong>: We use the parsed command arguments as the target-chair to search for. As discussed in the <a class="reference internal" href="Beginner-Tutorial-Searching-Things.html"><span class="doc std std-doc">search tutorial</span></a>, <code class="docutils literal notranslate"><span class="pre">self.caller.search()</span></code> will handle error messages itself. So if it returns <code class="docutils literal notranslate"><span class="pre">None</span></code>, we can just <code class="docutils literal notranslate"><span class="pre">return</span></code>.</p></li>
|
||||
<li><p><strong>Line 35-38</strong>: The <code class="docutils literal notranslate"><span class="pre">try...except</span></code> block ‘catches’ and exception and handles it. In this case we try to run <code class="docutils literal notranslate"><span class="pre">do_sit</span></code> on the object. If the object we found is <em>not</em> a <code class="docutils literal notranslate"><span class="pre">Sittable</span></code>, it will likely not have a <code class="docutils literal notranslate"><span class="pre">do_sit</span></code> method and an <code class="docutils literal notranslate"><span class="pre">AttributeError</span></code> will be raised. We should handle that case gracefully.</p></li>
|
||||
</ul>
|
||||
<p>Let’s do the <code class="docutils literal notranslate"><span class="pre">stand</span></code> command while we are at it. Since the Command is external to the chair we need to figure out if we are sitting down or not.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># end of mygame/commands/sittables.py</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdStand2</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Stand up.</span>
|
||||
|
||||
<span class="sd"> Usage:</span>
|
||||
<span class="sd"> stand</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"stand"</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="n">caller</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span>
|
||||
<span class="c1"># if we are sitting, this should be set on us</span>
|
||||
<span class="hll"> <span class="n">sittable</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">is_sitting</span>
|
||||
</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">sittable</span><span class="p">:</span>
|
||||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You are not sitting down."</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="hll"> <span class="n">sittable</span><span class="o">.</span><span class="n">do_stand</span><span class="p">(</span><span class="n">caller</span><span class="p">)</span>
|
||||
</span></pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 17</strong>: We didn’t need the <code class="docutils literal notranslate"><span class="pre">is_sitting</span></code> Attribute for the first version of these Commands, but we do need it now. Since we have this, we don’t need to search and know just which chair we sit on. If we don’t have this Attribute set, we are not sitting anywhere.</p></li>
|
||||
<li><p><strong>Line 21</strong>: We stand up using the sittable we found.</p></li>
|
||||
</ul>
|
||||
<p>All that is left now is to make <code class="docutils literal notranslate"><span class="pre">sit</span></code> and <code class="docutils literal notranslate"><span class="pre">stand</span></code> available to us. This type of Command should be available to us all the time so we can put it in the default Cmdset on the Character. Open <code class="docutils literal notranslate"><span class="pre">mygame/commands/default_cmdsets.py</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/commands/default_cmdsets.py</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
<span class="kn">from</span> <span class="nn">commands</span> <span class="kn">import</span> <span class="n">sittables</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CharacterCmdSet</span><span class="p">(</span><span class="n">CmdSet</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> (docstring)</span>
|
||||
<span class="sd"> """</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="c1"># ...</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">sittables</span><span class="o">.</span><span class="n">CmdSit2</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">sittables</span><span class="o">.</span><span class="n">CmdStand2</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Make sure to <code class="docutils literal notranslate"><span class="pre">reload</span></code>.</p>
|
||||
<p>Now let’s try it out:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> create/drop sofa : sittables.Sittable
|
||||
> sit sofa
|
||||
You sit down on sofa.
|
||||
> stand
|
||||
You stand up from sofa.
|
||||
> north
|
||||
> sit sofa
|
||||
> You can't find 'sofa'.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Storing commands on the Character centralizes them, but you must instead search or store any external objects you want that command to interact on.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="conclusions">
|
||||
<h2><span class="section-number">13.4. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In this lesson we built ourselves a chair and even a sofa!</p>
|
||||
<ul class="simple">
|
||||
<li><p>We modified our <code class="docutils literal notranslate"><span class="pre">Character</span></code> class to avoid moving when sitting down.</p></li>
|
||||
<li><p>We made a new <code class="docutils literal notranslate"><span class="pre">Sittable</span></code> typeclass</p></li>
|
||||
<li><p>We tried two ways to allow a user to interact with sittables using <code class="docutils literal notranslate"><span class="pre">sit</span></code> and <code class="docutils literal notranslate"><span class="pre">stand</span></code> commands.</p></li>
|
||||
</ul>
|
||||
<p>Eagle-eyed readers will notice that the <code class="docutils literal notranslate"><span class="pre">stand</span></code> command sitting “on” the chair (variant 1) would work just fine together with the <code class="docutils literal notranslate"><span class="pre">sit</span></code> command sitting “on” the Character (variant 2). There is nothing stopping you from mixing them, or even try a third solution that better fits what you have in mind.</p>
|
||||
<p>This concludes the first part of the Beginner tutorial!</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="../Part2/Beginner-Tutorial-Part2-Overview.html" title="Part 2: What We Want"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Django-queries.html" title="12. Advanced searching - Django Database queries"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">13. </span>Building a chair you can sit on</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,652 @@
|
|||
|
||||
<!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>9. Parsing Command input — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="10. Creating things" href="Beginner-Tutorial-Creating-Things.html" />
|
||||
<link rel="prev" title="8. Adding custom commands" href="Beginner-Tutorial-Adding-Commands.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Creating-Things.html" title="10. Creating things"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Adding-Commands.html" title="8. Adding custom commands"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">9. </span>Parsing Command input</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">9. Parsing Command input</a><ul>
|
||||
<li><a class="reference internal" href="#more-advanced-parsing">9.1. More advanced parsing</a></li>
|
||||
<li><a class="reference internal" href="#adding-a-command-to-an-object">9.2. Adding a Command to an object</a><ul>
|
||||
<li><a class="reference internal" href="#you-need-to-hold-the-sword">9.2.1. You need to hold the sword!</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#adding-the-command-to-a-default-cmdset">9.3. Adding the Command to a default Cmdset</a><ul>
|
||||
<li><a class="reference internal" href="#removing-commands">9.3.1. Removing Commands</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#replace-a-default-command">9.4. Replace a default command</a></li>
|
||||
<li><a class="reference internal" href="#summary">9.5. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Adding-Commands.html"
|
||||
title="previous chapter"><span class="section-number">8. </span>Adding custom commands</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Creating-Things.html"
|
||||
title="next chapter"><span class="section-number">10. </span>Creating things</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-More-on-Commands.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="parsing-command-input">
|
||||
<h1><span class="section-number">9. </span>Parsing Command input<a class="headerlink" href="#parsing-command-input" title="Permalink to this headline">¶</a></h1>
|
||||
<p>In this lesson we learn some basics about parsing the input of Commands. We will
|
||||
also learn how to add, modify and extend Evennia’s default commands.</p>
|
||||
<section id="more-advanced-parsing">
|
||||
<h2><span class="section-number">9.1. </span>More advanced parsing<a class="headerlink" href="#more-advanced-parsing" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In the <a class="reference internal" href="Beginner-Tutorial-Adding-Commands.html"><span class="doc std std-doc">last lesson</span></a> we made a <code class="docutils literal notranslate"><span class="pre">hit</span></code> Command and struck a dragon with it. You should have the code
|
||||
from that still around.</p>
|
||||
<p>Let’s expand our simple <code class="docutils literal notranslate"><span class="pre">hit</span></code> command to accept a little more complex input:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>hit <target> [[with] <weapon>]
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>That is, we want to support all of these forms</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>hit target
|
||||
hit target weapon
|
||||
hit target with weapon
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you don’t specify a weapon you’ll use your fists. It’s also nice to be able to skip “with” if
|
||||
you are in a hurry. Time to modify <code class="docutils literal notranslate"><span class="pre">mygame/commands/mycommands.py</span></code> again. Let us break out the parsing a little, in a new method <code class="docutils literal notranslate"><span class="pre">parse</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span>
|
||||
<span class="normal">22</span>
|
||||
<span class="normal">23</span>
|
||||
<span class="normal">24</span>
|
||||
<span class="normal">25</span>
|
||||
<span class="normal">26</span>
|
||||
<span class="normal">27</span>
|
||||
<span class="normal">28</span>
|
||||
<span class="normal">29</span>
|
||||
<span class="normal">30</span>
|
||||
<span class="normal">31</span>
|
||||
<span class="normal">32</span>
|
||||
<span class="normal">33</span>
|
||||
<span class="normal">34</span>
|
||||
<span class="normal">35</span>
|
||||
<span class="normal">36</span>
|
||||
<span class="normal">37</span>
|
||||
<span class="normal">38</span>
|
||||
<span class="normal">39</span>
|
||||
<span class="normal">40</span>
|
||||
<span class="normal">41</span>
|
||||
<span class="normal">42</span>
|
||||
<span class="normal">43</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1">#...</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdHit</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Hit a target.</span>
|
||||
|
||||
<span class="sd"> Usage:</span>
|
||||
<span class="sd"> hit <target></span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"hit"</span>
|
||||
|
||||
<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="hll"> <span class="bp">self</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><span class="hll"> <span class="n">target</span><span class="p">,</span> <span class="o">*</span><span class="n">weapon</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">split</span><span class="p">(</span><span class="s2">" with "</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||||
</span><span class="hll"> <span class="k">if</span> <span class="ow">not</span> <span class="n">weapon</span><span class="p">:</span>
|
||||
</span> <span class="n">target</span><span class="p">,</span> <span class="o">*</span><span class="n">weapon</span> <span class="o">=</span> <span class="n">target</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||||
<span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">target</span> <span class="o">=</span> <span class="n">target</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||||
</span> <span class="k">if</span> <span class="n">weapon</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">weapon</span> <span class="o">=</span> <span class="n">weapon</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">strip</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">weapon</span> <span class="o">=</span> <span class="s2">""</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="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">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Who do you want to hit?"</span><span class="p">)</span>
|
||||
<span class="k">return</span>
|
||||
<span class="c1"># get the target for the hit</span>
|
||||
<span class="hll"> <span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">)</span>
|
||||
</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">target</span><span class="p">:</span>
|
||||
<span class="k">return</span>
|
||||
<span class="c1"># get and handle the weapon</span>
|
||||
<span class="n">weapon</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">weapon</span><span class="p">:</span>
|
||||
<span class="hll"> <span class="n">weapon</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">weapon</span><span class="p">)</span>
|
||||
</span> <span class="k">if</span> <span class="n">weapon</span><span class="p">:</span>
|
||||
<span class="n">weaponstr</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">weapon</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">weaponstr</span> <span class="o">=</span> <span class="s2">"bare fists"</span>
|
||||
|
||||
<span class="hll"> <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">"You hit </span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> with </span><span class="si">{</span><span class="n">weaponstr</span><span class="si">}</span><span class="s2">!"</span><span class="p">)</span>
|
||||
</span> <span class="n">target</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You got hit by </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> with </span><span class="si">{</span><span class="n">weaponstr</span><span class="si">}</span><span class="s2">!"</span><span class="p">)</span>
|
||||
<span class="c1"># ...</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">parse</span></code> method is a special one Evennia knows to call <em>before</em> <code class="docutils literal notranslate"><span class="pre">func</span></code>. At this time it has access to all the same on-command variables as <code class="docutils literal notranslate"><span class="pre">func</span></code> does. Using <code class="docutils literal notranslate"><span class="pre">parse</span></code> not only makes things a little easier to read, it also means you can easily let other Commands <em>inherit</em> your parsing - if you wanted some other Command to also understand input on the form <code class="docutils literal notranslate"><span class="pre"><arg></span> <span class="pre">with</span> <span class="pre"><arg></span></code> you’d inherit from this class and just implement the <code class="docutils literal notranslate"><span class="pre">func</span></code> needed for that command without implementing <code class="docutils literal notranslate"><span class="pre">parse</span></code> anew.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Tuples and Lists</p>
|
||||
<ul class="simple">
|
||||
<li><p>A <code class="docutils literal notranslate"><span class="pre">list</span></code> is written as <code class="docutils literal notranslate"><span class="pre">[a,</span> <span class="pre">b,</span> <span class="pre">c,</span> <span class="pre">d,</span> <span class="pre">...]</span></code>. You can add and grow/shrink a list after it was first created.</p></li>
|
||||
<li><p>A <code class="docutils literal notranslate"><span class="pre">tuple</span></code> is written as <code class="docutils literal notranslate"><span class="pre">(a,</span> <span class="pre">b,</span> <span class="pre">c,</span> <span class="pre">d,</span> <span class="pre">...)</span></code>. A tuple cannot be modified once it is created.</p></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<ul>
|
||||
<li><p><strong>Line 14</strong> - We do the stripping of <code class="docutils literal notranslate"><span class="pre">self.args</span></code> once and for all here. We also store the stripped version back
|
||||
into <code class="docutils literal notranslate"><span class="pre">self.args</span></code>, overwriting it. So there is no way to get back the non-stripped version from here on, which is fine
|
||||
for this command.</p></li>
|
||||
<li><p><strong>Line 15</strong> - This makes use of the <code class="docutils literal notranslate"><span class="pre">.split</span></code> method of strings. <code class="docutils literal notranslate"><span class="pre">.split</span></code> will, well, split the string by some criterion.
|
||||
<code class="docutils literal notranslate"><span class="pre">.split("</span> <span class="pre">with</span> <span class="pre">",</span> <span class="pre">1)</span></code> means “split the string once, around the substring <code class="docutils literal notranslate"><span class="pre">"</span> <span class="pre">with</span> <span class="pre">"</span></code> if it exists”. The result
|
||||
of this split is a <em>list</em>. Just how that list looks depends on the string we are trying to split:</p>
|
||||
<ol class="simple">
|
||||
<li><p>If we entered just <code class="docutils literal notranslate"><span class="pre">hit</span> <span class="pre">smaug</span></code>, we’d be splitting just <code class="docutils literal notranslate"><span class="pre">"smaug"</span></code> which would give the result <code class="docutils literal notranslate"><span class="pre">["smaug"]</span></code>.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">hit</span> <span class="pre">smaug</span> <span class="pre">sword</span></code> gives <code class="docutils literal notranslate"><span class="pre">["smaug</span> <span class="pre">sword"]</span></code></p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">hit</span> <span class="pre">smaug</span> <span class="pre">with</span> <span class="pre">sword</span></code> gives <code class="docutils literal notranslate"><span class="pre">["smaug",</span> <span class="pre">"sword"]</span></code></p></li>
|
||||
</ol>
|
||||
<p>So we get a list of 1 or 2 elements. We assign it to two variables like this, <code class="docutils literal notranslate"><span class="pre">target,</span> <span class="pre">*weapon</span> <span class="pre">=</span> </code>. That asterisk in <code class="docutils literal notranslate"><span class="pre">*weapon</span></code> is a nifty trick - it will automatically become a tuple of <em>0 or more</em> values. It sorts of “soaks” up everything left over.</p>
|
||||
<ol class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">target</span></code> becomes <code class="docutils literal notranslate"><span class="pre">"smaug"</span></code> and <code class="docutils literal notranslate"><span class="pre">weapon</span></code> becomes <code class="docutils literal notranslate"><span class="pre">()</span></code> (an empty tuple)</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">target</span></code> becomes <code class="docutils literal notranslate"><span class="pre">"smaug</span> <span class="pre">sword"</span></code> and <code class="docutils literal notranslate"><span class="pre">weapon</span></code> becomes <code class="docutils literal notranslate"><span class="pre">()</span></code></p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">target</span></code> becomes <code class="docutils literal notranslate"><span class="pre">"smaug"</span></code> and <code class="docutils literal notranslate"><span class="pre">weapon</span></code> becomes <code class="docutils literal notranslate"><span class="pre">("sword",)</span></code> (this is a tuple with one element, the comma <a class="reference external" href="https://docs.python.org/3/tutorial/datastructures.html?highlight=tuple#tuples-and-sequences">is required</a> to indicate this).</p></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><p><strong>Lines 16-17</strong> - In this <code class="docutils literal notranslate"><span class="pre">if</span></code> condition we check if <code class="docutils literal notranslate"><span class="pre">weapon</span></code> is falsy (that is, the empty list). This can happen
|
||||
under two conditions (from the example above):</p>
|
||||
<ol class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">target</span></code> is simply <code class="docutils literal notranslate"><span class="pre">smaug</span></code></p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">target</span></code> is <code class="docutils literal notranslate"><span class="pre">smaug</span> <span class="pre">sword</span></code></p></li>
|
||||
</ol>
|
||||
<p>To separate these cases we split <code class="docutils literal notranslate"><span class="pre">target</span></code> once again, this time by empty space <code class="docutils literal notranslate"><span class="pre">"</span> <span class="pre">"</span></code>. Again we store the result back with <code class="docutils literal notranslate"><span class="pre">target,</span> <span class="pre">*weapon</span> <span class="pre">=</span></code>. The result will be one of the following:</p>
|
||||
<ol class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">target</span></code> remains <code class="docutils literal notranslate"><span class="pre">"smaug"</span></code> and <code class="docutils literal notranslate"><span class="pre">weapon</span></code> remains <code class="docutils literal notranslate"><span class="pre">[]</span></code></p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">target</span></code> becomes <code class="docutils literal notranslate"><span class="pre">"smaug"</span></code> and <code class="docutils literal notranslate"><span class="pre">weapon</span></code> becomes <code class="docutils literal notranslate"><span class="pre">("sword",)</span></code></p></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li><p><strong>Lines 18-22</strong> - We now store <code class="docutils literal notranslate"><span class="pre">target</span></code> and <code class="docutils literal notranslate"><span class="pre">weapon</span></code> into <code class="docutils literal notranslate"><span class="pre">self.target</span></code> and <code class="docutils literal notranslate"><span class="pre">self.weapon</span></code>. We must store on <code class="docutils literal notranslate"><span class="pre">self</span></code> in order for these local variables to become available in <code class="docutils literal notranslate"><span class="pre">func</span></code> later. Note that once we know that <code class="docutils literal notranslate"><span class="pre">weapon</span></code> exists, it must be a tuple (like <code class="docutils literal notranslate"><span class="pre">("sword",)</span></code>), so we use <code class="docutils literal notranslate"><span class="pre">weapon[0]</span></code> to get the first element of that tuple (tuples and lists in Python are indexed from 0). The instruction <code class="docutils literal notranslate"><span class="pre">weapon[0].strip()</span></code> can be read as “get the first string stored in the tuple <code class="docutils literal notranslate"><span class="pre">weapon</span></code> and remove all extra whitespace on it with <code class="docutils literal notranslate"><span class="pre">.strip()</span></code>”. If we forgot the <code class="docutils literal notranslate"><span class="pre">[0]</span></code> here, we’d get an error since a tuple (unlike the string inside the tuple) does not have the <code class="docutils literal notranslate"><span class="pre">.strip()</span></code> method.</p></li>
|
||||
</ul>
|
||||
<p>Now onto the <code class="docutils literal notranslate"><span class="pre">func</span></code> method. The main difference is we now have <code class="docutils literal notranslate"><span class="pre">self.target</span></code> and <code class="docutils literal notranslate"><span class="pre">self.weapon</span></code> available for convenient use.</p>
|
||||
<aside class="sidebar">
|
||||
<p>Here we create the messages to send to each side of the fight explicitly. Later we’ll find out how to use Evennia’s <a class="reference internal" href="../../../Components/FuncParser.html"><span class="doc std std-doc">inline functions</span></a> to send a single string that looks different depending on who sees it.</p>
|
||||
</aside>
|
||||
<ul>
|
||||
<li><p><strong>Lines 29 and 35</strong> - We make use of the previously parsed search terms for the target and weapon to find the
|
||||
respective resource.</p></li>
|
||||
<li><p><strong>Lines 34-39</strong> - Since the weapon is optional, we need to supply a default (use our fists!) if it’s not set. We
|
||||
use this to create a <code class="docutils literal notranslate"><span class="pre">weaponstr</span></code> that is different depending on if we have a weapon or not.</p></li>
|
||||
<li><p><strong>Lines 41-42</strong> - We merge the <code class="docutils literal notranslate"><span class="pre">weaponstr</span></code> with our attack texts and send it to attacker and target respectively.
|
||||
Let’s try it out!</p>
|
||||
<blockquote>
|
||||
<div><p>reload
|
||||
hit smaug with sword
|
||||
Could not find ‘sword’.
|
||||
You hit smaug with bare fists!</p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Oops, our <code class="docutils literal notranslate"><span class="pre">self.caller.search(self.weapon)</span></code> is telling us that it found no sword. This is reasonable (we don’t have a sword). Since we are not <code class="docutils literal notranslate"><span class="pre">return</span></code>ing when failing to find a weapon in the way we do if we find no <code class="docutils literal notranslate"><span class="pre">target</span></code>, we still continue fighting with our bare hands.</p>
|
||||
<p>This won’t do. Let’s make ourselves a sword:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> create sword
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Since we didn’t specify <code class="docutils literal notranslate"><span class="pre">/drop</span></code>, the sword will end up in our inventory and can seen with the <code class="docutils literal notranslate"><span class="pre">i</span></code> or
|
||||
<code class="docutils literal notranslate"><span class="pre">inventory</span></code> command. The <code class="docutils literal notranslate"><span class="pre">.search</span></code> helper will still find it there. There is no need to reload to see this
|
||||
change (no code changed, only stuff in the database).</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> hit smaug with sword
|
||||
You hit smaug with sword!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Poor Smaug.</p>
|
||||
</section>
|
||||
<section id="adding-a-command-to-an-object">
|
||||
<h2><span class="section-number">9.2. </span>Adding a Command to an object<a class="headerlink" href="#adding-a-command-to-an-object" title="Permalink to this headline">¶</a></h2>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Command Sets on Characters </p>
|
||||
<p>In case you wonder, the ‘Character CmdSet’ on <code class="docutils literal notranslate"><span class="pre">Characters</span></code> is configured to be available to <em>only</em> that Character. If not, you’d get command multi-matches for things like <code class="docutils literal notranslate"><span class="pre">look</span></code> whenever you were in the same room with another character using the same command set. See <a class="reference internal" href="../../../Components/Command-Sets.html"><span class="doc std std-doc">Command Sets</span></a> docs for more info.</p>
|
||||
</aside>
|
||||
<p>As we learned in the lesson about <a class="reference internal" href="Beginner-Tutorial-Adding-Commands.html"><span class="doc std std-doc">Adding commands</span></a>, Commands are are grouped in Command Sets. Such Command Sets are attached to an object with <code class="docutils literal notranslate"><span class="pre">obj.cmdset.add()</span></code> and will then be available for that object to use.</p>
|
||||
<p>What we didn’t mention before is that by default those commands are <em>also available to those in the same location as that object</em>. If you did the <a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html"><span class="doc std std-doc">Building quickstart lesson</span></a> you’ve seen an example of this with the “Red Button” object. The <a class="reference internal" href="Beginner-Tutorial-Tutorial-World.html"><span class="doc std std-doc">Tutorial world</span></a> also has many examples of objects with commands on them.</p>
|
||||
<p>To show how this could work, let’s put our ‘hit’ Command on our simple <code class="docutils literal notranslate"><span class="pre">sword</span></code> object from the previous section.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py self.search("sword").cmdset.add("commands.mycommands.MyCmdSet", persistent=True)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We find the sword (it’s still in our inventory so <code class="docutils literal notranslate"><span class="pre">self.search</span></code> should be able to find it), then
|
||||
add <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code> to it. This actually adds both <code class="docutils literal notranslate"><span class="pre">hit</span></code> and <code class="docutils literal notranslate"><span class="pre">echo</span></code> to the sword, which is fine.</p>
|
||||
<p>Let’s try to swing it!</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> hit
|
||||
More than one match for 'hit' (please narrow target):
|
||||
hit-1 (sword #11)
|
||||
hit-2
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Multi-matches</p>
|
||||
<p>Some game engines will just pick the first hit when finding more than one. Evennia will always give you a choice. The reason for this is that Evennia cannot know if <code class="docutils literal notranslate"><span class="pre">hit</span></code> and <code class="docutils literal notranslate"><span class="pre">hit</span></code> are different or the same - maybe it behaves differently depending on the object it sits on? Besides, imagine if you had a red and a blue button both with the command <code class="docutils literal notranslate"><span class="pre">push</span></code> on it. Now you just write <code class="docutils literal notranslate"><span class="pre">push</span></code>. Wouldn’t you prefer to be asked <em>which</em> button you really wanted to push?</p>
|
||||
</aside>
|
||||
<p>Woah, that didn’t go as planned. Evennia actually found <em>two</em> <code class="docutils literal notranslate"><span class="pre">hit</span></code> commands and didn’t know which one to use (<em>we</em> know they are the same, but Evennia can’t be sure of that). As we can see, <code class="docutils literal notranslate"><span class="pre">hit-1</span></code> is the one found on the sword. The other one is from adding <code class="docutils literal notranslate"><span class="pre">MyCmdSet</span></code> to ourself earlier. It’s easy enough to tell Evennia which one you meant:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> hit-1
|
||||
Who do you want to hit?
|
||||
> hit-2
|
||||
Who do you want to hit?
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In this case we don’t need both command-sets, so let’s just keep the one on the sword:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py self.cmdset.remove("commands.mycommands.MyCmdSet")
|
||||
> hit
|
||||
Who do you want to hit?
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now try making a new location and then drop the sword in it.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> tunnel n = kitchen
|
||||
> n
|
||||
> drop sword
|
||||
> s
|
||||
> hit
|
||||
Command 'hit' is not available. Maybe you meant ...
|
||||
> n
|
||||
> hit
|
||||
Who do you want to hit?
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">hit</span></code> command is only available if you hold <em>or</em> are in the same room as the sword.</p>
|
||||
<section id="you-need-to-hold-the-sword">
|
||||
<h3><span class="section-number">9.2.1. </span>You need to hold the sword!<a class="headerlink" href="#you-need-to-hold-the-sword" title="Permalink to this headline">¶</a></h3>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Locks</p>
|
||||
<p>Evennia Locks are defined as a mini-language defined in <code class="docutils literal notranslate"><span class="pre">lockstrings</span></code>. The lockstring is on a form <code class="docutils literal notranslate"><span class="pre"><situation>:<lockfuncs></span></code>, where <code class="docutils literal notranslate"><span class="pre">situation</span></code> determines when this lock applies and the <code class="docutils literal notranslate"><span class="pre">lockfuncs</span></code> (there can be more than one) are run to determine if the lock-check passes or not depending on circumstance.</p>
|
||||
</aside>
|
||||
<p>Let’s get a little ahead of ourselves and make it so you have to <em>hold</em> the sword for the <code class="docutils literal notranslate"><span class="pre">hit</span></code> command to be available. This involves a <a class="reference internal" href="../../../Components/Locks.html"><span class="doc std std-doc">Lock</span></a>. We’ll cover locks in more detail later, just know that they are useful for limiting the kind of things you can do with an object, including limiting just when you can call commands on it.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py self.search("sword").locks.add("call:holds()")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We added a new lock to the sword. The <em>lockstring</em> <code class="docutils literal notranslate"><span class="pre">"call:holds()"</span></code> means that you can only <em>call</em> commands on this object if you are <em>holding</em> the object (that is, it’s in your inventory).</p>
|
||||
<p>For locks to work, you cannot be <em>superuser</em>, since the superuser passes all locks. You need to <code class="docutils literal notranslate"><span class="pre">quell</span></code> yourself first:</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">quell/unquell</p>
|
||||
<p>Quelling allows you as a developer to take on the role of players with less priveleges. This is useful for testing and debugging, in particular since a superuser has a little <code class="docutils literal notranslate"><span class="pre">too</span></code> much power sometimes. Use <code class="docutils literal notranslate"><span class="pre">unquell</span></code> to get back to your normal self.</p>
|
||||
</aside>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> quell
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If the sword lies on the ground, try</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> hit
|
||||
Command 'hit' is not available. ..
|
||||
> get sword
|
||||
> hit
|
||||
> Who do you want to hit?
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Finally, we get rid of ours sword so we have a clean slate with no more <code class="docutils literal notranslate"><span class="pre">hit</span></code> commands floating around. We can do that in two ways:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>delete sword
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>or</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>py self.search("sword").delete()
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="adding-the-command-to-a-default-cmdset">
|
||||
<h2><span class="section-number">9.3. </span>Adding the Command to a default Cmdset<a class="headerlink" href="#adding-the-command-to-a-default-cmdset" title="Permalink to this headline">¶</a></h2>
|
||||
<p>As we have seen we can use <code class="docutils literal notranslate"><span class="pre">obj.cmdset.add()</span></code> to add a new cmdset to objects, whether that object is ourself (<code class="docutils literal notranslate"><span class="pre">self</span></code>) or other objects like the <code class="docutils literal notranslate"><span class="pre">sword</span></code>. Doing this this way is a little cumbersome though. It would be better to add this to all characters.</p>
|
||||
<p>The default cmdset are defined in <code class="docutils literal notranslate"><span class="pre">mygame/commands/default_cmdsets.py</span></code>. Open that file now:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">"""</span>
|
||||
<span class="sd">(module docstring)</span>
|
||||
<span class="sd">"""</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">default_cmds</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CharacterCmdSet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CharacterCmdSet</span><span class="p">):</span>
|
||||
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"DefaultCharacter"</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="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_cmdset_creation</span><span class="p">()</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="c1"># any commands you add below will overload the default ones</span>
|
||||
<span class="c1">#</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">AccountCmdSet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">AccountCmdSet</span><span class="p">):</span>
|
||||
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"DefaultAccount"</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="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_cmdset_creation</span><span class="p">()</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="c1"># any commands you add below will overload the default ones</span>
|
||||
<span class="c1">#</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">UnloggedinCmdSet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">UnloggedinCmdSet</span><span class="p">):</span>
|
||||
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"DefaultUnloggedin"</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="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_cmdset_creation</span><span class="p">()</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="c1"># any commands you add below will overload the default ones</span>
|
||||
<span class="c1">#</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">SessionCmdSet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">SessionCmdSet</span><span class="p">):</span>
|
||||
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"DefaultSession"</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="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_cmdset_creation</span><span class="p">()</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="c1"># any commands you add below will overload the default ones</span>
|
||||
<span class="c1">#</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">super()</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">super()</span></code> function refers to the parent of the current class and is commonly used to call same-named methods on the parent.</p>
|
||||
</aside>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">evennia.default_cmds</span></code> is a container that holds all of Evennia’s default commands and cmdsets. In this module we can see that this was imported and then a new child class was made for each cmdset. Each class looks familiar (except the <code class="docutils literal notranslate"><span class="pre">key</span></code>, that’s mainly used to easily identify the cmdset in listings). In each <code class="docutils literal notranslate"><span class="pre">at_cmdset_creation</span></code> all we do is call <code class="docutils literal notranslate"><span class="pre">super().at_cmdset_creation</span></code> which means that we call `at_cmdset_creation() on the <em>parent</em> CmdSet.
|
||||
This is what adds all the default commands to each CmdSet.</p>
|
||||
<p>When the <code class="docutils literal notranslate"><span class="pre">DefaultCharacter</span></code> (or a child of it) is created, you’ll find that the equivalence of <code class="docutils literal notranslate"><span class="pre">self.cmdset.add("default_cmdsets.CharacterCmdSet,</span> <span class="pre">persistent=True")</span></code> gets called. This means that all new Characters get this cmdset. After adding more commands to it, you just need to reload to have all characters see it.</p>
|
||||
<ul class="simple">
|
||||
<li><p>Characters (that is ‘you’ in the gameworld) has the <code class="docutils literal notranslate"><span class="pre">CharacterCmdSet</span></code>.</p></li>
|
||||
<li><p>Accounts (the thing that represents your out-of-character existence on the server) has the <code class="docutils literal notranslate"><span class="pre">AccountCmdSet</span></code></p></li>
|
||||
<li><p>Sessions (representing one single client connection) has the <code class="docutils literal notranslate"><span class="pre">SessionCmdSet</span></code></p></li>
|
||||
<li><p>Before you log in (at the connection screen) your Session have access to the <code class="docutils literal notranslate"><span class="pre">UnloggedinCmdSet</span></code>.</p></li>
|
||||
</ul>
|
||||
<p>For now, let’s add our own <code class="docutils literal notranslate"><span class="pre">hit</span></code> and <code class="docutils literal notranslate"><span class="pre">echo</span></code> commands to the <code class="docutils literal notranslate"><span class="pre">CharacterCmdSet</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># ...</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">commands</span> <span class="kn">import</span> <span class="n">mycommands</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CharacterCmdSet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CharacterCmdSet</span><span class="p">):</span>
|
||||
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"DefaultCharacter"</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="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_cmdset_creation</span><span class="p">()</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="c1"># any commands you add below will overload the default ones</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">mycommands</span><span class="o">.</span><span class="n">CmdEcho</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">mycommands</span><span class="o">.</span><span class="n">CmdHit</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> hit
|
||||
Who do you want to hit?
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Your new commands are now available for all player characters in the game. There is another way to add a bunch of commands at once, and that is to add your own <em>CmdSet</em> to the other cmdset.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">commands</span> <span class="kn">import</span> <span class="n">mycommands</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CharacterCmdSet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CharacterCmdSet</span><span class="p">):</span>
|
||||
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"DefaultCharacter"</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="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_cmdset_creation</span><span class="p">()</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="c1"># any commands you add below will overload the default ones</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">mycommands</span><span class="o">.</span><span class="n">MyCmdSet</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Which way you use depends on how much control you want, but if you already have a CmdSet,
|
||||
this is practical. A Command can be a part of any number of different CmdSets.</p>
|
||||
<section id="removing-commands">
|
||||
<h3><span class="section-number">9.3.1. </span>Removing Commands<a class="headerlink" href="#removing-commands" title="Permalink to this headline">¶</a></h3>
|
||||
<p>To remove your custom commands again, you of course just delete the change you did to
|
||||
<code class="docutils literal notranslate"><span class="pre">mygame/commands/default_cmdsets.py</span></code>. But what if you want to remove a default command?</p>
|
||||
<p>We already know that we use <code class="docutils literal notranslate"><span class="pre">cmdset.remove()</span></code> to remove a cmdset. It turns out you can
|
||||
do the same in <code class="docutils literal notranslate"><span class="pre">at_cmdset_creation</span></code>. For example, let’s remove the default <code class="docutils literal notranslate"><span class="pre">get</span></code> Command
|
||||
from Evennia. If you investigate the <code class="docutils literal notranslate"><span class="pre">default_cmds.CharacterCmdSet</span></code> parent, you’ll find that its class is <code class="docutils literal notranslate"><span class="pre">default_cmds.CmdGet</span></code> (the ‘real’ location is <code class="docutils literal notranslate"><span class="pre">evennia.commands.default.general.CmdGet</span></code>).</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># ...</span>
|
||||
<span class="kn">from</span> <span class="nn">commands</span> <span class="kn">import</span> <span class="n">mycommands</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CharacterCmdSet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CharacterCmdSet</span><span class="p">):</span>
|
||||
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"DefaultCharacter"</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="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_cmdset_creation</span><span class="p">()</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="c1"># any commands you add below will overload the default ones</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">mycommands</span><span class="o">.</span><span class="n">MyCmdSet</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CmdGet</span><span class="p">)</span>
|
||||
<span class="c1"># ...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> get
|
||||
Command 'get' is not available ...
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="replace-a-default-command">
|
||||
<h2><span class="section-number">9.4. </span>Replace a default command<a class="headerlink" href="#replace-a-default-command" title="Permalink to this headline">¶</a></h2>
|
||||
<p>At this point you already have all the pieces for how to do this! We just need to add a new
|
||||
command with the same <code class="docutils literal notranslate"><span class="pre">key</span></code> in the <code class="docutils literal notranslate"><span class="pre">CharacterCmdSet</span></code> to replace the default one.</p>
|
||||
<p>Let’s combine this with what we know about classes and how to <em>override</em> a parent class. Open <code class="docutils literal notranslate"><span class="pre">mygame/commands/mycommands.py</span></code> and make a new <code class="docutils literal notranslate"><span class="pre">get</span></code> command:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
|
||||
<span class="normal">2</span>
|
||||
<span class="normal">3</span>
|
||||
<span class="normal">4</span>
|
||||
<span class="normal">5</span>
|
||||
<span class="normal">6</span>
|
||||
<span class="normal">7</span>
|
||||
<span class="normal">8</span>
|
||||
<span class="normal">9</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># up top, by the other imports</span>
|
||||
<span class="hll"><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">default_cmds</span>
|
||||
</span>
|
||||
<span class="c1"># somewhere below</span>
|
||||
<span class="k">class</span> <span class="nc">MyCmdGet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CmdGet</span><span class="p">):</span>
|
||||
|
||||
<span class="hll"> <span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
</span><span class="hll"> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">func</span><span class="p">()</span>
|
||||
</span><span class="hll"> <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="nb">str</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">location</span><span class="o">.</span><span class="n">contents</span><span class="p">))</span>
|
||||
</span></pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 2</strong>: We import <code class="docutils literal notranslate"><span class="pre">default_cmds</span></code> so we can get the parent class.
|
||||
We made a new class and we make it <em>inherit</em> <code class="docutils literal notranslate"><span class="pre">default_cmds.CmdGet</span></code>. We don’t
|
||||
need to set <code class="docutils literal notranslate"><span class="pre">.key</span></code> or <code class="docutils literal notranslate"><span class="pre">.parse</span></code>, that’s already handled by the parent.
|
||||
In <code class="docutils literal notranslate"><span class="pre">func</span></code> we call <code class="docutils literal notranslate"><span class="pre">super().func()</span></code> to let the parent do its normal thing,</p></li>
|
||||
<li><p><strong>Line 7</strong>: By adding our own <code class="docutils literal notranslate"><span class="pre">func</span></code> we replace the one in the parent.</p></li>
|
||||
<li><p><strong>Line 8</strong>: For this simple change we still want the command to work the
|
||||
same as before, so we use <code class="docutils literal notranslate"><span class="pre">super()</span></code> to call <code class="docutils literal notranslate"><span class="pre">func</span></code> on the parent.</p></li>
|
||||
<li><p><strong>Line 9</strong>: <code class="docutils literal notranslate"><span class="pre">.location</span></code> is the place an object is at. <code class="docutils literal notranslate"><span class="pre">.contents</span></code> contains, well, the
|
||||
contents of an object. If you tried <code class="docutils literal notranslate"><span class="pre">py</span> <span class="pre">self.contents</span></code> you’d get a list that equals
|
||||
your inventory. For a room, the contents is everything in it.
|
||||
So <code class="docutils literal notranslate"><span class="pre">self.caller.location.contents</span></code> gets the contents of our current location. This is
|
||||
a <em>list</em>. In order send this to us with <code class="docutils literal notranslate"><span class="pre">.msg</span></code> we turn the list into a string. Python
|
||||
has a special function <code class="docutils literal notranslate"><span class="pre">str()</span></code> to do this.</p></li>
|
||||
</ul>
|
||||
<p>We now just have to add this so it replaces the default <code class="docutils literal notranslate"><span class="pre">get</span></code> command. Open
|
||||
<code class="docutils literal notranslate"><span class="pre">mygame/commands/default_cmdsets.py</span></code> again:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># ...</span>
|
||||
<span class="kn">from</span> <span class="nn">commands</span> <span class="kn">import</span> <span class="n">mycommands</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CharacterCmdSet</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">CharacterCmdSet</span><span class="p">):</span>
|
||||
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"DefaultCharacter"</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="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_cmdset_creation</span><span class="p">()</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="c1"># any commands you add below will overload the default ones</span>
|
||||
<span class="c1">#</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">mycommands</span><span class="o">.</span><span class="n">MyCmdSet</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">mycommands</span><span class="o">.</span><span class="n">MyCmdGet</span><span class="p">)</span>
|
||||
<span class="c1"># ...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We don’t need to use <code class="docutils literal notranslate"><span class="pre">self.remove()</span></code> first; just adding a command with the same <code class="docutils literal notranslate"><span class="pre">key</span></code> (<code class="docutils literal notranslate"><span class="pre">get</span></code>) will replace the default <code class="docutils literal notranslate"><span class="pre">get</span></code> we had from before.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Another way</p>
|
||||
<p>Instead of adding <code class="docutils literal notranslate"><span class="pre">MyCmdGet</span></code> explicitly in default_cmdset.py, you could also add it to <code class="docutils literal notranslate"><span class="pre">mycommands.MyCmdSet</span></code> and let it be added automatically here for you.</p>
|
||||
</aside>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> get
|
||||
Get What?
|
||||
[smaug, fluffy, YourName, ...]
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We just made a new <code class="docutils literal notranslate"><span class="pre">get</span></code>-command that tells us everything we could pick up (well, we can’t pick up ourselves, so there’s some room for improvement there …).</p>
|
||||
</section>
|
||||
<section id="summary">
|
||||
<h2><span class="section-number">9.5. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In this lesson we got into some more advanced string formatting - many of those tricks will help you a lot in the future! We also made a functional sword. Finally we got into how to add to, extend and replace a default command on ourselves. Knowing to add commands is a big part of making a game!</p>
|
||||
<p>We have been beating on poor Smaug for too long. Next we’ll create more things to play around with.</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="Beginner-Tutorial-Creating-Things.html" title="10. Creating things"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Adding-Commands.html" title="8. Adding custom commands"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">9. </span>Parsing Command input</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
|
||||
<!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>Part 1: What We Have — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="1. Using Commands and Building Stuff" href="Beginner-Tutorial-Building-Quickstart.html" />
|
||||
<link rel="prev" title="Beginner Tutorial" href="../Beginner-Tutorial-Overview.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Building-Quickstart.html" title="1. Using Commands and Building Stuff"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Beginner-Tutorial-Overview.html" title="Beginner Tutorial"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" accesskey="U">Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Part 1: What We Have</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Part 1: What We Have</a><ul>
|
||||
<li><a class="reference internal" href="#lessons">Lessons</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="../Beginner-Tutorial-Overview.html"
|
||||
title="previous chapter">Beginner Tutorial</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Building-Quickstart.html"
|
||||
title="next chapter"><span class="section-number">1. </span>Using Commands and Building Stuff</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Part1-Overview.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="part-1-what-we-have">
|
||||
<h1>Part 1: What We Have<a class="headerlink" href="#part-1-what-we-have" title="Permalink to this headline">¶</a></h1>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Beginner Tutorial Parts</p>
|
||||
<ul class="simple">
|
||||
<li><p><a class="reference internal" href="../Beginner-Tutorial-Overview.html"><span class="doc std std-doc">Introduction</span></a>
|
||||
<br>Getting set up.</p></li>
|
||||
<li><p><em>Part 1: <a class="reference internal" href="#"><span class="doc std std-doc">What We Have</span></a></em>
|
||||
<br>A tour of Evennia and how to use the tools, including an introduction to Python.</p></li>
|
||||
<li><p>Part 2: <a class="reference internal" href="../Part2/Beginner-Tutorial-Part2-Overview.html"><span class="doc std std-doc">What We Want</span></a>
|
||||
<br>Planning our tutorial game and what to consider when planning your own.</p></li>
|
||||
<li><p>Part 3: <a class="reference internal" href="../Part3/Beginner-Tutorial-Part3-Overview.html"><span class="doc std std-doc">How We Get There</span></a>
|
||||
<br>Getting down to the meat of extending Evennia to make your game.</p></li>
|
||||
<li><p>Part 4: <a class="reference internal" href="../Part4/Beginner-Tutorial-Part4-Overview.html"><span class="doc std std-doc">Using What We Created</span></a>
|
||||
<br>Building a tech-demo and world content to go with our code.</p></li>
|
||||
<li><p>Part 5: <a class="reference internal" href="../Part5/Beginner-Tutorial-Part5-Overview.html"><span class="doc std std-doc">Showing the World</span></a>
|
||||
<br>Taking our new game online and letting players try it out.</p></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<p>In this first part, we’ll focus on what we have out of the box with Evennia. We’ll familiarize you with the tools and how to find things that you are looking for. We will also dive into some of the things you’ll need to know to utilize the system fully, including giving you a brief rundown of Python concepts. If you are an experienced Python programmer, some sections may feel a bit basic – yet seeing things in the context of Evennia will still be worthwhile.</p>
|
||||
<section id="lessons">
|
||||
<h2>Lessons<a class="headerlink" href="#lessons" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="toctree-wrapper compound">
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html">1. Using Commands and Building Stuff</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html#getting-help">1.1. Getting Help</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html#looking-around">1.2. Looking Around</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html#stepping-down-from-godhood">1.3. Stepping Down from Godhood</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html#creating-an-object">1.4. Creating an Object</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html#get-a-personality">1.5. Get a Personality</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html#pushing-your-buttons">1.6. Pushing Your Buttons</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html#making-yourself-a-house">1.7. Making Yourself a House</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html#reshuffling-the-world">1.8. Reshuffling the World</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html#adding-a-help-entry">1.9. Adding a Help Entry</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html#adding-a-world">1.10. Adding a World</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Tutorial-World.html">2. The Tutorial World</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Tutorial-World.html#gameplay">2.1. Gameplay</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Tutorial-World.html#once-you-are-done-or-had-enough">2.2. Once you are done (or had enough)</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Tutorial-World.html#uninstall-the-tutorial-world">2.3. Uninstall the tutorial world</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html">3. Intro to using Python with Evennia</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html#evennia-hello-world">3.1. Evennia Hello world</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html#making-some-text-graphics">3.2. Making some text ‘graphics’</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html#importing-code-from-other-modules">3.3. Importing code from other modules</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html#sending-text-to-others">3.4. Sending text to others</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html#parsing-python-errors">3.5. Parsing Python errors</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html#passing-arguments-to-functions">3.6. Passing arguments to functions</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html#finding-others-to-send-to">3.7. Finding others to send to</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html#multi-line-py">3.8. Multi-line py</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html#other-ways-to-test-python-code">3.9. Other ways to test Python code</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html#ipython">3.10. ipython</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html#conclusions">3.11. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Gamedir-Overview.html">4. Overview of your new Game Dir</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Gamedir-Overview.html#commands">4.1. commands/</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Gamedir-Overview.html#server">4.2. server/</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Python-classes-and-objects.html">5. Introduction to Python classes and objects</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-classes-and-objects.html#importing-things">5.1. Importing things</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-classes-and-objects.html#on-classes-and-objects">5.2. On classes and objects</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Python-classes-and-objects.html#summary">5.3. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Evennia-Library-Overview.html">6. Overview of the Evennia library</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Evennia-Library-Overview.html#where-is-it">6.1. Where is it?</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Evennia-Library-Overview.html#an-example-of-exploring-the-library">6.2. An example of exploring the library</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Evennia-Library-Overview.html#conclusions">6.3. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Learning-Typeclasses.html">7. Making objects persistent</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Learning-Typeclasses.html#our-first-persistent-object">7.1. Our first persistent object</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Learning-Typeclasses.html#typeclasses">7.2. Typeclasses</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Learning-Typeclasses.html#modifying-ourselves">7.3. Modifying ourselves</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Learning-Typeclasses.html#extra-credits">7.4. Extra Credits</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Learning-Typeclasses.html#conclusions">7.5. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Adding-Commands.html">8. Adding custom commands</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Adding-Commands.html#creating-a-custom-command">8.1. Creating a custom command</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Adding-Commands.html#summary">8.2. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-More-on-Commands.html">9. Parsing Command input</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-More-on-Commands.html#more-advanced-parsing">9.1. More advanced parsing</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-More-on-Commands.html#adding-a-command-to-an-object">9.2. Adding a Command to an object</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-More-on-Commands.html#adding-the-command-to-a-default-cmdset">9.3. Adding the Command to a default Cmdset</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-More-on-Commands.html#replace-a-default-command">9.4. Replace a default command</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-More-on-Commands.html#summary">9.5. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Creating-Things.html">10. Creating things</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Creating-Things.html#creating-objects">10.1. Creating Objects</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Creating-Things.html#creating-rooms-characters-and-exits">10.2. Creating Rooms, Characters and Exits</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Creating-Things.html#creating-accounts">10.3. Creating Accounts</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Creating-Things.html#creating-channels">10.4. Creating Channels</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Creating-Things.html#creating-scripts">10.5. Creating Scripts</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Creating-Things.html#conclusion">10.6. Conclusion</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Searching-Things.html">11. Searching for things</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Searching-Things.html#main-search-functions">11.1. Main search functions</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Searching-Things.html#searching-using-object-search">11.2. Searching using Object.search</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Searching-Things.html#what-can-be-searched-for">11.3. What can be searched for</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Searching-Things.html#finding-objects-relative-each-other">11.4. Finding objects relative each other</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Searching-Things.html#summary">11.5. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Django-queries.html">12. Advanced searching - Django Database queries</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Django-queries.html#queryset-field-lookups">12.1. Queryset field lookups</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Django-queries.html#lets-get-that-werewolf">12.2. Let’s get that werewolf …</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Django-queries.html#queries-with-or-or-not">12.3. Queries with OR or NOT</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Django-queries.html#annotations">12.4. Annotations</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Django-queries.html#f-objects">12.5. F-objects</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Django-queries.html#grouping-and-returning-only-certain-properties">12.6. Grouping and returning only certain properties</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Django-queries.html#conclusions">12.7. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Making-A-Sittable-Object.html">13. Building a chair you can sit on</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Making-A-Sittable-Object.html#make-us-not-able-to-move-while-sitting">13.1. Make us not able to move while sitting</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Making-A-Sittable-Object.html#making-the-chair-itself">13.2. Making the Chair itself</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Making-A-Sittable-Object.html#adding-commands">13.3. Adding commands</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Making-A-Sittable-Object.html#conclusions">13.4. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</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="Beginner-Tutorial-Building-Quickstart.html" title="1. Using Commands and Building Stuff"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Beginner-Tutorial-Overview.html" title="Beginner Tutorial"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Part 1: What We Have</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,696 @@
|
|||
|
||||
<!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>3. Intro to using Python with Evennia — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="4. Overview of your new Game Dir" href="Beginner-Tutorial-Gamedir-Overview.html" />
|
||||
<link rel="prev" title="2. The Tutorial World" href="Beginner-Tutorial-Tutorial-World.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Gamedir-Overview.html" title="4. Overview of your new Game Dir"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Tutorial-World.html" title="2. The Tutorial World"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">3. </span>Intro to using Python with Evennia</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">3. Intro to using Python with Evennia</a><ul>
|
||||
<li><a class="reference internal" href="#evennia-hello-world">3.1. Evennia Hello world</a></li>
|
||||
<li><a class="reference internal" href="#making-some-text-graphics">3.2. Making some text ‘graphics’</a><ul>
|
||||
<li><a class="reference internal" href="#format">3.2.1. .format()</a></li>
|
||||
<li><a class="reference internal" href="#f-strings">3.2.2. f-strings</a></li>
|
||||
<li><a class="reference internal" href="#colored-text">3.2.3. Colored text</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#importing-code-from-other-modules">3.3. Importing code from other modules</a><ul>
|
||||
<li><a class="reference internal" href="#our-first-own-function">3.3.1. Our first own function</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#sending-text-to-others">3.4. Sending text to others</a></li>
|
||||
<li><a class="reference internal" href="#parsing-python-errors">3.5. Parsing Python errors</a></li>
|
||||
<li><a class="reference internal" href="#passing-arguments-to-functions">3.6. Passing arguments to functions</a></li>
|
||||
<li><a class="reference internal" href="#finding-others-to-send-to">3.7. Finding others to send to</a></li>
|
||||
<li><a class="reference internal" href="#multi-line-py">3.8. Multi-line py</a></li>
|
||||
<li><a class="reference internal" href="#other-ways-to-test-python-code">3.9. Other ways to test Python code</a></li>
|
||||
<li><a class="reference internal" href="#ipython">3.10. ipython</a></li>
|
||||
<li><a class="reference internal" href="#conclusions">3.11. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Tutorial-World.html"
|
||||
title="previous chapter"><span class="section-number">2. </span>The Tutorial World</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Gamedir-Overview.html"
|
||||
title="next chapter"><span class="section-number">4. </span>Overview of your new Game Dir</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Python-basic-introduction.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="intro-to-using-python-with-evennia">
|
||||
<h1><span class="section-number">3. </span>Intro to using Python with Evennia<a class="headerlink" href="#intro-to-using-python-with-evennia" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Time to dip our toe into some coding! Evennia is written and extended in <a class="reference external" href="https://python.org">Python</a>. Python is a mature and professional programming language that is very fast to work with.</p>
|
||||
<p>That said, even though Python is widely considered easy to learn, we can only cover the basics in these lessons. While we will hopefully get you started with the most important bits you need, you may likely need to compliment with some learning on your own. Luckily there’s a vast amount of free online learning resources available for Python. See our <a class="reference internal" href="../../../Links.html"><span class="doc std std-doc">link section</span></a> for some examples.</p>
|
||||
<blockquote>
|
||||
<div><p>While this will be quite basic if you are an experienced developer, you may want to at least stay around for the first few sections where we cover how to run Python from inside Evennia.</p>
|
||||
</div></blockquote>
|
||||
<p>First, if you were quelling yourself to play the tutorial world, make sure to get your
|
||||
superuser powers back:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> unquell
|
||||
</pre></div>
|
||||
</div>
|
||||
<section id="evennia-hello-world">
|
||||
<h2><span class="section-number">3.1. </span>Evennia Hello world<a class="headerlink" href="#evennia-hello-world" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">py</span></code> Command (or <code class="docutils literal notranslate"><span class="pre">!</span></code>, which is an alias) allows you as a superuser to execute raw Python from in-game. This is useful for quick testing. From the game’s input line, enter the following:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py print("Hello World!")
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Command input</p>
|
||||
<p>The line with <code class="docutils literal notranslate"><span class="pre">></span></code> indicates input to enter in-game, while the lines below are the
|
||||
expected return from that input.</p>
|
||||
</aside>
|
||||
<p>You will see</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> print("Hello world!")
|
||||
Hello World!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">print(...)</span></code> <em>function</em> is the basic, in-built way to output text in Python. We are sending “Hello World” as a single <em>argument</em> to this function. If we were to send multiple arguments, they’d be separated by commas.</p>
|
||||
<p>The quotes <code class="docutils literal notranslate"><span class="pre">"..."</span></code> mean that you are inputting a <em>string</em> (i.e. text). You could also have used single-quotes <code class="docutils literal notranslate"><span class="pre">'...'</span></code> - Python accepts both.</p>
|
||||
<blockquote>
|
||||
<div><p>A third way to enter Python strings is to use triple-quotes (<code class="docutils literal notranslate"><span class="pre">"""..."""</span></code> or <code class="docutils literal notranslate"><span class="pre">'''...'''</span></code>. This is used for longer strings stretching across multiple lines. When we insert code directly to <code class="docutils literal notranslate"><span class="pre">py</span></code> like this we can only use one line though.</p>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
<section id="making-some-text-graphics">
|
||||
<h2><span class="section-number">3.2. </span>Making some text ‘graphics’<a class="headerlink" href="#making-some-text-graphics" title="Permalink to this headline">¶</a></h2>
|
||||
<p>When making a text-game you will, unsurprisingly, be working a lot with text. Even if you have the occational button or even graphical element, the normal process is for the user to input commands as text and get text back. As we saw above, a piece of text is called a <em>string</em> in Python and is enclosed in either single- or double-quotes.</p>
|
||||
<p>Strings can be added together:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py print("This is a " + "breaking change.")
|
||||
This is a breaking change.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>A string multiplied with a number will repeat that string as many times:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py print("|" + "-" * 40 + "|")
|
||||
|----------------------------------------|
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>or</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py print("A" + "a" * 5 + "rgh!")
|
||||
Aaaaaargh!
|
||||
</pre></div>
|
||||
</div>
|
||||
<section id="format">
|
||||
<h3><span class="section-number">3.2.1. </span>.format()<a class="headerlink" href="#format" title="Permalink to this headline">¶</a></h3>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Functions and Methods</p>
|
||||
<ul class="simple">
|
||||
<li><p>Function: Something that performs an action when you call it with zero or more <code class="docutils literal notranslate"><span class="pre">arguments</span></code>. A function is stand-alone in a python module, like <code class="docutils literal notranslate"><span class="pre">print()</span></code></p></li>
|
||||
<li><p>Method: A function that sits “on” an object. It is accessed via the <code class="docutils literal notranslate"><span class="pre">.</span></code> operator, like <code class="docutils literal notranslate"><span class="pre">obj.msg()</span></code> or, in this case, <code class="docutils literal notranslate"><span class="pre"><string>.format()</span></code>.</p></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<p>While combining different strings is useful, even more powerful is the ability to modify the contents of the string in-place. There are several ways to do this in Python and we’ll show two of them here. The first is to use the <code class="docutils literal notranslate"><span class="pre">.format</span></code> <em>method</em> of the string:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py print("This is a {} idea!".format("good"))
|
||||
This is a good idea!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>A method can be thought of as a resource “on” another object. The method knows on which object it sits and can thus affect it in various ways. You access it with the period <code class="docutils literal notranslate"><span class="pre">.</span></code>. In this case, the string has a resource <code class="docutils literal notranslate"><span class="pre">format(...)</span></code> that modifies it. More specifically, it replaced the <code class="docutils literal notranslate"><span class="pre">{}</span></code> marker inside the string with the value passed to the format. You can do so many times:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py print("This is a {} idea!".format("good"))
|
||||
This is a good idea!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>or</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py print("This is the {} and {} {} idea!".format("first", "second", "great"))
|
||||
This is the first and second great idea!
|
||||
</pre></div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div><p>Note the double-parenthesis at the end - the first closes the <code class="docutils literal notranslate"><span class="pre">format(...</span></code> method and the outermost closes the <code class="docutils literal notranslate"><span class="pre">print(...</span></code>. Not closing them will give you a scary <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code>. We will talk a little more about errors in the next section, for now just fix until it prints as expected.</p>
|
||||
</div></blockquote>
|
||||
<p>Here we passed three comma-separated strings as <em>arguments</em> to the string’s <code class="docutils literal notranslate"><span class="pre">format</span></code> method. These replaced the <code class="docutils literal notranslate"><span class="pre">{}</span></code> markers in the same order as they were given.</p>
|
||||
<p>The input does not have to be strings either:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py print("STR: {}, DEX: {}, INT: {}".format(12, 14, 8))
|
||||
STR: 12, DEX: 14, INT: 8
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>To separate two Python instructions on the same line, you use the semi-colon, <code class="docutils literal notranslate"><span class="pre">;</span></code>. Try this:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py a = "awesome sauce" ; print("This is {}!".format(a))
|
||||
This is awesome sauce!
|
||||
</pre></div>
|
||||
</div>
|
||||
<div class="admonition warning">
|
||||
<p class="admonition-title">Warning</p>
|
||||
<p>MUD clients and semi-colon</p>
|
||||
<p>Some MUD clients use the semi-colon <code class="docutils literal notranslate"><span class="pre">;</span></code> to split client-inputs
|
||||
into separate sends. If so, the above will give an error. Most clients allow you to run in ‘verbatim’ mode or to remap to use some other separator than <code class="docutils literal notranslate"><span class="pre">;</span></code>. If you still have trouble, use the Evennia web client.</p>
|
||||
</div>
|
||||
<p>What happened here was that we <em>assigned</em> the string <code class="docutils literal notranslate"><span class="pre">"awesome</span> <span class="pre">sauce"</span></code> to a <em>variable</em> we chose to name <code class="docutils literal notranslate"><span class="pre">a</span></code>. In the next statement, Python remembered what <code class="docutils literal notranslate"><span class="pre">a</span></code> was and we passed that into <code class="docutils literal notranslate"><span class="pre">format()</span></code> to get the output. If you replaced the value of <code class="docutils literal notranslate"><span class="pre">a</span></code> with something else in between, <em>that</em> would be printed instead.</p>
|
||||
<p>Here’s the stat-example again, moving the stats to variables (here we just set them, but in a real game they may be changed over time, or modified by circumstance):</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py stren, dext, intel = 13, 14, 8 ; print("STR: {}, DEX: {}, INT: {}".format(stren, dext, intel))
|
||||
STR: 13, DEX: 14, INT: 8
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The point is that even if the values of the stats change, the print() statement would not change - it just keeps pretty-printing whatever is given to it.</p>
|
||||
<p>You can also use named markers, like this:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> > py print("STR: {stren}, INT: {intel}, STR again: {stren}".format(dext=10, intel=18, stren=9))
|
||||
STR: 9, INT: 18, Str again: 9
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>the <code class="docutils literal notranslate"><span class="pre">key=value</span></code> pairs we add are called <em>keyword arguments</em> for the <code class="docutils literal notranslate"><span class="pre">format()</span></code> method. Each named argument will go to the matching <code class="docutils literal notranslate"><span class="pre">{key}</span></code> in the string. When using keywords, the order we add them doesn’t matter. We have no <code class="docutils literal notranslate"><span class="pre">{dext}</span></code> and two <code class="docutils literal notranslate"><span class="pre">{stren}</span></code> in the string, and that works fine.</p>
|
||||
</section>
|
||||
<section id="f-strings">
|
||||
<h3><span class="section-number">3.2.2. </span>f-strings<a class="headerlink" href="#f-strings" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Using <code class="docutils literal notranslate"><span class="pre">.format()</span></code> is powerful (and there is a <a class="reference external" href="https://www.w3schools.com/python/ref_string_format.asp">lot more</a> you can do with it). But the <em>f-string</em> can be even more convenient. An f-string looks like a normal string … except there is an <code class="docutils literal notranslate"><span class="pre">f</span></code> front of it, like this:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>f"this is now an f-string."
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>An f-string on its own is just like any other string. But let’s redo the example we did before, using an f-string:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py a = "awesome sauce" ; print(f"This is {a}!")
|
||||
This is awesome sauce!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We insert that <code class="docutils literal notranslate"><span class="pre">a</span></code> variable directly into the f-string using <code class="docutils literal notranslate"><span class="pre">{a}</span></code>. Fewer parentheses to
|
||||
remember and arguable easier to read as well!</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py stren, dext, intel = 13, 14, 8 ; print(f"STR: {stren}, DEX: {dext}, INT: {intel}")
|
||||
STR: 13, DEX: 14, INT: 8
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In modern Python code, f-strings are more often used than <code class="docutils literal notranslate"><span class="pre">.format()</span></code> but to read code you need to be aware of both.</p>
|
||||
<p>We will be exploring more complex string concepts when we get to creating Commands and need to parse and understand player input.</p>
|
||||
</section>
|
||||
<section id="colored-text">
|
||||
<h3><span class="section-number">3.2.3. </span>Colored text<a class="headerlink" href="#colored-text" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Python itself knows nothing about colored text, this is an Evennia thing. Evennia supports the standard color schemes of traditional MUDs.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py print("|rThis is red text!|n This is normal color.")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Adding that <code class="docutils literal notranslate"><span class="pre">|r</span></code> at the start will turn our output bright red. <code class="docutils literal notranslate"><span class="pre">|R</span></code> will make it dark red. <code class="docutils literal notranslate"><span class="pre">|n</span></code>
|
||||
gives the normal text color. You can also use RGB (Red-Green-Blue) values from 0-5 (Xterm256 colors):</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py print("|043This is a blue-green color.|[530|003 Now dark blue text on orange background.")
|
||||
</pre></div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div><p>If you don’t see the expected color, your client or terminal may not support Xterm256 (or
|
||||
color at all). Use the Evennia webclient.</p>
|
||||
</div></blockquote>
|
||||
<p>Use the commands <code class="docutils literal notranslate"><span class="pre">color</span> <span class="pre">ansi</span></code> or <code class="docutils literal notranslate"><span class="pre">color</span> <span class="pre">xterm</span></code> to see which colors are available. Experiment! You can also read a lot more in the <a class="reference internal" href="../../../Concepts/Colors.html"><span class="doc std std-doc">Colors</span></a> documentation.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="importing-code-from-other-modules">
|
||||
<h2><span class="section-number">3.3. </span>Importing code from other modules<a class="headerlink" href="#importing-code-from-other-modules" title="Permalink to this headline">¶</a></h2>
|
||||
<p>As we saw in the previous sections, we used <code class="docutils literal notranslate"><span class="pre">.format</span></code> to format strings and <code class="docutils literal notranslate"><span class="pre">me.msg</span></code> to access the <code class="docutils literal notranslate"><span class="pre">msg</span></code> method on <code class="docutils literal notranslate"><span class="pre">me</span></code>. This use of the full-stop character is used to access all sorts of resources, including that in other Python modules.</p>
|
||||
<p>Keep your game running, then open a text editor of your choice. If your game folder is called
|
||||
<code class="docutils literal notranslate"><span class="pre">mygame</span></code>, create a new text file <code class="docutils literal notranslate"><span class="pre">test.py</span></code> in the subfolder <code class="docutils literal notranslate"><span class="pre">mygame/world</span></code>. This is how the file
|
||||
structure should look:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">mygame</span><span class="o">/</span>
|
||||
<span class="n">world</span><span class="o">/</span>
|
||||
<span class="n">test</span><span class="o">.</span><span class="n">py</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>For now, only add one line to <code class="docutils literal notranslate"><span class="pre">test.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="s2">"Hello World!"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Python module</p>
|
||||
<p>This is a text file with the <code class="docutils literal notranslate"><span class="pre">.py</span></code> file ending. A module
|
||||
contains Python source code and from within Python one can
|
||||
access its contents by importing it via its python-path.</p>
|
||||
</aside>
|
||||
<p>Don’t forget to <em>save</em> the file. We just created our first Python <em>module</em>!
|
||||
To use this in-game we have to <em>import</em> it. Try this:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py import world.test
|
||||
Hello World
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you make some error (we’ll cover how to handle errors below), make sure the text looks exactly like above and then run the <code class="docutils literal notranslate"><span class="pre">reload</span></code> command in-game for your changes to take effect.</p>
|
||||
<p>… So as you can see, importing <code class="docutils literal notranslate"><span class="pre">world.test</span></code> actually means importing <code class="docutils literal notranslate"><span class="pre">world/test.py</span></code>. Think of the period <code class="docutils literal notranslate"><span class="pre">.</span></code> as replacing <code class="docutils literal notranslate"><span class="pre">/</span></code> (or <code class="docutils literal notranslate"><span class="pre">\</span></code> for Windows) in your path.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">.py</span></code> ending of <code class="docutils literal notranslate"><span class="pre">test.py</span></code> is never included in this “Python-path”, but <em>only</em> files with that ending can be imported this way. Where is <code class="docutils literal notranslate"><span class="pre">mygame</span></code> in that Python-path? The answer is that Evennia has already told Python that your <code class="docutils literal notranslate"><span class="pre">mygame</span></code> folder is a good place to look for imports. So we should not include <code class="docutils literal notranslate"><span class="pre">mygame</span></code> in the path - Evennia handles this for us.</p>
|
||||
<p>When you import the module, the top “level” of it will execute. In this case, it will immediately
|
||||
print “Hello World”.</p>
|
||||
<p>Now try to run this a second time:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py import world.test
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You will <em>not</em> see any output this or any subsequent times! This is not a bug. Rather it is because of how Python importing works - it stores all imported modules and will avoid importing them more than once. So your <code class="docutils literal notranslate"><span class="pre">print</span></code> will only run the first time, when the module is first imported.</p>
|
||||
<p>Try this:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>And then</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py import world.test
|
||||
Hello World!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now we see it again. The <code class="docutils literal notranslate"><span class="pre">reload</span></code> wiped the server’s memory of what was imported, so it had to import it anew. You’d have to do this every time you wanted the hello-world to show, which is not very useful.</p>
|
||||
<blockquote>
|
||||
<div><p>We’ll get back to more advanced ways to import code in <a class="reference internal" href="Beginner-Tutorial-Python-classes-and-objects.html#importing-things"><span class="std std-doc">a later lesson</span></a> - this is an important topic. But for now, let’s press on and resolve this particular problem.</p>
|
||||
</div></blockquote>
|
||||
<section id="our-first-own-function">
|
||||
<h3><span class="section-number">3.3.1. </span>Our first own function<a class="headerlink" href="#our-first-own-function" title="Permalink to this headline">¶</a></h3>
|
||||
<p>We want to be able to print our hello-world message at any time, not just once after a server
|
||||
reload. Change your <code class="docutils literal notranslate"><span class="pre">mygame/world/test.py</span></code> file to look like this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">hello_world</span><span class="p">():</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Hello World!"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p>If you are coming from some other language like Javascript or C you may be familiar with variables and functions mixing cases in names, like <code class="docutils literal notranslate"><span class="pre">helloWorld()</span></code>. While you <em>could</em> choose to name things this way, it will clash with other Python code - Python standard is to use lower-case and underscores <code class="docutils literal notranslate"><span class="pre">_</span></code> for all variables and methods.</p>
|
||||
</aside>
|
||||
<p>As we are moving to multi-line Python code, there are some important things to remember:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Capitalization matters in Python. It must be <code class="docutils literal notranslate"><span class="pre">def</span></code> and not <code class="docutils literal notranslate"><span class="pre">DEF</span></code>, <code class="docutils literal notranslate"><span class="pre">hello_world()</span></code> is not the same as <code class="docutils literal notranslate"><span class="pre">Hello_World()</span></code>.</p></li>
|
||||
<li><p>Indentation matters in Python. The second line must be indented or it’s not valid code. You should also use a consistent indentation length. We <em>strongly</em> recommend that you, for your own sanity’s sake, set up your editor to always indent <em>4 spaces</em> (<strong>not</strong> a single tab-character) when you press the TAB key.</p></li>
|
||||
</ul>
|
||||
<p>So about that function. Line 1:</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">def</span></code> is short for “define” and defines a <em>function</em> (or a <em>method</em>, if sitting on an object). This is a <a class="reference external" href="https://docs.python.org/2.5/ref/keywords.html">reserved Python keyword</a>; try not to use these words anywhere else.</p></li>
|
||||
<li><p>A function name can not have spaces but otherwise we could have called it almost anything. We call it <code class="docutils literal notranslate"><span class="pre">hello_world</span></code>. Evennia follows <a class="reference internal" href="../../../Coding/Evennia-Code-Style.html"><span class="doc std std-doc">Python’s standard naming style</span></a> with lowercase letters and underscores. We recommend you do the same.</p></li>
|
||||
<li><p>The colon (<code class="docutils literal notranslate"><span class="pre">:</span></code>) at the end of line 1 indicates that the header of the function is complete.</p></li>
|
||||
</ul>
|
||||
<p>Line 2:</p>
|
||||
<ul class="simple">
|
||||
<li><p>The indentation marks the beginning of the actual operating code of the function (the function’s <em>body</em>). If we wanted more lines to belong to this function those lines would all have to start at least at this indentation level.</p></li>
|
||||
</ul>
|
||||
<p>Now let’s try this out. First <code class="docutils literal notranslate"><span class="pre">reload</span></code> your game to have it pick up our updated Python module, then import it.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> reload
|
||||
> py import world.test
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Nothing happened! That is because the function in our module won’t do anything just by importing it (this is what we wanted). It will only act when we <em>call</em> it. So we need to first import the module and then access the function within:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py import world.test ; world.test.hello_world()
|
||||
Hello world!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>There is our “Hello World”! As mentioned earlier, use use semi-colon to put multiple Python-statements on one line. Note also the previous warning about mud-clients using the <code class="docutils literal notranslate"><span class="pre">;</span></code> to their own ends.</p>
|
||||
<p>So what happened there? First we imported <code class="docutils literal notranslate"><span class="pre">world.test</span></code> as usual. But this time the ‘top level’ of the module only defined a function. It didn’t actually execute the body of that function.</p>
|
||||
<p>By adding <code class="docutils literal notranslate"><span class="pre">()</span></code> to the <code class="docutils literal notranslate"><span class="pre">hello_world</span></code> function we <em>call</em> it. That is, we execute the body of the function and print our text. We can now redo this as many times as we want without having to <code class="docutils literal notranslate"><span class="pre">reload</span></code> in between:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py import world.test ; world.test.hello_world()
|
||||
Hello world!
|
||||
> py import world.test ; world.test.hello_world()
|
||||
Hello world!
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="sending-text-to-others">
|
||||
<h2><span class="section-number">3.4. </span>Sending text to others<a class="headerlink" href="#sending-text-to-others" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">print</span></code> command is a standard Python structure. We can use that here in the <code class="docutils literal notranslate"><span class="pre">py</span></code> command since we can se the output. It’s great for debugging and quick testing. But if you need to send a text to an actual player, <code class="docutils literal notranslate"><span class="pre">print</span></code> won’t do, because it doesn’t know <em>who</em> to send to. Try this:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py me.msg("Hello world!")
|
||||
Hello world!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This looks the same as the <code class="docutils literal notranslate"><span class="pre">print</span></code> result, but we are now actually messaging a specific <em>object</em>, <code class="docutils literal notranslate"><span class="pre">me</span></code>. The <code class="docutils literal notranslate"><span class="pre">me</span></code> is a shortcut to ‘us’, the one running the <code class="docutils literal notranslate"><span class="pre">py</span></code> command. It is not some special Python thing, but something Evennia just makes available in the <code class="docutils literal notranslate"><span class="pre">py</span></code> command for convenience (<code class="docutils literal notranslate"><span class="pre">self</span></code> is an alias).</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">me</span></code> is an example of an <em>Object instance</em>. Objects are fundamental in Python and Evennia. The <code class="docutils literal notranslate"><span class="pre">me</span></code> object also contains a lot of useful resources for doing things with that object. We access those resources with ‘<code class="docutils literal notranslate"><span class="pre">.</span></code>’.</p>
|
||||
<p>One such resource is <code class="docutils literal notranslate"><span class="pre">msg</span></code>, which works like <code class="docutils literal notranslate"><span class="pre">print</span></code> except it sends the text to the object it
|
||||
is attached to. So if we, for example, had an object <code class="docutils literal notranslate"><span class="pre">you</span></code>, doing <code class="docutils literal notranslate"><span class="pre">you.msg(...)</span></code> would send a message to the object <code class="docutils literal notranslate"><span class="pre">you</span></code>.</p>
|
||||
<p>For now, <code class="docutils literal notranslate"><span class="pre">print</span></code> and <code class="docutils literal notranslate"><span class="pre">me.msg</span></code> behaves the same, just remember that <code class="docutils literal notranslate"><span class="pre">print</span></code> is mainly used for
|
||||
debugging and <code class="docutils literal notranslate"><span class="pre">.msg()</span></code> will be more useful for you in the future.</p>
|
||||
</section>
|
||||
<section id="parsing-python-errors">
|
||||
<h2><span class="section-number">3.5. </span>Parsing Python errors<a class="headerlink" href="#parsing-python-errors" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Let’s try this new text-sending in the function we just created. Go back to
|
||||
your <code class="docutils literal notranslate"><span class="pre">test.py</span></code> file and Replace the function with this instead:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">hello_world</span><span class="p">():</span>
|
||||
<span class="n">me</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Hello World!"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Save your file and <code class="docutils literal notranslate"><span class="pre">reload</span></code> your server to tell Evennia to re-import new code,
|
||||
then run it like before:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> > py import world.test ; world.test.hello_world()
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>No go - this time you get an <em>error</em>!</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">File</span> <span class="s2">"./world/test.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">2</span><span class="p">,</span> <span class="ow">in</span> <span class="n">hello_world</span>
|
||||
<span class="n">me</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Hello World!"</span><span class="p">)</span>
|
||||
<span class="ne">NameError</span><span class="p">:</span> <span class="n">name</span> <span class="s1">'me'</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">defined</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Errors in the logs</p>
|
||||
<p>In regular use, tracebacks will often appear in the log rather than
|
||||
in the game. Use <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">--log</span></code> to view the log in the terminal. Make
|
||||
sure to scroll back if you expect an error and don’t see it. Use
|
||||
<code class="docutils literal notranslate"><span class="pre">Ctrl-C</span></code> (or <code class="docutils literal notranslate"><span class="pre">Cmd-C</span></code> on Mac) to exit the log-view.</p>
|
||||
</aside>
|
||||
<p>This is called a <em>traceback</em>. Python’s errors are very friendly and will most of the time tell you exactly what and where things go wrong. It’s important that you learn to parse tracebacks so you know how to fix your code.</p>
|
||||
<p>A traceback is to be read from the <em>bottom up</em>:</p>
|
||||
<ul class="simple">
|
||||
<li><p>(line 3) An error of type <code class="docutils literal notranslate"><span class="pre">NameError</span></code> is the problem …</p></li>
|
||||
<li><p>(line 3) … more specifically it is due to the variable <code class="docutils literal notranslate"><span class="pre">me</span></code> not being defined.</p></li>
|
||||
<li><p>(line 2) This happened on the line <code class="docutils literal notranslate"><span class="pre">me.msg("Hello</span> <span class="pre">world!")</span></code> …</p></li>
|
||||
<li><p>(line 1) … which is on line <code class="docutils literal notranslate"><span class="pre">2</span></code> of the file <code class="docutils literal notranslate"><span class="pre">./world/test.py</span></code>.</p></li>
|
||||
</ul>
|
||||
<p>In our case the traceback is short. There may be many more lines above it, tracking just how
|
||||
different modules called each other until the program got to the faulty line. That can
|
||||
sometimes be useful information, but reading from the bottom is always a good start.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">NameError</span></code> we see here is due to a module being its own isolated thing. It knows nothing about the environment into which it is imported. It knew what <code class="docutils literal notranslate"><span class="pre">print</span></code> is because that is a special <a class="reference external" href="https://docs.python.org/2.5/ref/keywords.html">reserved Python keyword</a>. But <code class="docutils literal notranslate"><span class="pre">me</span></code> is <em>not</em> such a reserved word (as mentioned, it’s just something Evennia came up with for convenience in the <code class="docutils literal notranslate"><span class="pre">py</span></code> command). As far as the module is concerned <code class="docutils literal notranslate"><span class="pre">me</span></code> is an unfamiliar name, appearing out of nowhere. Hence the <code class="docutils literal notranslate"><span class="pre">NameError</span></code>.</p>
|
||||
</section>
|
||||
<section id="passing-arguments-to-functions">
|
||||
<h2><span class="section-number">3.6. </span>Passing arguments to functions<a class="headerlink" href="#passing-arguments-to-functions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>We know that <code class="docutils literal notranslate"><span class="pre">me</span></code> exists at the point when we run the <code class="docutils literal notranslate"><span class="pre">py</span></code> command, because we can do <code class="docutils literal notranslate"><span class="pre">py</span> <span class="pre">me.msg("Hello</span> <span class="pre">World!")</span></code> with no problem. So let’s <em>pass</em> that me along to the function so it knows what it should be. Go back to your <code class="docutils literal notranslate"><span class="pre">test.py</span></code> and change it to this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">hello_world</span><span class="p">(</span><span class="n">who</span><span class="p">):</span>
|
||||
<span class="n">who</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Hello World!"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We now added an <em>argument</em> to the function. We could have named it anything. Whatever <code class="docutils literal notranslate"><span class="pre">who</span></code> is, we will call a method <code class="docutils literal notranslate"><span class="pre">.msg()</span></code> on it.</p>
|
||||
<p>As usual, <code class="docutils literal notranslate"><span class="pre">reload</span></code> the server to make sure the new code is available.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py import world.test ; world.test.hello_world(me)
|
||||
Hello World!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now it worked. We <em>passed</em> <code class="docutils literal notranslate"><span class="pre">me</span></code> to our function. It will appear inside the function renamed as <code class="docutils literal notranslate"><span class="pre">who</span></code> and now the function works and prints as expected. Note how the <code class="docutils literal notranslate"><span class="pre">hello_world</span></code> function doesn’t care <em>what</em> you pass into it as long as it has a <code class="docutils literal notranslate"><span class="pre">.msg()</span></code> method on it. So you could reuse this function over and over for other suitable targets.</p>
|
||||
<blockquote>
|
||||
<div><p><strong>Extra Credit:</strong> As an exercise, try to pass something else into <code class="docutils literal notranslate"><span class="pre">hello_world</span></code>. Try for example
|
||||
to pass the number <code class="docutils literal notranslate"><span class="pre">5</span></code> or the string <code class="docutils literal notranslate"><span class="pre">"foo"</span></code>. You’ll get errors telling you that they don’t have
|
||||
the attribute <code class="docutils literal notranslate"><span class="pre">msg</span></code>. They don’t care about <code class="docutils literal notranslate"><span class="pre">me</span></code> itself not being a string or a number. If you are
|
||||
familiar with other programming languages (especially C/Java) you may be tempted to start <em>validating</em> <code class="docutils literal notranslate"><span class="pre">who</span></code> to make sure it’s of the right type before you send it. This is usually not recommended in Python. Python philosophy is to <a class="reference external" href="https://docs.python.org/2/tutorial/errors.html">handle</a> the error if it happens
|
||||
rather than to add a lot of code to prevent it from happening. See <a class="reference external" href="https://en.wikipedia.org/wiki/Duck_typing">duck typing</a>
|
||||
and the concept of <em>Leap before you Look</em>.</p>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
<section id="finding-others-to-send-to">
|
||||
<h2><span class="section-number">3.7. </span>Finding others to send to<a class="headerlink" href="#finding-others-to-send-to" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Let’s wrap up this first Python <code class="docutils literal notranslate"><span class="pre">py</span></code> crash-course by finding someone else to send to.</p>
|
||||
<p>In Evennia’s <code class="docutils literal notranslate"><span class="pre">contrib/</span></code> folder (<code class="docutils literal notranslate"><span class="pre">evennia/contrib/tutorial_examples/mirror.py</span></code>) is a handy little object called the <code class="docutils literal notranslate"><span class="pre">TutorialMirror</span></code>. The mirror will echo whatever is being sent to it to
|
||||
the room it is in.</p>
|
||||
<p>On the game command-line, let’s create a mirror:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> create/drop mirror:contrib.tutorials.mirror.TutorialMirror
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Creating objects</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">create</span></code> command was first used to create boxes in the
|
||||
<a class="reference internal" href="Beginner-Tutorial-Building-Quickstart.html"><span class="doc std std-doc">Building Stuff</span></a> tutorial. You should now recognize
|
||||
that it uses a “python-path” to tell Evennia where to load the mirror’s code from.</p>
|
||||
</aside>
|
||||
<p>A mirror should appear in your location.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> look mirror
|
||||
mirror shows your reflection:
|
||||
This is User #1
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>What you are seeing is actually your own avatar in the game, the same thing that is available as <code class="docutils literal notranslate"><span class="pre">me</span></code> in the <code class="docutils literal notranslate"><span class="pre">py</span></code> command.</p>
|
||||
<p>What we are aiming for now is the equivalent of <code class="docutils literal notranslate"><span class="pre">mirror.msg("Mirror</span> <span class="pre">Mirror</span> <span class="pre">on</span> <span class="pre">the</span> <span class="pre">wall")</span></code>. But the first thing that comes to mind will not work:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py mirror.msg("Mirror, Mirror on the wall ...")
|
||||
NameError: name 'mirror' is not defined.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This is not surprising: Python knows nothing about “mirrors” or locations or anything. The <code class="docutils literal notranslate"><span class="pre">me</span></code> we’ve been using is, as mentioned, just a convenient thing the Evennia devs makes available to the <code class="docutils literal notranslate"><span class="pre">py</span></code> command. They couldn’t possibly predict that you wanted to talk to mirrors.</p>
|
||||
<p>Instead we will need to <em>search</em> for that <code class="docutils literal notranslate"><span class="pre">mirror</span></code> object before we can send to it. Make sure you are in the same location as the mirror and try:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py me.search("mirror")
|
||||
mirror
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">me.search("name")</span></code> will, by default, search and <em>return</em> an object with the given name found in <em>the same location</em> as the <code class="docutils literal notranslate"><span class="pre">me</span></code> object is. If it can’t find anything you’ll see an error.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Function returns</p>
|
||||
<p>Whereas a function like <code class="docutils literal notranslate"><span class="pre">print</span></code> only prints its arguments, it’s very common
|
||||
for functions/methods to <code class="docutils literal notranslate"><span class="pre">return</span></code> a result of some kind. Think of the function
|
||||
as a machine - you put something in and out comes a result you can use. In the case of <code class="docutils literal notranslate"><span class="pre">me.search</span></code>, it will perform a database search and spit out the object it finds.</p>
|
||||
</aside>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py me.search("dummy")
|
||||
Could not find 'dummy'.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Wanting to find things in the same location is very common, but as we continue we’ll
|
||||
find that Evennia provides ample tools for tagging, searching and finding things from all over your game.</p>
|
||||
<p>Now that we know how to find the ‘mirror’ object, we just need to use that instead of <code class="docutils literal notranslate"><span class="pre">me</span></code>!</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py mirror = self.search("mirror") ; mirror.msg("Mirror, Mirror on the wall ...")
|
||||
mirror echoes back to you:
|
||||
"Mirror, Mirror on the wall ..."
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The mirror is useful for testing because its <code class="docutils literal notranslate"><span class="pre">.msg</span></code> method just echoes whatever is sent to it back to the room. More common would be to talk to a player character, in which case the text you sent would have appeared in their game client.</p>
|
||||
</section>
|
||||
<section id="multi-line-py">
|
||||
<h2><span class="section-number">3.8. </span>Multi-line py<a class="headerlink" href="#multi-line-py" title="Permalink to this headline">¶</a></h2>
|
||||
<p>So far we have use <code class="docutils literal notranslate"><span class="pre">py</span></code> in single-line mode, using <code class="docutils literal notranslate"><span class="pre">;</span></code> to separate multiple inputs. This is very convenient when you want to do some quick testing. But you can also start a full multi-line Python interactive interpreter inside Evennia.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py
|
||||
Evennia Interactive Python mode
|
||||
Python 3.11.0 (default, Nov 22 2022, 11:21:55)
|
||||
[GCC 8.2.0] on Linux
|
||||
[py mode - quit() to exit]
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>(the details of the output will vary with your Python version and OS). You are now in python interpreter mode. It means
|
||||
that <em>everything</em> you insert from now on will become a line of Python (you can no longer look around or do other
|
||||
commands).</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> print("Hello World")
|
||||
|
||||
>>> print("Hello World")
|
||||
Hello World
|
||||
[py mode - quit() to exit]
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Note that we didn’t need to put <code class="docutils literal notranslate"><span class="pre">py</span></code> in front now. The system will also echo your input (that’s the bit after the <code class="docutils literal notranslate"><span class="pre">>>></span></code>). For brevity in this tutorual we’ll turn the echo off. First exit <code class="docutils literal notranslate"><span class="pre">py</span></code> and then start again with the <code class="docutils literal notranslate"><span class="pre">/noecho</span></code> flag.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> quit()
|
||||
Closing the Python console.
|
||||
> py/noecho
|
||||
Evennia Interactive Python mode (no echoing of prompts)
|
||||
Python 3.11.0 (default, Nov 22 2022, 11:21:56)
|
||||
[GCC 8.2.0] on Linux
|
||||
[py mode - quit() to exit]
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">interactive py</p>
|
||||
<ul class="simple">
|
||||
<li><p>Start with <code class="docutils literal notranslate"><span class="pre">py</span></code>.</p></li>
|
||||
<li><p>Use <code class="docutils literal notranslate"><span class="pre">py/noecho</span></code> if you don’t want your input to be echoed for every line.</p></li>
|
||||
<li><p><em>All</em> your inputs will now be interpreted as Python code.</p></li>
|
||||
<li><p>Exit with <code class="docutils literal notranslate"><span class="pre">quit()</span></code>.</p></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<p>We can now enter multi-line Python code:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> a = "Test"
|
||||
> print(f"This is a {a}.")
|
||||
This is a Test.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Let’s try to define a function:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> def hello_world(who, txt):
|
||||
...
|
||||
> who.msg(txt)
|
||||
...
|
||||
>
|
||||
[py mode - quit() to exit]
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Some important things above:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Definining a function with <code class="docutils literal notranslate"><span class="pre">def</span></code> means we are starting a new code block. Python works so that you mark the content
|
||||
of the block with indention. So the next line must be manually indented (4 spaces is a good standard) in order
|
||||
for Python to know it’s part of the function body.</p></li>
|
||||
<li><p>We expand the <code class="docutils literal notranslate"><span class="pre">hello_world</span></code> function with another argument <code class="docutils literal notranslate"><span class="pre">txt</span></code>. This allows us to send any text, not just
|
||||
“Hello World” over and over.</p></li>
|
||||
<li><p>To tell <code class="docutils literal notranslate"><span class="pre">py</span></code> that no more lines will be added to the function body, we end with an empty input. When the normal prompt returns, we know we are done.</p></li>
|
||||
</ul>
|
||||
<p>Now we have defined a new function. Let’s try it out:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> hello_world(me, "Hello world to me!")
|
||||
Hello world to me!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">me</span></code> is still available to us, so we pass that as the <code class="docutils literal notranslate"><span class="pre">who</span></code> argument, along with a little longer
|
||||
string. Let’s combine this with searching for the mirror.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> mirror = me.search("mirror")
|
||||
> hello_world(mirror, "Mirror, Mirror on the wall ...")
|
||||
mirror echoes back to you:
|
||||
"Mirror, Mirror on the wall ..."
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Exit the <code class="docutils literal notranslate"><span class="pre">py</span></code> mode with</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> quit()
|
||||
Closing the Python console.
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="other-ways-to-test-python-code">
|
||||
<h2><span class="section-number">3.9. </span>Other ways to test Python code<a class="headerlink" href="#other-ways-to-test-python-code" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">py</span></code> command is very powerful for experimenting with Python in-game. It’s great for quick testing.
|
||||
But you are still limited to working over telnet or the webclient, interfaces that doesn’t know anything
|
||||
about Python per-se.</p>
|
||||
<p>Outside the game, go to the terminal where you ran Evennia (or any terminal where the <code class="docutils literal notranslate"><span class="pre">evennia</span></code> command
|
||||
is available).</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">cd</span></code> to your game dir.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">shell</span></code></p></li>
|
||||
</ul>
|
||||
<p>A Python shell opens. This works like <code class="docutils literal notranslate"><span class="pre">py</span></code> did inside the game, with the exception that you don’t have
|
||||
<code class="docutils literal notranslate"><span class="pre">me</span></code> available out of the box. If you want <code class="docutils literal notranslate"><span class="pre">me</span></code>, you need to first find yourself:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> import evennia
|
||||
> me = evennia.search_object("YourChar")[0]
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here we make use of one of evennia’s search functions, available by importing <code class="docutils literal notranslate"><span class="pre">evennia</span></code> directly.
|
||||
We will cover more advanced searching later, but suffice to say, you put your own character name instead of
|
||||
“YourChar” above.</p>
|
||||
<blockquote>
|
||||
<div><p>The <code class="docutils literal notranslate"><span class="pre">[0]</span></code> at the end is because <code class="docutils literal notranslate"><span class="pre">.search_object</span></code> returns a list of objects and we want to
|
||||
get at the first of them (counting starts from 0).</p>
|
||||
</div></blockquote>
|
||||
<p>Use <code class="docutils literal notranslate"><span class="pre">Ctrl-D</span></code> (<code class="docutils literal notranslate"><span class="pre">Cmd-D</span></code> on Mac) or <code class="docutils literal notranslate"><span class="pre">quit()</span></code> to exit the Python console.</p>
|
||||
</section>
|
||||
<section id="ipython">
|
||||
<h2><span class="section-number">3.10. </span>ipython<a class="headerlink" href="#ipython" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The default Python shell is quite limited and ugly. It’s <em>highly</em> recommended to install <code class="docutils literal notranslate"><span class="pre">ipython</span></code> instead. This
|
||||
is a much nicer, third-party Python interpreter with colors and many usability improvements.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>pip install ipython
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If <code class="docutils literal notranslate"><span class="pre">ipython</span></code> is installed, <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">shell</span></code> will use it automatically.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia shell
|
||||
...
|
||||
IPython 7.4.0 -- An enhanced Interactive Python. Type '?' for help
|
||||
In [1]: You now have Tab-completion:
|
||||
|
||||
> import evennia
|
||||
> evennia.<TAB>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>That is, enter <code class="docutils literal notranslate"><span class="pre">evennia.</span></code> and then press the TAB key - you will be given a list of all the resources
|
||||
available on the <code class="docutils literal notranslate"><span class="pre">evennia</span></code> object. This is great for exploring what Evennia has to offer. For example,
|
||||
use your arrow keys to scroll to <code class="docutils literal notranslate"><span class="pre">search_object()</span></code> to fill it in.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> evennia.search_object?
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Adding a <code class="docutils literal notranslate"><span class="pre">?</span></code> and pressing return will give you the full documentation for <code class="docutils literal notranslate"><span class="pre">.search_object</span></code>. Use <code class="docutils literal notranslate"><span class="pre">??</span></code> if you
|
||||
want to see the entire source code.</p>
|
||||
<p>As for the normal python interpreter, use <code class="docutils literal notranslate"><span class="pre">Ctrl-D</span></code>/<code class="docutils literal notranslate"><span class="pre">Cmd-D</span></code> or <code class="docutils literal notranslate"><span class="pre">quit()</span></code> to exit ipython.</p>
|
||||
<div class="admonition important">
|
||||
<p class="admonition-title">Important</p>
|
||||
<p>Persistent code</p>
|
||||
<p>Common for both <code class="docutils literal notranslate"><span class="pre">py</span></code> and <code class="docutils literal notranslate"><span class="pre">python</span></code>/<code class="docutils literal notranslate"><span class="pre">ipython</span></code> is that the code you write is not persistent - it will
|
||||
be gone after you shut down the interpreter (but ipython will remember your input history). For making long-lasting
|
||||
Python code, we need to save it in a Python module, like we did for <code class="docutils literal notranslate"><span class="pre">world/test.py</span></code>.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section id="conclusions">
|
||||
<h2><span class="section-number">3.11. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This covers quite a lot of basic Python usage. We printed and formatted strings, defined our own
|
||||
first function, fixed an error and even searched and talked to a mirror! Being able to access
|
||||
python inside and outside of the game is an important skill for testing and debugging, but in
|
||||
practice you will be writing most your code in Python modules.</p>
|
||||
<p>To that end we also created a first new Python module in the <code class="docutils literal notranslate"><span class="pre">mygame/</span></code> game dir, then imported and used it. Now let’s look at the rest of the stuff you’ve got going on inside that <code class="docutils literal notranslate"><span class="pre">mygame/</span></code> folder …</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="Beginner-Tutorial-Gamedir-Overview.html" title="4. Overview of your new Game Dir"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Tutorial-World.html" title="2. The Tutorial World"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">3. </span>Intro to using Python with Evennia</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,544 @@
|
|||
|
||||
<!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>5. Introduction to Python classes and objects — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="6. Overview of the Evennia library" href="Beginner-Tutorial-Evennia-Library-Overview.html" />
|
||||
<link rel="prev" title="4. Overview of your new Game Dir" href="Beginner-Tutorial-Gamedir-Overview.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Evennia-Library-Overview.html" title="6. Overview of the Evennia library"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Gamedir-Overview.html" title="4. Overview of your new Game Dir"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">5. </span>Introduction to Python classes and objects</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">5. Introduction to Python classes and objects</a><ul>
|
||||
<li><a class="reference internal" href="#importing-things">5.1. Importing things</a></li>
|
||||
<li><a class="reference internal" href="#on-classes-and-objects">5.2. On classes and objects</a><ul>
|
||||
<li><a class="reference internal" href="#classes-and-instances">5.2.1. Classes and instances</a></li>
|
||||
<li><a class="reference internal" href="#whats-so-good-about-objects">5.2.2. What’s so good about objects?</a></li>
|
||||
<li><a class="reference internal" href="#classes-can-have-children">5.2.3. Classes can have children</a></li>
|
||||
<li><a class="reference internal" href="#a-look-at-multiple-inheritance">5.2.4. A look at multiple inheritance</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#summary">5.3. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Gamedir-Overview.html"
|
||||
title="previous chapter"><span class="section-number">4. </span>Overview of your new Game Dir</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Evennia-Library-Overview.html"
|
||||
title="next chapter"><span class="section-number">6. </span>Overview of the Evennia library</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Python-classes-and-objects.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="introduction-to-python-classes-and-objects">
|
||||
<h1><span class="section-number">5. </span>Introduction to Python classes and objects<a class="headerlink" href="#introduction-to-python-classes-and-objects" title="Permalink to this headline">¶</a></h1>
|
||||
<p>We have now learned how to run some simple Python code from inside (and outside) your game server.
|
||||
We have also taken a look at what our game dir looks and what is where. Now we’ll start to use it.</p>
|
||||
<section id="importing-things">
|
||||
<h2><span class="section-number">5.1. </span>Importing things<a class="headerlink" href="#importing-things" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In a <a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html#importing-code-from-other-modules"><span class="std std-doc">previous lesson</span></a> we already learned how to import resources into our code. Now we’ll dive a little deeper.</p>
|
||||
<p>No one writes something as big as an online game in one single huge file. Instead one breaks up the code into separate files (modules). Each module is dedicated to different purposes. Not only does it make things cleaner, organized and easier to understand.</p>
|
||||
<p>Splitting code also makes it easier to re-use - you just import the resources you need and know you only get just what you requested. This makes it easier to spot errors and to know what code is good and which has issues.</p>
|
||||
<blockquote>
|
||||
<div><p>Evennia itself uses your code in the same way - you just tell it where a particular type of code is,
|
||||
and it will import and use it (often instead of its defaults).</p>
|
||||
</div></blockquote>
|
||||
<p>Here’s a familiar example:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py import world.test ; world.test.hello_world(me)
|
||||
Hello World!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In this example, on your hard drive, the files looks like this:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">mygame</span><span class="o">/</span>
|
||||
<span class="n">world</span><span class="o">/</span>
|
||||
<span class="n">test</span><span class="o">.</span><span class="n">py</span> <span class="o"><-</span> <span class="n">inside</span> <span class="n">this</span> <span class="n">file</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">function</span> <span class="n">hello_world</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you followed earlier tutorial lessons, the <code class="docutils literal notranslate"><span class="pre">mygame/world/test.py</span></code> file should look like this (if
|
||||
not, make it so):</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">hello_world</span><span class="p">(</span><span class="n">who</span><span class="p">):</span>
|
||||
<span class="n">who</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Hello World!"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Whitespace matters in Python!</p>
|
||||
<ul class="simple">
|
||||
<li><p>Indentation matters in Python</p></li>
|
||||
<li><p>So does capitalization</p></li>
|
||||
<li><p>Use 4 <code class="docutils literal notranslate"><span class="pre">spaces</span></code> to indent, not tabs</p></li>
|
||||
<li><p>Empty lines are fine</p></li>
|
||||
<li><p>Anything on a line after a <code class="docutils literal notranslate"><span class="pre">#</span></code> is a <code class="docutils literal notranslate"><span class="pre">comment</span></code>, ignored by Python</p></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<p>To reiterate, the <em>python_path</em> describes the relation between Python resources, both between and inside Python <em>modules</em> (that is, files ending with .py). Paths use <code class="docutils literal notranslate"><span class="pre">.</span></code> and always skips the <code class="docutils literal notranslate"><span class="pre">.py</span></code> file endings. Also, Evennia already knows to start looking for python resources inside <code class="docutils literal notranslate"><span class="pre">mygame/</span></code> so this should never be included.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>import world.test
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">import</span></code> Python instruction loads <code class="docutils literal notranslate"><span class="pre">world.test</span></code> so you have it available. You can now go “into”
|
||||
this module to get to the function you want:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>world.test.hello_world(me)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Using <code class="docutils literal notranslate"><span class="pre">import</span></code> like this means that you have to specify the full <code class="docutils literal notranslate"><span class="pre">world.test</span></code> every time you want
|
||||
to get to your function. Here’s an alternative:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>from world.test import hello_world
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">...</span> <span class="pre">import</span> <span class="pre">...</span></code> is very, very common as long as you want to get something with a longer
|
||||
python path. It imports <code class="docutils literal notranslate"><span class="pre">hello_world</span></code> directly, so you can use it right away!</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> > py from world.test import hello_world ; hello_world(me)
|
||||
Hello World!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Let’s say your <code class="docutils literal notranslate"><span class="pre">test.py</span></code> module had a bunch of interesting functions. You could then import them
|
||||
all one by one:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>from world.test import hello_world, my_func, awesome_func
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If there were <em>a lot</em> of functions, you could instead just import <code class="docutils literal notranslate"><span class="pre">test</span></code> and get the function
|
||||
from there when you need (without having to give the full <code class="docutils literal notranslate"><span class="pre">world.test</span></code> every time):</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> from world import test ; test.hello_world(me)
|
||||
Hello World!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can also <em>rename</em> stuff you import. Say for example that the module you import to already has a function <code class="docutils literal notranslate"><span class="pre">hello_world</span></code> but we also want to use the one from <code class="docutils literal notranslate"><span class="pre">world/test.py</span></code>:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>from world.test import hello_world as test_hello_world
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The form <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">...</span> <span class="pre">import</span> <span class="pre">...</span> <span class="pre">as</span> <span class="pre">...</span></code> renames the import.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> from world.test import hello_world as hw ; hw(me)
|
||||
Hello World!
|
||||
</pre></div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div><p>Avoid renaming unless it’s to avoid a name-collistion like above - you want to make things as easy to read as possible, and renaming adds another layer of potential confusion.</p>
|
||||
</div></blockquote>
|
||||
<p>In <a class="reference internal" href="Beginner-Tutorial-Python-basic-introduction.html"><span class="doc std std-doc">the basic intro to Python</span></a> we learned how to open the in-game
|
||||
multi-line interpreter.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py
|
||||
Evennia Interactive Python mode
|
||||
Python 3.7.1 (default, Oct 22 2018, 11:21:55)
|
||||
[GCC 8.2.0] on Linux
|
||||
[py mode - quit() to exit]
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You now only need to import once to use the imported function over and over.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> from world.test import hello_world
|
||||
> hello_world()
|
||||
Hello World!
|
||||
> hello_world()
|
||||
Hello World!
|
||||
> hello_world()
|
||||
Hello World!
|
||||
> quit()
|
||||
Closing the Python console.
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Alternative to py </p>
|
||||
<p>If you find entering multiple lines in the <code class="docutils literal notranslate"><span class="pre">py</span></code> command clunky (a traditional mud client is pretty limited for this kind of thing) you can also <code class="docutils literal notranslate"><span class="pre">cd</span></code> to your <code class="docutils literal notranslate"><span class="pre">mygame</span></code> folder and run <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">shell</span></code>. You will end up in a python shell where Evennia is available. If you do <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">ipython</span></code> you’ll get an even more modern python shell to use. This works outside the game but <code class="docutils literal notranslate"><span class="pre">print</span></code> will show in the same way.</p>
|
||||
</aside>
|
||||
<p>The same goes when writing code in a module - in most Python modules you will see a bunch of imports at the top, resources that are then used by all code in that module.</p>
|
||||
</section>
|
||||
<section id="on-classes-and-objects">
|
||||
<h2><span class="section-number">5.2. </span>On classes and objects<a class="headerlink" href="#on-classes-and-objects" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Now that we know about imports, let look at a real Evennia module and try to understand it.</p>
|
||||
<p>Open <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/scripts.py</span></code> in your text editor of choice.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/typeclasses/script.py</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd">module docstring</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultScript</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Script</span><span class="p">(</span><span class="n">DefaultScript</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> class docstring</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Docstrings vs Comments</p>
|
||||
<p>A docstring is not the same as a comment (created by <code class="docutils literal notranslate"><span class="pre">#</span></code>). A docstring is not ignored by Python but is an integral part of the thing it is documenting (the module and the class in this case). For example, we read docstrings to help text for <a class="reference internal" href="../../../Evennia-API.html"><span class="doc std std-doc">API documentation</span></a>; we could not do that with comments.</p>
|
||||
</aside>
|
||||
<p>The real file is much longer but we can ignore the multi-line strings (<code class="docutils literal notranslate"><span class="pre">"""</span> <span class="pre">...</span> <span class="pre">"""</span></code>). These serve as documentation-strings, or <em>docstrings</em> for the module (at the top) and the <code class="docutils literal notranslate"><span class="pre">class</span></code> below.</p>
|
||||
<p>Below the module doc string we have the <em>import</em>. In this case we are importing a resource
|
||||
from the core <code class="docutils literal notranslate"><span class="pre">evennia</span></code> library itself. We will dive into this later, for now we just treat this
|
||||
as a black box.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">class</span></code> named <code class="docutils literal notranslate"><span class="pre">Script</span></code> _ inherits_ from <code class="docutils literal notranslate"><span class="pre">DefaultScript</span></code>. As you can see <code class="docutils literal notranslate"><span class="pre">Script</span></code> is pretty much empty. All the useful code is actually in <code class="docutils literal notranslate"><span class="pre">DefaultScript</span></code> (<code class="docutils literal notranslate"><span class="pre">Script</span></code> <em>inherits</em> that code unless it <em>overrides</em> it with same-named code of its own).</p>
|
||||
<p>We need to do a little detour to understand what a ‘class’, an ‘object’ or ‘instance’ is. These are fundamental things to understand before you can use Evennia efficiently.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">OOP</p>
|
||||
<p>Classes, objects, instances and inheritance are fundamental to Python. This and some other concepts are often clumped together under the term Object-Oriented-Programming (OOP).</p>
|
||||
</aside>
|
||||
<section id="classes-and-instances">
|
||||
<h3><span class="section-number">5.2.1. </span>Classes and instances<a class="headerlink" href="#classes-and-instances" title="Permalink to this headline">¶</a></h3>
|
||||
<p>A ‘class’ can be seen as a ‘template’ for a ‘type’ of object. The class describes the basic functionality of everyone of that class. For example, we could have a class <code class="docutils literal notranslate"><span class="pre">Monster</span></code> which has resources for moving itself from room to room.</p>
|
||||
<p>Open a new file <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/monsters.py</span></code>. Add the following simple class:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||||
<span class="k">class</span> <span class="nc">Monster</span><span class="p">:</span>
|
||||
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"Monster"</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">move_around</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is moving!"</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Above we have defined a <code class="docutils literal notranslate"><span class="pre">Monster</span></code> class with one variable <code class="docutils literal notranslate"><span class="pre">key</span></code> (that is, the name) and one
|
||||
<em>method</em> on it. A method is like a function except it sits “on” the class. It also always has
|
||||
at least one argument (almost always written as <code class="docutils literal notranslate"><span class="pre">self</span></code> although you could in principle use
|
||||
another name), which is a reference back to itself. So when we print <code class="docutils literal notranslate"><span class="pre">self.key</span></code> we are referring back to the <code class="docutils literal notranslate"><span class="pre">key</span></code> on the class.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Terms</p>
|
||||
<ul class="simple">
|
||||
<li><p>A <code class="docutils literal notranslate"><span class="pre">class</span></code> is a code template describing a ‘type’ of something</p></li>
|
||||
<li><p>An <code class="docutils literal notranslate"><span class="pre">object</span></code> is an <code class="docutils literal notranslate"><span class="pre">instance</span></code> of a <code class="docutils literal notranslate"><span class="pre">class</span></code>. Like using a mold to cast tin soldiers, one class can be <code class="docutils literal notranslate"><span class="pre">instantiated</span></code> into any number of object-instances. Each instance does not need to be identical (much like each tin soldier can be painted differently).</p></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<p>A class is just a template. Before it can be used, we must create an <em>instance</em> of the class. If
|
||||
<code class="docutils literal notranslate"><span class="pre">Monster</span></code> is a class, then an instance is <code class="docutils literal notranslate"><span class="pre">Fluffy</span></code>, a specific dragon individual. You instantiate
|
||||
by <em>calling</em> the class, much like you would a function:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>fluffy = Monster()
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Let’s try it in-game (we use <code class="docutils literal notranslate"><span class="pre">py</span></code> multi-line mode, it’s easier)</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py
|
||||
> from typeclasses.monsters import Monster
|
||||
> fluffy = Monster()
|
||||
> fluffy.move_around()
|
||||
Monster is moving!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We created an <em>instance</em> of <code class="docutils literal notranslate"><span class="pre">Monster</span></code>, which we stored in the variable <code class="docutils literal notranslate"><span class="pre">fluffy</span></code>. We then
|
||||
called the <code class="docutils literal notranslate"><span class="pre">move_around</span></code> method on fluffy to get the printout.</p>
|
||||
<blockquote>
|
||||
<div><p>Note how we <em>didn’t</em> call the method as <code class="docutils literal notranslate"><span class="pre">fluffy.move_around(self)</span></code>. While the <code class="docutils literal notranslate"><span class="pre">self</span></code> has to be there when defining the method, we <em>never</em> add it explicitly when we call the method (Python will add the correct <code class="docutils literal notranslate"><span class="pre">self</span></code> for us automatically behind the scenes).</p>
|
||||
</div></blockquote>
|
||||
<p>Let’s create the sibling of Fluffy, Cuddly:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> cuddly = Monster()
|
||||
> cuddly.move_around()
|
||||
Monster is moving!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We now have two monsters and they’ll hang around until with call <code class="docutils literal notranslate"><span class="pre">quit()</span></code> to exit this Python
|
||||
instance. We can have them move as many times as we want. But no matter how many monsters we create, they will all show the same printout since <code class="docutils literal notranslate"><span class="pre">key</span></code> is always fixed as “Monster”.</p>
|
||||
<p>Let’s make the class a little more flexible:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||||
<span class="k">class</span> <span class="nc">Monster</span><span class="p">:</span>
|
||||
|
||||
<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">key</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">key</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">move_around</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is moving!"</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">__init__</span></code> is a special method that Python recognizes. If given, this handles extra arguments when you instantiate a new Monster. We have it add an argument <code class="docutils literal notranslate"><span class="pre">key</span></code> that we store on <code class="docutils literal notranslate"><span class="pre">self</span></code>.</p>
|
||||
<p>Now, for Evennia to see this code change, we need to reload the server. You can either do it this way:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> quit()
|
||||
Python Console is closing.
|
||||
> reload
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Or you can use a separate terminal and restart from outside the game:</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">On reloading</p>
|
||||
<p>Reloading with the python mode gets a little annoying since you need to redo everything after every reload. Just keep in mind that during regular development you will not be working this way. The in-game python mode is practical for quick fixes and experiments like this, but actual code is normally written externally, in python modules.</p>
|
||||
</aside>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>$ evennia reload (or restart)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Either way you’ll need to go into <code class="docutils literal notranslate"><span class="pre">py</span></code> again:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py
|
||||
> from typeclasses.monsters import Monster
|
||||
fluffy = Monster("Fluffy")
|
||||
fluffy.move_around()
|
||||
Fluffy is moving!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now we passed <code class="docutils literal notranslate"><span class="pre">"Fluffy"</span></code> as an argument to the class. This went into <code class="docutils literal notranslate"><span class="pre">__init__</span></code> and set <code class="docutils literal notranslate"><span class="pre">self.key</span></code>, which we later used to print with the right name!</p>
|
||||
</section>
|
||||
<section id="whats-so-good-about-objects">
|
||||
<h3><span class="section-number">5.2.2. </span>What’s so good about objects?<a class="headerlink" href="#whats-so-good-about-objects" title="Permalink to this headline">¶</a></h3>
|
||||
<p>So far all we’ve seen a class do is to behave like our first <code class="docutils literal notranslate"><span class="pre">hello_world</span></code> function but being more complex. We could just have made a function:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="k">def</span> <span class="nf">monster_move_around</span><span class="p">(</span><span class="n">key</span><span class="p">):</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">key</span><span class="si">}</span><span class="s2"> is moving!"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The difference between the function and an instance of a class (the object), is that the object retains <em>state</em>. Once you called the function it forgets everything about what you called it with last time. The object, on the other hand, remembers changes:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> fluffy.key = "Fluffy, the red dragon"
|
||||
> fluffy.move_around()
|
||||
Fluffy, the red dragon is moving!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">fluffy</span></code> object’s <code class="docutils literal notranslate"><span class="pre">key</span></code> was changed for as long as it’s around. This makes objects extremely useful for representing and remembering collections of data - some of which can be other objects in turn. Some examples:</p>
|
||||
<ul class="simple">
|
||||
<li><p>A player character with all its stats</p></li>
|
||||
<li><p>A monster with HP</p></li>
|
||||
<li><p>A chest with a number of gold coins in it</p></li>
|
||||
<li><p>A room with other objects inside it</p></li>
|
||||
<li><p>The current policy positions of a political party</p></li>
|
||||
<li><p>A rule with methods for resolving challenges or roll dice</p></li>
|
||||
<li><p>A multi-dimenstional data-point for a complex economic simulation</p></li>
|
||||
<li><p>And so much more!</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="classes-can-have-children">
|
||||
<h3><span class="section-number">5.2.3. </span>Classes can have children<a class="headerlink" href="#classes-can-have-children" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Classes can <em>inherit</em> from each other. A “child” class will inherit everything from its “parent” class. But if the child adds something with the same name as its parent, it will <em>override</em> whatever it got from its parent.</p>
|
||||
<p>Let’s expand <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/monsters.py</span></code> with another class:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span>
|
||||
<span class="k">class</span> <span class="nc">Monster</span><span class="p">:</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> This is a base class for Monster.</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<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">key</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">key</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">move_around</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is moving!"</span><span class="p">)</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">Dragon</span><span class="p">(</span><span class="n">Monster</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> This is a dragon monster.</span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">move_around</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> flies through the air high above!"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">firebreath</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Let our dragon breathe fire.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> breathes fire!"</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We added some docstrings for clarity. It’s always a good idea to add doc strings; you can do so also for methods, as exemplified for the new <code class="docutils literal notranslate"><span class="pre">firebreath</span></code> method.</p>
|
||||
<p>We created the new class <code class="docutils literal notranslate"><span class="pre">Dragon</span></code> but we also specified that <code class="docutils literal notranslate"><span class="pre">Monster</span></code> is the <em>parent</em> of <code class="docutils literal notranslate"><span class="pre">Dragon</span></code> but adding the parent in parenthesis. <code class="docutils literal notranslate"><span class="pre">class</span> <span class="pre">Classname(Parent)</span></code> is the way to do this.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Multi-inheritance</p>
|
||||
<p>It’s possible to add more comma-separated parents to a class. We show an example of such ‘multiple inheritance’ last in this lesson. You should usually avoid yourself setting up multiple inheritance until you know what you are doing. A single parent will be enough for almost every case you’ll need.</p>
|
||||
</aside>
|
||||
<p>Let’s try out our new class. First <code class="docutils literal notranslate"><span class="pre">reload</span></code> the server and then:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py
|
||||
> from typeclasses.monsters import Dragon
|
||||
> smaug = Dragon("Smaug")
|
||||
> smaug.move_around()
|
||||
Smaug flies through the air high above!
|
||||
> smaug.firebreath()
|
||||
Smaug breathes fire!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Because we didn’t (re)implement <code class="docutils literal notranslate"><span class="pre">__init__</span></code> in <code class="docutils literal notranslate"><span class="pre">Dragon</span></code>, we got the one from <code class="docutils literal notranslate"><span class="pre">Monster</span></code>. We did implement our own <code class="docutils literal notranslate"><span class="pre">move_around</span></code> in <code class="docutils literal notranslate"><span class="pre">Dragon</span></code>, so it <em>overrides</em> the one in <code class="docutils literal notranslate"><span class="pre">Monster</span></code>. And <code class="docutils literal notranslate"><span class="pre">firebreath</span></code> is only available for <code class="docutils literal notranslate"><span class="pre">Dragon</span></code>s. Having that on <code class="docutils literal notranslate"><span class="pre">Monster</span></code> would not have made much sense, since not every monster can breathe fire.</p>
|
||||
<p>One can also force a class to use resources from the parent even if you are overriding some of it. This is done with the <code class="docutils literal notranslate"><span class="pre">super()</span></code> method. Modify your <code class="docutils literal notranslate"><span class="pre">Dragon</span></code> class as follows:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># ...</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Dragon</span><span class="p">(</span><span class="n">Monster</span><span class="p">):</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">move_around</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">move_around</span><span class="p">()</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="s2">"The world trembles."</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div><p>Keep <code class="docutils literal notranslate"><span class="pre">Monster</span></code> and the <code class="docutils literal notranslate"><span class="pre">firebreath</span></code> method. The <code class="docutils literal notranslate"><span class="pre">#</span> <span class="pre">...</span></code> above indicates the rest of the code is unchanged.</p>
|
||||
</div></blockquote>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">super().move_around()</span></code> line means that we are calling <code class="docutils literal notranslate"><span class="pre">move_around()</span></code> on the parent of the class. So in this case, we will call <code class="docutils literal notranslate"><span class="pre">Monster.move_around</span></code> first, before doing our own thing.</p>
|
||||
<p>To see, <code class="docutils literal notranslate"><span class="pre">reload</span></code> the server and then:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> py
|
||||
> from typeclasses.monsters import Dragon
|
||||
> smaug = Dragon("Smaug")
|
||||
> smaug.move_around()
|
||||
Smaug is moving!
|
||||
The world trembles.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We can see that <code class="docutils literal notranslate"><span class="pre">Monster.move_around()</span></code> is called first and prints “Smaug is moving!”, followed by the extra bit about the trembling world from the <code class="docutils literal notranslate"><span class="pre">Dragon</span></code> class.</p>
|
||||
<p>Inheritance is a powerful concept. It allows you to organize and re-use code while only adding the special things you want to change. Evennia uses this a lot.</p>
|
||||
</section>
|
||||
<section id="a-look-at-multiple-inheritance">
|
||||
<h3><span class="section-number">5.2.4. </span>A look at multiple inheritance<a class="headerlink" href="#a-look-at-multiple-inheritance" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Open <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/objects.py</span></code> in your text editor of choice.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">"""</span>
|
||||
<span class="sd">module docstring</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultObject</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">ObjectParent</span><span class="p">:</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> class docstring </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Object</span><span class="p">(</span><span class="n">ObjectParent</span><span class="p">,</span> <span class="n">DefaultObject</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> class docstring</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In this module we have an empty <code class="docutils literal notranslate"><span class="pre">class</span></code> named <code class="docutils literal notranslate"><span class="pre">ObjectParent</span></code>. It doesn’t do anything, its only code (except the docstring) is <code class="docutils literal notranslate"><span class="pre">pass</span></code> which means, well, to pass and don’t do anything. Since it also doesn’t <em>inherit</em> from anything, it’s just an empty container.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">class</span></code> named <code class="docutils literal notranslate"><span class="pre">Object</span></code>_ inherits_ from <code class="docutils literal notranslate"><span class="pre">ObjectParent</span></code> and <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>. Normally a class only has one parent, but here there are two. We already learned that a child inherits everything from a parent unless it overrides it. When there are more than one parents (“multiple inheritance”), inheritance happens from left to right.</p>
|
||||
<p>So if <code class="docutils literal notranslate"><span class="pre">obj</span></code> is an instance of <code class="docutils literal notranslate"><span class="pre">Object</span></code> and we try to access <code class="docutils literal notranslate"><span class="pre">obj.foo</span></code>, Python will first check if the <code class="docutils literal notranslate"><span class="pre">Object</span></code> class has a property/method <code class="docutils literal notranslate"><span class="pre">foo</span></code>. Next it will check if <code class="docutils literal notranslate"><span class="pre">ObjectParent</span></code> has it. Finally, it will check in <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>. If neither have it, you get an error.</p>
|
||||
<p>Why has Evennia set up an empty class parent like this? To answer, let’s check out another module, <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/rooms.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">"""</span>
|
||||
<span class="sd">...</span>
|
||||
<span class="sd">"""</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia.objects.objects</span> <span class="kn">import</span> <span class="n">DefaultRoom</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.objects</span> <span class="kn">import</span> <span class="n">ObjectParent</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Room</span><span class="p">(</span><span class="n">ObjectParent</span><span class="p">,</span> <span class="n">DefaultRoom</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> ...</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here we see that a <code class="docutils literal notranslate"><span class="pre">Room</span></code> inherits from the same <code class="docutils literal notranslate"><span class="pre">ObjectParent</span></code> (imported from <code class="docutils literal notranslate"><span class="pre">objects.py</span></code>) along with a <code class="docutils literal notranslate"><span class="pre">DefaultRoom</span></code> parent from the <code class="docutils literal notranslate"><span class="pre">evennia</span></code> library. You’ll find the same is true for <code class="docutils literal notranslate"><span class="pre">Character</span></code> and <code class="docutils literal notranslate"><span class="pre">Exit</span></code> as well. These are all examples of ‘in-game objects’, so they could well have a lot in common. The precense of <code class="docutils literal notranslate"><span class="pre">ObjectParent</span></code> gives you an (optional) way to add code that <em>should be the same for all those in-game entities</em>. Just put that code in <code class="docutils literal notranslate"><span class="pre">ObjectParent</span></code> and all the objects, characters, rooms and exits will automatically have it as well!</p>
|
||||
<p>We will get back to the <code class="docutils literal notranslate"><span class="pre">objects.py</span></code> module in the <a class="reference internal" href="Beginner-Tutorial-Learning-Typeclasses.html"><span class="doc std std-doc">next lesson</span></a>.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="summary">
|
||||
<h2><span class="section-number">5.3. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline">¶</a></h2>
|
||||
<p>We have created our first dragons from classes. We have learned a little about how you <em>instantiate</em> a class into an <em>object</em>. We have seen some examples of <em>inheritance</em> and we tested to <em>override</em> a method in the parent with one in the child class. We also used <code class="docutils literal notranslate"><span class="pre">super()</span></code> to good effect.</p>
|
||||
<p>We have used pretty much raw Python so far. In the coming lessons we’ll start to look at the extra bits that Evennia provides. But first we need to learn just where to find everything.</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="Beginner-Tutorial-Evennia-Library-Overview.html" title="6. Overview of the Evennia library"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Gamedir-Overview.html" title="4. Overview of your new Game Dir"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">5. </span>Introduction to Python classes and objects</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,445 @@
|
|||
|
||||
<!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>11. Searching for things — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="12. Advanced searching - Django Database queries" href="Beginner-Tutorial-Django-queries.html" />
|
||||
<link rel="prev" title="10. Creating things" href="Beginner-Tutorial-Creating-Things.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Django-queries.html" title="12. Advanced searching - Django Database queries"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Creating-Things.html" title="10. Creating things"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">11. </span>Searching for things</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">11. Searching for things</a><ul>
|
||||
<li><a class="reference internal" href="#main-search-functions">11.1. Main search functions</a></li>
|
||||
<li><a class="reference internal" href="#searching-using-object-search">11.2. Searching using Object.search</a></li>
|
||||
<li><a class="reference internal" href="#what-can-be-searched-for">11.3. What can be searched for</a><ul>
|
||||
<li><a class="reference internal" href="#search-by-key">11.3.1. Search by key</a></li>
|
||||
<li><a class="reference internal" href="#search-by-aliases">11.3.2. Search by aliases</a></li>
|
||||
<li><a class="reference internal" href="#search-by-location">11.3.3. Search by location</a></li>
|
||||
<li><a class="reference internal" href="#search-by-tags">11.3.4. Search by Tags</a></li>
|
||||
<li><a class="reference internal" href="#search-by-attribute">11.3.5. Search by Attribute</a></li>
|
||||
<li><a class="reference internal" href="#search-by-typeclass">11.3.6. Search by Typeclass</a></li>
|
||||
<li><a class="reference internal" href="#search-by-dbref">11.3.7. Search by dbref</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#finding-objects-relative-each-other">11.4. Finding objects relative each other</a></li>
|
||||
<li><a class="reference internal" href="#summary">11.5. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Creating-Things.html"
|
||||
title="previous chapter"><span class="section-number">10. </span>Creating things</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Django-queries.html"
|
||||
title="next chapter"><span class="section-number">12. </span>Advanced searching - Django Database queries</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Searching-Things.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="searching-for-things">
|
||||
<h1><span class="section-number">11. </span>Searching for things<a class="headerlink" href="#searching-for-things" title="Permalink to this headline">¶</a></h1>
|
||||
<p>We have gone through how to create the various entities in Evennia. But creating something is of little use if we cannot find and use it afterwards.</p>
|
||||
<section id="main-search-functions">
|
||||
<h2><span class="section-number">11.1. </span>Main search functions<a class="headerlink" href="#main-search-functions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The base tools are the <code class="docutils literal notranslate"><span class="pre">evennia.search_*</span></code> functions, such as <code class="docutils literal notranslate"><span class="pre">evennia.search_object</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">evennia</span>
|
||||
|
||||
<span class="n">roses</span> <span class="o">=</span> <span class="n">evennia</span><span class="o">.</span><span class="n">search_object</span><span class="p">(</span><span class="s2">"rose"</span><span class="p">)</span>
|
||||
<span class="n">accts</span> <span class="o">=</span> <span class="n">evennia</span><span class="o">.</span><span class="n">search_account</span><span class="p">(</span><span class="s2">"MyAccountName"</span><span class="p">,</span> <span class="n">email</span><span class="o">=</span><span class="s2">"foo@bar.com"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Querysets</p>
|
||||
<p>What is returned from the main search functions is actually a <code class="docutils literal notranslate"><span class="pre">queryset</span></code>. They can be treated like lists except that they can’t modified in-place. We’ll discuss querysets in the <code class="docutils literal notranslate"><span class="pre">next</span> <span class="pre">lesson</span></code> <Django-queries>`_.</p>
|
||||
</aside>
|
||||
<p>This searches by <code class="docutils literal notranslate"><span class="pre">key</span></code> of the object. Strings are always case-insensitive, so searching for <code class="docutils literal notranslate"><span class="pre">"rose"</span></code>, <code class="docutils literal notranslate"><span class="pre">"Rose"</span></code> or <code class="docutils literal notranslate"><span class="pre">"rOsE"</span></code> give the same results. It’s important to remember that what is returned from these search methods is a <em>listing</em> of zero, one or more elements - all the matches to your search. To get the first match:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>rose = roses[0]
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Often you really want all matches to the search parameters you specify. In other situations, having zero or more than one match is a sign of a problem and you need to handle this case yourself.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="n">the_one_ring</span> <span class="o">=</span> <span class="n">evennia</span><span class="o">.</span><span class="n">search_object</span><span class="p">(</span><span class="s2">"The one Ring"</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">the_one_ring</span><span class="p">:</span>
|
||||
<span class="c1"># handle not finding the ring at all</span>
|
||||
<span class="k">elif</span> <span class="nb">len</span><span class="p">(</span><span class="n">the_one_ring</span><span class="p">)</span> <span class="o">></span> <span class="mi">1</span><span class="p">:</span>
|
||||
<span class="c1"># handle finding more than one ring</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># ok - exactly one ring found</span>
|
||||
<span class="n">the_one_ring</span> <span class="o">=</span> <span class="n">the_one_ring</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>There are equivalent search functions for all the main resources. You can find a listing of them
|
||||
<a class="reference internal" href="../../../Evennia-API.html"><span class="doc std std-doc">in the Search functions section</span></a> of the API frontpage.</p>
|
||||
</section>
|
||||
<section id="searching-using-object-search">
|
||||
<h2><span class="section-number">11.2. </span>Searching using Object.search<a class="headerlink" href="#searching-using-object-search" title="Permalink to this headline">¶</a></h2>
|
||||
<p>On the <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> is a <code class="docutils literal notranslate"><span class="pre">.search</span></code> method which we have already tried out when we made Commands. For this to be used you must already have an object available:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>obj = evennia.search_object("My Object")[0] # assuming this exists
|
||||
rose = obj.search("rose")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This searches for objects based on <code class="docutils literal notranslate"><span class="pre">key</span></code> or aliases. The <code class="docutils literal notranslate"><span class="pre">.search</span></code> method wraps <code class="docutils literal notranslate"><span class="pre">evennia.search_object</span></code> and handles its output in various ways.</p>
|
||||
<ul class="simple">
|
||||
<li><p>By default it will always search for objects among those in <code class="docutils literal notranslate"><span class="pre">obj.location.contents</span></code> and <code class="docutils literal notranslate"><span class="pre">obj.contents</span></code> (that is, things in obj’s inventory or in the same room).</p></li>
|
||||
<li><p>It will always return exactly one match. If it found zero or more than one match, the return is <code class="docutils literal notranslate"><span class="pre">None</span></code>. This is different from <code class="docutils literal notranslate"><span class="pre">evennia.search</span></code>, which always returns a list.</p></li>
|
||||
<li><p>On a no-match or multimatch, <code class="docutils literal notranslate"><span class="pre">.search</span></code> will automatically send an error message to <code class="docutils literal notranslate"><span class="pre">obj</span></code>. So you don’t have to worry about reporting messages if the result is <code class="docutils literal notranslate"><span class="pre">None</span></code>.</p></li>
|
||||
</ul>
|
||||
<p>So this method handles error messaging for you. A very common way to use it is in commands:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">Command</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CmdQuickFind</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Find an item in your current location.</span>
|
||||
|
||||
<span class="sd"> Usage: </span>
|
||||
<span class="sd"> quickfind <query></span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="n">key</span> <span class="o">=</span> <span class="s2">"quickfind"</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="n">query</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">args</span>
|
||||
<span class="n">result</span> <span class="o">=</span> <span class="bp">self</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">query</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">result</span><span class="p">:</span>
|
||||
<span class="k">return</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">"Found match for </span><span class="si">{</span><span class="n">query</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">result</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Remember, <code class="docutils literal notranslate"><span class="pre">self.caller</span></code> is the one calling the command. This is usually a Character, which
|
||||
inherits from <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>. So it has <code class="docutils literal notranslate"><span class="pre">.search()</span></code> available on it.</p>
|
||||
<p>This simple little Command takes its arguments and searches for a match. If it can’t find it, <code class="docutils literal notranslate"><span class="pre">result</span></code> will be <code class="docutils literal notranslate"><span class="pre">None</span></code>. The error has already been reported to <code class="docutils literal notranslate"><span class="pre">self.caller</span></code> so we just abort with <code class="docutils literal notranslate"><span class="pre">return</span></code>.</p>
|
||||
<p>With the <code class="docutils literal notranslate"><span class="pre">global_search</span></code> flag, you can use <code class="docutils literal notranslate"><span class="pre">.search</span></code> to find anything, not just stuff in the same room:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>volcano = self.caller.search("Vesuvio", global_search=True)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can limit your matches to particular typeclasses:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>water_glass = self.caller.search("glass", typeclass="typeclasses.objects.WaterGlass")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you only want to search for a specific list of things, you can do so too:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>stone = self.caller.search("MyStone", candidates=[obj1, obj2, obj3, obj4])
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This will only return a match if “MyStone” is in the room (or in your inventory) <em>and</em> is one of the four provided candidate objects. This is quite powerful, here’s how you’d find something only in your inventory:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>potion = self.caller.search("Healing potion", candidates=self.caller.contents)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can also turn off the automatic error handling:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>swords = self.caller.search("Sword", quiet=True)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>With <code class="docutils literal notranslate"><span class="pre">quiet=True</span></code> the user will not be notified on zero or multi-match errors. Instead you are expected to handle this yourself. Furthermore, what is returned is now a list of zero, one or more matches!</p>
|
||||
</section>
|
||||
<section id="what-can-be-searched-for">
|
||||
<h2><span class="section-number">11.3. </span>What can be searched for<a class="headerlink" href="#what-can-be-searched-for" title="Permalink to this headline">¶</a></h2>
|
||||
<p>These are the main database entities one can search for:</p>
|
||||
<ul class="simple">
|
||||
<li><p><a class="reference internal" href="../../../Components/Objects.html"><span class="doc std std-doc">Objects</span></a></p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Accounts.html"><span class="doc std std-doc">Accounts</span></a></p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Scripts</span></a>,</p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Channels.html"><span class="doc std std-doc">Channels</span></a></p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Msg.html"><span class="doc std std-doc">Messages</span></a> (used by <code class="docutils literal notranslate"><span class="pre">page</span></code> command by default)</p></li>
|
||||
<li><p><a class="reference internal" href="../../../Components/Help-System.html"><span class="doc std std-doc">Help Entries</span></a> (help entries created manually)</p></li>
|
||||
</ul>
|
||||
<p>Most of the time you’ll likely spend your time searching for Objects and the occasional Accounts.</p>
|
||||
<p>So to find an entity, what can be searched for?</p>
|
||||
<section id="search-by-key">
|
||||
<h3><span class="section-number">11.3.1. </span>Search by key<a class="headerlink" href="#search-by-key" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">key</span></code> is the name of the entity. Searching for this is always case-insensitive.</p>
|
||||
</section>
|
||||
<section id="search-by-aliases">
|
||||
<h3><span class="section-number">11.3.2. </span>Search by aliases<a class="headerlink" href="#search-by-aliases" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Objects and Accounts can have any number of aliases. When searching for <code class="docutils literal notranslate"><span class="pre">key</span></code> these will searched too, you can’t easily search only for aliases.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>rose.aliases.add("flower")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If the above <code class="docutils literal notranslate"><span class="pre">rose</span></code> has a <code class="docutils literal notranslate"><span class="pre">key</span></code> <code class="docutils literal notranslate"><span class="pre">"Rose"</span></code>, it can now also be found by searching for <code class="docutils literal notranslate"><span class="pre">flower</span></code>. In-game
|
||||
you can assign new aliases to things with the <code class="docutils literal notranslate"><span class="pre">alias</span></code> command.</p>
|
||||
</section>
|
||||
<section id="search-by-location">
|
||||
<h3><span class="section-number">11.3.3. </span>Search by location<a class="headerlink" href="#search-by-location" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Only Objects (things inheriting from <code class="docutils literal notranslate"><span class="pre">evennia.DefaultObject</span></code>) has a location. The location is usually a room. The <code class="docutils literal notranslate"><span class="pre">Object.search</span></code> method will automatically limit it search by location, but it also works for the general search function. If we assume <code class="docutils literal notranslate"><span class="pre">room</span></code> is a particular Room instance,</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>chest = evennia.search_object("Treasure chest", location=room)
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="search-by-tags">
|
||||
<h3><span class="section-number">11.3.4. </span>Search by Tags<a class="headerlink" href="#search-by-tags" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Think of a <a class="reference internal" href="../../../Components/Tags.html"><span class="doc std std-doc">Tag</span></a> as the label the airport puts on your luggage when flying. Everyone going on the same plane gets a tag grouping them together so the airport can know what should go to which plane. Entities in Evennia can be grouped in the same way. Any number of tags can be attached
|
||||
to each object.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>rose.tags.add("flowers")
|
||||
rose.tags.add("thorny")
|
||||
daffodil.tags.add("flowers")
|
||||
tulip.tags.add("flowers")
|
||||
cactus.tags.add("flowers")
|
||||
cactus.tags.add("thorny")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can now find all flowers using the <code class="docutils literal notranslate"><span class="pre">search_tag</span></code> function:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>all_flowers = evennia.search_tag("flowers")
|
||||
roses_and_cactii = evennia.search_tag("thorny")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Tags can also have categories. By default this category is <code class="docutils literal notranslate"><span class="pre">None</span></code> which is also considered a category.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>silmarillion.tags.add("fantasy", category="books")
|
||||
ice_and_fire.tags.add("fantasy", category="books")
|
||||
mona_lisa_overdrive.tags.add("cyberpunk", category="books")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Note that if you specify the tag you <em>must</em> also include its category, otherwise that category
|
||||
will be <code class="docutils literal notranslate"><span class="pre">None</span></code> and find no matches.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>all_fantasy_books = evennia.search_tag("fantasy") # no matches!
|
||||
all_fantasy_books = evennia.search_tag("fantasy", category="books")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Only the second line above returns the two fantasy books. If we specify a category however,
|
||||
we can get all tagged entities within that category:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>all_books = evennia.search_tag(category="books")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This gets all three books.</p>
|
||||
</section>
|
||||
<section id="search-by-attribute">
|
||||
<h3><span class="section-number">11.3.5. </span>Search by Attribute<a class="headerlink" href="#search-by-attribute" title="Permalink to this headline">¶</a></h3>
|
||||
<p>We can also search by the <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attributes</span></a> associated with entities.</p>
|
||||
<p>For example, let’s give our rose thorns:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>rose.db.has_thorns = True
|
||||
wines.db.has_thorns = True
|
||||
daffodil.db.has_thorns = False
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now we can find things attribute and the value we want it to have:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>is_ouch = evennia.search_object_attribute("has_thorns", True)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This returns the rose and the wines.</p>
|
||||
<blockquote>
|
||||
<div><p>Searching by Attribute can be very practical. But if you plan to do a search very often, searching
|
||||
by-tag is generally faster.</p>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
<section id="search-by-typeclass">
|
||||
<h3><span class="section-number">11.3.6. </span>Search by Typeclass<a class="headerlink" href="#search-by-typeclass" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Sometimes it’s useful to find all objects of a specific Typeclass. All of Evennia’s search tools support this.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>all_roses = evennia.search_object(typeclass="typeclasses.flowers.Rose")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you have the <code class="docutils literal notranslate"><span class="pre">Rose</span></code> class already imported you can also pass it directly:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>all_roses = evennia.search_object(typeclass=Rose)
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can also search using the typeclass itself:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>all_roses = Rose.objects.all()
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This last way of searching is a simple form of a Django <em>query</em>. This is a way to express SQL queries using Python. See <a class="reference internal" href="Beginner-Tutorial-Django-queries.html"><span class="doc std std-doc">the next lesson</span></a>, where we’ll explore this way to searching in more detail.</p>
|
||||
</section>
|
||||
<section id="search-by-dbref">
|
||||
<h3><span class="section-number">11.3.7. </span>Search by dbref<a class="headerlink" href="#search-by-dbref" title="Permalink to this headline">¶</a></h3>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Will I run out of dbrefs?</p>
|
||||
<p>Since dbrefs are not reused, do you need to worry about your database ids ‘running out’ in the future? <a class="reference internal" href="../../../Components/Typeclasses.html#will-i-run-out-of-dbrefs"><span class="std std-doc">No, and here’s why</span></a>.</p>
|
||||
</aside>
|
||||
<p>The database id or <code class="docutils literal notranslate"><span class="pre">#dbref</span></code> is unique and never-reused within each database table. In search methods you can replace the search for <code class="docutils literal notranslate"><span class="pre">key</span></code> with the dbref to search for. This must be written as a string <code class="docutils literal notranslate"><span class="pre">#dbref</span></code>:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>the_answer = self.caller.search("#42")
|
||||
eightball = evennia.search_object("#8")
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Since <code class="docutils literal notranslate"><span class="pre">#dbref</span></code> is always unique, this search is always global.</p>
|
||||
<div class="admonition warning">
|
||||
<p class="admonition-title">Warning</p>
|
||||
<p>Relying on #dbrefs</p>
|
||||
<p>In legacy code bases you may be used to relying a lot on #dbrefs to find and track things. Looking something up by #dbref can be practical - if used occationally. It is however considered <strong>bad practice</strong> to <em>rely</em> on hard-coded #dbrefs in Evennia. Especially to expect end users to know them. It makes your code fragile and hard to maintain, while tying your code to the exact layout of the database. In 99% of use cases you should organize your code such that you pass the actual objects around and search by key/tags/attribute instead.</p>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="finding-objects-relative-each-other">
|
||||
<h2><span class="section-number">11.4. </span>Finding objects relative each other<a class="headerlink" href="#finding-objects-relative-each-other" title="Permalink to this headline">¶</a></h2>
|
||||
<p>It’s important to understand how objects relate to one another when searching.
|
||||
Let’s consider a <code class="docutils literal notranslate"><span class="pre">chest</span></code> with a <code class="docutils literal notranslate"><span class="pre">coin</span></code> inside it. The chests stand in a room <code class="docutils literal notranslate"><span class="pre">dungeon</span></code>. In the dungeon is also a <code class="docutils literal notranslate"><span class="pre">door</span></code>. This is an exit leading outside.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>┌───────────────────────┐
|
||||
│dungeon │
|
||||
│ ┌─────────┐ │
|
||||
│ │chest │ ┌────┐ │
|
||||
│ │ ┌────┐ │ │door│ │
|
||||
│ │ │coin│ │ └────┘ │
|
||||
│ │ └────┘ │ │
|
||||
│ │ │ │
|
||||
│ └─────────┘ │
|
||||
│ │
|
||||
└───────────────────────┘
|
||||
</pre></div>
|
||||
</div>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">coin.location</span></code> is <code class="docutils literal notranslate"><span class="pre">chest</span></code>.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">chest.location</span></code> is <code class="docutils literal notranslate"><span class="pre">dungeon</span></code>.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">door.location</span></code> is <code class="docutils literal notranslate"><span class="pre">dungeon</span></code>.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">room.location</span></code> is <code class="docutils literal notranslate"><span class="pre">None</span></code> since it’s not inside something else.</p></li>
|
||||
</ul>
|
||||
<p>One can use this to find what is inside what. For example, <code class="docutils literal notranslate"><span class="pre">coin.location.location</span></code> is the <code class="docutils literal notranslate"><span class="pre">room</span></code>.
|
||||
We can also find what is inside each object. This is a list of things.</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">room.contents</span></code> is <code class="docutils literal notranslate"><span class="pre">[chest,</span> <span class="pre">door]</span></code></p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">chest.contents</span></code> is <code class="docutils literal notranslate"><span class="pre">[coin]</span></code></p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">coin.contents</span></code> is <code class="docutils literal notranslate"><span class="pre">[]</span></code>, the empty list since there’s nothing ‘inside’ the coin.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">door.contents</span></code> is <code class="docutils literal notranslate"><span class="pre">[]</span></code> too.</p></li>
|
||||
</ul>
|
||||
<p>A convenient helper is <code class="docutils literal notranslate"><span class="pre">.contents_get</span></code> - this allows to restrict what is returned:</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">room.contents_get(exclude=chest)</span></code> - this returns everything in the room except the chest (maybe it’s hidden?)</p></li>
|
||||
</ul>
|
||||
<p>There is a special property for finding exits:</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">room.exits</span></code> is <code class="docutils literal notranslate"><span class="pre">[door]</span></code></p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">coin.exits</span></code> is <code class="docutils literal notranslate"><span class="pre">[]</span></code> (same for all the other objects)</p></li>
|
||||
</ul>
|
||||
<p>There is a property <code class="docutils literal notranslate"><span class="pre">.destination</span></code> which is only used by exits:</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">door.destination</span></code> is <code class="docutils literal notranslate"><span class="pre">outside</span></code> (or wherever the door leads)</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">room.destination</span></code> is <code class="docutils literal notranslate"><span class="pre">None</span></code> (same for all the other non-exit objects)</p></li>
|
||||
</ul>
|
||||
<p>You can also include this information in searches:</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">search_object</span>
|
||||
|
||||
<span class="c1"># we assume only one match of each </span>
|
||||
<span class="n">dungeons</span> <span class="o">=</span> <span class="n">search_object</span><span class="p">(</span><span class="s2">"dungeon"</span><span class="p">,</span> <span class="n">typeclass</span><span class="o">=</span><span class="s2">"typeclasses.rooms.Room"</span><span class="p">)</span>
|
||||
<span class="n">chests</span> <span class="o">=</span> <span class="n">search_object</span><span class="p">(</span><span class="s2">"chest"</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">dungeons</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
|
||||
<span class="c1"># find if there are any skulls in the chest </span>
|
||||
<span class="n">skulls</span> <span class="o">=</span> <span class="n">search_object</span><span class="p">(</span><span class="s2">"Skull"</span><span class="p">,</span> <span class="n">candidates</span><span class="o">=</span><span class="n">chests</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">contents</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>More advanced, nested queries like this can however often be made more efficient by using the hints in the next lesson.</p>
|
||||
</section>
|
||||
<section id="summary">
|
||||
<h2><span class="section-number">11.5. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Knowing how to find things is important and the tools from this section will serve you well. These tools will cover most of your needs …</p>
|
||||
<p>… but not always. In the next lesson we will dive further into more complex searching when we look at Django queries and querysets in earnest.</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="Beginner-Tutorial-Django-queries.html" title="12. Advanced searching - Django Database queries"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Creating-Things.html" title="10. Creating things"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">11. </span>Searching for things</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
|
||||
<!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>2. The Tutorial World — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="3. Intro to using Python with Evennia" href="Beginner-Tutorial-Python-basic-introduction.html" />
|
||||
<link rel="prev" title="1. Using Commands and Building Stuff" href="Beginner-Tutorial-Building-Quickstart.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Python-basic-introduction.html" title="3. Intro to using Python with Evennia"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Building-Quickstart.html" title="1. Using Commands and Building Stuff"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" accesskey="U">Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">2. </span>The Tutorial World</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">2. The Tutorial World</a><ul>
|
||||
<li><a class="reference internal" href="#gameplay">2.1. Gameplay</a><ul>
|
||||
<li><a class="reference internal" href="#gameplay-hints">2.1.1. Gameplay hints</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#once-you-are-done-or-had-enough">2.2. Once you are done (or had enough)</a></li>
|
||||
<li><a class="reference internal" href="#uninstall-the-tutorial-world">2.3. Uninstall the tutorial world</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Building-Quickstart.html"
|
||||
title="previous chapter"><span class="section-number">1. </span>Using Commands and Building Stuff</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Python-basic-introduction.html"
|
||||
title="next chapter"><span class="section-number">3. </span>Intro to using Python with Evennia</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Tutorial-World.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="the-tutorial-world">
|
||||
<h1><span class="section-number">2. </span>The Tutorial World<a class="headerlink" href="#the-tutorial-world" title="Permalink to this headline">¶</a></h1>
|
||||
<p>The <em>Tutorial World</em> is a small, functioning MUD-style game world shipped with Evennia.
|
||||
It’s a small showcase of what is possible. It may also be useful for those who have an easier
|
||||
time learning by deconstructing existing code.</p>
|
||||
<p>To install the tutorial world, stand in the Limbo room and input:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>batchcommand tutorial_world.build
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This command runs the build script in <a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/contrib/tutorials/tutorial_world/build.ev">evennia/contrib/tutorials/tutorial_world/build.ev</a>.
|
||||
Basically, this script is a list of build-commands executed in sequence by the <code class="docutils literal notranslate"><span class="pre">batchcommand</span></code> command. Wait for the building to complete and don’t run it twice.</p>
|
||||
<blockquote>
|
||||
<div><p>After having run the batchcommand, the <code class="docutils literal notranslate"><span class="pre">intro</span></code> command becomes available in Limbo. Try it out for in-game help using an example of <a class="reference internal" href="../../../Components/EvMenu.html"><span class="doc std std-doc">EvMenu</span></a>, Evennia’s in-built
|
||||
menu generation system!</p>
|
||||
</div></blockquote>
|
||||
<p>The tutorial world consists of a single-player quest and has some 20 rooms to explore as you seek to discover the whereabouts of a mythical weapon.</p>
|
||||
<p>A new exit should have appeared named <em>Tutorial</em>. Enter the tutorial world by typing <code class="docutils literal notranslate"><span class="pre">tutorial</span></code>.</p>
|
||||
<p>You will automatically <code class="docutils literal notranslate"><span class="pre">quell</span></code> when you enter (and <code class="docutils literal notranslate"><span class="pre">unquell</span></code> when you leave), so you can play the way it was intended. Whether you are triumphant or use the <code class="docutils literal notranslate"><span class="pre">give</span> <span class="pre">up</span></code> command, you will eventually end up back in Limbo.</p>
|
||||
<div class="admonition important">
|
||||
<p class="admonition-title">Important</p>
|
||||
<p>Only LOSERS and QUITTERS use the <code class="docutils literal notranslate"><span class="pre">give</span> <span class="pre">up</span></code> command.</p>
|
||||
</div>
|
||||
<section id="gameplay">
|
||||
<h2><span class="section-number">2.1. </span>Gameplay<a class="headerlink" href="#gameplay" title="Permalink to this headline">¶</a></h2>
|
||||
<p><img alt="the castle off the moor" src="https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/22916c25-6299-453d-a221-446ec839f567/da2pmzu-46d63c6d-9cdc-41dd-87d6-1106db5a5e1a.jpg/v1/fill/w_600,h_849,q_75,strp/the_castle_off_the_moor_by_griatch_art_da2pmzu-fullview.jpg?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOiIsImlzcyI6InVybjphcHA6Iiwib2JqIjpbW3siaGVpZ2h0IjoiPD04NDkiLCJwYXRoIjoiXC9mXC8yMjkxNmMyNS02Mjk5LTQ1M2QtYTIyMS00NDZlYzgzOWY1NjdcL2RhMnBtenUtNDZkNjNjNmQtOWNkYy00MWRkLTg3ZDYtMTEwNmRiNWE1ZTFhLmpwZyIsIndpZHRoIjoiPD02MDAifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6aW1hZ2Uub3BlcmF0aW9ucyJdfQ.omuS3D1RmFiZCy9OSXiIita-HxVGrBok3_7asq0rflw" />
|
||||
(image by Griatch)</p>
|
||||
<p><em>To get into the mood of our miniature quest, imagine you are an adventurer out to find fame and fortune. You have heard rumours of an old castle ruin by the coast. In its depths, a warrior princess was buried together with her powerful magical weapon — a valuable prize, if true. Of course, this is a chance for adventure that you simply cannot turn down!</em></p>
|
||||
<p><em>You reach the ocean in the midst of a raging thunderstorm. With wind and rain screaming in your face, you stand where the moor meets the sea along a high, rocky coast…</em></p>
|
||||
<hr class="docutils" />
|
||||
<section id="gameplay-hints">
|
||||
<h3><span class="section-number">2.1.1. </span>Gameplay hints<a class="headerlink" href="#gameplay-hints" title="Permalink to this headline">¶</a></h3>
|
||||
<ul class="simple">
|
||||
<li><p>Use the command <code class="docutils literal notranslate"><span class="pre">tutorial</span></code> to get code insight behind the scenes of every room.</p></li>
|
||||
<li><p>Look at everything. While a demo, the Tutorial World is not necessarily trivial to solve - it depends on your experience with text-based adventure games. Just remember that everything can be solved or bypassed.</p></li>
|
||||
<li><p>Some objects are interactive in more than one way. Use the normal <code class="docutils literal notranslate"><span class="pre">help</span></code> command to get a feel for which commands are available at any given time.</p></li>
|
||||
<li><p>In order to fight, you need to first find some type of weapon.</p>
|
||||
<ul>
|
||||
<li><p><em>slash</em> is a normal attack</p></li>
|
||||
<li><p><em>stab</em> launches an attack that makes more damage but has a lower chance to hit.</p></li>
|
||||
<li><p><em>defend</em> will lower the chance to taking damage on your enemy’s next attack.</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p>Some things <em>cannot</em> be hurt by mundane weapons. In that case it’s OK to run away. Expect to be chased …</p></li>
|
||||
<li><p>Being defeated is a part of the experience. You can’t actually die, but getting knocked out
|
||||
means being left in the dark …</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
<section id="once-you-are-done-or-had-enough">
|
||||
<h2><span class="section-number">2.2. </span>Once you are done (or had enough)<a class="headerlink" href="#once-you-are-done-or-had-enough" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Afterwards you’ll either have conquered the old ruin and returned in glory and triumph … or
|
||||
you returned limping and whimpering from the challenge by using the <code class="docutils literal notranslate"><span class="pre">give</span> <span class="pre">up</span></code> command.
|
||||
Either way you should now be back in Limbo, able to reflect on the experience.</p>
|
||||
<p>Some features exemplified by the tutorial world:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Rooms with custom ability to show details (like looking at the wall in the dark room)</p></li>
|
||||
<li><p>Hidden or impassable exits until you fulfilled some criterion</p></li>
|
||||
<li><p>Objects with multiple custom interactions (like swords, the well, the obelisk …)</p></li>
|
||||
<li><p>Large-area rooms (that bridge is actually only one room!)</p></li>
|
||||
<li><p>Outdoor weather rooms with weather (the rain pummeling you)</p></li>
|
||||
<li><p>Dark room, needing light source to reveal itself (the burning splinter even burns out after a while)</p></li>
|
||||
<li><p>Puzzle object (the wines in the dark cell; hope you didn’t get stuck!)</p></li>
|
||||
<li><p>Multi-room puzzle (the obelisk and the crypt)</p></li>
|
||||
<li><p>Aggressive mobile with roam, pursue and battle state-engine AI (quite deadly until you find the right weapon)</p></li>
|
||||
<li><p>Weapons, also used by mobs (most are admittedly not that useful against the big baddie)</p></li>
|
||||
<li><p>Simple combat system with attack/defend commands (teleporting on-defeat)</p></li>
|
||||
<li><p>Object spawning (the weapons in the barrel and the final weapoon is actually randomized)</p></li>
|
||||
<li><p>Teleporter trap rooms (if you fail the obelisk puzzle)</p></li>
|
||||
</ul>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Extra Credit</p>
|
||||
<p>If you are already familiar with Python and want an early taste, it can be constructive to dig a little deeper into the Tutorial-world to learn how it achieves what it does. The code is heavily documented. You can find all the code in <a class="reference internal" href="../../../api/evennia.contrib.tutorials.tutorial_world.html"><span class="doc std std-doc">evennia/contrib/tutorials/tutorial_world</span></a>.
|
||||
The build-script is <a class="reference external" href="https://github.com/evennia/evennia/blob/main/evennia/contrib/tutorials/tutorial_world/build.ev">here</a>.</p>
|
||||
<p>When reading the tutorial-world code, note that the Tutorial World was designed to install easily and to not permanently modify the rest of the game. It therefore makes sure to only use temporary solutions and to clean up after itself. This is not something you will often need to worry about when making your own game.</p>
|
||||
</aside>
|
||||
<p>Quite a lot of stuff crammed in such a small area!</p>
|
||||
</section>
|
||||
<section id="uninstall-the-tutorial-world">
|
||||
<h2><span class="section-number">2.3. </span>Uninstall the tutorial world<a class="headerlink" href="#uninstall-the-tutorial-world" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Once you are done playing with the tutorial world, let’s uninstall it. Uninstalling the tutorial world basically means deleting all the rooms and objects it consists of. Make sure you are back in Limbo, then</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> find tut#01
|
||||
find tut#16
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This should locate the first and last rooms created by <code class="docutils literal notranslate"><span class="pre">build.ev</span></code> - <em>Intro</em> and <em>Outro</em>. If you installed normally, everything created between these two numbers should be part of the tutorial. Note their #dbref numbers, for example 5 and 80. Next we just delete all objects in that range:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> del 5-80
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You will see some errors since some objects are auto-deleted and so cannot be found when the delete mechanism gets to them. That’s fine. You should have removed the tutorial completely once the command finishes.</p>
|
||||
<p>Even if the game-style of the Tutorial-world was not similar to the one you are interested in, it should hopefully have given you a little taste of some of the possibilities of Evennia. Now we’ll move on with how to access this power through code.</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="Beginner-Tutorial-Python-basic-introduction.html" title="3. Intro to using Python with Evennia"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Building-Quickstart.html" title="1. Using Commands and Building Stuff"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Overview.html" >Part 1: What We Have</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">2. </span>The Tutorial World</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,366 @@
|
|||
|
||||
<!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>2. On Planning a Game — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="3. Planning our tutorial game" href="Beginner-Tutorial-Planning-The-Tutorial-Game.html" />
|
||||
<link rel="prev" title="1. Where do I begin?" href="Beginner-Tutorial-Planning-Where-Do-I-Begin.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Planning-The-Tutorial-Game.html" title="3. Planning our tutorial game"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Planning-Where-Do-I-Begin.html" title="1. Where do I begin?"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part2-Overview.html" accesskey="U">Part 2: What We Want</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">2. </span>On Planning a Game</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">2. On Planning a Game</a><ul>
|
||||
<li><a class="reference internal" href="#the-steps">2.1. The steps</a></li>
|
||||
<li><a class="reference internal" href="#planning">2.2. Planning</a><ul>
|
||||
<li><a class="reference internal" href="#administration">2.2.1. Administration</a></li>
|
||||
<li><a class="reference internal" href="#building">2.2.2. Building</a></li>
|
||||
<li><a class="reference internal" href="#systems">2.2.3. Systems</a></li>
|
||||
<li><a class="reference internal" href="#rooms">2.2.4. Rooms</a></li>
|
||||
<li><a class="reference internal" href="#objects-items">2.2.5. Objects / items</a></li>
|
||||
<li><a class="reference internal" href="#characters">2.2.6. Characters</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#coding-and-tech-demo">2.3. Coding and Tech demo</a></li>
|
||||
<li><a class="reference internal" href="#world-building">2.4. World Building</a></li>
|
||||
<li><a class="reference internal" href="#alpha-release">2.5. Alpha Release</a></li>
|
||||
<li><a class="reference internal" href="#beta-release-perpetual-beta">2.6. Beta Release/Perpetual Beta</a></li>
|
||||
<li><a class="reference internal" href="#congratulate-yourself">2.7. Congratulate yourself!</a></li>
|
||||
<li><a class="reference internal" href="#planning-our-tutorial-game">2.8. Planning our tutorial game</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Planning-Where-Do-I-Begin.html"
|
||||
title="previous chapter"><span class="section-number">1. </span>Where do I begin?</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Planning-The-Tutorial-Game.html"
|
||||
title="next chapter"><span class="section-number">3. </span>Planning our tutorial game</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part2/Beginner-Tutorial-Game-Planning.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="on-planning-a-game">
|
||||
<h1><span class="section-number">2. </span>On Planning a Game<a class="headerlink" href="#on-planning-a-game" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Last lesson we asked ourselves some questions about our motivation. In this one we’ll present
|
||||
some more technical questions to consider. In the next lesson we’ll answer them for the sake of
|
||||
our tutorial game.</p>
|
||||
<p>Note that the suggestions on this page are just that - suggestions. Also, they are primarily aimed at a lone
|
||||
hobby designer or a small team developing a game in their free time.</p>
|
||||
<div class="admonition important">
|
||||
<p class="admonition-title">Important</p>
|
||||
<p>Your first all overshadowing goal is to beat the odds and get <strong>something</strong> out the door!
|
||||
Even if it’s a scaled-down version of your dream game, lacking many “must-have” features!</p>
|
||||
</div>
|
||||
<p>Remember: <em>99.99999% of all great game ideas never lead to a game</em>. Especially not to an online
|
||||
game that people can actually play and enjoy. It’s better to get your game out there and expand on it
|
||||
later than to code in isolation until you burn out, lose interest or your hard drive crashes.</p>
|
||||
<ul class="simple">
|
||||
<li><p>Keep the scope of your initial release down. Way down.</p></li>
|
||||
<li><p>Start small, with an eye towards expansions later, after first release.</p></li>
|
||||
<li><p>If the suggestions here seems boring or a chore to you, do it your way instead. Everyone’s different.</p></li>
|
||||
<li><p>Keep having <em>fun</em>. You must keep your motivation up, whichever way works for <em>you</em>.</p></li>
|
||||
</ul>
|
||||
<section id="the-steps">
|
||||
<h2><span class="section-number">2.1. </span>The steps<a class="headerlink" href="#the-steps" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Here are the rough steps towards your goal.</p>
|
||||
<ol class="simple">
|
||||
<li><p>Planning</p></li>
|
||||
<li><p>Coding + Gradually building a tech-demo</p></li>
|
||||
<li><p>Building the actual game world</p></li>
|
||||
<li><p>Release</p></li>
|
||||
<li><p>Celebrate</p></li>
|
||||
</ol>
|
||||
</section>
|
||||
<section id="planning">
|
||||
<h2><span class="section-number">2.2. </span>Planning<a class="headerlink" href="#planning" title="Permalink to this headline">¶</a></h2>
|
||||
<p>You need to have at least a rough idea about what you want to create. Some like a lot of planning, others
|
||||
do it more seat-of-the-pants style. Regardless, while <em>some</em> planning is always good to do, it’s common
|
||||
to have your plans change on you as you create your code prototypes. So don’t get <em>too</em> bogged down in
|
||||
the details out of the gate.</p>
|
||||
<p>Many prospective game developers are very good at <em>parts</em> of this process, namely in defining what their
|
||||
world is “about”: The theme, the world concept, cool monsters and so on. Such things are very important. But
|
||||
unfortunately, they are not enough to make your game. You need to figure out how to accomplish your ideas in
|
||||
Evennia.</p>
|
||||
<p>Below are some questions to get you going. In the next lesson we will try to answer them for our particular
|
||||
tutorial game. There are of course many more questions you could be asking yourself.</p>
|
||||
<section id="administration">
|
||||
<h3><span class="section-number">2.2.1. </span>Administration<a class="headerlink" href="#administration" title="Permalink to this headline">¶</a></h3>
|
||||
<ul class="simple">
|
||||
<li><p>Should your game rules be enforced by coded systems or by human game masters?</p></li>
|
||||
<li><p>What is the staff hierarchy in your game? Is vanilla Evennia roles enough or do you need something else?</p></li>
|
||||
<li><p>Should players be able to post out-of-characters on channels and via other means like bulletin-boards?</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="building">
|
||||
<h3><span class="section-number">2.2.2. </span>Building<a class="headerlink" href="#building" title="Permalink to this headline">¶</a></h3>
|
||||
<ul class="simple">
|
||||
<li><p>How will the world be built? Traditionally (from in-game with build-commands) or externally (by batchcmds/code
|
||||
or directly with custom code)?</p></li>
|
||||
<li><p>Can only privileged Builders create things or should regular players also have limited build-capability?</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="systems">
|
||||
<h3><span class="section-number">2.2.3. </span>Systems<a class="headerlink" href="#systems" title="Permalink to this headline">¶</a></h3>
|
||||
<ul class="simple">
|
||||
<li><p>Do you base your game off an existing RPG system or make up your own?</p></li>
|
||||
<li><p>What are the game mechanics? How do you decide if an action succeeds or fails?</p></li>
|
||||
<li><p>Does the flow of time matter in your game - does night and day change? What about seasons?</p></li>
|
||||
<li><p>Do you want changing, global weather or should weather just be set manually in roleplay?</p></li>
|
||||
<li><p>Do you want a coded world-economy or just a simple barter system? Or no formal economy at all?</p></li>
|
||||
<li><p>Do you have concepts like reputation and influence?</p></li>
|
||||
<li><p>Will your characters be known by their name or only by their physical appearance?</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="rooms">
|
||||
<h3><span class="section-number">2.2.4. </span>Rooms<a class="headerlink" href="#rooms" title="Permalink to this headline">¶</a></h3>
|
||||
<ul class="simple">
|
||||
<li><p>Is a simple room description enough or should the description be able to change (such as with time, by
|
||||
light conditions, weather or season)?</p></li>
|
||||
<li><p>Should the room have different statuses? Can it have smells, sounds? Can it be affected by
|
||||
dramatic weather, fire or magical effects? If so, how would this affect things in the room? Or are
|
||||
these things something admins/game masters should handle manually?</p></li>
|
||||
<li><p>Can objects be hidden in the room? Can a person hide in the room? How does the room display this?</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="objects-items">
|
||||
<h3><span class="section-number">2.2.5. </span>Objects / items<a class="headerlink" href="#objects-items" title="Permalink to this headline">¶</a></h3>
|
||||
<ul class="simple">
|
||||
<li><p>How numerous are your objects? Do you want large loot-lists or are objects just role playing props
|
||||
created on demand?</p></li>
|
||||
<li><p>If you use money, is each coin a separate object or do you just store a bank account value?</p></li>
|
||||
<li><p>Do multiple similar objects form stacks and how are those stacks handled in that case?</p></li>
|
||||
<li><p>Does an object have weight or volume (so you cannot carry an infinite amount of them)?</p></li>
|
||||
<li><p>Can objects be broken? Can they be repaired?</p></li>
|
||||
<li><p>Can you fight with a chair or a flower or must you use a specific ‘weapon’ kind of thing?</p></li>
|
||||
<li><p>Will characters be able to craft new objects?</p></li>
|
||||
<li><p>Should mobs/NPCs have some sort of AI?</p></li>
|
||||
<li><p>Are NPCs and mobs different entities? How do they differ?</p></li>
|
||||
<li><p>Should there be NPCs giving quests? If so, how do you track Quest status?</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="characters">
|
||||
<h3><span class="section-number">2.2.6. </span>Characters<a class="headerlink" href="#characters" title="Permalink to this headline">¶</a></h3>
|
||||
<ul class="simple">
|
||||
<li><p>Can players have more than one Character active at a time or are they allowed to multi-play?</p></li>
|
||||
<li><p>How does the character-generation work? Walk from room-to-room? A menu?</p></li>
|
||||
<li><p>How do you implement different “classes” or “races”? Are they separate types of objects or do you
|
||||
simply load different stats on a basic object depending on what the Player wants?</p></li>
|
||||
<li><p>If a Character can hide in a room, what skill will decide if they are detected?</p></li>
|
||||
<li><p>What does the skill tree look like? Can a Character gain experience to improve? By killing
|
||||
enemies? Solving quests? By roleplaying?</p></li>
|
||||
<li><p>May player-characters attack each other (PvP)?</p></li>
|
||||
<li><p>What are the penalties of defeat? Permanent death? Quick respawn? Time in prison?</p></li>
|
||||
</ul>
|
||||
<p>A MUD’s a lot more involved than you would think and these things hang together in a complex web. It
|
||||
can easily become overwhelming and it’s tempting to want <em>all</em> functionality right out of the door.
|
||||
Try to identify the basic things that “make” your game and focus <em>only</em> on them for your first
|
||||
release. Make a list. Keep future expansions in mind but limit yourself.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="coding-and-tech-demo">
|
||||
<h2><span class="section-number">2.3. </span>Coding and Tech demo<a class="headerlink" href="#coding-and-tech-demo" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This is the actual work of creating the “game” part of your game. As you code and test systems you should
|
||||
build a little “tech demo” along the way.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Tech demo</p>
|
||||
<p>With “tech demo” we mean a small example of your code in-action: A room with a mob, a way to jump into and test character-creation etc. The tech demo need not be pretty, it’s there to test functionality. It’s not the beginning of your game world (unless you find that to be more fun).</p>
|
||||
</aside>
|
||||
<p>Try to avoid going wild with building a huge game world before you have a tech-demo showing off all parts
|
||||
you expect to have in the first version of your game. Otherwise you run the risk of having to redo it all
|
||||
again.</p>
|
||||
<p>Evennia tries hard to make the coding easier for you, but there is no way around the fact that if you want
|
||||
anything but a basic chat room you <em>will</em> have to bite the bullet and code your game (or find a coder willing
|
||||
to do it for you).</p>
|
||||
<blockquote>
|
||||
<div><p>Even if you won’t code anything yourself, as a designer you need to at least understand the basic
|
||||
paradigms and components of Evennia. It’s recommended you look over the rest of this Beginner Tutorial to learn
|
||||
what tools you have available.</p>
|
||||
</div></blockquote>
|
||||
<p>During Coding you look back at the things you wanted during the <strong>Planning</strong> phase and try to
|
||||
implement them. Don’t be shy to update your plans if you find things easier/harder than you thought.
|
||||
The earlier you revise problems, the easier they will be to fix.</p>
|
||||
<p>A good idea is to host your code online using <em>version control</em>. <a class="reference external" href="http://Github.com">Github.com</a> offers free Private repos
|
||||
these days if you don’t want the world to learn your secrets. Not only version control
|
||||
make it easy for your team to collaborate, it also means
|
||||
your work is backed up at all times. The page on <a class="reference internal" href="../../../Coding/Version-Control.html"><span class="doc std std-doc">Version Control</span></a>
|
||||
will help you to setting up a sane developer environment with proper version control.</p>
|
||||
</section>
|
||||
<section id="world-building">
|
||||
<h2><span class="section-number">2.4. </span>World Building<a class="headerlink" href="#world-building" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Up until this point we’ve only had a few tech-demo objects in the database. This step is the act of
|
||||
populating the database with a larger, thematic world. Too many would-be developers jump to this
|
||||
stage too soon (skipping the <strong>Coding</strong> or even <strong>Planning</strong> stages). What if the rooms you build
|
||||
now doesn’t include all the nice weather messages the code grows to support? Or the way you store
|
||||
data changes under the hood? Your building work would at best require some rework and at worst you
|
||||
would have to redo the whole thing. You could be in for a <em>lot</em> of unnecessary work if you build stuff
|
||||
en masse without having the underlying code systems in some reasonable shape first.</p>
|
||||
<p>So before starting to build, the “game” bit (<strong>Coding</strong> + <strong>Testing</strong>) should be more or less
|
||||
<strong>complete</strong>, <em>at least to the level of your initial release</em>.</p>
|
||||
<p>Make sure it is clear to yourself and your eventual builders just which parts of the world you want
|
||||
for your initial release. Establish for everyone which style, quality and level of detail you expect.</p>
|
||||
<p>Your goal should <em>not</em> be to complete your entire world in one go. You want just enough to make the
|
||||
game’s “feel” come across. You want a minimal but functioning world where the intended game play can
|
||||
be tested and roughly balanced. You can always add new areas later.</p>
|
||||
<p>During building you get free and extensive testing of whatever custom build commands and systems you
|
||||
have made at this point. If Builders and coders are different people you also
|
||||
get a chance to hear if some things are hard to understand or non-intuitive. Make sure to respond
|
||||
to this feedback.</p>
|
||||
</section>
|
||||
<section id="alpha-release">
|
||||
<h2><span class="section-number">2.5. </span>Alpha Release<a class="headerlink" href="#alpha-release" title="Permalink to this headline">¶</a></h2>
|
||||
<p>As mentioned, don’t hold onto your world more than necessary. <em>Get it out there</em> with a huge <em>Alpha</em>
|
||||
flag and let people try it!</p>
|
||||
<p>Call upon your alpha-players to try everything - they <em>will</em> find ways to break your game in ways that
|
||||
you never could have imagined. In Alpha you might be best off to
|
||||
focus on inviting friends and maybe other MUD developers, people who you can pester to give proper
|
||||
feedback and bug reports (there <em>will</em> be bugs, there is no way around it).</p>
|
||||
<p>Follow the quick instructions for <a class="reference internal" href="../../../Setup/Online-Setup.html"><span class="doc std std-doc">Online Setup</span></a> to make your
|
||||
game visible online.</p>
|
||||
<p>If you hadn’t already, make sure to put up your game on the
|
||||
<a class="reference external" href="http://games.evennia.com/">Evennia game index</a> so people know it’s in the works (actually, even
|
||||
pre-alpha games are allowed in the index so don’t be shy)!</p>
|
||||
</section>
|
||||
<section id="beta-release-perpetual-beta">
|
||||
<h2><span class="section-number">2.6. </span>Beta Release/Perpetual Beta<a class="headerlink" href="#beta-release-perpetual-beta" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Once things stabilize in Alpha you can move to <em>Beta</em> and let more people in. Many MUDs are in
|
||||
<a class="reference external" href="https://en.wikipedia.org/wiki/Perpetual_beta">perpetual beta</a>, meaning they are never considered
|
||||
“finished”, but just repeat the cycle of Planning, Coding, Testing and Building over and over as new
|
||||
features get implemented or Players come with suggestions. As the game designer it is now up to you
|
||||
to gradually perfect your vision.</p>
|
||||
</section>
|
||||
<section id="congratulate-yourself">
|
||||
<h2><span class="section-number">2.7. </span>Congratulate yourself!<a class="headerlink" href="#congratulate-yourself" title="Permalink to this headline">¶</a></h2>
|
||||
<p>You are worthy of a celebration since at this point you have joined the small, exclusive crowd who
|
||||
have made their dream game a reality!</p>
|
||||
</section>
|
||||
<section id="planning-our-tutorial-game">
|
||||
<h2><span class="section-number">2.8. </span>Planning our tutorial game<a class="headerlink" href="#planning-our-tutorial-game" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In the next lesson we’ll make use of these general points and try to plan out our tutorial game.</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="Beginner-Tutorial-Planning-The-Tutorial-Game.html" title="3. Planning our tutorial game"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Planning-Where-Do-I-Begin.html" title="1. Where do I begin?"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part2-Overview.html" >Part 2: What We Want</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">2. </span>On Planning a Game</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
|
||||
<!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>Part 2: What We Want — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="1. Where do I begin?" href="Beginner-Tutorial-Planning-Where-Do-I-Begin.html" />
|
||||
<link rel="prev" title="13. Building a chair you can sit on" href="../Part1/Beginner-Tutorial-Making-A-Sittable-Object.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Planning-Where-Do-I-Begin.html" title="1. Where do I begin?"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Part1/Beginner-Tutorial-Making-A-Sittable-Object.html" title="13. Building a chair you can sit on"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" accesskey="U">Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Part 2: What We Want</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Part 2: What We Want</a><ul>
|
||||
<li><a class="reference internal" href="#lessons">Lessons</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="../Part1/Beginner-Tutorial-Making-A-Sittable-Object.html"
|
||||
title="previous chapter"><span class="section-number">13. </span>Building a chair you can sit on</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Planning-Where-Do-I-Begin.html"
|
||||
title="next chapter"><span class="section-number">1. </span>Where do I begin?</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part2/Beginner-Tutorial-Part2-Overview.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="part-2-what-we-want">
|
||||
<h1>Part 2: What We Want<a class="headerlink" href="#part-2-what-we-want" title="Permalink to this headline">¶</a></h1>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Beginner Tutorial Parts</p>
|
||||
<ul class="simple">
|
||||
<li><p><a class="reference internal" href="../Beginner-Tutorial-Overview.html"><span class="doc std std-doc">Introduction</span></a>
|
||||
<br>Getting set up.</p></li>
|
||||
<li><p>Part 1: <a class="reference internal" href="../Part1/Beginner-Tutorial-Part1-Overview.html"><span class="doc std std-doc">What We Have</span></a>
|
||||
<br>A tour of Evennia and how to use the tools, including an introduction to Python.</p></li>
|
||||
<li><p><em>Part 2: <a class="reference internal" href="#"><span class="doc std std-doc">What We Want</span></a></em>
|
||||
<br>Planning our tutorial game and what to consider when planning your own.</p></li>
|
||||
<li><p>Part 3: <a class="reference internal" href="../Part3/Beginner-Tutorial-Part3-Overview.html"><span class="doc std std-doc">How We Get There</span></a>
|
||||
<br>Getting down to the meat of extending Evennia to make your game.</p></li>
|
||||
<li><p>Part 4: <a class="reference internal" href="../Part4/Beginner-Tutorial-Part4-Overview.html"><span class="doc std std-doc">Using What We Created</span></a>
|
||||
<br>Building a tech-demo and world content to go with our code.</p></li>
|
||||
<li><p>Part 5: <a class="reference internal" href="../Part5/Beginner-Tutorial-Part5-Overview.html"><span class="doc std std-doc">Showing the World</span></a>
|
||||
<br>Taking our new game online and letting players try it out.</p></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<p>In Part Two of the Evennia Beginner Tutorial, we’ll take a step back to plan the type of tutorial game that you will create. This part is more ‘theoretical’ in that we won’t do any hands-on programming.</p>
|
||||
<p>In the process, we’ll address the common questions of “where to start” and “what to think about” when creating a multiplayer online text game.</p>
|
||||
<section id="lessons">
|
||||
<h2>Lessons<a class="headerlink" href="#lessons" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="toctree-wrapper compound">
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Planning-Where-Do-I-Begin.html">1. Where do I begin?</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Planning-Where-Do-I-Begin.html#what-is-your-motivation-for-doing-this">1.1. What is your motivation for doing this?</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Planning-Where-Do-I-Begin.html#what-are-your-skills">1.2. What are your skills?</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Planning-Where-Do-I-Begin.html#so-where-do-i-begin-then">1.3. So, where do I begin, then?</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Planning-Where-Do-I-Begin.html#conclusions">1.4. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Game-Planning.html">2. On Planning a Game</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Game-Planning.html#the-steps">2.1. The steps</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Game-Planning.html#planning">2.2. Planning</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Game-Planning.html#coding-and-tech-demo">2.3. Coding and Tech demo</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Game-Planning.html#world-building">2.4. World Building</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Game-Planning.html#alpha-release">2.5. Alpha Release</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Game-Planning.html#beta-release-perpetual-beta">2.6. Beta Release/Perpetual Beta</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Game-Planning.html#congratulate-yourself">2.7. Congratulate yourself!</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Game-Planning.html#planning-our-tutorial-game">2.8. Planning our tutorial game</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Planning-The-Tutorial-Game.html">3. Planning our tutorial game</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Planning-The-Tutorial-Game.html#game-concept">3.1. Game concept</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Planning-The-Tutorial-Game.html#administration">3.2. Administration</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Planning-The-Tutorial-Game.html#building">3.3. Building</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Planning-The-Tutorial-Game.html#systems">3.4. Systems</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Planning-The-Tutorial-Game.html#rooms">3.5. Rooms</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Planning-The-Tutorial-Game.html#objects">3.6. Objects</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Planning-The-Tutorial-Game.html#characters">3.7. Characters</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Planning-The-Tutorial-Game.html#conclusions">3.8. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</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="Beginner-Tutorial-Planning-Where-Do-I-Begin.html" title="1. Where do I begin?"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Part1/Beginner-Tutorial-Making-A-Sittable-Object.html" title="13. Building a chair you can sit on"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Part 2: What We Want</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,597 @@
|
|||
|
||||
<!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>3. Planning our tutorial game — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="Part 3: How We Get There (Example Game)" href="../Part3/Beginner-Tutorial-Part3-Overview.html" />
|
||||
<link rel="prev" title="2. On Planning a Game" href="Beginner-Tutorial-Game-Planning.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Part3/Beginner-Tutorial-Part3-Overview.html" title="Part 3: How We Get There (Example Game)"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Game-Planning.html" title="2. On Planning a Game"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part2-Overview.html" accesskey="U">Part 2: What We Want</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">3. </span>Planning our tutorial game</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">3. Planning our tutorial game</a><ul>
|
||||
<li><a class="reference internal" href="#game-concept">3.1. Game concept</a></li>
|
||||
<li><a class="reference internal" href="#administration">3.2. Administration</a><ul>
|
||||
<li><a class="reference internal" href="#should-your-game-rules-be-enforced-by-coded-systems-by-human-game-masters">3.2.1. Should your game rules be enforced by coded systems by human game masters?</a></li>
|
||||
<li><a class="reference internal" href="#what-is-the-staff-hierarchy-in-your-game-is-vanilla-evennia-roles-enough-or-do-you-need-something-else">3.2.2. What is the staff hierarchy in your game? Is vanilla Evennia roles enough or do you need something else?</a></li>
|
||||
<li><a class="reference internal" href="#should-players-be-able-to-post-out-of-characters-on-channels-and-via-other-means-like-bulletin-boards">3.2.3. Should players be able to post out-of-characters on channels and via other means like bulletin-boards?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#building">3.3. Building</a><ul>
|
||||
<li><a class="reference internal" href="#how-will-the-world-be-built">3.3.1. How will the world be built?</a></li>
|
||||
<li><a class="reference internal" href="#can-only-privileged-builders-create-things-or-should-regular-players-also-have-limited-build-capability">3.3.2. Can only privileged Builders create things or should regular players also have limited build-capability?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#systems">3.4. Systems</a><ul>
|
||||
<li><a class="reference internal" href="#do-you-base-your-game-off-an-existing-rpg-system-or-make-up-your-own">3.4.1. Do you base your game off an existing RPG system or make up your own?</a></li>
|
||||
<li><a class="reference internal" href="#what-are-the-game-mechanics-how-do-you-decide-if-an-action-succeeds-or-fails">3.4.2. What are the game mechanics? How do you decide if an action succeeds or fails?</a></li>
|
||||
<li><a class="reference internal" href="#does-the-flow-of-time-matter-in-your-game-does-night-and-day-change-what-about-seasons">3.4.3. Does the flow of time matter in your game - does night and day change? What about seasons?</a></li>
|
||||
<li><a class="reference internal" href="#do-you-want-changing-global-weather-or-should-weather-just-be-set-manually-in-roleplay">3.4.4. Do you want changing, global weather or should weather just be set manually in roleplay?</a></li>
|
||||
<li><a class="reference internal" href="#do-you-want-a-coded-world-economy-or-just-a-simple-barter-system-or-no-formal-economy-at-all">3.4.5. Do you want a coded world-economy or just a simple barter system? Or no formal economy at all?</a></li>
|
||||
<li><a class="reference internal" href="#do-you-have-concepts-like-reputation-and-influence">3.4.6. Do you have concepts like reputation and influence?</a></li>
|
||||
<li><a class="reference internal" href="#will-your-characters-be-known-by-their-name-or-only-by-their-physical-appearance">3.4.7. Will your characters be known by their name or only by their physical appearance?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#rooms">3.5. Rooms</a><ul>
|
||||
<li><a class="reference internal" href="#is-a-simple-room-description-enough-or-should-the-description-be-able-to-change">3.5.1. Is a simple room description enough or should the description be able to change?</a></li>
|
||||
<li><a class="reference internal" href="#should-the-room-have-different-statuses">3.5.2. Should the room have different statuses?</a></li>
|
||||
<li><a class="reference internal" href="#can-objects-be-hidden-in-the-room-can-a-person-hide-in-the-room">3.5.3. Can objects be hidden in the room? Can a person hide in the room?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#objects">3.6. Objects</a><ul>
|
||||
<li><a class="reference internal" href="#how-numerous-are-your-objects-do-you-want-large-loot-lists-or-are-objects-just-role-playing-props">3.6.1. How numerous are your objects? Do you want large loot-lists or are objects just role playing props?</a></li>
|
||||
<li><a class="reference internal" href="#is-each-coin-a-separate-object-or-do-you-just-store-a-bank-account-value">3.6.2. Is each coin a separate object or do you just store a bank account value?</a></li>
|
||||
<li><a class="reference internal" href="#do-multiple-similar-objects-form-stack-and-how-are-those-stacks-handled-in-that-case">3.6.3. Do multiple similar objects form stack and how are those stacks handled in that case?</a></li>
|
||||
<li><a class="reference internal" href="#does-an-object-have-weight-or-volume-so-you-cannot-carry-an-infinite-amount-of-them">3.6.4. Does an object have weight or volume (so you cannot carry an infinite amount of them)?</a></li>
|
||||
<li><a class="reference internal" href="#can-objects-be-broken-can-they-be-repaired">3.6.5. Can objects be broken? Can they be repaired?</a></li>
|
||||
<li><a class="reference internal" href="#can-you-fight-with-a-chair-or-a-flower-or-must-you-use-a-special-weapon-kind-of-thing">3.6.6. Can you fight with a chair or a flower or must you use a special ‘weapon’ kind of thing?</a></li>
|
||||
<li><a class="reference internal" href="#will-characters-be-able-to-craft-new-objects">3.6.7. Will characters be able to craft new objects?</a></li>
|
||||
<li><a class="reference internal" href="#should-mobs-npcs-have-some-sort-of-ai">3.6.8. Should mobs/NPCs have some sort of AI?</a></li>
|
||||
<li><a class="reference internal" href="#are-npcs-and-mobs-different-entities-how-do-they-differ">3.6.9. Are NPCs and mobs different entities? How do they differ?</a></li>
|
||||
<li><a class="reference internal" href="#should-there-be-npcs-giving-quests-if-so-how-do-you-track-quest-status">3.6.10. _Should there be NPCs giving quests? If so, how do you track Quest status?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#characters">3.7. Characters</a><ul>
|
||||
<li><a class="reference internal" href="#can-players-have-more-than-one-character-active-at-a-time-or-are-they-allowed-to-multi-play">3.7.1. Can players have more than one Character active at a time or are they allowed to multi-play?</a></li>
|
||||
<li><a class="reference internal" href="#how-does-the-character-generation-work">3.7.2. How does the character-generation work?</a></li>
|
||||
<li><a class="reference internal" href="#how-do-you-implement-different-classes-or-races">3.7.3. How do you implement different “classes” or “races”?</a></li>
|
||||
<li><a class="reference internal" href="#if-a-character-can-hide-in-a-room-what-skill-will-decide-if-they-are-detected">3.7.4. If a Character can hide in a room, what skill will decide if they are detected?</a></li>
|
||||
<li><a class="reference internal" href="#what-does-the-skill-tree-look-like-can-a-character-gain-experience-to-improve-by-killing-enemies-solving-quests-by-roleplaying">3.7.5. What does the skill tree look like? Can a Character gain experience to improve? By killing enemies? Solving quests? By roleplaying?</a></li>
|
||||
<li><a class="reference internal" href="#may-player-characters-attack-each-other-pvp">3.7.6. May player-characters attack each other (PvP)?</a></li>
|
||||
<li><a class="reference internal" href="#what-are-the-penalties-of-defeat-permanent-death-quick-respawn-time-in-prison">3.7.7. What are the penalties of defeat? Permanent death? Quick respawn? Time in prison?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#conclusions">3.8. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Game-Planning.html"
|
||||
title="previous chapter"><span class="section-number">2. </span>On Planning a Game</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="../Part3/Beginner-Tutorial-Part3-Overview.html"
|
||||
title="next chapter">Part 3: How We Get There (Example Game)</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="planning-our-tutorial-game">
|
||||
<h1><span class="section-number">3. </span>Planning our tutorial game<a class="headerlink" href="#planning-our-tutorial-game" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Using the general plan from last lesson we’ll now establish what kind of game we want to create for this tutorial. We’ll call it … <em>EvAdventure</em>.
|
||||
Remembering that we need to keep the scope down, let’s establish some parameters.</p>
|
||||
<ul class="simple">
|
||||
<li><p>We want EvAdventure be a small game we can play ourselves for fun, but which could in principle be expanded to something more later.</p></li>
|
||||
<li><p>We want to have a clear game-loop, with clear goals.</p></li>
|
||||
<li><p>Let’s go with a fantasy theme, it’s well understood.</p></li>
|
||||
<li><p>We will use a small, existing tabletop RPG rule set (<a class="reference external" href="https://www.drivethrurpg.com/product/250888/Knave">Knave</a>, more info later)</p></li>
|
||||
<li><p>We want to be able to create and customize a character of our own.</p></li>
|
||||
<li><p>While not roleplay-focused, it should still be possible to socialize and to collaborate.</p></li>
|
||||
<li><p>We don’t want to have to rely on a Game master to resolve things, but will rely on code for skill resolution and combat.</p></li>
|
||||
<li><p>We want monsters to fight and NPCs we can talk to. So some sort of AI.</p></li>
|
||||
<li><p>We want some sort of quest system and merchants to buy stuff from.</p></li>
|
||||
</ul>
|
||||
<section id="game-concept">
|
||||
<h2><span class="section-number">3.1. </span>Game concept<a class="headerlink" href="#game-concept" title="Permalink to this headline">¶</a></h2>
|
||||
<p>With these points in mind, here’s a quick blurb for our game:</p>
|
||||
<p><em>Recently, the nearby village discovered that the old abandoned well contained a dark secret. The bottom of the well led to a previously undiscovered dungeon of ever shifting passages. No one knew why it was there or what its purpose was, but local rumors abound. The first adventurer that went down didn’t come back. The second … brought back a handful of glittering riches.</em></p>
|
||||
<p><em>Now the rush is on - there’s a dungeon to explore and coin to earn. Knaves, cutthroats, adventurers and maybe even a hero or two are coming from all over the realm to challenge whatever lurks at the bottom of that well.</em></p>
|
||||
<p><em>Local merchants and opportunists have seen a chance for profit. A camp of tents has sprung up around the old well, providing food and drink, equipment, entertainment and rumors for a price. It’s a festival to enjoy before paying the entrance fee for dropping down the well to find your fate among the shadows below …</em></p>
|
||||
<p>Our game will consist of two main game modes - above ground and below. The player starts above ground and is expected to do ‘expeditions’ into the dark. The design goal is for them to be forced back up again when their health, equipment and luck is about to run out.</p>
|
||||
<ul class="simple">
|
||||
<li><p>Above, in the “dungeon festival”, the player can restock and heal up, buy things and do a small set of quests. It’s the only place where the characters can sleep and fully heal. They also need to spend coin here to gain XP and levels. This is a place for players to socialize and RP. There is no combat above ground except for an optional spot for non-lethal PvP.</p></li>
|
||||
<li><p>Below is the mysterious dungeon. This is a procedurally generated set of rooms. Players can collaborate if they go down the well together, they will not be able to run into each other otherwise (so this works as an instance). Each room generally presents some challenge (normally a battle). Pushing deeper is more dangerous but can grant greater rewards. While the rooms could in theory go on forever, there should be a boss encounter once a player reaches deep enough.</p></li>
|
||||
</ul>
|
||||
<p>Here’s an overview of the topside camp for inspiration (quickly thrown together in the free version of <a class="reference external" href="https://inkarnate.com/">Inkarnate</a>). We’ll explore how to break this up into “rooms” (locations) when we get to creating the game world later.</p>
|
||||
<p><img alt="Last Step Camp" src="../../../_images/Dungeon_Merchant_Camp.jpg" /></p>
|
||||
<p>For the rest of this lesson we’ll answer and reason around the specific questions posed in the previous <a class="reference internal" href="Beginner-Tutorial-Game-Planning.html"><span class="doc std std-doc">Game Planning</span></a> lesson.</p>
|
||||
</section>
|
||||
<section id="administration">
|
||||
<h2><span class="section-number">3.2. </span>Administration<a class="headerlink" href="#administration" title="Permalink to this headline">¶</a></h2>
|
||||
<section id="should-your-game-rules-be-enforced-by-coded-systems-by-human-game-masters">
|
||||
<h3><span class="section-number">3.2.1. </span>Should your game rules be enforced by coded systems by human game masters?<a class="headerlink" href="#should-your-game-rules-be-enforced-by-coded-systems-by-human-game-masters" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Generally, the more work you expect human staffers/GMs to do, the less your code needs to work. To support GMs you’d need to design commands to support GM-specific actions and the type of game-mastering you want them to do. You may need to expand communication channels so you can easily talk to groups people in private and split off gaming groups from each other. RPG rules could be as simple
|
||||
as the GM sitting with the rule books and using a dice-roller for visibility.</p>
|
||||
<p>GM:ing is work-intensive however, and even the most skilled and enthusiastic GM can’t be awake all hours of the day to serve an international player base. The computer never needs sleep, so having the ability for players to “self-serve” their RP itch when no GMs are around is a good idea even for the most GM-heavy games.</p>
|
||||
<p>On the other side of the spectrum are games with no GMs at all; all gameplay are driven either by the computer or by the interactions between players. Such games still need an active staff, but nowhere as much active involvement. Allowing for solo-play with the computer also allows players to have fun when the number of active
|
||||
players is low.</p>
|
||||
<p><strong>EvAdventure Answer:</strong></p>
|
||||
<p>We want EvAdventure to work entirely without depending on human GMs. That said, there’d be nothing stopping a GM from stepping in and run an adventure for some players should they want to.</p>
|
||||
</section>
|
||||
<section id="what-is-the-staff-hierarchy-in-your-game-is-vanilla-evennia-roles-enough-or-do-you-need-something-else">
|
||||
<h3><span class="section-number">3.2.2. </span>What is the staff hierarchy in your game? Is vanilla Evennia roles enough or do you need something else?<a class="headerlink" href="#what-is-the-staff-hierarchy-in-your-game-is-vanilla-evennia-roles-enough-or-do-you-need-something-else" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The default hierarchy is</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">Player</span></code> - regular players</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">Player</span> <span class="pre">Helper</span></code> - can create/edit help entries</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">Builder</span></code> - can use build commands</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">Admin</span></code> - can kick and ban accounts</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">Developer</span></code> - full access, usually also trusted with server access</p></li>
|
||||
</ul>
|
||||
<p>There is also the <em>superuser</em>, the “owner” of the game you create when you first set up your database. This user goes outside the regular hierarchy and while powerful it’s not so suitable for testing since it bypasses all <a class="reference internal" href="../../../Components/Locks.html"><span class="doc std std-doc">locks</span></a> (using <code class="docutils literal notranslate"><span class="pre">quell</span></code> or a separate <code class="docutils literal notranslate"><span class="pre">Developer</span></code>-level account is recommended).</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>We are okay with keeping the default permission structure for our game.</p>
|
||||
</section>
|
||||
<section id="should-players-be-able-to-post-out-of-characters-on-channels-and-via-other-means-like-bulletin-boards">
|
||||
<h3><span class="section-number">3.2.3. </span>Should players be able to post out-of-characters on channels and via other means like bulletin-boards?<a class="headerlink" href="#should-players-be-able-to-post-out-of-characters-on-channels-and-via-other-means-like-bulletin-boards" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Evennia’s <em>Channels</em> are by default only available between <em>Accounts</em>. That is, for players to communicate with each
|
||||
other. By default, the <code class="docutils literal notranslate"><span class="pre">public</span></code> channel is created for general discourse.
|
||||
Channels are logged to a file and when you are coming back to the game you can view the history of a channel in case you missed something.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> public Hello world!
|
||||
[Public] MyName: Hello world!
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>But Channels can also be set up to work between Characters instead of Accounts. This would mean the channels would have an in-game meaning:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Members of a guild could be linked telepathically.</p></li>
|
||||
<li><p>Survivors of the apocalypse can communicate over walkie-talkies.</p></li>
|
||||
<li><p>Radio stations you can tune into or have to discover.</p></li>
|
||||
</ul>
|
||||
<p><em>Bulletin boards</em> are a sort of in-game forum where posts are made publicly or privately. Contrary to a channel, the messages are usually stored and are grouped into topics with replies. Evennia has no default bulletin-board system.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>In EvAdventure we will just use the default inter-account channels. We will also not be implementing any bulletin boards; instead the merchant NPCs will act as quest givers.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="building">
|
||||
<h2><span class="section-number">3.3. </span>Building<a class="headerlink" href="#building" title="Permalink to this headline">¶</a></h2>
|
||||
<section id="how-will-the-world-be-built">
|
||||
<h3><span class="section-number">3.3.1. </span>How will the world be built?<a class="headerlink" href="#how-will-the-world-be-built" title="Permalink to this headline">¶</a></h3>
|
||||
<p>There are two main ways to handle this:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Traditionally, from in-game with build-commands: This means builders creating content in their game client. This has the advantage of not requiring Python skills nor server access. This can often be a quite intuitive way to build since you are sort-of walking around in your creation as you build it. However, the developer (you) must make sure to provide build-commands that are flexible enough for builders to be able to create the content you want for your game.</p></li>
|
||||
<li><p>Externally (by batchcmds): Evennia’s <code class="docutils literal notranslate"><span class="pre">batchcmd</span></code> takes a text file with Evennia Commands and executes them in sequence. This allows the build process to be repeated and applied quickly to a new database during development.
|
||||
It also allows builders to use proper text-editing tools rather than writing things line-by-line in their clients. The drawback is that for their changes to go live they either need server access or they need to send their batchcode to the game administrator so they can apply the changes. Or use version control.</p></li>
|
||||
<li><p>Externally (with batchcode or custom code): This is the “professional game development” approach. This gives the builders maximum power by creating the content in Python using Evennia primitives. The <code class="docutils literal notranslate"><span class="pre">batchcode</span></code> processor
|
||||
allows Evennia to apply and re-apply build-scripts that are raw Python modules. Again, this would require the builder to have server access or to use version control to share their work with the rest of the development team.</p></li>
|
||||
</ul>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>For EvAdventure, we will build the above-ground part of the game world using batch-scripts. The world below-ground we will build procedurally, using raw code.</p>
|
||||
</section>
|
||||
<section id="can-only-privileged-builders-create-things-or-should-regular-players-also-have-limited-build-capability">
|
||||
<h3><span class="section-number">3.3.2. </span>Can only privileged Builders create things or should regular players also have limited build-capability?<a class="headerlink" href="#can-only-privileged-builders-create-things-or-should-regular-players-also-have-limited-build-capability" title="Permalink to this headline">¶</a></h3>
|
||||
<p>In some game styles, players have the ability to create objects and even script them. While giving regular users the ability to create objects with in-built commands is easy and safe, actual code-creation (aka <em>softcode</em> ) is not something Evennia supports natively.</p>
|
||||
<p>Regular, untrusted users should never be allowed to execute raw Python
|
||||
code (such as what you can do with the <code class="docutils literal notranslate"><span class="pre">py</span></code> command). You can
|
||||
<a class="reference internal" href="../../../Coding/Soft-Code.html"><span class="doc std std-doc">read more about Evennia’s stance on softcode here</span></a>. If you want users to do limited scripting, it’s suggested that this is accomplished by adding more powerful build-commands for them to use.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>For our tutorial-game, we will only allow privileged builders and admins to modify the world.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="systems">
|
||||
<h2><span class="section-number">3.4. </span>Systems<a class="headerlink" href="#systems" title="Permalink to this headline">¶</a></h2>
|
||||
<section id="do-you-base-your-game-off-an-existing-rpg-system-or-make-up-your-own">
|
||||
<h3><span class="section-number">3.4.1. </span>Do you base your game off an existing RPG system or make up your own?<a class="headerlink" href="#do-you-base-your-game-off-an-existing-rpg-system-or-make-up-your-own" title="Permalink to this headline">¶</a></h3>
|
||||
<p>There is a plethora of options out there, and what you choose depends on the game you want. It can be tempting to grab a short free-form ruleset, but remember that the computer does not have any intuitiion or common sense to interpret the rules like a human GM could. Conversely, if you pick a very ‘crunchy’ game system, with detailed simulation of the real world, remember that you’ll need to actually <em>code</em> all those exceptions and tables yourself.</p>
|
||||
<p>For speediest development, what you want is a game with a <em>consolidated</em> resolution mechanic - one you can code once and then use in a lot of situations. But you still want enough rules to help telling the computer how various situations should be resolved (combat is the most common system that needs such structure).</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>For this tutorial, we will make use of <a class="reference external" href="https://www.drivethrurpg.com/product/250888/Knave">Knave</a>, a very light <a class="reference external" href="https://en.wikipedia.org/wiki/Old_School_Renaissance">OSR</a> ruleset by Ben Milton. It’s only a few pages long but highly compatible with old-school D&D games. It’s consolidates all rules around a few opposed d20 rolls and includes clear rules for combat, inventory, equipment and so on. Since <em>Knave</em> is a tabletop RPG, we will have to do some minor changes here and there to fit it to the computer medium.</p>
|
||||
<p><em>Knave</em> is available under a Creative Commons Attributions 4.0 License, meaning it can be used for derivative work (even commercially). The above link allows you to purchase the PDF and supporting the author. Alternatively you can find unofficial fan releases of the rules <a class="reference external" href="https://dungeonsandpossums.com/2020/04/some-great-knave-rpg-resources/">on this page</a>.</p>
|
||||
</section>
|
||||
<section id="what-are-the-game-mechanics-how-do-you-decide-if-an-action-succeeds-or-fails">
|
||||
<h3><span class="section-number">3.4.2. </span>What are the game mechanics? How do you decide if an action succeeds or fails?<a class="headerlink" href="#what-are-the-game-mechanics-how-do-you-decide-if-an-action-succeeds-or-fails" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This follows from the RPG system decided upon in the previous question.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p><em>Knave</em> gives every character a set of six traditional stats: Strength, Intelligence, Dexterity, Constitution, Intelligence, Wisdom and Charisma. Each has a value from +1 to +10. To find its “Defense” value, you add 10.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You have Strength +1. Your Strength-Defense is 10 + 1 = 11
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>To make a check, say an arm-wrestling challenge you roll a twenty-sided die (d20) and add your stat. You have to roll higher than the opponents defense for that stat.</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>I have Strength +1, my opponent has a Strength of +2. To beat them in arm wrestling I must roll d20 + 1 and hope to get higher than 12, which is their Strength defense (10 + 2).
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you attack someone you do the same, except you roll against their <code class="docutils literal notranslate"><span class="pre">Armor</span></code> defense. If you rolled higher, you roll for how much damage you do (depends on your weapon).
|
||||
You can have <em>advantage</em> or <em>disadvantage</em> on a roll. This means rolling 2d20 and picking highest or lowest value.</p>
|
||||
<p>In Knave, combat is turn-based. In our implementation we’ll also play turn-based, but we’ll resolve everything <em>simultaneously</em>. This changes <em>Knave</em>’s feel quite a bit, but is a case where the computer can do things not practical to do when playing around a table.</p>
|
||||
<p>There are also a few tables we’ll need to implement. For example, if you lose all health, there’s a one-in-six chance you’ll die outright. We’ll keep this perma-death aspect, but make it very easy to start a new character and jump back in.</p>
|
||||
<blockquote>
|
||||
<div><p>In this tutorial we will not add opportunities to make use of all of the character stats, making some, like strength, intelligence and dexterity more useful than others. In a full game, one would want to expand so a user can utilize all of their character’s strengths.</p>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
<section id="does-the-flow-of-time-matter-in-your-game-does-night-and-day-change-what-about-seasons">
|
||||
<h3><span class="section-number">3.4.3. </span>Does the flow of time matter in your game - does night and day change? What about seasons?<a class="headerlink" href="#does-the-flow-of-time-matter-in-your-game-does-night-and-day-change-what-about-seasons" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Most commonly, game-time runs faster than real-world time. There are
|
||||
a few advantages with this:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Unlike in a single-player game, you can’t fast-forward time in a multiplayer game if you are waiting for something, like NPC shops opening.</p></li>
|
||||
<li><p>Healing and other things that we know takes time will go faster while still being reasonably ‘realistic’.</p></li>
|
||||
</ul>
|
||||
<p>The main drawback is for games with slower roleplay pace. While you are having a thoughtful roleplaying scene over dinner, the game world reports that two days have passed. Having a slower game time than real-time is a less common, but possible solution for such games.</p>
|
||||
<p>It is however <em>not</em> recommended to let game-time exactly equal the speed of real time. The reason for this is that people will join your game from all around the world, and they will often only be able to play at particular times of their day. With a game-time drifting relative real-time, everyone will eventually be able to experience both day and night in the game.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>The passage of time will have no impact on our particular game example, so we’ll go with Evennia’s default, which is that the game-time runs two times faster than real time.</p>
|
||||
</section>
|
||||
<section id="do-you-want-changing-global-weather-or-should-weather-just-be-set-manually-in-roleplay">
|
||||
<h3><span class="section-number">3.4.4. </span>Do you want changing, global weather or should weather just be set manually in roleplay?<a class="headerlink" href="#do-you-want-changing-global-weather-or-should-weather-just-be-set-manually-in-roleplay" title="Permalink to this headline">¶</a></h3>
|
||||
<p>A weather system is a good example of a game-global system that affects a subset of game entities (outdoor rooms).</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>We’ll not change the weather, but will add some random messages to echo through
|
||||
the game world at random intervals just to show the principle.</p>
|
||||
</section>
|
||||
<section id="do-you-want-a-coded-world-economy-or-just-a-simple-barter-system-or-no-formal-economy-at-all">
|
||||
<h3><span class="section-number">3.4.5. </span>Do you want a coded world-economy or just a simple barter system? Or no formal economy at all?<a class="headerlink" href="#do-you-want-a-coded-world-economy-or-just-a-simple-barter-system-or-no-formal-economy-at-all" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This is a big question and depends on how deep and interconnected the virtual transactions are that are happening in the game. Shop prices could rice and drop due to supply and demand, supply chains could involve crafting and production. One also could consider adding money sinks and manipulate the in-game market to combat inflation.</p>
|
||||
<p>The <a class="reference internal" href="../../../Contribs/Contrib-Barter.html"><span class="doc std std-doc">Barter</span></a> contrib provides a full interface for trading with another player in a safe way.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>We will not deal with any of this complexity. We will allow for players to buy from npc sellers and players will be able to trade using the normal <code class="docutils literal notranslate"><span class="pre">give</span></code> command.</p>
|
||||
</section>
|
||||
<section id="do-you-have-concepts-like-reputation-and-influence">
|
||||
<h3><span class="section-number">3.4.6. </span>Do you have concepts like reputation and influence?<a class="headerlink" href="#do-you-have-concepts-like-reputation-and-influence" title="Permalink to this headline">¶</a></h3>
|
||||
<p>These are useful things for a more social-interaction heavy game.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>We will not include them for this tutorial. Adding the Barter contrib is simple though.</p>
|
||||
</section>
|
||||
<section id="will-your-characters-be-known-by-their-name-or-only-by-their-physical-appearance">
|
||||
<h3><span class="section-number">3.4.7. </span>Will your characters be known by their name or only by their physical appearance?<a class="headerlink" href="#will-your-characters-be-known-by-their-name-or-only-by-their-physical-appearance" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This is a common thing in RP-heavy games. Others will only see you as “The tall woman” until you introduce yourself and they ‘recognize’ you with a name. Linked to this is the concept of more complex emoting and posing.</p>
|
||||
<p>Implementing such a system is not trivial, but the <a class="reference internal" href="../../../Contribs/Contrib-RPSystem.html"><span class="doc std std-doc">RPsystem</span></a> Evennia contrib offers a ready system with everything needed for free emoting, recognizing people by their appearance and more.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>We will not use any special RP systems for this tutorial. Adding the RPSystem contrib is a good extra expansion though!</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="rooms">
|
||||
<h2><span class="section-number">3.5. </span>Rooms<a class="headerlink" href="#rooms" title="Permalink to this headline">¶</a></h2>
|
||||
<section id="is-a-simple-room-description-enough-or-should-the-description-be-able-to-change">
|
||||
<h3><span class="section-number">3.5.1. </span>Is a simple room description enough or should the description be able to change?<a class="headerlink" href="#is-a-simple-room-description-enough-or-should-the-description-be-able-to-change" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Changing room descriptions for day and night, winder and summer is actually quite easy to do, but looks very impressive. We happen to know there is also a contrib that helps with this, so we’ll show how to include that.</p>
|
||||
<p>There is an <a class="reference internal" href="../../../Contribs/Contrib-Extended-Room.html"><span class="doc std std-doc">Extended Room</span></a> contrib that adds a Room type that is aware of the time-of-day as well as seasonal variations.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>We will stick to a normal room in this tutorial and let the world be in a perpetual daylight. Making Rooms into ExtendedRooms is not hard though.</p>
|
||||
</section>
|
||||
<section id="should-the-room-have-different-statuses">
|
||||
<h3><span class="section-number">3.5.2. </span>Should the room have different statuses?<a class="headerlink" href="#should-the-room-have-different-statuses" title="Permalink to this headline">¶</a></h3>
|
||||
<p>One could picture weather making outdoor rooms wet, cold or burnt. In rain, bow strings could get wet and fireballs fizz out. In a hot room, characters could require drinking more water, or even take damage if not finding shelter.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>For the above-ground we need to be able to disable combat all rooms except for the PvP location. We also need to consider how to auto-generate the rooms under ground. So we probably will need some statuses to control that.</p>
|
||||
<p>Since each room under ground should present some sort of challenge, we may need a few different room types different from the above-ground Rooms.</p>
|
||||
</section>
|
||||
<section id="can-objects-be-hidden-in-the-room-can-a-person-hide-in-the-room">
|
||||
<h3><span class="section-number">3.5.3. </span>Can objects be hidden in the room? Can a person hide in the room?<a class="headerlink" href="#can-objects-be-hidden-in-the-room-can-a-person-hide-in-the-room" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This ties into if you have hide/stealth mechanics. Maybe you could evesdrop or attack out of hiding.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>We will not model hiding and stealth. This will be a game of honorable face-to-face conflict.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="objects">
|
||||
<h2><span class="section-number">3.6. </span>Objects<a class="headerlink" href="#objects" title="Permalink to this headline">¶</a></h2>
|
||||
<section id="how-numerous-are-your-objects-do-you-want-large-loot-lists-or-are-objects-just-role-playing-props">
|
||||
<h3><span class="section-number">3.6.1. </span>How numerous are your objects? Do you want large loot-lists or are objects just role playing props?<a class="headerlink" href="#how-numerous-are-your-objects-do-you-want-large-loot-lists-or-are-objects-just-role-playing-props" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This also depends on the type of game. In a pure freeform RPG, most objects may be ‘imaginary’ and just appearing in fiction. If the game is more coded, you want objects with properties that the computer can measure, track and calculate. In many roleplaying-heavy games, you find a mixture of the two, with players imagining items for roleplaying scenes, but only using ‘real’ objects to resolve conflicts.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>We will want objects with properties, like weapons and potions and such. Monsters should drop loot even though our list of objects will not be huge in this example game.</p>
|
||||
</section>
|
||||
<section id="is-each-coin-a-separate-object-or-do-you-just-store-a-bank-account-value">
|
||||
<h3><span class="section-number">3.6.2. </span>Is each coin a separate object or do you just store a bank account value?<a class="headerlink" href="#is-each-coin-a-separate-object-or-do-you-just-store-a-bank-account-value" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The advantage of having multiple items is that it can be more immersive. The drawback is that it’s also very fiddly to deal with individual coins, especially if you have to deal with different currencies.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p><em>Knave</em> uses the “copper” as the base coin and so will we. Knave considers the weight of coin and one inventory “slot” can hold 100 coins. So we’ll implement a “coin item” to represent many coins.</p>
|
||||
</section>
|
||||
<section id="do-multiple-similar-objects-form-stack-and-how-are-those-stacks-handled-in-that-case">
|
||||
<h3><span class="section-number">3.6.3. </span>Do multiple similar objects form stack and how are those stacks handled in that case?<a class="headerlink" href="#do-multiple-similar-objects-form-stack-and-how-are-those-stacks-handled-in-that-case" title="Permalink to this headline">¶</a></h3>
|
||||
<p>If you drop two identical apples on the ground, Evennia will default to show this in the room as “two apples”, but this is just a visual effect - there are still two apple-objects in the room. One could picture instead merging the two into a single object “X nr of apples” when you drop the apples.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>We will keep Evennia’s default.</p>
|
||||
</section>
|
||||
<section id="does-an-object-have-weight-or-volume-so-you-cannot-carry-an-infinite-amount-of-them">
|
||||
<h3><span class="section-number">3.6.4. </span>Does an object have weight or volume (so you cannot carry an infinite amount of them)?<a class="headerlink" href="#does-an-object-have-weight-or-volume-so-you-cannot-carry-an-infinite-amount-of-them" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Limiting carrying weight is one way to stop players from hoarding. It also makes it more important for players to pick only the equipment they need. Carrying limits can easily come across as annoying to players though, so one needs to be careful with it.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p><em>Knave</em> limits your inventory to <code class="docutils literal notranslate"><span class="pre">Constitution</span> <span class="pre">+</span> <span class="pre">10</span></code> “slots”, where most items take up one slot and some large things, like armor, uses two. Small items (like rings) can fit 2-10 per slot and you can fit 100 coins in a slot. This is an important game mechanic to limit players from hoarding. Especially since you need coin to level up.</p>
|
||||
</section>
|
||||
<section id="can-objects-be-broken-can-they-be-repaired">
|
||||
<h3><span class="section-number">3.6.5. </span>Can objects be broken? Can they be repaired?<a class="headerlink" href="#can-objects-be-broken-can-they-be-repaired" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Item breakage is very useful for a game economy; breaking weapons adds tactical considerations (if it’s not too common, then it becomes annoying) and repairing things gives work for crafting players.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>In <em>Knave</em>, items will break if you make a critical failure on using them (rolls a native 1 on d20). This means they lose a level of <code class="docutils literal notranslate"><span class="pre">quality</span></code> and once at 0, it’s unusable. We will not allow players to repair, but we could allow merchants to repair items for a fee.</p>
|
||||
</section>
|
||||
<section id="can-you-fight-with-a-chair-or-a-flower-or-must-you-use-a-special-weapon-kind-of-thing">
|
||||
<h3><span class="section-number">3.6.6. </span>Can you fight with a chair or a flower or must you use a special ‘weapon’ kind of thing?<a class="headerlink" href="#can-you-fight-with-a-chair-or-a-flower-or-must-you-use-a-special-weapon-kind-of-thing" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Traditionally, only ‘weapons’ could be used to fight with. In the past this was a useful
|
||||
simplification, but with Python classes and inheritance, it’s not actually more work to just let all items in game work as a weapon in a pinch.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>Since <em>Knave</em> deals with weapon lists and positions where items can be wielded, we will have a separate “Weapon” class for everything you can use for fighting. So, you won’t be able to fight with a chair (unless we make it a weapon-inherited chair).</p>
|
||||
</section>
|
||||
<section id="will-characters-be-able-to-craft-new-objects">
|
||||
<h3><span class="section-number">3.6.7. </span>Will characters be able to craft new objects?<a class="headerlink" href="#will-characters-be-able-to-craft-new-objects" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Crafting is a common feature in multiplayer games. In code it usually means using a skill-check to combine base ingredients from a fixed recipe in order to create a new item. The classic example is to combine <em>leather straps</em>, a <em>hilt</em>, a <em>pommel</em> and a <em>blade</em> to make a new <em>sword</em>.</p>
|
||||
<p>A full-fledged crafting system could require multiple levels of crafting, including having to mine for ore or cut down trees for wood.</p>
|
||||
<p>Evennia’s <a class="reference internal" href="../../../Contribs/Contrib-Crafting.html"><span class="doc std std-doc">Crafting</span></a> contrib adds a full crafting system to any game. It’s based on <a class="reference internal" href="../../../Components/Tags.html"><span class="doc std std-doc">Tags</span></a>, meaning that pretty much any object can be made usable for crafting, even used in an unexpected way.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>In our case we will not add any crafting in order to limit the scope of our game. Maybe NPCs will be able to repair items - for a cost?</p>
|
||||
</section>
|
||||
<section id="should-mobs-npcs-have-some-sort-of-ai">
|
||||
<h3><span class="section-number">3.6.8. </span>Should mobs/NPCs have some sort of AI?<a class="headerlink" href="#should-mobs-npcs-have-some-sort-of-ai" title="Permalink to this headline">¶</a></h3>
|
||||
<p>As a rule, you should not hope to fool anyone into thinking your AI is actually intelligent. The best you will be able to do is to give interesting results and unless you have a side-gig as an AI researcher, users will likely not notice any practical difference between a simple state-machine and you spending a lot of time learning
|
||||
how to train a neural net.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>For this tutorial, we will show how to add a simple state-machine AI for monsters. NPCs will only be shop-keepers and quest-gives so they won’t need any real AI to speak of.</p>
|
||||
</section>
|
||||
<section id="are-npcs-and-mobs-different-entities-how-do-they-differ">
|
||||
<h3><span class="section-number">3.6.9. </span>Are NPCs and mobs different entities? How do they differ?<a class="headerlink" href="#are-npcs-and-mobs-different-entities-how-do-they-differ" title="Permalink to this headline">¶</a></h3>
|
||||
<p>“Mobs” or “mobiles” are things that move around. This is traditionally monsters you can fight with, but could also be city guards or the baker going to chat with the neighbor. Back in the day, they were often fundamentally different these days it’s often easier to just make NPCs and mobs essentially the same thing.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>In EvAdventure, Monsters and NPCs do very different things, so they will be different classes, sharing some code where possible.</p>
|
||||
</section>
|
||||
<section id="should-there-be-npcs-giving-quests-if-so-how-do-you-track-quest-status">
|
||||
<h3><span class="section-number">3.6.10. </span>_Should there be NPCs giving quests? If so, how do you track Quest status?<a class="headerlink" href="#should-there-be-npcs-giving-quests-if-so-how-do-you-track-quest-status" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Quests are a staple of many classic RPGs.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>We will design a simple quest system with some simple conditions for success, like carrying the right item or items back to the quest giver.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="characters">
|
||||
<h2><span class="section-number">3.7. </span>Characters<a class="headerlink" href="#characters" title="Permalink to this headline">¶</a></h2>
|
||||
<section id="can-players-have-more-than-one-character-active-at-a-time-or-are-they-allowed-to-multi-play">
|
||||
<h3><span class="section-number">3.7.1. </span>Can players have more than one Character active at a time or are they allowed to multi-play?<a class="headerlink" href="#can-players-have-more-than-one-character-active-at-a-time-or-are-they-allowed-to-multi-play" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Since Evennia differentiates between <code class="docutils literal notranslate"><span class="pre">Sessions</span></code> (the client-connection to the game), <code class="docutils literal notranslate"><span class="pre">Accounts</span></code> and <code class="docutils literal notranslate"><span class="pre">Character</span></code>s, it natively supports multi-play. This is controlled by the <code class="docutils literal notranslate"><span class="pre">MULTISESSION_MODE</span></code> setting, which has a value from <code class="docutils literal notranslate"><span class="pre">0</span></code> (default) to <code class="docutils literal notranslate"><span class="pre">3</span></code>.</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">0</span></code>- One Character per Account and one Session per Account. This means that if you login to the same
|
||||
account from another client you’ll be disconnected from the first. When creating a new account, a Character
|
||||
will be auto-created with the same name as your Account. This is default mode and mimics legacy code bases
|
||||
which had no separation between Account and Character.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">1</span></code> - One Character per Account, multiple Sessions per Account. So you can connect simultaneously from
|
||||
multiple clients and see the same output in all of them.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">2</span></code> - Multiple Characters per Account, one Session per Character. This will not auto-create a same-named
|
||||
Character for you, instead you get to create/choose between a number of Characters up to a max limit given by
|
||||
the <code class="docutils literal notranslate"><span class="pre">MAX_NR_CHARACTERS</span></code> setting (default 1). You can play them all simultaneously if you have multiple clients
|
||||
open, but only one client per Character.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">3</span></code> - Multiple Characters per Account, Multiple Sessions per Character. This is like mode 2, except players
|
||||
can control each Character from multiple clients, seeing the same output from each Character.</p></li>
|
||||
</ul>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>Due to the nature of <em>Knave</em>, characters are squishy and probably short-lived. So it makes little sense to keep a stable of them. We’ll use use mode 0 or 1.</p>
|
||||
</section>
|
||||
<section id="how-does-the-character-generation-work">
|
||||
<h3><span class="section-number">3.7.2. </span>How does the character-generation work?<a class="headerlink" href="#how-does-the-character-generation-work" title="Permalink to this headline">¶</a></h3>
|
||||
<p>There are a few common ways to do character generation:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Rooms. This is the traditional way. Each room’s description tells you what command to use to modify your character. When you are done you move to the next room. Only use this if you have another reason for using a room, like having a training dummy to test skills on, for example.</p></li>
|
||||
<li><p>A Menu. The Evennia <em>EvMenu</em> system allows you to code very flexible in-game menus without needing to walk between rooms. You can both have a step-by-step menu (a ‘wizard’) or allow the user to jump between the
|
||||
steps as they please. This tends to be a lot easier for newcomers to understand since it doesn’t require
|
||||
using custom commands they will likely never use again after this.</p></li>
|
||||
<li><p>Questions. A fun way to build a character is to answer a series of questions. This is usually implemented with a sequential menu.</p></li>
|
||||
</ul>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>Knave randomizes almost aspects of the Character generation. We’ll use a menu to let the player add their name and sex as well as do the minor re-assignment of stats allowed by the rules.</p>
|
||||
</section>
|
||||
<section id="how-do-you-implement-different-classes-or-races">
|
||||
<h3><span class="section-number">3.7.3. </span>How do you implement different “classes” or “races”?<a class="headerlink" href="#how-do-you-implement-different-classes-or-races" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The way classes and races work in most RPGs is that they act as static ‘templates’ that inform which bonuses and special abilities you have. Much of this only comes into play during character generation or when leveling up.</p>
|
||||
<p>Often all we need to store on the Character is <em>which</em> class and <em>which</em> race they have; the actual logic can sit in Python code and just be looked up when we need it.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>There are no races and no classes in <em>Knave</em>. Every character is a human.</p>
|
||||
</section>
|
||||
<section id="if-a-character-can-hide-in-a-room-what-skill-will-decide-if-they-are-detected">
|
||||
<h3><span class="section-number">3.7.4. </span>If a Character can hide in a room, what skill will decide if they are detected?<a class="headerlink" href="#if-a-character-can-hide-in-a-room-what-skill-will-decide-if-they-are-detected" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Hiding means a few things.</p>
|
||||
<ul class="simple">
|
||||
<li><p>The Character should not appear in the room’s description / character list</p></li>
|
||||
<li><p>Others hould not be able to interact with a hidden character. It’d be weird if you could do <code class="docutils literal notranslate"><span class="pre">attack</span> <span class="pre"><name></span></code>
|
||||
or <code class="docutils literal notranslate"><span class="pre">look</span> <span class="pre"><name></span></code> if the named character is in hiding.</p></li>
|
||||
<li><p>There must be a way for the person to come out of hiding, and probably for others to search or accidentally
|
||||
find the person (probably based on skill checks).</p></li>
|
||||
<li><p>The room will also need to be involved, maybe with some modifier as to how easy it is to hide in the room.</p></li>
|
||||
</ul>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>We will not be including a hide-mechanic in EvAdventure.</p>
|
||||
</section>
|
||||
<section id="what-does-the-skill-tree-look-like-can-a-character-gain-experience-to-improve-by-killing-enemies-solving-quests-by-roleplaying">
|
||||
<h3><span class="section-number">3.7.5. </span>What does the skill tree look like? Can a Character gain experience to improve? By killing enemies? Solving quests? By roleplaying?<a class="headerlink" href="#what-does-the-skill-tree-look-like-can-a-character-gain-experience-to-improve-by-killing-enemies-solving-quests-by-roleplaying" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Gaining experience points (XP) and improving one’s character is a staple of roleplaying games. There are many
|
||||
ways to implement this:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Gaining XP from kills is very common; it’s easy to let a monster be ‘worth’ a certain number of XP and it’s easy to tell when you should gain it.</p></li>
|
||||
<li><p>Gaining XP from quests is the same - each quest is ‘worth’ XP and you get them when completing the test.</p></li>
|
||||
<li><p>Gaining XP from roleplay is harder to define. Different games have tried a lot of different ways to do this:</p>
|
||||
<ul>
|
||||
<li><p>XP from being online - just being online gains you XP. This inflates player numbers but many players may
|
||||
just be lurking and not be actually playing the game at any given time.</p></li>
|
||||
<li><p>XP from roleplaying scenes - you gain XP according to some algorithm analyzing your emotes for ‘quality’,
|
||||
how often you post, how long your emotes are etc.</p></li>
|
||||
<li><p>XP from actions - you gain XP when doing things, anything. Maybe your XP is even specific to each action, so
|
||||
you gain XP only for running when you run, XP for your axe skill when you fight with an axe etc.</p></li>
|
||||
<li><p>XP from fails - you only gain XP when failing rolls.</p></li>
|
||||
<li><p>XP from other players - other players can award you XP for good RP.</p></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>We will use an alternative rule in <em>Knave</em>, where Characters gain XP by spending coins they carry back from their adventures. The above-ground merchants will allow you to spend your coins and exchange them for XP 1:1. Each level costs 1000 coins. Every level you have <code class="docutils literal notranslate"><span class="pre">1d8</span>  <span class="pre">*</span> <span class="pre">new</span> <span class="pre">level</span></code> (minimum what you had before + 1) HP, and can raise 3 different ability scores by 1 (max +10). There are no skills in <em>Knave</em>, but the principle of increasing them would be the same.</p>
|
||||
</section>
|
||||
<section id="may-player-characters-attack-each-other-pvp">
|
||||
<h3><span class="section-number">3.7.6. </span>May player-characters attack each other (PvP)?<a class="headerlink" href="#may-player-characters-attack-each-other-pvp" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Deciding this affects the style of your entire game. PvP makes for exciting gameplay but it opens a whole new can of worms when it comes to “fairness”. Players will usually accept dying to an overpowered NPC dragon. They will not be as accepting if they perceive another player as being overpowered. PvP means that you
|
||||
have to be very careful to balance the game - all characters does not have to be exactly equal but they should all be viable to play a fun game with.</p>
|
||||
<p>PvP does not only mean combat though. Players can compete in all sorts of ways, including gaining influence in a political game or gaining market share when selling their crafted merchandise.</p>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>We will allow PvP only in one place - a special Dueling location where players can play-fight each other for training and prestige, but not actually get killed. Otherwise no PvP will be allowed. Note that without a full Barter system in place (just regular <code class="docutils literal notranslate"><span class="pre">give</span></code>, it makes it theoretically easier for players to scam one another.</p>
|
||||
</section>
|
||||
<section id="what-are-the-penalties-of-defeat-permanent-death-quick-respawn-time-in-prison">
|
||||
<h3><span class="section-number">3.7.7. </span>What are the penalties of defeat? Permanent death? Quick respawn? Time in prison?<a class="headerlink" href="#what-are-the-penalties-of-defeat-permanent-death-quick-respawn-time-in-prison" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This is another big decision that strongly affects the mood and style of your game.</p>
|
||||
<p>Perma-death means that once your character dies, it’s gone and you have to make a new one.</p>
|
||||
<ul class="simple">
|
||||
<li><p>It allows for true heroism. If you genuinely risk losing your character of two years to fight the dragon,
|
||||
your triumph is an actual feat.</p></li>
|
||||
<li><p>It limits the old-timer dominance problem. If long-time players dies occationally, it will open things
|
||||
up for newcomers.</p></li>
|
||||
<li><p>It lowers inflation, since the hoarded resources of a dead character can be removed.</p></li>
|
||||
<li><p>It gives capital punishment genuine discouraging power.</p></li>
|
||||
<li><p>It’s realistic.</p></li>
|
||||
</ul>
|
||||
<p>Perma-death comes with some severe disadvantages however.</p>
|
||||
<ul class="simple">
|
||||
<li><p>Many players say they like the <em>idea</em> of permadeath except when it could happen to them.</p></li>
|
||||
<li><p>Some players refuse to take any risks if death is permanent.</p></li>
|
||||
<li><p>It may make players even more reluctant to play conflict-driving ‘bad guys’.</p></li>
|
||||
<li><p>Balancing PvP becomes very hard. Fairness and avoiding exploits becomes critical when the outcome
|
||||
is permanent.</p></li>
|
||||
</ul>
|
||||
<p>For these reasons, it’s very common to do hybrid systems. Some tried variations:</p>
|
||||
<ul class="simple">
|
||||
<li><p>NPCs cannot kill you, only other players can.</p></li>
|
||||
<li><p>Death is permanent, but it’s difficult to actually die - you are much more likely to end up being severely hurt/incapacitated.</p></li>
|
||||
<li><p>You can pre-pay ‘insurance’ to magically/technologically avoid actually dying. Only if don’t have insurance will
|
||||
you die permanently.</p></li>
|
||||
<li><p>Death just means harsh penalties, not actual death.</p></li>
|
||||
<li><p>When you die you can fight your way back to life from some sort of afterlife.</p></li>
|
||||
<li><p>You’ll only die permanently if you as a player explicitly allows it.</p></li>
|
||||
</ul>
|
||||
<p><strong>EvAdventure Answer</strong></p>
|
||||
<p>In <em>Knave</em>, when you hit 0 HP, you roll on a death table, with a 1/8 chance of immediate death (otherwise you lose
|
||||
points in a random stat). We will offer an “Insurance” that allows you to resurrect if you carry enough coin on you when
|
||||
you die. If not, you are perma-dead and have to create a new character (which is easy and quick since it’s mostly
|
||||
randomized).</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="conclusions">
|
||||
<h2><span class="section-number">3.8. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Going through the questions has helped us get a little bit more of a feel for the game we want to do. There are many, many other things we could ask ourselves, but if we can cover these points we will be a good way towards a complete,
|
||||
playable game!</p>
|
||||
<p>In the last of these planning lessons we’ll sketch out how these ideas will map to Evennia.</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="../Part3/Beginner-Tutorial-Part3-Overview.html" title="Part 3: How We Get There (Example Game)"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Game-Planning.html" title="2. On Planning a Game"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part2-Overview.html" >Part 2: What We Want</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">3. </span>Planning our tutorial game</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
|
||||
<!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>1. Where do I begin? — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="2. On Planning a Game" href="Beginner-Tutorial-Game-Planning.html" />
|
||||
<link rel="prev" title="Part 2: What We Want" href="Beginner-Tutorial-Part2-Overview.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Game-Planning.html" title="2. On Planning a Game"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Part2-Overview.html" title="Part 2: What We Want"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part2-Overview.html" accesskey="U">Part 2: What We Want</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">1. </span>Where do I begin?</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">1. Where do I begin?</a><ul>
|
||||
<li><a class="reference internal" href="#what-is-your-motivation-for-doing-this">1.1. What is your motivation for doing this?</a></li>
|
||||
<li><a class="reference internal" href="#what-are-your-skills">1.2. What are your skills?</a><ul>
|
||||
<li><a class="reference internal" href="#the-game-engine">1.2.1. The game engine</a></li>
|
||||
<li><a class="reference internal" href="#asset-creation">1.2.2. Asset creation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#so-where-do-i-begin-then">1.3. So, where do I begin, then?</a></li>
|
||||
<li><a class="reference internal" href="#conclusions">1.4. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Part2-Overview.html"
|
||||
title="previous chapter">Part 2: What We Want</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Game-Planning.html"
|
||||
title="next chapter"><span class="section-number">2. </span>On Planning a Game</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part2/Beginner-Tutorial-Planning-Where-Do-I-Begin.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="where-do-i-begin">
|
||||
<h1><span class="section-number">1. </span>Where do I begin?<a class="headerlink" href="#where-do-i-begin" title="Permalink to this headline">¶</a></h1>
|
||||
<p>The good news is that following this Starting tutorial is a great way to begin making an Evennia game.</p>
|
||||
<p>The bad news is that everyone’s different and when it comes to starting your own game there is no
|
||||
one-size-fits-all answer. Instead we will ask a series of questions
|
||||
to help you figure this out for yourself. It will also help you evaluate your own skills and maybe
|
||||
put some more realistic limits on how fast you can achieve your goals.</p>
|
||||
<blockquote>
|
||||
<div><p>The questions in this lesson do not really apply to our tutorial game since we know we are doing it
|
||||
to learn Evennia. If you just want to follow along with the technical bits you can skip this lesson and
|
||||
come back later when you feel ready to take on making your own game.</p>
|
||||
</div></blockquote>
|
||||
<section id="what-is-your-motivation-for-doing-this">
|
||||
<h2><span class="section-number">1.1. </span>What is your motivation for doing this?<a class="headerlink" href="#what-is-your-motivation-for-doing-this" title="Permalink to this headline">¶</a></h2>
|
||||
<p>So you want to make a game. First you need to make a few things clear to yourself.</p>
|
||||
<p>Making a multiplayer online game is a <em>big</em> undertaking. You will (if you are like most of us) be
|
||||
doing it as a hobby, without getting paid. And you’ll be doing it for a long time.</p>
|
||||
<p>So the very first thing you should ask yourself (and your team, if you have any) is
|
||||
<em>why am I doing this</em>? Do some soul-searching here. Here are some possible answers:</p>
|
||||
<ul class="simple">
|
||||
<li><p>I want to earn recognition and fame from my online community and/or among my friends.</p></li>
|
||||
<li><p>I want to build the game so I can play and enjoy it myself.</p></li>
|
||||
<li><p>I want to build the same game I already play but without the bad people.</p></li>
|
||||
<li><p>I want to create a game so that I can control it and be the head honcho.</p></li>
|
||||
<li><p>A friend or online acquaintance talked me into working on it.</p></li>
|
||||
<li><p>I work on this because I’m paid to (wow!)</p></li>
|
||||
<li><p>I only build this for my own benefit or to see if I can pull it off.</p></li>
|
||||
<li><p>I want to create something to give back to the community I love.</p></li>
|
||||
<li><p>I want to use this project as a stepping-stone towards other projects (like a career in game design
|
||||
or programming).</p></li>
|
||||
<li><p>I am interested in coding or server and network architectures, making a MUD just seems to be a good
|
||||
way to teach myself.</p></li>
|
||||
<li><p>I want to build a commercial game and earn money.</p></li>
|
||||
<li><p>I want to fulfill a life-long dream of game making.</p></li>
|
||||
</ul>
|
||||
<p>There are many other possibilities. How “solid” your answer is for a long-term development project
|
||||
is up to you. The important point is that you ask yourself the question.</p>
|
||||
<p><strong>Help someone else instead</strong> - Maybe you should <em>not</em> start a new project - maybe you’re better off
|
||||
helping someone else or improve on something that already exists. Or maybe you find you are more of a
|
||||
game engine developer than a game designer.</p>
|
||||
<p><strong>Driven by emotion</strong> - Some answers may suggest that you are driven by emotions of revenge or disconcert. Be careful with that and
|
||||
check so that’s not your <em>only</em> driving force. Those emotions may have abated later when the project
|
||||
most needs your enthusiasm and motivation.</p>
|
||||
<p><strong>Going commercial</strong> - If your aim is to earn money, your design goals will likely be very different from
|
||||
those of a person who only creates as a hobby or for their own benefit. You may also have a much stricter
|
||||
timeline for release.</p>
|
||||
<p>Whichever your motivation, you should at least have it clear in your own mind. It’s worth to make
|
||||
sure your eventual team is on the same page too.</p>
|
||||
</section>
|
||||
<section id="what-are-your-skills">
|
||||
<h2><span class="section-number">1.2. </span>What are your skills?<a class="headerlink" href="#what-are-your-skills" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Once you have your motivations straight you need to take a stock of your own skills and the skills
|
||||
available in your team, if you have any.</p>
|
||||
<p>Your game will have two principal components and you will need skills to cater for both:</p>
|
||||
<ul class="simple">
|
||||
<li><p>The game engine / code base - Evennia in this case.</p></li>
|
||||
<li><p>The assets created for using the game engine (“the game world”)</p></li>
|
||||
</ul>
|
||||
<section id="the-game-engine">
|
||||
<h3><span class="section-number">1.2.1. </span>The game engine<a class="headerlink" href="#the-game-engine" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The game engine is maintained and modified by programmers (coders). It represents the infrastructure
|
||||
that runs the game - the network code, the protocol support, the handling of commands, scripting and
|
||||
data storage.</p>
|
||||
<p>If you are just evaluating Evennia, it’s worth to do the following:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Hang out in the community/forums/chat. Expect to need to ask a lot of “stupid” questions as you start
|
||||
developing (hint: no question is stupid). Is this a community in which you would feel comfortable doing so?</p></li>
|
||||
<li><p>Keep tabs on the manual (you’re already here).</p></li>
|
||||
<li><p>How’s your Python skills? What are the skills in your team? Do you or your team already know it or are
|
||||
you willing to learn? Learning the language as you go is not too unusual with Evennia devs, but expect it
|
||||
to add development time. You will also be worse at predicting how ‘hard’ something is to do.</p></li>
|
||||
<li><p>If you don’t know Python, you should have gotten a few tastes from the first part of this tutorial. But
|
||||
expect to have to refer to external online tutorials - there are many details of Python that will not be
|
||||
covered.</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="asset-creation">
|
||||
<h3><span class="section-number">1.2.2. </span>Asset creation<a class="headerlink" href="#asset-creation" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Compared to the level of work needed to produce professional graphics for an MMORPG, detailed text
|
||||
assets for a mud are cheap to create. This is one of the many reasons muds are so well suited for a
|
||||
small team.</p>
|
||||
<p>This is not to say that making “professional” text content is easy though. Knowing how to write
|
||||
imaginative and grammatically correct prose is only the minimal starting requirement. A good asset-
|
||||
creator (traditionally called a “builder”) must also be able to utilize the tools of the game engine
|
||||
to its fullest in order to script events, make quests, triggers and interactive, interesting
|
||||
environments.</p>
|
||||
<p>Assuming you are not coding all alone, your team’s in-house builders will be the first ones to actually
|
||||
“use” your game framework and build tools. They will stumble on all the bugs. This means that you
|
||||
need people who are just not “artsy” or “good with words”. Assuming coders and builders are not the
|
||||
same people (common for early testing), builders need to be able to collaborate well and give clear
|
||||
and concise feedback.</p>
|
||||
<p>If you know your builders are not tech-savvy, you may need to spend more time making easier
|
||||
build-tools and commands for them.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="so-where-do-i-begin-then">
|
||||
<h2><span class="section-number">1.3. </span>So, where do I begin, then?<a class="headerlink" href="#so-where-do-i-begin-then" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Right, after all this soul-searching and skill-inventory-checking, let’s go back to the original
|
||||
question. And maybe you’ll find that you have a better feeling for the answer yourself already:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Keep following this tutorial and spend the time
|
||||
to really understand what is happening in the examples. Not only will this give you a better idea
|
||||
of how parts hang together, it may also give you ideas for what is possible. Maybe something
|
||||
is easier than you expected!</p></li>
|
||||
<li><p>Introduce yourself in the IRC/Discord chat and don’t be shy to ask questions as you go through
|
||||
the tutorial. Don’t get hung up on trying to resolve something that a seasoned Evennia dev may
|
||||
clear up for you in five minutes. Also, not all errors are your faults - it’s possible the
|
||||
tutorial is unclear or has bugs, asking will quickly bring those problems to light, if so.</p></li>
|
||||
<li><p>If Python is new to you, you should complement the tutorial with third-party Python references
|
||||
so you can read, understand and replicate example code without being completely in the dark.</p></li>
|
||||
</ul>
|
||||
<p>Once you are out of the starting tutorial, you’ll be off to do your own thing.</p>
|
||||
<ul class="simple">
|
||||
<li><p>The starting tutorial cannot cover everything. Skim through the <a class="reference internal" href="../../../index.html"><span class="doc std std-doc">Evennia docs</span></a>.
|
||||
Even if you don’t read everything, it gives you a feeling for what’s available should you need
|
||||
to look for something later. Make sure to use the search function.</p></li>
|
||||
<li><p>You can now start by expanding on the tutorial-game we will have created. In the last part there
|
||||
there will be a list of possible future projects you could take on. Working on your own, without help
|
||||
from a tutorial is the next step.</p></li>
|
||||
</ul>
|
||||
<p>As for your builders, they can start getting familiar with Evennia’s default build commands … but
|
||||
keep in mind that your game is not yet built! Don’t set your builders off on creating large zone projects.
|
||||
If they build anything at all, it should be small test areas to agree on a homogenous form, mood
|
||||
and literary style.</p>
|
||||
</section>
|
||||
<section id="conclusions">
|
||||
<h2><span class="section-number">1.4. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Remember that what kills a hobby game project will usually be your own lack of
|
||||
motivation. So do whatever you can to keep that motivation burning strong! Even if it means
|
||||
deviating from what you read in a tutorial like this one. Just get that game out there, whichever way
|
||||
works best for you.</p>
|
||||
<p>In the next lesson we’ll go through some of the technical questions you need to consider. This should
|
||||
hopefully help you figure out more about the game you want to make. In the lesson following that we’ll
|
||||
then try to answer those questions for the sake of creating our little tutorial game.</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="Beginner-Tutorial-Game-Planning.html" title="2. On Planning a Game"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Part2-Overview.html" title="Part 2: What We Want"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part2-Overview.html" >Part 2: What We Want</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">1. </span>Where do I begin?</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
|
||||
<!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>12. NPC and monster AI — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="13. Dynamically generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
|
||||
<link rel="prev" title="11. Turnbased Combat" href="Beginner-Tutorial-Combat-Turnbased.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Combat-Turnbased.html" title="11. Turnbased Combat"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">12. </span>NPC and monster AI</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Combat-Turnbased.html"
|
||||
title="previous chapter"><span class="section-number">11. </span>Turnbased Combat</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Dungeon.html"
|
||||
title="next chapter"><span class="section-number">13. </span>Dynamically generated Dungeon</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-AI.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="npc-and-monster-ai">
|
||||
<h1><span class="section-number">12. </span>NPC and monster AI<a class="headerlink" href="#npc-and-monster-ai" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="admonition warning">
|
||||
<p class="admonition-title">Warning</p>
|
||||
<p>This part of the Beginner tutorial is still being developed.</p>
|
||||
</div>
|
||||
</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="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Combat-Turnbased.html" title="11. Turnbased Combat"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">12. </span>NPC and monster AI</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,566 @@
|
|||
|
||||
<!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>3. Player Characters — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="4. In-game Objects and items" href="Beginner-Tutorial-Objects.html" />
|
||||
<link rel="prev" title="2. Rules and dice rolling" href="Beginner-Tutorial-Rules.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Objects.html" title="4. In-game Objects and items"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Rules.html" title="2. Rules and dice rolling"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">3. </span>Player Characters</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">3. Player Characters</a><ul>
|
||||
<li><a class="reference internal" href="#inheritance-structure">3.1. Inheritance structure</a></li>
|
||||
<li><a class="reference internal" href="#living-mixin-class">3.2. Living mixin class</a></li>
|
||||
<li><a class="reference internal" href="#character-class">3.3. Character class</a><ul>
|
||||
<li><a class="reference internal" href="#funcparser-inlines">3.3.1. Funcparser inlines</a></li>
|
||||
<li><a class="reference internal" href="#backtracking">3.3.2. Backtracking</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#connecting-the-character-with-evennia">3.4. Connecting the Character with Evennia</a></li>
|
||||
<li><a class="reference internal" href="#unit-testing">3.5. Unit Testing</a></li>
|
||||
<li><a class="reference internal" href="#about-races-and-classes">3.6. About races and classes</a></li>
|
||||
<li><a class="reference internal" href="#summary">3.7. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Rules.html"
|
||||
title="previous chapter"><span class="section-number">2. </span>Rules and dice rolling</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Objects.html"
|
||||
title="next chapter"><span class="section-number">4. </span>In-game Objects and items</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Characters.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="player-characters">
|
||||
<h1><span class="section-number">3. </span>Player Characters<a class="headerlink" href="#player-characters" title="Permalink to this headline">¶</a></h1>
|
||||
<p>In the <a class="reference internal" href="Beginner-Tutorial-Rules.html"><span class="doc std std-doc">previous lesson about rules and dice rolling</span></a> we made some
|
||||
assumptions about the “Player Character” entity:</p>
|
||||
<ul class="simple">
|
||||
<li><p>It should store Abilities on itself as <code class="docutils literal notranslate"><span class="pre">character.strength</span></code>, <code class="docutils literal notranslate"><span class="pre">character.constitution</span></code> etc.</p></li>
|
||||
<li><p>It should have a <code class="docutils literal notranslate"><span class="pre">.heal(amount)</span></code> method.</p></li>
|
||||
</ul>
|
||||
<p>So we have some guidelines of how it should look! A Character is a database entity with values that
|
||||
should be able to be changed over time. It makes sense to base it off Evennia’s
|
||||
<a class="reference internal" href="../../../Components/Typeclasses.html"><span class="doc std std-doc">DefaultCharacter Typeclass</span></a>. The Character class is like a ‘character sheet’ in a tabletop
|
||||
RPG, it will hold everything relevant to that PC.</p>
|
||||
<section id="inheritance-structure">
|
||||
<h2><span class="section-number">3.1. </span>Inheritance structure<a class="headerlink" href="#inheritance-structure" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Player Characters (PCs) are not the only “living” things in our world. We also have <em>NPCs</em>
|
||||
(like shopkeepers and other friendlies) as well as <em>monsters</em> (mobs) that can attack us.</p>
|
||||
<p>In code, there are a few ways we could structure this. If NPCs/monsters were just special cases of PCs,
|
||||
we could use a class inheritance like this:</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">DefaultCharacter</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
<span class="c1"># stuff </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureNPC</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">):</span>
|
||||
<span class="c1"># more stuff </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureMob</span><span class="p">(</span><span class="n">EvAdventureNPC</span><span class="p">):</span>
|
||||
<span class="c1"># more stuff </span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>All code we put on the <code class="docutils literal notranslate"><span class="pre">Character</span></code> class would now be inherited to <code class="docutils literal notranslate"><span class="pre">NPC</span></code> and <code class="docutils literal notranslate"><span class="pre">Mob</span></code> automatically.</p>
|
||||
<p>However, in <em>Knave</em>, NPCs and particularly monsters are <em>not</em> using the same rules as PCs - they are
|
||||
simplified to use a Hit-Die (HD) concept. So while still character-like, NPCs should be separate from
|
||||
PCs like this:</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">DefaultCharacter</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
<span class="c1"># stuff </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureNPC</span><span class="p">(</span><span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
<span class="c1"># separate stuff </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureMob</span><span class="p">(</span><span class="n">EvadventureNPC</span><span class="p">):</span>
|
||||
<span class="c1"># more separate stuff</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Nevertheless, there are some things that <em>should</em> be common for all ‘living things’:</p>
|
||||
<ul class="simple">
|
||||
<li><p>All can take damage.</p></li>
|
||||
<li><p>All can die.</p></li>
|
||||
<li><p>All can heal</p></li>
|
||||
<li><p>All can hold and lose coins</p></li>
|
||||
<li><p>All can loot their fallen foes.</p></li>
|
||||
<li><p>All can get looted when defeated.</p></li>
|
||||
</ul>
|
||||
<p>We don’t want to code this separately for every class but we no longer have a common parent
|
||||
class to put it on. So instead we’ll use the concept of a <em>mixin</em> class:</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">DefaultCharacter</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">LivingMixin</span><span class="p">:</span>
|
||||
<span class="c1"># stuff common for all living things</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
<span class="c1"># stuff </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureNPC</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
<span class="c1"># stuff </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureMob</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">EvadventureNPC</span><span class="p">):</span>
|
||||
<span class="c1"># more stuff</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p>In <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.characters.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/characters.py</span></a>
|
||||
is an example of a character class structure.</p>
|
||||
</aside>
|
||||
<p>Above, the <code class="docutils literal notranslate"><span class="pre">LivingMixin</span></code> class cannot work on its own - it just ‘patches’ the other classes with some
|
||||
extra functionality all living things should be able to do. This is an example of
|
||||
<em>multiple inheritance</em>. It’s useful to know about, but one should not over-do multiple inheritance
|
||||
since it can also get confusing to follow the code.</p>
|
||||
</section>
|
||||
<section id="living-mixin-class">
|
||||
<h2><span class="section-number">3.2. </span>Living mixin class<a class="headerlink" href="#living-mixin-class" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/characters.py</span></code></p>
|
||||
</div></blockquote>
|
||||
<p>Let’s get some useful common methods all living things should have in our game.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/characters.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.rules</span> <span class="kn">import</span> <span class="n">dice</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">LivingMixin</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># makes it easy for mobs to know to attack PCs</span>
|
||||
<span class="n">is_pc</span> <span class="o">=</span> <span class="kc">False</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">hurt_level</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> String describing how hurt this character is.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">percent</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">min</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">100</span> <span class="o">*</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">/</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span><span class="p">)))</span>
|
||||
<span class="k">if</span> <span class="mi">95</span> <span class="o"><</span> <span class="n">percent</span> <span class="o"><=</span> <span class="mi">100</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="s2">"|gPerfect|n"</span>
|
||||
<span class="k">elif</span> <span class="mi">80</span> <span class="o"><</span> <span class="n">percent</span> <span class="o"><=</span> <span class="mi">95</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="s2">"|gScraped|n"</span>
|
||||
<span class="k">elif</span> <span class="mi">60</span> <span class="o"><</span> <span class="n">percent</span> <span class="o"><=</span> <span class="mi">80</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="s2">"|GBruised|n"</span>
|
||||
<span class="k">elif</span> <span class="mi">45</span> <span class="o"><</span> <span class="n">percent</span> <span class="o"><=</span> <span class="mi">60</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="s2">"|yHurt|n"</span>
|
||||
<span class="k">elif</span> <span class="mi">30</span> <span class="o"><</span> <span class="n">percent</span> <span class="o"><=</span> <span class="mi">45</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="s2">"|yWounded|n"</span>
|
||||
<span class="k">elif</span> <span class="mi">15</span> <span class="o"><</span> <span class="n">percent</span> <span class="o"><=</span> <span class="mi">30</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="s2">"|rBadly wounded|n"</span>
|
||||
<span class="k">elif</span> <span class="mi">1</span> <span class="o"><</span> <span class="n">percent</span> <span class="o"><=</span> <span class="mi">15</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="s2">"|rBarely hanging on|n"</span>
|
||||
<span class="k">elif</span> <span class="n">percent</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="s2">"|RCollapsed!|n"</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">heal</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">hp</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Heal hp amount of health, not allowing to exceed our max hp</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">damage</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span> <span class="o">-</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp</span>
|
||||
<span class="n">healed</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">damage</span><span class="p">,</span> <span class="n">hp</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">+=</span> <span class="n">healed</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"You heal for </span><span class="si">{</span><span class="n">healed</span><span class="si">}</span><span class="s2"> HP."</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_pay</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">amount</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""When paying coins, make sure to never detract more than we have"""</span>
|
||||
<span class="n">amount</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">amount</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">coins</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">coins</span> <span class="o">-=</span> <span class="n">amount</span>
|
||||
<span class="k">return</span> <span class="n">amount</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_attacked</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attacker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called when being attacked and combat starts."""</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_damage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">damage</span><span class="p">,</span> <span class="n">attacker</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called when attacked and taking damage."""</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">-=</span> <span class="n">damage</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_defeat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called when defeated. By default this means death."""</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">at_death</span><span class="p">()</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_death</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called when this thing dies."""</span>
|
||||
<span class="c1"># this will mean different things for different living things</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_do_loot</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looted</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called when looting another entity"""</span>
|
||||
<span class="n">looted</span><span class="o">.</span><span class="n">at_looted</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_looted</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looter</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called when looted by another entity"""</span>
|
||||
|
||||
<span class="c1"># default to stealing some coins </span>
|
||||
<span class="n">max_steal</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d10"</span><span class="p">)</span>
|
||||
<span class="n">stolen</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">at_pay</span><span class="p">(</span><span class="n">max_steal</span><span class="p">)</span>
|
||||
<span class="n">looter</span><span class="o">.</span><span class="n">coins</span> <span class="o">+=</span> <span class="n">stolen</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Most of these are empty since they will behave differently for characters and npcs. But having them in the mixin means we can expect these methods to be available for all living things.</p>
|
||||
<p>Once we create more of our game, we will need to remember to actually call these hook methods so they serve a purpose. For example, once we implement combat, we must remember to call <code class="docutils literal notranslate"><span class="pre">at_attacked</span></code> as well as the other methods involving taking damage, getting defeated or dying.</p>
|
||||
</section>
|
||||
<section id="character-class">
|
||||
<h2><span class="section-number">3.3. </span>Character class<a class="headerlink" href="#character-class" title="Permalink to this headline">¶</a></h2>
|
||||
<p>We will now start making the basic Character class, based on what we need from <em>Knave</em>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/characters.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span><span class="p">,</span> <span class="n">AttributeProperty</span>
|
||||
<span class="kn">from</span> <span class="nn">.rules</span> <span class="kn">import</span> <span class="n">dice</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">LivingMixin</span><span class="p">:</span>
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> A character to use for EvAdventure. </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">is_pc</span> <span class="o">=</span> <span class="kc">True</span>
|
||||
|
||||
<span class="n">strength</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
||||
<span class="n">dexterity</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
||||
<span class="n">constitution</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
||||
<span class="n">intelligence</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
||||
<span class="n">wisdom</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
||||
<span class="n">charisma</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
||||
|
||||
<span class="n">hp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
|
||||
<span class="n">hp_max</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
|
||||
|
||||
<span class="n">level</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
||||
<span class="n">xp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
|
||||
<span class="n">coins</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_defeat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Characters roll on the death table"""</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">allow_death</span><span class="p">:</span>
|
||||
<span class="c1"># this allow rooms to have non-lethal battles</span>
|
||||
<span class="n">dice</span><span class="o">.</span><span class="n">roll_death</span><span class="p">(</span><span class="bp">self</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">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
|
||||
<span class="s2">"$You() $conj(collapse) in a heap, alive but beaten."</span><span class="p">,</span>
|
||||
<span class="n">from_obj</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">heal</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_death</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""We rolled 'dead' on the death table."""</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
|
||||
<span class="s2">"$You() collapse in a heap, embraced by death."</span><span class="p">,</span>
|
||||
<span class="n">from_obj</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
|
||||
<span class="c1"># TODO - go back into chargen to make a new character! </span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We make an assumption about our rooms here - that they have a property <code class="docutils literal notranslate"><span class="pre">.allow_death</span></code>. We need to make a note to actually add such a property to rooms later!</p>
|
||||
<p>In our <code class="docutils literal notranslate"><span class="pre">Character</span></code> class we implement all attributes we want to simulate from the <em>Knave</em> ruleset.
|
||||
The <code class="docutils literal notranslate"><span class="pre">AttributeProperty</span></code> is one way to add an Attribute in a field-like way; these will be accessible on every character in several ways:</p>
|
||||
<ul class="simple">
|
||||
<li><p>As <code class="docutils literal notranslate"><span class="pre">character.strength</span></code></p></li>
|
||||
<li><p>As <code class="docutils literal notranslate"><span class="pre">character.db.strength</span></code></p></li>
|
||||
<li><p>As <code class="docutils literal notranslate"><span class="pre">character.attributes.get("strength")</span></code></p></li>
|
||||
</ul>
|
||||
<p>See <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attributes</span></a> for seeing how Attributes work.</p>
|
||||
<p>Unlike in base <em>Knave</em>, we store <code class="docutils literal notranslate"><span class="pre">coins</span></code> as a separate Attribute rather than as items in the inventory, this makes it easier to handle barter and trading later.</p>
|
||||
<p>We implement the Player Character versions of <code class="docutils literal notranslate"><span class="pre">at_defeat</span></code> and <code class="docutils literal notranslate"><span class="pre">at_death</span></code>. We also make use of <code class="docutils literal notranslate"><span class="pre">.heal()</span></code> from the <code class="docutils literal notranslate"><span class="pre">LivingMixin</span></code> class.</p>
|
||||
<section id="funcparser-inlines">
|
||||
<h3><span class="section-number">3.3.1. </span>Funcparser inlines<a class="headerlink" href="#funcparser-inlines" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This piece of code is worth some more explanation:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
|
||||
<span class="s2">"$You() $conj(collapse) in a heap, alive but beaten."</span><span class="p">,</span>
|
||||
<span class="n">from_obj</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Remember that <code class="docutils literal notranslate"><span class="pre">self</span></code> is the Character instance here. So <code class="docutils literal notranslate"><span class="pre">self.location.msg_contents</span></code> means “send a message to everything inside my current location”. In other words, send a message to everyone in the same place as the character.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">$You()</span> <span class="pre">$conj(collapse)</span></code> are <a class="reference internal" href="../../../Components/FuncParser.html"><span class="doc std std-doc">FuncParser inlines</span></a>. These are functions that
|
||||
execute in the string. The resulting string may look different for different audiences. The <code class="docutils literal notranslate"><span class="pre">$You()</span></code> inline function will use <code class="docutils literal notranslate"><span class="pre">from_obj</span></code> to figure out who ‘you’ are and either show your name or ‘You’. The <code class="docutils literal notranslate"><span class="pre">$conj()</span></code> (verb conjugator) will tweak the (English) verb to match.</p>
|
||||
<ul class="simple">
|
||||
<li><p>You will see: <code class="docutils literal notranslate"><span class="pre">"You</span> <span class="pre">collapse</span> <span class="pre">in</span> <span class="pre">a</span> <span class="pre">heap,</span> <span class="pre">alive</span> <span class="pre">but</span> <span class="pre">beaten."</span></code></p></li>
|
||||
<li><p>Others in the room will see: <code class="docutils literal notranslate"><span class="pre">"Thomas</span> <span class="pre">collapses</span> <span class="pre">in</span> <span class="pre">a</span> <span class="pre">heap,</span> <span class="pre">alive</span> <span class="pre">but</span> <span class="pre">beaten."</span></code></p></li>
|
||||
</ul>
|
||||
<p>Note how <code class="docutils literal notranslate"><span class="pre">$conj()</span></code> chose <code class="docutils literal notranslate"><span class="pre">collapse/collapses</span></code> to make the sentences grammatically correct.</p>
|
||||
</section>
|
||||
<section id="backtracking">
|
||||
<h3><span class="section-number">3.3.2. </span>Backtracking<a class="headerlink" href="#backtracking" title="Permalink to this headline">¶</a></h3>
|
||||
<p>We make our first use of the <code class="docutils literal notranslate"><span class="pre">rules.dice</span></code> roller to roll on the death table! As you may recall, in the previous lesson, we didn’t know just what to do when rolling ‘dead’ on this table. Now we know - we should be calling <code class="docutils literal notranslate"><span class="pre">at_death</span></code> on the character. So let’s add that where we had TODOs before:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/rules.py </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll_death</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">character</span><span class="p">):</span>
|
||||
<span class="n">ability_name</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d8"</span><span class="p">,</span> <span class="n">death_table</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">ability_name</span> <span class="o">==</span> <span class="s2">"dead"</span><span class="p">:</span>
|
||||
<span class="c1"># kill the character!</span>
|
||||
<span class="n">character</span><span class="o">.</span><span class="n">at_death</span><span class="p">()</span> <span class="c1"># <------ TODO no more</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">if</span> <span class="n">current_ability</span> <span class="o"><</span> <span class="o">-</span><span class="mi">10</span><span class="p">:</span>
|
||||
<span class="c1"># kill the character!</span>
|
||||
<span class="n">character</span><span class="o">.</span><span class="n">at_death</span><span class="p">()</span> <span class="c1"># <------- TODO no more</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># ... </span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section id="connecting-the-character-with-evennia">
|
||||
<h2><span class="section-number">3.4. </span>Connecting the Character with Evennia<a class="headerlink" href="#connecting-the-character-with-evennia" title="Permalink to this headline">¶</a></h2>
|
||||
<p>You can easily make yourself an <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> in-game by using the
|
||||
<code class="docutils literal notranslate"><span class="pre">type</span></code> command:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>type self = evadventure.characters.EvAdventureCharacter
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can now do <code class="docutils literal notranslate"><span class="pre">examine</span> <span class="pre">self</span></code> to check your type updated.</p>
|
||||
<p>If you want <em>all</em> new Characters to be of this type you need to tell Evennia about it. Evennia
|
||||
uses a global setting <code class="docutils literal notranslate"><span class="pre">BASE_CHARACTER_TYPECLASS</span></code> to know which typeclass to use when creating
|
||||
Characters (when logging in, for example). This defaults to <code class="docutils literal notranslate"><span class="pre">typeclasses.characters.Character</span></code> (that is,
|
||||
the <code class="docutils literal notranslate"><span class="pre">Character</span></code> class in <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/characters.py</span></code>).</p>
|
||||
<p>There are thus two ways to weave your new Character class into Evennia:</p>
|
||||
<ol class="simple">
|
||||
<li><p>Change <code class="docutils literal notranslate"><span class="pre">mygame/server/conf/settings.py</span></code> and add <code class="docutils literal notranslate"><span class="pre">BASE_CHARACTER_TYPECLASS</span> <span class="pre">=</span> <span class="pre">"evadventure.characters.EvAdventureCharacter"</span></code>.</p></li>
|
||||
<li><p>Or, change <code class="docutils literal notranslate"><span class="pre">typeclasses.characters.Character</span></code> to inherit from <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code>.</p></li>
|
||||
</ol>
|
||||
<p>You must always reload the server for changes like this to take effect.</p>
|
||||
<div class="admonition important">
|
||||
<p class="admonition-title">Important</p>
|
||||
<p>In this tutorial we are making all changes in a folder <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/</span></code>. This means we can isolate
|
||||
our code but means we need to do some extra steps to tie the character (and other objects) into Evennia.
|
||||
For your own game it would be just fine to start editing <code class="docutils literal notranslate"><span class="pre">mygame/typeclasses/characters.py</span></code> directly
|
||||
instead.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section id="unit-testing">
|
||||
<h2><span class="section-number">3.5. </span>Unit Testing<a class="headerlink" href="#unit-testing" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/tests/test_characters.py</span></code></p>
|
||||
</div></blockquote>
|
||||
<p>For testing, we just need to create a new EvAdventure character and check
|
||||
that calling the methods on it doesn’t error out.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/tests/test_characters.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">create</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">BaseEvenniaTest</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">..characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">TestCharacters</span><span class="p">(</span><span class="n">BaseEvenniaTest</span><span class="p">):</span>
|
||||
<span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">character</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"testchar"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">test_heal</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">character</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="mi">0</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">hp_max</span> <span class="o">=</span> <span class="mi">8</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">heal</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">hp</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||||
<span class="c1"># make sure we can't heal more than max</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">heal</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">hp</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">test_at_pay</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">character</span><span class="o">.</span><span class="n">coins</span> <span class="o">=</span> <span class="mi">100</span>
|
||||
|
||||
<span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">at_pay</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="mi">60</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">coins</span><span class="p">,</span> <span class="mi">40</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># can't get more coins than we have </span>
|
||||
<span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">at_pay</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="mi">40</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">coins</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># tests for other methods ... </span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you followed the previous lessons, these tests should look familiar. Consider adding
|
||||
tests for other methods as practice. Refer to previous lessons for details.</p>
|
||||
<p>For running the tests you do:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> evennia test --settings settings.py .evadventure.tests.test_character
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="about-races-and-classes">
|
||||
<h2><span class="section-number">3.6. </span>About races and classes<a class="headerlink" href="#about-races-and-classes" title="Permalink to this headline">¶</a></h2>
|
||||
<p><em>Knave</em> doesn’t have any D&D-style <em>classes</em> (like Thief, Fighter etc). It also does not bother with
|
||||
<em>races</em> (like dwarves, elves etc). This makes the tutorial shorter, but you may ask yourself how you’d
|
||||
add these functions.</p>
|
||||
<p>In the framework we have sketched out for <em>Knave</em>, it would be simple - you’d add your race/class as
|
||||
an Attribute on your Character:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/characters.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span><span class="p">,</span> <span class="n">AttributeProperty</span>
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="n">charclass</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="s2">"Fighter"</span><span class="p">)</span>
|
||||
<span class="n">charrace</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="s2">"Human"</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We use <code class="docutils literal notranslate"><span class="pre">charclass</span></code> rather than <code class="docutils literal notranslate"><span class="pre">class</span></code> here, because <code class="docutils literal notranslate"><span class="pre">class</span></code> is a reserved Python keyword. Naming
|
||||
<code class="docutils literal notranslate"><span class="pre">race</span></code> as <code class="docutils literal notranslate"><span class="pre">charrace</span></code> thus matches in style.</p>
|
||||
<p>We’d then need to expand our <a class="reference internal" href="Beginner-Tutorial-Rules.html"><span class="doc std std-doc">rules module</span></a> (and later
|
||||
<a class="reference internal" href="Beginner-Tutorial-Chargen.html"><span class="doc std std-doc">character generation</span></a> to check and include what these classes mean.</p>
|
||||
</section>
|
||||
<section id="summary">
|
||||
<h2><span class="section-number">3.7. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline">¶</a></h2>
|
||||
<p>With the <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> class in place, we have a better understanding of how our PCs will look
|
||||
like under <em>Knave</em>.</p>
|
||||
<p>For now, we only have bits and pieces and haven’t been testing this code in-game. But if you want
|
||||
you can swap yourself into <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> right now. Log into your game and run
|
||||
the command</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>type self = evadventure.characters.EvAdventureCharacter
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If all went well, <code class="docutils literal notranslate"><span class="pre">ex</span> <span class="pre">self</span></code> will now show your typeclass as being <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code>.
|
||||
Check out your strength with</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>py self.strength = 3
|
||||
</pre></div>
|
||||
</div>
|
||||
<div class="admonition important">
|
||||
<p class="admonition-title">Important</p>
|
||||
<p>When doing <code class="docutils literal notranslate"><span class="pre">ex</span> <span class="pre">self</span></code> you will <em>not</em> see all your Abilities listed yet. That’s because
|
||||
Attributes added with <code class="docutils literal notranslate"><span class="pre">AttributeProperty</span></code> are not available until they have been accessed at
|
||||
least once. So once you set (or look at) <code class="docutils literal notranslate"><span class="pre">.strength</span></code> above, <code class="docutils literal notranslate"><span class="pre">strength</span></code> will show in <code class="docutils literal notranslate"><span class="pre">examine</span></code> from
|
||||
then on.</p>
|
||||
</div>
|
||||
</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="Beginner-Tutorial-Objects.html" title="4. In-game Objects and items"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Rules.html" title="2. Rules and dice rolling"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">3. </span>Player Characters</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,778 @@
|
|||
|
||||
<!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>6. Character Generation — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="7. In-game Rooms" href="Beginner-Tutorial-Rooms.html" />
|
||||
<link rel="prev" title="5. Handling Equipment" href="Beginner-Tutorial-Equipment.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Rooms.html" title="7. In-game Rooms"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Equipment.html" title="5. Handling Equipment"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">6. </span>Character Generation</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">6. Character Generation</a><ul>
|
||||
<li><a class="reference internal" href="#how-it-will-work">6.1. How it will work</a></li>
|
||||
<li><a class="reference internal" href="#random-tables">6.2. Random tables</a></li>
|
||||
<li><a class="reference internal" href="#storing-state-of-the-menu">6.3. Storing state of the menu</a><ul>
|
||||
<li><a class="reference internal" href="#showing-the-sheet">6.3.1. Showing the sheet</a></li>
|
||||
<li><a class="reference internal" href="#apply-character">6.3.2. Apply character</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#initializing-evmenu">6.4. Initializing EvMenu</a></li>
|
||||
<li><a class="reference internal" href="#main-node-choosing-what-to-do">6.5. Main Node: Choosing what to do</a></li>
|
||||
<li><a class="reference internal" href="#node-changing-your-name">6.6. Node: Changing your name</a></li>
|
||||
<li><a class="reference internal" href="#node-swapping-abilities-around">6.7. Node: Swapping Abilities around</a></li>
|
||||
<li><a class="reference internal" href="#node-creating-the-character">6.8. Node: Creating the Character</a></li>
|
||||
<li><a class="reference internal" href="#tying-the-nodes-together">6.9. Tying the nodes together</a></li>
|
||||
<li><a class="reference internal" href="#conclusions">6.10. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Equipment.html"
|
||||
title="previous chapter"><span class="section-number">5. </span>Handling Equipment</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Rooms.html"
|
||||
title="next chapter"><span class="section-number">7. </span>In-game Rooms</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Chargen.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="character-generation">
|
||||
<h1><span class="section-number">6. </span>Character Generation<a class="headerlink" href="#character-generation" title="Permalink to this headline">¶</a></h1>
|
||||
<p>In previous lessons we have established how a character looks. Now we need to give the player a
|
||||
chance to create one.</p>
|
||||
<section id="how-it-will-work">
|
||||
<h2><span class="section-number">6.1. </span>How it will work<a class="headerlink" href="#how-it-will-work" title="Permalink to this headline">¶</a></h2>
|
||||
<p>A fresh Evennia install will automatically create a new Character with the same name as your
|
||||
Account when you log in. This is quick and simple and mimics older MUD styles. You could picture
|
||||
doing this, and then customizing the Character in-place.</p>
|
||||
<p>We will be a little more sophisticated though. We want the user to be able to create a character
|
||||
using a menu when they log in.</p>
|
||||
<p>We do this by editing <code class="docutils literal notranslate"><span class="pre">mygame/server/conf/settings.py</span></code> and adding the line</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>AUTO_CREATE_CHARACTER_WITH_ACCOUNT = False
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>When doing this, connecting with the game with a new account will land you in “OOC” mode. The ooc-version of <code class="docutils literal notranslate"><span class="pre">look</span></code> (sitting in the Account cmdset) will show a list of available characters if you have any. You can also enter <code class="docutils literal notranslate"><span class="pre">charcreate</span></code> to make a new character. The <code class="docutils literal notranslate"><span class="pre">charcreate</span></code> is a simple command coming with Evennia that just lets you make a new character with a given name and description. We will later modify that to kick off our chargen. For now we’ll just keep in mind that’s how we’ll start off the menu.</p>
|
||||
<p>In <em>Knave</em>, most of the character-generation is random. This means this tutorial can be pretty
|
||||
compact while still showing the basic idea. What we will create is a menu looking like this:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Silas</span>
|
||||
|
||||
<span class="n">STR</span> <span class="o">+</span><span class="mi">1</span>
|
||||
<span class="n">DEX</span> <span class="o">+</span><span class="mi">2</span>
|
||||
<span class="n">CON</span> <span class="o">+</span><span class="mi">1</span>
|
||||
<span class="n">INT</span> <span class="o">+</span><span class="mi">3</span>
|
||||
<span class="n">WIS</span> <span class="o">+</span><span class="mi">1</span>
|
||||
<span class="n">CHA</span> <span class="o">+</span><span class="mi">2</span>
|
||||
|
||||
<span class="n">You</span> <span class="n">are</span> <span class="n">lanky</span> <span class="k">with</span> <span class="n">a</span> <span class="n">sunken</span> <span class="n">face</span> <span class="ow">and</span> <span class="n">filthy</span> <span class="n">hair</span><span class="p">,</span> <span class="n">breathy</span> <span class="n">speech</span><span class="p">,</span> <span class="ow">and</span> <span class="n">foreign</span> <span class="n">clothing</span><span class="o">.</span>
|
||||
<span class="n">You</span> <span class="n">were</span> <span class="n">a</span> <span class="n">herbalist</span><span class="p">,</span> <span class="n">but</span> <span class="n">you</span> <span class="n">were</span> <span class="n">pursued</span> <span class="ow">and</span> <span class="n">ended</span> <span class="n">up</span> <span class="n">a</span> <span class="n">knave</span><span class="o">.</span> <span class="n">You</span> <span class="n">are</span> <span class="n">honest</span> <span class="n">but</span> <span class="n">also</span>
|
||||
<span class="n">suspicious</span><span class="o">.</span> <span class="n">You</span> <span class="n">are</span> <span class="n">of</span> <span class="n">the</span> <span class="n">neutral</span> <span class="n">alignment</span><span class="o">.</span>
|
||||
|
||||
<span class="n">Your</span> <span class="n">belongings</span><span class="p">:</span>
|
||||
<span class="n">Brigandine</span> <span class="n">armor</span><span class="p">,</span> <span class="n">ration</span><span class="p">,</span> <span class="n">ration</span><span class="p">,</span> <span class="n">sword</span><span class="p">,</span> <span class="n">torch</span><span class="p">,</span> <span class="n">torch</span><span class="p">,</span> <span class="n">torch</span><span class="p">,</span> <span class="n">torch</span><span class="p">,</span> <span class="n">torch</span><span class="p">,</span>
|
||||
<span class="n">tinderbox</span><span class="p">,</span> <span class="n">chisel</span><span class="p">,</span> <span class="n">whistle</span>
|
||||
|
||||
<span class="o">----------------------------------------------------------------------------------------</span>
|
||||
<span class="mf">1.</span> <span class="n">Change</span> <span class="n">your</span> <span class="n">name</span>
|
||||
<span class="mf">2.</span> <span class="n">Swap</span> <span class="n">two</span> <span class="n">of</span> <span class="n">your</span> <span class="n">ability</span> <span class="n">scores</span> <span class="p">(</span><span class="n">once</span><span class="p">)</span>
|
||||
<span class="mf">3.</span> <span class="n">Accept</span> <span class="ow">and</span> <span class="n">create</span> <span class="n">character</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you select 1, you get a new menu node:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Your</span> <span class="n">current</span> <span class="n">name</span> <span class="ow">is</span> <span class="n">Silas</span><span class="o">.</span> <span class="n">Enter</span> <span class="n">a</span> <span class="n">new</span> <span class="n">name</span> <span class="ow">or</span> <span class="n">leave</span> <span class="n">empty</span> <span class="n">to</span> <span class="n">abort</span><span class="o">.</span>
|
||||
<span class="o">-----------------------------------------------------------------------------------------</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can now enter a new name. When pressing return you’ll get back to the first menu node
|
||||
showing your character, now with the new name.</p>
|
||||
<p>If you select 2, you go to another menu node:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>Your current abilities:
|
||||
|
||||
STR +1
|
||||
DEX +2
|
||||
CON +1
|
||||
INT +3
|
||||
WIS +1
|
||||
CHA +2
|
||||
|
||||
You can swap the values of two abilities around.
|
||||
You can only do this once, so choose carefully!
|
||||
|
||||
To swap the values of e.g. STR and INT, write 'STR INT'. Empty to abort.
|
||||
------------------------------------------------------------------------------------------
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you enter <code class="docutils literal notranslate"><span class="pre">WIS</span> <span class="pre">CHA</span></code> here, WIS will become <code class="docutils literal notranslate"><span class="pre">+2</span></code> and <code class="docutils literal notranslate"><span class="pre">CHA</span></code> <code class="docutils literal notranslate"><span class="pre">+1</span></code>. You will then again go back
|
||||
to the main node to see your new character, but this time the option to swap will no longer be
|
||||
available (you can only do it once).</p>
|
||||
<p>If you finally select the <code class="docutils literal notranslate"><span class="pre">Accept</span> <span class="pre">and</span> <span class="pre">create</span> <span class="pre">character</span></code> option, the character will be created
|
||||
and you’ll leave the menu;</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Character was created!
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="random-tables">
|
||||
<h2><span class="section-number">6.2. </span>Random tables<a class="headerlink" href="#random-tables" title="Permalink to this headline">¶</a></h2>
|
||||
<aside class="sidebar">
|
||||
<p>Full Knave random tables are found in
|
||||
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.random_tables.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/random_tables.py</span></a>.</p>
|
||||
</aside>
|
||||
<blockquote>
|
||||
<div><p>Make a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/random_tables.py</span></code>.</p>
|
||||
</div></blockquote>
|
||||
<p>Since most of <em>Knave</em>’s character generation is random we will need to roll on random tables
|
||||
from the <em>Knave</em> rulebook. While we added the ability to roll on a random table back in the
|
||||
<a class="reference internal" href="Beginner-Tutorial-Rules.html"><span class="doc std std-doc">Rules Tutorial</span></a>, we haven’t added the relevant tables yet.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/random_tables.py </span>
|
||||
|
||||
<span class="n">chargen_tables</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"physique"</span><span class="p">:</span> <span class="p">[</span>
|
||||
<span class="s2">"athletic"</span><span class="p">,</span> <span class="s2">"brawny"</span><span class="p">,</span> <span class="s2">"corpulent"</span><span class="p">,</span> <span class="s2">"delicate"</span><span class="p">,</span> <span class="s2">"gaunt"</span><span class="p">,</span> <span class="s2">"hulking"</span><span class="p">,</span> <span class="s2">"lanky"</span><span class="p">,</span>
|
||||
<span class="s2">"ripped"</span><span class="p">,</span> <span class="s2">"rugged"</span><span class="p">,</span> <span class="s2">"scrawny"</span><span class="p">,</span> <span class="s2">"short"</span><span class="p">,</span> <span class="s2">"sinewy"</span><span class="p">,</span> <span class="s2">"slender"</span><span class="p">,</span> <span class="s2">"flabby"</span><span class="p">,</span>
|
||||
<span class="s2">"statuesque"</span><span class="p">,</span> <span class="s2">"stout"</span><span class="p">,</span> <span class="s2">"tiny"</span><span class="p">,</span> <span class="s2">"towering"</span><span class="p">,</span> <span class="s2">"willowy"</span><span class="p">,</span> <span class="s2">"wiry"</span><span class="p">,</span>
|
||||
<span class="p">],</span>
|
||||
<span class="s2">"face"</span><span class="p">:</span> <span class="p">[</span>
|
||||
<span class="s2">"bloated"</span><span class="p">,</span> <span class="s2">"blunt"</span><span class="p">,</span> <span class="s2">"bony"</span><span class="p">,</span> <span class="c1"># ... </span>
|
||||
<span class="p">],</span> <span class="c1"># ... </span>
|
||||
<span class="p">}</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The tables are just copied from the <em>Knave</em> rules. We group the aspects in a dict
|
||||
<code class="docutils literal notranslate"><span class="pre">character_generation</span></code> to separate chargen-only tables from other random tables we’ll also
|
||||
keep in here.</p>
|
||||
</section>
|
||||
<section id="storing-state-of-the-menu">
|
||||
<h2><span class="section-number">6.3. </span>Storing state of the menu<a class="headerlink" href="#storing-state-of-the-menu" title="Permalink to this headline">¶</a></h2>
|
||||
<aside class="sidebar">
|
||||
<p>There is a full implementation of the chargen in
|
||||
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.chargen.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/chargen.py</span></a>.</p>
|
||||
</aside>
|
||||
<blockquote>
|
||||
<div><p>create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/chargen.py</span></code>.</p>
|
||||
</div></blockquote>
|
||||
<p>During character generation we will need an entity to store/retain the changes, like a
|
||||
‘temporary character sheet’.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.random_tables</span> <span class="kn">import</span> <span class="n">chargen_tables</span>
|
||||
<span class="kn">from</span> <span class="nn">.rules</span> <span class="kn">import</span> <span class="n">dice</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">TemporaryCharacterSheet</span><span class="p">:</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_random_ability</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="nb">min</span><span class="p">(</span><span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d6"</span><span class="p">),</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d6"</span><span class="p">),</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d6"</span><span class="p">))</span>
|
||||
|
||||
<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="bp">self</span><span class="o">.</span><span class="n">ability_changes</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># how many times we tried swap abilities</span>
|
||||
|
||||
<span class="c1"># name will likely be modified later</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d282"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"name"</span><span class="p">])</span>
|
||||
|
||||
<span class="c1"># base attribute values</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">strength</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_random_ability</span><span class="p">()</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">dexterity</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_random_ability</span><span class="p">()</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">constitution</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_random_ability</span><span class="p">()</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">intelligence</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_random_ability</span><span class="p">()</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">wisdom</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_random_ability</span><span class="p">()</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">charisma</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_random_ability</span><span class="p">()</span>
|
||||
|
||||
<span class="c1"># physical attributes (only for rp purposes)</span>
|
||||
<span class="n">physique</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"physique"</span><span class="p">])</span>
|
||||
<span class="n">face</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"face"</span><span class="p">])</span>
|
||||
<span class="n">skin</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"skin"</span><span class="p">])</span>
|
||||
<span class="n">hair</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"hair"</span><span class="p">])</span>
|
||||
<span class="n">clothing</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"clothing"</span><span class="p">])</span>
|
||||
<span class="n">speech</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"speech"</span><span class="p">])</span>
|
||||
<span class="n">virtue</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"virtue"</span><span class="p">])</span>
|
||||
<span class="n">vice</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"vice"</span><span class="p">])</span>
|
||||
<span class="n">background</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"background"</span><span class="p">])</span>
|
||||
<span class="n">misfortune</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"misfortune"</span><span class="p">])</span>
|
||||
<span class="n">alignment</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"alignment"</span><span class="p">])</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">desc</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"You are </span><span class="si">{</span><span class="n">physique</span><span class="si">}</span><span class="s2"> with a </span><span class="si">{</span><span class="n">face</span><span class="si">}</span><span class="s2"> face, </span><span class="si">{</span><span class="n">skin</span><span class="si">}</span><span class="s2"> skin, </span><span class="si">{</span><span class="n">hair</span><span class="si">}</span><span class="s2"> hair, </span><span class="si">{</span><span class="n">speech</span><span class="si">}</span><span class="s2"> speech,"</span>
|
||||
<span class="sa">f</span><span class="s2">" and </span><span class="si">{</span><span class="n">clothing</span><span class="si">}</span><span class="s2"> clothing. You were a </span><span class="si">{</span><span class="n">background</span><span class="o">.</span><span class="n">title</span><span class="p">()</span><span class="si">}</span><span class="s2">, but you were"</span>
|
||||
<span class="sa">f</span><span class="s2">" </span><span class="si">{</span><span class="n">misfortune</span><span class="si">}</span><span class="s2"> and ended up a knave. You are </span><span class="si">{</span><span class="n">virtue</span><span class="si">}</span><span class="s2"> but also </span><span class="si">{</span><span class="n">vice</span><span class="si">}</span><span class="s2">. You are of the"</span>
|
||||
<span class="sa">f</span><span class="s2">" </span><span class="si">{</span><span class="n">alignment</span><span class="si">}</span><span class="s2"> alignment."</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="c1"># </span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d8"</span><span class="p">))</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">xp</span> <span class="o">=</span> <span class="mi">0</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">level</span> <span class="o">=</span> <span class="mi">1</span>
|
||||
|
||||
<span class="c1"># random equipment</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">armor</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"armor"</span><span class="p">])</span>
|
||||
|
||||
<span class="n">_helmet_and_shield</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"helmets and shields"</span><span class="p">])</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">helmet</span> <span class="o">=</span> <span class="s2">"helmet"</span> <span class="k">if</span> <span class="s2">"helmet"</span> <span class="ow">in</span> <span class="n">_helmet_and_shield</span> <span class="k">else</span> <span class="s2">"none"</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">shield</span> <span class="o">=</span> <span class="s2">"shield"</span> <span class="k">if</span> <span class="s2">"shield"</span> <span class="ow">in</span> <span class="n">_helmet_and_shield</span> <span class="k">else</span> <span class="s2">"none"</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">weapon</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"starting weapon"</span><span class="p">])</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">backpack</span> <span class="o">=</span> <span class="p">[</span>
|
||||
<span class="s2">"ration"</span><span class="p">,</span>
|
||||
<span class="s2">"ration"</span><span class="p">,</span>
|
||||
<span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"dungeoning gear"</span><span class="p">]),</span>
|
||||
<span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"dungeoning gear"</span><span class="p">]),</span>
|
||||
<span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"general gear 1"</span><span class="p">]),</span>
|
||||
<span class="n">dice</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">,</span> <span class="n">chargen_tables</span><span class="p">[</span><span class="s2">"general gear 2"</span><span class="p">]),</span>
|
||||
<span class="p">]</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here we have followed the <em>Knave</em> rulebook to randomize abilities, description and equipment. The <code class="docutils literal notranslate"><span class="pre">dice.roll()</span></code> and <code class="docutils literal notranslate"><span class="pre">dice.roll_random_table</span></code> methods now become very useful! Everything here should be easy to follow.</p>
|
||||
<p>The main difference from baseline <em>Knave</em> is that we make a table of “starting weapon” (in Knave you can pick whatever you like).</p>
|
||||
<p>We also initialize <code class="docutils literal notranslate"><span class="pre">.ability_changes</span> <span class="pre">=</span> <span class="pre">0</span></code>. Knave only allows us to swap the values of two
|
||||
Abilities <em>once</em>. We will use this to know if it has been done or not.</p>
|
||||
<section id="showing-the-sheet">
|
||||
<h3><span class="section-number">6.3.1. </span>Showing the sheet<a class="headerlink" href="#showing-the-sheet" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Now that we have our temporary character sheet, we should make it easy to visualize it.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py </span>
|
||||
|
||||
<span class="n">_TEMP_SHEET</span> <span class="o">=</span> <span class="s2">"""</span>
|
||||
<span class="si">{name}</span>
|
||||
|
||||
<span class="s2">STR +</span><span class="si">{strength}</span>
|
||||
<span class="s2">DEX +</span><span class="si">{dexterity}</span>
|
||||
<span class="s2">CON +</span><span class="si">{constitution}</span>
|
||||
<span class="s2">INT +</span><span class="si">{intelligence}</span>
|
||||
<span class="s2">WIS +</span><span class="si">{wisdom}</span>
|
||||
<span class="s2">CHA +</span><span class="si">{charisma}</span>
|
||||
|
||||
<span class="si">{description}</span>
|
||||
<span class="s2"> </span>
|
||||
<span class="s2">Your belongings:</span>
|
||||
<span class="si">{equipment}</span>
|
||||
<span class="s2">"""</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">TemporaryCharacterSheet</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">show_sheet</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="n">equipment</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="nb">str</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
|
||||
<span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">armor</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">shield</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">weapon</span><span class="p">]</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">backpack</span>
|
||||
<span class="k">if</span> <span class="n">item</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="k">return</span> <span class="n">_TEMP_SHEET</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
|
||||
<span class="n">name</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
|
||||
<span class="n">strength</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">strength</span><span class="p">,</span>
|
||||
<span class="n">dexterity</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">dexterity</span><span class="p">,</span>
|
||||
<span class="n">constitution</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">constitution</span><span class="p">,</span>
|
||||
<span class="n">intelligence</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">intelligence</span><span class="p">,</span>
|
||||
<span class="n">wisdom</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">wisdom</span><span class="p">,</span>
|
||||
<span class="n">charisma</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">charisma</span><span class="p">,</span>
|
||||
<span class="n">description</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">desc</span><span class="p">,</span>
|
||||
<span class="n">equipment</span><span class="o">=</span><span class="s2">", "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">equipment</span><span class="p">),</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The new <code class="docutils literal notranslate"><span class="pre">show_sheet</span></code> method collect the data from the temporary sheet and return it in a pretty form. Making a ‘template’ string like <code class="docutils literal notranslate"><span class="pre">_TEMP_SHEET</span></code> makes it easier to change things later if you want to change how things look.</p>
|
||||
</section>
|
||||
<section id="apply-character">
|
||||
<h3><span class="section-number">6.3.2. </span>Apply character<a class="headerlink" href="#apply-character" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Once we are happy with our character, we need to actually create it with the stats we chose.
|
||||
This is a bit more involved.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_object</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="k">class</span> <span class="nc">TemporaryCharacterSheet</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">apply</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="c1"># create character object with given abilities</span>
|
||||
<span class="n">new_character</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span>
|
||||
<span class="n">EvAdventureCharacter</span><span class="p">,</span>
|
||||
<span class="n">key</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
|
||||
<span class="n">attrs</span><span class="o">=</span><span class="p">(</span>
|
||||
<span class="p">(</span><span class="s2">"strength"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">strength</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"dexterity"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">dexterity</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"constitution"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">constitution</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"intelligence"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">intelligence</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"wisdom"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">wisdom</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"charisma"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">wisdom</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"hp"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"hp_max"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"desc"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">desc</span><span class="p">),</span>
|
||||
<span class="p">),</span>
|
||||
<span class="p">)</span>
|
||||
<span class="c1"># spawn equipment (will require prototypes created before it works)</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">weapon</span><span class="p">:</span>
|
||||
<span class="n">weapon</span> <span class="o">=</span> <span class="n">spawn</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">weapon</span><span class="p">)</span>
|
||||
<span class="n">new_character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="n">weapon</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">shield</span><span class="p">:</span>
|
||||
<span class="n">shield</span> <span class="o">=</span> <span class="n">spawn</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">shield</span><span class="p">)</span>
|
||||
<span class="n">new_character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="n">shield</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">armor</span><span class="p">:</span>
|
||||
<span class="n">armor</span> <span class="o">=</span> <span class="n">spawn</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">armor</span><span class="p">)</span>
|
||||
<span class="n">new_character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="n">armor</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">:</span>
|
||||
<span class="n">helmet</span> <span class="o">=</span> <span class="n">spawn</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">)</span>
|
||||
<span class="n">new_character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="n">helmet</span><span class="p">)</span>
|
||||
|
||||
<span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">backpack</span><span class="p">:</span>
|
||||
<span class="n">item</span> <span class="o">=</span> <span class="n">spawn</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
|
||||
<span class="n">new_character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">store</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
|
||||
|
||||
<span class="k">return</span> <span class="n">new_character</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We use <code class="docutils literal notranslate"><span class="pre">create_object</span></code> to create a new <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code>. We feed it with all relevant data from the temporary character sheet. This is when these become an actual character.</p>
|
||||
<aside class="sidebar">
|
||||
<p>A prototype is basically a <code class="docutils literal notranslate"><span class="pre">dict</span></code> describing how the object should be created. Since
|
||||
it’s just a piece of code, it can stored in a Python module and used to quickly <em>spawn</em> (create)
|
||||
things from those prototypes.</p>
|
||||
</aside>
|
||||
<p>Each piece of equipment is an object in in its own right. We will here assume that all game
|
||||
items are defined as <a class="reference internal" href="../../../Components/Prototypes.html"><span class="doc std std-doc">Prototypes</span></a> keyed to its name, such as “sword”, “brigandine
|
||||
armor” etc.</p>
|
||||
<p>We haven’t actually created those prototypes yet, so for now we’ll need to assume they are there. Once a piece of equipment has been spawned, we make sure to move it into the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> we created in the <a class="reference internal" href="Beginner-Tutorial-Equipment.html"><span class="doc std std-doc">Equipment lesson</span></a>.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="initializing-evmenu">
|
||||
<h2><span class="section-number">6.4. </span>Initializing EvMenu<a class="headerlink" href="#initializing-evmenu" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Evennia comes with a full menu-generation system based on <a class="reference internal" href="../../../Components/Command-Sets.html"><span class="doc std std-doc">Command sets</span></a>, called
|
||||
<a class="reference internal" href="../../../Components/EvMenu.html"><span class="doc std std-doc">EvMenu</span></a>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">EvMenu</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="c1"># chargen menu </span>
|
||||
|
||||
|
||||
<span class="c1"># this goes to the bottom of the module</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">start_chargen</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> This is a start point for spinning up the chargen from a command later.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="n">menutree</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># TODO!</span>
|
||||
|
||||
<span class="c1"># this generates all random components of the character</span>
|
||||
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">TemporaryCharacterSheet</span><span class="p">()</span>
|
||||
|
||||
<span class="n">EvMenu</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">menutree</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span> <span class="n">tmp_character</span><span class="o">=</span><span class="n">tmp_character</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This first function is what we will call from elsewhere (for example from a custom <code class="docutils literal notranslate"><span class="pre">charcreate</span></code>
|
||||
command) to kick the menu into gear.</p>
|
||||
<p>It takes the <code class="docutils literal notranslate"><span class="pre">caller</span></code> (the one to want to start the menu) and a <code class="docutils literal notranslate"><span class="pre">session</span></code> argument. The latter will help track just which client-connection we are using (depending on Evennia settings, you could be connecting with multiple clients).</p>
|
||||
<p>We create a <code class="docutils literal notranslate"><span class="pre">TemporaryCharacterSheet</span></code> and call <code class="docutils literal notranslate"><span class="pre">.generate()</span></code> to make a random character. We then feed all this into <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code>.</p>
|
||||
<p>The moment this happens, the user will be in the menu, there are no further steps needed.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">menutree</span></code> is what we’ll create next. It describes which menu ‘nodes’ are available to jump
|
||||
between.</p>
|
||||
</section>
|
||||
<section id="main-node-choosing-what-to-do">
|
||||
<h2><span class="section-number">6.5. </span>Main Node: Choosing what to do<a class="headerlink" href="#main-node-choosing-what-to-do" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This is the first menu node. It will act as a central hub, from which one can choose different
|
||||
actions.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py </span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="c1"># at the end of the module, but before the `start_chargen` function</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">node_chargen</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="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
|
||||
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"tmp_character"</span><span class="p">]</span>
|
||||
|
||||
<span class="n">text</span> <span class="o">=</span> <span class="n">tmp_character</span><span class="o">.</span><span class="n">show_sheet</span><span class="p">()</span>
|
||||
|
||||
<span class="n">options</span> <span class="o">=</span> <span class="p">[</span>
|
||||
<span class="p">{</span>
|
||||
<span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Change your name"</span><span class="p">,</span>
|
||||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"node_change_name"</span><span class="p">,</span> <span class="n">kwargs</span><span class="p">)</span>
|
||||
<span class="p">}</span>
|
||||
<span class="p">]</span>
|
||||
<span class="k">if</span> <span class="n">tmp_character</span><span class="o">.</span><span class="n">ability_changes</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">:</span>
|
||||
<span class="n">options</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
<span class="p">{</span>
|
||||
<span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Swap two of your ability scores (once)"</span><span class="p">,</span>
|
||||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"node_swap_abilities"</span><span class="p">,</span> <span class="n">kwargs</span><span class="p">),</span>
|
||||
<span class="p">}</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">options</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
|
||||
<span class="p">{</span>
|
||||
<span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"Accept and create character"</span><span class="p">,</span>
|
||||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"node_apply_character"</span><span class="p">,</span> <span class="n">kwargs</span><span class="p">)</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>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>A lot to unpack here! In Evennia, it’s convention to name your node-functions <code class="docutils literal notranslate"><span class="pre">node_*</span></code>. While
|
||||
not required, it helps you track what is a node and not.</p>
|
||||
<p>Every menu-node, should accept <code class="docutils literal notranslate"><span class="pre">caller,</span> <span class="pre">raw_string,</span> <span class="pre">**kwargs</span></code> as arguments. Here <code class="docutils literal notranslate"><span class="pre">caller</span></code> is the <code class="docutils literal notranslate"><span class="pre">caller</span></code> you passed into the <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> call. <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> is the input given by the user in order to <em>get to this node</em>, so currently empty. The <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> are all extra keyword arguments passed into <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code>. They can also be passed between nodes. In this case, we passed the keyword <code class="docutils literal notranslate"><span class="pre">tmp_character</span></code> to <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code>. We now have the temporary character sheet available in the node!</p>
|
||||
<p>An <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> node must always return two things - <code class="docutils literal notranslate"><span class="pre">text</span></code> and <code class="docutils literal notranslate"><span class="pre">options</span></code>. The <code class="docutils literal notranslate"><span class="pre">text</span></code> is what will
|
||||
show to the user when looking at this node. The <code class="docutils literal notranslate"><span class="pre">options</span></code> are, well, what options should be
|
||||
presented to move on from here to some other place.</p>
|
||||
<p>For the text, we simply get a pretty-print of the temporary character sheet. A single option is
|
||||
defined as a <code class="docutils literal notranslate"><span class="pre">dict</span></code> like this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"name"</span><span class="o">.</span> <span class="s2">"alias1"</span><span class="p">,</span> <span class="s2">"alias2"</span><span class="p">,</span> <span class="o">...</span><span class="p">),</span> <span class="c1"># if skipped, auto-show a number</span>
|
||||
<span class="s2">"desc"</span><span class="p">:</span> <span class="s2">"text to describe what happens when selecting option"</span><span class="p">,</span><span class="o">.</span>
|
||||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="s2">"name of node or a callable"</span><span class="p">,</span> <span class="n">kwargs_to_pass_into_next_node_or_callable</span><span class="p">)</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Multiple option-dicts are returned in a list or tuple. The <code class="docutils literal notranslate"><span class="pre">goto</span></code> option-key is important to
|
||||
understand. The job of this is to either point directly to another node (by giving its name), or
|
||||
by pointing to a Python callable (like a function) <em>that then returns that name</em>. You can also
|
||||
pass kwargs (as a dict). This will be made available as <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> in the callable or next node.</p>
|
||||
<p>While an option can have a <code class="docutils literal notranslate"><span class="pre">key</span></code>, you can also skip it to just get a running number.</p>
|
||||
<p>In our <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code> node, we point to three nodes by name: <code class="docutils literal notranslate"><span class="pre">node_change_name</span></code>,
|
||||
<code class="docutils literal notranslate"><span class="pre">node_swap_abilities</span></code>, and <code class="docutils literal notranslate"><span class="pre">node_apply_character</span></code>. We also make sure to pass along <code class="docutils literal notranslate"><span class="pre">kwargs</span></code>
|
||||
to each node, since that contains our temporary character sheet.</p>
|
||||
<p>The middle of these options only appear if we haven’t already switched two abilities around - to know this, we check the <code class="docutils literal notranslate"><span class="pre">.ability_changes</span></code> property to make sure it’s still 0.</p>
|
||||
</section>
|
||||
<section id="node-changing-your-name">
|
||||
<h2><span class="section-number">6.6. </span>Node: Changing your name<a class="headerlink" href="#node-changing-your-name" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This is where you end up if you opted to change your name in <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="c1"># after previous node </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_update_name</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="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Used by node_change_name below to check what user </span>
|
||||
<span class="sd"> entered and update the name if appropriate.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="n">raw_string</span><span class="p">:</span>
|
||||
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"tmp_character"</span><span class="p">]</span>
|
||||
<span class="n">tmp_character</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">raw_string</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="o">.</span><span class="n">capitalize</span><span class="p">()</span>
|
||||
|
||||
<span class="k">return</span> <span class="s2">"node_chargen"</span><span class="p">,</span> <span class="n">kwargs</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">node_change_name</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="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Change the random name of the character.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"tmp_character"</span><span class="p">]</span>
|
||||
|
||||
<span class="n">text</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"Your current name is |w</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">|n. "</span>
|
||||
<span class="s2">"Enter a new name or leave empty to abort."</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="n">options</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="s2">"_default"</span><span class="p">,</span>
|
||||
<span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_update_name</span><span class="p">,</span> <span class="n">kwargs</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>There are two functions here - the menu node itself (<code class="docutils literal notranslate"><span class="pre">node_change_name</span></code>) and a
|
||||
helper <em>goto_function</em> (<code class="docutils literal notranslate"><span class="pre">_update_name</span></code>) to handle the user’s input.</p>
|
||||
<p>For the (single) option, we use a special <code class="docutils literal notranslate"><span class="pre">key</span></code> named <code class="docutils literal notranslate"><span class="pre">_default</span></code>. This makes this option
|
||||
a catch-all: If the user enters something that does not match any other option, this is
|
||||
the option that will be used. Since we have no other options here, we will always use this option no matter what the user enters.</p>
|
||||
<p>Also note that the <code class="docutils literal notranslate"><span class="pre">goto</span></code> part of the option points to the <code class="docutils literal notranslate"><span class="pre">_update_name</span></code> callable rather than to
|
||||
the name of a node. It’s important we keep passing <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> along to it!</p>
|
||||
<p>When a user writes anything at this node, the <code class="docutils literal notranslate"><span class="pre">_update_name</span></code> callable will be called. This has
|
||||
the same arguments as a node, but it is <em>not</em> a node - we will only use it to <em>figure out</em> which
|
||||
node to go to next.</p>
|
||||
<p>In <code class="docutils literal notranslate"><span class="pre">_update_name</span></code> we now have a use for the <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> argument - this is what was written by the user on the previous node, remember? This is now either an empty string (meaning to ignore it) or the new name of the character.</p>
|
||||
<p>A goto-function like <code class="docutils literal notranslate"><span class="pre">_update_name</span></code> must return the name of the next node to use. It can also
|
||||
optionally return the <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> to pass into that node - we want to always do this, so we don’t
|
||||
loose our temporary character sheet. Here we will always go back to the <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code>.</p>
|
||||
<blockquote>
|
||||
<div><p>Hint: If returning <code class="docutils literal notranslate"><span class="pre">None</span></code> from a goto-callable, you will always return to the last node you
|
||||
were at.</p>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
<section id="node-swapping-abilities-around">
|
||||
<h2><span class="section-number">6.7. </span>Node: Swapping Abilities around<a class="headerlink" href="#node-swapping-abilities-around" title="Permalink to this headline">¶</a></h2>
|
||||
<p>You get here by selecting the second option from the <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code> node.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/chargen.py </span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="c1"># after previous node </span>
|
||||
|
||||
<span class="n">_ABILITIES</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"STR"</span><span class="p">:</span> <span class="s2">"strength"</span><span class="p">,</span>
|
||||
<span class="s2">"DEX"</span><span class="p">:</span> <span class="s2">"dexterity"</span><span class="p">,</span>
|
||||
<span class="s2">"CON"</span><span class="p">:</span> <span class="s2">"constitution"</span><span class="p">,</span>
|
||||
<span class="s2">"INT"</span><span class="p">:</span> <span class="s2">"intelligence"</span><span class="p">,</span>
|
||||
<span class="s2">"WIS"</span><span class="p">:</span> <span class="s2">"wisdom"</span><span class="p">,</span>
|
||||
<span class="s2">"CHA"</span><span class="p">:</span> <span class="s2">"charisma"</span><span class="p">,</span>
|
||||
<span class="p">}</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">_swap_abilities</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="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Used by node_swap_abilities to parse the user's input and swap ability</span>
|
||||
<span class="sd"> values.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="n">raw_string</span><span class="p">:</span>
|
||||
<span class="n">abi1</span><span class="p">,</span> <span class="o">*</span><span class="n">abi2</span> <span class="o">=</span> <span class="n">raw_string</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">abi2</span><span class="p">:</span>
|
||||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"That doesn't look right."</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="kc">None</span><span class="p">,</span> <span class="n">kwargs</span>
|
||||
<span class="n">abi2</span> <span class="o">=</span> <span class="n">abi2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
||||
<span class="n">abi1</span><span class="p">,</span> <span class="n">abi2</span> <span class="o">=</span> <span class="n">abi1</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">(),</span> <span class="n">abi2</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="n">abi1</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">_ABILITIES</span> <span class="ow">or</span> <span class="n">abi2</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">_ABILITIES</span><span class="p">:</span>
|
||||
<span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"Not a familiar set of abilites."</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="kc">None</span><span class="p">,</span> <span class="n">kwargs</span>
|
||||
|
||||
<span class="c1"># looks okay = swap values. We need to convert STR to strength etc</span>
|
||||
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"tmp_character"</span><span class="p">]</span>
|
||||
<span class="n">abi1</span> <span class="o">=</span> <span class="n">_ABILITIES</span><span class="p">[</span><span class="n">abi1</span><span class="p">]</span>
|
||||
<span class="n">abi2</span> <span class="o">=</span> <span class="n">_ABILITIES</span><span class="p">[</span><span class="n">abi2</span><span class="p">]</span>
|
||||
<span class="n">abival1</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">tmp_character</span><span class="p">,</span> <span class="n">abi1</span><span class="p">)</span>
|
||||
<span class="n">abival2</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">tmp_character</span><span class="p">,</span> <span class="n">abi2</span><span class="p">)</span>
|
||||
|
||||
<span class="nb">setattr</span><span class="p">(</span><span class="n">tmp_character</span><span class="p">,</span> <span class="n">abi1</span><span class="p">,</span> <span class="n">abival2</span><span class="p">)</span>
|
||||
<span class="nb">setattr</span><span class="p">(</span><span class="n">tmp_character</span><span class="p">,</span> <span class="n">abi2</span><span class="p">,</span> <span class="n">abival1</span><span class="p">)</span>
|
||||
|
||||
<span class="n">tmp_character</span><span class="o">.</span><span class="n">ability_changes</span> <span class="o">+=</span> <span class="mi">1</span>
|
||||
|
||||
<span class="k">return</span> <span class="s2">"node_chargen"</span><span class="p">,</span> <span class="n">kwargs</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">node_swap_abilities</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="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> One is allowed to swap the values of two abilities around, once.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"tmp_character"</span><span class="p">]</span>
|
||||
|
||||
<span class="n">text</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"""</span>
|
||||
<span class="s2">Your current abilities:</span>
|
||||
|
||||
<span class="s2">STR +</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">strength</span><span class="si">}</span>
|
||||
<span class="s2">DEX +</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">dexterity</span><span class="si">}</span>
|
||||
<span class="s2">CON +</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">constitution</span><span class="si">}</span>
|
||||
<span class="s2">INT +</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">intelligence</span><span class="si">}</span>
|
||||
<span class="s2">WIS +</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">wisdom</span><span class="si">}</span>
|
||||
<span class="s2">CHA +</span><span class="si">{</span><span class="n">tmp_character</span><span class="o">.</span><span class="n">charisma</span><span class="si">}</span>
|
||||
|
||||
<span class="s2">You can swap the values of two abilities around.</span>
|
||||
<span class="s2">You can only do this once, so choose carefully!</span>
|
||||
|
||||
<span class="s2">To swap the values of e.g. STR and INT, write |wSTR INT|n. Empty to abort.</span>
|
||||
<span class="s2">"""</span>
|
||||
|
||||
<span class="n">options</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"key"</span><span class="p">:</span> <span class="s2">"_default"</span><span class="p">,</span> <span class="s2">"goto"</span><span class="p">:</span> <span class="p">(</span><span class="n">_swap_abilities</span><span class="p">,</span> <span class="n">kwargs</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>This is more code, but the logic is the same - we have a node (<code class="docutils literal notranslate"><span class="pre">node_swap_abilities</span></code>) and
|
||||
and a goto-callable helper (<code class="docutils literal notranslate"><span class="pre">_swap_abilities</span></code>). We catch everything the user writes on the
|
||||
node (such as <code class="docutils literal notranslate"><span class="pre">WIS</span> <span class="pre">CON</span></code>) and feed it into the helper.</p>
|
||||
<p>In <code class="docutils literal notranslate"><span class="pre">_swap_abilities</span></code>, we need to analyze the <code class="docutils literal notranslate"><span class="pre">raw_string</span></code> from the user to see what they
|
||||
want to do.</p>
|
||||
<p>Most code in the helper is validating the user didn’t enter nonsense. If they did,
|
||||
we use <code class="docutils literal notranslate"><span class="pre">caller.msg()</span></code> to tell them and then return <code class="docutils literal notranslate"><span class="pre">None,</span> <span class="pre">kwargs</span></code>, which re-runs the same node (the name-selection) all over again.</p>
|
||||
<p>Since we want users to be able to write “CON” instead of the longer “constitution”, we need a mapping <code class="docutils literal notranslate"><span class="pre">_ABILITIES</span></code> to easily convert between the two (it’s stored as <code class="docutils literal notranslate"><span class="pre">consitution</span></code> on the temporary character sheet). Once we know which abilities they want to swap, we do so and tick up the <code class="docutils literal notranslate"><span class="pre">.ability_changes</span></code> counter. This means this option will no longer be available from the main node.</p>
|
||||
<p>Finally, we return to <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code> again.</p>
|
||||
</section>
|
||||
<section id="node-creating-the-character">
|
||||
<h2><span class="section-number">6.8. </span>Node: Creating the Character<a class="headerlink" href="#node-creating-the-character" title="Permalink to this headline">¶</a></h2>
|
||||
<p>We get here from the main node by opting to finish chargen.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">node_apply_character</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="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> End chargen and create the character. We will also puppet it.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">kwargs</span><span class="p">[</span><span class="s2">"tmp_character"</span><span class="p">]</span>
|
||||
<span class="n">new_character</span> <span class="o">=</span> <span class="n">tmp_character</span><span class="o">.</span><span class="n">apply</span><span class="p">(</span><span class="n">caller</span><span class="p">)</span>
|
||||
|
||||
<span class="n">caller</span><span class="o">.</span><span class="n">account</span><span class="o">.</span><span class="n">add_character</span><span class="p">(</span><span class="n">new_character</span><span class="p">)</span>
|
||||
|
||||
<span class="n">text</span> <span class="o">=</span> <span class="s2">"Character created!"</span>
|
||||
|
||||
<span class="k">return</span> <span class="n">text</span><span class="p">,</span> <span class="kc">None</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>When entering the node, we will take the Temporary character sheet and use its <code class="docutils literal notranslate"><span class="pre">.appy</span></code> method to create a new Character with all equipment.</p>
|
||||
<p>This is what is called an <em>end node</em>, because it returns <code class="docutils literal notranslate"><span class="pre">None</span></code> instead of options. After this, the menu will exit. We will be back to the default character selection screen. The characters found on that screen are the ones listed in the <code class="docutils literal notranslate"><span class="pre">_playable_characters</span></code> Attribute, so we need to also the new character to it.</p>
|
||||
</section>
|
||||
<section id="tying-the-nodes-together">
|
||||
<h2><span class="section-number">6.9. </span>Tying the nodes together<a class="headerlink" href="#tying-the-nodes-together" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">start_chargen</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="sd">This is a start point for spinning up the chargen from a command later.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">menutree</span> <span class="o">=</span> <span class="p">{</span> <span class="c1"># <----- can now add this!</span>
|
||||
<span class="s2">"node_chargen"</span><span class="p">:</span> <span class="n">node_chargen</span><span class="p">,</span>
|
||||
<span class="s2">"node_change_name"</span><span class="p">:</span> <span class="n">node_change_name</span><span class="p">,</span>
|
||||
<span class="s2">"node_swap_abilities"</span><span class="p">:</span> <span class="n">node_swap_abilities</span><span class="p">,</span>
|
||||
<span class="s2">"node_apply_character"</span><span class="p">:</span> <span class="n">node_apply_character</span>
|
||||
<span class="p">}</span>
|
||||
|
||||
<span class="c1"># this generates all random components of the character</span>
|
||||
<span class="n">tmp_character</span> <span class="o">=</span> <span class="n">TemporaryCharacterSheet</span><span class="p">()</span>
|
||||
|
||||
<span class="n">EvMenu</span><span class="p">(</span><span class="n">caller</span><span class="p">,</span> <span class="n">menutree</span><span class="p">,</span> <span class="n">session</span><span class="o">=</span><span class="n">session</span><span class="p">,</span>
|
||||
<span class="n">startnode</span><span class="o">=</span><span class="s2">"node_chargen"</span><span class="p">,</span> <span class="c1"># <----- </span>
|
||||
<span class="n">tmp_character</span><span class="o">=</span><span class="n">tmp_character</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now that we have all the nodes, we add them to the <code class="docutils literal notranslate"><span class="pre">menutree</span></code> we left empty before. We only add the nodes, <em>not</em> the goto-helpers! The keys we set in the <code class="docutils literal notranslate"><span class="pre">menutree</span></code> dictionary are the names we should use to point to nodes from inside the menu (and we did).</p>
|
||||
<p>We also add a keyword argument <code class="docutils literal notranslate"><span class="pre">startnode</span></code> pointing to the <code class="docutils literal notranslate"><span class="pre">node_chargen</span></code> node. This tells EvMenu to first jump into that node when the menu is starting up.</p>
|
||||
</section>
|
||||
<section id="conclusions">
|
||||
<h2><span class="section-number">6.10. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This lesson taught us how to use <code class="docutils literal notranslate"><span class="pre">EvMenu</span></code> to make an interactive character generator. In an RPG more complex than <em>Knave</em>, the menu would be bigger and more intricate, but the same principles apply.</p>
|
||||
<p>Together with the previous lessons we have now fished most of the basics around player
|
||||
characters - how they store their stats, handle their equipment and how to create them.</p>
|
||||
<p>In the next lesson we’ll address how EvAdventure <em>Rooms</em> work.</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="Beginner-Tutorial-Rooms.html" title="7. In-game Rooms"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Equipment.html" title="5. Handling Equipment"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">6. </span>Character Generation</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,983 @@
|
|||
|
||||
<!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>9. Combat base framework — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="10. Twitch Combat" href="Beginner-Tutorial-Combat-Twitch.html" />
|
||||
<link rel="prev" title="8. Non-Player-Characters" href="Beginner-Tutorial-NPCs.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Combat-Twitch.html" title="10. Twitch Combat"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">9. </span>Combat base framework</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">9. Combat base framework</a><ul>
|
||||
<li><a class="reference internal" href="#combathandler">9.1. CombatHandler</a><ul>
|
||||
<li><a class="reference internal" href="#combathandler-get-or-create-combathandler">9.1.1. CombatHandler.get_or_create_combathandler</a></li>
|
||||
<li><a class="reference internal" href="#combathandler-msg">9.1.2. CombatHandler.msg</a></li>
|
||||
<li><a class="reference internal" href="#combathandler-get-combat-summary">9.1.3. Combathandler.get_combat_summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#actions">9.2. Actions</a></li>
|
||||
<li><a class="reference internal" href="#action-dicts">9.3. Action dicts</a></li>
|
||||
<li><a class="reference internal" href="#action-classes">9.4. Action classes</a><ul>
|
||||
<li><a class="reference internal" href="#hold-action">9.4.1. Hold Action</a></li>
|
||||
<li><a class="reference internal" href="#attack-action">9.4.2. Attack Action</a></li>
|
||||
<li><a class="reference internal" href="#stunt-action">9.4.3. Stunt Action</a></li>
|
||||
<li><a class="reference internal" href="#use-item-action">9.4.4. Use Item Action</a></li>
|
||||
<li><a class="reference internal" href="#wield-action">9.4.5. Wield Action</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#testing">9.5. Testing</a></li>
|
||||
<li><a class="reference internal" href="#conclusions">9.6. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-NPCs.html"
|
||||
title="previous chapter"><span class="section-number">8. </span>Non-Player-Characters</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Combat-Twitch.html"
|
||||
title="next chapter"><span class="section-number">10. </span>Twitch Combat</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Combat-Base.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="combat-base-framework">
|
||||
<h1><span class="section-number">9. </span>Combat base framework<a class="headerlink" href="#combat-base-framework" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Combat is core to many games. Exactly how it works is very game-dependent. In this lesson we will build a framework to implement two common flavors:</p>
|
||||
<ul class="simple">
|
||||
<li><p>“Twitch-based” combat (<a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html"><span class="doc std std-doc">specific lesson here</span></a>) means that you perform a combat action by entering a command, and after some delay (which may depend on your skills etc), the action happens. It’s called ‘twitch’ because actions often happen fast enough that changing your strategy may involve some element of quick thinking and a ‘twitchy trigger finger’.</p></li>
|
||||
<li><p>“Turn-based” combat (<a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html"><span class="doc std std-doc">specific lesson here</span></a>) means that players input actions in clear turns. Timeout for entering/queuing your actions is often much longer than twitch-based style. Once everyone made their choice (or the timeout is reached), everyone’s action happens all at once, after which the next turn starts. This style of combat requires less player reflexes.</p></li>
|
||||
</ul>
|
||||
<p>We will design a base combat system that supports both styles.</p>
|
||||
<ul class="simple">
|
||||
<li><p>We need a <code class="docutils literal notranslate"><span class="pre">CombatHandler</span></code> to track the progress of combat. This will be a <a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Script</span></a>. Exactly how this works (and where it is stored) will be a bit different between Twitch- and Turnbased combat. We will create its common framework in this lesson.</p></li>
|
||||
<li><p>Combat are divided into <em>actions</em>. We want to be able to easily extend our combat with more possible actions. An action needs Python code to show what actually happens when the action is performed. We will define such code in <code class="docutils literal notranslate"><span class="pre">Action</span></code> classes.</p></li>
|
||||
<li><p>We also need a way to describe a <em>specific instance</em> of a given action. That is, when we do an “attack” action, we need at the minimum to know who is being attacked. For this will we use Python <code class="docutils literal notranslate"><span class="pre">dicts</span></code> that we will refer to as <code class="docutils literal notranslate"><span class="pre">action_dicts</span></code>.</p></li>
|
||||
</ul>
|
||||
<section id="combathandler">
|
||||
<h2><span class="section-number">9.1. </span>CombatHandler<a class="headerlink" href="#combathandler" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/combat_base.py</span></code></p>
|
||||
</div></blockquote>
|
||||
<aside class="sidebar">
|
||||
<p>In <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.combat_base.html#evennia-contrib-tutorials-evadventure-combat-base"><span class="std std-ref">evennia/contrib/tutorials/evadventure/combat_base.py</span></a> you’ll find a complete implementation of the base combat module.</p>
|
||||
</aside>
|
||||
<p>Our “Combat Handler” will handle the administration around combat. It needs to be <em>persistent</em> (even is we reload the server your combat should keep going).</p>
|
||||
<p>Creating the CombatHandler is a little of a catch-22 - how it works depends on how Actions and Action-dicts look. But without having the CombatHandler, it’s hard to know how to design Actions and Action-dicts. So we’ll start with its general structure and fill out the details later in this lesson.</p>
|
||||
<p>Below, methods with <code class="docutils literal notranslate"><span class="pre">pass</span></code> will be filled out this lesson while those raising <code class="docutils literal notranslate"><span class="pre">NotImplementedError</span></code> will be different for Twitch/Turnbased combat and will be implemented in their respective lessons following this one.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultScript</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">CombatFailure</span><span class="p">(</span><span class="ne">RuntimeError</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""If some error happens in combat"""</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">DefaultSCript</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> This should be created when combat starts. It 'ticks' the combat </span>
|
||||
<span class="sd"> and tracks all sides of it.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="c1"># common for all types of combat</span>
|
||||
|
||||
<span class="n">action_classes</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># to fill in later </span>
|
||||
<span class="n">fallback_action_dict</span> <span class="o">=</span> <span class="p">{}</span>
|
||||
|
||||
<span class="nd">@classmethod</span>
|
||||
<span class="k">def</span> <span class="nf">get_or_create_combathandler</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" Get or create combathandler on `obj`."""</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<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="n">combatant</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Send a message to all combatants.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span> <span class="c1"># TODO</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Get a nicely formatted 'battle report' of combat, from the </span>
|
||||
<span class="sd"> perspective of the combatant.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span> <span class="c1"># TODO</span>
|
||||
|
||||
<span class="c1"># implemented differently by Twitch- and Turnbased combat</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_sides</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Get who's still alive on the two sides of combat, as a </span>
|
||||
<span class="sd"> tuple `([allies], [enemies])` from the perspective of `combatant` </span>
|
||||
<span class="sd"> (who is _not_ included in the `allies` list.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">give_advantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Give advantage to recipient against target.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">give_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Give disadvantage to recipient against target. </span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">has_advantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Does combatant have advantage against target?</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">has_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Does combatant have disadvantage against target?</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">queue_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Queue an action for the combatant by providing </span>
|
||||
<span class="sd"> action dict.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">execute_next_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Perform a combatant's next action.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">start_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Start combat.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">check_stop_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Check if the combat is over and if it should be stopped.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">stop_combat</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Stop combat and do cleanup.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
|
||||
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The Combat Handler is a <a class="reference internal" href="../../../Components/Scripts.html"><span class="doc std std-doc">Script</span></a>. Scripts are typeclassed entities, which means that they are persistently stored in the database. Scripts can optionally be stored “on” other objects (such as on Characters or Rooms) or be ‘global’ without any such connection. While Scripts has an optional timer component, it is not active by default and Scripts are commonly used just as plain storage. Since Scripts don’t have an in-game existence, they are great for storing data on ‘systems’ of all kinds, including our combat.</p>
|
||||
<p>Let’s implement the generic methods we need.</p>
|
||||
<section id="combathandler-get-or-create-combathandler">
|
||||
<h3><span class="section-number">9.1.1. </span>CombatHandler.get_or_create_combathandler<a class="headerlink" href="#combathandler-get-or-create-combathandler" title="Permalink to this headline">¶</a></h3>
|
||||
<p>A helper method for quickly getting the combathandler for an ongoing combat and combatant.</p>
|
||||
<p>We expect to create the script “on” an object (which one we don’t know yet, but we expect it to be a typeclassed entity).</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_script</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">DefaultScript</span><span class="p">):</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="nd">@classmethod</span>
|
||||
<span class="k">def</span> <span class="nf">get_or_create_combathandler</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Get or create a combathandler on `obj`.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> obj (any): The Typeclassed entity to store this Script on. </span>
|
||||
<span class="sd"> Keyword Args:</span>
|
||||
<span class="sd"> combathandler_key (str): Identifier for script. 'combathandler' by</span>
|
||||
<span class="sd"> default.</span>
|
||||
<span class="sd"> **kwargs: Extra arguments to the Script, if it is created.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">obj</span><span class="p">:</span>
|
||||
<span class="k">raise</span> <span class="n">CombatFailure</span><span class="p">(</span><span class="s2">"Cannot start combat without a place to do it!"</span><span class="p">)</span>
|
||||
|
||||
<span class="n">combathandler_key</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">"key"</span><span class="p">,</span> <span class="s2">"combathandler"</span><span class="p">)</span>
|
||||
<span class="n">combathandler</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combathandler</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">combathandler</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">combathandler</span><span class="o">.</span><span class="n">id</span><span class="p">:</span>
|
||||
<span class="n">combathandler</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">scripts</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">combathandler_key</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">combathandler</span><span class="p">:</span>
|
||||
<span class="c1"># have to create from scratch</span>
|
||||
<span class="n">persistent</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s2">"persistent"</span><span class="p">,</span> <span class="kc">True</span><span class="p">)</span>
|
||||
<span class="n">combathandler</span> <span class="o">=</span> <span class="n">create_script</span><span class="p">(</span>
|
||||
<span class="bp">cls</span><span class="p">,</span>
|
||||
<span class="n">key</span><span class="o">=</span><span class="n">combathandler_key</span><span class="p">,</span>
|
||||
<span class="n">obj</span><span class="o">=</span><span class="n">obj</span><span class="p">,</span>
|
||||
<span class="n">persistent</span><span class="o">=</span><span class="n">persistent</span><span class="p">,</span>
|
||||
<span class="o">**</span><span class="n">kwargs</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">obj</span><span class="o">.</span><span class="n">ndb</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combathandler</span>
|
||||
<span class="k">return</span> <span class="n">combathandler</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This helper method uses <code class="docutils literal notranslate"><span class="pre">obj.scripts.get()</span></code> to find if the combat script already exists ‘on’ the provided <code class="docutils literal notranslate"><span class="pre">obj</span></code>. If not, it will create it using Evennia’s <a class="reference internal" href="../../../api/evennia.utils.create.html#evennia.utils.create.create_script" title="evennia.utils.create.create_script"><span class="xref myst py py-func">create_script</span></a> function. For some extra speed we cache the handler as <code class="docutils literal notranslate"><span class="pre">obj.ndb.combathandler</span></code> The <code class="docutils literal notranslate"><span class="pre">.ndb.</span></code> (non-db) means that handler is cached only in memory.</p>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Checking .id (or .pk)</p>
|
||||
<p>When getting it from cache, we make sure to also check if the combathandler we got has a database <code class="docutils literal notranslate"><span class="pre">.id</span></code> that is not <code class="docutils literal notranslate"><span class="pre">None</span></code> (we could also check <code class="docutils literal notranslate"><span class="pre">.pk</span></code>, stands for “primary key”) . If it’s <code class="docutils literal notranslate"><span class="pre">None</span></code>, this means the database entity was deleted and we just got its cached python representation from memory - we need to recreate it.</p>
|
||||
</aside>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">get_or_create_combathandler</span></code> is decorated to be a <a class="reference external" href="https://docs.python.org/3/library/functions.html#classmethod">classmethod</a>, meaning it should be used on the handler class directly (rather than on an <em>instance</em> of said class). This makes sense because this method actually should return the new instance.</p>
|
||||
<p>As a class method we’ll need to call this directly on the class, like this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">EvAdventureCombatBaseHandler</span><span class="o">.</span><span class="n">get_or_create_combathandler</span><span class="p">(</span><span class="n">combatant</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The result will be a new handler <em>or</em> one that was already defined.</p>
|
||||
</section>
|
||||
<section id="combathandler-msg">
|
||||
<h3><span class="section-number">9.1.2. </span>CombatHandler.msg<a class="headerlink" href="#combathandler-msg" title="Permalink to this headline">¶</a></h3>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">DefaultScript</span><span class="p">):</span>
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<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="n">combatant</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="kc">True</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="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Central place for sending messages to combatants. This allows</span>
|
||||
<span class="sd"> for adding any combat-specific text-decoration in one place.</span>
|
||||
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> message (str): The message to send.</span>
|
||||
<span class="sd"> combatant (Object): The 'You' in the message, if any.</span>
|
||||
<span class="sd"> broadcast (bool): If `False`, `combatant` must be included and</span>
|
||||
<span class="sd"> will be the only one to see the message. If `True`, send to</span>
|
||||
<span class="sd"> everyone in the location.</span>
|
||||
<span class="sd"> location (Object, optional): If given, use this as the location to</span>
|
||||
<span class="sd"> send broadcast messages to. If not, use `self.obj` as that</span>
|
||||
<span class="sd"> location.</span>
|
||||
|
||||
<span class="sd"> Notes:</span>
|
||||
<span class="sd"> If `combatant` is given, use `$You/you()` markup to create</span>
|
||||
<span class="sd"> a message that looks different depending on who sees it. Use</span>
|
||||
<span class="sd"> `$You(combatant_key)` to refer to other combatants.</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">location</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">obj</span>
|
||||
|
||||
<span class="n">location_objs</span> <span class="o">=</span> <span class="n">location</span><span class="o">.</span><span class="n">contents</span>
|
||||
|
||||
<span class="n">exclude</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">broadcast</span> <span class="ow">and</span> <span class="n">combatant</span><span class="p">:</span>
|
||||
<span class="n">exclude</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">location_objs</span> <span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">combatant</span><span class="p">]</span>
|
||||
|
||||
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
|
||||
<span class="n">message</span><span class="p">,</span>
|
||||
<span class="n">exclude</span><span class="o">=</span><span class="n">exclude</span><span class="p">,</span>
|
||||
<span class="n">from_obj</span><span class="o">=</span><span class="n">combatant</span><span class="p">,</span>
|
||||
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">locobj</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">locobj</span> <span class="k">for</span> <span class="n">locobj</span> <span class="ow">in</span> <span class="n">location_objs</span><span class="p">},</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> property of a Script is the entity on which the Script ‘sits’. If set on a Character, <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> will be that Character. If on a room, it’d be that room. For a global script, <code class="docutils literal notranslate"><span class="pre">self.obj</span></code> is <code class="docutils literal notranslate"><span class="pre">None</span></code>.</p>
|
||||
</aside>
|
||||
<p>We saw the <code class="docutils literal notranslate"><span class="pre">location.msg_contents()</span></code> method before in the <a class="reference internal" href="Beginner-Tutorial-Objects.html#weapons"><span class="std std-doc">Weapon class of the Objects lesson</span></a>. Its purpose is to take a string on the form <code class="docutils literal notranslate"><span class="pre">"$You()</span> <span class="pre">do</span> <span class="pre">stuff</span> <span class="pre">against</span> <span class="pre">$you(key)"</span></code> and make sure all sides see a string suitable just to them. Our <code class="docutils literal notranslate"><span class="pre">msg()</span></code> method will by default broadcast the message to everyone in the room.</p>
|
||||
<div style="clear: right;"></div>
|
||||
<p>You’d use it like this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">combathandler</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"$You() $conj(throw) </span><span class="si">{</span><span class="n">item</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> at $you(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">)."</span><span class="p">,</span>
|
||||
<span class="n">combatant</span><span class="o">=</span><span class="n">combatant</span><span class="p">,</span>
|
||||
<span class="n">location</span><span class="o">=</span><span class="n">combatant</span><span class="o">.</span><span class="n">location</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If combatant is <code class="docutils literal notranslate"><span class="pre">Trickster</span></code>, <code class="docutils literal notranslate"><span class="pre">item.key</span></code> is “a colorful ball” and <code class="docutils literal notranslate"><span class="pre">target.key</span></code> is “Goblin”, then</p>
|
||||
<p>The combatant would see:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You throw a colorful ball at Goblin.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The Goblin sees</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Trickster throws a colorful ball at you.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Everyone else in the room sees</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Trickster throws a colorful ball at Goblin.
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="combathandler-get-combat-summary">
|
||||
<h3><span class="section-number">9.1.3. </span>Combathandler.get_combat_summary<a class="headerlink" href="#combathandler-get-combat-summary" title="Permalink to this headline">¶</a></h3>
|
||||
<p>We want to be able to show a nice summary of the current combat:</p>
|
||||
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span><span class="w"> </span>Goblin<span class="w"> </span>shaman<span class="w"> </span><span class="o">(</span>Perfect<span class="o">)</span>
|
||||
<span class="w"> </span>Gregor<span class="w"> </span><span class="o">(</span>Hurt<span class="o">)</span><span class="w"> </span>Goblin<span class="w"> </span>brawler<span class="o">(</span>Hurt<span class="o">)</span>
|
||||
<span class="w"> </span>Bob<span class="w"> </span><span class="o">(</span>Perfect<span class="o">)</span><span class="w"> </span>vs<span class="w"> </span>Goblin<span class="w"> </span>grunt<span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="o">(</span>Hurt<span class="o">)</span>
|
||||
<span class="w"> </span>Goblin<span class="w"> </span>grunt<span class="w"> </span><span class="m">2</span><span class="w"> </span><span class="o">(</span>Perfect<span class="o">)</span>
|
||||
<span class="w"> </span>Goblin<span class="w"> </span>grunt<span class="w"> </span><span class="m">3</span><span class="w"> </span><span class="o">(</span>Wounded<span class="o">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span>
|
||||
<span class="normal">22</span>
|
||||
<span class="normal">23</span>
|
||||
<span class="normal">24</span>
|
||||
<span class="normal">25</span>
|
||||
<span class="normal">26</span>
|
||||
<span class="normal">27</span>
|
||||
<span class="normal">28</span>
|
||||
<span class="normal">29</span>
|
||||
<span class="normal">30</span>
|
||||
<span class="normal">31</span>
|
||||
<span class="normal">32</span>
|
||||
<span class="normal">33</span>
|
||||
<span class="normal">34</span>
|
||||
<span class="normal">35</span>
|
||||
<span class="normal">36</span>
|
||||
<span class="normal">37</span>
|
||||
<span class="normal">38</span>
|
||||
<span class="normal">39</span>
|
||||
<span class="normal">40</span>
|
||||
<span class="normal">41</span>
|
||||
<span class="normal">42</span>
|
||||
<span class="normal">43</span>
|
||||
<span class="normal">44</span>
|
||||
<span class="normal">45</span>
|
||||
<span class="normal">46</span>
|
||||
<span class="normal">47</span>
|
||||
<span class="normal">48</span>
|
||||
<span class="normal">49</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/combat_base.py</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">EvTable</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">DefaultScript</span><span class="p">):</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">combatant</span><span class="p">):</span>
|
||||
|
||||
<span class="hll"> <span class="n">allies</span><span class="p">,</span> <span class="n">enemies</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_sides</span><span class="p">(</span><span class="n">combatant</span><span class="p">)</span>
|
||||
</span> <span class="n">nallies</span><span class="p">,</span> <span class="n">nenemies</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">allies</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">enemies</span><span class="p">)</span>
|
||||
<span class="hll">
|
||||
</span> <span class="c1"># prepare colors and hurt-levels</span>
|
||||
<span class="n">allies</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">ally</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">ally</span><span class="o">.</span><span class="n">hurt_level</span><span class="si">}</span><span class="s2">)"</span> <span class="k">for</span> <span class="n">ally</span> <span class="ow">in</span> <span class="n">allies</span><span class="p">]</span>
|
||||
<span class="n">enemies</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">enemy</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="n">enemy</span><span class="o">.</span><span class="n">hurt_level</span><span class="si">}</span><span class="s2">)"</span> <span class="k">for</span> <span class="n">enemy</span> <span class="ow">in</span> <span class="n">enemies</span><span class="p">]</span>
|
||||
<span class="hll">
|
||||
</span><span class="hll"> <span class="c1"># the center column with the 'vs'</span>
|
||||
</span> <span class="n">vs_column</span> <span class="o">=</span> <span class="p">[</span><span class="s2">""</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="n">nallies</span><span class="p">,</span> <span class="n">nenemies</span><span class="p">))]</span>
|
||||
<span class="n">vs_column</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">vs_column</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"|wvs|n"</span>
|
||||
|
||||
<span class="c1"># the two allies / enemies columns should be centered vertically</span>
|
||||
<span class="n">diff</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="n">nallies</span> <span class="o">-</span> <span class="n">nenemies</span><span class="p">)</span>
|
||||
<span class="hll"> <span class="n">top_empty</span> <span class="o">=</span> <span class="n">diff</span> <span class="o">//</span> <span class="mi">2</span>
|
||||
</span> <span class="n">bot_empty</span> <span class="o">=</span> <span class="n">diff</span> <span class="o">-</span> <span class="n">top_empty</span>
|
||||
<span class="n">topfill</span> <span class="o">=</span> <span class="p">[</span><span class="s2">""</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">top_empty</span><span class="p">)]</span>
|
||||
<span class="n">botfill</span> <span class="o">=</span> <span class="p">[</span><span class="s2">""</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">bot_empty</span><span class="p">)]</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">nallies</span> <span class="o">>=</span> <span class="n">nenemies</span><span class="p">:</span>
|
||||
<span class="n">enemies</span> <span class="o">=</span> <span class="n">topfill</span> <span class="o">+</span> <span class="n">enemies</span> <span class="o">+</span> <span class="n">botfill</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">allies</span> <span class="o">=</span> <span class="n">topfill</span> <span class="o">+</span> <span class="n">allies</span> <span class="o">+</span> <span class="n">botfill</span>
|
||||
|
||||
<span class="c1"># make a table with three columns</span>
|
||||
<span class="k">return</span> <span class="n">evtable</span><span class="o">.</span><span class="n">EvTable</span><span class="p">(</span>
|
||||
<span class="n">table</span><span class="o">=</span><span class="p">[</span>
|
||||
<span class="hll"> <span class="n">evtable</span><span class="o">.</span><span class="n">EvColumn</span><span class="p">(</span><span class="o">*</span><span class="n">allies</span><span class="p">,</span> <span class="n">align</span><span class="o">=</span><span class="s2">"l"</span><span class="p">),</span>
|
||||
</span> <span class="n">evtable</span><span class="o">.</span><span class="n">EvColumn</span><span class="p">(</span><span class="o">*</span><span class="n">vs_column</span><span class="p">,</span> <span class="n">align</span><span class="o">=</span><span class="s2">"c"</span><span class="p">),</span>
|
||||
<span class="n">evtable</span><span class="o">.</span><span class="n">EvColumn</span><span class="p">(</span><span class="o">*</span><span class="n">enemies</span><span class="p">,</span> <span class="n">align</span><span class="o">=</span><span class="s2">"r"</span><span class="p">),</span>
|
||||
<span class="p">],</span>
|
||||
<span class="n">border</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">maxwidth</span><span class="o">=</span><span class="mi">78</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<p>This may look complex, but the complexity is only in figuring out how to organize three columns, especially how to to adjust to the two sides on each side of the <code class="docutils literal notranslate"><span class="pre">vs</span></code> are roughly vertically aligned.</p>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 15</strong> : We make use of the <code class="docutils literal notranslate"><span class="pre">self.get_sides(combatant)</span></code> method which we haven’t actually implemented yet. This is because turn-based and twitch-based combat will need different ways to find out what the sides are. The <code class="docutils literal notranslate"><span class="pre">allies</span></code> and <code class="docutils literal notranslate"><span class="pre">enemies</span></code> are lists.</p></li>
|
||||
<li><p><strong>Line 17</strong>: The <code class="docutils literal notranslate"><span class="pre">combatant</span></code> is not a part of the <code class="docutils literal notranslate"><span class="pre">allies</span></code> list (this is how we defined <code class="docutils literal notranslate"><span class="pre">get_sides</span></code> to work), so we insert it at the top of the list (so they show first on the left-hand side).</p></li>
|
||||
<li><p><strong>Lines 21, 22</strong>: We make use of the <code class="docutils literal notranslate"><span class="pre">.hurt_level</span></code> values of all living things (see the <a class="reference internal" href="Beginner-Tutorial-Characters.html"><span class="doc std std-doc">LivingMixin of the Character lesson</span></a>).</p></li>
|
||||
<li><p><strong>Lines 28-39</strong>: We determine how to vertically center the two sides by adding empty lines above and below the content.</p></li>
|
||||
<li><p><strong>Line 41</strong>: The <a class="reference internal" href="../../../Components/EvTable.html"><span class="doc std std-doc">Evtable</span></a> is an Evennia utility for making, well, text tables. Once we are happy with the columns, we feed them to the table and let Evennia do the rest. It’s worth to explore <code class="docutils literal notranslate"><span class="pre">EvTable</span></code> since it can help you create all sorts of nice layouts.</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
<section id="actions">
|
||||
<h2><span class="section-number">9.2. </span>Actions<a class="headerlink" href="#actions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In EvAdventure we will only support a few common combat actions, mapping to the equivalent rolls and checks used in <em>Knave</em>. We will design our combat framework so that it’s easy to expand with other actions later.</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">hold</span></code> - The simplest action. You just lean back and do nothing.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">attack</span></code> - You attack a given <code class="docutils literal notranslate"><span class="pre">target</span></code> using your currently equipped weapon. This will become a roll of STR or WIS against the targets’ ARMOR.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">stunt</span></code> - You make a ‘stunt’, which in roleplaying terms would mean you tripping your opponent, taunting or otherwise trying to gain the upper hand without hurting them. You can do this to give yourself (or an ally) <em>advantage</em> against a <code class="docutils literal notranslate"><span class="pre">target</span></code> on the next action. You can also give a <code class="docutils literal notranslate"><span class="pre">target</span></code> <em>disadvantage</em> against you or an ally for their next action.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">use</span> <span class="pre">item</span></code> - You make use of a <code class="docutils literal notranslate"><span class="pre">Consumable</span></code> in your inventory. When used on yourself, it’d normally be something like a healing potion. If used on an enemy it could be a firebomb or a bottle of acid.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">wield</span></code> - You wield an item. Depending on what is being wielded, it will be wielded in different ways: A helmet will be placed on the head, a piece of armor on the chest. A sword will be wielded in one hand, a shield in another. A two-handed axe will use up two hands. Doing so will move whatever was there previously to the backpack.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">flee</span></code> - You run away/disengage. This action is only applicable in turn-based combat (in twitch-based combat you just move to another room to flee). We will thus wait to define this action until the <a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html"><span class="doc std std-doc">Turnbased combat lesson</span></a>.</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="action-dicts">
|
||||
<h2><span class="section-number">9.3. </span>Action dicts<a class="headerlink" href="#action-dicts" title="Permalink to this headline">¶</a></h2>
|
||||
<p>To pass around the details of an attack (the second point above), we will use a <code class="docutils literal notranslate"><span class="pre">dict</span></code>. A <code class="docutils literal notranslate"><span class="pre">dict</span></code> is simple and also easy to save in an <code class="docutils literal notranslate"><span class="pre">Attribute</span></code>. We’ll call this the <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> and here’s what we need for each action.</p>
|
||||
<blockquote>
|
||||
<div><p>You don’t need to type these out anywhere, it’s listed here for reference. We will use these dicts when calling <code class="docutils literal notranslate"><span class="pre">combathandler.queue_action(combatant,</span> <span class="pre">action_dict)</span></code>.</p>
|
||||
</div></blockquote>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">hold_action_dict</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="s2">"hold"</span>
|
||||
<span class="p">}</span>
|
||||
<span class="n">attack_action_dict</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="s2">"attack"</span><span class="p">,</span>
|
||||
<span class="s2">"target"</span><span class="p">:</span> <span class="o"><</span><span class="n">Character</span><span class="o">/</span><span class="n">NPC</span><span class="o">></span>
|
||||
<span class="p">}</span>
|
||||
<span class="n">stunt_action_dict</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="s2">"stunt"</span><span class="p">,</span>
|
||||
<span class="s2">"recipient"</span><span class="p">:</span> <span class="o"><</span><span class="n">Character</span><span class="o">/</span><span class="n">NPC</span><span class="o">></span><span class="p">,</span> <span class="c1"># who gains advantage/disadvantage</span>
|
||||
<span class="s2">"target"</span><span class="p">:</span> <span class="o"><</span><span class="n">Character</span><span class="o">/</span><span class="n">NPC</span><span class="o">></span><span class="p">,</span> <span class="c1"># who the recipient gainst adv/dis against</span>
|
||||
<span class="s2">"advantage"</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span> <span class="c1"># grant advantage or disadvantage?</span>
|
||||
<span class="s2">"stunt_type"</span><span class="p">:</span> <span class="n">Ability</span><span class="p">,</span> <span class="c1"># Ability to use for the challenge</span>
|
||||
<span class="s2">"defense_type"</span><span class="p">:</span> <span class="n">Ability</span><span class="p">,</span> <span class="c1"># what Ability for recipient to defend with if we</span>
|
||||
<span class="c1"># are trying to give disadvantage </span>
|
||||
<span class="p">}</span>
|
||||
<span class="n">use_item_action_dict</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="s2">"use"</span><span class="p">,</span>
|
||||
<span class="s2">"item"</span><span class="p">:</span> <span class="o"><</span><span class="n">Object</span><span class="o">></span>
|
||||
<span class="s2">"target"</span><span class="p">:</span> <span class="o"><</span><span class="n">Character</span><span class="o">/</span><span class="n">NPC</span><span class="o">/</span><span class="kc">None</span><span class="o">></span> <span class="c1"># if using item against someone else </span>
|
||||
<span class="p">}</span>
|
||||
<span class="n">wield_action_dict</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="s2">"wield"</span><span class="p">,</span>
|
||||
<span class="s2">"item"</span><span class="p">:</span> <span class="o"><</span><span class="n">Object</span><span class="o">></span>
|
||||
<span class="p">}</span>
|
||||
|
||||
<span class="c1"># used only for the turnbased combat, so its Action will be defined there</span>
|
||||
<span class="n">flee_action_dict</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="s2">"flee"</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Apart from the <code class="docutils literal notranslate"><span class="pre">stunt</span></code> action, these dicts are all pretty simple. The <code class="docutils literal notranslate"><span class="pre">key</span></code> identifes the action to perform and the other fields identifies the minimum things you need to know in order to resolve each action.</p>
|
||||
<p>We have not yet written the code to set these dicts, but we will assume that we know who is performing each of these actions. So if <code class="docutils literal notranslate"><span class="pre">Beowulf</span></code> attacks <code class="docutils literal notranslate"><span class="pre">Grendel</span></code>, Beowulf is not himself included in the attack dict:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">attack_action_dict</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="s2">"attack"</span><span class="p">,</span>
|
||||
<span class="s2">"target"</span><span class="p">:</span> <span class="n">Grendel</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Let’s explain the longest action dict, the <code class="docutils literal notranslate"><span class="pre">Stunt</span></code> action dict in more detail as well. In this example, The <code class="docutils literal notranslate"><span class="pre">Trickster</span></code> is performing a <em>Stunt</em> in order to help his friend <code class="docutils literal notranslate"><span class="pre">Paladin</span></code> to gain an INT- <em>advantage</em> against the <code class="docutils literal notranslate"><span class="pre">Goblin</span></code> (maybe the paladin is preparing to cast a spell of something). Since <code class="docutils literal notranslate"><span class="pre">Trickster</span></code> is doing the action, he’s not showing up in the dict:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">stunt_action_dict</span> <span class="o">-</span> <span class="p">{</span>
|
||||
<span class="s2">"key"</span><span class="p">:</span> <span class="s2">"stunt"</span><span class="p">,</span>
|
||||
<span class="s2">"recipient"</span><span class="p">:</span> <span class="n">Paladin</span><span class="p">,</span>
|
||||
<span class="s2">"target"</span><span class="p">:</span> <span class="n">Goblin</span><span class="p">,</span>
|
||||
<span class="s2">"advantage"</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
|
||||
<span class="s2">"stunt_type"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">INT</span><span class="p">,</span>
|
||||
<span class="s2">"defense_type"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">INT</span><span class="p">,</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p>In EvAdventure, we’ll always set <code class="docutils literal notranslate"><span class="pre">stunt_type</span> <span class="pre">==</span> <span class="pre">defense_type</span></code> for simplicity. But you could also consider mixing things up so you could use DEX to confuse someone and give them INT disadvantage, for example.</p>
|
||||
</aside>
|
||||
<p>This should result in an INT vs INT based check between the <code class="docutils literal notranslate"><span class="pre">Trickster</span></code> and the <code class="docutils literal notranslate"><span class="pre">Goblin</span></code> (maybe the trickster is trying to confuse the goblin with some clever word play). If the <code class="docutils literal notranslate"><span class="pre">Trickster</span></code> wins, the <code class="docutils literal notranslate"><span class="pre">Paladin</span></code> gains advantage against the Goblin on the <code class="docutils literal notranslate"><span class="pre">Paladin</span></code>’s next action .</p>
|
||||
</section>
|
||||
<section id="action-classes">
|
||||
<h2><span class="section-number">9.4. </span>Action classes<a class="headerlink" href="#action-classes" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Once our <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> identifies the particular action we should use, we need something that reads those keys/values and actually <em>performs</em> the action.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CombatAction</span><span class="p">:</span>
|
||||
|
||||
<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">combathandler</span><span class="p">,</span> <span class="n">combatant</span><span class="p">,</span> <span class="n">action_dict</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combathandler</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="o">=</span> <span class="n">combatant</span>
|
||||
|
||||
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">action_dict</span><span class="o">.</span><span class="n">items</span><span class="p">();</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="s2">"_"</span><span class="p">):</span>
|
||||
<span class="nb">setattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We will create a new instance of this class <em>every time an action is happening</em>. So we store some key things every action will need - we will need a reference to the common <code class="docutils literal notranslate"><span class="pre">combathandler</span></code> (which we will design in the next section), and to the <code class="docutils literal notranslate"><span class="pre">combatant</span></code> (the one performing this action). The <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> is a dict matching the action we want to perform.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">setattr</span></code> Python standard function assigns the keys/values of the <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> to be properties “on” this action. This is very convenient to use in other methods. So for the <code class="docutils literal notranslate"><span class="pre">stunt</span></code> action, other methods could just access <code class="docutils literal notranslate"><span class="pre">self.key</span></code>, <code class="docutils literal notranslate"><span class="pre">self.recipient</span></code>, <code class="docutils literal notranslate"><span class="pre">self.target</span></code> and so on directly.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CombatAction</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<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="n">broadcast</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
|
||||
<span class="s2">"Send message to others in combat"</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</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="n">combatant</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">,</span> <span class="n">broadcast</span><span class="o">=</span><span class="n">broadcast</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">can_use</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Return False if combatant can's use this action right now"""</span>
|
||||
<span class="k">return</span> <span class="kc">True</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Does the actional action"""</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">post_execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called after `execute`"""</span>
|
||||
<span class="k">pass</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>It’s <em>very</em> common to want to send messages to everyone in combat - you need to tell people they are getting attacked, if they get hurt and so on. So having a <code class="docutils literal notranslate"><span class="pre">msg</span></code> helper method on the action is convenient. We offload all the complexity to the combathandler.msg() method.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">can_use</span></code>, <code class="docutils literal notranslate"><span class="pre">execute</span></code> and <code class="docutils literal notranslate"><span class="pre">post_execute</span></code> should all be called in a chain and we should make sure the <code class="docutils literal notranslate"><span class="pre">combathandler</span></code> calls them like this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">action</span><span class="o">.</span><span class="n">can_use</span><span class="p">():</span>
|
||||
<span class="n">action</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
|
||||
<span class="n">action</span><span class="o">.</span><span class="n">post_execute</span><span class="p">()</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<section id="hold-action">
|
||||
<h3><span class="section-number">9.4.1. </span>Hold Action<a class="headerlink" href="#hold-action" title="Permalink to this headline">¶</a></h3>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CombatActionHold</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Action that does nothing </span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> action_dict = {</span>
|
||||
<span class="sd"> "key": "hold"</span>
|
||||
<span class="sd"> }</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Holding does nothing but it’s cleaner to nevertheless have a separate class for it. We use the docstring to specify how its action-dict should look.</p>
|
||||
</section>
|
||||
<section id="attack-action">
|
||||
<h3><span class="section-number">9.4.2. </span>Attack Action<a class="headerlink" href="#attack-action" title="Permalink to this headline">¶</a></h3>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CombatActionAttack</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> A regular attack, using a wielded weapon.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> action-dict = {</span>
|
||||
<span class="sd"> "key": "attack",</span>
|
||||
<span class="sd"> "target": Character/Object</span>
|
||||
<span class="sd"> }</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="n">attacker</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
|
||||
<span class="n">weapon</span> <span class="o">=</span> <span class="n">attacker</span><span class="o">.</span><span class="n">weapon</span>
|
||||
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">weapon</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
|
||||
<span class="n">weapon</span><span class="o">.</span><span class="n">use</span><span class="p">(</span>
|
||||
<span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="n">advantage</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">weapon</span><span class="o">.</span><span class="n">at_post_use</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Refer to how we <a class="reference internal" href="Beginner-Tutorial-Objects.html#weapons"><span class="std std-doc">designed Evadventure weapons</span></a> to understand what happens here - most of the work is performed by the weapon class - we just plug in the relevant arguments.</p>
|
||||
</section>
|
||||
<section id="stunt-action">
|
||||
<h3><span class="section-number">9.4.3. </span>Stunt Action<a class="headerlink" href="#stunt-action" title="Permalink to this headline">¶</a></h3>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CombatActionStunt</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Perform a stunt the grants a beneficiary (can be self) advantage on their next action against a </span>
|
||||
<span class="sd"> target. Whenever performing a stunt that would affect another negatively (giving them</span>
|
||||
<span class="sd"> disadvantage against an ally, or granting an advantage against them, we need to make a check</span>
|
||||
<span class="sd"> first. We don't do a check if giving an advantage to an ally or ourselves.</span>
|
||||
|
||||
<span class="sd"> action_dict = {</span>
|
||||
<span class="sd"> "key": "stunt",</span>
|
||||
<span class="sd"> "recipient": Character/NPC,</span>
|
||||
<span class="sd"> "target": Character/NPC,</span>
|
||||
<span class="sd"> "advantage": bool, # if False, it's a disadvantage</span>
|
||||
<span class="sd"> "stunt_type": Ability, # what ability (like STR, DEX etc) to use to perform this stunt. </span>
|
||||
<span class="sd"> "defense_type": Ability, # what ability to use to defend against (negative) effects of</span>
|
||||
<span class="sd"> this stunt.</span>
|
||||
<span class="sd"> }</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="n">combathandler</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span>
|
||||
<span class="n">attacker</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
|
||||
<span class="n">recipient</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">recipient</span> <span class="c1"># the one to receive the effect of the stunt</span>
|
||||
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span> <span class="c1"># the affected by the stunt (can be the same as recipient/combatant)</span>
|
||||
<span class="n">txt</span> <span class="o">=</span> <span class="s2">""</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">recipient</span> <span class="o">==</span> <span class="n">target</span><span class="p">:</span>
|
||||
<span class="c1"># grant another entity dis/advantage against themselves</span>
|
||||
<span class="n">defender</span> <span class="o">=</span> <span class="n">recipient</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># recipient not same as target; who will defend depends on disadvantage or advantage</span>
|
||||
<span class="c1"># to give.</span>
|
||||
<span class="n">defender</span> <span class="o">=</span> <span class="n">target</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">advantage</span> <span class="k">else</span> <span class="n">recipient</span>
|
||||
|
||||
<span class="c1"># trying to give advantage to recipient against target. Target defends against caller</span>
|
||||
<span class="n">is_success</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">txt</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">dice</span><span class="o">.</span><span class="n">opposed_saving_throw</span><span class="p">(</span>
|
||||
<span class="n">attacker</span><span class="p">,</span>
|
||||
<span class="n">defender</span><span class="p">,</span>
|
||||
<span class="n">attack_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">stunt_type</span><span class="p">,</span>
|
||||
<span class="n">defense_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">defense_type</span><span class="p">,</span>
|
||||
<span class="n">advantage</span><span class="o">=</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">defender</span><span class="p">),</span>
|
||||
<span class="n">disadvantage</span><span class="o">=</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_disadvantage</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">defender</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="sa">f</span><span class="s2">"$You() $conj(attempt) stunt on $You(</span><span class="si">{</span><span class="n">defender</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">txt</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># deal with results</span>
|
||||
<span class="k">if</span> <span class="n">is_success</span><span class="p">:</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">advantage</span><span class="p">:</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">give_advantage</span><span class="p">(</span><span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">combathandler</span><span class="o">.</span><span class="n">give_disadvantage</span><span class="p">(</span><span class="n">recipient</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">recipient</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</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="sa">f</span><span class="s2">"$You() $conj(gain) </span><span class="si">{</span><span class="s1">'advantage'</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="bp">self</span><span class="o">.</span><span class="n">advantage</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="s1">'disadvantage'</span><span class="si">}</span><span class="s2"> "</span>
|
||||
<span class="sa">f</span><span class="s2">"against $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">)!"</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">msg</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"$You() $conj(cause) $You(</span><span class="si">{</span><span class="n">recipient</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) "</span>
|
||||
<span class="sa">f</span><span class="s2">"to gain </span><span class="si">{</span><span class="s1">'advantage'</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="bp">self</span><span class="o">.</span><span class="n">advantage</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="s1">'disadvantage'</span><span class="si">}</span><span class="s2"> "</span>
|
||||
<span class="sa">f</span><span class="s2">"against $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">)!"</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">"|yHaving succeeded, you hold back to plan your next move.|n [hold]"</span><span class="p">,</span>
|
||||
<span class="n">broadcast</span><span class="o">=</span><span class="kc">False</span><span class="p">,</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">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"$You(</span><span class="si">{</span><span class="n">defender</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) $conj(resist)! $You() $conj(fail) the stunt."</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The main action here is the call to the <code class="docutils literal notranslate"><span class="pre">rules.dice.opposed_saving_throw</span></code> to determine if the stunt succeeds. After that, most lines is about figuring out who should be given advantage/disadvantage and to communicate the result to the affected parties.</p>
|
||||
<p>Note that we make heavy use of the helper methods on the <code class="docutils literal notranslate"><span class="pre">combathandler</span></code> here, even those that are not yet implemented. As long as we pass the <code class="docutils literal notranslate"><span class="pre">action_dict</span></code> into the <code class="docutils literal notranslate"><span class="pre">combathandler</span></code>, the action doesn’t actually care what happens next.</p>
|
||||
<p>After we have performed a successful stunt, we queue the <code class="docutils literal notranslate"><span class="pre">combathandler.fallback_action_dict</span></code>. This is because stunts are meant to be one-off things are if we are repeating actions, it would not make sense to repeat the stunt over and over.</p>
|
||||
</section>
|
||||
<section id="use-item-action">
|
||||
<h3><span class="section-number">9.4.4. </span>Use Item Action<a class="headerlink" href="#use-item-action" title="Permalink to this headline">¶</a></h3>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CombatActionUseItem</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Use an item in combat. This is meant for one-off or limited-use items (so things like scrolls and potions, not swords and shields). If this is some sort of weapon or spell rune, we refer to the item to determine what to use for attack/defense rolls.</span>
|
||||
|
||||
<span class="sd"> action_dict = {</span>
|
||||
<span class="sd"> "key": "use",</span>
|
||||
<span class="sd"> "item": Object</span>
|
||||
<span class="sd"> "target": Character/NPC/Object/None</span>
|
||||
<span class="sd"> }</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="n">item</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">item</span>
|
||||
<span class="n">user</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">combatant</span>
|
||||
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">target</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">item</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
|
||||
<span class="n">item</span><span class="o">.</span><span class="n">use</span><span class="p">(</span>
|
||||
<span class="n">user</span><span class="p">,</span>
|
||||
<span class="n">target</span><span class="p">,</span>
|
||||
<span class="n">advantage</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_advantage</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">),</span>
|
||||
<span class="n">disadvantage</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">has_disadvantage</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">),</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">item</span><span class="o">.</span><span class="n">at_post_use</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>See the <a class="reference internal" href="Beginner-Tutorial-Objects.html"><span class="doc std std-doc">Consumable items in the Object lesson</span></a> to see how consumables work. Like with weapons, we offload all the logic to the item we use.</p>
|
||||
</section>
|
||||
<section id="wield-action">
|
||||
<h3><span class="section-number">9.4.5. </span>Wield Action<a class="headerlink" href="#wield-action" title="Permalink to this headline">¶</a></h3>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/combat_base.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">CombatActionWield</span><span class="p">(</span><span class="n">CombatAction</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Wield a new weapon (or spell) from your inventory. This will </span>
|
||||
<span class="sd"> swap out the one you are currently wielding, if any.</span>
|
||||
|
||||
<span class="sd"> action_dict = {</span>
|
||||
<span class="sd"> "key": "wield",</span>
|
||||
<span class="sd"> "item": Object</span>
|
||||
<span class="sd"> }</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">execute</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">combatant</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">item</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We rely on the <a class="reference internal" href="Beginner-Tutorial-Equipment.html"><span class="doc std std-doc">Equipment handler</span></a> we created to handle the swapping of items for us. Since it doesn’t make sense to keep swapping over and over, we queue the fallback action after this one.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="testing">
|
||||
<h2><span class="section-number">9.5. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Create a module <code class="docutils literal notranslate"><span class="pre">evadventure/tests/test_combat.py</span></code>.</p>
|
||||
</div></blockquote>
|
||||
<aside class="sidebar">
|
||||
<p>See <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_combat.html#evennia-contrib-tutorials-evadventure-tests-test-combat"><span class="std std-ref">evennia/contrib/tutorials/evadventure/tests/test_combat.py</span></a> for ready-made combat unit tests.</p>
|
||||
</aside>
|
||||
<p>Unit testing the combat base classes can seem impossible because we have not yet implemented most of it. We can however get very far by the use of <a class="reference external" href="https://docs.python.org/3/library/unittest.mock.html">Mocks</a>. The idea of a Mock is that you <em>replace</em> a piece of code with a dummy object (a ‘mock’) that can be called to return some specific value.</p>
|
||||
<p>For example, consider this following test of the <code class="docutils literal notranslate"><span class="pre">CombatHandler.get_combat_summary</span></code>. We can’t just call this because it internally calls <code class="docutils literal notranslate"><span class="pre">.get_sides</span></code> which would raise a <code class="docutils literal notranslate"><span class="pre">NotImplementedError</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span>
|
||||
<span class="normal">22</span>
|
||||
<span class="normal">23</span>
|
||||
<span class="normal">24</span>
|
||||
<span class="normal">25</span>
|
||||
<span class="normal">26</span>
|
||||
<span class="normal">27</span>
|
||||
<span class="normal">28</span>
|
||||
<span class="normal">29</span>
|
||||
<span class="normal">30</span>
|
||||
<span class="normal">31</span>
|
||||
<span class="normal">32</span>
|
||||
<span class="normal">33</span>
|
||||
<span class="normal">34</span>
|
||||
<span class="normal">35</span>
|
||||
<span class="normal">36</span>
|
||||
<span class="normal">37</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/tests/test_combat.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">unittest.mock</span> <span class="kn">import</span> <span class="n">Mock</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTestCase</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_object</span>
|
||||
<span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">combat_base</span>
|
||||
<span class="kn">from</span> <span class="nn">..rooms</span> <span class="kn">import</span> <span class="n">EvAdventureRoom</span>
|
||||
<span class="kn">from</span> <span class="nn">..characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">TestEvAdventureCombatBaseHandler</span><span class="p">(</span><span class="n">EvenniaTestCase</span><span class="p">):</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">setUp</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">location</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"testroom"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">combatant</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"testchar"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">target</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureMob</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"testmonster"</span><span class="p">)</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span> <span class="o">=</span> <span class="n">combat_base</span><span class="o">.</span><span class="n">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">location</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">test_get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
|
||||
<span class="c1"># do the test from perspective of combatant</span>
|
||||
<span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_sides</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="p">([],</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">]))</span>
|
||||
</span> <span class="n">result</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">))</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span>
|
||||
<span class="n">result</span><span class="p">,</span>
|
||||
<span class="s2">" testchar (Perfect) vs testmonster (Perfect)"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="c1"># test from the perspective of the monster </span>
|
||||
<span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_sides</span> <span class="o">=</span> <span class="n">Mock</span><span class="p">(</span><span class="n">return_value</span><span class="o">=</span><span class="p">([],</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">combatant</span><span class="p">]))</span>
|
||||
</span> <span class="n">result</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">combathandler</span><span class="o">.</span><span class="n">get_combat_summary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">target</span><span class="p">))</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span>
|
||||
<span class="n">result</span><span class="p">,</span>
|
||||
<span class="s2">" testmonster (Perfect) vs testchar (Perfect)"</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<p>The interesting places are where we apply the mocks:</p>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 25</strong> and <strong>Line 32</strong>: While <code class="docutils literal notranslate"><span class="pre">get_sides</span></code> is not implemented yet, we know what it is <em>supposed</em> to return - a tuple of lists. So for the sake of the test, we <em>replace</em> the <code class="docutils literal notranslate"><span class="pre">get_sides</span></code> method with a mock that when called will return something useful.</p></li>
|
||||
</ul>
|
||||
<p>With this kind of approach it’s possible to fully test a system also when it’s not ‘complete’ yet.</p>
|
||||
</section>
|
||||
<section id="conclusions">
|
||||
<h2><span class="section-number">9.6. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>We have the core functionality we need for our combat system! In the following two lessons we will make use of these building blocks to create two styles of combat.</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="Beginner-Tutorial-Combat-Twitch.html" title="10. Twitch Combat"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">9. </span>Combat base framework</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,151 @@
|
|||
|
||||
<!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>16. In-game Commands — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="Part 4: Using What We Created" href="../Part4/Beginner-Tutorial-Part4-Overview.html" />
|
||||
<link rel="prev" title="15. In-game Shops" href="Beginner-Tutorial-Shops.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Part4/Beginner-Tutorial-Part4-Overview.html" title="Part 4: Using What We Created"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">16. </span>In-game Commands</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Shops.html"
|
||||
title="previous chapter"><span class="section-number">15. </span>In-game Shops</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="../Part4/Beginner-Tutorial-Part4-Overview.html"
|
||||
title="next chapter">Part 4: Using What We Created</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Commands.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="in-game-commands">
|
||||
<h1><span class="section-number">16. </span>In-game Commands<a class="headerlink" href="#in-game-commands" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="admonition warning">
|
||||
<p class="admonition-title">Warning</p>
|
||||
<p>This part of the Beginner tutorial is still being developed.</p>
|
||||
</div>
|
||||
</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="../Part4/Beginner-Tutorial-Part4-Overview.html" title="Part 4: Using What We Created"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">16. </span>In-game Commands</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
|
||||
<!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>13. Dynamically generated Dungeon — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="14. Game Quests" href="Beginner-Tutorial-Quests.html" />
|
||||
<link rel="prev" title="12. NPC and monster AI" href="Beginner-Tutorial-AI.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Quests.html" title="14. Game Quests"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-AI.html" title="12. NPC and monster AI"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">13. </span>Dynamically generated Dungeon</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-AI.html"
|
||||
title="previous chapter"><span class="section-number">12. </span>NPC and monster AI</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Quests.html"
|
||||
title="next chapter"><span class="section-number">14. </span>Game Quests</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Dungeon.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="dynamically-generated-dungeon">
|
||||
<h1><span class="section-number">13. </span>Dynamically generated Dungeon<a class="headerlink" href="#dynamically-generated-dungeon" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="admonition warning">
|
||||
<p class="admonition-title">Warning</p>
|
||||
<p>This part of the Beginner tutorial is still being developed.</p>
|
||||
</div>
|
||||
</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="Beginner-Tutorial-Quests.html" title="14. Game Quests"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-AI.html" title="12. NPC and monster AI"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">13. </span>Dynamically generated Dungeon</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,733 @@
|
|||
|
||||
<!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>5. Handling Equipment — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="6. Character Generation" href="Beginner-Tutorial-Chargen.html" />
|
||||
<link rel="prev" title="4. In-game Objects and items" href="Beginner-Tutorial-Objects.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Chargen.html" title="6. Character Generation"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Objects.html" title="4. In-game Objects and items"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">5. </span>Handling Equipment</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">5. Handling Equipment</a><ul>
|
||||
<li><a class="reference internal" href="#equipmenthandler-that-saves">5.1. EquipmentHandler that saves</a></li>
|
||||
<li><a class="reference internal" href="#connecting-the-equipmenthandler">5.2. Connecting the EquipmentHandler</a></li>
|
||||
<li><a class="reference internal" href="#expanding-the-equipmenthandler">5.3. Expanding the Equipmenthandler</a></li>
|
||||
<li><a class="reference internal" href="#validate-slot-usage">5.4. <code class="docutils literal notranslate"><span class="pre">.validate_slot_usage</span></code></a><ul>
|
||||
<li><a class="reference internal" href="#max-slots">5.4.1. <code class="docutils literal notranslate"><span class="pre">.max_slots</span></code></a></li>
|
||||
<li><a class="reference internal" href="#count-slots">5.4.2. <code class="docutils literal notranslate"><span class="pre">.count_slots</span></code></a></li>
|
||||
<li><a class="reference internal" href="#validating-slots">5.4.3. Validating slots</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#add-and-remove">5.5. <code class="docutils literal notranslate"><span class="pre">.add</span></code> and <code class="docutils literal notranslate"><span class="pre">.remove</span></code></a></li>
|
||||
<li><a class="reference internal" href="#moving-things-around">5.6. Moving things around</a></li>
|
||||
<li><a class="reference internal" href="#get-everything">5.7. Get everything</a></li>
|
||||
<li><a class="reference internal" href="#weapon-and-armor">5.8. Weapon and armor</a><ul>
|
||||
<li><a class="reference internal" href="#fixing-the-character-class">5.8.1. Fixing the Character class</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#extra-credits">5.9. Extra credits</a></li>
|
||||
<li><a class="reference internal" href="#unit-testing">5.10. Unit Testing</a></li>
|
||||
<li><a class="reference internal" href="#summary">5.11. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Objects.html"
|
||||
title="previous chapter"><span class="section-number">4. </span>In-game Objects and items</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Chargen.html"
|
||||
title="next chapter"><span class="section-number">6. </span>Character Generation</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Equipment.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="handling-equipment">
|
||||
<h1><span class="section-number">5. </span>Handling Equipment<a class="headerlink" href="#handling-equipment" title="Permalink to this headline">¶</a></h1>
|
||||
<p>In <em>Knave</em>, you have a certain number of inventory “slots”. The amount of slots is given by <code class="docutils literal notranslate"><span class="pre">CON</span> <span class="pre">+</span> <span class="pre">10</span></code>. All items (except coins) have a <code class="docutils literal notranslate"><span class="pre">size</span></code>, indicating how many slots it uses. You can’t carry more items than you have slot-space for. Also items wielded or worn count towards the slots.</p>
|
||||
<p>We still need to track what the character is using however: What weapon they have readied affects the damage they can do. The shield, helmet and armor they use affects their defense.</p>
|
||||
<p>We have already set up the possible ‘wear/wield locations’ when we defined our Objects
|
||||
<a class="reference internal" href="Beginner-Tutorial-Objects.html"><span class="doc std std-doc">in the previous lesson</span></a>. This is what we have in <code class="docutils literal notranslate"><span class="pre">enums.py</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/enums.py</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">WieldLocation</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
|
||||
|
||||
<span class="n">BACKPACK</span> <span class="o">=</span> <span class="s2">"backpack"</span>
|
||||
<span class="n">WEAPON_HAND</span> <span class="o">=</span> <span class="s2">"weapon_hand"</span>
|
||||
<span class="n">SHIELD_HAND</span> <span class="o">=</span> <span class="s2">"shield_hand"</span>
|
||||
<span class="n">TWO_HANDS</span> <span class="o">=</span> <span class="s2">"two_handed_weapons"</span>
|
||||
<span class="n">BODY</span> <span class="o">=</span> <span class="s2">"body"</span> <span class="c1"># armor</span>
|
||||
<span class="n">HEAD</span> <span class="o">=</span> <span class="s2">"head"</span> <span class="c1"># helmets</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Basically, all the weapon/armor locations are exclusive - you can only have one item in each (or none). The BACKPACK is special - it contains any number of items (up to the maximum slot usage).</p>
|
||||
<section id="equipmenthandler-that-saves">
|
||||
<h2><span class="section-number">5.1. </span>EquipmentHandler that saves<a class="headerlink" href="#equipmenthandler-that-saves" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/equipment.py</span></code>.</p>
|
||||
</div></blockquote>
|
||||
<aside class="sidebar">
|
||||
<p>If you want to understand more about behind how Evennia uses handlers, there is a
|
||||
<a class="reference internal" href="../../Tutorial-Persistent-Handler.html"><span class="doc std std-doc">dedicated tutorial</span></a> talking about the principle.</p>
|
||||
</aside>
|
||||
<p>In default Evennia, everything you pick up will end up “inside” your character object (that is, have you as its <code class="docutils literal notranslate"><span class="pre">.location</span></code>). This is called your <em>inventory</em> and has no limit. We will keep ‘moving items into us’ when we pick them up, but we will add more functionality using an <em>Equipment handler</em>.</p>
|
||||
<p>A handler is (for our purposes) an object that sits “on” another entity, containing functionality for doing one specific thing (managing equipment, in our case).</p>
|
||||
<p>This is the start of our handler:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/equipment.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EquipmentHandler</span><span class="p">:</span>
|
||||
<span class="n">save_attribute</span> <span class="o">=</span> <span class="s2">"inventory_slots"</span>
|
||||
|
||||
<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">obj</span><span class="p">):</span>
|
||||
<span class="c1"># here obj is the character we store the handler on </span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="n">obj</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_load</span><span class="p">()</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_load</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Load our data from an Attribute on `self.obj`"""</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</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="bp">self</span><span class="o">.</span><span class="n">save_attribute</span><span class="p">,</span>
|
||||
<span class="n">category</span><span class="o">=</span><span class="s2">"inventory"</span><span class="p">,</span>
|
||||
<span class="n">default</span><span class="o">=</span><span class="p">{</span>
|
||||
<span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">WieldLocation</span><span class="o">.</span><span class="n">BODY</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">WieldLocation</span><span class="o">.</span><span class="n">HEAD</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span>
|
||||
<span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">:</span> <span class="p">[]</span>
|
||||
<span class="p">}</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">_save</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Save our data back to the same Attribute"""</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">attributes</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">save_attribute</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"inventory"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This is a compact and functional little handler. Before analyzing how it works, this is how
|
||||
we will add it to the Character:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/characters.py</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">lazy_property</span>
|
||||
<span class="kn">from</span> <span class="nn">.equipment</span> <span class="kn">import</span> <span class="n">EquipmentHandler</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="nd">@lazy_property</span>
|
||||
<span class="k">def</span> <span class="nf">equipment</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="n">EquipmentHandler</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>After reloading the server, the equipment-handler will now be accessible on character-instances as</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>character.equipment
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">@lazy_property</span></code> works such that it will not load the handler until someone actually tries to fetch it with <code class="docutils literal notranslate"><span class="pre">character.equipment</span></code>. When that happens, we start up the handler and feed it <code class="docutils literal notranslate"><span class="pre">self</span></code> (the <code class="docutils literal notranslate"><span class="pre">Character</span></code> instance itself). This is what enters <code class="docutils literal notranslate"><span class="pre">__init__</span></code> as <code class="docutils literal notranslate"><span class="pre">.obj</span></code> in the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> code above.</p>
|
||||
<p>So we now have a handler on the character, and the handler has a back-reference to the character it sits on.</p>
|
||||
<p>Since the handler itself is just a regular Python object, we need to use the <code class="docutils literal notranslate"><span class="pre">Character</span></code> to store
|
||||
our data - our <em>Knave</em> “slots”. We must save them to the database, because we want the server to remember them even after reloading.</p>
|
||||
<p>Using <code class="docutils literal notranslate"><span class="pre">self.obj.attributes.add()</span></code> and <code class="docutils literal notranslate"><span class="pre">.get()</span></code> we save the data to the Character in a specially named <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attribute</span></a>. Since we use a <code class="docutils literal notranslate"><span class="pre">category</span></code>, we are unlikely to collide with
|
||||
other Attributes.</p>
|
||||
<p>Our storage structure is a <code class="docutils literal notranslate"><span class="pre">dict</span></code> with keys after our available <code class="docutils literal notranslate"><span class="pre">WieldLocation</span></code> enums. Each can only have one item except <code class="docutils literal notranslate"><span class="pre">WieldLocation.BACKPACK</span></code>, which is a list.</p>
|
||||
</section>
|
||||
<section id="connecting-the-equipmenthandler">
|
||||
<h2><span class="section-number">5.2. </span>Connecting the EquipmentHandler<a class="headerlink" href="#connecting-the-equipmenthandler" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Whenever an object leaves from one location to the next, Evennia will call a set of <em>hooks</em> (methods) on the object that moves, on the source-location and on its destination. This is the same for all moving things - whether it’s a character moving between rooms or an item being dropping from your hand to the ground.</p>
|
||||
<p>We need to tie our new <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> into this system. By reading the doc page on <a class="reference internal" href="../../../Components/Objects.html"><span class="doc std std-doc">Objects</span></a>, or looking at the <a class="reference internal" href="../../../api/evennia.objects.objects.html#evennia.objects.objects.DefaultObject.move_to" title="evennia.objects.objects.DefaultObject.move_to"><span class="xref myst py py-meth">DefaultObject.move_to</span></a> docstring, we’ll find out what hooks Evennia will call. Here <code class="docutils literal notranslate"><span class="pre">self</span></code> is the object being moved from <code class="docutils literal notranslate"><span class="pre">source_location</span></code> to <code class="docutils literal notranslate"><span class="pre">destination</span></code>:</p>
|
||||
<ol class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">self.at_pre_move(destination)</span></code> (abort if return False)</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">source_location.at_pre_object_leave(self,</span> <span class="pre">destination)</span></code> (abort if return False)</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">destination.at_pre_object_receive(self,</span> <span class="pre">source_location)</span></code> (abort if return False)</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">source_location.at_object_leave(self,</span> <span class="pre">destination)</span></code></p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">self.announce_move_from(destination)</span></code></p></li>
|
||||
<li><p>(move happens here)</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">self.announce_move_to(source_location)</span></code></p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">destination.at_object_receive(self,</span> <span class="pre">source_location)</span></code></p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">self.at_post_move(source_location)</span></code></p></li>
|
||||
</ol>
|
||||
<p>All of these hooks can be overridden to customize movement behavior. In this case we are interested in controlling how items ‘enter’ and ‘leave’ our character - being ‘inside’ the character is the same as them ‘carrying’ it. We have three good hook-candidates to use for this.</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">.at_pre_object_receive</span></code> - used to check if you can actually pick something up, or if your equipment-store is full.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">.at_object_receive</span></code> - used to add the item to the equipmenthandler</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">.at_object_leave</span></code> - used to remove the item from the equipmenthandler</p></li>
|
||||
</ul>
|
||||
<p>You could also picture using <code class="docutils literal notranslate"><span class="pre">.at_pre_object_leave</span></code> to restrict dropping (cursed?) items, but
|
||||
we will skip that for this tutorial.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/character.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_pre_object_receive</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">moved_object</span><span class="p">,</span> <span class="n">source_location</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called by Evennia before object arrives 'in' this character (that is,</span>
|
||||
<span class="sd"> if they pick up something). If it returns False, move is aborted.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">validate_slot_usage</span><span class="p">(</span><span class="n">moved_object</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_object_receive</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">moved_object</span><span class="p">,</span> <span class="n">source_location</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Called by Evennia when an object arrives 'in' the character.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">moved_object</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_object_leave</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">moved_object</span><span class="p">,</span> <span class="n">destination</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Called by Evennia when object leaves the Character. </span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">moved_object</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Above we have assumed the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> (<code class="docutils literal notranslate"><span class="pre">.equipment</span></code>) has methods <code class="docutils literal notranslate"><span class="pre">.validate_slot_usage</span></code>, <code class="docutils literal notranslate"><span class="pre">.add</span></code> and <code class="docutils literal notranslate"><span class="pre">.remove</span></code>. But we haven’t actually added them yet - we just put some reasonable names! Before we can use this, we need to go actually adding those methods.</p>
|
||||
<p>When you do things like <code class="docutils literal notranslate"><span class="pre">create/drop</span> <span class="pre">monster:NPC</span></code>, the npc will briefly be in your inventory before being dropped on the ground. Since an NPC is not a valid thing to equip, the EquipmentHandler will complain with an <code class="docutils literal notranslate"><span class="pre">EquipmentError</span></code> (we define this see below). So we need to</p>
|
||||
</section>
|
||||
<section id="expanding-the-equipmenthandler">
|
||||
<h2><span class="section-number">5.3. </span>Expanding the Equipmenthandler<a class="headerlink" href="#expanding-the-equipmenthandler" title="Permalink to this headline">¶</a></h2>
|
||||
</section>
|
||||
<section id="validate-slot-usage">
|
||||
<h2><span class="section-number">5.4. </span><code class="docutils literal notranslate"><span class="pre">.validate_slot_usage</span></code><a class="headerlink" href="#validate-slot-usage" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Let’s start with implementing the first method we came up with above, <code class="docutils literal notranslate"><span class="pre">validate_slot_usage</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/equipment.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">Ability</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EquipmentError</span><span class="p">(</span><span class="ne">TypeError</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""All types of equipment-errors"""</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EquipmentHandler</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">max_slots</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Max amount of slots, based on CON defense (CON + 10)"""</span>
|
||||
<span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">,</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CON</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="mi">10</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">count_slots</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Count current slot usage"""</span>
|
||||
<span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span>
|
||||
<span class="n">wield_usage</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span>
|
||||
<span class="nb">getattr</span><span class="p">(</span><span class="n">slotobj</span><span class="p">,</span> <span class="s2">"size"</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="ow">or</span> <span class="mi">0</span>
|
||||
<span class="k">for</span> <span class="n">slot</span><span class="p">,</span> <span class="n">slotobj</span> <span class="ow">in</span> <span class="n">slots</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="n">slot</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">backpack_usage</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span>
|
||||
<span class="nb">getattr</span><span class="p">(</span><span class="n">slotobj</span><span class="p">,</span> <span class="s2">"size"</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="ow">or</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">slotobj</span> <span class="ow">in</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">wield_usage</span> <span class="o">+</span> <span class="n">backpack_usage</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">validate_slot_usage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Check if obj can fit in equipment, based on its size.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</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="n">EvAdventureObject</span><span class="p">):</span>
|
||||
<span class="c1"># in case we mix with non-evadventure objects</span>
|
||||
<span class="k">raise</span> <span class="n">EquipmentError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">obj</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is not something that can be equipped."</span><span class="p">)</span>
|
||||
|
||||
<span class="n">size</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">size</span>
|
||||
<span class="n">max_slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">max_slots</span>
|
||||
<span class="n">current_slot_usage</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">count_slots</span><span class="p">()</span>
|
||||
<span class="k">return</span> <span class="n">current_slot_usage</span> <span class="o">+</span> <span class="n">size</span> <span class="o"><=</span> <span class="n">max_slots</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">@property</span></code> decorator turns a method into a property so you don’t need to ‘call’ it.
|
||||
That is, you can access <code class="docutils literal notranslate"><span class="pre">.max_slots</span></code> instead of <code class="docutils literal notranslate"><span class="pre">.max_slots()</span></code>. In this case, it’s just a
|
||||
little less to type.</p>
|
||||
</aside>
|
||||
<p>We add two helpers - the <code class="docutils literal notranslate"><span class="pre">max_slots</span></code> <em>property</em> and <code class="docutils literal notranslate"><span class="pre">count_slots</span></code>, a method that calculate the current slots being in use. Let’s figure out how they work.</p>
|
||||
<section id="max-slots">
|
||||
<h3><span class="section-number">5.4.1. </span><code class="docutils literal notranslate"><span class="pre">.max_slots</span></code><a class="headerlink" href="#max-slots" title="Permalink to this headline">¶</a></h3>
|
||||
<p>For <code class="docutils literal notranslate"><span class="pre">max_slots</span></code>, remember that <code class="docutils literal notranslate"><span class="pre">.obj</span></code> on the handler is a back-reference to the <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> we put this handler on. <code class="docutils literal notranslate"><span class="pre">getattr</span></code> is a Python method for retrieving a named property on an object. The <code class="docutils literal notranslate"><span class="pre">Enum</span></code> <code class="docutils literal notranslate"><span class="pre">Ability.CON.value</span></code> is the string <code class="docutils literal notranslate"><span class="pre">Constitution</span></code> (check out the <a class="reference internal" href="Beginner-Tutorial-Utilities.html"><span class="doc std std-doc">first Utility and Enums tutorial</span></a> if you don’t recall).</p>
|
||||
<p>So to be clear,</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">,</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CON</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="o">+</span> <span class="mi">10</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>is the same as writing</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nb">getattr</span><span class="p">(</span><span class="n">your_character</span><span class="p">,</span> <span class="s2">"Constitution"</span><span class="p">)</span> <span class="o">+</span> <span class="mi">10</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>which is the same as doing something like this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">your_character</span><span class="o">.</span><span class="n">Constitution</span> <span class="o">+</span> <span class="mi">10</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In our code we write <code class="docutils literal notranslate"><span class="pre">getattr(self.obj,</span> <span class="pre">Ability.CON.value,</span> <span class="pre">1)</span></code> - that extra <code class="docutils literal notranslate"><span class="pre">1</span></code> means that if there should happen to <em>not</em> be a property “Constitution” on <code class="docutils literal notranslate"><span class="pre">self.obj</span></code>, we should not error out but just return 1.</p>
|
||||
</section>
|
||||
<section id="count-slots">
|
||||
<h3><span class="section-number">5.4.2. </span><code class="docutils literal notranslate"><span class="pre">.count_slots</span></code><a class="headerlink" href="#count-slots" title="Permalink to this headline">¶</a></h3>
|
||||
<p>In this helper we use two Python tools - the <code class="docutils literal notranslate"><span class="pre">sum()</span></code> function and a <a class="reference external" href="https://www.w3schools.com/python/python_lists_comprehension.asp">list comprehension</a>. The former simply adds the values of any iterable together. The latter is a more efficient way to create a list:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>new_list = [item for item in some_iterable if condition]
|
||||
all_above_5 = [num for num in range(10) if num > 5] # [6, 7, 8, 9]
|
||||
all_below_5 = [num for num in range(10) if num < 5] # [0, 1, 2, 3, 4]
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>To make it easier to understand, try reading the last line above as “for every number in the range 0-9, pick all with a value below 5 and make a list of them”. You can also embed such comprehensions directly in a function call like <code class="docutils literal notranslate"><span class="pre">sum()</span></code> without using <code class="docutils literal notranslate"><span class="pre">[]</span></code> around it.</p>
|
||||
<p>In <code class="docutils literal notranslate"><span class="pre">count_slots</span></code> we have this code:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">wield_usage</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span>
|
||||
<span class="nb">getattr</span><span class="p">(</span><span class="n">slotobj</span><span class="p">,</span> <span class="s2">"size"</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
|
||||
<span class="k">for</span> <span class="n">slot</span><span class="p">,</span> <span class="n">slotobj</span> <span class="ow">in</span> <span class="n">slots</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="n">slot</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We should be able to follow all except <code class="docutils literal notranslate"><span class="pre">slots.items()</span></code>. Since <code class="docutils literal notranslate"><span class="pre">slots</span></code> is a <code class="docutils literal notranslate"><span class="pre">dict</span></code>, we can use <code class="docutils literal notranslate"><span class="pre">.items()</span></code> to get a sequence of <code class="docutils literal notranslate"><span class="pre">(key,</span> <span class="pre">value)</span></code> pairs. We store these in <code class="docutils literal notranslate"><span class="pre">slot</span></code> and <code class="docutils literal notranslate"><span class="pre">slotobj</span></code>. So the above can be understood as “for every <code class="docutils literal notranslate"><span class="pre">slot</span></code> and <code class="docutils literal notranslate"><span class="pre">slotobj</span></code>-pair in <code class="docutils literal notranslate"><span class="pre">slots</span></code>, check which slot location it is. If it is <em>not</em> in the backpack, get its size and add it to the list. Sum over all these
|
||||
sizes”.</p>
|
||||
<p>A less compact but maybe more readonable way to write this would be:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">backpack_item_sizes</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">for</span> <span class="n">slot</span><span class="p">,</span> <span class="n">slotobj</span> <span class="ow">in</span> <span class="n">slots</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
|
||||
<span class="k">if</span> <span class="n">slot</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">:</span>
|
||||
<span class="n">size</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">slotobj</span><span class="p">,</span> <span class="s2">"size"</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
|
||||
<span class="n">backpack_item_sizes</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">size</span><span class="p">)</span>
|
||||
<span class="n">wield_usage</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">backpack_item_sizes</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The same is done for the items actually in the BACKPACK slot. The total sizes are added
|
||||
together.</p>
|
||||
</section>
|
||||
<section id="validating-slots">
|
||||
<h3><span class="section-number">5.4.3. </span>Validating slots<a class="headerlink" href="#validating-slots" title="Permalink to this headline">¶</a></h3>
|
||||
<p>With these helpers in place, <code class="docutils literal notranslate"><span class="pre">validate_slot_usage</span></code> now becomes simple. We use <code class="docutils literal notranslate"><span class="pre">max_slots</span></code> to see how much we can carry. We then get how many slots we are already using (with <code class="docutils literal notranslate"><span class="pre">count_slots</span></code>) and see if our new <code class="docutils literal notranslate"><span class="pre">obj</span></code>’s size would be too much for us.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="add-and-remove">
|
||||
<h2><span class="section-number">5.5. </span><code class="docutils literal notranslate"><span class="pre">.add</span></code> and <code class="docutils literal notranslate"><span class="pre">.remove</span></code><a class="headerlink" href="#add-and-remove" title="Permalink to this headline">¶</a></h2>
|
||||
<p>We will make it so <code class="docutils literal notranslate"><span class="pre">.add</span></code> puts something in the <code class="docutils literal notranslate"><span class="pre">BACKPACK</span></code> location and <code class="docutils literal notranslate"><span class="pre">remove</span></code> drops it, wherever it is (even if it was in your hands).</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/equipment.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">Ability</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EquipmentHandler</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Put something in the backpack.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">validate_slot_usage</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]</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="bp">self</span><span class="o">.</span><span class="n">_save</span><span class="p">()</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">remove</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj_or_slot</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Remove specific object or objects from a slot.</span>
|
||||
|
||||
<span class="sd"> Returns a list of 0, 1 or more objects removed from inventory.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span>
|
||||
<span class="n">ret</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">obj_or_slot</span><span class="p">,</span> <span class="n">WieldLocation</span><span class="p">):</span>
|
||||
<span class="c1"># a slot; if this fails, obj_or_slot must be obj</span>
|
||||
<span class="k">if</span> <span class="n">obj_or_slot</span> <span class="ow">is</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">:</span>
|
||||
<span class="c1"># empty entire backpack</span>
|
||||
<span class="n">ret</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">obj_or_slot</span><span class="p">])</span>
|
||||
<span class="n">slots</span><span class="p">[</span><span class="n">obj_or_slot</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">ret</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">obj_or_slot</span><span class="p">])</span>
|
||||
<span class="n">slots</span><span class="p">[</span><span class="n">obj_or_slot</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="k">elif</span> <span class="n">obj_or_slot</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span><span class="o">.</span><span class="n">values</span><span class="p">():</span>
|
||||
<span class="c1"># obj in use/wear slot</span>
|
||||
<span class="k">for</span> <span class="n">slot</span><span class="p">,</span> <span class="n">objslot</span> <span class="ow">in</span> <span class="n">slots</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
|
||||
<span class="k">if</span> <span class="n">objslot</span> <span class="ow">is</span> <span class="n">obj_or_slot</span><span class="p">:</span>
|
||||
<span class="n">slots</span><span class="p">[</span><span class="n">slot</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">ret</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">objslot</span><span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">obj_or_slot</span> <span class="ow">in</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]:</span> <span class="c1"># obj in backpack slot</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">obj_or_slot</span><span class="p">)</span>
|
||||
<span class="n">ret</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">obj_or_slot</span><span class="p">)</span>
|
||||
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
|
||||
<span class="k">pass</span>
|
||||
<span class="k">if</span> <span class="n">ret</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_save</span><span class="p">()</span>
|
||||
<span class="k">return</span> <span class="n">ret</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In <code class="docutils literal notranslate"><span class="pre">.add</span></code>, we make use of <code class="docutils literal notranslate"><span class="pre">validate_slot_usage</span></code> to
|
||||
double-check we can actually fit the thing, then we add the item to the backpack.</p>
|
||||
<p>In <code class="docutils literal notranslate"><span class="pre">.remove</span></code>, we allow emptying both by <code class="docutils literal notranslate"><span class="pre">WieldLocation</span></code> or by explicitly saying which object to remove. Note that the first <code class="docutils literal notranslate"><span class="pre">if</span></code> statement checks if <code class="docutils literal notranslate"><span class="pre">obj_or_slot</span></code> is a slot. So if that fails then code in the other <code class="docutils literal notranslate"><span class="pre">elif</span></code> can safely assume that it must instead be an object!</p>
|
||||
<p>Any removed objects are returned. If we gave <code class="docutils literal notranslate"><span class="pre">BACKPACK</span></code> as the slot, we empty the backpack and return all items inside it.</p>
|
||||
<p>Whenever we change the equipment loadout we must make sure to <code class="docutils literal notranslate"><span class="pre">._save()</span></code> the result, or it will be lost after a server reload.</p>
|
||||
</section>
|
||||
<section id="moving-things-around">
|
||||
<h2><span class="section-number">5.6. </span>Moving things around<a class="headerlink" href="#moving-things-around" title="Permalink to this headline">¶</a></h2>
|
||||
<p>With the help of <code class="docutils literal notranslate"><span class="pre">.remove()</span></code> and <code class="docutils literal notranslate"><span class="pre">.add()</span></code> we can get things in and out of the <code class="docutils literal notranslate"><span class="pre">BACKPACK</span></code> equipment location. We also need to grab stuff from the backpack and wield or wear it. We add a <code class="docutils literal notranslate"><span class="pre">.move</span></code> method on the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> to do this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/equipment.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">Ability</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EquipmentHandler</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Move object from backpack to its intended `inventory_use_slot`."""</span>
|
||||
|
||||
<span class="c1"># make sure to remove from equipment/backpack first, to avoid double-adding</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">validate_slot_usage</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
|
||||
<span class="k">return</span>
|
||||
|
||||
<span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span>
|
||||
<span class="n">use_slot</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="s2">"inventory_use_slot"</span><span class="p">,</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">)</span>
|
||||
|
||||
<span class="n">to_backpack</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">if</span> <span class="n">use_slot</span> <span class="ow">is</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">:</span>
|
||||
<span class="c1"># two-handed weapons can't co-exist with weapon/shield-hand used items</span>
|
||||
<span class="n">to_backpack</span> <span class="o">=</span> <span class="p">[</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">],</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">]]</span>
|
||||
<span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">]</span> <span class="o">=</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">slots</span><span class="p">[</span><span class="n">use_slot</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span>
|
||||
<span class="k">elif</span> <span class="n">use_slot</span> <span class="ow">in</span> <span class="p">(</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">,</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">):</span>
|
||||
<span class="c1"># can't keep a two-handed weapon if adding a one-handed weapon or shield</span>
|
||||
<span class="n">to_backpack</span> <span class="o">=</span> <span class="p">[</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">]]</span>
|
||||
<span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="n">slots</span><span class="p">[</span><span class="n">use_slot</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span>
|
||||
<span class="k">elif</span> <span class="n">use_slot</span> <span class="ow">is</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">:</span>
|
||||
<span class="c1"># it belongs in backpack, so goes back to it</span>
|
||||
<span class="n">to_backpack</span> <span class="o">=</span> <span class="p">[</span><span class="n">obj</span><span class="p">]</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># for others (body, head), just replace whatever's there</span>
|
||||
<span class="n">replaced</span> <span class="o">=</span> <span class="p">[</span><span class="n">obj</span><span class="p">]</span>
|
||||
<span class="n">slots</span><span class="p">[</span><span class="n">use_slot</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span>
|
||||
|
||||
<span class="k">for</span> <span class="n">to_backpack_obj</span> <span class="ow">in</span> <span class="n">to_backpack</span><span class="p">:</span>
|
||||
<span class="c1"># put stuff in backpack</span>
|
||||
<span class="n">slots</span><span class="p">[</span><span class="n">use_slot</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">to_backpack_obj</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># store new state</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">_save</span><span class="p">()</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here we remember that every <code class="docutils literal notranslate"><span class="pre">EvAdventureObject</span></code> has an <code class="docutils literal notranslate"><span class="pre">inventory_use_slot</span></code> property that tells us where it goes. So we just need to move the object to that slot, replacing whatever is in that place from before. Anything we replace goes back to the backpack.</p>
|
||||
</section>
|
||||
<section id="get-everything">
|
||||
<h2><span class="section-number">5.7. </span>Get everything<a class="headerlink" href="#get-everything" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In order to visualize our inventory, we need some method to get everything we are carrying.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/equipment.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">Ability</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EquipmentHandler</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">all</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Get all objects in inventory, regardless of location.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span>
|
||||
<span class="n">lst</span> <span class="o">=</span> <span class="p">[</span>
|
||||
<span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">],</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">],</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">],</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BODY</span><span class="p">],</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BODY</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">HEAD</span><span class="p">],</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">HEAD</span><span class="p">),</span>
|
||||
<span class="p">]</span> <span class="o">+</span> <span class="p">[(</span><span class="n">item</span><span class="p">,</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">)</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">]]</span>
|
||||
<span class="k">return</span> <span class="n">lst</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here we get all the equipment locations and add their contents together into a list of tuples
|
||||
<code class="docutils literal notranslate"><span class="pre">[(item,</span> <span class="pre">WieldLocation),</span> <span class="pre">...]</span></code>. This is convenient for display.</p>
|
||||
</section>
|
||||
<section id="weapon-and-armor">
|
||||
<h2><span class="section-number">5.8. </span>Weapon and armor<a class="headerlink" href="#weapon-and-armor" title="Permalink to this headline">¶</a></h2>
|
||||
<p>It’s convenient to have the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> easily tell you what weapon is currently wielded and what <em>armor</em> level all worn equipment provides. Otherwise you’d need to figure out what item is in which wield-slot and to add up armor slots manually every time you need to know.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/equipment.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">Ability</span>
|
||||
<span class="kn">from</span> <span class="nn">.objects</span> <span class="kn">import</span> <span class="n">get_bare_hand</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EquipmentHandler</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">armor</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span>
|
||||
<span class="k">return</span> <span class="nb">sum</span><span class="p">(</span>
|
||||
<span class="p">(</span>
|
||||
<span class="c1"># armor is listed using its defense, so we remove 10 from it</span>
|
||||
<span class="c1"># (11 is base no-armor value in Knave)</span>
|
||||
<span class="nb">getattr</span><span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BODY</span><span class="p">],</span> <span class="s2">"armor"</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
|
||||
<span class="c1"># shields and helmets are listed by their bonus to armor</span>
|
||||
<span class="nb">getattr</span><span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span><span class="p">],</span> <span class="s2">"armor"</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
|
||||
<span class="nb">getattr</span><span class="p">(</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">HEAD</span><span class="p">],</span> <span class="s2">"armor"</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
|
||||
<span class="p">)</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">weapon</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="c1"># first checks two-handed wield, then one-handed; the two</span>
|
||||
<span class="c1"># should never appear simultaneously anyhow (checked in `move` method).</span>
|
||||
<span class="n">slots</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">slots</span>
|
||||
<span class="n">weapon</span> <span class="o">=</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span><span class="p">]</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">weapon</span><span class="p">:</span>
|
||||
<span class="n">weapon</span> <span class="o">=</span> <span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">]</span>
|
||||
<span class="c1"># if we still don't have a weapon, we return None here</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">weapon</span><span class="p">:</span>
|
||||
<span class="o">~</span> <span class="n">weapon</span> <span class="o">=</span> <span class="n">get_bare_hands</span><span class="p">()</span>
|
||||
<span class="k">return</span> <span class="n">weapon</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In the <code class="docutils literal notranslate"><span class="pre">.armor()</span></code> method we get the item (if any) out of each relevant wield-slot (body, shield, head), and grab their <code class="docutils literal notranslate"><span class="pre">armor</span></code> Attribute. We then <code class="docutils literal notranslate"><span class="pre">sum()</span></code> them all up.</p>
|
||||
<p>In <code class="docutils literal notranslate"><span class="pre">.weapon()</span></code>, we simply check which of the possible weapon slots (weapon-hand or two-hands) have something in them. If not we fall back to the ‘Bare Hands’ object we created in the <a class="reference internal" href="Beginner-Tutorial-Objects.html#your-bare-hands"><span class="std std-doc">Object tutorial lesson</span></a> earlier.</p>
|
||||
<section id="fixing-the-character-class">
|
||||
<h3><span class="section-number">5.8.1. </span>Fixing the Character class<a class="headerlink" href="#fixing-the-character-class" title="Permalink to this headline">¶</a></h3>
|
||||
<p>So we have added our equipment handler which validate what we put in it. This will however lead to a problem when we create things like NPCs in game, e.g. with</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>create/drop monster:evadventure.npcs.EvAdventureNPC
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The problem is that when the/ monster is created it will briefly appear in your inventory before being dropped, so this code will fire on you when you do that (assuming you are an <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code>):</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/characters.py</span>
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_object_receive</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">moved_object</span><span class="p">,</span> <span class="n">source_location</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Called by Evennia when an object arrives 'in' the character.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">moved_object</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>At this means that the equipmenthandler will check the NPC, and since it’s not a equippable thing, an <code class="docutils literal notranslate"><span class="pre">EquipmentError</span></code> will be raised, failing the creation. Since we want to be able to create npcs etc easily, we will handle this error with a <code class="docutils literal notranslate"><span class="pre">try...except</span></code> statement like so:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/characters.py</span>
|
||||
<span class="c1"># ... </span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">logger</span>
|
||||
<span class="kn">from</span> <span class="nn">.equipment</span> <span class="kn">import</span> <span class="n">EquipmentError</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureCharacter</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_object_receive</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">moved_object</span><span class="p">,</span> <span class="n">source_location</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Called by Evennia when an object arrives 'in' the character.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">moved_object</span><span class="p">)</span>
|
||||
<span class="k">except</span> <span class="n">EquipmentError</span><span class="p">:</span>
|
||||
<span class="n">logger</span><span class="o">.</span><span class="n">log_trace</span><span class="p">()</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Using Evennia’s <code class="docutils literal notranslate"><span class="pre">logger.log_trace()</span></code> we catch the error and direct it to the server log. This allows you to see if there are real errors here as well, but once things work and these errors are spammy, you can also just replace the <code class="docutils literal notranslate"><span class="pre">logger.log_trace()</span></code> line with a <code class="docutils literal notranslate"><span class="pre">pass</span></code> to hide these errors.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="extra-credits">
|
||||
<h2><span class="section-number">5.9. </span>Extra credits<a class="headerlink" href="#extra-credits" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This covers the basic functionality of the equipment handler. There are other useful methods that
|
||||
can be added:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Given an item, figure out which equipment slot it is currently in</p></li>
|
||||
<li><p>Make a string representing the current loadout</p></li>
|
||||
<li><p>Get everything in the backpack (only)</p></li>
|
||||
<li><p>Get all wieldable items (weapons, shields) from backpack</p></li>
|
||||
<li><p>Get all usable items (items with a use-location of <code class="docutils literal notranslate"><span class="pre">BACKPACK</span></code>) from the backpack</p></li>
|
||||
</ul>
|
||||
<p>Experiment with adding those. A full example is found in
|
||||
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.equipment.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/equipment.py</span></a>.</p>
|
||||
</section>
|
||||
<section id="unit-testing">
|
||||
<h2><span class="section-number">5.10. </span>Unit Testing<a class="headerlink" href="#unit-testing" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/tests/test_equipment.py</span></code>.</p>
|
||||
</div></blockquote>
|
||||
<aside class="sidebar">
|
||||
<p>See <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_equipment.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/tests/test_equipment.py</span></a>
|
||||
for a finished testing example.</p>
|
||||
</aside>
|
||||
<p>To test the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code>, easiest is create an <code class="docutils literal notranslate"><span class="pre">EvAdventureCharacter</span></code> (this should by now
|
||||
have <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code> available on itself as <code class="docutils literal notranslate"><span class="pre">.equipment</span></code>) and a few test objects; then test
|
||||
passing these into the handler’s methods.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/tests/test_equipment.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">create</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">BaseEvenniaTest</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">..objects</span> <span class="kn">import</span> <span class="n">EvAdventureObject</span><span class="p">,</span> <span class="n">EvAdventureHelmet</span><span class="p">,</span> <span class="n">EvAdventureWeapon</span>
|
||||
<span class="kn">from</span> <span class="nn">..enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span>
|
||||
<span class="kn">from</span> <span class="nn">..characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">TestEquipment</span><span class="p">(</span><span class="n">BaseEvenniaTest</span><span class="p">):</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">setUp</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">character</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s1">'testchar'</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">helmet</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureHelmet</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"helmet"</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">weapon</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureWeapon</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"weapon"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">test_add_remove</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">],</span>
|
||||
<span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">]</span>
|
||||
<span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">helmet</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">character</span><span class="o">.</span><span class="n">equipment</span><span class="o">.</span><span class="n">slots</span><span class="p">[</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span><span class="p">],</span> <span class="p">[])</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="summary">
|
||||
<h2><span class="section-number">5.11. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline">¶</a></h2>
|
||||
<p><em>Handlers</em> are useful for grouping functionality together. Now that we spent our time making the <code class="docutils literal notranslate"><span class="pre">EquipmentHandler</span></code>, we shouldn’t need to worry about item-slots anymore - the handler ‘handles’ all the details for us. As long as we call its methods, the details can be forgotten about.</p>
|
||||
<p>We also learned to use <em>hooks</em> to tie <em>Knave</em>’s custom equipment handling into Evennia.</p>
|
||||
<p>With <code class="docutils literal notranslate"><span class="pre">Characters</span></code>, <code class="docutils literal notranslate"><span class="pre">Objects</span></code> and now <code class="docutils literal notranslate"><span class="pre">Equipment</span></code> in place, we should be able to move on to character generation - where players get to make their own character!</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="Beginner-Tutorial-Chargen.html" title="6. Character Generation"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Objects.html" title="4. In-game Objects and items"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">5. </span>Handling Equipment</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,356 @@
|
|||
|
||||
<!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>8. Non-Player-Characters — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="9. Combat base framework" href="Beginner-Tutorial-Combat-Base.html" />
|
||||
<link rel="prev" title="7. In-game Rooms" href="Beginner-Tutorial-Rooms.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Combat-Base.html" title="9. Combat base framework"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Rooms.html" title="7. In-game Rooms"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">8. </span>Non-Player-Characters</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">8. Non-Player-Characters</a><ul>
|
||||
<li><a class="reference internal" href="#the-npc-base-class">8.1. The NPC base class</a></li>
|
||||
<li><a class="reference internal" href="#testing">8.2. Testing</a></li>
|
||||
<li><a class="reference internal" href="#conclusions">8.3. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Rooms.html"
|
||||
title="previous chapter"><span class="section-number">7. </span>In-game Rooms</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Combat-Base.html"
|
||||
title="next chapter"><span class="section-number">9. </span>Combat base framework</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-NPCs.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="non-player-characters">
|
||||
<h1><span class="section-number">8. </span>Non-Player-Characters<a class="headerlink" href="#non-player-characters" title="Permalink to this headline">¶</a></h1>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">vNPCs</p>
|
||||
<p>You should usually avoid creating hundreds of NPC objects to populate your ‘busy town’ - in a text game so many NPCs will just spam the screen and annoy your players. Since this is a text game, you can usually get away with using <em>vNPcs</em> - virtual NPCs. vNPCs are only described in text - a room could be described as a bustling street, farmers can be described shouting to each other. Using room descriptions for this works well, but the tutorial lesson about <a class="reference internal" href="Beginner-Tutorial-Rooms.html"><span class="doc std std-doc">EvAdventure Rooms</span></a> has a section called <a class="reference internal" href="Beginner-Tutorial-Rooms.html#adding-life-to-a-room"><span class="std std-doc">adding life to a room</span></a> that can be used for making vNPCs appear to do things in the background.</p>
|
||||
</aside>
|
||||
<p><em>Non-Player-Characters</em>, or NPCs, is the common term for all active agents that are <em>not</em> controlled by players. NPCs could be anything from merchants and quest givers, to monsters and bosses. They could also be ‘flavor’ - townsfolk doing their chores, farmers tending their fields - there to make the world feel “more alive”.</p>
|
||||
<p>In this lesson we will create the base class of <em>EvAdventure</em> NPCs based on the <em>Knave</em> ruleset. According to the <em>Knave</em> rules, NPCs have some simplified stats compared to the <a class="reference internal" href="Beginner-Tutorial-Characters.html"><span class="doc std std-doc">PC characters</span></a> we designed earlier.</p>
|
||||
<div style="clear: right;"></div>
|
||||
<section id="the-npc-base-class">
|
||||
<h2><span class="section-number">8.1. </span>The NPC base class<a class="headerlink" href="#the-npc-base-class" title="Permalink to this headline">¶</a></h2>
|
||||
<aside class="sidebar">
|
||||
<p>See <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.npcs.html#evennia-contrib-tutorials-evadventure-npcs"><span class="std std-ref">evennia/contrib/tutorials/evadventure/npcs.py</span></a> for a ready-made example of an npc module.</p>
|
||||
</aside>
|
||||
<blockquote>
|
||||
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/npcs.py</span></code>.</p>
|
||||
</div></blockquote>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span>
|
||||
<span class="normal">22</span>
|
||||
<span class="normal">23</span>
|
||||
<span class="normal">24</span>
|
||||
<span class="normal">25</span>
|
||||
<span class="normal">26</span>
|
||||
<span class="normal">27</span>
|
||||
<span class="normal">28</span>
|
||||
<span class="normal">29</span>
|
||||
<span class="normal">30</span>
|
||||
<span class="normal">31</span>
|
||||
<span class="normal">32</span>
|
||||
<span class="normal">33</span>
|
||||
<span class="normal">34</span>
|
||||
<span class="normal">35</span>
|
||||
<span class="normal">36</span>
|
||||
<span class="normal">37</span>
|
||||
<span class="normal">38</span>
|
||||
<span class="normal">39</span>
|
||||
<span class="normal">40</span>
|
||||
<span class="normal">41</span>
|
||||
<span class="normal">42</span>
|
||||
<span class="normal">43</span>
|
||||
<span class="normal">44</span>
|
||||
<span class="normal">45</span>
|
||||
<span class="normal">46</span>
|
||||
<span class="normal">47</span>
|
||||
<span class="normal">48</span>
|
||||
<span class="normal">49</span>
|
||||
<span class="normal">50</span>
|
||||
<span class="normal">51</span>
|
||||
<span class="normal">52</span>
|
||||
<span class="normal">53</span>
|
||||
<span class="normal">54</span>
|
||||
<span class="normal">55</span>
|
||||
<span class="normal">56</span>
|
||||
<span class="normal">57</span>
|
||||
<span class="normal">58</span>
|
||||
<span class="normal">59</span>
|
||||
<span class="normal">60</span>
|
||||
<span class="normal">61</span>
|
||||
<span class="normal">62</span>
|
||||
<span class="normal">63</span>
|
||||
<span class="normal">64</span>
|
||||
<span class="normal">65</span>
|
||||
<span class="normal">66</span>
|
||||
<span class="normal">67</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/npcs.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span><span class="p">,</span> <span class="n">AttributeProperty</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.characters</span> <span class="kn">import</span> <span class="n">LivingMixin</span>
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">Ability</span>
|
||||
|
||||
|
||||
<span class="hll"><span class="k">class</span> <span class="nc">EvAdventureNPC</span><span class="p">(</span><span class="n">LivingMixin</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">):</span>
|
||||
</span><span class="w"> </span><span class="sd">"""Base class for NPCs"""</span>
|
||||
|
||||
<span class="hll"> <span class="n">is_pc</span> <span class="o">=</span> <span class="kc">False</span>
|
||||
</span><span class="hll"> <span class="n">hit_dice</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
</span> <span class="n">armor</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># +10 to get armor defense</span>
|
||||
<span class="hll"> <span class="n">hp_multiplier</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># 4 default in Knave</span>
|
||||
</span> <span class="n">hp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># internal tracking, use .hp property</span>
|
||||
<span class="hll"> <span class="n">morale</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">9</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
</span> <span class="n">allegiance</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="n">Ability</span><span class="o">.</span><span class="n">ALLEGIANCE_HOSTILE</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
<span class="hll">
|
||||
</span> <span class="n">weapon</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="n">BARE_HANDS</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># instead of inventory</span>
|
||||
<span class="n">coins</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="c1"># coin loot</span>
|
||||
|
||||
<span class="hll"> <span class="n">is_idle</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
</span>
|
||||
<span class="hll"> <span class="nd">@property</span>
|
||||
</span> <span class="k">def</span> <span class="nf">strength</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">dexterity</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">constitution</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">intelligence</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">wisdom</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">charisma</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span>
|
||||
|
||||
<span class="nd">@property</span>
|
||||
<span class="k">def</span> <span class="nf">hp_max</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">hit_dice</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_multiplier</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="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Start with max health.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">hp</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">hp_max</span>
|
||||
<span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"npcs"</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"group"</span><span class="p">)</span>
|
||||
</span>
|
||||
<span class="hll"> <span class="k">def</span> <span class="nf">ai_next_action</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><span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> The system should regularly poll this method to have </span>
|
||||
<span class="sd"> the NPC do their next AI action. </span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">pass</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 9</strong>: By use of <em>multiple inheritance</em> we use the <code class="docutils literal notranslate"><span class="pre">LinvingMixin</span></code> we created in the <a class="reference internal" href="Beginner-Tutorial-Characters.html"><span class="doc std std-doc">Character lesson</span></a>. This includes a lot of useful methods, such as showing our ‘hurt level’, methods to use to heal, hooks to call when getting attacked, hurt and so on. We can re-use all of those in upcoming NPC subclasses.</p></li>
|
||||
<li><p><strong>Line 12</strong>: The <code class="docutils literal notranslate"><span class="pre">is_pc</span></code> is a quick and convenient way to check if this is, well, a PC or not. We will use it in the upcoming <a class="reference internal" href="Beginner-Tutorial-Combat-Base.html"><span class="doc std std-doc">Combat base lesson</span></a>.</p></li>
|
||||
<li><p><strong>Line 13</strong>: The NPC is simplified in that all stats are just based on the <code class="docutils literal notranslate"><span class="pre">Hit</span> <span class="pre">dice</span></code> number (see <strong>Lines 25-51</strong>). We store <code class="docutils literal notranslate"><span class="pre">armor</span></code> and a <code class="docutils literal notranslate"><span class="pre">weapon</span></code> as direct <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attributes</span></a> on the class rather than bother implementing a full equipment system.</p></li>
|
||||
<li><p><strong>Lines 17, 18</strong>: The <code class="docutils literal notranslate"><span class="pre">morale</span></code> and <code class="docutils literal notranslate"><span class="pre">allegiance</span></code> are <em>Knave</em> properties determining how likely the NPC is to flee in a combat situation and if they are hostile or friendly.</p></li>
|
||||
<li><p><strong>Line 19</strong>: The <code class="docutils literal notranslate"><span class="pre">is_idle</span></code> Attribute is a useful property. It should be available on all NPCs and will be used to disable AI entirely.</p></li>
|
||||
<li><p><strong>Line 59</strong>: We make sure to tag NPCs. We may want to group different NPCs together later, for example to have all NPCs with the same tag respond if one of them is attacked.</p></li>
|
||||
<li><p><strong>Line 61</strong>: The <code class="docutils literal notranslate"><span class="pre">ai_next_action</span></code> is a method we prepare for the system to be able to ask the NPC ‘what do you want to do next?’. In it we will add all logic related to the artificial intelligence of the NPC - such as walking around, attacking and performing other actions.</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="testing">
|
||||
<h2><span class="section-number">8.2. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/tests/test_npcs.py</span></code></p>
|
||||
</div></blockquote>
|
||||
<p>Not so much to test yet, but we will be using the same module to test other aspects of NPCs in the future, so let’s create it now.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/tests/test_npcs.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_object</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTest</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">npcs</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">TestNPCBase</span><span class="p">(</span><span class="n">EvenniaTest</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Test the NPC base class"""</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">test_npc_base</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="n">npc</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span>
|
||||
<span class="n">npcs</span><span class="o">.</span><span class="n">EvAdventureNPC</span><span class="p">,</span>
|
||||
<span class="n">key</span><span class="o">=</span><span class="s2">"TestNPC"</span><span class="p">,</span>
|
||||
<span class="n">attributes</span><span class="o">=</span><span class="p">[(</span><span class="s2">"hit_dice"</span><span class="p">,</span> <span class="mi">4</span><span class="p">)],</span> <span class="c1"># set hit_dice to 4</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">hp_multiplier</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">hp</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">strength</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">npc</span><span class="o">.</span><span class="n">charisma</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
|
||||
|
||||
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Nothing special here. Note how the <code class="docutils literal notranslate"><span class="pre">create_object</span></code> helper function takes <code class="docutils literal notranslate"><span class="pre">attributes</span></code> as a keyword. This is a list of tuples we use to set different values than the default ones to Attributes. We then check a few of the properties to make sure they return what we expect.</p>
|
||||
</section>
|
||||
<section id="conclusions">
|
||||
<h2><span class="section-number">8.3. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In <em>Knave</em>, an NPC is a simplified version of a Player Character. In other games and rule systems, they may be all but identical.</p>
|
||||
<p>With the NPC class in place, we have enough to create a ‘test dummy’. Since it has no AI yet, it won’t fight back, but it will be enough to have something to hit when we test our combat in the upcoming lessons.</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="Beginner-Tutorial-Combat-Base.html" title="9. Combat base framework"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Rooms.html" title="7. In-game Rooms"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">8. </span>Non-Player-Characters</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,612 @@
|
|||
|
||||
<!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>4. In-game Objects and items — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="5. Handling Equipment" href="Beginner-Tutorial-Equipment.html" />
|
||||
<link rel="prev" title="3. Player Characters" href="Beginner-Tutorial-Characters.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Equipment.html" title="5. Handling Equipment"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Characters.html" title="3. Player Characters"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">4. </span>In-game Objects and items</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">4. In-game Objects and items</a><ul>
|
||||
<li><a class="reference internal" href="#new-enums">4.1. New Enums</a></li>
|
||||
<li><a class="reference internal" href="#the-base-object">4.2. The base object</a><ul>
|
||||
<li><a class="reference internal" href="#using-attributes-or-not">4.2.1. Using Attributes or not</a></li>
|
||||
<li><a class="reference internal" href="#creating-tags-in-at-object-creation">4.2.2. Creating tags in <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#other-object-types">4.3. Other object types</a></li>
|
||||
<li><a class="reference internal" href="#consumables">4.4. Consumables</a></li>
|
||||
<li><a class="reference internal" href="#weapons">4.5. Weapons</a></li>
|
||||
<li><a class="reference internal" href="#magic">4.6. Magic</a></li>
|
||||
<li><a class="reference internal" href="#armor">4.7. Armor</a></li>
|
||||
<li><a class="reference internal" href="#your-bare-hands">4.8. Your Bare hands</a></li>
|
||||
<li><a class="reference internal" href="#testing-and-extra-credits">4.9. Testing and Extra credits</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Characters.html"
|
||||
title="previous chapter"><span class="section-number">3. </span>Player Characters</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Equipment.html"
|
||||
title="next chapter"><span class="section-number">5. </span>Handling Equipment</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Objects.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="in-game-objects-and-items">
|
||||
<h1><span class="section-number">4. </span>In-game Objects and items<a class="headerlink" href="#in-game-objects-and-items" title="Permalink to this headline">¶</a></h1>
|
||||
<p>In the previous lesson we established what a ‘Character’ is in our game. Before we continue
|
||||
we also need to have a notion what an ‘item’ or ‘object’ is.</p>
|
||||
<p>Looking at <em>Knave</em>’s item lists, we can get some ideas of what we need to track:</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">size</span></code> - this is how many ‘slots’ the item uses in the character’s inventory.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">value</span></code> - a base value if we want to sell or buy the item.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">inventory_use_slot</span></code> - some items can be worn or wielded. For example, a helmet needs to be worn on the head and a shield in the shield hand. Some items can’t be used this way at all, but only belong in the backpack.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">obj_type</span></code> - Which ‘type’ of item this is.</p></li>
|
||||
</ul>
|
||||
<section id="new-enums">
|
||||
<h2><span class="section-number">4.1. </span>New Enums<a class="headerlink" href="#new-enums" title="Permalink to this headline">¶</a></h2>
|
||||
<p>We added a few enumberations for Abilities back in the <a class="reference internal" href="Beginner-Tutorial-Utilities.html"><span class="doc std std-doc">Utilities tutorial</span></a>.
|
||||
Before we continue, let’s expand with enums for use-slots and object types.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/enums.py</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">WieldLocation</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
|
||||
|
||||
<span class="n">BACKPACK</span> <span class="o">=</span> <span class="s2">"backpack"</span>
|
||||
<span class="n">WEAPON_HAND</span> <span class="o">=</span> <span class="s2">"weapon_hand"</span>
|
||||
<span class="n">SHIELD_HAND</span> <span class="o">=</span> <span class="s2">"shield_hand"</span>
|
||||
<span class="n">TWO_HANDS</span> <span class="o">=</span> <span class="s2">"two_handed_weapons"</span>
|
||||
<span class="n">BODY</span> <span class="o">=</span> <span class="s2">"body"</span> <span class="c1"># armor</span>
|
||||
<span class="n">HEAD</span> <span class="o">=</span> <span class="s2">"head"</span> <span class="c1"># helmets</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">ObjType</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
|
||||
|
||||
<span class="n">WEAPON</span> <span class="o">=</span> <span class="s2">"weapon"</span>
|
||||
<span class="n">ARMOR</span> <span class="o">=</span> <span class="s2">"armor"</span>
|
||||
<span class="n">SHIELD</span> <span class="o">=</span> <span class="s2">"shield"</span>
|
||||
<span class="n">HELMET</span> <span class="o">=</span> <span class="s2">"helmet"</span>
|
||||
<span class="n">CONSUMABLE</span> <span class="o">=</span> <span class="s2">"consumable"</span>
|
||||
<span class="n">GEAR</span> <span class="o">=</span> <span class="s2">"gear"</span>
|
||||
<span class="n">MAGIC</span> <span class="o">=</span> <span class="s2">"magic"</span>
|
||||
<span class="n">QUEST</span> <span class="o">=</span> <span class="s2">"quest"</span>
|
||||
<span class="n">TREASURE</span> <span class="o">=</span> <span class="s2">"treasure"</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Once we have these enums, we will use them for referencing things.</p>
|
||||
</section>
|
||||
<section id="the-base-object">
|
||||
<h2><span class="section-number">4.2. </span>The base object<a class="headerlink" href="#the-base-object" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/objects.py</span></code></p>
|
||||
</div></blockquote>
|
||||
<aside class="sidebar">
|
||||
<p><a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.objects.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/objects.py</span></a> has
|
||||
a full set of objects implemented.</p>
|
||||
</aside>
|
||||
<div style="clear: right;"></div>
|
||||
<p>We will make a base <code class="docutils literal notranslate"><span class="pre">EvAdventureObject</span></code> class off Evennia’s standard <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code>. We will then add child classes to represent the relevant types:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">AttributeProperty</span><span class="p">,</span> <span class="n">DefaultObject</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">make_iter</span>
|
||||
<span class="kn">from</span> <span class="nn">.utils</span> <span class="kn">import</span> <span class="n">get_obj_stats</span>
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">ObjType</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureObject</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Base for all evadventure objects. </span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span>
|
||||
<span class="n">size</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># this can be either a single type or a list of types (for objects able to be </span>
|
||||
<span class="c1"># act as multiple). This is used to tag this object during creation.</span>
|
||||
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">GEAR</span>
|
||||
|
||||
<span class="c1"># default evennia hooks</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="w"> </span><span class="sd">"""Called when this object is first created. We convert the .obj_type </span>
|
||||
<span class="sd"> property to a database tag."""</span>
|
||||
|
||||
<span class="k">for</span> <span class="n">obj_type</span> <span class="ow">in</span> <span class="n">make_iter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj_type</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">tags</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj_type</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"obj_type"</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_display_header</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""The top of the description"""</span>
|
||||
<span class="k">return</span> <span class="s2">""</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_display_desc</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
|
||||
<span class="w"> </span><span class="sd">"""The main display - show object stats"""</span>
|
||||
<span class="k">return</span> <span class="n">get_obj_stats</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="o">=</span><span class="n">looker</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># custom evadventure methods</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">has_obj_type</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">objtype</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Check if object is of a certain type"""</span>
|
||||
<span class="k">return</span> <span class="n">objtype</span><span class="o">.</span><span class="n">value</span> <span class="ow">in</span> <span class="n">make_iter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj_type</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called before use. If returning False, can't be used"""</span>
|
||||
<span class="k">return</span> <span class="kc">True</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Use this object, whatever that means"""</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">post_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Always called after use."""</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_help</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Get any help text for this item"""</span>
|
||||
<span class="k">return</span> <span class="s2">"No help for this item"</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<section id="using-attributes-or-not">
|
||||
<h3><span class="section-number">4.2.1. </span>Using Attributes or not<a class="headerlink" href="#using-attributes-or-not" title="Permalink to this headline">¶</a></h3>
|
||||
<p>In theory, <code class="docutils literal notranslate"><span class="pre">size</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span></code> does not change and <em>could</em> also be just set as a regular Python
|
||||
property on the class:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">EvAdventureObject</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</span>
|
||||
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BACKPACK</span>
|
||||
<span class="n">size</span> <span class="o">=</span> <span class="mi">1</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="mi">0</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The problem with this is that if we want to make a new object of <code class="docutils literal notranslate"><span class="pre">size</span> <span class="pre">3</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span> <span class="pre">20</span></code>, we have to make a new class for it. We can’t change it on the fly because the change would only be in memory and be lost on next server reload.</p>
|
||||
<p>Because we use <code class="docutils literal notranslate"><span class="pre">AttributeProperties</span></code>, we can set <code class="docutils literal notranslate"><span class="pre">size</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span></code> to whatever we like when we create the object (or later), and the Attributes will remember our changes to that object indefinitely.</p>
|
||||
<p>To make this a little more efficient, we use <code class="docutils literal notranslate"><span class="pre">autocreate=False</span></code>. Normally when you create a new object with defined <code class="docutils literal notranslate"><span class="pre">AttributeProperties</span></code>, a matching <code class="docutils literal notranslate"><span class="pre">Attribute</span></code> is immediately created at the same time. So normally, the object would be created along with two Attributes <code class="docutils literal notranslate"><span class="pre">size</span></code> and <code class="docutils literal notranslate"><span class="pre">value</span></code>. With <code class="docutils literal notranslate"><span class="pre">autocreate=False</span></code>, no Attribute will be created <em>unless the default is changed</em>. That is, as long as your object has <code class="docutils literal notranslate"><span class="pre">size=1</span></code> no database <code class="docutils literal notranslate"><span class="pre">Attribute</span></code> will be created at all. This saves time and resources when creating large number of objects.</p>
|
||||
<p>The drawback is that since no Attribute is created you can’t refer to it with <code class="docutils literal notranslate"><span class="pre">obj.db.size</span></code> or <code class="docutils literal notranslate"><span class="pre">obj.attributes.get("size")</span></code> <em>unless you change its default</em>. You also can’t query the database for all objects with <code class="docutils literal notranslate"><span class="pre">size=1</span></code>, since most objects would not yet have an in-database
|
||||
<code class="docutils literal notranslate"><span class="pre">size</span></code> Attribute to search for.</p>
|
||||
<p>In our case, we’ll only refer to these properties as <code class="docutils literal notranslate"><span class="pre">obj.size</span></code> etc, and have no need to find
|
||||
all objects of a particular size. So we should be safe.</p>
|
||||
</section>
|
||||
<section id="creating-tags-in-at-object-creation">
|
||||
<h3><span class="section-number">4.2.2. </span>Creating tags in <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code><a class="headerlink" href="#creating-tags-in-at-object-creation" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">at_object_creation</span></code> is a method Evennia calls on every child of <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> whenever it is first created.</p>
|
||||
<p>We do a tricky thing here, converting our <code class="docutils literal notranslate"><span class="pre">.obj_type</span></code> to one or more <a class="reference internal" href="../../../Components/Tags.html"><span class="doc std std-doc">Tags</span></a>. Tagging the object like this means you can later efficiently find all objects of a given type (or combination of
|
||||
types) with Evennia’s search functions:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">ObjType</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">search</span>
|
||||
|
||||
<span class="c1"># get all shields in the game</span>
|
||||
<span class="n">all_shields</span> <span class="o">=</span> <span class="n">search</span><span class="o">.</span><span class="n">search_object_by_tag</span><span class="p">(</span><span class="n">ObjType</span><span class="o">.</span><span class="n">SHIELD</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">"obj_type"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We allow <code class="docutils literal notranslate"><span class="pre">.obj_type</span></code> to be given as a single value or a list of values. We use <code class="docutils literal notranslate"><span class="pre">make_iter</span></code> from the evennia utility library to make sure we don’t balk at either. This means you could have a Shield that is also Magical, for example.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="other-object-types">
|
||||
<h2><span class="section-number">4.3. </span>Other object types<a class="headerlink" href="#other-object-types" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Some of the other object types are very simple so far.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">AttributeProperty</span><span class="p">,</span> <span class="n">DefaultObject</span>
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">ObjType</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureObject</span><span class="p">(</span><span class="n">DefaultObject</span><span class="p">):</span>
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureQuestObject</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Quest objects should usually not be possible to sell or trade."""</span>
|
||||
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">QUEST</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureTreasure</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Treasure is usually just for selling for coin"""</span>
|
||||
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">TREASURE</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="consumables">
|
||||
<h2><span class="section-number">4.4. </span>Consumables<a class="headerlink" href="#consumables" title="Permalink to this headline">¶</a></h2>
|
||||
<p>A ‘consumable’ is an item that has a certain number of ‘uses’. Once fully consumed, it can’t be used anymore. An example would be a health potion.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureConsumable</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""An item that can be used up"""</span>
|
||||
|
||||
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">CONSUMABLE</span>
|
||||
<span class="n">value</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mf">0.25</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
<span class="n">uses</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called before using. If returning False, abort use."""</span>
|
||||
<span class="k">if</span> <span class="n">target</span> <span class="ow">and</span> <span class="n">user</span><span class="o">.</span><span class="n">location</span> <span class="o">!=</span> <span class="n">target</span><span class="o">.</span><span class="n">location</span><span class="p">:</span>
|
||||
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You are not close enough to the target!"</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="kc">False</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">:</span>
|
||||
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"|w</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> is used up.|n"</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="kc">False</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called when using the item"""</span>
|
||||
<span class="k">pass</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_post_use</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called after using the item"""</span>
|
||||
<span class="c1"># detract a usage, deleting the item if used up.</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o">-=</span> <span class="mi">1</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">:</span>
|
||||
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2"> was used up."</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">delete</span><span class="p">()</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In <code class="docutils literal notranslate"><span class="pre">at_pre_use</span></code> we check if we have specified a target (heal someone else or throw a fire bomb at an enemy?), making sure we are in the same location. We also make sure we have <code class="docutils literal notranslate"><span class="pre">usages</span></code> left. In <code class="docutils literal notranslate"><span class="pre">at_post_use</span></code> we make sure to tick off usages.</p>
|
||||
<p>What exactly each consumable does will vary - we will need to implement children of this class later, overriding <code class="docutils literal notranslate"><span class="pre">at_use</span></code> with different effects.</p>
|
||||
</section>
|
||||
<section id="weapons">
|
||||
<h2><span class="section-number">4.5. </span>Weapons<a class="headerlink" href="#weapons" title="Permalink to this headline">¶</a></h2>
|
||||
<p>All weapons need properties that describe how efficient they are in battle. To ‘use’ a weapon means to attack with it, so we can let the weapon itself handle all logic around performing an attack. Having the attack code on the weapon also means that if we in the future wanted a weapon doing something special on-attack (for example, a vampiric sword that heals the attacker when hurting the enemy), we could easily add that on the weapon subclass in question without modifying other code.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">WieldLocation</span><span class="p">,</span> <span class="n">ObjType</span><span class="p">,</span> <span class="n">Ability</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureWeapon</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Base class for all weapons"""</span>
|
||||
|
||||
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">WEAPON</span>
|
||||
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
<span class="n">quality</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
|
||||
<span class="n">attack_type</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
<span class="n">defend_type</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">Ability</span><span class="o">.</span><span class="n">ARMOR</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
|
||||
<span class="n">damage_roll</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="s2">"1d6"</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_pre_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="k">if</span> <span class="n">target</span> <span class="ow">and</span> <span class="n">user</span><span class="o">.</span><span class="n">location</span> <span class="o">!=</span> <span class="n">target</span><span class="o">.</span><span class="n">location</span><span class="p">:</span>
|
||||
<span class="c1"># we assume weapons can only be used in the same location</span>
|
||||
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="s2">"You are not close enough to the target!"</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="kc">False</span>
|
||||
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">:</span>
|
||||
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">user</span><span class="p">)</span><span class="si">}</span><span class="s2"> is broken and can't be used!"</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="kc">False</span>
|
||||
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">at_pre_use</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="n">target</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attacker</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">advantage</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">disadvantage</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="w"> </span><span class="sd">"""When a weapon is used, it attacks an opponent"""</span>
|
||||
|
||||
<span class="n">location</span> <span class="o">=</span> <span class="n">attacker</span><span class="o">.</span><span class="n">location</span>
|
||||
|
||||
<span class="n">is_hit</span><span class="p">,</span> <span class="n">quality</span><span class="p">,</span> <span class="n">txt</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">dice</span><span class="o">.</span><span class="n">opposed_saving_throw</span><span class="p">(</span>
|
||||
<span class="n">attacker</span><span class="p">,</span>
|
||||
<span class="n">target</span><span class="p">,</span>
|
||||
<span class="n">attack_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">attack_type</span><span class="p">,</span>
|
||||
<span class="n">defense_type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">defense_type</span><span class="p">,</span>
|
||||
<span class="n">advantage</span><span class="o">=</span><span class="n">advantage</span><span class="p">,</span>
|
||||
<span class="n">disadvantage</span><span class="o">=</span><span class="n">disadvantage</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"$You() $conj(attack) $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) with </span><span class="si">{</span><span class="bp">self</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">txt</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span>
|
||||
<span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span>
|
||||
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">},</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">is_hit</span><span class="p">:</span>
|
||||
<span class="c1"># enemy hit, calculate damage</span>
|
||||
<span class="n">dmg</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">damage_roll</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">quality</span> <span class="ow">is</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CRITICAL_SUCCESS</span><span class="p">:</span>
|
||||
<span class="c1"># doble damage roll for critical success</span>
|
||||
<span class="n">dmg</span> <span class="o">+=</span> <span class="n">rules</span><span class="o">.</span><span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">damage_roll</span><span class="p">)</span>
|
||||
<span class="n">message</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">" $You() |ycritically|n $conj(hit) $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) for |r</span><span class="si">{</span><span class="n">dmg</span><span class="si">}</span><span class="s2">|n damage!"</span>
|
||||
<span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">message</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">" $You() $conj(hit) $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) for |r</span><span class="si">{</span><span class="n">dmg</span><span class="si">}</span><span class="s2">|n damage!"</span>
|
||||
|
||||
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span> <span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">})</span>
|
||||
<span class="c1"># call hook</span>
|
||||
<span class="n">target</span><span class="o">.</span><span class="n">at_damage</span><span class="p">(</span><span class="n">dmg</span><span class="p">,</span> <span class="n">attacker</span><span class="o">=</span><span class="n">attacker</span><span class="p">)</span>
|
||||
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># a miss</span>
|
||||
<span class="n">message</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">" $You() $conj(miss) $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">)."</span>
|
||||
<span class="k">if</span> <span class="n">quality</span> <span class="ow">is</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CRITICAL_FAILURE</span><span class="p">:</span>
|
||||
<span class="n">message</span> <span class="o">+=</span> <span class="s2">".. it's a |rcritical miss!|n, damaging the weapon."</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="o">-=</span> <span class="mi">1</span>
|
||||
<span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span> <span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">})</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_post_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">quality</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">:</span>
|
||||
<span class="n">user</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">"|r</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">get_display_name</span><span class="p">(</span><span class="n">user</span><span class="p">)</span><span class="si">}</span><span class="s2"> breaks and can no longer be used!"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In EvAdventure, we will assume all weapons (including bows etc) are used in the same location as the target. Weapons also have a <code class="docutils literal notranslate"><span class="pre">quality</span></code> attribute that gets worn down if the user rolls a critical failure. Once quality is down to 0, the weapon is broken and needs to be repaired.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">quality</span></code> is something we need to track in <em>Knave</em>. When getting critical failures on attacks, a weapon’s quality will go down. When it reaches 0, it will break. We assume that a <code class="docutils literal notranslate"><span class="pre">quality</span></code> of <code class="docutils literal notranslate"><span class="pre">None</span></code> means that quality doesn’t apply (that is, the item is unbreakable), so we must consider that when checking.</p>
|
||||
<p>The attack/defend type tracks how we resolve attacks with the weapon, like <code class="docutils literal notranslate"><span class="pre">roll</span> <span class="pre">+</span> <span class="pre">STR</span> <span class="pre">vs</span> <span class="pre">ARMOR</span> <span class="pre">+</span> <span class="pre">10</span></code>.</p>
|
||||
<p>In the <code class="docutils literal notranslate"><span class="pre">use</span></code> method we make use of the <code class="docutils literal notranslate"><span class="pre">rules</span></code> module we <a class="reference internal" href="Beginner-Tutorial-Rules.html"><span class="doc std std-doc">created earlier</span></a> to perform all the dice rolls needed to resolve the attack.</p>
|
||||
<p>This code requires some additional explanation:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">location</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span>
|
||||
<span class="sa">f</span><span class="s2">"$You() $conj(attack) $you(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) with </span><span class="si">{</span><span class="bp">self</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">txt</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span>
|
||||
<span class="n">from_obj</span><span class="o">=</span><span class="n">attacker</span><span class="p">,</span>
|
||||
<span class="n">mapping</span><span class="o">=</span><span class="p">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="p">:</span> <span class="n">target</span><span class="p">},</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">location.msg_contents</span></code> sends a message to everyone in <code class="docutils literal notranslate"><span class="pre">location</span></code>. Since people will usually notice if you swing a sword at somone, this makes sense to tell people about. This message should however look <em>different</em> depending on who sees it.</p>
|
||||
<p>I should see:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>You attack Grendel with sword: <dice roll results>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Others should see</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Beowulf attacks Grendel with sword: <dice roll results>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>And Grendel should see</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Beowulf attacks you with sword: <dice roll results>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We provide the following string to <code class="docutils literal notranslate"><span class="pre">msg_contents</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sa">f</span><span class="s2">"$You() $conj(attack) $You(</span><span class="si">{</span><span class="n">target</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">) with </span><span class="si">{</span><span class="bp">self</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">txt</span><span class="si">}</span><span class="s2">"</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">{...}</span></code> are normal f-string formatting markers like those we have used before. The <code class="docutils literal notranslate"><span class="pre">$func(...)</span></code> bits are <a class="reference internal" href="../../../Components/FuncParser.html"><span class="doc std std-doc">Evennnia FuncParser</span></a> function calls. FuncParser calls are executed as functions and the result replaces their position in the string. As this string is parsed by Evennia, this is what happens:</p>
|
||||
<p>First the f-string markers are replaced, so that we get this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="s2">"$You() $cobj(attack) $you(Grendel) with sword: </span><span class="se">\n</span><span class="s2"> rolled 8 on d20 ..."</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Next the funcparser functions are run:</p>
|
||||
<ul class="simple">
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">$You()</span></code> becomes the name or <code class="docutils literal notranslate"><span class="pre">You</span></code> depending on if the string is to be sent to that object or not. It uses the <code class="docutils literal notranslate"><span class="pre">from_obj=</span></code> kwarg to the <code class="docutils literal notranslate"><span class="pre">msg_contents</span></code> method to know this. Since <code class="docutils literal notranslate"><span class="pre">msg_contents=attacker</span></code> , this becomes <code class="docutils literal notranslate"><span class="pre">You</span></code> or <code class="docutils literal notranslate"><span class="pre">Beowulf</span></code> in this example.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">$you(Grendel)</span></code> looks for the <code class="docutils literal notranslate"><span class="pre">mapping=</span></code> kwarg to <code class="docutils literal notranslate"><span class="pre">msg_contents</span></code> to determine who should be addressed here. If will replace this with the display name or the lowercase <code class="docutils literal notranslate"><span class="pre">you</span></code>. We have added <code class="docutils literal notranslate"><span class="pre">mapping={target.key:</span> <span class="pre">target}</span></code> - that is <code class="docutils literal notranslate"><span class="pre">{"Grendel":</span> <span class="pre"><grendel_obj>}</span></code>. So this will become <code class="docutils literal notranslate"><span class="pre">you</span></code> or <code class="docutils literal notranslate"><span class="pre">Grendel</span></code> depending on who sees the string.</p></li>
|
||||
<li><p><code class="docutils literal notranslate"><span class="pre">$conj(attack)</span></code> <em>conjugates</em> the verb depending on who sees it. The result will be <code class="docutils literal notranslate"><span class="pre">You</span> <span class="pre">attack</span> <span class="pre">...</span></code> or <code class="docutils literal notranslate"><span class="pre">Beowulf</span> <span class="pre">attacks</span></code> (note the extra <code class="docutils literal notranslate"><span class="pre">s</span></code>).</p></li>
|
||||
</ul>
|
||||
<p>A few funcparser calls compacts all these points of view into one string!</p>
|
||||
</section>
|
||||
<section id="magic">
|
||||
<h2><span class="section-number">4.6. </span>Magic<a class="headerlink" href="#magic" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In <em>Knave</em>, anyone can use magic if they are wielding a rune stone (our name for spell books) in both hands. You can only use a rune stone once per rest. So a rune stone is an example of a ‘magical weapon’ that is also a ‘consumable’ of sorts.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
<span class="k">class</span> <span class="nc">EvAdventureConsumable</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureWeapon</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureRuneStone</span><span class="p">(</span><span class="n">EvAdventureWeapon</span><span class="p">,</span> <span class="n">EvAdventureConsumable</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Base for all magical rune stones"""</span>
|
||||
|
||||
<span class="n">obj_type</span> <span class="o">=</span> <span class="p">(</span><span class="n">ObjType</span><span class="o">.</span><span class="n">WEAPON</span><span class="p">,</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">MAGIC</span><span class="p">)</span>
|
||||
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">TWO_HANDS</span> <span class="c1"># always two hands for magic</span>
|
||||
<span class="n">quality</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
|
||||
<span class="n">attack_type</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">Ability</span><span class="o">.</span><span class="n">INT</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
<span class="n">defend_type</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="n">Ability</span><span class="o">.</span><span class="n">DEX</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
|
||||
<span class="n">damage_roll</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="s2">"1d8"</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">at_post_use</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called after usage/spell was cast"""</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o">-=</span> <span class="mi">1</span>
|
||||
<span class="c1"># we don't delete the rune stone here, but </span>
|
||||
<span class="c1"># it must be reset on next rest.</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">refresh</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Refresh the rune stone (normally after rest)"""</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">uses</span> <span class="o">=</span> <span class="mi">1</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We make the rune stone a mix of weapon and consumable. Note that we don’t have to add <code class="docutils literal notranslate"><span class="pre">.uses</span></code> again, it’s inherited from <code class="docutils literal notranslate"><span class="pre">EvAdventureConsumable</span></code> parent. The <code class="docutils literal notranslate"><span class="pre">at_pre_use</span></code> and <code class="docutils literal notranslate"><span class="pre">use</span></code> methods are also inherited; we only override <code class="docutils literal notranslate"><span class="pre">at_post_use</span></code> since we don’t want the runestone to be deleted when it runs out of uses.</p>
|
||||
<p>We add a little convenience method <code class="docutils literal notranslate"><span class="pre">refresh</span></code> - we should call this when the character rests, to make the runestone active again.</p>
|
||||
<p>Exactly what rune stones <em>do</em> will be implemented in the <code class="docutils literal notranslate"><span class="pre">at_use</span></code> methods of subclasses to this base class. Since magic in <em>Knave</em> tends to be pretty custom, it makes sense that it will lead to a lot of custom code.</p>
|
||||
</section>
|
||||
<section id="armor">
|
||||
<h2><span class="section-number">4.7. </span>Armor<a class="headerlink" href="#armor" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Armor, shields and helmets increase the <code class="docutils literal notranslate"><span class="pre">ARMOR</span></code> stat of the character. In <em>Knave</em>, what is stored is the defense value of the armor (values 11-20). We will instead store the ‘armor bonus’ (1-10). As we know, defending is always <code class="docutils literal notranslate"><span class="pre">bonus</span> <span class="pre">+</span> <span class="pre">10</span></code>, so the result will be the same - this means we can use <code class="docutils literal notranslate"><span class="pre">Ability.ARMOR</span></code> as any other defensive ability without worrying about a special case.</p>
|
||||
<p>``</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureAmor</span><span class="p">(</span><span class="n">EvAdventureObject</span><span class="p">):</span>
|
||||
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">ARMOR</span>
|
||||
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">BODY</span>
|
||||
|
||||
<span class="n">armor</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
<span class="n">quality</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureShield</span><span class="p">(</span><span class="n">EvAdventureArmor</span><span class="p">):</span>
|
||||
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">SHIELD</span>
|
||||
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">SHIELD_HAND</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureHelmet</span><span class="p">(</span><span class="n">EvAdventureArmor</span><span class="p">):</span>
|
||||
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">HELMET</span>
|
||||
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">HEAD</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="your-bare-hands">
|
||||
<h2><span class="section-number">4.8. </span>Your Bare hands<a class="headerlink" href="#your-bare-hands" title="Permalink to this headline">¶</a></h2>
|
||||
<p>When we don’t have any weapons, we’ll be using our bare fists to fight.</p>
|
||||
<p>We will use this in the upcoming <a class="reference internal" href="Beginner-Tutorial-Equipment.html"><span class="doc std std-doc">Equipment tutorial lesson</span></a> to represent when you have ‘nothing’ in your hands. This way we don’t need to add any special case for this.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/objects.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">search_object</span><span class="p">,</span> <span class="n">create_object</span>
|
||||
|
||||
<span class="n">_BARE_HANDS</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">WeaponBareHands</span><span class="p">(</span><span class="n">EvAdventureWeapon</span><span class="p">)</span>
|
||||
<span class="n">obj_type</span> <span class="o">=</span> <span class="n">ObjType</span><span class="o">.</span><span class="n">WEAPON</span>
|
||||
<span class="n">inventory_use_slot</span> <span class="o">=</span> <span class="n">WieldLocation</span><span class="o">.</span><span class="n">WEAPON_HAND</span>
|
||||
<span class="n">attack_type</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">STR</span>
|
||||
<span class="n">defense_type</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">ARMOR</span>
|
||||
<span class="n">damage_roll</span> <span class="o">=</span> <span class="s2">"1d4"</span>
|
||||
<span class="n">quality</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># let's assume fists are indestructible ...</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_bare_hands</span><span class="p">():</span>
|
||||
<span class="w"> </span><span class="sd">"""Get the bare hands"""</span>
|
||||
<span class="k">global</span> <span class="n">_BARE_HANDS</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">_BARE_HANDS</span><span class="p">:</span>
|
||||
<span class="n">_BARE_HANDS</span> <span class="o">=</span> <span class="n">search_object</span><span class="p">(</span><span class="s2">"Bare hands"</span><span class="p">,</span> <span class="n">typeclass</span><span class="o">=</span><span class="n">WeaponBareHands</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">_BARE_HANDS</span><span class="p">:</span>
|
||||
<span class="n">_BARE_HANDS</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">WeaponBareHands</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"Bare hands"</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">_BARE_HANDS</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p>Creating a single instance of something that is used everywhere is called to create a <em>Singleton</em>.</p>
|
||||
</aside>
|
||||
<p>Since everyone’s empty hands are the same (in our game), we create <em>one</em> <code class="docutils literal notranslate"><span class="pre">Bare</span> <span class="pre">hands</span></code> weapon object that everyone shares. We do this by searching for the object with <code class="docutils literal notranslate"><span class="pre">search_object</span></code> (the <code class="docutils literal notranslate"><span class="pre">.first()</span></code> means we grab the first one even if we should by accident have created multiple hands, see <a class="reference internal" href="../Part1/Beginner-Tutorial-Django-queries.html"><span class="doc std std-doc">The Django querying tutorial</span></a> for more info). If we find none, we create it.</p>
|
||||
<p>By use of the <code class="docutils literal notranslate"><span class="pre">global</span></code> Python keyword, we cache the bare hands object get/create in a module level property <code class="docutils literal notranslate"><span class="pre">_BARE_HANDS</span></code>. So this acts as a cache to not have to search the database more than necessary.</p>
|
||||
<p>From now on, other modules can just import and run this function to get the bare hands.</p>
|
||||
</section>
|
||||
<section id="testing-and-extra-credits">
|
||||
<h2><span class="section-number">4.9. </span>Testing and Extra credits<a class="headerlink" href="#testing-and-extra-credits" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Remember the <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> function from the <a class="reference internal" href="Beginner-Tutorial-Utilities.html"><span class="doc std std-doc">Utility Tutorial</span></a> earlier? We had to use dummy-values since we didn’t yet know how we would store properties on Objects in the game.</p>
|
||||
<p>Well, we just figured out all we need! You can go back and update <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> to properly read the data from the object it receives.</p>
|
||||
<p>When you change this function you must also update the related unit test - so your existing test becomes a nice way to test your new Objects as well! Add more tests showing the output of feeding different object-types to <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code>.</p>
|
||||
<p>Try it out yourself. If you need help, a finished utility example is found in <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.utils.html#evennia.contrib.tutorials.evadventure.utils.get_obj_stats" title="evennia.contrib.tutorials.evadventure.utils.get_obj_stats"><span class="xref myst py py-func">evennia/contrib/tutorials/evadventure/utils.py</span></a>.</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="Beginner-Tutorial-Equipment.html" title="5. Handling Equipment"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Characters.html" title="3. Player Characters"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">4. </span>In-game Objects and items</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,311 @@
|
|||
|
||||
<!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>Part 3: How We Get There (Example Game) — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="1. Code structure and Utilities" href="Beginner-Tutorial-Utilities.html" />
|
||||
<link rel="prev" title="3. Planning our tutorial game" href="../Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Utilities.html" title="1. Code structure and Utilities"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html" title="3. Planning our tutorial game"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" accesskey="U">Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Part 3: How We Get There (Example Game)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Part 3: How We Get There (Example Game)</a><ul>
|
||||
<li><a class="reference internal" href="#lessons">Lessons</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="../Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html"
|
||||
title="previous chapter"><span class="section-number">3. </span>Planning our tutorial game</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Utilities.html"
|
||||
title="next chapter"><span class="section-number">1. </span>Code structure and Utilities</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Part3-Overview.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="part-3-how-we-get-there-example-game">
|
||||
<h1>Part 3: How We Get There (Example Game)<a class="headerlink" href="#part-3-how-we-get-there-example-game" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="admonition warning">
|
||||
<p class="admonition-title">Warning</p>
|
||||
<p>The tutorial game is under development and is not yet complete, nor tested. Use the existing lessons as inspiration and to help get you going, but don’t expect out-of-the-box perfection from it at this time.</p>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Beginner Tutorial Parts</p>
|
||||
<ul class="simple">
|
||||
<li><p><a class="reference internal" href="../Beginner-Tutorial-Overview.html"><span class="doc std std-doc">Introduction</span></a>
|
||||
<br>Getting set up.</p></li>
|
||||
<li><p>Part 1: <a class="reference internal" href="../Part1/Beginner-Tutorial-Part1-Overview.html"><span class="doc std std-doc">What We Have</span></a>
|
||||
<br>A tour of Evennia and how to use the tools, including an introduction to Python.</p></li>
|
||||
<li><p>Part 2: <a class="reference internal" href="../Part2/Beginner-Tutorial-Part2-Overview.html"><span class="doc std std-doc">What We Want</span></a>
|
||||
<br>Planning our tutorial game and what to consider when planning your own.</p></li>
|
||||
<li><p><em>Part 3: <a class="reference internal" href="#"><span class="doc std std-doc">How We Get There</span></a></em>
|
||||
<br>Getting down to the meat of extending Evennia to make your game.</p></li>
|
||||
<li><p>Part 4: <a class="reference internal" href="../Part4/Beginner-Tutorial-Part4-Overview.html"><span class="doc std std-doc">Using What We Created</span></a>
|
||||
<br>Building a tech-demo and world content to go with our code.</p></li>
|
||||
<li><p>Part 5: <a class="reference internal" href="../Part5/Beginner-Tutorial-Part5-Overview.html"><span class="doc std std-doc">Showing the World</span></a>
|
||||
<br>Taking our new game online and letting players try it out.</p></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<p>In part three of the Evennia Beginner tutorial we will go through the actual creation of
|
||||
our tutorial game <em>EvAdventure</em>, based on the <a class="reference external" href="https://www.drivethrurpg.com/product/250888/Knave">Knave</a> RPG ruleset.</p>
|
||||
<p>If you followed the previous parts of this tutorial series you will have some notions about Python and where to find and make use of things in Evennia. We also have a good idea of the type of game we will create.</p>
|
||||
<p>Even if this is not the game-style you are interested in, following along will give you a lot
|
||||
of experience using Evennia and be really helpful for doing your own thing later! The EvAdventure game code is also built to easily be expanded upon.</p>
|
||||
<p>Fully coded examples of all code we make in this part can be found in the
|
||||
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure</span></a> package. There are three common ways to learn from this:</p>
|
||||
<ol class="simple">
|
||||
<li><p>Follow the tutorial lessons in sequence and use it to write your own code, referring to the ready-made code as extra help, context, or as a ‘facit’ to check yourself.</p></li>
|
||||
<li><p>Read through the code in the package and refer to the tutorial lesson for each part for more information on what you see.</p></li>
|
||||
<li><p>Some mix of the two.</p></li>
|
||||
</ol>
|
||||
<p>Which approach you choose is individual - we all learn in different ways.</p>
|
||||
<p>Either way, this is a big part. You’ll be seeing a lot of code and there are plenty of lessons to go through. We are making a whole game from scratch after all. Take your time!</p>
|
||||
<section id="lessons">
|
||||
<h2>Lessons<a class="headerlink" href="#lessons" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="toctree-wrapper compound">
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Utilities.html">1. Code structure and Utilities</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#folder-structure">1.1. Folder structure</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#enums">1.2. Enums</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#utility-module">1.3. Utility module</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#testing">1.4. Testing</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Utilities.html#summary">1.5. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Rules.html">2. Rules and dice rolling</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#summary-of-knave-rules">2.1. Summary of <em>Knave</em> rules</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#making-a-rule-module">2.2. Making a rule module</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#rolling-dice">2.3. Rolling dice</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#testing">2.4. Testing</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rules.html#summary">2.5. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Characters.html">3. Player Characters</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#inheritance-structure">3.1. Inheritance structure</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#living-mixin-class">3.2. Living mixin class</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#character-class">3.3. Character class</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#connecting-the-character-with-evennia">3.4. Connecting the Character with Evennia</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#unit-testing">3.5. Unit Testing</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#about-races-and-classes">3.6. About races and classes</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Characters.html#summary">3.7. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Objects.html">4. In-game Objects and items</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#new-enums">4.1. New Enums</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#the-base-object">4.2. The base object</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#other-object-types">4.3. Other object types</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#consumables">4.4. Consumables</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#weapons">4.5. Weapons</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#magic">4.6. Magic</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#armor">4.7. Armor</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#your-bare-hands">4.8. Your Bare hands</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Objects.html#testing-and-extra-credits">4.9. Testing and Extra credits</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Equipment.html">5. Handling Equipment</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#equipmenthandler-that-saves">5.1. EquipmentHandler that saves</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#connecting-the-equipmenthandler">5.2. Connecting the EquipmentHandler</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#expanding-the-equipmenthandler">5.3. Expanding the Equipmenthandler</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#validate-slot-usage">5.4. <code class="docutils literal notranslate"><span class="pre">.validate_slot_usage</span></code></a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#add-and-remove">5.5. <code class="docutils literal notranslate"><span class="pre">.add</span></code> and <code class="docutils literal notranslate"><span class="pre">.remove</span></code></a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#moving-things-around">5.6. Moving things around</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#get-everything">5.7. Get everything</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#weapon-and-armor">5.8. Weapon and armor</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#extra-credits">5.9. Extra credits</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#unit-testing">5.10. Unit Testing</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Equipment.html#summary">5.11. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Chargen.html">6. Character Generation</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#how-it-will-work">6.1. How it will work</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#random-tables">6.2. Random tables</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#storing-state-of-the-menu">6.3. Storing state of the menu</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#initializing-evmenu">6.4. Initializing EvMenu</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#main-node-choosing-what-to-do">6.5. Main Node: Choosing what to do</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#node-changing-your-name">6.6. Node: Changing your name</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#node-swapping-abilities-around">6.7. Node: Swapping Abilities around</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#node-creating-the-character">6.8. Node: Creating the Character</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#tying-the-nodes-together">6.9. Tying the nodes together</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Chargen.html#conclusions">6.10. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Rooms.html">7. In-game Rooms</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#the-base-room">7.1. The base room</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#pvp-room">7.2. PvP room</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#adding-a-room-map">7.3. Adding a room map</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#adding-life-to-a-room">7.4. Adding life to a room</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#testing">7.5. Testing</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Rooms.html#conclusion">7.6. Conclusion</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-NPCs.html">8. Non-Player-Characters</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-NPCs.html#the-npc-base-class">8.1. The NPC base class</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-NPCs.html#testing">8.2. Testing</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-NPCs.html#conclusions">8.3. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html">9. Combat base framework</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#combathandler">9.1. CombatHandler</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#actions">9.2. Actions</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#action-dicts">9.3. Action dicts</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#action-classes">9.4. Action classes</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#testing">9.5. Testing</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Base.html#conclusions">9.6. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html">10. Twitch Combat</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#general-principle">10.1. General principle</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#twitch-combat-handler">10.2. Twitch combat handler</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#commands">10.3. Commands</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#grouping-commands-for-use">10.4. Grouping Commands for use</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#unit-testing">10.5. Unit Testing</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#a-small-combat-test">10.6. A small combat test</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Twitch.html#conclusions">10.7. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html">11. Turnbased Combat</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#general-principle">11.1. General Principle</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#turnbased-combat-handler">11.2. Turnbased combat handler</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#using-evmenu-for-the-combat-menu">11.3. Using EvMenu for the combat menu</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#menu-for-turnbased-combat">11.4. Menu for Turnbased combat</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#attack-command">11.5. Attack Command</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#making-sure-the-menu-stops">11.6. Making sure the menu stops</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#testing">11.7. Testing</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#a-small-combat-test">11.8. A small combat test</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Beginner-Tutorial-Combat-Turnbased.html#conclusions">11.9. Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-AI.html">12. NPC and monster AI</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Dungeon.html">13. Dynamically generated Dungeon</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Quests.html">14. Game Quests</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Shops.html">15. In-game Shops</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Beginner-Tutorial-Commands.html">16. In-game Commands</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</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="Beginner-Tutorial-Utilities.html" title="1. Code structure and Utilities"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Part2/Beginner-Tutorial-Planning-The-Tutorial-Game.html" title="3. Planning our tutorial game"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Part 3: How We Get There (Example Game)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
|
||||
<!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>14. Game Quests — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="15. In-game Shops" href="Beginner-Tutorial-Shops.html" />
|
||||
<link rel="prev" title="13. Dynamically generated Dungeon" href="Beginner-Tutorial-Dungeon.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">14. </span>Game Quests</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Dungeon.html"
|
||||
title="previous chapter"><span class="section-number">13. </span>Dynamically generated Dungeon</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Shops.html"
|
||||
title="next chapter"><span class="section-number">15. </span>In-game Shops</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Quests.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="game-quests">
|
||||
<h1><span class="section-number">14. </span>Game Quests<a class="headerlink" href="#game-quests" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="admonition warning">
|
||||
<p class="admonition-title">Warning</p>
|
||||
<p>This part of the Beginner tutorial is still being developed.</p>
|
||||
</div>
|
||||
</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="Beginner-Tutorial-Shops.html" title="15. In-game Shops"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Dungeon.html" title="13. Dynamically generated Dungeon"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">14. </span>Game Quests</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,502 @@
|
|||
|
||||
<!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>7. In-game Rooms — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="8. Non-Player-Characters" href="Beginner-Tutorial-NPCs.html" />
|
||||
<link rel="prev" title="6. Character Generation" href="Beginner-Tutorial-Chargen.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Chargen.html" title="6. Character Generation"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">7. </span>In-game Rooms</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">7. In-game Rooms</a><ul>
|
||||
<li><a class="reference internal" href="#the-base-room">7.1. The base room</a></li>
|
||||
<li><a class="reference internal" href="#pvp-room">7.2. PvP room</a></li>
|
||||
<li><a class="reference internal" href="#adding-a-room-map">7.3. Adding a room map</a></li>
|
||||
<li><a class="reference internal" href="#adding-life-to-a-room">7.4. Adding life to a room</a></li>
|
||||
<li><a class="reference internal" href="#testing">7.5. Testing</a></li>
|
||||
<li><a class="reference internal" href="#conclusion">7.6. Conclusion</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Chargen.html"
|
||||
title="previous chapter"><span class="section-number">6. </span>Character Generation</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-NPCs.html"
|
||||
title="next chapter"><span class="section-number">8. </span>Non-Player-Characters</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Rooms.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="in-game-rooms">
|
||||
<h1><span class="section-number">7. </span>In-game Rooms<a class="headerlink" href="#in-game-rooms" title="Permalink to this headline">¶</a></h1>
|
||||
<p>A <em>room</em> describes a specific location in the game world. Being an abstract concept, it can represent any area of game content that is convenient to group together. In this lesson we will also create a small in-game automap.</p>
|
||||
<p>In EvAdventure, we will have two main types of rooms:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Normal, above-ground rooms. Based on a fixed map, these will be created once and then don’t change. We’ll cover them in this lesson.</p></li>
|
||||
<li><p>Dungeon rooms - these will be examples of <em>procedurally generated</em> rooms, created on the fly as the players explore the underworld. Being subclasses of the normal room, we’ll get to them in the <a class="reference internal" href="Beginner-Tutorial-Dungeon.html"><span class="doc std std-doc">Dungeon generation lesson</span></a>.</p></li>
|
||||
</ul>
|
||||
<section id="the-base-room">
|
||||
<h2><span class="section-number">7.1. </span>The base room<a class="headerlink" href="#the-base-room" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/rooms.py</span></code>.</p>
|
||||
</div></blockquote>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/rooms.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">AttributeProperty</span><span class="p">,</span> <span class="n">DefaultRoom</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureRoom</span><span class="p">(</span><span class="n">DefaultRoom</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Simple room supporting some EvAdventure-specifics.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="n">allow_combat</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
<span class="n">allow_pvp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
<span class="n">allow_death</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">False</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Our <code class="docutils literal notranslate"><span class="pre">EvadventureRoom</span></code> is very simple. We use Evennia’s <code class="docutils literal notranslate"><span class="pre">DefaultRoom</span></code> as a base and just add three additional Attributes that defines</p>
|
||||
<ul class="simple">
|
||||
<li><p>If combat is allowed to start in the room at all.</p></li>
|
||||
<li><p>If combat is allowed, if PvP (player vs player) combat is allowed.</p></li>
|
||||
<li><p>If combat is allowed, if any side is allowed to die from it.</p></li>
|
||||
</ul>
|
||||
<p>Later on we must make sure our combat systems honors these values.</p>
|
||||
</section>
|
||||
<section id="pvp-room">
|
||||
<h2><span class="section-number">7.2. </span>PvP room<a class="headerlink" href="#pvp-room" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Here’s a room that allows non-lethal PvP (sparring):</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/rooms.py</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventurePvPRoom</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Room where PvP can happen, but noone gets killed.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="n">allow_combat</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">True</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
<span class="n">allow_pvp</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="kc">True</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_display_footer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Customize footer of description.</span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">return</span> <span class="s2">"|yNon-lethal PvP combat is allowed here!|n"</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The return of <code class="docutils literal notranslate"><span class="pre">get_display_footer</span></code> will show after the <a class="reference internal" href="../../../Components/Objects.html#changing-an-objects-appearance"><span class="std std-doc">main room description</span></a>, showing that the room is a sparring room. This means that when a player drops to 0 HP, they will lose the combat, but don’t stand any risk of dying (weapons wear out normally during sparring though).</p>
|
||||
</section>
|
||||
<section id="adding-a-room-map">
|
||||
<h2><span class="section-number">7.3. </span>Adding a room map<a class="headerlink" href="#adding-a-room-map" title="Permalink to this headline">¶</a></h2>
|
||||
<p>We want a dynamic map that visualizes the exits you can use at any moment. Here’s how our room will display:</p>
|
||||
<div class="highlight-shell notranslate"><div class="highlight"><pre><span></span><span class="w"> </span>o<span class="w"> </span>o<span class="w"> </span>o
|
||||
<span class="w"> </span><span class="se">\|</span>/
|
||||
<span class="w"> </span>o-@-o
|
||||
<span class="w"> </span><span class="p">|</span><span class="w"> </span>
|
||||
<span class="w"> </span>o
|
||||
The<span class="w"> </span>crossroads<span class="w"> </span>
|
||||
A<span class="w"> </span>place<span class="w"> </span>where<span class="w"> </span>many<span class="w"> </span>roads<span class="w"> </span>meet.<span class="w"> </span>
|
||||
Exits:<span class="w"> </span>north,<span class="w"> </span>northeast,<span class="w"> </span>south,<span class="w"> </span>west,<span class="w"> </span>and<span class="w"> </span>northwest
|
||||
</pre></div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div><p>Documentation does not show ansi colors.</p>
|
||||
</div></blockquote>
|
||||
<p>Let’s expand the base <code class="docutils literal notranslate"><span class="pre">EvAdventureRoom</span></code> with the map.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span>
|
||||
<span class="normal">22</span>
|
||||
<span class="normal">23</span>
|
||||
<span class="normal">24</span>
|
||||
<span class="normal">25</span>
|
||||
<span class="normal">26</span>
|
||||
<span class="normal">27</span>
|
||||
<span class="normal">28</span>
|
||||
<span class="normal">29</span>
|
||||
<span class="normal">30</span>
|
||||
<span class="normal">31</span>
|
||||
<span class="normal">32</span>
|
||||
<span class="normal">33</span>
|
||||
<span class="normal">34</span>
|
||||
<span class="normal">35</span>
|
||||
<span class="normal">36</span>
|
||||
<span class="normal">37</span>
|
||||
<span class="normal">38</span>
|
||||
<span class="normal">39</span>
|
||||
<span class="normal">40</span>
|
||||
<span class="normal">41</span>
|
||||
<span class="normal">42</span>
|
||||
<span class="normal">43</span>
|
||||
<span class="normal">44</span>
|
||||
<span class="normal">45</span>
|
||||
<span class="normal">46</span>
|
||||
<span class="normal">47</span>
|
||||
<span class="normal">48</span>
|
||||
<span class="normal">49</span>
|
||||
<span class="normal">50</span>
|
||||
<span class="normal">51</span>
|
||||
<span class="normal">52</span>
|
||||
<span class="normal">53</span>
|
||||
<span class="normal">54</span>
|
||||
<span class="normal">55</span>
|
||||
<span class="normal">56</span>
|
||||
<span class="normal">57</span>
|
||||
<span class="normal">58</span>
|
||||
<span class="normal">59</span>
|
||||
<span class="normal">60</span>
|
||||
<span class="normal">61</span>
|
||||
<span class="normal">62</span>
|
||||
<span class="normal">63</span>
|
||||
<span class="normal">64</span>
|
||||
<span class="normal">65</span>
|
||||
<span class="normal">66</span>
|
||||
<span class="normal">67</span>
|
||||
<span class="normal">68</span>
|
||||
<span class="normal">69</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventyre/rooms.py</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">copy</span> <span class="kn">import</span> <span class="n">deepcopy</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultCharacter</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils.utils</span> <span class="kn">import</span> <span class="n">inherits_from</span>
|
||||
|
||||
<span class="n">CHAR_SYMBOL</span> <span class="o">=</span> <span class="s2">"|w@|n"</span>
|
||||
<span class="n">CHAR_ALT_SYMBOL</span> <span class="o">=</span> <span class="s2">"|w>|n"</span>
|
||||
<span class="n">ROOM_SYMBOL</span> <span class="o">=</span> <span class="s2">"|bo|n"</span>
|
||||
<span class="hll"><span class="n">LINK_COLOR</span> <span class="o">=</span> <span class="s2">"|B"</span>
|
||||
</span>
|
||||
<span class="n">_MAP_GRID</span> <span class="o">=</span> <span class="p">[</span>
|
||||
<span class="p">[</span><span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">],</span>
|
||||
<span class="p">[</span><span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">],</span>
|
||||
<span class="p">[</span><span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">"@"</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">],</span>
|
||||
<span class="p">[</span><span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">],</span>
|
||||
<span class="hll"> <span class="p">[</span><span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">,</span> <span class="s2">" "</span><span class="p">],</span>
|
||||
</span><span class="p">]</span>
|
||||
<span class="n">_EXIT_GRID_SHIFT</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"north"</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">"||"</span><span class="p">),</span>
|
||||
<span class="s2">"east"</span><span class="p">:</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">"-"</span><span class="p">),</span>
|
||||
<span class="s2">"south"</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s2">"||"</span><span class="p">),</span>
|
||||
<span class="s2">"west"</span><span class="p">:</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">"-"</span><span class="p">),</span>
|
||||
<span class="s2">"northeast"</span><span class="p">:</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">"/"</span><span class="p">),</span>
|
||||
<span class="s2">"southeast"</span><span class="p">:</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s2">"</span><span class="se">\\</span><span class="s2">"</span><span class="p">),</span>
|
||||
<span class="s2">"southwest"</span><span class="p">:</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s2">"/"</span><span class="p">),</span>
|
||||
<span class="s2">"northwest"</span><span class="p">:</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">"</span><span class="se">\\</span><span class="s2">"</span><span class="p">),</span>
|
||||
<span class="p">}</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureRoom</span><span class="p">(</span><span class="n">DefaultRoom</span><span class="p">):</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">format_appearance</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">appearance</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Don't left-strip the appearance string"""</span>
|
||||
<span class="k">return</span> <span class="n">appearance</span><span class="o">.</span><span class="n">rstrip</span><span class="p">()</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_display_header</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">looker</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> Display the current location as a mini-map.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="c1"># make sure to not show make a map for users of screenreaders.</span>
|
||||
<span class="c1"># for optimization we also don't show it to npcs/mobs</span>
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="n">inherits_from</span><span class="p">(</span><span class="n">looker</span><span class="p">,</span> <span class="n">DefaultCharacter</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span>
|
||||
<span class="n">looker</span><span class="o">.</span><span class="n">account</span> <span class="ow">and</span> <span class="n">looker</span><span class="o">.</span><span class="n">account</span><span class="o">.</span><span class="n">uses_screenreader</span><span class="p">()</span>
|
||||
<span class="p">):</span>
|
||||
<span class="k">return</span> <span class="s2">""</span>
|
||||
<span class="hll">
|
||||
</span><span class="hll"> <span class="c1"># build a map</span>
|
||||
</span> <span class="n">map_grid</span> <span class="o">=</span> <span class="n">deepcopy</span><span class="p">(</span><span class="n">_MAP_GRID</span><span class="p">)</span>
|
||||
<span class="n">dx0</span><span class="p">,</span> <span class="n">dy0</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span>
|
||||
<span class="n">map_grid</span><span class="p">[</span><span class="n">dy0</span><span class="p">][</span><span class="n">dx0</span><span class="p">]</span> <span class="o">=</span> <span class="n">CHAR_SYMBOL</span>
|
||||
<span class="k">for</span> <span class="n">exi</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">exits</span><span class="p">:</span>
|
||||
<span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="p">,</span> <span class="n">symbol</span> <span class="o">=</span> <span class="n">_EXIT_GRID_SHIFT</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">exi</span><span class="o">.</span><span class="n">key</span><span class="p">,</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">))</span>
|
||||
<span class="hll"> <span class="k">if</span> <span class="n">symbol</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||||
</span> <span class="c1"># we have a non-cardinal direction to go to - indicate this</span>
|
||||
<span class="n">map_grid</span><span class="p">[</span><span class="n">dy0</span><span class="p">][</span><span class="n">dx0</span><span class="p">]</span> <span class="o">=</span> <span class="n">CHAR_ALT_SYMBOL</span>
|
||||
<span class="k">continue</span>
|
||||
<span class="n">map_grid</span><span class="p">[</span><span class="n">dy0</span> <span class="o">+</span> <span class="n">dy</span><span class="p">][</span><span class="n">dx0</span> <span class="o">+</span> <span class="n">dx</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">LINK_COLOR</span><span class="si">}{</span><span class="n">symbol</span><span class="si">}</span><span class="s2">|n"</span>
|
||||
<span class="k">if</span> <span class="n">exi</span><span class="o">.</span><span class="n">destination</span> <span class="o">!=</span> <span class="bp">self</span><span class="p">:</span>
|
||||
<span class="n">map_grid</span><span class="p">[</span><span class="n">dy0</span> <span class="o">+</span> <span class="n">dy</span> <span class="o">+</span> <span class="n">dy</span><span class="p">][</span><span class="n">dx0</span> <span class="o">+</span> <span class="n">dx</span> <span class="o">+</span> <span class="n">dx</span><span class="p">]</span> <span class="o">=</span> <span class="n">ROOM_SYMBOL</span>
|
||||
|
||||
<span class="c1"># Note that on the grid, dy is really going *downwards* (origo is</span>
|
||||
<span class="hll"> <span class="c1"># in the top left), so we need to reverse the order at the end to mirror it</span>
|
||||
</span> <span class="c1"># vertically and have it come out right.</span>
|
||||
<span class="k">return</span> <span class="s2">" "</span> <span class="o">+</span> <span class="s2">"</span><span class="se">\n</span><span class="s2"> "</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">""</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="n">map_grid</span><span class="p">))</span>
|
||||
</pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<p>The string returned from <code class="docutils literal notranslate"><span class="pre">get_display_header</span></code> will end up at the top of the <span class="xref myst">room description</span>, a good place to have the map appear!</p>
|
||||
<ul class="simple">
|
||||
<li><p><strong>Line 12</strong>: The map itself consists of the 2D matrix <code class="docutils literal notranslate"><span class="pre">_MAP_GRID</span></code>. This is a 2D area described by a list of Python lists. To find a given place in the list, you first first need to find which of the nested lists to use, and then which element to use in that list. Indices start from 0 in Python. So to draw the <code class="docutils literal notranslate"><span class="pre">o</span></code> symbol for the southermost room, you’d need to do so at <code class="docutils literal notranslate"><span class="pre">_MAP_GRID[4][2]</span></code>.</p></li>
|
||||
<li><p><strong>Line 19</strong>: The <code class="docutils literal notranslate"><span class="pre">_EXIT_GRID_SHIFT</span></code> indicates the direction to go for each cardinal exit, along with the map symbol to draw at that point. So <code class="docutils literal notranslate"><span class="pre">"east":</span> <span class="pre">(1,</span> <span class="pre">0,</span> <span class="pre">"-")</span></code> means the east exit will be drawn one step in the positive x direction (to the right), using the “-” symbol. For symbols like <code class="docutils literal notranslate"><span class="pre">|</span></code> and “\” we need to escape with a double-symbol since these would otherwise be interpreted as part of other formatting.</p></li>
|
||||
<li><p><strong>Line 51</strong>: We start by making a <code class="docutils literal notranslate"><span class="pre">deepcopy</span></code> of the <code class="docutils literal notranslate"><span class="pre">_MAP_GRID</span></code>. This is so that we don’t modify the original but always have an empty template to work from.</p></li>
|
||||
<li><p><strong>Line 52</strong>: We use <code class="docutils literal notranslate"><span class="pre">@</span></code> to indicate the location of the player (at coordinate <code class="docutils literal notranslate"><span class="pre">(2,</span> <span class="pre">2)</span></code>). We then take the actual exits from the room use their names to figure out what symbols to draw out from the center.</p></li>
|
||||
<li><p><strong>Line 58</strong>: We want to be able to get on/off the grid if so needed. So if a room has a non-cardinal exit in it (like ‘back’ or up/down), we’ll indicate this by showing the <code class="docutils literal notranslate"><span class="pre">></span></code> symbol instead of the <code class="docutils literal notranslate"><span class="pre">@</span></code> in your current room.</p></li>
|
||||
<li><p><strong>Line 67</strong>: Once we have placed all the exit- and room-symbols in the grid, we merge it all together into a single string. At the end we use Python’s standard <a class="reference external" href="https://www.w3schools.com/python/ref_string_join.asp">join</a> to convert the grid into a single string. In doing so we must flip the grid upside down (reverse the outermost list). Why is this? If you think about how a MUD game displays its data - by printing at the bottom and then scrolling upwards - you’ll realize that Evennia has to send out the top of your map <em>first</em> and the bottom of it <em>last</em> for it to show correctly to the user.</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="adding-life-to-a-room">
|
||||
<h2><span class="section-number">7.4. </span>Adding life to a room<a class="headerlink" href="#adding-life-to-a-room" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Normally the room is static until you do something in it. But let’s say you are in a room described to be a bustling market. Would it not be nice to occasionally get some random messages like</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>"You hear a merchant calling out his wares."
|
||||
"The sound of music drifts over the square from an open tavern door."
|
||||
"The sound of commerse rises and fall in a steady rythm."
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here’s an example of how to accomplish this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||||
<span class="normal"> 2</span>
|
||||
<span class="normal"> 3</span>
|
||||
<span class="normal"> 4</span>
|
||||
<span class="normal"> 5</span>
|
||||
<span class="normal"> 6</span>
|
||||
<span class="normal"> 7</span>
|
||||
<span class="normal"> 8</span>
|
||||
<span class="normal"> 9</span>
|
||||
<span class="normal">10</span>
|
||||
<span class="normal">11</span>
|
||||
<span class="normal">12</span>
|
||||
<span class="normal">13</span>
|
||||
<span class="normal">14</span>
|
||||
<span class="normal">15</span>
|
||||
<span class="normal">16</span>
|
||||
<span class="normal">17</span>
|
||||
<span class="normal">18</span>
|
||||
<span class="normal">19</span>
|
||||
<span class="normal">20</span>
|
||||
<span class="normal">21</span>
|
||||
<span class="normal">22</span>
|
||||
<span class="normal">23</span>
|
||||
<span class="normal">24</span>
|
||||
<span class="normal">25</span></pre></div></td><td class="code"><div><pre><span></span><span class="c1"># in evadventure/rooms.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">choice</span><span class="p">,</span> <span class="n">random</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">TICKER_HANDLER</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EchoingRoom</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""A room that randomly echoes messages to everyone inside it"""</span>
|
||||
|
||||
<span class="n">echoes</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
<span class="n">echo_rate</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mi">60</span> <span class="o">*</span> <span class="mi">2</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
<span class="n">echo_chance</span> <span class="o">=</span> <span class="n">AttributeProperty</span><span class="p">(</span><span class="mf">0.1</span><span class="p">,</span> <span class="n">autocreate</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">send_echo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">echoes</span> <span class="ow">and</span> <span class="n">random</span><span class="p">()</span> <span class="o"><</span> <span class="bp">self</span><span class="o">.</span><span class="n">echo_chance</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">msg_contents</span><span class="p">(</span><span class="n">choice</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">echoes</span><span class="p">))</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">start_echo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="hll"> <span class="n">TICKER_HANDLER</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">echo_rate</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">send_echo</span><span class="p">)</span>
|
||||
</span>
|
||||
<span class="k">def</span> <span class="nf">stop_echo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="hll"> <span class="n">TICKER_HANDLER</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">echo_rate</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">send_echo</span><span class="p">)</span>
|
||||
</span></pre></div></td></tr></table></div>
|
||||
</div>
|
||||
<p>The <a class="reference internal" href="../../../Components/TickerHandler.html"><span class="doc std std-doc">TickerHandler</span></a>. This is acts as a ‘please tick me - subscription service’. In <strong>Line 22</strong> we tell add our <code class="docutils literal notranslate"><span class="pre">.send_echo</span></code> method to the handler and tell the TickerHandler to call that method every <code class="docutils literal notranslate"><span class="pre">.echo_rate</span></code> seconds.</p>
|
||||
<p>When the <code class="docutils literal notranslate"><span class="pre">.send_echo</span></code> method is called, it will use <code class="docutils literal notranslate"><span class="pre">random.random()</span></code> to check if we should <em>actually</em> do anything. In our example we only show a message 10% of the time. In that case we use Python’s <code class="docutils literal notranslate"><span class="pre">random.choice()</span></code> to grab a random text string from the <code class="docutils literal notranslate"><span class="pre">.echoes</span></code> list to send to everyone inside this room.</p>
|
||||
<p>Here’s how you’d use this room in-game:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>> dig market:evadventure.rooms.EchoingRoom = market,back
|
||||
> market
|
||||
> set here/echoes = ["You hear a merchant shouting", "You hear the clatter of coins"]
|
||||
> py here.start_echo()
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you wait a while you’ll eventually see one of the two echoes show up. Use <code class="docutils literal notranslate"><span class="pre">py</span> <span class="pre">here.stop_echo()</span></code> if you want.</p>
|
||||
<p>It’s a good idea to be able to turn on/off the echoes at will, if nothing else because you’d be surprised how annoying they can be if they show too often.</p>
|
||||
<p>In this example we had to resort to <code class="docutils literal notranslate"><span class="pre">py</span></code> to activate/deactivate the echoes, but you could very easily make little utility <a class="reference internal" href="../Part1/Beginner-Tutorial-Adding-Commands.html"><span class="doc std std-doc">Commands</span></a> <code class="docutils literal notranslate"><span class="pre">startecho</span></code> and <code class="docutils literal notranslate"><span class="pre">stopecho</span></code> to do it for you. This we leave as a bonus exercise.</p>
|
||||
</section>
|
||||
<section id="testing">
|
||||
<h2><span class="section-number">7.5. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">evadventure/tests/test_rooms.py</span></code>.</p>
|
||||
</div></blockquote>
|
||||
<aside class="sidebar">
|
||||
<p>You can find a ready testing module <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_rooms.html#evennia-contrib-tutorials-evadventure-tests-test-rooms"><span class="std std-ref">here in the tutorial folder</span></a>.</p>
|
||||
</aside>
|
||||
<p>The main thing to test with our new rooms is the map. Here’s the basic principle for how to do this testing:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in evadventure/tests/test_rooms.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">DefaultExit</span><span class="p">,</span> <span class="n">create_object</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTestCase</span>
|
||||
<span class="kn">from</span> <span class="nn">..characters</span> <span class="kn">import</span> <span class="n">EvAdventureCharacter</span>
|
||||
<span class="kn">from</span> <span class="nn">..rooms</span> <span class="kn">import</span> <span class="n">EvAdventureRoom</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureRoomTest</span><span class="p">(</span><span class="n">EvenniaTestCase</span><span class="p">):</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">test_map</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="n">center_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"room_center"</span><span class="p">)</span>
|
||||
|
||||
<span class="n">n_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"room_n)</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">"north"</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">n_room</span><span class="p">)</span>
|
||||
<span class="n">ne_room</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureRoom</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s2">"room=ne"</span><span class="p">)</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">"northeast"</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">,</span> <span class="n">destination</span><span class="o">=</span><span class="n">ne_room</span><span class="p">)</span>
|
||||
<span class="c1"># ... etc for all cardinal directions </span>
|
||||
|
||||
<span class="n">char</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="n">EvAdventureCharacter</span><span class="p">,</span>
|
||||
<span class="n">key</span><span class="o">=</span><span class="s2">"TestChar"</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">center_room</span><span class="p">)</span>
|
||||
<span class="n">desc</span> <span class="o">=</span> <span class="n">center_room</span><span class="o">.</span><span class="n">return_appearance</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># compare the desc we got with the expected description here</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>So we create a bunch of rooms, link them to one centr room and then make sure the map in that room looks like we’d expect.</p>
|
||||
</section>
|
||||
<section id="conclusion">
|
||||
<h2><span class="section-number">7.6. </span>Conclusion<a class="headerlink" href="#conclusion" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In this lesson we manipulated strings and made a map. Changing the description of an object is a big part of changing the ‘graphics’ of a text-based game, so checking out the <span class="xref myst">parts making up an object description</span> is good extra reading.</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="Beginner-Tutorial-NPCs.html" title="8. Non-Player-Characters"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Chargen.html" title="6. Character Generation"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">7. </span>In-game Rooms</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,790 @@
|
|||
|
||||
<!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>2. Rules and dice rolling — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="3. Player Characters" href="Beginner-Tutorial-Characters.html" />
|
||||
<link rel="prev" title="1. Code structure and Utilities" href="Beginner-Tutorial-Utilities.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Characters.html" title="3. Player Characters"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Utilities.html" title="1. Code structure and Utilities"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">2. </span>Rules and dice rolling</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">2. Rules and dice rolling</a><ul>
|
||||
<li><a class="reference internal" href="#summary-of-knave-rules">2.1. Summary of <em>Knave</em> rules</a></li>
|
||||
<li><a class="reference internal" href="#making-a-rule-module">2.2. Making a rule module</a></li>
|
||||
<li><a class="reference internal" href="#rolling-dice">2.3. Rolling dice</a><ul>
|
||||
<li><a class="reference internal" href="#generic-dice-roller">2.3.1. Generic dice roller</a></li>
|
||||
<li><a class="reference internal" href="#rolling-with-advantage">2.3.2. Rolling with advantage</a></li>
|
||||
<li><a class="reference internal" href="#saving-throws">2.3.3. Saving throws</a></li>
|
||||
<li><a class="reference internal" href="#opposed-saving-throw">2.3.4. Opposed saving throw</a></li>
|
||||
<li><a class="reference internal" href="#morale-check">2.3.5. Morale check</a></li>
|
||||
<li><a class="reference internal" href="#roll-for-healing">2.3.6. Roll for Healing</a></li>
|
||||
<li><a class="reference internal" href="#rolling-on-a-table">2.3.7. Rolling on a table</a></li>
|
||||
<li><a class="reference internal" href="#roll-for-death">2.3.8. Roll for death</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#testing">2.4. Testing</a><ul>
|
||||
<li><a class="reference internal" href="#mocking-and-patching">2.4.1. Mocking and patching</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#summary">2.5. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Utilities.html"
|
||||
title="previous chapter"><span class="section-number">1. </span>Code structure and Utilities</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Characters.html"
|
||||
title="next chapter"><span class="section-number">3. </span>Player Characters</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Rules.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="rules-and-dice-rolling">
|
||||
<h1><span class="section-number">2. </span>Rules and dice rolling<a class="headerlink" href="#rules-and-dice-rolling" title="Permalink to this headline">¶</a></h1>
|
||||
<p>In <em>EvAdventure</em> we have decided to use the <a class="reference external" href="https://www.drivethrurpg.com/product/250888/Knave">Knave</a>
|
||||
RPG ruleset. This is commercial, but released under Creative Commons 4.0, meaning it’s okay to share and
|
||||
adapt <em>Knave</em> for any purpose, even commercially. If you don’t want to buy it but still follow
|
||||
along, you can find a <a class="reference external" href="http://abominablefancy.blogspot.com/2018/10/knaves-fancypants.html">free fan-version here</a>.</p>
|
||||
<section id="summary-of-knave-rules">
|
||||
<h2><span class="section-number">2.1. </span>Summary of <em>Knave</em> rules<a class="headerlink" href="#summary-of-knave-rules" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Knave, being inspired by early Dungeons & Dragons, is very simple.</p>
|
||||
<ul class="simple">
|
||||
<li><p>It uses six Ability bonuses
|
||||
<em>Strength</em> (STR), <em>Dexterity</em> (DEX), <em>Constitution</em> (CON), <em>Intelligence</em> (INT), <em>Wisdom</em> (WIS)
|
||||
and <em>Charisma</em> (CHA). These are rated from <code class="docutils literal notranslate"><span class="pre">+1</span></code> to <code class="docutils literal notranslate"><span class="pre">+10</span></code>.</p></li>
|
||||
<li><p>Rolls are made with a twenty-sided die (<code class="docutils literal notranslate"><span class="pre">1d20</span></code>), usually adding a suitable Ability bonus to the roll.</p></li>
|
||||
<li><p>If you roll <em>with advantage</em>, you roll <code class="docutils literal notranslate"><span class="pre">2d20</span></code> and pick the
|
||||
<em>highest</em> value, If you roll <em>with disadvantage</em>, you roll <code class="docutils literal notranslate"><span class="pre">2d20</span></code> and pick the <em>lowest</em>.</p></li>
|
||||
<li><p>Rolling a natural <code class="docutils literal notranslate"><span class="pre">1</span></code> is a <em>critical failure</em>. A natural <code class="docutils literal notranslate"><span class="pre">20</span></code> is a <em>critical success</em>. Rolling such
|
||||
in combat means your weapon or armor loses quality, which will eventually destroy it.</p></li>
|
||||
<li><p>A <em>saving throw</em> (trying to succeed against the environment) means making a roll to beat <code class="docutils literal notranslate"><span class="pre">15</span></code> (always).
|
||||
So if you are lifting a heavy stone and have <code class="docutils literal notranslate"><span class="pre">STR</span> <span class="pre">+2</span></code>, you’d roll <code class="docutils literal notranslate"><span class="pre">1d20</span> <span class="pre">+</span> <span class="pre">2</span></code> and hope the result
|
||||
is higher than <code class="docutils literal notranslate"><span class="pre">15</span></code>.</p></li>
|
||||
<li><p>An <em>opposed saving throw</em> means beating the enemy’s suitable Ability ‘defense’, which is always their
|
||||
<code class="docutils literal notranslate"><span class="pre">Ability</span> <span class="pre">bonus</span> <span class="pre">+</span> <span class="pre">10</span></code>. So if you have <code class="docutils literal notranslate"><span class="pre">STR</span> <span class="pre">+1</span></code> and are arm wrestling someone with <code class="docutils literal notranslate"><span class="pre">STR</span> <span class="pre">+2</span></code>, you roll
|
||||
<code class="docutils literal notranslate"><span class="pre">1d20</span> <span class="pre">+</span> <span class="pre">1</span></code> and hope to roll higher than <code class="docutils literal notranslate"><span class="pre">2</span> <span class="pre">+</span> <span class="pre">10</span> <span class="pre">=</span> <span class="pre">12</span></code>.</p></li>
|
||||
<li><p>A special bonus is <code class="docutils literal notranslate"><span class="pre">Armor</span></code>, <code class="docutils literal notranslate"><span class="pre">+1</span></code> is unarmored, additional armor is given by equipment. Melee attacks
|
||||
test <code class="docutils literal notranslate"><span class="pre">STR</span></code> versus the <code class="docutils literal notranslate"><span class="pre">Armor</span></code> defense value while ranged attacks uses <code class="docutils literal notranslate"><span class="pre">WIS</span></code> vs <code class="docutils literal notranslate"><span class="pre">Armor</span></code>.</p></li>
|
||||
<li><p><em>Knave</em> has no skills or classes. Everyone can use all items and using magic means having a special
|
||||
‘rune stone’ in your hands; one spell per stone and day.</p></li>
|
||||
<li><p>A character has <code class="docutils literal notranslate"><span class="pre">CON</span> <span class="pre">+</span> <span class="pre">10</span></code> carry ‘slots’. Most normal items uses one slot, armor and large weapons uses
|
||||
two or three.</p></li>
|
||||
<li><p>Healing is random, <code class="docutils literal notranslate"><span class="pre">1d8</span> <span class="pre">+</span> <span class="pre">CON</span></code> health healed after food and sleep.</p></li>
|
||||
<li><p>Monster difficulty is listed by hy many 1d8 HP they have; this is called their “hit die” or HD. If
|
||||
needing to test Abilities, monsters have HD bonus in every Ability.</p></li>
|
||||
<li><p>Monsters have a <em>morale rating</em>. When things go bad, they have a chance to panic and flee if
|
||||
rolling <code class="docutils literal notranslate"><span class="pre">2d6</span></code> over their morale rating.</p></li>
|
||||
<li><p>All Characters in <em>Knave</em> are mostly randomly generated. HP is <code class="docutils literal notranslate"><span class="pre"><level>d8</span></code> but we give every
|
||||
new character max HP to start.</p></li>
|
||||
<li><p><em>Knave</em> also have random tables, such as for starting equipment and to see if dying when
|
||||
hitting 0. Death, if it happens, is permanent.</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="making-a-rule-module">
|
||||
<h2><span class="section-number">2.2. </span>Making a rule module<a class="headerlink" href="#making-a-rule-module" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Create a new module mygame/evadventure/rules.py</p>
|
||||
</div></blockquote>
|
||||
<aside class="sidebar">
|
||||
<p>A complete version of the rule module is found in
|
||||
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.rules.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/rules.py</span></a>.</p>
|
||||
</aside>
|
||||
<p>There are three broad sets of rules for most RPGS:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Character generation rules, often only used during character creation</p></li>
|
||||
<li><p>Regular gameplay rules - rolling dice and resolving game situations</p></li>
|
||||
<li><p>Character improvement - getting and spending experience to improve the character</p></li>
|
||||
</ul>
|
||||
<p>We want our <code class="docutils literal notranslate"><span class="pre">rules</span></code> module to cover as many aspeects of what we’d otherwise would have to look up
|
||||
in a rulebook.</p>
|
||||
</section>
|
||||
<section id="rolling-dice">
|
||||
<h2><span class="section-number">2.3. </span>Rolling dice<a class="headerlink" href="#rolling-dice" title="Permalink to this headline">¶</a></h2>
|
||||
<p>We will start by making a dice roller. Let’s group all of our dice rolling into a structure like this
|
||||
(not functional code yet):</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
|
||||
<span class="c1"># get result of one generic roll, for any type and number of dice</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll_with_advantage_or_disadvantage</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
|
||||
<span class="c1"># get result of normal d20 roll, with advantage/disadvantage (or not)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">saving_throw</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
|
||||
<span class="c1"># do a saving throw against a specific target number</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">opposed_saving_throw</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
|
||||
<span class="c1"># do an opposed saving throw against a target's defense</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll_random_table</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
|
||||
<span class="c1"># make a roll against a random table (loaded elsewere)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">morale_check</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
|
||||
<span class="c1"># roll a 2d6 morale check for a target</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">heal_from_rest</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
|
||||
<span class="c1"># heal 1d8 when resting+eating, but not more than max value.</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll_death</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
|
||||
<span class="c1"># roll to determine penalty when hitting 0 HP. </span>
|
||||
|
||||
|
||||
<span class="n">dice</span> <span class="o">=</span> <span class="n">EvAdventureRollEngine</span><span class="p">()</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p>This groups all dice-related code into one ‘container’ that is easy to import. But it’s mostly a matter
|
||||
of taste. You <em>could</em> also break up the class’ methods into normal functions at the top-level of the
|
||||
module if you wanted.</p>
|
||||
</aside>
|
||||
<p>This structure (called a <em>singleton</em>) means we group all dice rolls into one class that we then initiate
|
||||
into a variable <code class="docutils literal notranslate"><span class="pre">dice</span></code> at the end of the module. This means that we can do the following from other
|
||||
modules:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="kn">from</span> <span class="nn">.rules</span> <span class="kn">import</span> <span class="n">dice</span>
|
||||
|
||||
<span class="n">dice</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d8"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<section id="generic-dice-roller">
|
||||
<h3><span class="section-number">2.3.1. </span>Generic dice roller<a class="headerlink" href="#generic-dice-roller" title="Permalink to this headline">¶</a></h3>
|
||||
<p>We want to be able to do <code class="docutils literal notranslate"><span class="pre">roll("1d20")</span></code> and get a random result back from the roll.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">roll_string</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Roll XdY dice, where X is the number of dice </span>
|
||||
<span class="sd"> and Y the number of sides per die. </span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> roll_string (str): A dice string on the form XdY.</span>
|
||||
<span class="sd"> Returns:</span>
|
||||
<span class="sd"> int: The result of the roll. </span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="c1"># split the XdY input on the 'd' one time</span>
|
||||
<span class="n">number</span><span class="p">,</span> <span class="n">diesize</span> <span class="o">=</span> <span class="n">roll_string</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"d"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># convert from string to integers</span>
|
||||
<span class="n">number</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">number</span><span class="p">)</span>
|
||||
<span class="n">diesize</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">diesize</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># make the roll</span>
|
||||
<span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">diesize</span><span class="p">)</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">number</span><span class="p">))</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p>For this tutorial we have opted to not use any contribs, so we create
|
||||
our own dice roller. But normally you could instead use the <a class="reference internal" href="../../../Contribs/Contrib-Dice.html"><span class="doc std std-doc">dice</span></a> contrib for this.
|
||||
We’ll point out possible helpful contribs in sidebars as we proceed.</p>
|
||||
</aside>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">randint</span></code> standard Python library module produces a random integer<br />
|
||||
in a specific range. The line</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nb">sum</span><span class="p">(</span><span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">diesize</span><span class="p">)</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">number</span><span class="p">))</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>works like this:</p>
|
||||
<ul class="simple">
|
||||
<li><p>For a certain <code class="docutils literal notranslate"><span class="pre">number</span></code> of times …</p></li>
|
||||
<li><p>… create a random integer between <code class="docutils literal notranslate"><span class="pre">1</span></code> and <code class="docutils literal notranslate"><span class="pre">diesize</span></code> …</p></li>
|
||||
<li><p>… and <code class="docutils literal notranslate"><span class="pre">sum</span></code> all those integers together.</p></li>
|
||||
</ul>
|
||||
<p>You could write the same thing less compactly like this:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">rolls</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">number</span><span class="p">):</span>
|
||||
<span class="n">random_result</span> <span class="o">=</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">diesize</span><span class="p">)</span>
|
||||
<span class="n">rolls</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">random_result</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">rolls</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<p>Note that <code class="docutils literal notranslate"><span class="pre">range</span></code> generates a value <code class="docutils literal notranslate"><span class="pre">0...number-1</span></code>. We use <code class="docutils literal notranslate"><span class="pre">_</span></code> in the <code class="docutils literal notranslate"><span class="pre">for</span></code> loop to
|
||||
indicate we don’t really care what this value is - we just want to repeat the loop
|
||||
a certain amount of times.</p>
|
||||
</aside>
|
||||
<p>We don’t ever expect end users to call this method; if we did, we would have to validate the inputs
|
||||
much more - We would have to make sure that <code class="docutils literal notranslate"><span class="pre">number</span></code> or <code class="docutils literal notranslate"><span class="pre">diesize</span></code> are valid inputs and not
|
||||
crazy big so the loop takes forever!</p>
|
||||
</section>
|
||||
<section id="rolling-with-advantage">
|
||||
<h3><span class="section-number">2.3.2. </span>Rolling with advantage<a class="headerlink" href="#rolling-with-advantage" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Now that we have the generic roller, we can start using it to do a more complex roll.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll</span><span class="p">(</span><span class="n">roll_string</span><span class="p">):</span>
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll_with_advantage_or_disadvantage</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">advantage</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">disadvantage</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
|
||||
|
||||
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">advantage</span> <span class="ow">or</span> <span class="n">disadvantage</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">advantage</span> <span class="ow">and</span> <span class="n">disadvantage</span><span class="p">):</span>
|
||||
<span class="c1"># normal roll - advantage/disadvantage not set or they cancel </span>
|
||||
<span class="c1"># each other out </span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">)</span>
|
||||
<span class="k">elif</span> <span class="n">advantage</span><span class="p">:</span>
|
||||
<span class="c1"># highest of two d20 rolls</span>
|
||||
<span class="k">return</span> <span class="nb">max</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">),</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">))</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># disadvantage - lowest of two d20 rolls </span>
|
||||
<span class="k">return</span> <span class="nb">min</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">),</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d20"</span><span class="p">))</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">min()</span></code> and <code class="docutils literal notranslate"><span class="pre">max()</span></code> functions are standard Python fare for getting the biggest/smallest
|
||||
of two arguments.</p>
|
||||
</section>
|
||||
<section id="saving-throws">
|
||||
<h3><span class="section-number">2.3.3. </span>Saving throws<a class="headerlink" href="#saving-throws" title="Permalink to this headline">¶</a></h3>
|
||||
<p>We want the saving throw to itself figure out if it succeeded or not. This means it needs to know
|
||||
the Ability bonus (like STR <code class="docutils literal notranslate"><span class="pre">+1</span></code>). It would be convenient if we could just pass the entity
|
||||
doing the saving throw to this method, tell it what type of save was needed, and then
|
||||
have it figure things out:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">result</span><span class="p">,</span> <span class="n">quality</span> <span class="o">=</span> <span class="n">dice</span><span class="o">.</span><span class="n">saving_throw</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The return will be a boolean <code class="docutils literal notranslate"><span class="pre">True/False</span></code> if they pass, as well as a <code class="docutils literal notranslate"><span class="pre">quality</span></code> that tells us if
|
||||
a perfect fail/success was rolled or not.</p>
|
||||
<p>To make the saving throw method this clever, we need to think some more about how we want to store our
|
||||
data on the character.</p>
|
||||
<p>For our purposes it sounds reasonable that we will be using <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attributes</span></a> for storing
|
||||
the Ability scores. To make it easy, we will name them the same as the
|
||||
<a class="reference internal" href="Beginner-Tutorial-Utilities.html#enums"><span class="std std-doc">Enum values</span></a> we set up in the previous lesson. So if we have
|
||||
an enum <code class="docutils literal notranslate"><span class="pre">STR</span> <span class="pre">=</span> <span class="pre">"strength"</span></code>, we want to store the Ability on the character as an Attribute <code class="docutils literal notranslate"><span class="pre">strength</span></code>.</p>
|
||||
<p>From the Attribute documentation, we can see that we can use <code class="docutils literal notranslate"><span class="pre">AttributeProperty</span></code> to make it so the
|
||||
Attribute is available as <code class="docutils literal notranslate"><span class="pre">character.strength</span></code>, and this is what we will do.</p>
|
||||
<p>So, in short, we’ll create the saving throws method with the assumption that we will be able to do
|
||||
<code class="docutils literal notranslate"><span class="pre">character.strength</span></code>, <code class="docutils literal notranslate"><span class="pre">character.constitution</span></code>, <code class="docutils literal notranslate"><span class="pre">character.charisma</span></code> etc to get the relevant Abilities.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
|
||||
<span class="c1"># ...</span>
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">Ability</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll_with_advantage_or_disadvantage</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">saving_throw</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">character</span><span class="p">,</span> <span class="n">bonus_type</span><span class="o">=</span><span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">,</span> <span class="n">target</span><span class="o">=</span><span class="mi">15</span><span class="p">,</span>
|
||||
<span class="n">advantage</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">disadvantage</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Do a saving throw, trying to beat a target.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> character (Character): A character (assumed to have Ability bonuses</span>
|
||||
<span class="sd"> stored on itself as Attributes).</span>
|
||||
<span class="sd"> bonus_type (Ability): A valid Ability bonus enum.</span>
|
||||
<span class="sd"> target (int): The target number to beat. Always 15 in Knave.</span>
|
||||
<span class="sd"> advantage (bool): If character has advantage on this roll.</span>
|
||||
<span class="sd"> disadvantage (bool): If character has disadvantage on this roll.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> Returns:</span>
|
||||
<span class="sd"> tuple: A tuple (bool, Ability), showing if the throw succeeded and </span>
|
||||
<span class="sd"> the quality is one of None or Ability.CRITICAL_FAILURE/SUCCESS</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="c1"># make a roll </span>
|
||||
<span class="n">dice_roll</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll_with_advantage_or_disadvantage</span><span class="p">(</span><span class="n">advantage</span><span class="p">,</span> <span class="n">disadvantage</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># figure out if we had critical failure/success</span>
|
||||
<span class="n">quality</span> <span class="o">=</span> <span class="kc">None</span>
|
||||
<span class="k">if</span> <span class="n">dice_roll</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
|
||||
<span class="n">quality</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CRITICAL_FAILURE</span>
|
||||
<span class="k">elif</span> <span class="n">dice_roll</span> <span class="o">==</span> <span class="mi">20</span><span class="p">:</span>
|
||||
<span class="n">quality</span> <span class="o">=</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CRITICAL_SUCCESS</span>
|
||||
|
||||
<span class="c1"># figure out bonus</span>
|
||||
<span class="n">bonus</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="n">bonus_type</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># return a tuple (bool, quality)</span>
|
||||
<span class="k">return</span> <span class="p">(</span><span class="n">dice_roll</span> <span class="o">+</span> <span class="n">bonus</span><span class="p">)</span> <span class="o">></span> <span class="n">target</span><span class="p">,</span> <span class="n">quality</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">getattr(obj,</span> <span class="pre">attrname,</span> <span class="pre">default)</span></code> function is a very useful Python tool for getting an attribute
|
||||
off an object and getting a default value if the attribute is not defined.</p>
|
||||
</section>
|
||||
<section id="opposed-saving-throw">
|
||||
<h3><span class="section-number">2.3.4. </span>Opposed saving throw<a class="headerlink" href="#opposed-saving-throw" title="Permalink to this headline">¶</a></h3>
|
||||
<p>With the building pieces we already created, this method is simple. Remember that the defense you have
|
||||
to beat is always the relevant bonus + 10 in <em>Knave</em>. So if the enemy defends with <code class="docutils literal notranslate"><span class="pre">STR</span> <span class="pre">+3</span></code>, you must
|
||||
roll higher than <code class="docutils literal notranslate"><span class="pre">13</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">Ability</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll_with_advantage_or_disadvantage</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">saving_throw</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">opposed_saving_throw</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attacker</span><span class="p">,</span> <span class="n">defender</span><span class="p">,</span>
|
||||
<span class="n">attack_type</span><span class="o">=</span><span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">,</span> <span class="n">defense_type</span><span class="o">=</span><span class="n">Ability</span><span class="o">.</span><span class="n">ARMOR</span><span class="p">,</span>
|
||||
<span class="n">advantage</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">disadvantage</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
|
||||
<span class="n">defender_defense</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">defender</span><span class="p">,</span> <span class="n">defense_type</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="mi">10</span>
|
||||
<span class="n">result</span><span class="p">,</span> <span class="n">quality</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">saving_throw</span><span class="p">(</span><span class="n">attacker</span><span class="p">,</span> <span class="n">bonus_type</span><span class="o">=</span><span class="n">attack_type</span><span class="p">,</span>
|
||||
<span class="n">target</span><span class="o">=</span><span class="n">defender_defense</span><span class="p">,</span>
|
||||
<span class="n">advantage</span><span class="o">=</span><span class="n">advantage</span><span class="p">,</span> <span class="n">disadvantage</span><span class="o">=</span><span class="n">disadvantage</span><span class="p">)</span>
|
||||
|
||||
<span class="k">return</span> <span class="n">result</span><span class="p">,</span> <span class="n">quality</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="morale-check">
|
||||
<h3><span class="section-number">2.3.5. </span>Morale check<a class="headerlink" href="#morale-check" title="Permalink to this headline">¶</a></h3>
|
||||
<p>We will make the assumption that the <code class="docutils literal notranslate"><span class="pre">morale</span></code> value is available from the creature simply as
|
||||
<code class="docutils literal notranslate"><span class="pre">monster.morale</span></code> - we need to remember to make this so later!</p>
|
||||
<p>In <em>Knave</em>, a creature have roll with <code class="docutils literal notranslate"><span class="pre">2d6</span></code> equal or under its morale to not flee or surrender
|
||||
when things go south. The standard morale value is 9.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ...</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">morale_check</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">defender</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"2d6"</span><span class="p">)</span> <span class="o"><=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">defender</span><span class="p">,</span> <span class="s2">"morale"</span><span class="p">,</span> <span class="mi">9</span><span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="roll-for-healing">
|
||||
<h3><span class="section-number">2.3.6. </span>Roll for Healing<a class="headerlink" href="#roll-for-healing" title="Permalink to this headline">¶</a></h3>
|
||||
<p>To be able to handle healing, we need to make some more assumptions about how we store
|
||||
health on game entities. We will need <code class="docutils literal notranslate"><span class="pre">hp_max</span></code> (the total amount of available HP) and <code class="docutils literal notranslate"><span class="pre">hp</span></code>
|
||||
(the current health value). We again assume these will be available as <code class="docutils literal notranslate"><span class="pre">obj.hp</span></code> and <code class="docutils literal notranslate"><span class="pre">obj.hp_max</span></code>.</p>
|
||||
<p>According to the rules, after consuming a ration and having a full night’s sleep, a character regains
|
||||
<code class="docutils literal notranslate"><span class="pre">1d8</span> <span class="pre">+</span> <span class="pre">CON</span></code> HP.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">Ability</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">heal_from_rest</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">character</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> A night's rest retains 1d8 + CON HP </span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">con_bonus</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CON</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||||
<span class="n">character</span><span class="o">.</span><span class="n">heal</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d8"</span><span class="p">)</span> <span class="o">+</span> <span class="n">con_bonus</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We make another assumption here - that <code class="docutils literal notranslate"><span class="pre">character.heal()</span></code> is a thing. We tell this function how
|
||||
much the character should heal, and it will do so, making sure to not heal more than its max
|
||||
number of HPs</p>
|
||||
<blockquote>
|
||||
<div><p>Knowing what is available on the character and what rule rolls we need is a bit of a chicken-and-egg
|
||||
problem. We will make sure to implement the matching <em>Character</em> class next lesson.</p>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
<section id="rolling-on-a-table">
|
||||
<h3><span class="section-number">2.3.7. </span>Rolling on a table<a class="headerlink" href="#rolling-on-a-table" title="Permalink to this headline">¶</a></h3>
|
||||
<p>We occasionally need to roll on a ‘table’ - a selection of choices. There are two main table-types
|
||||
we need to support:</p>
|
||||
<p>Simply one element per row of the table (same odds to get each result).</p>
|
||||
<table class="colwidths-auto docutils align-default">
|
||||
<thead>
|
||||
<tr class="row-odd"><th class="text-align:center head"><p>Result</p></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="row-even"><td class="text-align:center"><p>item1</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td class="text-align:center"><p>item2</p></td>
|
||||
</tr>
|
||||
<tr class="row-even"><td class="text-align:center"><p>item3</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td class="text-align:center"><p>item4</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>This we will simply represent as a plain list</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="s2">"item1"</span><span class="p">,</span> <span class="s2">"item2"</span><span class="p">,</span> <span class="s2">"item3"</span><span class="p">,</span> <span class="s2">"item4"</span><span class="p">]</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Ranges per item (varying odds per result):</p>
|
||||
<table class="colwidths-auto docutils align-default">
|
||||
<thead>
|
||||
<tr class="row-odd"><th class="text-align:center head"><p>Range</p></th>
|
||||
<th class="text-align:center head"><p>Result</p></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="row-even"><td class="text-align:center"><p>1-5</p></td>
|
||||
<td class="text-align:center"><p>item1</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td class="text-align:center"><p>6-15</p></td>
|
||||
<td class="text-align:center"><p>item2</p></td>
|
||||
</tr>
|
||||
<tr class="row-even"><td class="text-align:center"><p>16-19</p></td>
|
||||
<td class="text-align:center"><p>item3</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td class="text-align:center"><p>20</p></td>
|
||||
<td class="text-align:center"><p>item4</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>This we will represent as a list of tuples:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="p">[(</span><span class="s2">"1-5"</span><span class="p">,</span> <span class="s2">"item1"</span><span class="p">),</span> <span class="p">(</span><span class="s2">"6-15"</span><span class="p">,</span> <span class="s2">"item2"</span><span class="p">),</span> <span class="p">(</span><span class="s2">"16-19"</span><span class="p">,</span> <span class="s2">"item4"</span><span class="p">),</span> <span class="p">(</span><span class="s2">"20"</span><span class="p">,</span> <span class="s2">"item5"</span><span class="p">)]</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We also need to know what die to roll to get a result on the table (it may not always
|
||||
be obvious, and in some games you could be asked to roll a lower dice to only get
|
||||
early table results, for example).</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span><span class="p">,</span> <span class="n">choice</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll_random_table</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dieroll</span><span class="p">,</span> <span class="n">table_choices</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Args: </span>
|
||||
<span class="sd"> dieroll (str): A die roll string, like "1d20".</span>
|
||||
<span class="sd"> table_choices (iterable): A list of either single elements or </span>
|
||||
<span class="sd"> of tuples.</span>
|
||||
<span class="sd"> Returns: </span>
|
||||
<span class="sd"> Any: A random result from the given list of choices.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> Raises:</span>
|
||||
<span class="sd"> RuntimeError: If rolling dice giving results outside the table.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="n">roll_result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="n">dieroll</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">table_choices</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">(</span><span class="nb">tuple</span><span class="p">,</span> <span class="nb">list</span><span class="p">)):</span>
|
||||
<span class="c1"># the first element is a tuple/list; treat as on the form [("1-5", "item"),...]</span>
|
||||
<span class="k">for</span> <span class="p">(</span><span class="n">valrange</span><span class="p">,</span> <span class="n">choice</span><span class="p">)</span> <span class="ow">in</span> <span class="n">table_choices</span><span class="p">:</span>
|
||||
<span class="n">minval</span><span class="p">,</span> <span class="o">*</span><span class="n">maxval</span> <span class="o">=</span> <span class="n">valrange</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"-"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||||
<span class="n">minval</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">minval</span><span class="p">))</span>
|
||||
<span class="n">maxval</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">maxval</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">if</span> <span class="n">maxval</span> <span class="k">else</span> <span class="n">minval</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">minval</span> <span class="o"><=</span> <span class="n">roll_result</span> <span class="o"><=</span> <span class="n">maxval</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="n">choice</span>
|
||||
|
||||
<span class="c1"># if we get here we must have set a dieroll producing a value </span>
|
||||
<span class="c1"># outside of the table boundaries - raise error</span>
|
||||
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span><span class="s2">"roll_random_table: Invalid die roll"</span><span class="p">)</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># a simple regular list</span>
|
||||
<span class="n">roll_result</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">min</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">table_choices</span><span class="p">),</span> <span class="n">roll_result</span><span class="p">))</span>
|
||||
<span class="k">return</span> <span class="n">table_choices</span><span class="p">[</span><span class="n">roll_result</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Check that you understand what this does.</p>
|
||||
<p>This may be confusing:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">minval</span><span class="p">,</span> <span class="o">*</span><span class="n">maxval</span> <span class="o">=</span> <span class="n">valrange</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"-"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||||
<span class="n">minval</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">minval</span><span class="p">))</span>
|
||||
<span class="n">maxval</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">maxval</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">if</span> <span class="n">maxval</span> <span class="k">else</span> <span class="n">minval</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If <code class="docutils literal notranslate"><span class="pre">valrange</span></code> is the string <code class="docutils literal notranslate"><span class="pre">1-5</span></code>, then <code class="docutils literal notranslate"><span class="pre">valrange.split("-",</span> <span class="pre">1)</span></code> would result in a tuple <code class="docutils literal notranslate"><span class="pre">("1",</span> <span class="pre">"5")</span></code>.
|
||||
But if the string was in fact just <code class="docutils literal notranslate"><span class="pre">"20"</span></code> (possible for a single entry in an RPG table), this would
|
||||
lead to an error since it would only split out a single element - and we expected two.</p>
|
||||
<p>By using <code class="docutils literal notranslate"><span class="pre">*maxval</span></code> (with the <code class="docutils literal notranslate"><span class="pre">*</span></code>), <code class="docutils literal notranslate"><span class="pre">maxval</span></code> is told to expect <em>0 or more</em> elements in a tuple.
|
||||
So the result for <code class="docutils literal notranslate"><span class="pre">1-5</span></code> will be <code class="docutils literal notranslate"><span class="pre">("1",</span> <span class="pre">("5",))</span></code> and for <code class="docutils literal notranslate"><span class="pre">20</span></code> it will become <code class="docutils literal notranslate"><span class="pre">("20",</span> <span class="pre">())</span></code>. In the line</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">maxval</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">maxval</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">if</span> <span class="n">maxval</span> <span class="k">else</span> <span class="n">minval</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>we check if <code class="docutils literal notranslate"><span class="pre">maxval</span></code> actually has a value <code class="docutils literal notranslate"><span class="pre">("5",)</span></code> or if its empty <code class="docutils literal notranslate"><span class="pre">()</span></code>. The result is either
|
||||
<code class="docutils literal notranslate"><span class="pre">"5"</span></code> or the value of <code class="docutils literal notranslate"><span class="pre">minval</span></code>.</p>
|
||||
</section>
|
||||
<section id="roll-for-death">
|
||||
<h3><span class="section-number">2.3.8. </span>Roll for death<a class="headerlink" href="#roll-for-death" title="Permalink to this headline">¶</a></h3>
|
||||
<p>While original Knave suggests hitting 0 HP means insta-death, we will grab the optional “death table” from the “prettified” Knave’s optional rules to make it a little less punishing. We also changed the result of <code class="docutils literal notranslate"><span class="pre">2</span></code> to ‘dead’ since we don’t simulate ‘dismemberment’ in this tutorial:</p>
|
||||
<table class="colwidths-auto docutils align-default">
|
||||
<thead>
|
||||
<tr class="row-odd"><th class="text-align:center head"><p>Roll</p></th>
|
||||
<th class="text-align:center head"><p>Result</p></th>
|
||||
<th class="text-align:center head"><p>-1d4 Loss of Ability</p></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="row-even"><td class="text-align:center"><p>1-2</p></td>
|
||||
<td class="text-align:center"><p>dead</p></td>
|
||||
<td class="text-align:center"><p>-</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td class="text-align:center"><p>3</p></td>
|
||||
<td class="text-align:center"><p>weakened</p></td>
|
||||
<td class="text-align:center"><p>STR</p></td>
|
||||
</tr>
|
||||
<tr class="row-even"><td class="text-align:center"><p>4</p></td>
|
||||
<td class="text-align:center"><p>unsteady</p></td>
|
||||
<td class="text-align:center"><p>DEX</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td class="text-align:center"><p>5</p></td>
|
||||
<td class="text-align:center"><p>sickly</p></td>
|
||||
<td class="text-align:center"><p>CON</p></td>
|
||||
</tr>
|
||||
<tr class="row-even"><td class="text-align:center"><p>6</p></td>
|
||||
<td class="text-align:center"><p>addled</p></td>
|
||||
<td class="text-align:center"><p>INT</p></td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td class="text-align:center"><p>7</p></td>
|
||||
<td class="text-align:center"><p>rattled</p></td>
|
||||
<td class="text-align:center"><p>WIS</p></td>
|
||||
</tr>
|
||||
<tr class="row-even"><td class="text-align:center"><p>8</p></td>
|
||||
<td class="text-align:center"><p>disfigured</p></td>
|
||||
<td class="text-align:center"><p>CHA</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>All the non-dead values map to a loss of 1d4 in one of the six Abilities (but you get HP back). We need to map back to this from the above table. One also cannot have less than -10 Ability bonus, if you do, you die too.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/rules.py </span>
|
||||
|
||||
<span class="n">death_table</span> <span class="o">=</span> <span class="p">(</span>
|
||||
<span class="p">(</span><span class="s2">"1-2"</span><span class="p">,</span> <span class="s2">"dead"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"3"</span><span class="p">,</span> <span class="s2">"strength"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"4"</span><span class="p">,</span> <span class="s2">"dexterity"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"5"</span><span class="p">,</span> <span class="s2">"constitution"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"6"</span><span class="p">,</span> <span class="s2">"intelligence"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"7"</span><span class="p">,</span> <span class="s2">"wisdom"</span><span class="p">),</span>
|
||||
<span class="p">(</span><span class="s2">"8"</span><span class="p">,</span> <span class="s2">"charisma"</span><span class="p">),</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">EvAdventureRollEngine</span><span class="p">:</span>
|
||||
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll_random_table</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
|
||||
<span class="c1"># ... </span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">roll_death</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">character</span><span class="p">):</span>
|
||||
<span class="n">ability_name</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll_random_table</span><span class="p">(</span><span class="s2">"1d8"</span><span class="p">,</span> <span class="n">death_table</span><span class="p">)</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">ability_name</span> <span class="o">==</span> <span class="s2">"dead"</span><span class="p">:</span>
|
||||
<span class="c1"># TODO - kill the character! </span>
|
||||
<span class="k">pass</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">loss</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d4"</span><span class="p">)</span>
|
||||
|
||||
<span class="n">current_ability</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="n">ability_name</span><span class="p">)</span>
|
||||
<span class="n">current_ability</span> <span class="o">-=</span> <span class="n">loss</span>
|
||||
|
||||
<span class="k">if</span> <span class="n">current_ability</span> <span class="o"><</span> <span class="o">-</span><span class="mi">10</span><span class="p">:</span>
|
||||
<span class="c1"># TODO - kill the character!</span>
|
||||
<span class="k">pass</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="c1"># refresh 1d4 health, but suffer 1d4 ability loss</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">heal</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d4"</span><span class="p">))</span>
|
||||
<span class="nb">setattr</span><span class="p">(</span><span class="n">character</span><span class="p">,</span> <span class="n">ability_name</span><span class="p">,</span> <span class="n">current_ability</span><span class="p">)</span>
|
||||
|
||||
<span class="n">character</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span>
|
||||
<span class="s2">"You survive your brush with death, and while you recover "</span>
|
||||
<span class="sa">f</span><span class="s2">"some health, you permanently lose </span><span class="si">{</span><span class="n">loss</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">ability_name</span><span class="si">}</span><span class="s2"> instead."</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
<span class="n">dice</span> <span class="o">=</span> <span class="n">EvAdventureRollEngine</span><span class="p">()</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here we roll on the ‘death table’ from the rules to see what happens. We give the character
|
||||
a message if they survive, to let them know what happened.</p>
|
||||
<p>We don’t yet know what ‘killing the character’ technically means, so we mark this as <code class="docutils literal notranslate"><span class="pre">TODO</span></code> and return to it in a later lesson. We just know that we need to do <em>something</em> here to kill off the character!</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="testing">
|
||||
<h2><span class="section-number">2.4. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Make a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/tests/test_rules.py</span></code></p>
|
||||
</div></blockquote>
|
||||
<p>Testing the <code class="docutils literal notranslate"><span class="pre">rules</span></code> module will also showcase some very useful tools when testing.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/tests/test_rules.py </span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">unittest.mock</span> <span class="kn">import</span> <span class="n">patch</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">BaseEvenniaTest</span>
|
||||
<span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">rules</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">TestEvAdventureRuleEngine</span><span class="p">(</span><span class="n">BaseEvenniaTest</span><span class="p">):</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""Called before every test method"""</span>
|
||||
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">roll_engine</span> <span class="o">=</span> <span class="n">rules</span><span class="o">.</span><span class="n">EvAdventureRollEngine</span><span class="p">()</span>
|
||||
|
||||
<span class="nd">@patch</span><span class="p">(</span><span class="s2">"evadventure.rules.randint"</span><span class="p">)</span>
|
||||
<span class="k">def</span> <span class="nf">test_roll</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mock_randint</span><span class="p">):</span>
|
||||
<span class="n">mock_randint</span><span class="o">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="mi">4</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">roll_engine</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"1d6"</span><span class="p">),</span> <span class="mi">4</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">roll_engine</span><span class="o">.</span><span class="n">roll</span><span class="p">(</span><span class="s2">"2d6"</span><span class="p">),</span> <span class="mi">2</span> <span class="o">*</span> <span class="mi">4</span><span class="p">)</span>
|
||||
|
||||
<span class="c1"># test of the other rule methods below ...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>As before, run the specific test with</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings settings.py .evadventure.tests.test_rules
|
||||
</pre></div>
|
||||
</div>
|
||||
<section id="mocking-and-patching">
|
||||
<h3><span class="section-number">2.4.1. </span>Mocking and patching<a class="headerlink" href="#mocking-and-patching" title="Permalink to this headline">¶</a></h3>
|
||||
<aside class="sidebar">
|
||||
<p>In <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_rules.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/tests/test_rules.py</span></a>
|
||||
has a complete example of rule testing.</p>
|
||||
</aside>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">setUp</span></code> method is a special method of the testing class. It will be run before every
|
||||
test method. We use <code class="docutils literal notranslate"><span class="pre">super().setUp()</span></code> to make sure the parent class’ version of this method
|
||||
always fire. Then we create a fresh <code class="docutils literal notranslate"><span class="pre">EvAdventureRollEngine</span></code> we can test with.</p>
|
||||
<p>In our test, we import <code class="docutils literal notranslate"><span class="pre">patch</span></code> from the <code class="docutils literal notranslate"><span class="pre">unittest.mock</span></code> library. This is a very useful tool for testing.
|
||||
Normally the <code class="docutils literal notranslate"><span class="pre">randint</span></code> function we imported in <code class="docutils literal notranslate"><span class="pre">rules</span></code> will return a random value. That’s very hard to test for, since the value will be different every test.</p>
|
||||
<p>With <code class="docutils literal notranslate"><span class="pre">@patch</span></code> (this is called a <em>decorator</em>), we temporarily replace <code class="docutils literal notranslate"><span class="pre">rules.randint</span></code> with a ‘mock’ - a dummy entity. This mock is passed into the testing method. We then take this <code class="docutils literal notranslate"><span class="pre">mock_randint</span></code> and set <code class="docutils literal notranslate"><span class="pre">.return_value</span> <span class="pre">=</span> <span class="pre">4</span></code> on it.</p>
|
||||
<p>Adding <code class="docutils literal notranslate"><span class="pre">return_value</span></code> to the mock means that every time this mock is called, it will return 4. For the duration of the test we can now check with <code class="docutils literal notranslate"><span class="pre">self.assertEqual</span></code> that our <code class="docutils literal notranslate"><span class="pre">roll</span></code> method always returns a result as-if the random result was 4.</p>
|
||||
<p>There are <a class="reference external" href="https://realpython.com/python-mock-library/">many resources for understanding mock</a>, refer to
|
||||
them for further help.</p>
|
||||
<blockquote>
|
||||
<div><p>The <code class="docutils literal notranslate"><span class="pre">EvAdventureRollEngine</span></code> have many methods to test. We leave this as an extra exercise!</p>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
</section>
|
||||
<section id="summary">
|
||||
<h2><span class="section-number">2.5. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This concludes all the core rule mechanics of <em>Knave</em> - the rules used during play. We noticed here that we are going to soon need to establish how our <em>Character</em> actually stores data. So we will address that next.</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="Beginner-Tutorial-Characters.html" title="3. Player Characters"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Utilities.html" title="1. Code structure and Utilities"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">2. </span>Rules and dice rolling</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
|
||||
<!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>15. In-game Shops — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="16. In-game Commands" href="Beginner-Tutorial-Commands.html" />
|
||||
<link rel="prev" title="14. Game Quests" href="Beginner-Tutorial-Quests.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Commands.html" title="16. In-game Commands"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Quests.html" title="14. Game Quests"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">15. </span>In-game Shops</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Quests.html"
|
||||
title="previous chapter"><span class="section-number">14. </span>Game Quests</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Commands.html"
|
||||
title="next chapter"><span class="section-number">16. </span>In-game Commands</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Shops.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="in-game-shops">
|
||||
<h1><span class="section-number">15. </span>In-game Shops<a class="headerlink" href="#in-game-shops" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="admonition warning">
|
||||
<p class="admonition-title">Warning</p>
|
||||
<p>This part of the Beginner tutorial is still being developed.</p>
|
||||
</div>
|
||||
</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="Beginner-Tutorial-Commands.html" title="16. In-game Commands"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Quests.html" title="14. Game Quests"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">15. </span>In-game Shops</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,434 @@
|
|||
|
||||
<!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>1. Code structure and Utilities — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="2. Rules and dice rolling" href="Beginner-Tutorial-Rules.html" />
|
||||
<link rel="prev" title="Part 3: How We Get There (Example Game)" href="Beginner-Tutorial-Part3-Overview.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Rules.html" title="2. Rules and dice rolling"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Part3-Overview.html" title="Part 3: How We Get There (Example Game)"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" accesskey="U">Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">1. </span>Code structure and Utilities</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">1. Code structure and Utilities</a><ul>
|
||||
<li><a class="reference internal" href="#folder-structure">1.1. Folder structure</a></li>
|
||||
<li><a class="reference internal" href="#enums">1.2. Enums</a></li>
|
||||
<li><a class="reference internal" href="#utility-module">1.3. Utility module</a></li>
|
||||
<li><a class="reference internal" href="#testing">1.4. Testing</a><ul>
|
||||
<li><a class="reference internal" href="#running-your-test">1.4.1. Running your test</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#summary">1.5. Summary</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Part3-Overview.html"
|
||||
title="previous chapter">Part 3: How We Get There (Example Game)</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Rules.html"
|
||||
title="next chapter"><span class="section-number">2. </span>Rules and dice rolling</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Utilities.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="code-structure-and-utilities">
|
||||
<h1><span class="section-number">1. </span>Code structure and Utilities<a class="headerlink" href="#code-structure-and-utilities" title="Permalink to this headline">¶</a></h1>
|
||||
<p>In this lesson we will set up the file structure for <em>EvAdventure</em>. We will make some
|
||||
utilities that will be useful later. We will also learn how to write <em>tests</em>.</p>
|
||||
<section id="folder-structure">
|
||||
<h2><span class="section-number">1.1. </span>Folder structure<a class="headerlink" href="#folder-structure" title="Permalink to this headline">¶</a></h2>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">This layout is for the tutorial!</p>
|
||||
<p>We make the <code class="docutils literal notranslate"><span class="pre">evadventure</span></code> folder stand-alone for the sake of the tutorial only. Leaving the code isolated makes it clear what we changed - and for you to grab what you want later. It also makes it easier to refer to matching code in <code class="docutils literal notranslate"><span class="pre">evennia/contrib/tutorials/evadventure</span></code>.</p>
|
||||
<p>For your own game you are encouraged to modify your game dir in-place instead (such as add to <code class="docutils literal notranslate"><span class="pre">commands/commands.py</span></code> and to modify the <code class="docutils literal notranslate"><span class="pre">typeclasses/</span></code> modules directly). Except for the <code class="docutils literal notranslate"><span class="pre">server/</span></code> folder, you are infact free to structure your game dir code pretty much as you like.</p>
|
||||
</aside>
|
||||
<p>Create a new folder under your <code class="docutils literal notranslate"><span class="pre">mygame</span></code> folder, named <code class="docutils literal notranslate"><span class="pre">evadventure</span></code>. Inside it, create
|
||||
another folder <code class="docutils literal notranslate"><span class="pre">tests/</span></code> and make sure to put empty <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> files in both. This turns both
|
||||
folders into packages Python understands to import from.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">mygame</span><span class="o">/</span>
|
||||
<span class="n">commands</span><span class="o">/</span>
|
||||
<span class="n">evadventure</span><span class="o">/</span> <span class="o"><---</span>
|
||||
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span> <span class="o"><---</span>
|
||||
<span class="n">tests</span><span class="o">/</span> <span class="o"><---</span>
|
||||
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span> <span class="o"><---</span>
|
||||
<span class="fm">__init__</span><span class="o">.</span><span class="n">py</span>
|
||||
<span class="n">README</span><span class="o">.</span><span class="n">md</span>
|
||||
<span class="n">server</span><span class="o">/</span>
|
||||
<span class="n">typeclasses</span><span class="o">/</span>
|
||||
<span class="n">web</span><span class="o">/</span>
|
||||
<span class="n">world</span><span class="o">/</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Importing anything from inside this folder from anywhere else under <code class="docutils literal notranslate"><span class="pre">mygame</span></code> will be done by</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># from anywhere in mygame/</span>
|
||||
<span class="kn">from</span> <span class="nn">evadventure.yourmodulename</span> <span class="kn">import</span> <span class="n">whatever</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This is the ‘absolute path` type of import.</p>
|
||||
<p>Between two modules both in <code class="docutils literal notranslate"><span class="pre">evadventure/</span></code>, you can use a ‘relative’ import with <code class="docutils literal notranslate"><span class="pre">.</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># from a module inside mygame/evadventure</span>
|
||||
<span class="kn">from</span> <span class="nn">.yourmodulename</span> <span class="kn">import</span> <span class="n">whatever</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>From e.g. inside <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/tests/</span></code> you can import from one level above using <code class="docutils literal notranslate"><span class="pre">..</span></code>:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># from mygame/evadventure/tests/ </span>
|
||||
<span class="kn">from</span> <span class="nn">..yourmodulename</span> <span class="kn">import</span> <span class="n">whatever</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="enums">
|
||||
<h2><span class="section-number">1.2. </span>Enums<a class="headerlink" href="#enums" title="Permalink to this headline">¶</a></h2>
|
||||
<aside class="sidebar">
|
||||
<p>A full example of the enum module is found in
|
||||
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.enums.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/enums.py</span></a>.</p>
|
||||
</aside>
|
||||
<p>Create a new file <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/enums.py</span></code>.</p>
|
||||
<p>An <a class="reference external" href="https://docs.python.org/3/library/enum.html">enum</a> (enumeration) is a way to establish constants in Python. Best is to show an example:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in a file mygame/evadventure/enums.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">enum</span> <span class="kn">import</span> <span class="n">Enum</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Ability</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
|
||||
|
||||
<span class="n">STR</span> <span class="o">=</span> <span class="s2">"strength"</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You access an enum like this:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># from another module in mygame/evadventure</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">.enums</span> <span class="kn">import</span> <span class="n">Ability</span>
|
||||
|
||||
<span class="n">Ability</span><span class="o">.</span><span class="n">STR</span> <span class="c1"># the enum itself </span>
|
||||
<span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="o">.</span><span class="n">value</span> <span class="c1"># this is the string "strength"</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Having enums is recommended practice. With them set up, it means we can make sure to refer to the same thing every time. Having all enums in one place also means you have a good overview of the constants you are dealing with.</p>
|
||||
<p>The alternative would be to for example pass around a string <code class="docutils literal notranslate"><span class="pre">"constitution"</span></code>. If you mis-spell this (<code class="docutils literal notranslate"><span class="pre">"consitution"</span></code>), you would not necessarily know it right away - the error would happen later when the string is not recognized. If you make a typo getting <code class="docutils literal notranslate"><span class="pre">Ability.COM</span></code> instead of <code class="docutils literal notranslate"><span class="pre">Ability.CON</span></code>, Python will immediately raise an error since this enum is not recognized.</p>
|
||||
<p>With enums you can also do nice direct comparisons like <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">ability</span> <span class="pre">is</span> <span class="pre">Ability.WIS:</span> <span class="pre"><do</span> <span class="pre">stuff></span></code>.</p>
|
||||
<p>Note that the <code class="docutils literal notranslate"><span class="pre">Ability.STR</span></code> enum does not have the actual <em>value</em> of e.g. your Strength. It’s just a fixed label for the Strength ability.</p>
|
||||
<p>Here is the <code class="docutils literal notranslate"><span class="pre">enum.py</span></code> module needed for <em>Knave</em>. It covers the basic aspects of rule systems we need to track (check out the <em>Knave</em> rules. If you use another rule system you’ll likely gradually expand on your enums as you figure out what you’ll need).</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/enums.py</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Ability</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">"""</span>
|
||||
<span class="sd"> The six base ability-bonuses and other </span>
|
||||
<span class="sd"> abilities</span>
|
||||
|
||||
<span class="sd"> """</span>
|
||||
|
||||
<span class="n">STR</span> <span class="o">=</span> <span class="s2">"strength"</span>
|
||||
<span class="n">DEX</span> <span class="o">=</span> <span class="s2">"dexterity"</span>
|
||||
<span class="n">CON</span> <span class="o">=</span> <span class="s2">"constitution"</span>
|
||||
<span class="n">INT</span> <span class="o">=</span> <span class="s2">"intelligence"</span>
|
||||
<span class="n">WIS</span> <span class="o">=</span> <span class="s2">"wisdom"</span>
|
||||
<span class="n">CHA</span> <span class="o">=</span> <span class="s2">"charisma"</span>
|
||||
|
||||
<span class="n">ARMOR</span> <span class="o">=</span> <span class="s2">"armor"</span>
|
||||
|
||||
<span class="n">CRITICAL_FAILURE</span> <span class="o">=</span> <span class="s2">"critical_failure"</span>
|
||||
<span class="n">CRITICAL_SUCCESS</span> <span class="o">=</span> <span class="s2">"critical_success"</span>
|
||||
|
||||
<span class="n">ALLEGIANCE_HOSTILE</span> <span class="o">=</span> <span class="s2">"hostile"</span>
|
||||
<span class="n">ALLEGIANCE_NEUTRAL</span> <span class="o">=</span> <span class="s2">"neutral"</span>
|
||||
<span class="n">ALLEGIANCE_FRIENDLY</span> <span class="o">=</span> <span class="s2">"friendly"</span>
|
||||
|
||||
|
||||
<span class="n">ABILITY_REVERSE_MAP</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s2">"str"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">STR</span><span class="p">,</span>
|
||||
<span class="s2">"dex"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">DEX</span><span class="p">,</span>
|
||||
<span class="s2">"con"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CON</span><span class="p">,</span>
|
||||
<span class="s2">"int"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">INT</span><span class="p">,</span>
|
||||
<span class="s2">"wis"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">WIS</span><span class="p">,</span>
|
||||
<span class="s2">"cha"</span><span class="p">:</span> <span class="n">Ability</span><span class="o">.</span><span class="n">CHA</span>
|
||||
<span class="p">}</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here the <code class="docutils literal notranslate"><span class="pre">Ability</span></code> class holds basic properties of a character sheet.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">ABILITY_REVERSE_MAP</span></code> is a convenient map to go the other way - if you in some command were to enter the string ‘cha’, we could use this mapping to directly convert your input to the correct <code class="docutils literal notranslate"><span class="pre">Ability</span></code>:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>ability = ABILITY_REVERSE_MAP.get(your_input)
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="utility-module">
|
||||
<h2><span class="section-number">1.3. </span>Utility module<a class="headerlink" href="#utility-module" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>Create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/utils.py</span></code></p>
|
||||
</div></blockquote>
|
||||
<aside class="sidebar">
|
||||
<p>An example of the utility module is found in
|
||||
<a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.utils.html"><span class="doc std std-doc">evennia/contrib/tutorials/evadventure/utils.py</span></a></p>
|
||||
</aside>
|
||||
<p>This is for general functions we may need from all over. In this case we only picture one utility, a function that produces a pretty display of any object we pass to it.</p>
|
||||
<p>This is an example of the string we want to see:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Chipped</span> <span class="n">Sword</span>
|
||||
<span class="n">Value</span><span class="p">:</span> <span class="o">~</span><span class="mi">10</span> <span class="n">coins</span> <span class="p">[</span><span class="n">wielded</span> <span class="ow">in</span> <span class="n">Weapon</span> <span class="n">hand</span><span class="p">]</span>
|
||||
|
||||
<span class="n">A</span> <span class="n">simple</span> <span class="n">sword</span> <span class="n">used</span> <span class="n">by</span> <span class="n">mercenaries</span> <span class="nb">all</span> <span class="n">over</span>
|
||||
<span class="n">the</span> <span class="n">world</span><span class="o">.</span>
|
||||
|
||||
<span class="n">Slots</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">Used</span> <span class="n">from</span><span class="p">:</span> <span class="n">weapon</span> <span class="n">hand</span>
|
||||
<span class="n">Quality</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="n">Uses</span><span class="p">:</span> <span class="kc">None</span>
|
||||
<span class="n">Attacks</span> <span class="n">using</span> <span class="n">strength</span> <span class="n">against</span> <span class="n">armor</span><span class="o">.</span>
|
||||
<span class="n">Damage</span> <span class="n">roll</span><span class="p">:</span> <span class="mi">1</span><span class="n">d6</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here’s the start of how the function could look:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/evadventure/utils.py</span>
|
||||
|
||||
<span class="n">_OBJ_STATS</span> <span class="o">=</span> <span class="s2">"""</span>
|
||||
<span class="s2">|c</span><span class="si">{key}</span><span class="s2">|n</span>
|
||||
<span class="s2">Value: ~|y</span><span class="si">{value}</span><span class="s2">|n coins</span><span class="si">{carried}</span>
|
||||
|
||||
<span class="si">{desc}</span>
|
||||
|
||||
<span class="s2">Slots: |w</span><span class="si">{size}</span><span class="s2">|n, Used from: |w</span><span class="si">{use_slot_name}</span><span class="s2">|n</span>
|
||||
<span class="s2">Quality: |w</span><span class="si">{quality}</span><span class="s2">|n, Uses: |w</span><span class="si">{uses}</span><span class="s2">|n</span>
|
||||
<span class="s2">Attacks using |w</span><span class="si">{attack_type_name}</span><span class="s2">|n against |w</span><span class="si">{defense_type_name}</span><span class="s2">|n</span>
|
||||
<span class="s2">Damage roll: |w</span><span class="si">{damage_roll}</span><span class="s2">|n</span>
|
||||
<span class="s2">"""</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||||
|
||||
|
||||
<span class="k">def</span> <span class="nf">get_obj_stats</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">owner</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd"> Get a string of stats about the object.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> Args:</span>
|
||||
<span class="sd"> obj (Object): The object to get stats for.</span>
|
||||
<span class="sd"> owner (Object): The one currently owning/carrying `obj`, if any. Can be </span>
|
||||
<span class="sd"> used to show e.g. where they are wielding it.</span>
|
||||
<span class="sd"> Returns:</span>
|
||||
<span class="sd"> str: A nice info string to display about the object.</span>
|
||||
<span class="sd"> </span>
|
||||
<span class="sd"> """</span>
|
||||
<span class="k">return</span> <span class="n">_OBJ_STATS</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
|
||||
<span class="n">key</span><span class="o">=</span><span class="n">obj</span><span class="o">.</span><span class="n">key</span><span class="p">,</span>
|
||||
<span class="n">value</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span>
|
||||
<span class="n">carried</span><span class="o">=</span><span class="s2">"[Not carried]"</span><span class="p">,</span>
|
||||
<span class="n">desc</span><span class="o">=</span><span class="n">obj</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="n">size</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
|
||||
<span class="n">quality</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
|
||||
<span class="n">uses</span><span class="o">=</span><span class="s2">"infinite"</span><span class="p">,</span>
|
||||
<span class="n">use_slot_name</span><span class="o">=</span><span class="s2">"backpack"</span><span class="p">,</span>
|
||||
<span class="n">attack_type_name</span><span class="o">=</span><span class="s2">"strength"</span><span class="p">,</span>
|
||||
<span class="n">defense_type_name</span><span class="o">=</span><span class="s2">"armor"</span><span class="p">,</span>
|
||||
<span class="n">damage_roll</span><span class="o">=</span><span class="s2">"1d6"</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Here we set up the string template with place holders for where every piece of info should go. Study this string so you understand what it does. The <code class="docutils literal notranslate"><span class="pre">|c</span></code>, <code class="docutils literal notranslate"><span class="pre">|y</span></code>, <code class="docutils literal notranslate"><span class="pre">|w</span></code> and <code class="docutils literal notranslate"><span class="pre">|n</span></code> markers are <a class="reference internal" href="../../../Concepts/Colors.html"><span class="doc std std-doc">Evennia color markup</span></a> for making the text cyan, yellow, white and neutral-color respectively.</p>
|
||||
<p>We can guess some things, such that <code class="docutils literal notranslate"><span class="pre">obj.key</span></code> is the name of the object, and that <code class="docutils literal notranslate"><span class="pre">obj.db.desc</span></code> will hold its description (this is how it is in default Evennia).</p>
|
||||
<p>But so far we have not established how to get any of the other properties like <code class="docutils literal notranslate"><span class="pre">size</span></code> or <code class="docutils literal notranslate"><span class="pre">attack_type</span></code>. So we just set them to dummy values. We’ll need to get back to this when we have more code in place!</p>
|
||||
</section>
|
||||
<section id="testing">
|
||||
<h2><span class="section-number">1.4. </span>Testing<a class="headerlink" href="#testing" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><p>create a new module <code class="docutils literal notranslate"><span class="pre">mygame/evadventure/tests/test_utils.py</span></code></p>
|
||||
</div></blockquote>
|
||||
<p>How do you know if you made a typo in the code above? You could <em>manually</em> test it by reloading your Evennia server and do the following from in-game:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>py from evadventure.utils import get_obj_stats;print(get_obj_stats(self))
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You should get back a nice string about yourself! If that works, great! But you’ll need to remember doing that test when you change this code later.</p>
|
||||
<aside class="sidebar">
|
||||
<p>In <a class="reference internal" href="../../../api/evennia.contrib.tutorials.evadventure.tests.test_utils.html#evennia-contrib-tutorials-evadventure-tests-test-utils"><span class="std std-ref">evennia/contrib/tutorials/evadventure/tests/test_utils.py</span></a>
|
||||
is an example of the testing module. To dive deeper into unit testing in Evennia, see the <a class="reference internal" href="../../../Coding/Unit-Testing.html"><span class="doc std std-doc">Unit testing</span></a> documentation.</p>
|
||||
</aside>
|
||||
<p>A <em>unit test</em> allows you to set up automated testing of code. Once you’ve written your test you can run it over and over and make sure later changes to your code didn’t break things.</p>
|
||||
<p>In this particular case, we <em>expect</em> to later have to update the test when <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> becomes more complete and returns more reasonable data.</p>
|
||||
<p>Evennia comes with extensive functionality to help you test your code. Here’s a module for
|
||||
testing <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code>.</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># mygame/evadventure/tests/test_utils.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils</span> <span class="kn">import</span> <span class="n">create</span>
|
||||
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTest</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">..import</span> <span class="n">utils</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">TestUtils</span><span class="p">(</span><span class="n">EvenniaTest</span><span class="p">):</span>
|
||||
<span class="k">def</span> <span class="nf">test_get_obj_stats</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||
<span class="c1"># make a simple object to test with </span>
|
||||
<span class="n">obj</span> <span class="o">=</span> <span class="n">create</span><span class="o">.</span><span class="n">create_object</span><span class="p">(</span>
|
||||
<span class="n">key</span><span class="o">=</span><span class="s2">"testobj"</span><span class="p">,</span>
|
||||
<span class="n">attributes</span><span class="o">=</span><span class="p">((</span><span class="s2">"desc"</span><span class="p">,</span> <span class="s2">"A test object"</span><span class="p">),)</span>
|
||||
<span class="p">)</span>
|
||||
<span class="c1"># run it through the function </span>
|
||||
<span class="n">result</span> <span class="o">=</span> <span class="n">utils</span><span class="o">.</span><span class="n">get_obj_stats</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
|
||||
<span class="c1"># check that the result is what we expected</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span>
|
||||
<span class="n">result</span><span class="p">,</span>
|
||||
<span class="w"> </span><span class="sd">""" </span>
|
||||
<span class="sd">|ctestobj|n</span>
|
||||
<span class="sd">Value: ~|y10|n coins[not carried]</span>
|
||||
|
||||
<span class="sd">A test object</span>
|
||||
|
||||
<span class="sd">Slots: |w1|n, Used from: |wbackpack|n</span>
|
||||
<span class="sd">Quality: |w3|n, Uses: |winfinite|n</span>
|
||||
<span class="sd">Attacks using |wstrength|n against |warmor|n</span>
|
||||
<span class="sd">Damage roll: |w1d6|n</span>
|
||||
<span class="sd">"""</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>What happens here is that we create a new test-class <code class="docutils literal notranslate"><span class="pre">TestUtils</span></code> that inherits from <code class="docutils literal notranslate"><span class="pre">EvenniaTest</span></code>. This inheritance is what makes this a testing class.</p>
|
||||
<div class="admonition important">
|
||||
<p class="admonition-title">Important</p>
|
||||
<p>It’s useful for any game dev to know how to effectively test their code. So we’ll try to include a <em>Testing</em> section at the end of each of the implementation lessons to follow. Writing tests for your code is optional but highly recommended. It can feel a little cumbersome or time-consuming at first … but you’ll thank yourself later.</p>
|
||||
</div>
|
||||
<p>We can have any number of methods on this class. To have a method recognized as one containing code to test, its name <em>must</em> start with <code class="docutils literal notranslate"><span class="pre">test_</span></code>. We have one - <code class="docutils literal notranslate"><span class="pre">test_get_obj_stats</span></code>.</p>
|
||||
<p>In this method we create a dummy <code class="docutils literal notranslate"><span class="pre">obj</span></code> and gives it a <code class="docutils literal notranslate"><span class="pre">key</span></code> “testobj”. Note how we add the <code class="docutils literal notranslate"><span class="pre">desc</span></code> <a class="reference internal" href="../../../Components/Attributes.html"><span class="doc std std-doc">Attribute</span></a> directly in the <code class="docutils literal notranslate"><span class="pre">create_object</span></code> call by specifying the attribute as a tuple <code class="docutils literal notranslate"><span class="pre">(name,</span> <span class="pre">value)</span></code>!</p>
|
||||
<p>We then get the result of passing this dummy-object through <code class="docutils literal notranslate"><span class="pre">get_obj_stats</span></code> we imported earlier.</p>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">assertEqual</span></code> method is available on all testing classes and checks that the <code class="docutils literal notranslate"><span class="pre">result</span></code> is equal to the string we specify. If they are the same, the test <em>passes</em>, otherwise it <em>fails</em> and we need to investigate what went wrong.</p>
|
||||
<section id="running-your-test">
|
||||
<h3><span class="section-number">1.4.1. </span>Running your test<a class="headerlink" href="#running-your-test" title="Permalink to this headline">¶</a></h3>
|
||||
<p>To run your test you need to stand inside your <code class="docutils literal notranslate"><span class="pre">mygame</span></code> folder and execute the following command:</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings settings.py evadventure.tests
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This will run all your <code class="docutils literal notranslate"><span class="pre">evadventure</span></code> tests (if you had more of them). To only run your utility tests you could do</p>
|
||||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings settings.py evadventure.tests.test_utils
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If all goes well, you should get an <code class="docutils literal notranslate"><span class="pre">OK</span></code> back. Otherwise you need to check the failure, maybe your return string doesn’t quite match what you expected.</p>
|
||||
<blockquote>
|
||||
<div><p>Hint: The example unit test code above contains a deliberate error in capitalization. See if you can interpret the error and fix it!</p>
|
||||
</div></blockquote>
|
||||
</section>
|
||||
</section>
|
||||
<section id="summary">
|
||||
<h2><span class="section-number">1.5. </span>Summary<a class="headerlink" href="#summary" title="Permalink to this headline">¶</a></h2>
|
||||
<p>It’s very important to understand how you import code between modules in Python, so if this is still confusing to you, it’s worth to read up on this more.</p>
|
||||
<p>That said, many newcomers are confused with how to begin, so by creating the folder structure, some small modules and even making your first unit test, you are off to a great start!</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="Beginner-Tutorial-Rules.html" title="2. Rules and dice rolling"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Part3-Overview.html" title="Part 3: How We Get There (Example Game)"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part3-Overview.html" >Part 3: How We Get There (Example Game)</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">1. </span>Code structure and Utilities</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
|
||||
<!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>Part 4: Using What We Created — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="1. Unimplemented" href="../../../Unimplemented.html" />
|
||||
<link rel="prev" title="16. In-game Commands" href="../Part3/Beginner-Tutorial-Commands.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../../../Unimplemented.html" title="1. Unimplemented"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Part3/Beginner-Tutorial-Commands.html" title="16. In-game Commands"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" accesskey="U">Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Part 4: Using What We Created</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Part 4: Using What We Created</a><ul>
|
||||
<li><a class="reference internal" href="#lessons">Lessons</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="../Part3/Beginner-Tutorial-Commands.html"
|
||||
title="previous chapter"><span class="section-number">16. </span>In-game Commands</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="../../../Unimplemented.html"
|
||||
title="next chapter"><span class="section-number">1. </span>Unimplemented</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part4/Beginner-Tutorial-Part4-Overview.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="part-4-using-what-we-created">
|
||||
<h1>Part 4: Using What We Created<a class="headerlink" href="#part-4-using-what-we-created" title="Permalink to this headline">¶</a></h1>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Beginner Tutorial Parts</p>
|
||||
<ul class="simple">
|
||||
<li><p><a class="reference internal" href="../Beginner-Tutorial-Overview.html"><span class="doc std std-doc">Introduction</span></a>
|
||||
<br>Getting set up.</p></li>
|
||||
<li><p>Part 1: <a class="reference internal" href="../Part1/Beginner-Tutorial-Part1-Overview.html"><span class="doc std std-doc">What We Have</span></a>
|
||||
<br>A tour of Evennia and how to use the tools, including an introduction to Python.</p></li>
|
||||
<li><p>Part 2: <a class="reference internal" href="../Part2/Beginner-Tutorial-Part2-Overview.html"><span class="doc std std-doc">What We Want</span></a>
|
||||
<br>Planning our tutorial game and what to consider when planning your own.</p></li>
|
||||
<li><p>Part 3: <a class="reference internal" href="../Part3/Beginner-Tutorial-Part3-Overview.html"><span class="doc std std-doc">How We Get There</span></a>
|
||||
<br>Getting down to the meat of extending Evennia to make your game.</p></li>
|
||||
<li><p><em>Part 4: <a class="reference internal" href="#"><span class="doc std std-doc">Using What We Created</span></a></em>
|
||||
<br>Building a tech-demo and world content to go with our code.</p></li>
|
||||
<li><p>Part 5: <a class="reference internal" href="../Part5/Beginner-Tutorial-Part5-Overview.html"><span class="doc std std-doc">Showing the World</span></a>
|
||||
<br>Taking our new game online and letting players try it out.</p></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<p>We now have the code underpinnings of everything we need. We have also tested the various components and has a simple tech-demo to show it all works together. But there is no real coherence to it at this point - we need to actually make a world. In part four we will expand our tech demo into a more full-fledged (if small) game by use of batchcommand and batchcode processors.</p>
|
||||
<section id="lessons">
|
||||
<h2>Lessons<a class="headerlink" href="#lessons" title="Permalink to this headline">¶</a></h2>
|
||||
<p><em>TODO</em></p>
|
||||
<div class="toctree-wrapper compound">
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../../../Unimplemented.html">1. Unimplemented</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</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="../../../Unimplemented.html" title="1. Unimplemented"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../Part3/Beginner-Tutorial-Commands.html" title="16. In-game Commands"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Part 4: Using What We Created</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
|
||||
<!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>1. Add a simple new web page — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="Adding a Command Prompt" href="../../Howto-Command-Prompt.html" />
|
||||
<link rel="prev" title="Part 5: Showing the World" href="Beginner-Tutorial-Part5-Overview.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../../Howto-Command-Prompt.html" title="Adding a Command Prompt"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Part5-Overview.html" title="Part 5: Showing the World"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part5-Overview.html" accesskey="U">Part 5: Showing the World</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">1. </span>Add a simple new web page</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">1. Add a simple new web page</a><ul>
|
||||
<li><a class="reference internal" href="#create-the-view">1.1. Create the view</a></li>
|
||||
<li><a class="reference internal" href="#the-html-page">1.2. The HTML page</a></li>
|
||||
<li><a class="reference internal" href="#the-url">1.3. The URL</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="Beginner-Tutorial-Part5-Overview.html"
|
||||
title="previous chapter">Part 5: Showing the World</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="../../Howto-Command-Prompt.html"
|
||||
title="next chapter">Adding a Command Prompt</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part5/Add-a-simple-new-web-page.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="add-a-simple-new-web-page">
|
||||
<h1><span class="section-number">1. </span>Add a simple new web page<a class="headerlink" href="#add-a-simple-new-web-page" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Evennia leverages <a class="reference external" href="https://docs.djangoproject.com">Django</a> which is a web development framework.
|
||||
Huge professional websites are made in Django and there is extensive documentation (and books) on it.
|
||||
You are encouraged to at least look at the Django basic tutorials. Here we will just give a brief
|
||||
introduction for how things hang together, to get you started.</p>
|
||||
<p>We assume you have installed and set up Evennia to run. A webserver and website comes along with the
|
||||
default Evennia install out of the box. You can view the default website by pointing your web browser
|
||||
to <code class="docutils literal notranslate"><span class="pre">http://localhost:4001</span></code>. You will see a generic welcome page with some game statistics and a link
|
||||
to the Evennia web client.</p>
|
||||
<p>In this tutorial, we will add a new page that you can visit at <code class="docutils literal notranslate"><span class="pre">http://localhost:4001/story</span></code>.</p>
|
||||
<section id="create-the-view">
|
||||
<h2><span class="section-number">1.1. </span>Create the view<a class="headerlink" href="#create-the-view" title="Permalink to this headline">¶</a></h2>
|
||||
<p>A django “view” is a normal Python function that django calls to render the HTML page you will see
|
||||
in the web browser. Django can do all sorts of cool stuff to a page by using the view function — like
|
||||
adding dynamic content or making changes to a page on the fly — but, here, we will just have it spit
|
||||
back raw HTML.</p>
|
||||
<p>Open <code class="docutils literal notranslate"><span class="pre">mygame/web/website</span></code> folder and create a new module file there named <code class="docutils literal notranslate"><span class="pre">story.py</span></code>. (You could also
|
||||
put it in its own folder if you wanted to be neat but, if you do, don’t forget to add an empty
|
||||
<code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> file in the new folfder. Adding the <code class="docutils literal notranslate"><span class="pre">__init__.py</span></code> file tells Python that modules can be
|
||||
imported from the new folder. For this tutorial, here’s what the example contents of your new <code class="docutils literal notranslate"><span class="pre">story.py</span></code>
|
||||
module should look like:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># in mygame/web/website/story.py</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">django.shortcuts</span> <span class="kn">import</span> <span class="n">render</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">storypage</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s2">"story.html"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The above view takes advantage of a shortcut provided for use by Django: <em>render</em>. The render shortcut
|
||||
gives the template information from the request. For instance, it might provide the game name, and then
|
||||
renders it.</p>
|
||||
</section>
|
||||
<section id="the-html-page">
|
||||
<h2><span class="section-number">1.2. </span>The HTML page<a class="headerlink" href="#the-html-page" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Next, we need to find the location where Evennia (and Django) looks for HTML files, which are referred
|
||||
to as <em>templates</em> in Django’s parlance. You can specify such locations in your settings (see the
|
||||
<code class="docutils literal notranslate"><span class="pre">TEMPLATES</span></code> variable in <code class="docutils literal notranslate"><span class="pre">default_settings.py</span></code> for more info) but, here we’ll use an existing one.</p>
|
||||
<p>Navigate to <code class="docutils literal notranslate"><span class="pre">mygame/web/templates/website/</span></code> and create a new file there called <code class="docutils literal notranslate"><span class="pre">story.html</span></code>. This
|
||||
is not an HTML tutorial, so this file’s content will be simple:</p>
|
||||
<div class="highlight-html notranslate"><div class="highlight"><pre><span></span>{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"row"</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"col"</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">h1</span><span class="p">></span>A story about a tree<span class="p"></</span><span class="nt">h1</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">p</span><span class="p">></span>
|
||||
This is a story about a tree, a classic tale ...
|
||||
<span class="p"></</span><span class="nt">p</span><span class="p">></span>
|
||||
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
|
||||
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
|
||||
{% endblock %}
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>As shown above, Django will allow us to extend our base styles easily because we’ve used the
|
||||
<em>render</em> shortcut. If you’d prefer to not take advantage of Evennia’s base styles, you might
|
||||
instead do something like this:</p>
|
||||
<div class="highlight-html notranslate"><div class="highlight"><pre><span></span><span class="p"><</span><span class="nt">html</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">h1</span><span class="p">></span>A story about a tree<span class="p"></</span><span class="nt">h1</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">p</span><span class="p">></span>
|
||||
This is a story about a tree, a classic tale ...
|
||||
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
|
||||
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="the-url">
|
||||
<h2><span class="section-number">1.3. </span>The URL<a class="headerlink" href="#the-url" title="Permalink to this headline">¶</a></h2>
|
||||
<p>When you enter the address <code class="docutils literal notranslate"><span class="pre">http://localhost:4001/story</span></code> in your web browser, Django will parse the
|
||||
stub following the port — here, <code class="docutils literal notranslate"><span class="pre">/story</span></code> — to find out to which page you would like displayed. How
|
||||
does Django know what HTML file <code class="docutils literal notranslate"><span class="pre">/story</span></code> should link to? You inform Django about what address stub
|
||||
patterns correspond to what files in the file <code class="docutils literal notranslate"><span class="pre">mygame/web/website/urls.py</span></code>. Open it in your editor now.</p>
|
||||
<p>Django looks for the variable <code class="docutils literal notranslate"><span class="pre">urlpatterns</span></code> in this file. You will want to add your new <code class="docutils literal notranslate"><span class="pre">story</span></code> pattern
|
||||
and corresponding path to <code class="docutils literal notranslate"><span class="pre">urlpatterns</span></code> list — which is then, in turn, merged with the default
|
||||
<code class="docutils literal notranslate"><span class="pre">urlpatterns</span></code>. Here’s how it could look:</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="sd">"""</span>
|
||||
<span class="sd">This reroutes from an URL to a python view-function/class.</span>
|
||||
<span class="sd">The main web/urls.py includes these routes for all urls (the root of the url)</span>
|
||||
<span class="sd">so it can reroute to all website pages.</span>
|
||||
<span class="sd">"""</span>
|
||||
<span class="kn">from</span> <span class="nn">django.urls</span> <span class="kn">import</span> <span class="n">path</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">web.website</span> <span class="kn">import</span> <span class="n">story</span>
|
||||
|
||||
<span class="kn">from</span> <span class="nn">evennia.web.website.urls</span> <span class="kn">import</span> <span class="n">urlpatterns</span> <span class="k">as</span> <span class="n">evennia_website_urlpatterns</span>
|
||||
|
||||
<span class="c1"># add patterns here</span>
|
||||
<span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
|
||||
<span class="c1"># path("url-pattern", imported_python_view),</span>
|
||||
<span class="n">path</span><span class="p">(</span><span class="sa">r</span><span class="s2">"story"</span><span class="p">,</span> <span class="n">story</span><span class="o">.</span><span class="n">storypage</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s2">"Story"</span><span class="p">),</span>
|
||||
<span class="p">]</span>
|
||||
|
||||
<span class="c1"># read by Django</span>
|
||||
<span class="n">urlpatterns</span> <span class="o">=</span> <span class="n">urlpatterns</span> <span class="o">+</span> <span class="n">evennia_website_urlpatterns</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The above code imports our <code class="docutils literal notranslate"><span class="pre">story.py</span></code> Python view module from where we created it earlier — in
|
||||
<code class="docutils literal notranslate"><span class="pre">mygame/web/website/</span></code> — and then add the corresponding <code class="docutils literal notranslate"><span class="pre">path</span></code> instance. The first argument to
|
||||
<code class="docutils literal notranslate"><span class="pre">path</span></code> is the pattern of the URL that we want to find (<code class="docutils literal notranslate"><span class="pre">"story"</span></code>) as a regular expression, and
|
||||
then the view function from <code class="docutils literal notranslate"><span class="pre">story.py</span></code> that we want to call.</p>
|
||||
<p>That should be it. Reload Evennia — <code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">reload</span></code> — and you should now be able to navigate
|
||||
your browser to the <code class="docutils literal notranslate"><span class="pre">http://localhost:4001/story</span></code> location and view your new story page as
|
||||
rendered by Python!</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="../../Howto-Command-Prompt.html" title="Adding a Command Prompt"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Beginner-Tutorial-Part5-Overview.html" title="Part 5: Showing the World"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part5-Overview.html" >Part 5: Showing the World</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href=""><span class="section-number">1. </span>Add a simple new web page</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
|
||||
<!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>Part 5: Showing the World — Evennia 2.x documentation</title>
|
||||
<link rel="stylesheet" href="../../../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
|
||||
<script src="../../../_static/jquery.js"></script>
|
||||
<script src="../../../_static/underscore.js"></script>
|
||||
<script src="../../../_static/doctools.js"></script>
|
||||
<script src="../../../_static/language_data.js"></script>
|
||||
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
|
||||
<link rel="index" title="Index" href="../../../genindex.html" />
|
||||
<link rel="search" title="Search" href="../../../search.html" />
|
||||
<link rel="next" title="1. Add a simple new web page" href="Add-a-simple-new-web-page.html" />
|
||||
<link rel="prev" title="1. Unimplemented" href="../../../Unimplemented.html" />
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../../../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../../../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="Add-a-simple-new-web-page.html" title="1. Add a simple new web page"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../../../Unimplemented.html" title="1. Unimplemented"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" accesskey="U">Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Part 5: Showing the World</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
|
||||
<div class="documentwrapper">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<p class="logo"><a href="../../../index.html">
|
||||
<img class="logo" src="../../../_static/evennia_logo.png" alt="Logo"/>
|
||||
</a></p>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="../../../search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
<h3><a href="../../../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Part 5: Showing the World</a><ul>
|
||||
<li><a class="reference internal" href="#lessons">Lessons</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="../../../Unimplemented.html"
|
||||
title="previous chapter"><span class="section-number">1. </span>Unimplemented</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="Add-a-simple-new-web-page.html"
|
||||
title="next chapter"><span class="section-number">1. </span>Add a simple new web page</a></p>
|
||||
<div role="note" aria-label="source link">
|
||||
<!--h3>This Page</h3-->
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part5/Beginner-Tutorial-Part5-Overview.md.txt"
|
||||
rel="nofollow">Show Page Source</a></li>
|
||||
</ul>
|
||||
</div><h3>Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
|
||||
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
|
||||
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
|
||||
<li><a href="http://games.evennia.com">Game Index</a> </li>
|
||||
<li>
|
||||
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
|
||||
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
|
||||
<a href="https://evennia.blogspot.com/">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<section class="tex2jax_ignore mathjax_ignore" id="part-5-showing-the-world">
|
||||
<h1>Part 5: Showing the World<a class="headerlink" href="#part-5-showing-the-world" title="Permalink to this headline">¶</a></h1>
|
||||
<aside class="sidebar">
|
||||
<p class="sidebar-title">Beginner Tutorial Parts</p>
|
||||
<ul class="simple">
|
||||
<li><p><a class="reference internal" href="../Beginner-Tutorial-Overview.html"><span class="doc std std-doc">Introduction</span></a>
|
||||
<br>Getting set up.</p></li>
|
||||
<li><p>Part 1: <a class="reference internal" href="../Part1/Beginner-Tutorial-Part1-Overview.html"><span class="doc std std-doc">What We Have</span></a>
|
||||
<br>A tour of Evennia and how to use the tools, including an introduction to Python.</p></li>
|
||||
<li><p>Part 2: <a class="reference internal" href="../Part2/Beginner-Tutorial-Part2-Overview.html"><span class="doc std std-doc">What We Want</span></a>
|
||||
<br>Planning our tutorial game and what to consider when planning your own.</p></li>
|
||||
<li><p>Part 3: <a class="reference internal" href="../Part3/Beginner-Tutorial-Part3-Overview.html"><span class="doc std std-doc">How We Get There</span></a>
|
||||
<br>Getting down to the meat of extending Evennia to make your game.</p></li>
|
||||
<li><p>Part 4: <a class="reference internal" href="../Part4/Beginner-Tutorial-Part4-Overview.html"><span class="doc std std-doc">Using What We Created</span></a>
|
||||
<br>Building a tech-demo and world content to go with our code.</p></li>
|
||||
<li><p><em>Part 5: <a class="reference internal" href="#"><span class="doc std std-doc">Showing the World</span></a></em>
|
||||
<br>Taking our new game online and letting players try it out.</p></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<p>You have a working game! In part five we will look at the web-components of Evennia and how to modify them
|
||||
to fit your game. We will also look at hosting your game and if you feel up to it we’ll also go through how
|
||||
to bring your game online so you can invite your first players.</p>
|
||||
<section id="lessons">
|
||||
<h2>Lessons<a class="headerlink" href="#lessons" title="Permalink to this headline">¶</a></h2>
|
||||
<p><em>TODO</em></p>
|
||||
<div class="toctree-wrapper compound">
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Add-a-simple-new-web-page.html">1. Add a simple new web page</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Add-a-simple-new-web-page.html#create-the-view">1.1. Create the view</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Add-a-simple-new-web-page.html#the-html-page">1.2. The HTML page</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Add-a-simple-new-web-page.html#the-url">1.3. The URL</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</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="Add-a-simple-new-web-page.html" title="1. Add a simple new web page"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../../../Unimplemented.html" title="1. Unimplemented"
|
||||
>previous</a> |</li>
|
||||
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 2.x</a> »</li>
|
||||
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and How-To’s</a> »</li>
|
||||
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Overview.html" >Beginner Tutorial</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Part 5: Showing the World</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="admonition important">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2023, The Evennia developer community.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue