diff --git a/docs/source/Components/Command-Sets.md b/docs/source/Components/Command-Sets.md index eb2343e932..5b52001185 100644 --- a/docs/source/Components/Command-Sets.md +++ b/docs/source/Components/Command-Sets.md @@ -42,16 +42,16 @@ Sets#merge-rules) section). 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 from commands import mycommands -class MyCmdSet(CmdSet): +class MyCmdSet(CmdSet): def at_cmdset_creation(self): """ The only thing this method should need to do is to add commands to the set. - """ + """ self.add(mycommands.MyCommand1()) self.add(mycommands.MyCommand2()) 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: ```python - def at_cmdset_creation(): + def at_cmdset_creation(): ... 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: ``` -@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 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 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) @@ -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. # 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 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 - **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 it appropriately. -More advanced cmdset example: +More advanced cmdset example: ```python from commands import mycommands class MyCmdSet(CmdSet): - + key = "MyCmdSet" priority = 4 mergetype = "Replace" - key_mergetypes = {'MyOtherCmdSet':'Union'} - + key_mergetypes = {'MyOtherCmdSet':'Union'} + def at_cmdset_creation(self): """ The only thing this method should need to do is to add commands to the set. - """ + """ self.add(mycommands.MyCommand1()) self.add(mycommands.MyCommand2()) 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 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. diff --git a/docs/source/Howto/Building-a-mech-tutorial.md b/docs/source/Howto/Building-a-mech-tutorial.md index ac36fbf92b..675cf361a0 100644 --- a/docs/source/Howto/Building-a-mech-tutorial.md +++ b/docs/source/Howto/Building-a-mech-tutorial.md @@ -190,7 +190,7 @@ class Mech(Object): def at_object_creation(self): "This is called only when object is first created" self.cmdset.add_default(default_cmds.CharacterCmdSet) - self.cmdset.add(MechCmdSet, permanent=True) + self.cmdset.add(MechCmdSet, persistent=True) self.locks.add("puppet:all();call:false()") self.db.desc = "This is a huge mech. It has missiles and stuff." ``` @@ -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 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. \ No newline at end of file +And of course you could put more guns on it. And make it fly. diff --git a/docs/source/Howto/Coding-FAQ.md b/docs/source/Howto/Coding-FAQ.md index 42ba18f59f..89f809a2e0 100644 --- a/docs/source/Howto/Coding-FAQ.md +++ b/docs/source/Howto/Coding-FAQ.md @@ -126,7 +126,7 @@ class BlockingCmdSet(CmdSet): class BlockingRoom(Room): 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 # are NOT Builders or higher 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 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 -discussion](https://github.com/evennia/evennia/issues/1522) where some suitable fonts are suggested. \ No newline at end of file +discussion](https://github.com/evennia/evennia/issues/1522) where some suitable fonts are suggested. diff --git a/docs/source/Howto/NPC-shop-Tutorial.md b/docs/source/Howto/NPC-shop-Tutorial.md index fc175140a1..f06ab98862 100644 --- a/docs/source/Howto/NPC-shop-Tutorial.md +++ b/docs/source/Howto/NPC-shop-Tutorial.md @@ -232,7 +232,7 @@ from evennia.utils.create import create_object # class for our front shop room class NPCShop(DefaultRoom): 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.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 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 -it well stocked. \ No newline at end of file +it well stocked. diff --git a/docs/source/Howto/Starting/Part1/Adding-Commands.md b/docs/source/Howto/Starting/Part1/Adding-Commands.md index 95d1f06a40..27e5b741bc 100644 --- a/docs/source/Howto/Starting/Part1/Adding-Commands.md +++ b/docs/source/Howto/Starting/Part1/Adding-Commands.md @@ -1,30 +1,30 @@ # Our own commands -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. +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. 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 -what is in it. +what is in it. ```sidebar:: Commands are not typeclassed - 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 + 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 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- -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 -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 -big cmdset. +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 +big cmdset. -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: +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 are classes - A group of Commands is stored in a CmdSet @@ -45,25 +45,25 @@ from evennia import Command as BaseCommand class Command(BaseCommand): """ (class docstring) - """ + """ pass # (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. 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 -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. +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. > 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 -`mygame/commands/mycommands.py` and add the following code: +`mygame/commands/mycommands.py` and add the following code: ```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 -what you will use to call this command later. +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. 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 -like this - this is what Evennia will be looking for when setting up the cmdset later, so -if you didn't set it up, it will use the parent's version, which is empty. Inside we add the -command class to the cmdset by `self.add()`. If you wanted to add more commands to this CmdSet you -could just add more lines of `self.add` after this. +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 +if you didn't set it up, it will use the parent's version, which is empty. Inside we add the +command class to the cmdset by `self.add()`. If you wanted to add more commands to this CmdSet you +could just add more lines of `self.add` after this. 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") -Now try - - > echo +Now try + + > echo 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 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 (): echo raw_cmdname (): echo cmdstring (): echo - args (): + args (): 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, 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 session (): Griatch(#1)@1:2:7:.:0:.:0:.:1 account (): Griatch(account 1) - raw_string (): echo + raw_string (): echo -------------------------------------------------- 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 current cmdset (self.cmdset): ChannelCmdSet -These are all properties you can access with `.` on the Command instance, such as `.key`, `.args` and so on. -Evennia makes these available to you and they will be different every time a command is run. The most -important ones we will make use of now are: - +These are all properties you can access with `.` on the Command instance, such as `.key`, `.args` and so on. +Evennia makes these available to you and they will be different every time a command is run. The most +important ones we will make use of now are: + - `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"`. - `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: ```python @@ -165,47 +165,47 @@ looks for to figure out what a Command actually does. Modify your `CmdEcho` clas class CmdEcho(Command): """ A simple echo command - + Usage: - echo + echo """ key = "echo" 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 -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 +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 +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 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 -try out the new version: - - > reload +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: + + > reload > py self.cmdset.add("commands.mycommands.MyCmdSet") - > echo + > echo Echo: '' -Try to pass an argument: +Try to pass an argument: > echo Woo Tang! Echo: ' Woo Tang!' - + 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! Echo: 'Woo Tang!' - -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: + +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: ```python # ... @@ -213,15 +213,15 @@ it looks a bit weird for our echo example. Tweak the code: class CmdEcho(Command): """ A simple echo command - + Usage: - echo + echo """ key = "echo" 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 have any space in front of it. - > reload + > reload > py self.cmdset.add("commands.mycommands.MyCmdSet") > echo Woo Tang! Echo: 'Woo Tang!' - -Don't forget to look at the help for the echo command: - > help echo - -You will get the docstring you put in your Command-class. +Don't forget to look at the help for the echo command: + + > help echo + +You will get the docstring you put in your Command-class. ### 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 -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) - -Now you can `reload` as much as you want and your code changes will be available directly without + > 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 needing to re-add the MyCmdSet again. To remove the cmdset again, do > 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 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 - You hit with full force! + You hit with full force! Not only that, we want the to see - You got hit by with full force! + You got hit by with full force! -Here, `` would be the one using the `hit` command and `` is the one doing the punching. +Here, `` would be the one using the `hit` command and `` 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 # ... @@ -277,7 +277,7 @@ Still in `mygame/commands/mycommands.py`, add a new class, between `CmdEcho` and class CmdHit(Command): """ Hit a target. - + Usage: hit @@ -288,11 +288,11 @@ class CmdHit(Command): args = self.args.strip() if not args: self.caller.msg("Who do you want to hit?") - return + return target = self.caller.search(args) if not target: - return - self.caller.msg(f"You hit {target.key} with full force!") + return + self.caller.msg(f"You hit {target.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. - **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 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 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. ```sidebar:: if-statements - - The full form of the if statement is + + The full form of the if statement is if condition: ... elif othercondition: ... - else: + else: ... - - 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 `if..elif..else` structure also serves the same function as `case` in some other languages. + + 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 `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 :` and only if that condition is 'truthy' will the indented code block under the `if` statement run. To learn what is truthy in - Python it's usually easier to learn what is "falsy": + Python it's usually easier to learn what is "falsy": - `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. - - `0` or `0.0` + - `0` or `0.0` - The empty string `""` or `''` or `""""""` or `''''''` - 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 - 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. -- **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. - **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. -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 # ... @@ -357,17 +357,17 @@ class MyCmdSet(CmdSet): ```sidebar:: Errors in your code 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 - 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 + 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). + 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 `_ for 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 Who do you want to hit? 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 you could try to hit it (if you dare): - hit smaug + hit smaug You hit Smaug with full force! 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 -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. +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. -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. \ No newline at end of file +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. diff --git a/docs/source/Howto/Starting/Part1/More-on-Commands.md b/docs/source/Howto/Starting/Part1/More-on-Commands.md index 13fa5f05c5..a730bc5d06 100644 --- a/docs/source/Howto/Starting/Part1/More-on-Commands.md +++ b/docs/source/Howto/Starting/Part1/More-on-Commands.md @@ -1,25 +1,25 @@ # More about Commands -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. +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. -## 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 -from that still around. +In the last lesson we made a `hit` Command and hit a dragon with it. You should have the code +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 [[with] ] - + That is, we want to support all of these forms - hit target + hit target hit target 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 -you are in a hurry. Time to modify `mygame/commands/mycommands.py` again. Let us break out the parsing +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 a little, in a new method `parse`: @@ -29,19 +29,19 @@ a little, in a new method `parse`: class CmdHit(Command): """ Hit a target. - + Usage: hit """ key = "hit" - def parse(self): + def parse(self): self.args = self.args.strip() target, *weapon = self.args.split(" with ", 1) if not weapon: - target, *weapon = target.split(" ", 1) - self.target = target.strip() + target, *weapon = target.split(" ", 1) + self.target = target.strip() if weapon: self.weapon = weapon.strip() else: @@ -50,162 +50,162 @@ class CmdHit(Command): def func(self): if not self.args: self.caller.msg("Who do you want to hit?") - return + return # get the target for the hit - target = self.caller.search(self.target) + target = self.caller.search(self.target) if not target: - return - # get and handle the weapon + return + # get and handle the weapon weapon = None if self.weapon: weapon = self.caller.search(self.weapon) - if weapon: + if weapon: weaponstr = f"{weapon.key}" else: 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}!") # ... ``` 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 ` with ` you'd inherit 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 `tuple` is written as `(a, b, c, d, ...)`. A tuple cannot be modified once it is 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. ``` -- **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 - 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. `.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: 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"]` 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 "soaks" up everything left over. 1. `target` becomes `"smaug"` and `weapon` becomes `[]` 2. `target` becomes `"smaug sword"` and `weapon` becomes `[]` 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 - under two conditions (from the example above): + under two conditions (from the example above): 1. `target` is simply `smaug` 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: 1. `target` remains `smaug` and `weapon` remains `[]` 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 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). - -Now onto the `func` method. The main difference is we now have `self.target` and `self.weapon` available for -convenient use. -- **Lines 29 and 35** - We make use of the previously parsed search terms for the target and weapon to find the - 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 + +Now onto the `func` method. The main difference is we now have `self.target` and `self.weapon` available for +convenient use. +- **Lines 29 and 35** - We make use of the previously parsed search terms for the target and weapon to find the + 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 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. Let's try it out! - > reload - > hit smaug with sword + > reload + > hit smaug with sword Could not find 'sword'. 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 - -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 +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 + +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). - > hit smaug with sword - You hit smaug with sword! + > 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 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) -also has many examples of objects with commands on them. +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. 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 -add `MyCmdSet` to it. This actually adds both `hit` and `echo` to the sword, which is fine. +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. Let's try to swing it! - > hit + > hit More than one match for 'hit' (please narrow target): hit-1 (sword #11) hit-2 ```sidebar:: Multi-matches - + 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 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 `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 -(_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 -one you meant: +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 +the sword. The other one is from adding `MyCmdSet` to ourself earlier. It's easy enough to tell Evennia which +one you meant: - > hit-1 + > hit-1 Who do you want to hit? > hit-2 - 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: + 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: > self.cmdset.remove("commands.mycommands.MyCmdSet") > hit Who do you want to hit? -Now try this: +Now try this: > tunnel n = kitchen - > n - > drop sword + > n + > drop sword > s > hit Command 'hit' is not available. Maybe you meant ... > n - > 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. + > 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. ### 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 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 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()") - -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). + +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). For locks to work, you cannot be _superuser_, since the superuser passes all locks. You need to `quell` yourself -first: +first: ```sidebar:: quell/unquell 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. Use `unquell` to get back to your normal self. ``` > quell - + If the sword lies on the ground, try > hit Command 'hit' is not available. .. - > get sword - > hit + > get sword + > 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. We can do that in two ways: - delete sword - -or + delete sword - py self.search("sword").delete() +or + + py self.search("sword").delete() ## 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 -is ourself (`self`) or other objects like the `sword`. +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`. -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. +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. - 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` @@ -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 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 """ @@ -321,19 +321,19 @@ class SessionCmdSet(default_cmds.SessionCmdSet): ``` ```sidebar:: super() - + 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 (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. -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 -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. +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. 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 - 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 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) ``` -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. ### Removing Commands -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? +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? -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 +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 from Evennia. We happen to know this can be found as `default_cmds.CmdGet`. - + ```python # ... from commands import mycommands @@ -413,16 +413,16 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet): # ... ``` - > reload + > reload > get Command 'get' is not available ... ## 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 -command with the same `key` in the `CharacterCmdSet` to replace the default one. +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. -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 that `CmdGet` command. @@ -430,7 +430,7 @@ that `CmdGet` command. # up top, by the other imports from evennia import default_cmds -# somewhere below +# somewhere below class MyCmdGet(default_cmds.CmdGet): def func(self): @@ -440,20 +440,20 @@ class MyCmdGet(default_cmds.CmdGet): ``` - **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 -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, +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. +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 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. -- **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 - your inventory. For a room, the contents is everything in it. +- **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 + 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 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. - -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: ```python @@ -476,22 +476,22 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet): ``` ```sidebar:: Another way - Instead of adding `MyCmdGet` explicitly in default_cmdset.py, - you could also add it to `mycommands.MyCmdSet` and let it be + Instead of adding `MyCmdGet` explicitly in default_cmdset.py, + you could also add it to `mycommands.MyCmdSet` and let it be added automatically for you. ``` - > reload - > get + > reload + > get 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 -there's some room for improvement there). +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). ## Summary -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 +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 command on ourselves. diff --git a/docs/source/Howto/Starting/Part3/Tutorial-for-basic-MUSH-like-game.md b/docs/source/Howto/Starting/Part3/Tutorial-for-basic-MUSH-like-game.md index 70c4963e3b..940ddbd8c3 100644 --- a/docs/source/Howto/Starting/Part3/Tutorial-for-basic-MUSH-like-game.md +++ b/docs/source/Howto/Starting/Part3/Tutorial-for-basic-MUSH-like-game.md @@ -56,8 +56,8 @@ class Character(DefaultCharacter): [...] """ def at_object_creation(self): - "This is called when object is first created, only." - self.db.power = 1 + "This is called when object is first created, only." + self.db.power = 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: -- 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 custom `ChargenRoom` type that makes this set of commands available to players in such rooms. -- One such room to test things in. +- 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 custom `ChargenRoom` type that makes this set of commands available to players in such rooms. +- One such room to test things in. ### 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, 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 # end of command.py @@ -124,13 +124,13 @@ class CmdSetPower(Command): """ set the power of a character - Usage: + Usage: +setpower <1-10> - This sets the power of the current character. This can only be - used during character generation. + This sets the power of the current character. This can only be + used during character generation. """ - + key = "+setpower" help_category = "mush" @@ -138,10 +138,10 @@ class CmdSetPower(Command): "This performs the actual command" errmsg = "You must supply a number between 1 and 10." if not self.args: - self.caller.msg(errmsg) + self.caller.msg(errmsg) return try: - power = int(self.args) + power = int(self.args) except ValueError: self.caller.msg(errmsg) return @@ -180,7 +180,7 @@ class ChargenCmdset(CmdSet): key = "Chargen" def at_cmdset_creation(self): "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 @@ -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 `mygame/typeclasses/rooms.py` next: -```python +```python from commands.default_cmdsets import ChargenCmdset -# ... +# ... # down at the end of rooms.py class ChargenRoom(Room): @@ -206,10 +206,10 @@ class ChargenRoom(Room): """ def at_object_creation(self): "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. -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 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 an exit back here named `finish`. If you see errors at this stage, you must fix them in your code. `@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 @@ -263,19 +263,19 @@ set during Character generation: > +attack 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 -# ... +# ... class CmdAttack(Command): """ - issues an attack + issues an attack - Usage: - +attack + Usage: + +attack This will calculate a new combat score based on your Power. Your combat score is visible to everyone in the same location. @@ -288,8 +288,8 @@ class CmdAttack(Command): caller = self.caller power = caller.db.power if not power: - # this can happen if caller is not of - # our custom Character typeclass + # this can happen if caller is not of + # our custom Character typeclass power = 1 combat_score = random.randint(1, 10 * power) caller.db.combat_score = combat_score @@ -297,10 +297,10 @@ class CmdAttack(Command): # announce message = "%s +attack%s with a combat score of %s!" caller.msg(message % ("You", "", combat_score)) - caller.location.msg_contents(message % + caller.location.msg_contents(message % (caller.key, "s", combat_score), exclude=caller) -``` +``` 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. @@ -349,8 +349,8 @@ class Character(DefaultCharacter): [...] """ def at_object_creation(self): - "This is called when object is first created, only." - self.db.power = 1 + "This is called when object is first created, only." + self.db.power = 1 self.db.combat_score = 1 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 -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 - You created the NPC 'Anna'. + You created the NPC 'Anna'. At the end of `command.py`, create our new command: ```python from evennia import create_object - + class CmdCreateNPC(Command): """ create a new npc @@ -413,12 +413,12 @@ class CmdCreateNPC(Command): +createNPC Creates a new, named NPC. The NPC will start with a Power of 1. - """ + """ key = "+createnpc" aliases = ["+createNPC"] locks = "call:not perm(nonpcs)" - help_category = "mush" - + help_category = "mush" + def func(self): "creates the object and names it" caller = self.caller @@ -432,15 +432,15 @@ class CmdCreateNPC(Command): # make name always start with capital letter name = self.args.strip().capitalize() # create npc in caller's location - npc = create_object("characters.Character", - key=name, + npc = create_object("characters.Character", + key=name, location=caller.location, locks="edit:id(%i) and perm(Builders);call:false()" % caller.id) - # announce + # announce message = "%s created the NPC '%s'." - caller.msg(message % ("You", name)) - caller.location.msg_contents(message % (caller.key, name), - exclude=caller) + caller.msg(message % ("You", name)) + caller.location.msg_contents(message % (caller.key, name), + exclude=caller) ``` 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 @@ -475,38 +475,38 @@ principle re-work our old `+setpower` command, but let's try something more usef `+editNPC` command. > +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 class CmdEditNPC(Command): """ edit an existing NPC - Usage: + Usage: +editnpc [/ [= value]] - + Examples: +editnpc mynpc/power = 5 +editnpc mynpc/power - displays power value - +editnpc mynpc - shows all editable + +editnpc mynpc - shows all editable 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. """ key = "+editnpc" aliases = ["+editNPC"] locks = "cmd:not perm(nonpcs)" - help_category = "mush" + help_category = "mush" def parse(self): "We need to do some parsing here" args = self.args propname, propval = None, None - if "=" in args: - args, propval = [part.strip() for part in args.rsplit("=", 1)] + if "=" in args: + args, propval = [part.strip() for part in args.rsplit("=", 1)] if "/" in args: args, propname = [part.strip() for part in args.rsplit("/", 1)] # store, so we can access it below in func() @@ -519,38 +519,38 @@ class CmdEditNPC(Command): "do the editing" allowed_propnames = ("power", "attribute1", "attribute2") - + caller = self.caller if not self.args or not self.name: - caller.msg("Usage: +editnpc name[/propname][=propval]") + caller.msg("Usage: +editnpc name[/propname][=propval]") return npc = caller.search(self.name) if not npc: return if not npc.access(caller, "edit"): caller.msg("You cannot change this NPC.") - return + return if not self.propname: # this means we just list the values output = "Properties of %s:" % npc.key - for propname in allowed_propnames: + for propname in allowed_propnames: propvalue = npc.attributes.get(propname, default="N/A") output += "\n %s = %s" % (propname, propvalue) caller.msg(output) - elif self.propname not in allowed_propnames: - caller.msg("You may only change %s." % + elif self.propname not in allowed_propnames: + caller.msg("You may only change %s." % ", ".join(allowed_propnames)) elif self.propval: # assigning a new propvalue # in this example, the properties are all integers... - intpropval = int(self.propval) - npc.attributes.add(self.propname, intpropval) + intpropval = int(self.propval) + npc.attributes.add(self.propname, intpropval) caller.msg("Set %s's property '%s' to %s" % (npc.key, self.propname, self.propval)) else: # propname set, but not propval - show current value - caller.msg("%s has property %s = %s" % - (npc.key, self.propname, + caller.msg("%s has property %s = %s" % + (npc.key, self.propname, 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 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 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 - Usage: + Usage: +npc = 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" locks = "call:not perm(nonpcs)" @@ -601,7 +601,7 @@ class CmdNPC(Command): "Simple split of the = sign" name, cmdname = None, None if "=" in self.args: - name, cmdname = [part.strip() + name, cmdname = [part.strip() for part in self.args.rsplit("=", 1)] self.name, self.cmdname = name, cmdname @@ -611,7 +611,7 @@ class CmdNPC(Command): if not self.cmdname: caller.msg("Usage: +npc = ") return - npc = caller.search(self.name) + npc = caller.search(self.name) if not npc: return 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 more specific ideas, see the [other tutorials and hints](../../Howto-Overview) as well -as the [Evennia Component overview](../../../Components/Components-Overview). \ No newline at end of file +as the [Evennia Component overview](../../../Components/Components-Overview). diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index 01bbe499dd..4874c8fd29 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -847,7 +847,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase): def delete(self, *args, **kwargs): """ - Deletes the account permanently. + Deletes the account persistently. Notes: `*args` and `**kwargs` are passed on to the base delete @@ -1196,7 +1196,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase): self.locks.add(lockstring) # 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): """ diff --git a/evennia/commands/cmdset.py b/evennia/commands/cmdset.py index 5f2e7d9305..e23143c563 100644 --- a/evennia/commands/cmdset.py +++ b/evennia/commands/cmdset.py @@ -177,7 +177,7 @@ class CmdSet(object, metaclass=_CmdSetMeta): # merge-stack, every cmdset in the stack must have `duplicates` set explicitly. duplicates = None - permanent = False + persistent = False key_mergetypes = {} errmessage = "" # pre-store properties to duplicate straight off @@ -187,7 +187,7 @@ class CmdSet(object, metaclass=_CmdSetMeta): "no_exits", "no_objs", "no_channels", - "permanent", + "persistent", "mergetype", "priority", "duplicates", @@ -357,7 +357,7 @@ class CmdSet(object, metaclass=_CmdSetMeta): 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([ "{}:{}".format(opt, "T" if getattr(self, opt) else "F") for opt in ("no_exits", "no_objs", "no_channels", "duplicates") diff --git a/evennia/commands/cmdsethandler.py b/evennia/commands/cmdsethandler.py index 354ea6fb74..6f76a74842 100644 --- a/evennia/commands/cmdsethandler.py +++ b/evennia/commands/cmdsethandler.py @@ -305,7 +305,7 @@ class CmdSetHandler(object): self.mergetype_stack = ["Union"] # the subset of the cmdset_paths that are to be stored in the database - self.permanent_paths = [""] + self.persistent_paths = [""] if init_true: self.update(init_mode=True) # is then called from the object __init__. @@ -363,7 +363,7 @@ class CmdSetHandler(object): Args: 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. Notes: @@ -378,7 +378,7 @@ class CmdSetHandler(object): """ if init_mode: - # reimport all permanent cmdsets + # reimport all persistent cmdsets storage = self.obj.cmdset_storage if storage: self.cmdset_stack = [] @@ -408,7 +408,7 @@ class CmdSetHandler(object): if _IN_GAME_ERRORS: self.obj.msg(err) continue - cmdset.permanent = cmdset.key != "_CMDSET_ERROR" + cmdset.persistent = cmdset.key != "_CMDSET_ERROR" self.cmdset_stack.append(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.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): """ 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. cmdset = self._import_cmdset(cmdset) 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": # store the path permanently storage = self.obj.cmdset_storage or [""] @@ -514,7 +514,7 @@ class CmdSetHandler(object): # remove the default cmdset only if self.cmdset_stack: cmdset = self.cmdset_stack[0] - if cmdset.permanent: + if cmdset.persistent: storage = self.obj.cmdset_storage or [""] storage[0] = "" self.obj.cmdset_storage = storage @@ -531,7 +531,7 @@ class CmdSetHandler(object): if not cmdset: # remove the last one in the stack cmdset = self.cmdset_stack.pop() - if cmdset.permanent: + if cmdset.persistent: storage = self.obj.cmdset_storage storage.pop() self.obj.cmdset_storage = storage @@ -548,12 +548,12 @@ class CmdSetHandler(object): ] 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 storage = self.obj.cmdset_storage updated = False for cset in delcmdsets: - if cset.permanent: + if cset.persistent: try: storage.remove(cset.path) updated = True diff --git a/evennia/contrib/building_menu.py b/evennia/contrib/building_menu.py index 94910d6d25..0bf3e40d2d 100644 --- a/evennia/contrib/building_menu.py +++ b/evennia/contrib/building_menu.py @@ -162,7 +162,7 @@ def _menu_savefunc(caller, buf): def _menu_quitfunc(caller): caller.cmdset.add( 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: caller.ndb._building_menu.move(back=True) @@ -951,7 +951,7 @@ class BuildingMenu(object): if caller.cmdset.has(BuildingMenuCmdSet): caller.cmdset.remove(BuildingMenuCmdSet) - self.caller.cmdset.add(BuildingMenuCmdSet, permanent=self.persistent) + self.caller.cmdset.add(BuildingMenuCmdSet, persistent=self.persistent) self.display() def open_parent_menu(self): diff --git a/evennia/contrib/evscaperoom/room.py b/evennia/contrib/evscaperoom/room.py index d591c348d0..9b4a8205e8 100644 --- a/evennia/contrib/evscaperoom/room.py +++ b/evennia/contrib/evscaperoom/room.py @@ -50,7 +50,7 @@ class EvscapeRoom(EvscaperoomObject, DefaultRoom): "total_achievements": 14, } - self.cmdset.add(CmdSetEvScapeRoom, permanent=True) + self.cmdset.add(CmdSetEvScapeRoom, persistent=True) self.log("Room created and log started.") diff --git a/evennia/contrib/rpsystem.py b/evennia/contrib/rpsystem.py index 9e24cc7ad9..4cb54fb68f 100644 --- a/evennia/contrib/rpsystem.py +++ b/evennia/contrib/rpsystem.py @@ -1550,7 +1550,7 @@ class ContribRPCharacter(DefaultCharacter, ContribRPObject): self.db._recog_obj2regex = {} self.db._recog_obj2recog = {} - self.cmdset.add(RPSystemCmdSet, permanent=True) + self.cmdset.add(RPSystemCmdSet, persistent=True) # initializing sdesc self.sdesc.add("A normal person") diff --git a/evennia/contrib/talking_npc.py b/evennia/contrib/talking_npc.py index ddf0d372f9..31872a170a 100644 --- a/evennia/contrib/talking_npc.py +++ b/evennia/contrib/talking_npc.py @@ -130,4 +130,4 @@ class TalkingNPC(DefaultObject): "This is called when object is first created." self.db.desc = "This is a talkative NPC." # assign the talk command to npc - self.cmdset.add_default(TalkingCmdSet, permanent=True) + self.cmdset.add_default(TalkingCmdSet, persistent=True) diff --git a/evennia/contrib/tutorial_world/mob.py b/evennia/contrib/tutorial_world/mob.py index 7048c4ac78..04ffa475db 100644 --- a/evennia/contrib/tutorial_world/mob.py +++ b/evennia/contrib/tutorial_world/mob.py @@ -112,7 +112,7 @@ class Mob(tut_objects.TutorialObject): Called the first time the object is created. 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 # chase the mob around when building. self.db.patrolling = True diff --git a/evennia/contrib/tutorial_world/objects.py b/evennia/contrib/tutorial_world/objects.py index 8b9b725e4b..5a07926221 100644 --- a/evennia/contrib/tutorial_world/objects.py +++ b/evennia/contrib/tutorial_world/objects.py @@ -129,7 +129,7 @@ class TutorialReadable(TutorialObject): ) self.db.readable_text = "There is no text written on %s." % self.key # 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): """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. self.db.desc = "A splinter of wood with remnants of resin on it, enough for burning." # add the Light command - self.cmdset.add_default(CmdSetLight, permanent=True) + self.cmdset.add_default(CmdSetLight, persistent=True) def _burnout(self): """ @@ -670,7 +670,7 @@ class CrumblingWall(TutorialObject, DefaultExit): # exit_open is set to True. self.locks.add("cmd:locattr(is_lit);traverse:objattr(exit_open)") # set cmdset - self.cmdset.add(CmdSetCrumblingWall, permanent=True) + self.cmdset.add(CmdSetCrumblingWall, persistent=True) def open_wall(self): """ @@ -950,7 +950,7 @@ class TutorialWeapon(TutorialObject): self.db.parry = 0.8 # parry chance self.db.damage = 1.0 self.db.magic = False - self.cmdset.add_default(CmdSetWeapon, permanent=True) + self.cmdset.add_default(CmdSetWeapon, persistent=True) def reset(self): """ @@ -1148,7 +1148,7 @@ class TutorialWeaponRack(TutorialObject): """ called at creation """ - self.cmdset.add_default(CmdSetWeaponRack, permanent=True) + self.cmdset.add_default(CmdSetWeaponRack, persistent=True) self.db.rack_id = "weaponrack_1" # these are prototype names from the prototype # dictionary above. diff --git a/evennia/contrib/tutorial_world/rooms.py b/evennia/contrib/tutorial_world/rooms.py index 16eae80a1d..4e8f3514b7 100644 --- a/evennia/contrib/tutorial_world/rooms.py +++ b/evennia/contrib/tutorial_world/rooms.py @@ -429,7 +429,7 @@ class IntroRoom(TutorialRoom): "This assigns the health Attribute to " "the account." ) - self.cmdset.add(CmdSetEvenniaIntro, permanent=True) + self.cmdset.add(CmdSetEvenniaIntro, persistent=True) def at_object_receive(self, character, source_location): """ @@ -732,7 +732,7 @@ class BridgeRoom(WeatherRoom): self.db.east_exit = "gate" self.db.fall_exit = "cliffledge" # 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 # return_description (this skips the cmdset) when # 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." # the room starts dark. self.db.is_lit = False - self.cmdset.add(DarkCmdSet, permanent=True) + self.cmdset.add(DarkCmdSet, persistent=True) def at_init(self): """ @@ -1009,7 +1009,7 @@ class DarkRoom(TutorialRoom): # noone is carrying light - darken the room self.db.is_lit = 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): if char.is_superuser: char.msg("You are Superuser, so you are not affected by the dark state.") diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 3c54697e2b..66dd18736e 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -1310,7 +1310,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): 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, deletion is aborted. @@ -2261,7 +2261,7 @@ class DefaultCharacter(DefaultObject): ";".join(["get:false()", "call:false()"]) # noone can pick up the character ) # no commands can be called on character from outside # 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): """ @@ -2711,7 +2711,7 @@ class DefaultExit(DefaultObject): 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. - 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): """ diff --git a/evennia/utils/eveditor.py b/evennia/utils/eveditor.py index 9a75bbd590..4500989246 100644 --- a/evennia/utils/eveditor.py +++ b/evennia/utils/eveditor.py @@ -893,7 +893,7 @@ class EvEditor: persistent = False # Create the commands we need - caller.cmdset.add(EvEditorCmdSet, permanent=persistent) + caller.cmdset.add(EvEditorCmdSet, persistent=persistent) # echo inserted text back to caller self._echo_mode = True diff --git a/evennia/utils/evmenu.py b/evennia/utils/evmenu.py index 72e010130e..15617af6be 100644 --- a/evennia/utils/evmenu.py +++ b/evennia/utils/evmenu.py @@ -665,7 +665,7 @@ class EvMenu: menu_cmdset = EvMenuCmdSet() menu_cmdset.mergetype = str(cmdset_mergetype).lower().capitalize() or "Replace" 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")) startnode_kwargs = {}