<pclass="last">You are reading an old version of the Evennia documentation. <ahref="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
<h1>A voice operated elevator using events<aclass="headerlink"href="#a-voice-operated-elevator-using-events"title="Permalink to this headline">¶</a></h1>
<p>This tutorial will walk you through the steps to create a voice-operated elevator, using the <aclass="reference internal"href="Contrib-Ingame-Python.html"><spanclass="doc std std-doc">in-
game Python system</span></a>. This tutorial assumes the in-game Python
system is installed per the instructions in that doc. <strong>You do not need to read</strong> the entire
documentation, it’s a good reference, but not the easiest way to learn about it. Hence these
tutorials.</p>
<p>The in-game Python system allows to run code on individual objects in some situations. You don’t
have to modify the source code to add these features, past the installation. The entire system
makes it easy to add specific features to some objects, but not all.</p>
<blockquote>
<div><p>What will we try to do?</p>
</div></blockquote>
<p>In this tutorial, we are going to create a simple voice-operated elevator. In terms of features, we
will:</p>
<ulclass="simple">
<li><p>Explore events with parameters.</p></li>
<li><p>Work on more interesting callbacks.</p></li>
<li><p>Learn about chained events.</p></li>
<li><p>Play with variable modification in callbacks.</p></li>
</ul>
<sectionid="our-study-case">
<h2>Our study case<aclass="headerlink"href="#our-study-case"title="Permalink to this headline">¶</a></h2>
<p>Let’s summarize what we want to achieve first. We would like to create a room that will represent
the inside of our elevator. In this room, a character could just say “1”, “2” or “3”, and the
elevator will start moving. The doors will close and open on the new floor (the exits leading in
and out of the elevator will be modified).</p>
<p>We will work on basic features first, and then will adjust some, showing you how easy and powerfully
independent actions can be configured through the in-game Python system.</p>
<h2>Creating the rooms and exits we need<aclass="headerlink"href="#creating-the-rooms-and-exits-we-need"title="Permalink to this headline">¶</a></h2>
<p>We’ll create an elevator right in our room (generally called “Limbo”, of ID 2). You could easily
adapt the following instructions if you already have some rooms and exits, of course, just remember
to check the IDs.</p>
<blockquote>
<div><p>Note: the in-game Python system uses IDs for a lot of things. While it is not mandatory, it is
good practice to know the IDs you have for your callbacks, because it will make manipulation much
quicker. There are other ways to identify objects, but as they depend on many factors, IDs are
usually the safest path in our callbacks.</p>
</div></blockquote>
<p>Let’s go into limbo (<codeclass="docutils literal notranslate"><spanclass="pre">#2</span></code>) to add our elevator. We’ll add it to the north. To create this room,
in-game you could type:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>tunnel n = Inside of an elevator
</pre></div>
</div>
<p>The game should respond by telling you:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>Created room Inside of an elevator(#3) of type typeclasses.rooms.Room.
Created Exit from Limbo to Inside of an elevator: north(#4) (n).
Created Exit back from Inside of an elevator to Limbo: south(#5) (s).
</pre></div>
</div>
<p>Note the given IDs:</p>
<ulclass="simple">
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">#2</span></code> is limbo, the first room the system created.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">#3</span></code> is our room inside of an elevator.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">#4</span></code> is the north exit from Limbo to our elevator.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">#5</span></code> is the south exit from an elevator to Limbo.</p></li>
</ul>
<p>Keep these IDs somewhere for the demonstration. You will shortly see why they are important.</p>
<blockquote>
<div><p>Why have we created exits to our elevator and back to Limbo? Isn’t the elevator supposed to move?</p>
</div></blockquote>
<p>It is. But we need to have exits that will represent the way inside the elevator and out. What we
will do, at every floor, will be to change these exits so they become connected to the right room.
You’ll see this process a bit later.</p>
<p>We have two more rooms to create: our floor 2 and 3. This time, we’ll use <codeclass="docutils literal notranslate"><spanclass="pre">dig</span></code>, because we don’t
need exits leading there, not yet anyway.</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>dig The second floor
dig The third floor
</pre></div>
</div>
<p>Evennia should answer with:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>Created room The second floor(#6) of type typeclasses.rooms.Room.
Created room The third floor(#7) of type typeclasses.rooms.Room.
</pre></div>
</div>
<p>Add these IDs to your list, we will use them too.</p>
</section>
<sectionid="our-first-callback-in-the-elevator">
<h2>Our first callback in the elevator<aclass="headerlink"href="#our-first-callback-in-the-elevator"title="Permalink to this headline">¶</a></h2>
<p>Let’s go to the elevator (you could use <codeclass="docutils literal notranslate"><spanclass="pre">tel</span><spanclass="pre">#3</span></code> if you have the same IDs I have).</p>
<p>This is our elevator room. It looks a bit empty, feel free to add a prettier description or other
things to decorate it a bit.</p>
<p>But what we want now is to be able to say “1”, “2” or “3” and have the elevator move in that
direction.</p>
<p>If you have read
<aclass="reference internal"href="Contrib-Ingame-Python-Tutorial-Dialogue.html"><spanclass="doc std std-doc">the other in-game Python tutorial about adding dialogues in events</span></a>, you
may remember what we need to do. If not, here’s a summary: we need to run some code when somebody
speaks in the room. So we need to create a callback (the callback will contain our lines of code).
We just need to know on which event this should be set. You can enter <codeclass="docutils literal notranslate"><spanclass="pre">call</span><spanclass="pre">here</span></code> to see the
possible events in this room.</p>
<p>In the table, you should see the “say” event, which is called when somebody says something in the
room. So we’ll need to add a callback to this event. Don’t worry if you’re a bit lost, just follow
the following steps, the way they connect together will become more obvious.</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>call/add here = say 1, 2, 3
</pre></div>
</div>
<olclass="simple">
<li><p>We need to add a callback. A callback contains the code that will be executed at a given time.
So we use the <codeclass="docutils literal notranslate"><spanclass="pre">call/add</span></code> command and switch.</p></li>
<li><p><codeclass="docutils literal notranslate"><spanclass="pre">here</span></code> is our object, the room in which we are.</p></li>
<li><p>An equal sign.</p></li>
<li><p>The name of the event to which the callback should be connected. Here, the event is “say”.
Meaning this callback will be executed every time somebody says something in the room.</p></li>
<li><p>But we add an event parameter to indicate the keywords said in the room that should execute our
callback. Otherwise, our callback would be called every time somebody speaks, no matter what. Here
we limit, indicating our callback should be executed only if the spoken message contains “1”, “2” or
“3”.</p></li>
</ol>
<p>An editor should open, inviting you to enter the Python code that should be executed. The first
thing to remember is to read the text provided (it can contain important information) and, most of
all, the list of variables that are available in this callback:</p>
<p>This is important, in order to know what variables we can use in our callback out-of-the-box. Let’s
write a single line to be sure our callback is called when we expect it to:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="n">character</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"You just said </span><spanclass="si">{</span><spanclass="n">message</span><spanclass="si">}</span><spanclass="s2">."</span><spanclass="p">)</span>
</pre></div>
</div>
<p>You can paste this line in-game, then type the <codeclass="docutils literal notranslate"><spanclass="pre">:wq</span></code> command to exit the editor and save your
modifications.</p>
<p>Let’s check. Try to say “hello” in the room. You should see the standard message, but nothing
more. Now try to say “1”. Below the standard message, you should see:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>You just said 1.
</pre></div>
</div>
<p>You can try it. Our callback is only called when we say “1”, “2” or “3”. Which is just what we
want.</p>
<p>Let’s go back in our code editor and add something more useful.</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>call/edit here = say
</pre></div>
</div>
<blockquote>
<div><p>Notice that we used the “edit” switch this time, since the callback exists, we just want to edit
it.</p>
</div></blockquote>
<p>The editor opens again. Let’s empty it first:</p>
<spanclass="n">character</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"Which floor do you want?"</span><spanclass="p">)</span>
<spanclass="n">character</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"The elevator already is at this floor."</span><spanclass="p">)</span>
<spanclass="k">else</span><spanclass="p">:</span>
<spanclass="c1"># 'floor' contains the new room where the elevator should be</span>
<spanclass="n">room</span><spanclass="o">.</span><spanclass="n">msg_contents</span><spanclass="p">(</span><spanclass="s2">"The doors of the elevator close with a clank."</span><spanclass="p">)</span>
<spanclass="n">room</span><spanclass="o">.</span><spanclass="n">msg_contents</span><spanclass="p">(</span><spanclass="s2">"The doors of the elevator open to </span><spanclass="si">{floor}</span><spanclass="s2">."</span><spanclass="p">,</span>
<li><p>We first obtain the objects of both exits and our three floors. We use the <codeclass="docutils literal notranslate"><spanclass="pre">get()</span></code> eventfunc,
which is a shortcut to obtaining objects. We usually use it to retrieve specific objects with an
ID. We put the floors in a dictionary. The keys of the dictionary are the floor number (as str),
the values are room objects.</p></li>
<li><p>Remember, the <codeclass="docutils literal notranslate"><spanclass="pre">message</span></code> variable contains the message spoken in the room. So either “1”, “2”, or
“3”. We still need to check it, however, because if the character says something like “1 2” in the
room, our callback will be executed. Let’s be sure what she says is a floor number.</p></li>
<li><p>We then check if the elevator is already at this floor. Notice that we use <codeclass="docutils literal notranslate"><spanclass="pre">TO_EXIT.location</span></code>.
<codeclass="docutils literal notranslate"><spanclass="pre">TO_EXIT</span></code> contains our “north” exit, leading inside of our elevator. Therefore, its <codeclass="docutils literal notranslate"><spanclass="pre">location</span></code> will
be the room where the elevator currently is.</p></li>
<li><p>If the floor is a different one, have the elevator “move”, changing just the location and
destination of both exits.</p>
<ulclass="simple">
<li><p>The <codeclass="docutils literal notranslate"><spanclass="pre">BACK_EXIT</span></code> (that is “north”) should change its location. The elevator shouldn’t be
accessible through our old floor.</p></li>
<li><p>The <codeclass="docutils literal notranslate"><spanclass="pre">TO_EXIT</span></code> (that is “south”, the exit leading out of the elevator) should have a different
destination. When we go out of the elevator, we should find ourselves in the new floor, not the old
one.</p></li>
</ul>
</li>
</ol>
<p>Feel free to expand on this example, changing messages, making further checks. Usage and practice
are keys.</p>
<p>You can quit the editor as usual with <codeclass="docutils literal notranslate"><spanclass="pre">:wq</span></code> and test it out.</p>
</section>
<sectionid="adding-a-pause-in-our-callback">
<h2>Adding a pause in our callback<aclass="headerlink"href="#adding-a-pause-in-our-callback"title="Permalink to this headline">¶</a></h2>
<p>Let’s improve our callback. One thing that’s worth adding would be a pause: for the time being,
when we say the floor number in the elevator, the doors close and open right away. It would be
better to have a pause of several seconds. More logical.</p>
<p>This is a great opportunity to learn about chained events. Chained events are very useful to create
pauses. Contrary to the events we have seen so far, chained events aren’t called automatically.
They must be called by you, and can be called after some time.</p>
<ulclass="simple">
<li><p>Chained events always have the name <codeclass="docutils literal notranslate"><spanclass="pre">"chain_X"</span></code>. Usually, X is a number, but you can give the
chained event a more explicit name.</p></li>
<li><p>In our original callback, we will call our chained events in, say, 15 seconds.</p></li>
<li><p>We’ll also have to make sure the elevator isn’t already moving.</p></li>
</ul>
<p>Other than that, a chained event can be connected to a callback as usual. We’ll create a chained
event in our elevator, that will only contain the code necessary to open the doors to the new floor.</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>call/add here = chain_1
</pre></div>
</div>
<p>The callback is added to the <codeclass="docutils literal notranslate"><spanclass="pre">"chain_1"</span></code> event, an event that will not be automatically called by the
system when something happens. Inside this event, you can paste the code to open the doors at the
<spanclass="n">room</span><spanclass="o">.</span><spanclass="n">msg_contents</span><spanclass="p">(</span><spanclass="s2">"The doors of the elevator open to </span><spanclass="si">{floor}</span><spanclass="s2">."</span><spanclass="p">,</span>
<p>Paste this code into the editor, then use <codeclass="docutils literal notranslate"><spanclass="pre">:wq</span></code> to save and quit the editor.</p>
<p>Now let’s edit our callback in the “say” event. We’ll have to change it a bit:</p>
<ulclass="simple">
<li><p>The callback will have to check the elevator isn’t already moving.</p></li>
<li><p>It must change the exits when the elevator move.</p></li>
<li><p>It has to call the <codeclass="docutils literal notranslate"><spanclass="pre">"chain_1"</span></code> event we have defined. It should call it 15 seconds later.</p></li>
</ul>
<p>Let’s see the code in our callback.</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>call/edit here = say
</pre></div>
</div>
<p>Remove the current code and disable auto-indentation again:</p>
<spanclass="n">character</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"Which floor do you want?"</span><spanclass="p">)</span>
<spanclass="n">character</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"The elevator is between floors."</span><spanclass="p">)</span>
<spanclass="n">character</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"The elevator already is at this floor."</span><spanclass="p">)</span>
<spanclass="k">else</span><spanclass="p">:</span>
<spanclass="c1"># 'floor' contains the new room where the elevator should be</span>
<spanclass="n">room</span><spanclass="o">.</span><spanclass="n">msg_contents</span><spanclass="p">(</span><spanclass="s2">"The doors of the elevator close with a clank."</span><spanclass="p">)</span>
<li><p>We added a little test to make sure the elevator wasn’t already moving. If it is, the
<codeclass="docutils literal notranslate"><spanclass="pre">BACK_EXIT.location</span></code> (the “south” exit leading out of the elevator) should be <codeclass="docutils literal notranslate"><spanclass="pre">None</span></code>. We’ll remove
the exit while the elevator is moving.</p></li>
<li><p>When the doors close, we set both exits’<codeclass="docutils literal notranslate"><spanclass="pre">location</span></code> to <codeclass="docutils literal notranslate"><spanclass="pre">None</span></code>. Which “removes” them from their
room but doesn’t destroy them. The exits still exist but they don’t connect anything. If you say
“2” in the elevator and look around while the elevator is moving, you won’t see any exits.</p></li>
<li><p>Instead of opening the doors immediately, we call <codeclass="docutils literal notranslate"><spanclass="pre">call_event</span></code>. We give it the object containing
the event to be called (here, our elevator), the name of the event to be called (here, “chain_1”)
and the number of seconds from now when the event should be called (here, <codeclass="docutils literal notranslate"><spanclass="pre">15</span></code>).</p></li>
<li><p>The <codeclass="docutils literal notranslate"><spanclass="pre">chain_1</span></code> callback we have created contains the code to “re-open” the elevator doors. That
is, besides displaying a message, it reset the exits’<codeclass="docutils literal notranslate"><spanclass="pre">location</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">destination</span></code>.</p></li>
</ol>
<p>If you try to say “3” in the elevator, you should see the doors closing. Look around you and you
won’t see any exit. Then, 15 seconds later, the doors should open, and you can leave the elevator
to go to the third floor. While the elevator is moving, the exit leading to it will be
inaccessible.</p>
<blockquote>
<div><p>Note: we don’t define the variables again in our chained event, we just call them. When we
execute <codeclass="docutils literal notranslate"><spanclass="pre">call_event</span></code>, a copy of our current variables is placed in the database. These variables
will be restored and accessible again when the chained event is called.</p>
</div></blockquote>
<p>You can use the <codeclass="docutils literal notranslate"><spanclass="pre">call/tasks</span></code> command to see the tasks waiting to be executed. For instance, say “2”
in the room, notice the doors closing, and then type the <codeclass="docutils literal notranslate"><spanclass="pre">call/tasks</span></code> command. You will see a task
in the elevator, waiting to call the <codeclass="docutils literal notranslate"><spanclass="pre">chain_1</span></code> event.</p>
</section>
<sectionid="changing-exit-messages">
<h2>Changing exit messages<aclass="headerlink"href="#changing-exit-messages"title="Permalink to this headline">¶</a></h2>
<p>Here’s another nice little feature of events: you can modify the message of a single exit without
altering the others. In this case, when someone goes north into our elevator, we’d like to see
something like: “someone walks into the elevator.” Something similar for the back exit would be
great too.</p>
<p>Inside of the elevator, you can look at the available events on the exit leading outside (south).</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>call south
</pre></div>
</div>
<p>You should see two interesting rows in this table:</p>
<p>So we can change the message others see when a character leaves, by editing the “msg_leave” event.
Let’s do that:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>call/add south = msg_leave
</pre></div>
</div>
<p>Take the time to read the help. It gives you all the information you should need. We’ll need to
change the “message” variable, and use custom mapping (between braces) to alter the message. We’re
given an example, let’s use it. In the code editor, you can paste the following line:</p>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="n">message</span><spanclass="o">=</span><spanclass="s2">"</span><spanclass="si">{character}</span><spanclass="s2"> walks out of the elevator."</span>
</pre></div>
</div>
<p>Again, save and quit the editor by entering <codeclass="docutils literal notranslate"><spanclass="pre">:wq</span></code>. You can create a new character to see it leave.</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>charcreate A beggar
<p>This is a crude way to force our beggar out of the elevator, but it allows us to test. You should
see:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>A beggar(#8) walks out of the elevator.
</pre></div>
</div>
<p>Great! Let’s do the same thing for the exit leading inside of the elevator. Follow the beggar,
then edit “msg_leave” of “north”:</p>
<divclass="highlight-none notranslate"><divclass="highlight"><pre><span></span>call/add north = msg_leave
</pre></div>
</div>
<divclass="highlight-python notranslate"><divclass="highlight"><pre><span></span><spanclass="n">message</span><spanclass="o">=</span><spanclass="s2">"</span><spanclass="si">{character}</span><spanclass="s2"> walks into the elevator."</span>
</pre></div>
</div>
<p>Again, you can force our beggar to move and see the message we have just set. This modification
applies to these two exits, obviously: the custom message won’t be used for other exits. Since we
use the same exits for every floor, this will be available no matter at what floor the elevator is,
which is pretty neat!</p>
</section>
<sectionid="tutorial-f-a-q">
<h2>Tutorial F.A.Q.<aclass="headerlink"href="#tutorial-f-a-q"title="Permalink to this headline">¶</a></h2>
<ulclass="simple">
<li><p><strong>Q:</strong> what happens if the game reloads or shuts down while a task is waiting to happen?</p></li>
<li><p><strong>A:</strong> if your game reloads while a task is in pause (like our elevator between floors), when the
game is accessible again, the task will be called (if necessary, with a new time difference to take
into account the reload). If the server shuts down, obviously, the task will not be called, but
will be stored and executed when the server is up again.</p></li>
<li><p><strong>Q:</strong> can I use all kinds of variables in my callback? Whether chained or not?</p></li>
<li><p><strong>A:</strong> you can use every variable type you like in your original callback. However, if you
execute <codeclass="docutils literal notranslate"><spanclass="pre">call_event</span></code>, since your variables are stored in the database, they will need to respect the
constraints on persistent attributes. A callback will not be stored in this way, for instance.
This variable will not be available in your chained event.</p></li>
<li><p><strong>Q:</strong> when you say I can call my chained events something else than “chain_1”, “chain_2” and
such, what is the naming convention?</p></li>
<li><p><strong>A:</strong> chained events have names beginning by <codeclass="docutils literal notranslate"><spanclass="pre">"chain_"</span></code>. This is useful for you and for the
system. But after the underscore, you can give a more useful name, like <codeclass="docutils literal notranslate"><spanclass="pre">"chain_open_doors"</span></code> in our
case.</p></li>
<li><p><strong>Q:</strong> do I have to pause several seconds to call a chained event?</p></li>
<li><p><strong>A:</strong> no, you can call it right away. Just leave the third parameter of <codeclass="docutils literal notranslate"><spanclass="pre">call_event</span></code> out (it
will default to 0, meaning the chained event will be called right away). This will not create a
task.</p></li>
<li><p><strong>Q:</strong> can I have chained events calling themselves?</p></li>
<li><p><strong>A:</strong> you can. There’s no limitation. Just be careful, a callback that calls itself,
particularly without delay, might be a good recipe for an infinite loop. However, in some cases, it
is useful to have chained events calling themselves, to do the same repeated action every X seconds
for instance.</p></li>
<li><p><strong>Q:</strong> what if I need several elevators, do I need to copy/paste these callbacks each time?</p></li>
<li><p><strong>A:</strong> not advisable. There are definitely better ways to handle this situation. One of them is
to consider adding the code in the source itself. Another possibility is to call chained events
with the expected behavior, which makes porting code very easy. This side of chained events will be
<pclass="last">You are reading an old version of the Evennia documentation. <ahref="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.