In this lesson we'll learn how to create our own Evennia [Commands](../../../Components/Commands.md) If you are new to Python you'll also learn some more basics about how to manipulate strings and get information out of Evennia.
[previous lesson about it](./Beginner-Tutorial-Python-classes-and-objects.md)! A Command inherits from `evennia.Command` or from one of the alternative command- classes, such as `MuxCommand` which is what most default commands use.
All Commands are grouped in another class called a _Command Set_. 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.
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 `CharacterCmdSet`. It sits on `DefaultCharacter` (and thus, through inheritance, on `typeclasses.characters.Character`).
We can see that we import `Command` from `evennia` and use the `from ... import ... as ...` form to rename it to `BaseCommand`. This is so we can let our child class also be named `Command` to make it easier to reference. The class itself doesn't do anything, it just has `pass`. So in the same way as `Object` and `Character` in the previous lessons, this class is identical to its parent.
We could modify this module directly, but let's work in a separate module just for the heck of it. Open a new file `mygame/commands/mycommands.py` and add the following code:
Our `MyCmdSet` class must have an `at_cmdset_creation` 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 `self.add()`. If you wanted to add more commands to this CmdSet you could just add more lines of `self.add` after this.
`echo` works! You should be getting a long list of outputs. The reason for this is that your `echo` 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:
These are all properties you can access with `.` on the Command instance, such as `.key`, `.args` 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:
The reason our command doesn't do anything yet is because it's missing a `func` method. This is what Evennia looks for to figure out what a Command actually does. Modify your `CmdEcho` class:
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!
In a Command class, the `self.msg()` acts as a convenient shortcut for `self.caller.msg()`. Not only is it shorter, it also has some advantages because the command can include more metadata with the message. So using `self.msg()` is usually better. For this tutorial though, `self.caller.msg()` is more explicit in showing what is going on.
```
Next we add the `func` 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 [basic Python tutorial](./Beginner-Tutorial-Python-basic-introduction.md), you will recognize `.msg` - this will send a message to the object it is attached to us - in this case `self.caller`, that is, us. We grab `self.args` and includes that in the message.
Since we haven't changed `MyCmdSet`, that will work as before. Reload and re-add this command to ourselves to try out the new version:
Note that there is an extra space before `Woo`. That is because self.args contains _everything_ after the command name, including spaces. Let's remove that extra space with a small tweak:
The only difference is that we called `.strip()` on `self.args`. 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.
Above we added the `echo` command to ourselves. It will _only_ be available to us and noone else in the game. But all commands in Evennia are part of command-sets, including the normal `look` and `py` commands we have been using all the while. You can easily extend the default command set with your `echo` command - this way _everyone_ in the game will have access to it!
In `mygame/commands/` you'll find an existing module named `default_cmdsets.py` Open it and you'll find four empty cmdset-classes:
-`CharacterCmdSet` - this sits on all Characters (this is the one we usually want to modify)
-`AccountCmdSet` - this sits on all Accounts (shared between Characters, like `logout` etc)
-`UnloggedCmdSet` - commands available _before_ you login, like the commands for creating your password and connecting to the game.
-`SessionCmdSet` - commands unique to your Session (your particular client connection). This is unused by default.
Tweak this file as follows:
```python
# in mygame/commands/default_cmdsets.py
# ,..
from .mycommands import CmdEcho # <-------
class CharacterCmdSet(default_cmds.CharacterCmdSet):
"""
The `CharacterCmdSet` contains general in-game commands like `look`,
`get`, etc available on in-game Character objects. It is merged with
the `AccountCmdSet` when an Account puppets a Character.
"""
key = "DefaultCharacter"
def at_cmdset_creation(self):
"""
Populates the cmdset
"""
super().at_cmdset_creation()
#
# any commands you add below will overload the default ones.
#
self.add(command.CmdEcho) # <-----------
# ...
```
```{sidebar} super() and overriding defaults
The `super()` Python keyword means that the _parent_ is called. In this case, the parent adds all default commands to this cmdset.
Coincidentally, this is also how you replace default commands in Evennia!jj To replace e.g. the command `get`, you just give your replacement command the `key` 'get' and add it here - since it's added after `super()`, it will replace the default version of `get`.
```
This works the same way as when you added `CmdEcho` to your `MyCmdSet`. 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 `CmdEcho` from your `mycommands` module in order for this module to know about it. The period `.` in `from .mycommands import ...` means that we are telling Python that `mycommands.py` sits in the same directory as this current module.
Just `reload` the server and your `echo` command will be available again. There is no limit to how many cmdsets a given Command can be a part of.
Let's try something a little more exciting than just echo. Let's make a `hit` command, for punching someone in the face! This is how we want it to work:
Here, `<hitter>` would be the one using the `hit` command and `<target>` is the one doing the punching; so if your name was `Anna`, and you hit someone named `Bob`, this would look like this:
There can be any number of `elifs` to mark when different branches of the code should run. If `else` is provided, it will run if none of the other conditions were truthy.
- **Line 15** has our first _conditional_, an `if` statement. This is written on the form `if <condition>:` and only if that condition is 'truthy' will the indented code block under the `if` statement run. To learn what is truthy in Python it's usually easier to learn what is "falsy":
- **Line 16**'s condition is `not args`. The `not`_inverses_ the result, so if `args` is the empty string (falsy), the whole conditional becomes truthy. Let's continue in the code:
- **Lines 19-20**: A feature of `.search` is that it will already inform `self.caller` if it couldn't find the target. In that case, `target` will be `None` and we should just directly `return`.
Don't panic; tracebacks are your friends - they are to be read bottom-up and usually describe exactly where your problem is. Refer to [The Python introduction lesson](./Beginner-Tutorial-Python-basic-introduction.md) for more hints. If you get stuck, reach out to the Evennia community for help.