mirror of
https://github.com/evennia/evennia.git
synced 2026-03-17 21:36:30 +01:00
237 lines
10 KiB
Markdown
237 lines
10 KiB
Markdown
# Building a mech tutorial
|
||
|
||
> This page was adapted from the article "Building a Giant Mech in Evennia" by Griatch, published in
|
||
Imaginary Realities Volume 6, issue 1, 2014. The original article is no longer available online,
|
||
this is a version adopted to be compatible with the latest Evennia.
|
||
|
||
## Creating the Mech
|
||
|
||
Let us create a functioning giant mech using the Python MUD-creation system Evennia. Everyone likes
|
||
a giant mech, right? Start in-game as a character with build privileges (or the superuser).
|
||
|
||
@create/drop Giant Mech ; mech
|
||
|
||
Boom. We created a Giant Mech Object and dropped it in the room. We also gave it an alias *mech*.
|
||
Let’s describe it.
|
||
|
||
@desc mech = This is a huge mech. It has missiles and stuff.
|
||
|
||
Next we define who can “puppet” the mech object.
|
||
|
||
@lock mech = puppet:all()
|
||
|
||
This makes it so that everyone can control the mech. More mechs to the people! (Note that whereas
|
||
Evennia’s default commands may look vaguely MUX-like, you can change the syntax to look like
|
||
whatever interface style you prefer.)
|
||
|
||
Before we continue, let’s make a brief detour. Evennia is very flexible about its objects and even
|
||
more flexible about using and adding commands to those objects. Here are some ground rules well
|
||
worth remembering for the remainder of this article:
|
||
|
||
- The [Account](../Components/Accounts.md) represents the real person logging in and has no game-world existence.
|
||
- Any [Object](../Components/Objects.md) can be puppeted by an Account (with proper permissions).
|
||
- [Characters](../Components/Objects.md#characters), [Rooms](../Components/Objects.md#rooms), and [Exits](../Components/Objects.md#exits) are just
|
||
children of normal Objects.
|
||
- Any Object can be inside another (except if it creates a loop).
|
||
- Any Object can store custom sets of commands on it. Those commands can:
|
||
- be made available to the puppeteer (Account),
|
||
- be made available to anyone in the same location as the Object, and
|
||
- be made available to anyone “inside” the Object
|
||
- Also Accounts can store commands on themselves. Account commands are always available unless
|
||
commands on a puppeted Object explicitly override them.
|
||
|
||
In Evennia, using the `@ic` command will allow you to puppet a given Object (assuming you have
|
||
puppet-access to do so). As mentioned above, the bog-standard Character class is in fact like any
|
||
Object: it is auto-puppeted when logging in and just has a command set on it containing the normal
|
||
in-game commands, like look, inventory, get and so on.
|
||
|
||
@ic mech
|
||
|
||
You just jumped out of your Character and *are* now the mech! If people look at you in-game, they
|
||
will look at a mech. The problem at this point is that the mech Object has no commands of its own.
|
||
The usual things like look, inventory and get sat on the Character object, remember? So at the
|
||
moment the mech is not quite as cool as it could be.
|
||
|
||
@ic <Your old Character>
|
||
|
||
You just jumped back to puppeting your normal, mundane Character again. All is well.
|
||
|
||
> (But, you ask, where did that `@ic` command come from, if the mech had no commands on it? The
|
||
answer is that it came from the Account's command set. This is important. Without the Account being
|
||
the one with the `@ic` command, we would not have been able to get back out of our mech again.)
|
||
|
||
|
||
### Arming the Mech
|
||
|
||
Let us make the mech a little more interesting. In our favorite text editor, we will create some new
|
||
mech-suitable commands. In Evennia, commands are defined as Python classes.
|
||
|
||
```python
|
||
# in a new file mygame/commands/mechcommands.py
|
||
|
||
from evennia import Command
|
||
|
||
class CmdShoot(Command):
|
||
"""
|
||
Firing the mech’s gun
|
||
|
||
Usage:
|
||
shoot [target]
|
||
|
||
This will fire your mech’s main gun. If no
|
||
target is given, you will shoot in the air.
|
||
"""
|
||
key = "shoot"
|
||
aliases = ["fire", "fire!"]
|
||
|
||
def func(self):
|
||
"This actually does the shooting"
|
||
|
||
caller = self.caller
|
||
location = caller.location
|
||
|
||
if not self.args:
|
||
# no argument given to command - shoot in the air
|
||
message = "BOOM! The mech fires its gun in the air!"
|
||
location.msg_contents(message)
|
||
return
|
||
|
||
# we have an argument, search for target
|
||
target = caller.search(self.args.strip())
|
||
if target:
|
||
location.msg_contents(
|
||
f"BOOM! The mech fires its gun at {target.key}"
|
||
)
|
||
|
||
class CmdLaunch(Command):
|
||
# make your own 'launch'-command here as an exercise!
|
||
# (it's very similar to the 'shoot' command above).
|
||
|
||
```
|
||
|
||
This is saved as a normal Python module (let’s call it `mechcommands.py`), in a place Evennia looks
|
||
for such modules (`mygame/commands/`). This command will trigger when the player gives the command
|
||
“shoot”, “fire,” or even “fire!” with an exclamation mark. The mech can shoot in the air or at a
|
||
target if you give one. In a real game the gun would probably be given a chance to hit and give
|
||
damage to the target, but this is enough for now.
|
||
|
||
We also make a second command for launching missiles (`CmdLaunch`). To save
|
||
space we won’t describe it here; it looks the same except it returns a text
|
||
about the missiles being fired and has different `key` and `aliases`. We leave
|
||
that up to you to create as an exercise. You could have it print "WOOSH! The
|
||
mech launches missiles against <target>!", for example.
|
||
|
||
Now we shove our commands into a command set. A [Command Set](../Components/Command-Sets.md) (CmdSet) is a container
|
||
holding any number of commands. The command set is what we will store on the mech.
|
||
|
||
```python
|
||
# in the same file mygame/commands/mechcommands.py
|
||
|
||
from evennia import CmdSet
|
||
from evennia import default_cmds
|
||
|
||
class MechCmdSet(CmdSet):
|
||
"""
|
||
This allows mechs to do do mech stuff.
|
||
"""
|
||
key = "mechcmdset"
|
||
|
||
def at_cmdset_creation(self):
|
||
"Called once, when cmdset is first created"
|
||
self.add(CmdShoot())
|
||
self.add(CmdLaunch())
|
||
```
|
||
|
||
This simply groups all the commands we want. We add our new shoot/launch commands. Let’s head back
|
||
into the game. For testing we will manually attach our new CmdSet to the mech.
|
||
|
||
@py self.search("mech").cmdset.add("commands.mechcommands.MechCmdSet")
|
||
|
||
This is a little Python snippet (run from the command line as an admin) that searches for the mech
|
||
in our current location and attaches our new MechCmdSet to it. What we add is actually the Python
|
||
path to our cmdset class. Evennia will import and initialize it behind the scenes.
|
||
|
||
@ic mech
|
||
|
||
We are back as the mech! Let’s do some shooting!
|
||
|
||
fire!
|
||
BOOM! The mech fires its gun in the air!
|
||
|
||
There we go, one functioning mech. Try your own `launch` command and see that it works too. We can
|
||
not only walk around as the mech — since the CharacterCmdSet is included in our MechCmdSet, the mech
|
||
can also do everything a Character could do, like look around, pick up stuff, and have an inventory.
|
||
We could now shoot the gun at a target or try the missile launch command. Once you have your own
|
||
mech, what else do you need?
|
||
|
||
> Note: You'll find that the mech's commands are available to you by just standing in the same
|
||
location (not just by puppeting it). We'll solve this with a *lock* in the next section.
|
||
|
||
## Making a Mech production line
|
||
|
||
What we’ve done so far is just to make a normal Object, describe it and put some commands on it.
|
||
This is great for testing. The way we added it, the MechCmdSet will even go away if we reload the
|
||
server. Now we want to make the mech an actual object “type” so we can create mechs without those
|
||
extra steps. For this we need to create a new Typeclass.
|
||
|
||
A [Typeclass](../Components/Typeclasses.md) is a near-normal Python class that stores its existence to the database
|
||
behind the scenes. A Typeclass is created in a normal Python source file:
|
||
|
||
```python
|
||
# in the new file mygame/typeclasses/mech.py
|
||
|
||
from typeclasses.objects import Object
|
||
from commands.mechcommands import MechCmdSet
|
||
from evennia import default_cmds
|
||
|
||
class Mech(Object):
|
||
"""
|
||
This typeclass describes an armed Mech.
|
||
"""
|
||
def at_object_creation(self):
|
||
"This is called only when object is first created"
|
||
self.cmdset.add_default(default_cmds.CharacterCmdSet)
|
||
self.cmdset.add(MechCmdSet, persistent=True)
|
||
self.locks.add("puppet:all();call:false()")
|
||
self.db.desc = "This is a huge mech. It has missiles and stuff."
|
||
```
|
||
|
||
For convenience we include the full contents of the default `CharacterCmdSet` in there. This will
|
||
make a Character’s normal commands available to the mech. We also add the mech-commands from before,
|
||
making sure they are stored persistently in the database. The locks specify that anyone can puppet
|
||
the meck and no-one can "call" the mech's Commands from 'outside' it - you have to puppet it to be
|
||
able to shoot.
|
||
|
||
That’s it. When Objects of this type are created, they will always start out with the mech’s command
|
||
set and the correct lock. We set a default description, but you would probably change this with
|
||
`@desc` to individualize your mechs as you build them.
|
||
|
||
Back in the game, just exit the old mech (`@ic` back to your old character) then do
|
||
|
||
@create/drop The Bigger Mech ; bigmech : mech.Mech
|
||
|
||
We create a new, bigger mech with an alias bigmech. Note how we give the python-path to our
|
||
Typeclass at the end — this tells Evennia to create the new object based on that class (we don't
|
||
have to give the full path in our game dir `typeclasses.mech.Mech` because Evennia knows to look in
|
||
the `typeclasses` folder already). A shining new mech will appear in the room! Just use
|
||
|
||
@ic bigmech
|
||
|
||
to take it on a test drive.
|
||
|
||
## Future Mechs
|
||
|
||
To expand on this you could add more commands to the mech and remove others. Maybe the mech
|
||
shouldn’t work just like a Character after all. Maybe it makes loud noises every time it passes from
|
||
room to room. Maybe it cannot pick up things without crushing them. Maybe it needs fuel, ammo and
|
||
repairs. Maybe you’ll lock it down so it can only be puppeted by emo teenagers.
|
||
|
||
Having you puppet the mech-object directly is also just one way to implement a giant mech in
|
||
Evennia.
|
||
|
||
For example, you could instead picture a mech as a “vehicle” that you “enter” as your normal
|
||
Character (since any Object can move inside another). In that case the “insides” of the mech Object
|
||
could be the “cockpit”. The cockpit would have the `MechCommandSet` stored on itself and all the
|
||
shooting goodness would be made available to you only when you enter it.
|
||
|
||
And of course you could put more guns on it. And make it fly.
|