<h1>Building a train that moves<aclass="headerlink"href="#building-a-train-that-moves"title="Permalink to this headline">¶</a></h1>
<blockquote>
<div><p>TODO: This should be updated for latest Evennia use.</p>
</div></blockquote>
<p>Vehicles are things that you can enter and then move around in your game world. Here we’ll explain how to create a train, but this can be equally applied to create other kind of vehicles
<p>Objects in Evennia have an interesting property: you can put any object inside another object. This is most obvious in rooms: a room in Evennia is just like any other game object (except rooms tend to not themselves be inside anything else).</p>
<p>Our train will be similar: it will be an object that other objects can get inside. We then simply
move the Train, which brings along everyone inside it.</p>
<sectionid="creating-our-train-object">
<h2>Creating our train object<aclass="headerlink"href="#creating-our-train-object"title="Permalink to this headline">¶</a></h2>
<p>The first step we need to do is create our train object, including a new typeclass. To do this,
create a new file, for instance in <codeclass="docutils literal notranslate"><spanclass="pre">mygame/typeclasses/train.py</span></code> with the following content:</p>
<p>Using the <codeclass="docutils literal notranslate"><spanclass="pre">tel</span></code>command like shown above is obviously not what we want. <codeclass="docutils literal notranslate"><spanclass="pre">@tel</span></code> is an admin command and normal players will thus never be able to enter the train!</p>
<p>It is also not really a good idea to use <spanclass="xref myst">Exits</span> to get in and out of the train - Exits are (at least by default) objects too. They point to a specific destination. If we put an Exit in this room leading inside the train it would stay here when the train moved away (still leading into the train like a magic portal!). In the same way, if we put an Exit object inside the train, it would always point back to this room, regardless of where the Train has moved.</p>
<p>Now, one <em>could</em> define custom Exit types that move with the train or change their destination in the right way - but this seems to be a pretty cumbersome solution.</p>
<p>What we will do instead is to create some new <aclass="reference internal"href="../Components/Commands.html"><spanclass="doc std std-doc">commands</span></a>: one for entering the train and
another for leaving it again. These will be stored <em>on the train object</em> and will thus be made
available to whomever is either inside it or in the same room as the train.</p>
<p>Let’s create a new command module as <codeclass="docutils literal notranslate"><spanclass="pre">mygame/commands/train.py</span></code>:</p>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">caller</span><spanclass="o">.</span><spanclass="n">msg</span><spanclass="p">(</span><spanclass="s2">"You board the train."</span><spanclass="p">)</span>
<p>These commands are work in a pretty straightforward way: <codeclass="docutils literal notranslate"><spanclass="pre">CmdEnterTrain</span></code> moves the location of the player to inside the train and <codeclass="docutils literal notranslate"><spanclass="pre">CmdLeaveTrain</span></code> does the opposite: it moves the player back to the
current location of the train (back outside to its current location). We stacked them in a <aclass="reference internal"href="../Components/Command-Sets.html"><spanclass="doc std std-doc">cmdset</span></a><codeclass="docutils literal notranslate"><spanclass="pre">CmdSetTrain</span></code> so they can be used.</p>
<p>If we now <codeclass="docutils literal notranslate"><spanclass="pre">reload</span></code> our game and reset our train, those commands should work and we can now enter and leave the train:</p>
<p>Note the switches used with the <codeclass="docutils literal notranslate"><spanclass="pre">typeclass</span></code> command: The <codeclass="docutils literal notranslate"><spanclass="pre">/force</span></code> switch is necessary to assign our object the same typeclass we already have. The <codeclass="docutils literal notranslate"><spanclass="pre">/reset</span></code> re-triggers the typeclass’<codeclass="docutils literal notranslate"><spanclass="pre">at_object_creation()</span></code> hook (which is otherwise only called the very first an instance is created).
As seen above, when this hook is called on our train, our new cmdset will be loaded.</p>
</section>
<sectionid="locking-down-the-commands">
<h2>Locking down the commands<aclass="headerlink"href="#locking-down-the-commands"title="Permalink to this headline">¶</a></h2>
<p>If you have played around a bit, you’ve probably figured out that you can use <codeclass="docutils literal notranslate"><spanclass="pre">leave</span><spanclass="pre">train</span></code> when
outside the train and <codeclass="docutils literal notranslate"><spanclass="pre">enter</span><spanclass="pre">train</span></code> when inside. This doesn’t make any sense … so let’s go ahead
and fix that. We need to tell Evennia that you can not enter the train when you’re already inside
or leave the train when you’re outside. One solution to this is <aclass="reference internal"href="../Components/Locks.html"><spanclass="doc std std-doc">locks</span></a>: we will lock down the commands so that they can only be called if the player is at the correct location.</p>
<p>Since we didn’t set a <codeclass="docutils literal notranslate"><spanclass="pre">lock</span></code> property on the Command, it defaults to <codeclass="docutils literal notranslate"><spanclass="pre">cmd:all()</span></code>. This means that everyone can use the command as long as they are in the same room <em>or inside the train</em>.</p>
already, but none that we can use for locking a command in this particular case. Create a new entry in <codeclass="docutils literal notranslate"><spanclass="pre">mygame/server/conf/lockfuncs.py</span></code>:</p>
<p>Our new lock function, <codeclass="docutils literal notranslate"><spanclass="pre">cmdinside</span></code>, is to be used by Commands. The <codeclass="docutils literal notranslate"><spanclass="pre">accessed_obj</span></code> is the Command object (in our case this will be <codeclass="docutils literal notranslate"><spanclass="pre">CmdEnterTrain</span></code> and <codeclass="docutils literal notranslate"><spanclass="pre">CmdLeaveTrain</span></code>) — Every command has an <codeclass="docutils literal notranslate"><spanclass="pre">obj</span></code> property: this is the the object on which the command “sits”. Since we added those commands to our train object, the <codeclass="docutils literal notranslate"><spanclass="pre">.obj</span></code> property will be set to the train object. Conversely, <codeclass="docutils literal notranslate"><spanclass="pre">accessing_obj</span></code> is the object that called the command: in our case it’s the Character trying to enter or leave the train.</p>
<p>What this function does is to check that the player’s location is the same as the train object. If
it is, it means the player is inside the train. Otherwise it means the player is somewhere else and
the check will fail.</p>
<p>The next step is to actually use this new lock function to create a lock of type <codeclass="docutils literal notranslate"><spanclass="pre">cmd</span></code>:</p>
<p>Notice how we use the <codeclass="docutils literal notranslate"><spanclass="pre">not</span></code> here so that we can use the same <codeclass="docutils literal notranslate"><spanclass="pre">cmdinside</span></code> to check if we are inside
and outside, without having to create two separate lock functions. After a <codeclass="docutils literal notranslate"><spanclass="pre">@reload</span></code> our commands
should be locked down appropriately and you should only be able to use them at the right places.</p>
<blockquote>
<div><p>Note: If you’re logged in as the super user (user <codeclass="docutils literal notranslate"><spanclass="pre">#1</span></code>) then this lock will not work: the super
user ignores lock functions. In order to use this functionality you need to <codeclass="docutils literal notranslate"><spanclass="pre">@quell</span></code> first.</p>
</div></blockquote>
</section>
<sectionid="making-our-train-move">
<h2>Making our train move<aclass="headerlink"href="#making-our-train-move"title="Permalink to this headline">¶</a></h2>
<p>Now that we can enter and leave the train correctly, it’s time to make it move. There are different
<p>For our example train we’re going to go with automatic movement through a predefined route (its track). The train will stop for a bit at the start and end of the route to allow players to enter and leave it.</p>
<p>Go ahead and create some rooms for our train. Make a list of the room ids along the route (using the <codeclass="docutils literal notranslate"><spanclass="pre">xe</span></code> command).</p>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">msg_contents</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"The train is moving forward to </span><spanclass="si">{</span><spanclass="n">room</span><spanclass="o">.</span><spanclass="n">name</span><spanclass="si">}</span><spanclass="s2">."</span><spanclass="p">)</span>
<p>We added a lot of code here. Since we changed the <codeclass="docutils literal notranslate"><spanclass="pre">at_object_creation</span></code> to add in variables we will have to reset our train object like earlier (using the <codeclass="docutils literal notranslate"><spanclass="pre">@typeclass/force/reset</span></code> command).</p>
<p>We also added some methods: one to start moving the train, another to stop and a third that actually moves the train to the next room in the list. Or makes it stop driving if it reaches the last stop.</p>
<p>Let’s try it out, using <codeclass="docutils literal notranslate"><spanclass="pre">py</span></code> to call the new train functionality:</p>
<p>If we wanted full control of the train we could now just add a command to step it along the track when desired. We want the train to move on its own though, without us having to force it by manually calling the <codeclass="docutils literal notranslate"><spanclass="pre">goto_next_room</span></code> method.</p>
<p>To do this we will create two <aclass="reference internal"href="../Components/Scripts.html"><spanclass="doc std std-doc">scripts</span></a>: one script that runs when the train has stopped at
a station and is responsible for starting the train again after a while. The other script will take
care of the driving.</p>
<p>Let’s make a new file in <codeclass="docutils literal notranslate"><spanclass="pre">mygame/typeclasses/trainscript.py</span></code></p>
<li><p>Make it impossible to exit and enter the train mid-ride. This could be made by having the enter/exit commands check so the train is not moving before allowing the caller to proceed.</p></li>
<li><p>Have a rail road track instead of hard-coding the rooms in the train object. This could for example be a custom <spanclass="xref myst">Exit</span> only traversable by trains. The train will follow the track. Some track segments can split to lead to two different rooms and a player can switch the direction to which room it goes.</p></li>