Complete permanent->persistent rename of cmdset kwarg for consistency
This commit is contained in:
parent
6e38d0ae4c
commit
a815db4ca9
20 changed files with 362 additions and 362 deletions
|
|
@ -42,16 +42,16 @@ Sets#merge-rules) section).
|
||||||
|
|
||||||
from evennia import CmdSet
|
from evennia import CmdSet
|
||||||
|
|
||||||
# this is a theoretical custom module with commands we
|
# this is a theoretical custom module with commands we
|
||||||
# created previously: mygame/commands/mycommands.py
|
# created previously: mygame/commands/mycommands.py
|
||||||
from commands import mycommands
|
from commands import mycommands
|
||||||
|
|
||||||
class MyCmdSet(CmdSet):
|
class MyCmdSet(CmdSet):
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"""
|
"""
|
||||||
The only thing this method should need
|
The only thing this method should need
|
||||||
to do is to add commands to the set.
|
to do is to add commands to the set.
|
||||||
"""
|
"""
|
||||||
self.add(mycommands.MyCommand1())
|
self.add(mycommands.MyCommand1())
|
||||||
self.add(mycommands.MyCommand2())
|
self.add(mycommands.MyCommand2())
|
||||||
self.add(mycommands.MyCommand3())
|
self.add(mycommands.MyCommand3())
|
||||||
|
|
@ -61,7 +61,7 @@ The CmdSet's `add()` method can also take another CmdSet as input. In this case
|
||||||
from that CmdSet will be appended to this one as if you added them line by line:
|
from that CmdSet will be appended to this one as if you added them line by line:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def at_cmdset_creation():
|
def at_cmdset_creation():
|
||||||
...
|
...
|
||||||
self.add(AdditionalCmdSet) # adds all command from this set
|
self.add(AdditionalCmdSet) # adds all command from this set
|
||||||
...
|
...
|
||||||
|
|
@ -71,10 +71,10 @@ If you added your command to an existing cmdset (like to the default cmdset), th
|
||||||
loaded into memory. You need to make the server aware of the code changes:
|
loaded into memory. You need to make the server aware of the code changes:
|
||||||
|
|
||||||
```
|
```
|
||||||
@reload
|
@reload
|
||||||
```
|
```
|
||||||
|
|
||||||
You should now be able to use the command.
|
You should now be able to use the command.
|
||||||
|
|
||||||
If you created a new, fresh cmdset, this must be added to an object in order to make the commands
|
If you created a new, fresh cmdset, this must be added to an object in order to make the commands
|
||||||
within available. A simple way to temporarily test a cmdset on yourself is use the `@py` command to
|
within available. A simple way to temporarily test a cmdset on yourself is use the `@py` command to
|
||||||
|
|
@ -93,15 +93,15 @@ This will stay with you until you `@reset` or `@shutdown` the server, or you run
|
||||||
In the example above, a specific Cmdset class is removed. Calling `delete` without arguments will
|
In the example above, a specific Cmdset class is removed. Calling `delete` without arguments will
|
||||||
remove the latest added cmdset.
|
remove the latest added cmdset.
|
||||||
|
|
||||||
> Note: Command sets added using `cmdset.add` are, by default, *not* persistent in the database.
|
> Note: Command sets added using `cmdset.add` are, by default, *not* persistent in the database.
|
||||||
|
|
||||||
If you want the cmdset to survive a reload, you can do:
|
If you want the cmdset to survive a reload, you can do:
|
||||||
|
|
||||||
```
|
```
|
||||||
@py self.cmdset.add(commands.mycmdset.MyCmdSet, permanent=True)
|
@py self.cmdset.add(commands.mycmdset.MyCmdSet, persistent=True)
|
||||||
```
|
```
|
||||||
|
|
||||||
Or you could add the cmdset as the *default* cmdset:
|
Or you could add the cmdset as the *default* cmdset:
|
||||||
|
|
||||||
```
|
```
|
||||||
@py self.cmdset.add_default(commands.mycmdset.MyCmdSet)
|
@py self.cmdset.add_default(commands.mycmdset.MyCmdSet)
|
||||||
|
|
@ -288,12 +288,12 @@ theory](https://en.wikipedia.org/wiki/Set_theory).
|
||||||
cmdset ends up in the merged cmdset. Same-key commands are merged by priority.
|
cmdset ends up in the merged cmdset. Same-key commands are merged by priority.
|
||||||
|
|
||||||
# Union
|
# Union
|
||||||
A1,A2 + B1,B2,B3,B4 = A1,A2,B3,B4
|
A1,A2 + B1,B2,B3,B4 = A1,A2,B3,B4
|
||||||
|
|
||||||
- **Intersect** - Only commands found in *both* cmdsets (i.e. which have the same keys) end up in
|
- **Intersect** - Only commands found in *both* cmdsets (i.e. which have the same keys) end up in
|
||||||
the merged cmdset, with the higher-priority cmdset replacing the lower one's commands.
|
the merged cmdset, with the higher-priority cmdset replacing the lower one's commands.
|
||||||
|
|
||||||
# Intersect
|
# Intersect
|
||||||
A1,A3,A5 + B1,B2,B4,B5 = A1,A5
|
A1,A3,A5 + B1,B2,B4,B5 = A1,A5
|
||||||
|
|
||||||
- **Replace** - The commands of the higher-prio cmdset completely replaces the lower-priority
|
- **Replace** - The commands of the higher-prio cmdset completely replaces the lower-priority
|
||||||
|
|
@ -337,23 +337,23 @@ onto E and not B, our `key_mergetype` directive won't trigger. To make sure it w
|
||||||
sure we merge onto B. Setting E's priority to, say, -4 will make sure to merge it onto B and affect
|
sure we merge onto B. Setting E's priority to, say, -4 will make sure to merge it onto B and affect
|
||||||
it appropriately.
|
it appropriately.
|
||||||
|
|
||||||
More advanced cmdset example:
|
More advanced cmdset example:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from commands import mycommands
|
from commands import mycommands
|
||||||
|
|
||||||
class MyCmdSet(CmdSet):
|
class MyCmdSet(CmdSet):
|
||||||
|
|
||||||
key = "MyCmdSet"
|
key = "MyCmdSet"
|
||||||
priority = 4
|
priority = 4
|
||||||
mergetype = "Replace"
|
mergetype = "Replace"
|
||||||
key_mergetypes = {'MyOtherCmdSet':'Union'}
|
key_mergetypes = {'MyOtherCmdSet':'Union'}
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"""
|
"""
|
||||||
The only thing this method should need
|
The only thing this method should need
|
||||||
to do is to add commands to the set.
|
to do is to add commands to the set.
|
||||||
"""
|
"""
|
||||||
self.add(mycommands.MyCommand1())
|
self.add(mycommands.MyCommand1())
|
||||||
self.add(mycommands.MyCommand2())
|
self.add(mycommands.MyCommand2())
|
||||||
self.add(mycommands.MyCommand3())
|
self.add(mycommands.MyCommand3())
|
||||||
|
|
@ -373,4 +373,4 @@ exits are merged in), these two commands will be considered *identical* since th
|
||||||
means only one of them will remain after the merger. Each will also be compared with all other
|
means only one of them will remain after the merger. Each will also be compared with all other
|
||||||
commands having any combination of the keys and/or aliases "kick", "punch" or "fight".
|
commands having any combination of the keys and/or aliases "kick", "punch" or "fight".
|
||||||
|
|
||||||
... So avoid duplicate aliases, it will only cause confusion.
|
... So avoid duplicate aliases, it will only cause confusion.
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@ class Mech(Object):
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"This is called only when object is first created"
|
"This is called only when object is first created"
|
||||||
self.cmdset.add_default(default_cmds.CharacterCmdSet)
|
self.cmdset.add_default(default_cmds.CharacterCmdSet)
|
||||||
self.cmdset.add(MechCmdSet, permanent=True)
|
self.cmdset.add(MechCmdSet, persistent=True)
|
||||||
self.locks.add("puppet:all();call:false()")
|
self.locks.add("puppet:all();call:false()")
|
||||||
self.db.desc = "This is a huge mech. It has missiles and stuff."
|
self.db.desc = "This is a huge mech. It has missiles and stuff."
|
||||||
```
|
```
|
||||||
|
|
@ -233,4 +233,4 @@ Character (since any Object can move inside another). In that case the “inside
|
||||||
could be the “cockpit”. The cockpit would have the `MechCommandSet` stored on itself and all the
|
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.
|
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.
|
And of course you could put more guns on it. And make it fly.
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ class BlockingCmdSet(CmdSet):
|
||||||
|
|
||||||
class BlockingRoom(Room):
|
class BlockingRoom(Room):
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
self.cmdset.add(BlockingCmdSet, permanent=True)
|
self.cmdset.add(BlockingCmdSet, persistent=True)
|
||||||
# only share commands with players in the room that
|
# only share commands with players in the room that
|
||||||
# are NOT Builders or higher
|
# are NOT Builders or higher
|
||||||
self.locks.add("call:not perm(Builders)")
|
self.locks.add("call:not perm(Builders)")
|
||||||
|
|
@ -375,4 +375,4 @@ for staff to browse the list and display how long ago the login occurred.
|
||||||
their len() suggests. There is little Evennia can (reliably) do about this. If you are using such
|
their len() suggests. There is little Evennia can (reliably) do about this. If you are using such
|
||||||
characters, you need to make sure to use a suitable mono-spaced font where are width are equal. You
|
characters, you need to make sure to use a suitable mono-spaced font where are width are equal. You
|
||||||
can set this in your web client and need to recommend it for telnet-client users. See [this
|
can set this in your web client and need to recommend it for telnet-client users. See [this
|
||||||
discussion](https://github.com/evennia/evennia/issues/1522) where some suitable fonts are suggested.
|
discussion](https://github.com/evennia/evennia/issues/1522) where some suitable fonts are suggested.
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,7 @@ from evennia.utils.create import create_object
|
||||||
# class for our front shop room
|
# class for our front shop room
|
||||||
class NPCShop(DefaultRoom):
|
class NPCShop(DefaultRoom):
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
# we could also use add(ShopCmdSet, permanent=True)
|
# we could also use add(ShopCmdSet, persistent=True)
|
||||||
self.cmdset.add_default(ShopCmdSet)
|
self.cmdset.add_default(ShopCmdSet)
|
||||||
self.db.storeroom = None
|
self.db.storeroom = None
|
||||||
|
|
||||||
|
|
@ -331,4 +331,4 @@ Fixing these issues are left as an exercise.
|
||||||
If you want to keep the shop fully NPC-run you could add a [Script](../Components/Scripts) to restock the shop's
|
If you want to keep the shop fully NPC-run you could add a [Script](../Components/Scripts) to restock the shop's
|
||||||
store room regularly. This shop example could also easily be owned by a human Player (run for them
|
store room regularly. This shop example could also easily be owned by a human Player (run for them
|
||||||
by a hired NPC) - the shop owner would get the key to the store room and be responsible for keeping
|
by a hired NPC) - the shop owner would get the key to the store room and be responsible for keeping
|
||||||
it well stocked.
|
it well stocked.
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,30 @@
|
||||||
# Our own commands
|
# Our own commands
|
||||||
|
|
||||||
In this lesson we'll learn how to create our own Evennia _Commands_. If you are new to Python you'll
|
In this lesson we'll learn how to create our own Evennia _Commands_. If you are new to Python you'll
|
||||||
also learn some more basics about how to manipulate strings and get information out of Evennia.
|
also learn some more basics about how to manipulate strings and get information out of Evennia.
|
||||||
|
|
||||||
A Command is something that handles the input from a user and causes a result to happen.
|
A Command is something that handles the input from a user and causes a result to happen.
|
||||||
An example is `look`, which examines your current location and tells how it looks like and
|
An example is `look`, which examines your current location and tells how it looks like and
|
||||||
what is in it.
|
what is in it.
|
||||||
|
|
||||||
```sidebar:: Commands are not typeclassed
|
```sidebar:: Commands are not typeclassed
|
||||||
|
|
||||||
If you just came from the previous lesson, you might want to know that Commands and
|
If you just came from the previous lesson, you might want to know that Commands and
|
||||||
CommandSets are not `typeclassed`. That is, instances of them are not saved to the
|
CommandSets are not `typeclassed`. That is, instances of them are not saved to the
|
||||||
database. They are "just" normal Python classes.
|
database. They are "just" normal Python classes.
|
||||||
```
|
```
|
||||||
|
|
||||||
In Evennia, a Command is a Python _class_. If you are unsure about what a class is, review the
|
In Evennia, a Command is a Python _class_. If you are unsure about what a class is, review the
|
||||||
previous lessons! A Command inherits from `evennia.Command` or from one of the alternative command-
|
previous lessons! A Command inherits from `evennia.Command` or from one of the alternative command-
|
||||||
classes, such as `MuxCommand` which is what most default commands use.
|
classes, such as `MuxCommand` which is what most default commands use.
|
||||||
|
|
||||||
All Commands are in turn grouped in another class called a _Command Set_. Think of a Command Set
|
All Commands are in turn 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
|
as a bag holding many different commands. One CmdSet could for example hold all commands for
|
||||||
combat, another for building etc. By default, Evennia groups all character-commands into one
|
combat, another for building etc. By default, Evennia groups all character-commands into one
|
||||||
big cmdset.
|
big cmdset.
|
||||||
|
|
||||||
Command-Sets are then associated with objects, for example with your Character. Doing so makes the
|
Command-Sets are then associated with objects, for example with your Character. Doing so makes the
|
||||||
commands in that cmdset available to the object. So, to summarize:
|
commands in that cmdset available to the object. So, to summarize:
|
||||||
|
|
||||||
- Commands are classes
|
- Commands are classes
|
||||||
- A group of Commands is stored in a CmdSet
|
- A group of Commands is stored in a CmdSet
|
||||||
|
|
@ -45,25 +45,25 @@ from evennia import Command as BaseCommand
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""
|
"""
|
||||||
(class docstring)
|
(class docstring)
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# (lots of commented-out stuff)
|
# (lots of commented-out stuff)
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
Ignoring the docstrings (which you can read if you want), this is the only really active code in the module.
|
Ignoring the docstrings (which you can read if you want), this is the only really active code in the module.
|
||||||
|
|
||||||
We can see that we import `Command` from `evennia` and use the `from ... import ... as ...` form to rename it
|
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` for reference. The class
|
to `BaseCommand`. This is so we can let our child class also be named `Command` for reference. The class
|
||||||
itself doesn't do anything, it just has `pass`. So in the same way as `Object` in the previous lesson, this
|
itself doesn't do anything, it just has `pass`. So in the same way as `Object` in the previous lesson, this
|
||||||
class is identical to its parent.
|
class is identical to its parent.
|
||||||
|
|
||||||
> The commented out `default_cmds` gives us access to Evennia's default commands for easy overriding. We'll try
|
> The commented out `default_cmds` gives us access to Evennia's default commands for easy overriding. We'll try
|
||||||
> that a little later.
|
> that a little later.
|
||||||
|
|
||||||
We could modify this module directly, but to train imports we'll work in a separate module. Open a new file
|
We could modify this module directly, but to train imports we'll work in a separate module. Open a new file
|
||||||
`mygame/commands/mycommands.py` and add the following code:
|
`mygame/commands/mycommands.py` and add the following code:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
||||||
|
|
@ -74,8 +74,8 @@ class CmdEcho(Command):
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This is the simplest form of command you can imagine. It just gives itself a name, "echo". This is
|
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.
|
what you will use to call this command later.
|
||||||
|
|
||||||
Next we need to put this in a CmdSet. It will be a one-command CmdSet for now! Change your file as such:
|
Next we need to put this in a CmdSet. It will be a one-command CmdSet for now! Change your file as such:
|
||||||
|
|
||||||
|
|
@ -96,24 +96,24 @@ class MyCmdSet(CmdSet):
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Our `EchoCmdSet` class must have an `at_cmdset_creation` method, named exactly
|
Our `EchoCmdSet` 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
|
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
|
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
|
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.
|
could just add more lines of `self.add` after this.
|
||||||
|
|
||||||
Finally, let's add this command to ourselves so we can try it out. In-game you can experiment with `py` again:
|
Finally, let's add this command to ourselves so we can try it out. In-game you can experiment with `py` again:
|
||||||
|
|
||||||
> py self.cmdset.add("commands.mycommands.MyCmdSet")
|
> py self.cmdset.add("commands.mycommands.MyCmdSet")
|
||||||
|
|
||||||
Now try
|
Now try
|
||||||
|
|
||||||
> echo
|
> echo
|
||||||
Command echo has no defined `func()` - showing on-command variables:
|
Command echo has no defined `func()` - showing on-command variables:
|
||||||
...
|
...
|
||||||
...
|
...
|
||||||
|
|
||||||
You should be getting a long list of outputs. The reason for this is that your `echo` function is not really
|
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
|
"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:
|
use your Command. Let's look at some of those listed:
|
||||||
|
|
||||||
|
|
@ -124,7 +124,7 @@ use your Command. Let's look at some of those listed:
|
||||||
cmdname (<class 'str'>): echo
|
cmdname (<class 'str'>): echo
|
||||||
raw_cmdname (<class 'str'>): echo
|
raw_cmdname (<class 'str'>): echo
|
||||||
cmdstring (<class 'str'>): echo
|
cmdstring (<class 'str'>): echo
|
||||||
args (<class 'str'>):
|
args (<class 'str'>):
|
||||||
cmdset (<class 'evennia.commands.cmdset.CmdSet'>): @mail, about, access, accounts, addcom, alias, allcom, ban, batchcode, batchcommands, boot, cboot, ccreate,
|
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,
|
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,
|
desc, destroy, dig, dolphin, drop, echo, emit, examine, find, force, get, give, grapevine2chan, help, home, ic, inventory, irc2chan, ircstatus, link, lock,
|
||||||
|
|
@ -133,7 +133,7 @@ use your Command. Let's look at some of those listed:
|
||||||
tickers, time, tunnel, typeclass, unban, unlink, up, up, userpassword, wall, whisper, who, wipe
|
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
|
session (<class 'evennia.server.serversession.ServerSession'>): Griatch(#1)@1:2:7:.:0:.:0:.:1
|
||||||
account (<class 'typeclasses.accounts.Account'>): Griatch(account 1)
|
account (<class 'typeclasses.accounts.Account'>): Griatch(account 1)
|
||||||
raw_string (<class 'str'>): echo
|
raw_string (<class 'str'>): echo
|
||||||
|
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
echo - Command variables from evennia:
|
echo - Command variables from evennia:
|
||||||
|
|
@ -147,16 +147,16 @@ use your Command. Let's look at some of those listed:
|
||||||
command string given (self.cmdstring): echo
|
command string given (self.cmdstring): echo
|
||||||
current cmdset (self.cmdset): ChannelCmdSet
|
current cmdset (self.cmdset): ChannelCmdSet
|
||||||
|
|
||||||
These are all properties you can access with `.` on the Command instance, such as `.key`, `.args` and so on.
|
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
|
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:
|
important ones we will make use of now are:
|
||||||
|
|
||||||
- `caller` - this is 'you', the person calling the command.
|
- `caller` - this is 'you', the person calling the command.
|
||||||
- `args` - this is all arguments to the command. Now it's empty, but if you tried `echo foo bar` you'd find
|
- `args` - this is all arguments to the command. Now it's empty, but if you tried `echo foo bar` you'd find
|
||||||
that this would be `" foo bar"`.
|
that this would be `" foo bar"`.
|
||||||
- `obj` - this is object on which this Command (and CmdSet) "sits". So you, in this case.
|
- `obj` - this is object on which this Command (and CmdSet) "sits". So you, in this case.
|
||||||
|
|
||||||
The reason our command doesn't do anything yet is because it's missing a `func` method. This is what Evennia
|
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:
|
looks for to figure out what a Command actually does. Modify your `CmdEcho` class:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
@ -165,47 +165,47 @@ looks for to figure out what a Command actually does. Modify your `CmdEcho` clas
|
||||||
class CmdEcho(Command):
|
class CmdEcho(Command):
|
||||||
"""
|
"""
|
||||||
A simple echo command
|
A simple echo command
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
echo <something>
|
echo <something>
|
||||||
|
|
||||||
"""
|
"""
|
||||||
key = "echo"
|
key = "echo"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
self.caller.msg(f"Echo: '{self.args}'")
|
self.caller.msg(f"Echo: '{self.args}'")
|
||||||
|
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
First we added a docstring. This is always a good thing to do in general, but for a Command class, it will also
|
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! Next we add the `func` method. It has one active line where it
|
automatically become the in-game help entry! Next we add the `func` method. It has one active line where it
|
||||||
makes use of some of those variables we found the Command offers to us. If you did the
|
makes use of some of those variables we found the Command offers to us. If you did the
|
||||||
[basic Python tutorial](./Python-basic-introduction), you will recognize `.msg` - this will send a message
|
[basic Python tutorial](./Python-basic-introduction), 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
|
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.
|
that in the message.
|
||||||
|
|
||||||
Since we haven't changed `MyCmdSet`, that will work as before. Reload and re-add this command to ourselves to
|
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:
|
try out the new version:
|
||||||
|
|
||||||
> reload
|
> reload
|
||||||
> py self.cmdset.add("commands.mycommands.MyCmdSet")
|
> py self.cmdset.add("commands.mycommands.MyCmdSet")
|
||||||
> echo
|
> echo
|
||||||
Echo: ''
|
Echo: ''
|
||||||
|
|
||||||
Try to pass an argument:
|
Try to pass an argument:
|
||||||
|
|
||||||
> echo Woo Tang!
|
> echo Woo Tang!
|
||||||
Echo: ' Woo Tang!'
|
Echo: ' Woo Tang!'
|
||||||
|
|
||||||
Note that there is an extra space before `Woo!`. That is because self.args contains the _everything_ after
|
Note that there is an extra space before `Woo!`. That is because self.args contains the _everything_ after
|
||||||
the command name, including spaces. Evennia will happily understand if you skip that space too:
|
the command name, including spaces. Evennia will happily understand if you skip that space too:
|
||||||
|
|
||||||
> echoWoo Tang!
|
> echoWoo Tang!
|
||||||
Echo: 'Woo Tang!'
|
Echo: 'Woo Tang!'
|
||||||
|
|
||||||
There are ways to force Evennia to _require_ an initial space, but right now we want to just ignore it since
|
There are ways to force Evennia to _require_ an initial space, but right now we want to just ignore it since
|
||||||
it looks a bit weird for our echo example. Tweak the code:
|
it looks a bit weird for our echo example. Tweak the code:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# ...
|
# ...
|
||||||
|
|
@ -213,15 +213,15 @@ it looks a bit weird for our echo example. Tweak the code:
|
||||||
class CmdEcho(Command):
|
class CmdEcho(Command):
|
||||||
"""
|
"""
|
||||||
A simple echo command
|
A simple echo command
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
echo <something>
|
echo <something>
|
||||||
|
|
||||||
"""
|
"""
|
||||||
key = "echo"
|
key = "echo"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
self.caller.msg(f"Echo: '{self.args.strip()}'")
|
self.caller.msg(f"Echo: '{self.args.strip()}'")
|
||||||
|
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
@ -230,25 +230,25 @@ The only difference is that we called `.strip()` on `self.args`. This is a helpe
|
||||||
strings - it strips out all whitespace before and after the string. Now the Command-argument will no longer
|
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.
|
have any space in front of it.
|
||||||
|
|
||||||
> reload
|
> reload
|
||||||
> py self.cmdset.add("commands.mycommands.MyCmdSet")
|
> py self.cmdset.add("commands.mycommands.MyCmdSet")
|
||||||
> echo Woo Tang!
|
> echo Woo Tang!
|
||||||
Echo: 'Woo Tang!'
|
Echo: 'Woo Tang!'
|
||||||
|
|
||||||
Don't forget to look at the help for the echo command:
|
|
||||||
|
|
||||||
> help echo
|
Don't forget to look at the help for the echo command:
|
||||||
|
|
||||||
You will get the docstring you put in your Command-class.
|
> help echo
|
||||||
|
|
||||||
|
You will get the docstring you put in your Command-class.
|
||||||
|
|
||||||
### Making our cmdset persistent
|
### Making our cmdset persistent
|
||||||
|
|
||||||
It's getting a little annoying to have to re-add our cmdset every time we reload, right? It's simple
|
It's getting a little annoying to have to re-add our cmdset every time we reload, right? It's simple
|
||||||
enough to make `echo` a _permanent_ change though:
|
enough to make `echo` a _persistent_ change though:
|
||||||
|
|
||||||
> py self.cmdset.add("commands.mycommands.MyCmdSet", permanent=True)
|
> py self.cmdset.add("commands.mycommands.MyCmdSet", persistent=True)
|
||||||
|
|
||||||
Now you can `reload` as much as you want and your code changes will be available directly without
|
Now you can `reload` as much as you want and your code changes will be available directly without
|
||||||
needing to re-add the MyCmdSet again. To remove the cmdset again, do
|
needing to re-add the MyCmdSet again. To remove the cmdset again, do
|
||||||
|
|
||||||
> py self.cmdset.remove("commands.mycommands.MyCmdSet")
|
> py self.cmdset.remove("commands.mycommands.MyCmdSet")
|
||||||
|
|
@ -258,18 +258,18 @@ But for now, keep it around, we'll expand it with some more examples.
|
||||||
### Figuring out who to hit
|
### Figuring out who to hit
|
||||||
|
|
||||||
Let's try something a little more exciting than just echo. Let's make a `hit` command, for punching
|
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:
|
someone in the face! This is how we want it to work:
|
||||||
|
|
||||||
> hit <target>
|
> hit <target>
|
||||||
You hit <target> with full force!
|
You hit <target> with full force!
|
||||||
|
|
||||||
Not only that, we want the <target> to see
|
Not only that, we want the <target> to see
|
||||||
|
|
||||||
You got hit by <hitter> with full force!
|
You got hit by <hitter> with full force!
|
||||||
|
|
||||||
Here, `<hitter>` would be the one using the `hit` command and `<target>` is the one doing the punching.
|
Here, `<hitter>` would be the one using the `hit` command and `<target>` is the one doing the punching.
|
||||||
|
|
||||||
Still in `mygame/commands/mycommands.py`, add a new class, between `CmdEcho` and `MyCmdSet`.
|
Still in `mygame/commands/mycommands.py`, add a new class, between `CmdEcho` and `MyCmdSet`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# ...
|
# ...
|
||||||
|
|
@ -277,7 +277,7 @@ Still in `mygame/commands/mycommands.py`, add a new class, between `CmdEcho` and
|
||||||
class CmdHit(Command):
|
class CmdHit(Command):
|
||||||
"""
|
"""
|
||||||
Hit a target.
|
Hit a target.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
hit <target>
|
hit <target>
|
||||||
|
|
||||||
|
|
@ -288,11 +288,11 @@ class CmdHit(Command):
|
||||||
args = self.args.strip()
|
args = self.args.strip()
|
||||||
if not args:
|
if not args:
|
||||||
self.caller.msg("Who do you want to hit?")
|
self.caller.msg("Who do you want to hit?")
|
||||||
return
|
return
|
||||||
target = self.caller.search(args)
|
target = self.caller.search(args)
|
||||||
if not target:
|
if not target:
|
||||||
return
|
return
|
||||||
self.caller.msg(f"You hit {target.key} with full force!")
|
self.caller.msg(f"You hit {target.key} with full force!")
|
||||||
target.msg(f"You got hit by {self.caller.key} with full force!")
|
target.msg(f"You got hit by {self.caller.key} with full force!")
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
|
|
@ -302,46 +302,46 @@ A lot of things to dissect here:
|
||||||
- **Line 4**: The normal `class` header. We inherit from `Command` which we imported at the top of this file.
|
- **Line 4**: The normal `class` header. We inherit from `Command` which we imported at the top of this file.
|
||||||
- **Lines 5**-11: The docstring and help-entry for the command. You could expand on this as much as you wanted.
|
- **Lines 5**-11: The docstring and help-entry for the command. You could expand on this as much as you wanted.
|
||||||
- **Line 12**: We want to write `hit` to use this command.
|
- **Line 12**: We want to write `hit` to use this command.
|
||||||
- **Line 15**: We strip the whitespace from the argument like before. Since we don't want to have to do
|
- **Line 15**: We strip the whitespace from the argument like before. Since we don't want to have to do
|
||||||
`self.args.strip()` over and over, we store the stripped version
|
`self.args.strip()` over and over, we store the stripped version
|
||||||
in a _local variable_ `args`. Note that we don't modify `self.args` by doing this, `self.args` will still
|
in a _local variable_ `args`. Note that we don't modify `self.args` by doing this, `self.args` will still
|
||||||
have the whitespace and is not the same as `args` in this example.
|
have the whitespace and is not the same as `args` in this example.
|
||||||
```sidebar:: if-statements
|
```sidebar:: if-statements
|
||||||
|
|
||||||
The full form of the if statement is
|
The full form of the if statement is
|
||||||
|
|
||||||
if condition:
|
if condition:
|
||||||
...
|
...
|
||||||
elif othercondition:
|
elif othercondition:
|
||||||
...
|
...
|
||||||
else:
|
else:
|
||||||
...
|
...
|
||||||
|
|
||||||
There can be any number of `elifs` to mark when different branches of the code should run. If
|
There can be any number of `elifs` to mark when different branches of the code should run. If
|
||||||
the `else` condition is given, it will run if none of the other conditions was truthy. In Python
|
the `else` condition is given, it will run if none of the other conditions was truthy. In Python
|
||||||
the `if..elif..else` structure also serves the same function as `case` in some other languages.
|
the `if..elif..else` structure also serves the same function as `case` in some other languages.
|
||||||
|
|
||||||
```
|
```
|
||||||
- **Line 16** has our first _conditional_, an `if` statement. This is written on the form `if <condition>:` and only
|
- **Line 16** 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
|
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":
|
Python it's usually easier to learn what is "falsy":
|
||||||
- `False` - this is a reserved boolean word in Python. The opposite is `True`.
|
- `False` - this is a reserved boolean word in Python. The opposite is `True`.
|
||||||
- `None` - another reserved word. This represents nothing, a null-result or value.
|
- `None` - another reserved word. This represents nothing, a null-result or value.
|
||||||
- `0` or `0.0`
|
- `0` or `0.0`
|
||||||
- The empty string `""` or `''` or `""""""` or `''''''`
|
- The empty string `""` or `''` or `""""""` or `''''''`
|
||||||
- Empty _iterables_ we haven't seen yet, like empty lists `[]`, empty tuples `()` and empty dicts `{}`.
|
- Empty _iterables_ we haven't seen yet, like empty lists `[]`, empty tuples `()` and empty dicts `{}`.
|
||||||
- Everything else is "truthy".
|
- Everything else is "truthy".
|
||||||
|
|
||||||
Line 16's condition is `not args`. The `not` _inverses_ the result, so if `args` is the empty string (falsy), the
|
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:
|
whole conditional becomes truthy. Let's continue in the code:
|
||||||
- **Lines 17-18**: This code will only run if the `if` statement is truthy, in this case if `args` is the empty string.
|
- **Lines 17-18**: This code will only run if the `if` statement is truthy, in this case if `args` is the empty string.
|
||||||
- **Line 18**: `return` is a reserved Python word that exits `func` immediately.
|
- **Line 18**: `return` is a reserved Python word that exits `func` immediately.
|
||||||
- **Line 19**: We use `self.caller.search` to look for the target in the current location.
|
- **Line 19**: We use `self.caller.search` to look for the target in the current location.
|
||||||
- **Lines 20-21**: A feature of `.search` is that it will already inform `self.caller` if it couldn't find the target.
|
- **Lines 20-21**: 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`.
|
In that case, `target` will be `None` and we should just directly `return`.
|
||||||
- **Lines 22-23**: At this point we have a suitable target and can send our punching strings to each.
|
- **Lines 22-23**: At this point we have a suitable target and can send our punching strings to each.
|
||||||
|
|
||||||
Finally we must also add this to a CmdSet. Let's add it to `MyCmdSet` which we made permanent earlier.
|
Finally we must also add this to a CmdSet. Let's add it to `MyCmdSet` which we made persistent earlier.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# ...
|
# ...
|
||||||
|
|
@ -357,17 +357,17 @@ class MyCmdSet(CmdSet):
|
||||||
```sidebar:: Errors in your code
|
```sidebar:: Errors in your code
|
||||||
|
|
||||||
With longer code snippets to try, it gets more and more likely you'll
|
With longer code snippets to try, it gets more and more likely you'll
|
||||||
make an error and get a `traceback` when you reload. This will either appear
|
make an error and get a `traceback` when you reload. This will either appear
|
||||||
directly in-game or in your log (view it with `evennia -l` in a terminal).
|
directly in-game or in your log (view it with `evennia -l` in a terminal).
|
||||||
Don't panic; tracebacks are your friends - they are to be read bottom-up and usually describe
|
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 intro <Python-basic-introduction.html>`_ for
|
exactly where your problem is. Refer to `The Python intro <Python-basic-introduction.html>`_ for
|
||||||
more hints. If you get stuck, reach out to the Evennia community for help.
|
more hints. If you get stuck, reach out to the Evennia community for help.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Next we reload to let Evennia know of these code changes and try it out:
|
Next we reload to let Evennia know of these code changes and try it out:
|
||||||
|
|
||||||
> reload
|
> reload
|
||||||
hit
|
hit
|
||||||
Who do you want to hit?
|
Who do you want to hit?
|
||||||
hit me
|
hit me
|
||||||
|
|
@ -377,7 +377,7 @@ Next we reload to let Evennia know of these code changes and try it out:
|
||||||
Lacking a target, we hit ourselves. If you have one of the dragons still around from the previous lesson
|
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):
|
you could try to hit it (if you dare):
|
||||||
|
|
||||||
hit smaug
|
hit smaug
|
||||||
You hit Smaug with full force!
|
You hit Smaug with full force!
|
||||||
|
|
||||||
You won't see the second string. Only Smaug sees that (and is not amused).
|
You won't see the second string. Only Smaug sees that (and is not amused).
|
||||||
|
|
@ -385,8 +385,8 @@ You won't see the second string. Only Smaug sees that (and is not amused).
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
In this lesson we learned how to create our own Command, add it to a CmdSet and then to ourselves.
|
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.
|
We also upset a dragon.
|
||||||
|
|
||||||
In the next lesson we'll learn how to hit Smaug with different weapons. We'll also
|
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.
|
get into how we replace and extend Evennia's default Commands.
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,25 @@
|
||||||
# More about Commands
|
# More about Commands
|
||||||
|
|
||||||
In this lesson we learn some basics about parsing the input of Commands. We will
|
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.
|
also learn how to add, modify and extend Evennia's default commands.
|
||||||
|
|
||||||
## More advanced parsing
|
## More advanced parsing
|
||||||
|
|
||||||
In the last lesson we made a `hit` Command and hit a dragon with it. You should have the code
|
In the last lesson we made a `hit` Command and hit a dragon with it. You should have the code
|
||||||
from that still around.
|
from that still around.
|
||||||
|
|
||||||
Let's expand our simple `hit` command to accept a little more complex input:
|
Let's expand our simple `hit` command to accept a little more complex input:
|
||||||
|
|
||||||
hit <target> [[with] <weapon>]
|
hit <target> [[with] <weapon>]
|
||||||
|
|
||||||
That is, we want to support all of these forms
|
That is, we want to support all of these forms
|
||||||
|
|
||||||
hit target
|
hit target
|
||||||
hit target weapon
|
hit target weapon
|
||||||
hit target with weapon
|
hit target with weapon
|
||||||
|
|
||||||
If you don't specify a weapon you'll use your fists. It's also nice to be able to skip "with" if
|
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 `mygame/commands/mycommands.py` again. Let us break out the parsing
|
you are in a hurry. Time to modify `mygame/commands/mycommands.py` again. Let us break out the parsing
|
||||||
a little, in a new method `parse`:
|
a little, in a new method `parse`:
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -29,19 +29,19 @@ a little, in a new method `parse`:
|
||||||
class CmdHit(Command):
|
class CmdHit(Command):
|
||||||
"""
|
"""
|
||||||
Hit a target.
|
Hit a target.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
hit <target>
|
hit <target>
|
||||||
|
|
||||||
"""
|
"""
|
||||||
key = "hit"
|
key = "hit"
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
self.args = self.args.strip()
|
self.args = self.args.strip()
|
||||||
target, *weapon = self.args.split(" with ", 1)
|
target, *weapon = self.args.split(" with ", 1)
|
||||||
if not weapon:
|
if not weapon:
|
||||||
target, *weapon = target.split(" ", 1)
|
target, *weapon = target.split(" ", 1)
|
||||||
self.target = target.strip()
|
self.target = target.strip()
|
||||||
if weapon:
|
if weapon:
|
||||||
self.weapon = weapon.strip()
|
self.weapon = weapon.strip()
|
||||||
else:
|
else:
|
||||||
|
|
@ -50,162 +50,162 @@ class CmdHit(Command):
|
||||||
def func(self):
|
def func(self):
|
||||||
if not self.args:
|
if not self.args:
|
||||||
self.caller.msg("Who do you want to hit?")
|
self.caller.msg("Who do you want to hit?")
|
||||||
return
|
return
|
||||||
# get the target for the hit
|
# get the target for the hit
|
||||||
target = self.caller.search(self.target)
|
target = self.caller.search(self.target)
|
||||||
if not target:
|
if not target:
|
||||||
return
|
return
|
||||||
# get and handle the weapon
|
# get and handle the weapon
|
||||||
weapon = None
|
weapon = None
|
||||||
if self.weapon:
|
if self.weapon:
|
||||||
weapon = self.caller.search(self.weapon)
|
weapon = self.caller.search(self.weapon)
|
||||||
if weapon:
|
if weapon:
|
||||||
weaponstr = f"{weapon.key}"
|
weaponstr = f"{weapon.key}"
|
||||||
else:
|
else:
|
||||||
weaponstr = "bare fists"
|
weaponstr = "bare fists"
|
||||||
|
|
||||||
self.caller.msg(f"You hit {target.key} with {weaponstr}!")
|
self.caller.msg(f"You hit {target.key} with {weaponstr}!")
|
||||||
target.msg(f"You got hit by {self.caller.key} with {weaponstr}!")
|
target.msg(f"You got hit by {self.caller.key} with {weaponstr}!")
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The `parse` method is called before `func` and has access to all the same on-command variables as in `func`. Using
|
The `parse` method is called before `func` and has access to all the same on-command variables as in `func`. Using
|
||||||
`parse` not only makes things a little easier to read, it also means you can easily let other Commands _inherit_
|
`parse` not only makes things a little easier to read, it also means you can easily let other Commands _inherit_
|
||||||
your parsing - if you wanted some other Command to also understand input on the form `<arg> with <arg>` you'd inherit
|
your parsing - if you wanted some other Command to also understand input on the form `<arg> with <arg>` you'd inherit
|
||||||
from this class and just implement the `func` needed for that command without implementing `parse` anew.
|
from this class and just implement the `func` needed for that command without implementing `parse` anew.
|
||||||
|
|
||||||
```sidebar:: Tuples and Lists
|
```sidebar:: Tuples and Lists
|
||||||
|
|
||||||
- A `list` is written as `[a, b, c, d, ...]`. You can add and grow/shrink a list after it was first created.
|
- A `list` is written as `[a, b, c, d, ...]`. You can add and grow/shrink a list after it was first created.
|
||||||
- A `tuple` is written as `(a, b, c, d, ...)`. A tuple cannot be modified once it is created.
|
- A `tuple` is written as `(a, b, c, d, ...)`. A tuple cannot be modified once it is created.
|
||||||
|
|
||||||
```
|
```
|
||||||
- **Line 14** - We do the stripping of `self.args` once and for all here. We also store the stripped version back
|
- **Line 14** - We do the stripping of `self.args` once and for all here. We also store the stripped version back
|
||||||
into `self.args`, overwriting it. So there is no way to get back the non-stripped version from here on, which is fine
|
into `self.args`, overwriting it. So there is no way to get back the non-stripped version from here on, which is fine
|
||||||
for this command.
|
for this command.
|
||||||
- **Line 15** - This makes use of the `.split` method of strings. `.split` will, well, split the string by some criterion.
|
- **Line 15** - This makes use of the `.split` method of strings. `.split` will, well, split the string by some criterion.
|
||||||
`.split(" with ", 1)` means "split the string once, around the substring `" with "` if it exists". The result
|
`.split(" with ", 1)` means "split the string once, around the substring `" with "` if it exists". The result
|
||||||
of this split is a _list_. Just how that list looks depends on the string we are trying to split:
|
of this split is a _list_. Just how that list looks depends on the string we are trying to split:
|
||||||
1. If we entered just `hit smaug`, we'd be splitting just `"smaug"` which would give the result `["smaug"]`.
|
1. If we entered just `hit smaug`, we'd be splitting just `"smaug"` which would give the result `["smaug"]`.
|
||||||
2. `hit smaug sword` gives `["smaug sword"]`
|
2. `hit smaug sword` gives `["smaug sword"]`
|
||||||
3. `hit smaug with sword` gives `["smaug", "sword"]`
|
3. `hit smaug with sword` gives `["smaug", "sword"]`
|
||||||
|
|
||||||
So we get a list of 1 or 2 elements. We assign it to two variables like this, `target, *weapon = `. That
|
So we get a list of 1 or 2 elements. We assign it to two variables like this, `target, *weapon = `. That
|
||||||
asterisk in `*weapon` is a nifty trick - it will automatically become a list of _0 or more_ values. It sorts of
|
asterisk in `*weapon` is a nifty trick - it will automatically become a list of _0 or more_ values. It sorts of
|
||||||
"soaks" up everything left over.
|
"soaks" up everything left over.
|
||||||
1. `target` becomes `"smaug"` and `weapon` becomes `[]`
|
1. `target` becomes `"smaug"` and `weapon` becomes `[]`
|
||||||
2. `target` becomes `"smaug sword"` and `weapon` becomes `[]`
|
2. `target` becomes `"smaug sword"` and `weapon` becomes `[]`
|
||||||
3. `target` becomes `"smaug"` and `weapon` becomes `sword`
|
3. `target` becomes `"smaug"` and `weapon` becomes `sword`
|
||||||
- **Lines 16-17** - In this `if` condition we check if `weapon` is falsy (that is, the empty list). This can happen
|
- **Lines 16-17** - In this `if` condition we check if `weapon` is falsy (that is, the empty list). This can happen
|
||||||
under two conditions (from the example above):
|
under two conditions (from the example above):
|
||||||
1. `target` is simply `smaug`
|
1. `target` is simply `smaug`
|
||||||
2. `target` is `smaug sword`
|
2. `target` is `smaug sword`
|
||||||
|
|
||||||
To separate these cases we split `target` once again, this time by empty space `" "`. Again we store the
|
To separate these cases we split `target` once again, this time by empty space `" "`. Again we store the
|
||||||
result back with `target, *weapon =`. The result will be one of the following:
|
result back with `target, *weapon =`. The result will be one of the following:
|
||||||
1. `target` remains `smaug` and `weapon` remains `[]`
|
1. `target` remains `smaug` and `weapon` remains `[]`
|
||||||
2. `target` becomes `smaug` and `weapon` becomes `sword`
|
2. `target` becomes `smaug` and `weapon` becomes `sword`
|
||||||
- **Lines 18-22** - We now store `target` and `weapon` into `self.target` and `self.weapon`. We must do this in order
|
- **Lines 18-22** - We now store `target` and `weapon` into `self.target` and `self.weapon`. We must do this in order
|
||||||
for these local variables to made available in `func` later. Note how we need to check so `weapon` is not falsy
|
for these local variables to made available in `func` later. Note how we need to check so `weapon` is not falsy
|
||||||
before running `strip()` on it. This is because we know that if it's falsy, it's an empty list `[]` and lists
|
before running `strip()` on it. This is because we know that if it's falsy, it's an empty list `[]` and lists
|
||||||
don't have the `.strip()` method on them (so if we tried to use it, we'd get an error).
|
don't have the `.strip()` method on them (so if we tried to use it, we'd get an error).
|
||||||
|
|
||||||
Now onto the `func` method. The main difference is we now have `self.target` and `self.weapon` available for
|
Now onto the `func` method. The main difference is we now have `self.target` and `self.weapon` available for
|
||||||
convenient use.
|
convenient use.
|
||||||
- **Lines 29 and 35** - We make use of the previously parsed search terms for the target and weapon to find the
|
- **Lines 29 and 35** - We make use of the previously parsed search terms for the target and weapon to find the
|
||||||
respective resource.
|
respective resource.
|
||||||
- **Lines 34-39** - Since the weapon is optional, we need to supply a default (use our fists!) if it's not set. We
|
- **Lines 34-39** - 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 `weaponstr` that is different depending on if we have a weapon or not.
|
use this to create a `weaponstr` that is different depending on if we have a weapon or not.
|
||||||
- **Lines 41-42** - We merge the `weaponstr` with our attack text.
|
- **Lines 41-42** - We merge the `weaponstr` with our attack text.
|
||||||
|
|
||||||
Let's try it out!
|
Let's try it out!
|
||||||
|
|
||||||
> reload
|
> reload
|
||||||
> hit smaug with sword
|
> hit smaug with sword
|
||||||
Could not find 'sword'.
|
Could not find 'sword'.
|
||||||
You hit smaug with bare fists!
|
You hit smaug with bare fists!
|
||||||
|
|
||||||
Oops, our `self.caller.search(self.weapon)` is telling us that it found no sword. Since we are not `return`ing
|
|
||||||
in this situation (like we do if failing to find `target`) we still continue fighting with our bare hands.
|
|
||||||
This won't do. Let's make ourselves a sword.
|
|
||||||
|
|
||||||
> create sword
|
Oops, our `self.caller.search(self.weapon)` is telling us that it found no sword. Since we are not `return`ing
|
||||||
|
in this situation (like we do if failing to find `target`) we still continue fighting with our bare hands.
|
||||||
Since we didn't specify `/drop`, the sword will end up in our inventory and can seen with the `i` or
|
This won't do. Let's make ourselves a sword.
|
||||||
`inventory` command. The `.search` helper will still find it there. There is no need to reload to see this
|
|
||||||
|
> create sword
|
||||||
|
|
||||||
|
Since we didn't specify `/drop`, the sword will end up in our inventory and can seen with the `i` or
|
||||||
|
`inventory` command. The `.search` helper will still find it there. There is no need to reload to see this
|
||||||
change (no code changed, only stuff in the database).
|
change (no code changed, only stuff in the database).
|
||||||
|
|
||||||
> hit smaug with sword
|
> hit smaug with sword
|
||||||
You hit smaug with sword!
|
You hit smaug with sword!
|
||||||
|
|
||||||
|
|
||||||
## Adding a Command to an object
|
## Adding a Command to an object
|
||||||
|
|
||||||
The commands of a cmdset attached to an object with `obj.cmdset.add()` will by default be made available to that object
|
The commands of a cmdset attached to an object with `obj.cmdset.add()` will by default be made available to that object
|
||||||
but _also to those in the same location as that object_. If you did the [Building introduction](./Building-Quickstart)
|
but _also to those in the same location as that object_. If you did the [Building introduction](./Building-Quickstart)
|
||||||
you've seen an example of this with the "Red Button" object. The [Tutorial world](./Tutorial-World-Introduction)
|
you've seen an example of this with the "Red Button" object. The [Tutorial world](./Tutorial-World-Introduction)
|
||||||
also has many examples of objects with commands on them.
|
also has many examples of objects with commands on them.
|
||||||
|
|
||||||
To show how this could work, let's put our 'hit' Command on our simple `sword` object from the previous section.
|
To show how this could work, let's put our 'hit' Command on our simple `sword` object from the previous section.
|
||||||
|
|
||||||
> self.search("sword").cmdset.add("commands.mycommands.MyCmdSet", permanent=True)
|
> self.search("sword").cmdset.add("commands.mycommands.MyCmdSet", persistent=True)
|
||||||
|
|
||||||
We find the sword (it's still in our inventory so `self.search` should be able to find it), then
|
We find the sword (it's still in our inventory so `self.search` should be able to find it), then
|
||||||
add `MyCmdSet` to it. This actually adds both `hit` and `echo` to the sword, which is fine.
|
add `MyCmdSet` to it. This actually adds both `hit` and `echo` to the sword, which is fine.
|
||||||
|
|
||||||
Let's try to swing it!
|
Let's try to swing it!
|
||||||
|
|
||||||
> hit
|
> hit
|
||||||
More than one match for 'hit' (please narrow target):
|
More than one match for 'hit' (please narrow target):
|
||||||
hit-1 (sword #11)
|
hit-1 (sword #11)
|
||||||
hit-2
|
hit-2
|
||||||
|
|
||||||
```sidebar:: Multi-matches
|
```sidebar:: Multi-matches
|
||||||
|
|
||||||
Some game engines will just pick the first hit when finding more than one.
|
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
|
Evennia will always give you a choice. The reason for this is that Evennia
|
||||||
cannot know if `hit` and `hit` are different or the same - maybe it behaves
|
cannot know if `hit` and `hit` are different or the same - maybe it behaves
|
||||||
differently depending on the object it sits on? Besides, imagine if you had
|
differently depending on the object it sits on? Besides, imagine if you had
|
||||||
a red and a blue button both with the command `push` on it. Now you just write
|
a red and a blue button both with the command `push` on it. Now you just write
|
||||||
`push`. Wouldn't you prefer to be asked `which` button you really wanted to push?
|
`push`. Wouldn't you prefer to be asked `which` button you really wanted to push?
|
||||||
```
|
```
|
||||||
Woah, that didn't go as planned. Evennia actually found _two_ `hit` commands to didn't know which one to use
|
Woah, that didn't go as planned. Evennia actually found _two_ `hit` commands to didn't know which one to use
|
||||||
(_we_ know they are the same, but Evennia can't be sure of that). As we can see, `hit-1` is the one found on
|
(_we_ know they are the same, but Evennia can't be sure of that). As we can see, `hit-1` is the one found on
|
||||||
the sword. The other one is from adding `MyCmdSet` to ourself earlier. It's easy enough to tell Evennia which
|
the sword. The other one is from adding `MyCmdSet` to ourself earlier. It's easy enough to tell Evennia which
|
||||||
one you meant:
|
one you meant:
|
||||||
|
|
||||||
> hit-1
|
> hit-1
|
||||||
Who do you want to hit?
|
Who do you want to hit?
|
||||||
> hit-2
|
> hit-2
|
||||||
Who do you want to hit?
|
Who do you want to hit?
|
||||||
|
|
||||||
In this case we don't need both command-sets, so let's just keep the one on the sword:
|
In this case we don't need both command-sets, so let's just keep the one on the sword:
|
||||||
|
|
||||||
> self.cmdset.remove("commands.mycommands.MyCmdSet")
|
> self.cmdset.remove("commands.mycommands.MyCmdSet")
|
||||||
> hit
|
> hit
|
||||||
Who do you want to hit?
|
Who do you want to hit?
|
||||||
|
|
||||||
Now try this:
|
Now try this:
|
||||||
|
|
||||||
> tunnel n = kitchen
|
> tunnel n = kitchen
|
||||||
> n
|
> n
|
||||||
> drop sword
|
> drop sword
|
||||||
> s
|
> s
|
||||||
> hit
|
> hit
|
||||||
Command 'hit' is not available. Maybe you meant ...
|
Command 'hit' is not available. Maybe you meant ...
|
||||||
> n
|
> n
|
||||||
> hit
|
> hit
|
||||||
Who do you want to hit?
|
Who do you want to hit?
|
||||||
|
|
||||||
The `hit` command is now only available if you hold or are in the same room as the sword.
|
The `hit` command is now only available if you hold or are in the same room as the sword.
|
||||||
|
|
||||||
### You need to hold the sword!
|
### You need to hold the sword!
|
||||||
|
|
||||||
Let's get a little ahead of ourselves and make it so you have to _hold_ the sword for the `hit` command to
|
Let's get a little ahead of ourselves and make it so you have to _hold_ the sword for the `hit` command to
|
||||||
be available. This involves a _Lock_. We've cover locks in more detail later, just know that they are useful
|
be available. This involves a _Lock_. We've 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
|
for limiting the kind of things you can do with an object, including limiting just when you can call commands on
|
||||||
it.
|
it.
|
||||||
```sidebar:: Locks
|
```sidebar:: Locks
|
||||||
|
|
||||||
Evennia Locks are defined as a mini-language defined in `lockstrings`. The lockstring
|
Evennia Locks are defined as a mini-language defined in `lockstrings`. The lockstring
|
||||||
|
|
@ -215,49 +215,49 @@ it.
|
||||||
```
|
```
|
||||||
|
|
||||||
> py self.search("sword").locks.add("call:holds()")
|
> py self.search("sword").locks.add("call:holds()")
|
||||||
|
|
||||||
We added a new lock to the sword. The _lockstring_ `"call:holds()"` means that you can only _call_ commands on
|
We added a new lock to the sword. The _lockstring_ `"call:holds()"` means that you can only _call_ commands on
|
||||||
this object if you are _holding_ the object (that is, it's in your inventory).
|
this object if you are _holding_ the object (that is, it's in your inventory).
|
||||||
|
|
||||||
For locks to work, you cannot be _superuser_, since the superuser passes all locks. You need to `quell` yourself
|
For locks to work, you cannot be _superuser_, since the superuser passes all locks. You need to `quell` yourself
|
||||||
first:
|
first:
|
||||||
```sidebar:: quell/unquell
|
```sidebar:: quell/unquell
|
||||||
|
|
||||||
Quelling allows you as a developer to take on the role of players with less
|
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
|
priveleges. This is useful for testing and debugging, in particular since a
|
||||||
superuser has a little `too` much power sometimes.
|
superuser has a little `too` much power sometimes.
|
||||||
Use `unquell` to get back to your normal self.
|
Use `unquell` to get back to your normal self.
|
||||||
```
|
```
|
||||||
|
|
||||||
> quell
|
> quell
|
||||||
|
|
||||||
If the sword lies on the ground, try
|
If the sword lies on the ground, try
|
||||||
|
|
||||||
> hit
|
> hit
|
||||||
Command 'hit' is not available. ..
|
Command 'hit' is not available. ..
|
||||||
> get sword
|
> get sword
|
||||||
> hit
|
> hit
|
||||||
> Who do you want to hit?
|
> Who do you want to hit?
|
||||||
|
|
||||||
|
|
||||||
Finally, we get rid of ours sword so we have a clean slate with no more `hit` commands floating around.
|
Finally, we get rid of ours sword so we have a clean slate with no more `hit` commands floating around.
|
||||||
We can do that in two ways:
|
We can do that in two ways:
|
||||||
|
|
||||||
delete sword
|
delete sword
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
py self.search("sword").delete()
|
or
|
||||||
|
|
||||||
|
py self.search("sword").delete()
|
||||||
|
|
||||||
|
|
||||||
## Adding the Command to a default Cmdset
|
## Adding the Command to a default Cmdset
|
||||||
|
|
||||||
|
|
||||||
As we have seen we can use `obj.cmdset.add()` to add a new cmdset to objects, whether that object
|
As we have seen we can use `obj.cmdset.add()` to add a new cmdset to objects, whether that object
|
||||||
is ourself (`self`) or other objects like the `sword`.
|
is ourself (`self`) or other objects like the `sword`.
|
||||||
|
|
||||||
This is how all commands in Evennia work, including default commands like `look`, `dig`, `inventory` and so on.
|
This is how all commands in Evennia work, including default commands like `look`, `dig`, `inventory` and so on.
|
||||||
All these commands are in just loaded on the default objects that Evennia provides out of the box.
|
All these commands are in just loaded on the default objects that Evennia provides out of the box.
|
||||||
|
|
||||||
- Characters (that is 'you' in the gameworld) has the `CharacterCmdSet`.
|
- Characters (that is 'you' in the gameworld) has the `CharacterCmdSet`.
|
||||||
- Accounts (the thing that represents your out-of-character existence on the server) has the `AccountCmdSet`
|
- Accounts (the thing that represents your out-of-character existence on the server) has the `AccountCmdSet`
|
||||||
|
|
@ -266,7 +266,7 @@ All these commands are in just loaded on the default objects that Evennia provid
|
||||||
|
|
||||||
The thing must commonly modified is the `CharacterCmdSet`.
|
The thing must commonly modified is the `CharacterCmdSet`.
|
||||||
|
|
||||||
The default cmdset are defined in `mygame/commands/default_cmdsets.py`. Open that file now:
|
The default cmdset are defined in `mygame/commands/default_cmdsets.py`. Open that file now:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
"""
|
"""
|
||||||
|
|
@ -321,19 +321,19 @@ class SessionCmdSet(default_cmds.SessionCmdSet):
|
||||||
```
|
```
|
||||||
|
|
||||||
```sidebar:: super()
|
```sidebar:: super()
|
||||||
|
|
||||||
The `super()` function refers to the parent of the current class and is commonly
|
The `super()` function refers to the parent of the current class and is commonly
|
||||||
used to call same-named methods on the parent.
|
used to call same-named methods on the parent.
|
||||||
```
|
```
|
||||||
`evennia.default_cmds` is a container that holds all of Evennia's default commands and cmdsets. In this module
|
`evennia.default_cmds` 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
|
we can see that this was imported and then a new child class was made for each cmdset. Each class looks familiar
|
||||||
(except the `key`, that's mainly used to easily identify the cmdset in listings). In each `at_cmdset_creation` all
|
(except the `key`, that's mainly used to easily identify the cmdset in listings). In each `at_cmdset_creation` all
|
||||||
we do is call `super().at_cmdset_creation` which means that we call `at_cmdset_creation() on the _parent_ CmdSet.
|
we do is call `super().at_cmdset_creation` which means that we call `at_cmdset_creation() on the _parent_ CmdSet.
|
||||||
This is what adds all the default commands to each CmdSet.
|
This is what adds all the default commands to each CmdSet.
|
||||||
|
|
||||||
To add even more Commands to a default cmdset, we can just add them below the `super()` line. Usefully, if we were to
|
To add even more Commands to a default cmdset, we can just add them below the `super()` line. Usefully, if we were to
|
||||||
add a Command with the same `.key` as a default command, it would completely replace that original. So if you were
|
add a Command with the same `.key` as a default command, it would completely replace that original. So if you were
|
||||||
to add a command with a key `look`, the original `look` command would be replaced by your own version.
|
to add a command with a key `look`, the original `look` command would be replaced by your own version.
|
||||||
|
|
||||||
For now, let's add our own `hit` and `echo` commands to the `CharacterCmdSet`:
|
For now, let's add our own `hit` and `echo` commands to the `CharacterCmdSet`:
|
||||||
|
|
||||||
|
|
@ -358,9 +358,9 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> reload
|
> reload
|
||||||
> hit
|
> hit
|
||||||
Who do you want to hit?
|
Who do you want to hit?
|
||||||
|
|
||||||
Your new commands are now available for all player characters in the game. There is another way to add a bunch
|
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 a _CmdSet_ to the other cmdset. All commands in that cmdset will then be added:
|
of commands at once, and that is to add a _CmdSet_ to the other cmdset. All commands in that cmdset will then be added:
|
||||||
|
|
@ -381,19 +381,19 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||||
self.add(mycommands.MyCmdSet)
|
self.add(mycommands.MyCmdSet)
|
||||||
```
|
```
|
||||||
|
|
||||||
Which way you use depends on how much control you want, but if you already have a CmdSet,
|
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.
|
this is practical. A Command can be a part of any number of different CmdSets.
|
||||||
|
|
||||||
### Removing Commands
|
### Removing Commands
|
||||||
|
|
||||||
To remove your custom commands again, you of course just delete the change you did to
|
To remove your custom commands again, you of course just delete the change you did to
|
||||||
`mygame/commands/default_cmdsets.py`. But what if you want to remove a default command?
|
`mygame/commands/default_cmdsets.py`. But what if you want to remove a default command?
|
||||||
|
|
||||||
We already know that we use `cmdset.remove()` to remove a cmdset. It turns out you can
|
We already know that we use `cmdset.remove()` to remove a cmdset. It turns out you can
|
||||||
do the same in `at_cmdset_creation`. For example, let's remove the default `get` Command
|
do the same in `at_cmdset_creation`. For example, let's remove the default `get` Command
|
||||||
from Evennia. We happen to know this can be found as `default_cmds.CmdGet`.
|
from Evennia. We happen to know this can be found as `default_cmds.CmdGet`.
|
||||||
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# ...
|
# ...
|
||||||
from commands import mycommands
|
from commands import mycommands
|
||||||
|
|
@ -413,16 +413,16 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
> reload
|
> reload
|
||||||
> get
|
> get
|
||||||
Command 'get' is not available ...
|
Command 'get' is not available ...
|
||||||
|
|
||||||
## Replace a default command
|
## Replace a default command
|
||||||
|
|
||||||
At this point you already have all the pieces for how to do this! We just need to add a new
|
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 `key` in the `CharacterCmdSet` to replace the default one.
|
command with the same `key` in the `CharacterCmdSet` to replace the default one.
|
||||||
|
|
||||||
Let's combine this with what we know about classes and
|
Let's combine this with what we know about classes and
|
||||||
how to _override_ a parent class. Open `mygame/commands/mycommands.py` and lets override
|
how to _override_ a parent class. Open `mygame/commands/mycommands.py` and lets override
|
||||||
that `CmdGet` command.
|
that `CmdGet` command.
|
||||||
|
|
||||||
|
|
@ -430,7 +430,7 @@ that `CmdGet` command.
|
||||||
# up top, by the other imports
|
# up top, by the other imports
|
||||||
from evennia import default_cmds
|
from evennia import default_cmds
|
||||||
|
|
||||||
# somewhere below
|
# somewhere below
|
||||||
class MyCmdGet(default_cmds.CmdGet):
|
class MyCmdGet(default_cmds.CmdGet):
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
|
|
@ -440,20 +440,20 @@ class MyCmdGet(default_cmds.CmdGet):
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Line2**: We import `default_cmds` so we can get the parent class.
|
- **Line2**: We import `default_cmds` so we can get the parent class.
|
||||||
We made a new class and we make it _inherit_ `default_cmds.CmdGet`. We don't
|
We made a new class and we make it _inherit_ `default_cmds.CmdGet`. We don't
|
||||||
need to set `.key` or `.parse`, that's already handled by the parent.
|
need to set `.key` or `.parse`, that's already handled by the parent.
|
||||||
In `func` we call `super().func()` to let the parent do its normal thing,
|
In `func` we call `super().func()` to let the parent do its normal thing,
|
||||||
- **Line 7**: By adding our own `func` we replace the one in the parent.
|
- **Line 7**: By adding our own `func` we replace the one in the parent.
|
||||||
- **Line 8**: For this simple change we still want the command to work the
|
- **Line 8**: For this simple change we still want the command to work the
|
||||||
same as before, so we use `super()` to call `func` on the parent.
|
same as before, so we use `super()` to call `func` on the parent.
|
||||||
- **Line 9**: `.location` is the place an object is at. `.contents` contains, well, the
|
- **Line 9**: `.location` is the place an object is at. `.contents` contains, well, the
|
||||||
contents of an object. If you tried `py self.contents` you'd get a list that equals
|
contents of an object. If you tried `py self.contents` you'd get a list that equals
|
||||||
your inventory. For a room, the contents is everything in it.
|
your inventory. For a room, the contents is everything in it.
|
||||||
So `self.caller.location.contents` gets the contents of our current location. This is
|
So `self.caller.location.contents` gets the contents of our current location. This is
|
||||||
a _list_. In order send this to us with `.msg` we turn the list into a string. Python
|
a _list_. In order send this to us with `.msg` we turn the list into a string. Python
|
||||||
has a special function `str()` to do this.
|
has a special function `str()` to do this.
|
||||||
|
|
||||||
We now just have to add this so it replaces the default `get` command. Open
|
We now just have to add this so it replaces the default `get` command. Open
|
||||||
`mygame/commands/default_cmdsets.py` again:
|
`mygame/commands/default_cmdsets.py` again:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
@ -476,22 +476,22 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||||
```
|
```
|
||||||
```sidebar:: Another way
|
```sidebar:: Another way
|
||||||
|
|
||||||
Instead of adding `MyCmdGet` explicitly in default_cmdset.py,
|
Instead of adding `MyCmdGet` explicitly in default_cmdset.py,
|
||||||
you could also add it to `mycommands.MyCmdSet` and let it be
|
you could also add it to `mycommands.MyCmdSet` and let it be
|
||||||
added automatically for you.
|
added automatically for you.
|
||||||
```
|
```
|
||||||
|
|
||||||
> reload
|
> reload
|
||||||
> get
|
> get
|
||||||
Get What?
|
Get What?
|
||||||
[smaug, fluffy, YourName, ...]
|
[smaug, fluffy, YourName, ...]
|
||||||
|
|
||||||
We just made a new `get`-command that tells us everything we could pick up (well, we can't pick up ourselves, so
|
We just made a new `get`-command that tells us everything we could pick up (well, we can't pick up ourselves, so
|
||||||
there's some room for improvement there).
|
there's some room for improvement there).
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
In this lesson we got into some more advanced string formatting - many of those tricks will help you a lot in
|
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
|
the future! We also made a functional sword. Finally we got into how to add to, extend and replace a default
|
||||||
command on ourselves.
|
command on ourselves.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,8 @@ class Character(DefaultCharacter):
|
||||||
[...]
|
[...]
|
||||||
"""
|
"""
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"This is called when object is first created, only."
|
"This is called when object is first created, only."
|
||||||
self.db.power = 1
|
self.db.power = 1
|
||||||
self.db.combat_score = 1
|
self.db.combat_score = 1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -94,10 +94,10 @@ check it. Using this method however will make it easy to add more functionality
|
||||||
|
|
||||||
What we need are the following:
|
What we need are the following:
|
||||||
|
|
||||||
- One character generation [Command](../../../Components/Commands) to set the "Power" on the `Character`.
|
- One character generation [Command](../../../Components/Commands) to set the "Power" on the `Character`.
|
||||||
- A chargen [CmdSet](../../../Components/Command-Sets) to hold this command. Lets call it `ChargenCmdset`.
|
- A chargen [CmdSet](../../../Components/Command-Sets) to hold this command. Lets call it `ChargenCmdset`.
|
||||||
- A custom `ChargenRoom` type that makes this set of commands available to players in such rooms.
|
- A custom `ChargenRoom` type that makes this set of commands available to players in such rooms.
|
||||||
- One such room to test things in.
|
- One such room to test things in.
|
||||||
|
|
||||||
### The +setpower command
|
### The +setpower command
|
||||||
|
|
||||||
|
|
@ -114,7 +114,7 @@ Open `command.py` file. It contains documented empty templates for the base comm
|
||||||
`MuxCommand` class offers some extra features like stripping whitespace that may be useful - if so,
|
`MuxCommand` class offers some extra features like stripping whitespace that may be useful - if so,
|
||||||
just import from that instead.
|
just import from that instead.
|
||||||
|
|
||||||
Add the following to the end of the `command.py` file:
|
Add the following to the end of the `command.py` file:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# end of command.py
|
# end of command.py
|
||||||
|
|
@ -124,13 +124,13 @@ class CmdSetPower(Command):
|
||||||
"""
|
"""
|
||||||
set the power of a character
|
set the power of a character
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
+setpower <1-10>
|
+setpower <1-10>
|
||||||
|
|
||||||
This sets the power of the current character. This can only be
|
This sets the power of the current character. This can only be
|
||||||
used during character generation.
|
used during character generation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "+setpower"
|
key = "+setpower"
|
||||||
help_category = "mush"
|
help_category = "mush"
|
||||||
|
|
||||||
|
|
@ -138,10 +138,10 @@ class CmdSetPower(Command):
|
||||||
"This performs the actual command"
|
"This performs the actual command"
|
||||||
errmsg = "You must supply a number between 1 and 10."
|
errmsg = "You must supply a number between 1 and 10."
|
||||||
if not self.args:
|
if not self.args:
|
||||||
self.caller.msg(errmsg)
|
self.caller.msg(errmsg)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
power = int(self.args)
|
power = int(self.args)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.caller.msg(errmsg)
|
self.caller.msg(errmsg)
|
||||||
return
|
return
|
||||||
|
|
@ -180,7 +180,7 @@ class ChargenCmdset(CmdSet):
|
||||||
key = "Chargen"
|
key = "Chargen"
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"This is called at initialization"
|
"This is called at initialization"
|
||||||
self.add(command.CmdSetPower())
|
self.add(command.CmdSetPower())
|
||||||
```
|
```
|
||||||
|
|
||||||
In the future you can add any number of commands to this cmdset, to expand your character generation
|
In the future you can add any number of commands to this cmdset, to expand your character generation
|
||||||
|
|
@ -193,10 +193,10 @@ It's cleaner to put it on a room, so it's only available when players are in tha
|
||||||
We will create a simple Room typeclass to act as a template for all our Chargen areas. Edit
|
We will create a simple Room typeclass to act as a template for all our Chargen areas. Edit
|
||||||
`mygame/typeclasses/rooms.py` next:
|
`mygame/typeclasses/rooms.py` next:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from commands.default_cmdsets import ChargenCmdset
|
from commands.default_cmdsets import ChargenCmdset
|
||||||
|
|
||||||
# ...
|
# ...
|
||||||
# down at the end of rooms.py
|
# down at the end of rooms.py
|
||||||
|
|
||||||
class ChargenRoom(Room):
|
class ChargenRoom(Room):
|
||||||
|
|
@ -206,10 +206,10 @@ class ChargenRoom(Room):
|
||||||
"""
|
"""
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"this is called only at first creation"
|
"this is called only at first creation"
|
||||||
self.cmdset.add(ChargenCmdset, permanent=True)
|
self.cmdset.add(ChargenCmdset, persistent=True)
|
||||||
```
|
```
|
||||||
Note how new rooms created with this typeclass will always start with `ChargenCmdset` on themselves.
|
Note how new rooms created with this typeclass will always start with `ChargenCmdset` on themselves.
|
||||||
Don't forget the `permanent=True` keyword or you will lose the cmdset after a server reload. For
|
Don't forget the `persistent=True` keyword or you will lose the cmdset after a server reload. For
|
||||||
more information about [Command Sets](../../../Components/Command-Sets) and [Commands](../../../Components/Commands), see the respective
|
more information about [Command Sets](../../../Components/Command-Sets) and [Commands](../../../Components/Commands), see the respective
|
||||||
links.
|
links.
|
||||||
|
|
||||||
|
|
@ -235,7 +235,7 @@ as `chargen;character generation`.
|
||||||
So in summary, this will create a new room of type ChargenRoom and open an exit `chargen` to it and
|
So in summary, this will create a new room of type ChargenRoom and open an exit `chargen` to it and
|
||||||
an exit back here named `finish`. If you see errors at this stage, you must fix them in your code.
|
an exit back here named `finish`. If you see errors at this stage, you must fix them in your code.
|
||||||
`@reload`
|
`@reload`
|
||||||
between fixes. Don't continue until the creation seems to have worked okay.
|
between fixes. Don't continue until the creation seems to have worked okay.
|
||||||
|
|
||||||
chargen
|
chargen
|
||||||
|
|
||||||
|
|
@ -263,19 +263,19 @@ set during Character generation:
|
||||||
> +attack
|
> +attack
|
||||||
You +attack with a combat score of 12!
|
You +attack with a combat score of 12!
|
||||||
|
|
||||||
Go back to `mygame/commands/command.py` and add the command to the end like this:
|
Go back to `mygame/commands/command.py` and add the command to the end like this:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import random
|
import random
|
||||||
|
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
class CmdAttack(Command):
|
class CmdAttack(Command):
|
||||||
"""
|
"""
|
||||||
issues an attack
|
issues an attack
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
+attack
|
+attack
|
||||||
|
|
||||||
This will calculate a new combat score based on your Power.
|
This will calculate a new combat score based on your Power.
|
||||||
Your combat score is visible to everyone in the same location.
|
Your combat score is visible to everyone in the same location.
|
||||||
|
|
@ -288,8 +288,8 @@ class CmdAttack(Command):
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
power = caller.db.power
|
power = caller.db.power
|
||||||
if not power:
|
if not power:
|
||||||
# this can happen if caller is not of
|
# this can happen if caller is not of
|
||||||
# our custom Character typeclass
|
# our custom Character typeclass
|
||||||
power = 1
|
power = 1
|
||||||
combat_score = random.randint(1, 10 * power)
|
combat_score = random.randint(1, 10 * power)
|
||||||
caller.db.combat_score = combat_score
|
caller.db.combat_score = combat_score
|
||||||
|
|
@ -297,10 +297,10 @@ class CmdAttack(Command):
|
||||||
# announce
|
# announce
|
||||||
message = "%s +attack%s with a combat score of %s!"
|
message = "%s +attack%s with a combat score of %s!"
|
||||||
caller.msg(message % ("You", "", combat_score))
|
caller.msg(message % ("You", "", combat_score))
|
||||||
caller.location.msg_contents(message %
|
caller.location.msg_contents(message %
|
||||||
(caller.key, "s", combat_score),
|
(caller.key, "s", combat_score),
|
||||||
exclude=caller)
|
exclude=caller)
|
||||||
```
|
```
|
||||||
|
|
||||||
What we do here is simply to generate a "combat score" using Python's inbuilt `random.randint()`
|
What we do here is simply to generate a "combat score" using Python's inbuilt `random.randint()`
|
||||||
function. We then store that and echo the result to everyone involved.
|
function. We then store that and echo the result to everyone involved.
|
||||||
|
|
@ -349,8 +349,8 @@ class Character(DefaultCharacter):
|
||||||
[...]
|
[...]
|
||||||
"""
|
"""
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"This is called when object is first created, only."
|
"This is called when object is first created, only."
|
||||||
self.db.power = 1
|
self.db.power = 1
|
||||||
self.db.combat_score = 1
|
self.db.combat_score = 1
|
||||||
|
|
||||||
def return_appearance(self, looker):
|
def return_appearance(self, looker):
|
||||||
|
|
@ -395,16 +395,16 @@ instead put all relevant NPC commands in the default command set and limit event
|
||||||
|
|
||||||
### Creating an NPC with +createNPC
|
### Creating an NPC with +createNPC
|
||||||
|
|
||||||
We need a command for creating the NPC, this is a very straightforward command:
|
We need a command for creating the NPC, this is a very straightforward command:
|
||||||
|
|
||||||
> +createnpc Anna
|
> +createnpc Anna
|
||||||
You created the NPC 'Anna'.
|
You created the NPC 'Anna'.
|
||||||
|
|
||||||
At the end of `command.py`, create our new command:
|
At the end of `command.py`, create our new command:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from evennia import create_object
|
from evennia import create_object
|
||||||
|
|
||||||
class CmdCreateNPC(Command):
|
class CmdCreateNPC(Command):
|
||||||
"""
|
"""
|
||||||
create a new npc
|
create a new npc
|
||||||
|
|
@ -413,12 +413,12 @@ class CmdCreateNPC(Command):
|
||||||
+createNPC <name>
|
+createNPC <name>
|
||||||
|
|
||||||
Creates a new, named NPC. The NPC will start with a Power of 1.
|
Creates a new, named NPC. The NPC will start with a Power of 1.
|
||||||
"""
|
"""
|
||||||
key = "+createnpc"
|
key = "+createnpc"
|
||||||
aliases = ["+createNPC"]
|
aliases = ["+createNPC"]
|
||||||
locks = "call:not perm(nonpcs)"
|
locks = "call:not perm(nonpcs)"
|
||||||
help_category = "mush"
|
help_category = "mush"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"creates the object and names it"
|
"creates the object and names it"
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
|
@ -432,15 +432,15 @@ class CmdCreateNPC(Command):
|
||||||
# make name always start with capital letter
|
# make name always start with capital letter
|
||||||
name = self.args.strip().capitalize()
|
name = self.args.strip().capitalize()
|
||||||
# create npc in caller's location
|
# create npc in caller's location
|
||||||
npc = create_object("characters.Character",
|
npc = create_object("characters.Character",
|
||||||
key=name,
|
key=name,
|
||||||
location=caller.location,
|
location=caller.location,
|
||||||
locks="edit:id(%i) and perm(Builders);call:false()" % caller.id)
|
locks="edit:id(%i) and perm(Builders);call:false()" % caller.id)
|
||||||
# announce
|
# announce
|
||||||
message = "%s created the NPC '%s'."
|
message = "%s created the NPC '%s'."
|
||||||
caller.msg(message % ("You", name))
|
caller.msg(message % ("You", name))
|
||||||
caller.location.msg_contents(message % (caller.key, name),
|
caller.location.msg_contents(message % (caller.key, name),
|
||||||
exclude=caller)
|
exclude=caller)
|
||||||
```
|
```
|
||||||
Here we define a `+createnpc` (`+createNPC` works too) that is callable by everyone *not* having the
|
Here we define a `+createnpc` (`+createNPC` works too) that is callable by everyone *not* having the
|
||||||
`nonpcs` "[permission](../../../Components/Locks#Permissions)" (in Evennia, a "permission" can just as well be used to
|
`nonpcs` "[permission](../../../Components/Locks#Permissions)" (in Evennia, a "permission" can just as well be used to
|
||||||
|
|
@ -475,38 +475,38 @@ principle re-work our old `+setpower` command, but let's try something more usef
|
||||||
`+editNPC` command.
|
`+editNPC` command.
|
||||||
|
|
||||||
> +editNPC Anna/power = 10
|
> +editNPC Anna/power = 10
|
||||||
Set Anna's property 'power' to 10.
|
Set Anna's property 'power' to 10.
|
||||||
|
|
||||||
This is a slightly more complex command. It goes at the end of your `command.py` file as before.
|
This is a slightly more complex command. It goes at the end of your `command.py` file as before.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class CmdEditNPC(Command):
|
class CmdEditNPC(Command):
|
||||||
"""
|
"""
|
||||||
edit an existing NPC
|
edit an existing NPC
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
+editnpc <name>[/<attribute> [= value]]
|
+editnpc <name>[/<attribute> [= value]]
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
+editnpc mynpc/power = 5
|
+editnpc mynpc/power = 5
|
||||||
+editnpc mynpc/power - displays power value
|
+editnpc mynpc/power - displays power value
|
||||||
+editnpc mynpc - shows all editable
|
+editnpc mynpc - shows all editable
|
||||||
attributes and values
|
attributes and values
|
||||||
|
|
||||||
This command edits an existing NPC. You must have
|
This command edits an existing NPC. You must have
|
||||||
permission to edit the NPC to use this.
|
permission to edit the NPC to use this.
|
||||||
"""
|
"""
|
||||||
key = "+editnpc"
|
key = "+editnpc"
|
||||||
aliases = ["+editNPC"]
|
aliases = ["+editNPC"]
|
||||||
locks = "cmd:not perm(nonpcs)"
|
locks = "cmd:not perm(nonpcs)"
|
||||||
help_category = "mush"
|
help_category = "mush"
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
"We need to do some parsing here"
|
"We need to do some parsing here"
|
||||||
args = self.args
|
args = self.args
|
||||||
propname, propval = None, None
|
propname, propval = None, None
|
||||||
if "=" in args:
|
if "=" in args:
|
||||||
args, propval = [part.strip() for part in args.rsplit("=", 1)]
|
args, propval = [part.strip() for part in args.rsplit("=", 1)]
|
||||||
if "/" in args:
|
if "/" in args:
|
||||||
args, propname = [part.strip() for part in args.rsplit("/", 1)]
|
args, propname = [part.strip() for part in args.rsplit("/", 1)]
|
||||||
# store, so we can access it below in func()
|
# store, so we can access it below in func()
|
||||||
|
|
@ -519,38 +519,38 @@ class CmdEditNPC(Command):
|
||||||
"do the editing"
|
"do the editing"
|
||||||
|
|
||||||
allowed_propnames = ("power", "attribute1", "attribute2")
|
allowed_propnames = ("power", "attribute1", "attribute2")
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
if not self.args or not self.name:
|
if not self.args or not self.name:
|
||||||
caller.msg("Usage: +editnpc name[/propname][=propval]")
|
caller.msg("Usage: +editnpc name[/propname][=propval]")
|
||||||
return
|
return
|
||||||
npc = caller.search(self.name)
|
npc = caller.search(self.name)
|
||||||
if not npc:
|
if not npc:
|
||||||
return
|
return
|
||||||
if not npc.access(caller, "edit"):
|
if not npc.access(caller, "edit"):
|
||||||
caller.msg("You cannot change this NPC.")
|
caller.msg("You cannot change this NPC.")
|
||||||
return
|
return
|
||||||
if not self.propname:
|
if not self.propname:
|
||||||
# this means we just list the values
|
# this means we just list the values
|
||||||
output = "Properties of %s:" % npc.key
|
output = "Properties of %s:" % npc.key
|
||||||
for propname in allowed_propnames:
|
for propname in allowed_propnames:
|
||||||
propvalue = npc.attributes.get(propname, default="N/A")
|
propvalue = npc.attributes.get(propname, default="N/A")
|
||||||
output += "\n %s = %s" % (propname, propvalue)
|
output += "\n %s = %s" % (propname, propvalue)
|
||||||
caller.msg(output)
|
caller.msg(output)
|
||||||
elif self.propname not in allowed_propnames:
|
elif self.propname not in allowed_propnames:
|
||||||
caller.msg("You may only change %s." %
|
caller.msg("You may only change %s." %
|
||||||
", ".join(allowed_propnames))
|
", ".join(allowed_propnames))
|
||||||
elif self.propval:
|
elif self.propval:
|
||||||
# assigning a new propvalue
|
# assigning a new propvalue
|
||||||
# in this example, the properties are all integers...
|
# in this example, the properties are all integers...
|
||||||
intpropval = int(self.propval)
|
intpropval = int(self.propval)
|
||||||
npc.attributes.add(self.propname, intpropval)
|
npc.attributes.add(self.propname, intpropval)
|
||||||
caller.msg("Set %s's property '%s' to %s" %
|
caller.msg("Set %s's property '%s' to %s" %
|
||||||
(npc.key, self.propname, self.propval))
|
(npc.key, self.propname, self.propval))
|
||||||
else:
|
else:
|
||||||
# propname set, but not propval - show current value
|
# propname set, but not propval - show current value
|
||||||
caller.msg("%s has property %s = %s" %
|
caller.msg("%s has property %s = %s" %
|
||||||
(npc.key, self.propname,
|
(npc.key, self.propname,
|
||||||
npc.attributes.get(self.propname, default="N/A")))
|
npc.attributes.get(self.propname, default="N/A")))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -559,7 +559,7 @@ checking. It searches for the given npc in the same room, and checks so the call
|
||||||
permission to "edit" it before continuing. An account without the proper permission won't even be
|
permission to "edit" it before continuing. An account without the proper permission won't even be
|
||||||
able to view the properties on the given NPC. It's up to each game if this is the way it should be.
|
able to view the properties on the given NPC. It's up to each game if this is the way it should be.
|
||||||
|
|
||||||
Add this to the default command set like before and you should be able to try it out.
|
Add this to the default command set like before and you should be able to try it out.
|
||||||
|
|
||||||
_Note: If you wanted a player to use this command to change an on-object property like the NPC's
|
_Note: If you wanted a player to use this command to change an on-object property like the NPC's
|
||||||
name (the `key` property), you'd need to modify the command since "key" is not an Attribute (it is
|
name (the `key` property), you'd need to modify the command since "key" is not an Attribute (it is
|
||||||
|
|
@ -587,11 +587,11 @@ class CmdNPC(Command):
|
||||||
"""
|
"""
|
||||||
controls an NPC
|
controls an NPC
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
+npc <name> = <command>
|
+npc <name> = <command>
|
||||||
|
|
||||||
This causes the npc to perform a command as itself. It will do so
|
This causes the npc to perform a command as itself. It will do so
|
||||||
with its own permissions and accesses.
|
with its own permissions and accesses.
|
||||||
"""
|
"""
|
||||||
key = "+npc"
|
key = "+npc"
|
||||||
locks = "call:not perm(nonpcs)"
|
locks = "call:not perm(nonpcs)"
|
||||||
|
|
@ -601,7 +601,7 @@ class CmdNPC(Command):
|
||||||
"Simple split of the = sign"
|
"Simple split of the = sign"
|
||||||
name, cmdname = None, None
|
name, cmdname = None, None
|
||||||
if "=" in self.args:
|
if "=" in self.args:
|
||||||
name, cmdname = [part.strip()
|
name, cmdname = [part.strip()
|
||||||
for part in self.args.rsplit("=", 1)]
|
for part in self.args.rsplit("=", 1)]
|
||||||
self.name, self.cmdname = name, cmdname
|
self.name, self.cmdname = name, cmdname
|
||||||
|
|
||||||
|
|
@ -611,7 +611,7 @@ class CmdNPC(Command):
|
||||||
if not self.cmdname:
|
if not self.cmdname:
|
||||||
caller.msg("Usage: +npc <name> = <command>")
|
caller.msg("Usage: +npc <name> = <command>")
|
||||||
return
|
return
|
||||||
npc = caller.search(self.name)
|
npc = caller.search(self.name)
|
||||||
if not npc:
|
if not npc:
|
||||||
return
|
return
|
||||||
if not npc.access(caller, "edit"):
|
if not npc.access(caller, "edit"):
|
||||||
|
|
@ -651,4 +651,4 @@ specific player (or npc) and automatically compare their relevant attributes to
|
||||||
|
|
||||||
To continue from here, you can take a look at the [Tutorial World](../Part1/Tutorial-World-Introduction). For
|
To continue from here, you can take a look at the [Tutorial World](../Part1/Tutorial-World-Introduction). For
|
||||||
more specific ideas, see the [other tutorials and hints](../../Howto-Overview) as well
|
more specific ideas, see the [other tutorials and hints](../../Howto-Overview) as well
|
||||||
as the [Evennia Component overview](../../../Components/Components-Overview).
|
as the [Evennia Component overview](../../../Components/Components-Overview).
|
||||||
|
|
|
||||||
|
|
@ -847,7 +847,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Deletes the account permanently.
|
Deletes the account persistently.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
`*args` and `**kwargs` are passed on to the base delete
|
`*args` and `**kwargs` are passed on to the base delete
|
||||||
|
|
@ -1196,7 +1196,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
self.locks.add(lockstring)
|
self.locks.add(lockstring)
|
||||||
|
|
||||||
# The ooc account cmdset
|
# The ooc account cmdset
|
||||||
self.cmdset.add_default(_CMDSET_ACCOUNT, permanent=True)
|
self.cmdset.add_default(_CMDSET_ACCOUNT, persistent=True)
|
||||||
|
|
||||||
def at_account_creation(self):
|
def at_account_creation(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ class CmdSet(object, metaclass=_CmdSetMeta):
|
||||||
# merge-stack, every cmdset in the stack must have `duplicates` set explicitly.
|
# merge-stack, every cmdset in the stack must have `duplicates` set explicitly.
|
||||||
duplicates = None
|
duplicates = None
|
||||||
|
|
||||||
permanent = False
|
persistent = False
|
||||||
key_mergetypes = {}
|
key_mergetypes = {}
|
||||||
errmessage = ""
|
errmessage = ""
|
||||||
# pre-store properties to duplicate straight off
|
# pre-store properties to duplicate straight off
|
||||||
|
|
@ -187,7 +187,7 @@ class CmdSet(object, metaclass=_CmdSetMeta):
|
||||||
"no_exits",
|
"no_exits",
|
||||||
"no_objs",
|
"no_objs",
|
||||||
"no_channels",
|
"no_channels",
|
||||||
"permanent",
|
"persistent",
|
||||||
"mergetype",
|
"mergetype",
|
||||||
"priority",
|
"priority",
|
||||||
"duplicates",
|
"duplicates",
|
||||||
|
|
@ -357,7 +357,7 @@ class CmdSet(object, metaclass=_CmdSetMeta):
|
||||||
commands (str): Representation of commands in Cmdset.
|
commands (str): Representation of commands in Cmdset.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
perm = "perm" if self.permanent else "non-perm"
|
perm = "perm" if self.persistent else "non-perm"
|
||||||
options = ", ".join([
|
options = ", ".join([
|
||||||
"{}:{}".format(opt, "T" if getattr(self, opt) else "F")
|
"{}:{}".format(opt, "T" if getattr(self, opt) else "F")
|
||||||
for opt in ("no_exits", "no_objs", "no_channels", "duplicates")
|
for opt in ("no_exits", "no_objs", "no_channels", "duplicates")
|
||||||
|
|
|
||||||
|
|
@ -305,7 +305,7 @@ class CmdSetHandler(object):
|
||||||
self.mergetype_stack = ["Union"]
|
self.mergetype_stack = ["Union"]
|
||||||
|
|
||||||
# the subset of the cmdset_paths that are to be stored in the database
|
# the subset of the cmdset_paths that are to be stored in the database
|
||||||
self.permanent_paths = [""]
|
self.persistent_paths = [""]
|
||||||
|
|
||||||
if init_true:
|
if init_true:
|
||||||
self.update(init_mode=True) # is then called from the object __init__.
|
self.update(init_mode=True) # is then called from the object __init__.
|
||||||
|
|
@ -363,7 +363,7 @@ class CmdSetHandler(object):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
init_mode (bool, optional): Used automatically right after
|
init_mode (bool, optional): Used automatically right after
|
||||||
this handler was created; it imports all permanent cmdsets
|
this handler was created; it imports all persistent cmdsets
|
||||||
from the database.
|
from the database.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
@ -378,7 +378,7 @@ class CmdSetHandler(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if init_mode:
|
if init_mode:
|
||||||
# reimport all permanent cmdsets
|
# reimport all persistent cmdsets
|
||||||
storage = self.obj.cmdset_storage
|
storage = self.obj.cmdset_storage
|
||||||
if storage:
|
if storage:
|
||||||
self.cmdset_stack = []
|
self.cmdset_stack = []
|
||||||
|
|
@ -408,7 +408,7 @@ class CmdSetHandler(object):
|
||||||
if _IN_GAME_ERRORS:
|
if _IN_GAME_ERRORS:
|
||||||
self.obj.msg(err)
|
self.obj.msg(err)
|
||||||
continue
|
continue
|
||||||
cmdset.permanent = cmdset.key != "_CMDSET_ERROR"
|
cmdset.persistent = cmdset.key != "_CMDSET_ERROR"
|
||||||
self.cmdset_stack.append(cmdset)
|
self.cmdset_stack.append(cmdset)
|
||||||
|
|
||||||
# merge the stack into a new merged cmdset
|
# merge the stack into a new merged cmdset
|
||||||
|
|
@ -423,7 +423,7 @@ class CmdSetHandler(object):
|
||||||
self.mergetype_stack.append(new_current.actual_mergetype)
|
self.mergetype_stack.append(new_current.actual_mergetype)
|
||||||
self.current = new_current
|
self.current = new_current
|
||||||
|
|
||||||
def add(self, cmdset, emit_to_obj=None, persistent=True, default_cmdset=False,
|
def add(self, cmdset, emit_to_obj=None, persistent=False, default_cmdset=False,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
Add a cmdset to the handler, on top of the old ones, unless it
|
Add a cmdset to the handler, on top of the old ones, unless it
|
||||||
|
|
@ -465,7 +465,7 @@ class CmdSetHandler(object):
|
||||||
# this is (maybe) a python path. Try to import from cache.
|
# this is (maybe) a python path. Try to import from cache.
|
||||||
cmdset = self._import_cmdset(cmdset)
|
cmdset = self._import_cmdset(cmdset)
|
||||||
if cmdset and cmdset.key != "_CMDSET_ERROR":
|
if cmdset and cmdset.key != "_CMDSET_ERROR":
|
||||||
cmdset.permanent = persistent # TODO change on cmdset too
|
cmdset.persistent = persistent
|
||||||
if persistent and cmdset.key != "_CMDSET_ERROR":
|
if persistent and cmdset.key != "_CMDSET_ERROR":
|
||||||
# store the path permanently
|
# store the path permanently
|
||||||
storage = self.obj.cmdset_storage or [""]
|
storage = self.obj.cmdset_storage or [""]
|
||||||
|
|
@ -514,7 +514,7 @@ class CmdSetHandler(object):
|
||||||
# remove the default cmdset only
|
# remove the default cmdset only
|
||||||
if self.cmdset_stack:
|
if self.cmdset_stack:
|
||||||
cmdset = self.cmdset_stack[0]
|
cmdset = self.cmdset_stack[0]
|
||||||
if cmdset.permanent:
|
if cmdset.persistent:
|
||||||
storage = self.obj.cmdset_storage or [""]
|
storage = self.obj.cmdset_storage or [""]
|
||||||
storage[0] = ""
|
storage[0] = ""
|
||||||
self.obj.cmdset_storage = storage
|
self.obj.cmdset_storage = storage
|
||||||
|
|
@ -531,7 +531,7 @@ class CmdSetHandler(object):
|
||||||
if not cmdset:
|
if not cmdset:
|
||||||
# remove the last one in the stack
|
# remove the last one in the stack
|
||||||
cmdset = self.cmdset_stack.pop()
|
cmdset = self.cmdset_stack.pop()
|
||||||
if cmdset.permanent:
|
if cmdset.persistent:
|
||||||
storage = self.obj.cmdset_storage
|
storage = self.obj.cmdset_storage
|
||||||
storage.pop()
|
storage.pop()
|
||||||
self.obj.cmdset_storage = storage
|
self.obj.cmdset_storage = storage
|
||||||
|
|
@ -548,12 +548,12 @@ class CmdSetHandler(object):
|
||||||
]
|
]
|
||||||
storage = []
|
storage = []
|
||||||
|
|
||||||
if any(cset.permanent for cset in delcmdsets):
|
if any(cset.persistent for cset in delcmdsets):
|
||||||
# only hit database if there's need to
|
# only hit database if there's need to
|
||||||
storage = self.obj.cmdset_storage
|
storage = self.obj.cmdset_storage
|
||||||
updated = False
|
updated = False
|
||||||
for cset in delcmdsets:
|
for cset in delcmdsets:
|
||||||
if cset.permanent:
|
if cset.persistent:
|
||||||
try:
|
try:
|
||||||
storage.remove(cset.path)
|
storage.remove(cset.path)
|
||||||
updated = True
|
updated = True
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,7 @@ def _menu_savefunc(caller, buf):
|
||||||
def _menu_quitfunc(caller):
|
def _menu_quitfunc(caller):
|
||||||
caller.cmdset.add(
|
caller.cmdset.add(
|
||||||
BuildingMenuCmdSet,
|
BuildingMenuCmdSet,
|
||||||
permanent=caller.ndb._building_menu and caller.ndb._building_menu.persistent or False,
|
persistent=caller.ndb._building_menu and caller.ndb._building_menu.persistent or False,
|
||||||
)
|
)
|
||||||
if caller.ndb._building_menu:
|
if caller.ndb._building_menu:
|
||||||
caller.ndb._building_menu.move(back=True)
|
caller.ndb._building_menu.move(back=True)
|
||||||
|
|
@ -951,7 +951,7 @@ class BuildingMenu(object):
|
||||||
if caller.cmdset.has(BuildingMenuCmdSet):
|
if caller.cmdset.has(BuildingMenuCmdSet):
|
||||||
caller.cmdset.remove(BuildingMenuCmdSet)
|
caller.cmdset.remove(BuildingMenuCmdSet)
|
||||||
|
|
||||||
self.caller.cmdset.add(BuildingMenuCmdSet, permanent=self.persistent)
|
self.caller.cmdset.add(BuildingMenuCmdSet, persistent=self.persistent)
|
||||||
self.display()
|
self.display()
|
||||||
|
|
||||||
def open_parent_menu(self):
|
def open_parent_menu(self):
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ class EvscapeRoom(EvscaperoomObject, DefaultRoom):
|
||||||
"total_achievements": 14,
|
"total_achievements": 14,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cmdset.add(CmdSetEvScapeRoom, permanent=True)
|
self.cmdset.add(CmdSetEvScapeRoom, persistent=True)
|
||||||
|
|
||||||
self.log("Room created and log started.")
|
self.log("Room created and log started.")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1550,7 +1550,7 @@ class ContribRPCharacter(DefaultCharacter, ContribRPObject):
|
||||||
self.db._recog_obj2regex = {}
|
self.db._recog_obj2regex = {}
|
||||||
self.db._recog_obj2recog = {}
|
self.db._recog_obj2recog = {}
|
||||||
|
|
||||||
self.cmdset.add(RPSystemCmdSet, permanent=True)
|
self.cmdset.add(RPSystemCmdSet, persistent=True)
|
||||||
# initializing sdesc
|
# initializing sdesc
|
||||||
self.sdesc.add("A normal person")
|
self.sdesc.add("A normal person")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,4 +130,4 @@ class TalkingNPC(DefaultObject):
|
||||||
"This is called when object is first created."
|
"This is called when object is first created."
|
||||||
self.db.desc = "This is a talkative NPC."
|
self.db.desc = "This is a talkative NPC."
|
||||||
# assign the talk command to npc
|
# assign the talk command to npc
|
||||||
self.cmdset.add_default(TalkingCmdSet, permanent=True)
|
self.cmdset.add_default(TalkingCmdSet, persistent=True)
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ class Mob(tut_objects.TutorialObject):
|
||||||
Called the first time the object is created.
|
Called the first time the object is created.
|
||||||
We set up the base properties and flags here.
|
We set up the base properties and flags here.
|
||||||
"""
|
"""
|
||||||
self.cmdset.add(MobCmdSet, permanent=True)
|
self.cmdset.add(MobCmdSet, persistent=True)
|
||||||
# Main AI flags. We start in dead mode so we don't have to
|
# Main AI flags. We start in dead mode so we don't have to
|
||||||
# chase the mob around when building.
|
# chase the mob around when building.
|
||||||
self.db.patrolling = True
|
self.db.patrolling = True
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ class TutorialReadable(TutorialObject):
|
||||||
)
|
)
|
||||||
self.db.readable_text = "There is no text written on %s." % self.key
|
self.db.readable_text = "There is no text written on %s." % self.key
|
||||||
# define a command on the object.
|
# define a command on the object.
|
||||||
self.cmdset.add_default(CmdSetReadable, permanent=True)
|
self.cmdset.add_default(CmdSetReadable, persistent=True)
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
@ -195,7 +195,7 @@ class TutorialClimbable(TutorialObject):
|
||||||
|
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"""Called at initial creation only"""
|
"""Called at initial creation only"""
|
||||||
self.cmdset.add_default(CmdSetClimbable, permanent=True)
|
self.cmdset.add_default(CmdSetClimbable, persistent=True)
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
@ -343,7 +343,7 @@ class LightSource(TutorialObject):
|
||||||
# when created.
|
# when created.
|
||||||
self.db.desc = "A splinter of wood with remnants of resin on it, enough for burning."
|
self.db.desc = "A splinter of wood with remnants of resin on it, enough for burning."
|
||||||
# add the Light command
|
# add the Light command
|
||||||
self.cmdset.add_default(CmdSetLight, permanent=True)
|
self.cmdset.add_default(CmdSetLight, persistent=True)
|
||||||
|
|
||||||
def _burnout(self):
|
def _burnout(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -670,7 +670,7 @@ class CrumblingWall(TutorialObject, DefaultExit):
|
||||||
# exit_open is set to True.
|
# exit_open is set to True.
|
||||||
self.locks.add("cmd:locattr(is_lit);traverse:objattr(exit_open)")
|
self.locks.add("cmd:locattr(is_lit);traverse:objattr(exit_open)")
|
||||||
# set cmdset
|
# set cmdset
|
||||||
self.cmdset.add(CmdSetCrumblingWall, permanent=True)
|
self.cmdset.add(CmdSetCrumblingWall, persistent=True)
|
||||||
|
|
||||||
def open_wall(self):
|
def open_wall(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -950,7 +950,7 @@ class TutorialWeapon(TutorialObject):
|
||||||
self.db.parry = 0.8 # parry chance
|
self.db.parry = 0.8 # parry chance
|
||||||
self.db.damage = 1.0
|
self.db.damage = 1.0
|
||||||
self.db.magic = False
|
self.db.magic = False
|
||||||
self.cmdset.add_default(CmdSetWeapon, permanent=True)
|
self.cmdset.add_default(CmdSetWeapon, persistent=True)
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1148,7 +1148,7 @@ class TutorialWeaponRack(TutorialObject):
|
||||||
"""
|
"""
|
||||||
called at creation
|
called at creation
|
||||||
"""
|
"""
|
||||||
self.cmdset.add_default(CmdSetWeaponRack, permanent=True)
|
self.cmdset.add_default(CmdSetWeaponRack, persistent=True)
|
||||||
self.db.rack_id = "weaponrack_1"
|
self.db.rack_id = "weaponrack_1"
|
||||||
# these are prototype names from the prototype
|
# these are prototype names from the prototype
|
||||||
# dictionary above.
|
# dictionary above.
|
||||||
|
|
|
||||||
|
|
@ -429,7 +429,7 @@ class IntroRoom(TutorialRoom):
|
||||||
"This assigns the health Attribute to "
|
"This assigns the health Attribute to "
|
||||||
"the account."
|
"the account."
|
||||||
)
|
)
|
||||||
self.cmdset.add(CmdSetEvenniaIntro, permanent=True)
|
self.cmdset.add(CmdSetEvenniaIntro, persistent=True)
|
||||||
|
|
||||||
def at_object_receive(self, character, source_location):
|
def at_object_receive(self, character, source_location):
|
||||||
"""
|
"""
|
||||||
|
|
@ -732,7 +732,7 @@ class BridgeRoom(WeatherRoom):
|
||||||
self.db.east_exit = "gate"
|
self.db.east_exit = "gate"
|
||||||
self.db.fall_exit = "cliffledge"
|
self.db.fall_exit = "cliffledge"
|
||||||
# add the cmdset on the room.
|
# add the cmdset on the room.
|
||||||
self.cmdset.add(BridgeCmdSet, permanent=True)
|
self.cmdset.add(BridgeCmdSet, persistent=True)
|
||||||
# since the default Character's at_look() will access the room's
|
# since the default Character's at_look() will access the room's
|
||||||
# return_description (this skips the cmdset) when
|
# return_description (this skips the cmdset) when
|
||||||
# first entering it, we need to explicitly turn off the room
|
# first entering it, we need to explicitly turn off the room
|
||||||
|
|
@ -957,7 +957,7 @@ class DarkRoom(TutorialRoom):
|
||||||
self.db.tutorial_info = "This is a room with custom command sets on itself."
|
self.db.tutorial_info = "This is a room with custom command sets on itself."
|
||||||
# the room starts dark.
|
# the room starts dark.
|
||||||
self.db.is_lit = False
|
self.db.is_lit = False
|
||||||
self.cmdset.add(DarkCmdSet, permanent=True)
|
self.cmdset.add(DarkCmdSet, persistent=True)
|
||||||
|
|
||||||
def at_init(self):
|
def at_init(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1009,7 +1009,7 @@ class DarkRoom(TutorialRoom):
|
||||||
# noone is carrying light - darken the room
|
# noone is carrying light - darken the room
|
||||||
self.db.is_lit = False
|
self.db.is_lit = False
|
||||||
self.locks.add("view:false()")
|
self.locks.add("view:false()")
|
||||||
self.cmdset.add(DarkCmdSet, permanent=True)
|
self.cmdset.add(DarkCmdSet, persistent=True)
|
||||||
for char in (obj for obj in self.contents if obj.has_account):
|
for char in (obj for obj in self.contents if obj.has_account):
|
||||||
if char.is_superuser:
|
if char.is_superuser:
|
||||||
char.msg("You are Superuser, so you are not affected by the dark state.")
|
char.msg("You are Superuser, so you are not affected by the dark state.")
|
||||||
|
|
|
||||||
|
|
@ -1310,7 +1310,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
||||||
|
|
||||||
def at_object_delete(self):
|
def at_object_delete(self):
|
||||||
"""
|
"""
|
||||||
Called just before the database object is permanently
|
Called just before the database object is persistently
|
||||||
delete()d from the database. If this method returns False,
|
delete()d from the database. If this method returns False,
|
||||||
deletion is aborted.
|
deletion is aborted.
|
||||||
|
|
||||||
|
|
@ -2261,7 +2261,7 @@ class DefaultCharacter(DefaultObject):
|
||||||
";".join(["get:false()", "call:false()"]) # noone can pick up the character
|
";".join(["get:false()", "call:false()"]) # noone can pick up the character
|
||||||
) # no commands can be called on character from outside
|
) # no commands can be called on character from outside
|
||||||
# add the default cmdset
|
# add the default cmdset
|
||||||
self.cmdset.add_default(settings.CMDSET_CHARACTER, permanent=True)
|
self.cmdset.add_default(settings.CMDSET_CHARACTER, persistent=True)
|
||||||
|
|
||||||
def at_after_move(self, source_location, **kwargs):
|
def at_after_move(self, source_location, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -2711,7 +2711,7 @@ class DefaultExit(DefaultObject):
|
||||||
|
|
||||||
if "force_init" in kwargs or not self.cmdset.has_cmdset("ExitCmdSet", must_be_default=True):
|
if "force_init" in kwargs or not self.cmdset.has_cmdset("ExitCmdSet", must_be_default=True):
|
||||||
# we are resetting, or no exit-cmdset was set. Create one dynamically.
|
# we are resetting, or no exit-cmdset was set. Create one dynamically.
|
||||||
self.cmdset.add_default(self.create_exit_cmdset(self), permanent=False)
|
self.cmdset.add_default(self.create_exit_cmdset(self), persistent=False)
|
||||||
|
|
||||||
def at_init(self):
|
def at_init(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -893,7 +893,7 @@ class EvEditor:
|
||||||
persistent = False
|
persistent = False
|
||||||
|
|
||||||
# Create the commands we need
|
# Create the commands we need
|
||||||
caller.cmdset.add(EvEditorCmdSet, permanent=persistent)
|
caller.cmdset.add(EvEditorCmdSet, persistent=persistent)
|
||||||
|
|
||||||
# echo inserted text back to caller
|
# echo inserted text back to caller
|
||||||
self._echo_mode = True
|
self._echo_mode = True
|
||||||
|
|
|
||||||
|
|
@ -665,7 +665,7 @@ class EvMenu:
|
||||||
menu_cmdset = EvMenuCmdSet()
|
menu_cmdset = EvMenuCmdSet()
|
||||||
menu_cmdset.mergetype = str(cmdset_mergetype).lower().capitalize() or "Replace"
|
menu_cmdset.mergetype = str(cmdset_mergetype).lower().capitalize() or "Replace"
|
||||||
menu_cmdset.priority = int(cmdset_priority)
|
menu_cmdset.priority = int(cmdset_priority)
|
||||||
self.caller.cmdset.add(menu_cmdset, permanent=persistent)
|
self.caller.cmdset.add(menu_cmdset, persistent=persistent)
|
||||||
|
|
||||||
reserved_startnode_kwargs = set(("nodename", "raw_string"))
|
reserved_startnode_kwargs = set(("nodename", "raw_string"))
|
||||||
startnode_kwargs = {}
|
startnode_kwargs = {}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue