diff --git a/docs/failing/Evennia-for-MUSH-Users.md b/docs/failing/Evennia-for-MUSH-Users.md new file mode 100644 index 0000000000..3d0ae040fc --- /dev/null +++ b/docs/failing/Evennia-for-MUSH-Users.md @@ -0,0 +1,124 @@ +*This page is adopted from an article originally posted for the MUSH community [here on musoapbox.net](http://musoapbox.net/topic/1150/evennia-for-mushers).* + +[MUSH](https://en.wikipedia.org/wiki/MUSH)es are text multiplayer games traditionally used for heavily roleplay-focused game styles. They are often (but not always) utilizing game masters and human oversight over code automation. MUSHes are traditionally built on the TinyMUSH-family of game servers, like PennMUSH, TinyMUSH, TinyMUX and RhostMUSH. Also their siblings [MUCK](https://en.wikipedia.org/wiki/TinyMUCK) and [MOO](https://en.wikipedia.org/wiki/MOO) are often mentioned together with MUSH since they all inherit from the same [TinyMUD](https://en.wikipedia.org/wiki/MUD_trees#TinyMUD_family_tree) base. A major feature is the ability to modify and program the game world from inside the game by using a custom scripting language. We will refer to this online scripting as *softcode* here. + +Evennia works quite differently from a MUSH both in its overall design and under the hood. The same things are achievable, just in a different way. Here are some fundamental differences to keep in mind if you are coming from the MUSH world. + +## Developers vs Players + +In MUSH, users tend to code and expand all aspects of the game from inside it using softcode. A MUSH can thus be said to be managed solely by *Players* with different levels of access. Evennia on the other hand, differentiates between the role of the *Player* and the *Developer*. + +- An Evennia *Developer* works in Python from *outside* the game, in what MUSH would consider “hardcode”. Developers implement larger-scale code changes and can fundamentally change how the game works. They then load their changes into the running Evennia server. Such changes will usually not drop any connected players. +- An Evennia *Player* operates from *inside* the game. Some staff-level players are likely to double as developers. Depending on access level, players can modify and expand the game's world by digging new rooms, creating new objects, alias commands, customize their experience and so on. Trusted staff may get access to Python via the `@py` command, but this would be a security risk for normal Players to use. So the *Player* usually operates by making use of the tools prepared for them by the *Developer* - tools that can be as rigid or flexible as the developer desires. + +## Collaborating on a game - Python vs Softcode + +For a *Player*, collaborating on a game need not be too different between MUSH and Evennia. The building and description of the game world can still happen mostly in-game using build commands, using text tags and [inline functions](https://github.com/evennia/evennia/wiki/TextTags#inline-functions) to prettify and customize the experience. Evennia offers external ways to build a world but those are optional. There is also nothing *in principle* stopping a Developer from offering a softcode-like language to Players if that is deemed necessary. + +For *Developers* of the game, the difference is larger: Code is mainly written outside the game in Python modules rather than in-game on the command line. Python is a very popular and well-supported language with tons of documentation and help to be found. The Python standard library is also a great help for not having to reinvent the wheel. But that said, while Python is considered one of the easier languages to learn and use it is undoubtedly very different from MUSH softcode. + +While softcode allows collaboration in-game, Evennia's external coding instead opens up the possibility for collaboration using professional version control tools and bug tracking using websites like github (or bitbucket for a free private repo). Source code can be written in proper text editors and IDEs with refactoring, syntax highlighting and all other conveniences. In short, collaborative development of an Evennia game is done in the same way most professional collaborative development is done in the world, meaning all the best tools can be used. + + +## `@parent` vs `@typeclass` and `@spawn` + +Inheritance works differently in Python than in softcode. Evennia has no concept of a "master object" that other objects inherit from. There is in fact no reason at all to introduce "virtual objects" in the game world - code and data are kept separate from one another. + +In Python (which is an [object oriented](https://en.wikipedia.org/wiki/Object-oriented_programming) language) one instead creates *classes* - these are like blueprints from which you spawn any number of *object instances*. Evennia also adds the extra feature that every instance is persistent in the database (this means no SQL is ever needed). To take one example, a unique character in Evennia is an instances of the class `Character`. + +One parallel to MUSH's `@parent` command may be Evennia's `@typeclass` command, which changes which class an already existing object is an instance of. This way you can literally turn a `Character` into a `Flowerpot` on the spot. + +if you are new to object oriented design it's important to note that all object instances of a class does *not* have to be identical. If they did, all Characters would be named the same. Evennia allows to customize individual objects in many different ways. One way is through *Attributes*, which are database-bound properties that can be linked to any object. For example, you could have an `Orc` class that defines all the stuff an Orc should be able to do (probably in turn inheriting from some `Monster` class shared by all monsters). Setting different Attributes on different instances (different strength, equipment, looks etc) would make each Orc unique despite all sharing the same class. + + The `@spawn` command allows one to conveniently choose between different "sets" of Attributes to put on each new Orc (like the "warrior" set or "shaman" set) . Such sets can even inherit one another which is again somewhat remniscent at least of the *effect* of `@parent` and the object-based inheritance of MUSH. + +There are other differences for sure, but that should give some feel for things. Enough with the theory. Let's get down to more practical matters next. To install, see the [Getting Started instructions](Getting-Started). + +## A first step making things more familiar + +We will here give two examples of customizing Evennia to be more familiar to a MUSH *Player*. + +### Activating a multi-descer + +By default Evennia’s `desc` command updates your description and that’s it. There is a more feature-rich optional “multi-descer” in `evennia/contrib/multidesc.py` though. This alternative allows for managing and combining a multitude of keyed descriptions. + +To activate the multi-descer, `cd` to your game folder and into the `commands` sub-folder. There you’ll find the file `default_cmdsets.py`. In Python lingo all `*.py` files are called *modules*. Open the module in a text editor. We won’t go into Evennia in-game *Commands* and *Command sets* further here, but suffice to say Evennia allows you to change which commands (or versions of commands) are available to the player from moment to moment depending on circumstance. + +Add two new lines to the module as seen below: + +```python +# the file mygame/commands/default_cmdsets.py +# [...] + +from evennia.contrib import multidescer # <- added now + +class CharacterCmdSet(default_cmds.CharacterCmdSet): + """ + The CharacterCmdSet contains general in-game commands like look, + get etc available on in-game Character objects. It is merged with + the AccountCmdSet when an Account puppets a Character. + """ + key = "DefaultCharacter" + + def at_cmdset_creation(self): + """ + Populates the cmdset + """ + super().at_cmdset_creation() + # + # any commands you add below will overload the default ones. + # + self.add(multidescer.CmdMultiDesc()) # <- added now +# [...] +``` + +Note that Python cares about indentation, so make sure to indent with the same number of spaces as shown above! + +So what happens above? We [import the module](http://www.linuxtopia.org/online_books/programming_books/python_programming/python_ch28s03.html) `evennia/contrib/multidescer.py` at the top. Once imported we can access stuff inside that module using full stop (`.`). The multidescer is defined as a class `CmdMultiDesc` (we could find this out by opening said module in a text editor). At the bottom we create a new instance of this class and add it to the `CharacterCmdSet` class. For the sake of this tutorial we only need to know that `CharacterCmdSet` contains all commands that should be be available to the `Character` by default. + +This whole thing will be triggered when the command set is first created, which happens on server start. So we need to reload Evennia with `@reload` - no one will be disconnected by doing this. If all went well you should now be able to use `desc` (or `+desc`) and find that you have more possibilities: + +```text +> help +desc # get help on the command +> +desc eyes = His eyes are blue. +> +desc basic = A big guy. +> +desc/set basic + + eyes # we add an extra space between +> look me +A big guy. His eyes are blue. +``` + +If there are errors, a *traceback* will show in the server log - several lines of text showing where the error occurred. Find where the error is by locating the line number related to the `default_cmdsets.py` file (it's the only one you've changed so far). Most likely you mis-spelled something or missed the indentation. Fix it and either `@reload` again or run `evennia start` as needed. + +### Customizing the multidescer syntax + +As seen above the multidescer uses syntax like this (where `|/` are Evennia's tags for line breaks) : + +```text +> +desc/set basic + |/|/ + cape + footwear + |/|/ + attitude +``` + +This use of `+ ` was prescribed by the *Developer* that coded this `+desc` command. What if the *Player* doesn’t like this syntax though? Do players need to pester the dev to change it? Not necessarily. While Evennia does not allow the player to build their own multi-descer on the command line, it does allow for *re-mapping* the command syntax to one they prefer. This is done using the `nick` command. + +Here’s a nick that changes how to input the command above: + +```text +> nick setdesc $1 $2 $3 $4 = +desc/set $1 + |/|/ + $2 + $3 + |/|/ + $4 +``` + +The string on the left will be matched against your input and if matching, it will be replaced with the string on the right. The `$`-type tags will store space-separated arguments and put them into the replacement. The nick allows [shell-like wildcards](http://www.linfo.org/wildcard.html), so you can use `*`, `?`, `[...]`, `[!...]` etc to match parts of the input. + +The same description as before can now be set as + +```text +> setdesc basic cape footwear attitude +``` + +With the `nick` functionality players can mitigate a lot of syntax dislikes even without the developer changing the underlying Python code. + +## Next steps + +If you are a *Developer* and are interested in making a more MUSH-like Evennia game, a good start is to look into the Evennia [Tutorial for a first MUSH-like game](https://github.com/evennia/evennia/wiki/Tutorial%20for%20basic%20MUSH%20like%20game). That steps through building a simple little game from scratch and helps to acquaint you with the various corners of Evennia. There is also the [Tutorial for running roleplaying sessions](https://github.com/evennia/evennia/wiki/Evennia%20for%20roleplaying%20sessions) that can be of interest. + +An important aspect of making things more familiar for *Players* is adding new and tweaking existing commands. How this is done is covered by the [Tutorial on adding new commands](https://github.com/evennia/evennia/wiki/Adding%20Command%20Tutorial). You may also find it useful to shop through the `evennia/contrib/` folder. The [Tutorial world](https://github.com/evennia/evennia/wiki/Tutorial%20World%20Introduction) is a small single-player quest you can try (it’s not very MUSH-like but it does show many Evennia concepts in action). Beyond that there are [many more tutorials](https://github.com/evennia/evennia/wiki/Tutorials) to try out. If you feel you want a more visual overview you can also look at [Evennia in pictures](https://evennia.blogspot.se/2016/05/evennia-in-pictures.html). + +… And of course, if you need further help you can always drop into the [Evennia chatroom](http://webchat.freenode.net/?channels=evennia&uio=MT1mYWxzZSY5PXRydWUmMTE9MTk1JjEyPXRydWUbb) or post a question in our [forum/mailing list](https://groups.google.com/forum/#%21forum/evennia)! diff --git a/docs/failing/Installing-on-Android.md b/docs/failing/Installing-on-Android.md new file mode 100644 index 0000000000..ca0a32552c --- /dev/null +++ b/docs/failing/Installing-on-Android.md @@ -0,0 +1,122 @@ + +This page describes how to install and run the Evennia server on an Android phone. This will involve +installing a slew of third-party programs from the Google Play store, so make sure you are okay with +this before starting. + +## Install Termux + +The first thing to do is install a terminal emulator that allows a "full" version of linux to be run. Note that Android is essentially running on top of linux so if you have a rooted phone, you may be able to skip this step. You *don't* require a rooted phone to install Evennia though. + +Assuming we do not have root, we will install [Termux](https://play.google.com/store/apps/details?id=com.termux&hl=en). +Termux provides a base installation of Linux essentials, including apt and Python, and makes them available under a writeable directory. It also gives us a terminal where we can enter commands. By default, Android doesn't give you permissions to the root folder, so Termux pretends that its own installation directory is the root directory. + +Termux will set up a base system for us on first launch, but we will need to install some prerequisites for Evennia. Commands you should run in Termux will look like this: + +``` +$ cat file.txt +``` +The `$` symbol is your prompt - do not include it when running commands. + +## Prerequisites + +To install some of the libraries Evennia requires, namely Pillow and Twisted, we have to first install some packages they depend on. In Termux, run the following +``` +$ pkg install -y clang git zlib ndk-sysroot libjpeg-turbo libcrypt python +``` + +Termux ships with Python 3, perfect. Python 3 has venv (virtualenv) and pip (Python's module installer) built-in. + +So, let's set up our virtualenv. This keeps the Python packages we install separate from the system versions. + +``` +$ cd +$ python3 -m venv evenv +``` + +This will create a new folder, called `evenv`, containing the new python executable. +Next, let's activate our new virtualenv. Every time you want to work on Evennia, you need to run the following command: + +``` +$ source evenv/bin/activate +``` + +Your prompt will change to look like this: +``` +(evenv) $ +``` +Update the updaters and installers in the venv: pip, setuptools and wheel. +``` +python3 -m pip install --upgrade pip setuptools wheel +``` + +### Installing Evennia + +Now that we have everything in place, we're ready to download and install Evennia itself. + +Mysterious incantations +``` +export LDFLAGS="-L/data/data/com.termux/files/usr/lib/" +export CFLAGS="-I/data/data/com.termux/files/usr/include/" +``` +(these tell clang, the C compiler, where to find the bits for zlib when building Pillow) + +Install the latest Evennia in a way that lets you edit the source +``` +(evenv) $ pip install --upgrade -e 'git+https://github.com/evennia/evennia#egg=evennia' +``` + +This step will possibly take quite a while - we are downloading Evennia and are then installing it, building all of the requirements for Evennia to run. If you run into trouble on this step, please see [Troubleshooting](#troubleshooting). + +You can go to the dir where Evennia is installed with `cd $VIRTUAL_ENV/src/evennia`. `git grep (something)` can be handy, as can `git diff` + +### Final steps + +At this point, Evennia is installed on your phone! You can now continue with the original [Getting Started](Getting-Started) instruction, we repeat them here for clarity. + +To start a new game: + +``` +(evenv) $ evennia --init mygame +(evenv) $ ls +mygame evenv +``` + +To start the game for the first time: + +``` +(evenv) $ cd mygame +(evenv) $ evennia migrate +(evenv) $ evennia start +``` + +Your game should now be running! Open a web browser at http://localhost:4001 or point a telnet client to localhost:4000 and log in with the user you created. + +## Running Evennia + +When you wish to run Evennia, get into your Termux console and make sure you have activated your virtualenv as well as are in your game's directory. You can then run evennia start as normal. + +``` +$ cd ~ && source evenv/bin/activate +(evenv) $ cd mygame +(evenv) $ evennia start +``` + +You may wish to look at the [Linux Instructions](https://github.com/evennia/evennia/wiki/Getting-Started#linux-install) for more. + +## Caveats + +- Android's os module doesn't support certain functions - in particular getloadavg. Thusly, running the command @server in-game will throw an exception. So far, there is no fix for this problem. +- As you might expect, performance is not amazing. +- Android is fairly aggressive about memory handling, and you may find that your server process is killed if your phone is heavily taxed. Termux seems to keep a notification up to discourage this. + +## Troubleshooting + +As time goes by and errors are reported, this section will be added to. + +Some steps to try anyway: +* Make sure your packages are up-to-date, try running `pkg update && pkg upgrade -y` +* Make sure you've installed the clang package. If not, try `pkg install clang -y` +* Make sure you're in the right directory. `cd ~/mygame +* Make sure you've sourced your virtualenv. type `cd && source evenv/bin/activate` +* See if a shell will start: `cd ~/mygame ; evennia shell` +* Look at the log files in ~/mygame/server/logs/ \ No newline at end of file diff --git a/docs/failing/Nicks.md b/docs/failing/Nicks.md new file mode 100644 index 0000000000..45a885e74b --- /dev/null +++ b/docs/failing/Nicks.md @@ -0,0 +1,99 @@ + +*Nicks*, short for *Nicknames* is a system allowing an object (usually a [Account](Accounts)) to assign custom replacement names for other game entities. + +Nicks are not to be confused with *Aliases*. Setting an Alias on a game entity actually changes an inherent attribute on that entity, and everyone in the game will be able to use that alias to address the entity thereafter. A *Nick* on the other hand, is used to map a different way *you alone* can refer to that entity. Nicks are also commonly used to replace your input text which means you can create your own aliases to default commands. + +Default Evennia use Nicks in three flavours that determine when Evennia actually tries to do the substitution. + +- inputline - replacement is attempted whenever you write anything on the command line. This is the default. +- objects - replacement is only attempted when referring to an object +- accounts - replacement is only attempted when referring an account + +Here's how to use it in the default command set (using the `nick` command): + + nick ls = look + +This is a good one for unix/linux users who are accustomed to using the `ls` command in their daily life. It is equivalent to `nick/inputline ls = look`. + + nick/object mycar2 = The red sports car + +With this example, substitutions will only be done specifically for commands expecting an object reference, such as + + look mycar2 + +becomes equivalent to "`look The red sports car`". + + nick/accounts tom = Thomas Johnsson + +This is useful for commands searching for accounts explicitly: + + @find *tom + +One can use nicks to speed up input. Below we add ourselves a quicker way to build red buttons. In the future just writing *rb* will be enough to execute that whole long string. + + nick rb = @create button:examples.red_button.RedButton + +Nicks could also be used as the start for building a "recog" system suitable for an RP mud. + + nick/account Arnold = The mysterious hooded man + +The nick replacer also supports unix-style *templating*: + + nick build $1 $2 = @create/drop $1;$2 + +This will catch space separated arguments and store them in the the tags `$1` and `$2`, to be inserted in the replacement string. This example allows you to do `build box crate` and have Evennia see `@create/drop box;crate`. You may use any `$` numbers between 1 and 99, but the markers must match between the nick pattern and the replacement. + +> If you want to catch "the rest" of a command argument, make sure to put a `$` tag *with no spaces to the right of it* - it will then receive everything up until the end of the line. + +You can also use [shell-type wildcards](http://www.linfo.org/wildcard.html): + +- \* - matches everything. +- ? - matches a single character. +- [seq] - matches everything in the sequence, e.g. [xyz] will match both x, y and z +- [!seq] - matches everything *not* in the sequence. e.g. [!xyz] will match all but x,y z. + + + + + +## Coding with nicks + +Nicks are stored as the `Nick` database model and are referred from the normal Evennia [object](Objects) through the `nicks` property - this is known as the *NickHandler*. The NickHandler offers effective error checking, searches and conversion. + +```python + # A command/channel nick: + obj.nicks.add("greetjack", "tell Jack = Hello pal!") + + # An object nick: + obj.nicks.add("rose", "The red flower", nick_type="object") + + # An account nick: + obj.nicks.add("tom", "Tommy Hill", nick_type="account") + + # My own custom nick type (handled by my own game code somehow): + obj.nicks.add("hood", "The hooded man", nick_type="my_identsystem") + + # get back the translated nick: + full_name = obj.nicks.get("rose", nick_type="object") + + # delete a previous set nick + object.nicks.remove("rose", nick_type="object") +``` + +In a command definition you can reach the nick handler through `self.caller.nicks`. See the `nick` command in `evennia/commands/default/general.py` for more examples. + +As a last note, The Evennia [channel](Communications) alias systems are using nicks with the `nick_type="channel"` in order to allow users to create their own custom aliases to channels. + +# Advanced note + +Internally, nicks are [Attributes](Attributes) saved with the `db_attrype` set to "nick" (normal Attributes has this set to `None`). + +The nick stores the replacement data in the Attribute.db_value field as a tuple with four fields `(regex_nick, template_string, raw_nick, raw_template)`. Here `regex_nick` is the converted regex representation of the `raw_nick` and the `template-string` is a version of the `raw_template` prepared for efficient replacement of any `$`- type markers. The `raw_nick` and `raw_template` are basically the unchanged strings you enter to the `nick` command (with unparsed `$` etc). + +If you need to access the tuple for some reason, here's how: + +```python +tuple = obj.nicks.get("nickname", return_tuple=True) +# or, alternatively +tuple = obj.nicks.get("nickname", return_obj=True).value +``` \ No newline at end of file diff --git a/docs/failing/Spawner-and-Prototypes.md b/docs/failing/Spawner-and-Prototypes.md new file mode 100644 index 0000000000..64c42c888d --- /dev/null +++ b/docs/failing/Spawner-and-Prototypes.md @@ -0,0 +1,275 @@ + +The *spawner* is a system for defining and creating individual objects from a base template called a +*prototype*. It is only designed for use with in-game [Objects](Objects), not any other type of +entity. + +The normal way to create a custom object in Evennia is to make a [Typeclass](Typeclasses). If you +haven't read up on Typeclasses yet, think of them as normal Python classes that save to the database +behind the scenes. Say you wanted to create a "Goblin" enemy. A common way to do this would be to +first create a `Mobile` typeclass that holds everything common to mobiles in the game, like generic +AI, combat code and various movement methods. A `Goblin` subclass is then made to inherit from +`Mobile`. The `Goblin` class adds stuff unique to goblins, like group-based AI (because goblins are +smarter in a group), the ability to panic, dig for gold etc. + +But now it's time to actually start to create some goblins and put them in the world. What if we +wanted those goblins to not all look the same? Maybe we want grey-skinned and green-skinned goblins +or some goblins that can cast spells or which wield different weapons? We *could* make subclasses of +`Goblin`, like `GreySkinnedGoblin` and `GoblinWieldingClub`. But that seems a bit excessive (and a +lot of Python code for every little thing). Using classes can also become impractical when wanting +to combine them - what if we want a grey-skinned goblin shaman wielding a spear - setting up a web +of classes inheriting each other with multiple inheritance can be tricky. + +This is what the *prototype* is for. It is a Python dictionary that describes these per-instance +changes to an object. The prototype also has the advantage of allowing an in-game builder to +customize an object without access to the Python backend. Evennia also allows for saving and +searching prototypes so other builders can find and use (and tweak) them later. Having a library of +interesting prototypes is a good reasource for builders. The OLC system allows for creating, saving, +loading and manipulating prototypes using a menu system. + +The *spawner* takes a prototype and uses it to create (spawn) new, custom objects. + +## Using the OLC + +Enter the `olc` command or `@spawn/olc` to enter the prototype wizard. This is a menu system for +creating, loading, saving and manipulating prototypes. It's intended to be used by in-game builders +and will give a better understanding of prototypes in general. Use `help` on each node of the menu +for more information. Below are further details about how prototypes work and how they are used. + +## The prototype + +The prototype dictionary can either be created for you by the OLC (see above), be written manually in a Python module (and then referenced by the `@spawn` command/OLC), or created on-the-fly and manually loaded into the spawner function or `@spawn` command. + +The dictionary defines all possible database-properties of an Object. It has a fixed set of allowed keys. When preparing to store the prototype in the database (or when using the OLC), some +of these keys are mandatory. When just passing a one-time prototype-dict to the spawner the system is +more lenient and will use defaults for keys not explicitly provided. + +In dictionary form, a prototype can look something like this: + +```python +{ + "prototype_key": "house" + "key": "Large house" + "typeclass": "typeclasses.rooms.house.House" + } +``` +If you wanted to load it into the spawner in-game you could just put all on one line: + + @spawn {"prototype_key="house", "key": "Large house", ...} + +> Note that the prototype dict as given on the command line must be a valid Python structure - +so you need to put quotes around strings etc. For security reasons, a dict inserted from-in game cannot have any +other advanced Python functionality, such as executable code, `lambda` etc. If builders are supposed +to be able to use such features, you need to offer them through [$protfuncs](#protfuncs), embedded runnable functions that you have full control to check and vet before running. + +### Prototype keys + +All keys starting with `prototype_` are for book keeping. + + - `prototype_key` - the 'name' of the prototype. While this can sometimes be skipped (such as when + defining a prototype in a module or feeding a prototype-dict manually to the spawner function), it's good + practice to try to include this. It is used for book-keeping and storing of the prototype so you + can find it later. + - `prototype_parent` - If given, this should be the `prototype_key` of another prototype stored in + the system or available in a module. This makes this prototype *inherit* the keys from the + parent and only override what is needed. Give a tuple `(parent1, parent2, ...)` for multiple + left-right inheritance. If this is not given, a `typeclass` should usually be defined (below). + - `prototype_desc` - this is optional and used when listing the prototype in in-game listings. + - `protototype_tags` - this is optional and allows for tagging the prototype in order to find it + easier later. + - `prototype_locks` - two lock types are supported: `edit` and `spawn`. The first lock restricts + the copying and editing of the prototype when loaded through the OLC. The second determines who + may use the prototype to create new objects. + +The remaining keys determine actual aspects of the objects to spawn from this prototype: + + - `key` - the main object identifier. Defaults to "Spawned Object *X*", where *X* is a random integer. + - `typeclass` - A full python-path (from your gamedir) to the typeclass you want to use. If not set, the `prototype_parent` should be + defined, with `typeclass` defined somewhere in the parent chain. When creating a one-time prototype + dict just for spawning, one could omit this - `settings.BASE_OBJECT_TYPECLASS` will be used instead. + - `location` - this should be a `#dbref`. + - `home` - a valid `#dbref`. Defaults to `location` or `settings.DEFAULT_HOME` if location does not exist. + - `destination` - a valid `#dbref`. Only used by exits. + - `permissions` - list of permission strings, like `["Accounts", "may_use_red_door"]` + - `locks` - a [lock-string](Locks) like `"edit:all();control:perm(Builder)"` + - `aliases` - list of strings for use as aliases + - `tags` - list [Tags](Tags). These are given as tuples `(tag, category, data)`. + - `attrs` - list of [Attributes](Attributes). These are given as tuples `(attrname, value, category, lockstring)` + - Any other keywords are interpreted as non-category [Attributes](Attributes) and their values. This is + convenient for simple Attributes - use `attrs` for full control of Attributes. + +Deprecated as of Evennia 0.8: + + - `ndb_` - sets the value of a non-persistent attribute (`"ndb_"` is stripped from the name). + This is simply not useful in a prototype and is deprecated. + - `exec` - This accepts a code snippet or a list of code snippets to run. This should not be used - + use callables or [$protfuncs](#protfuncs) instead (see below). + +### Prototype values + +The prototype supports values of several different types. + +It can be a hard-coded value: + +```python + {"key": "An ugly goblin", ...} + +``` + +It can also be a *callable*. This callable is called without arguments whenever the prototype is used to +spawn a new object: + +```python + {"key": _get_a_random_goblin_name, ...} + +``` + +By use of Python `lambda` one can wrap the callable so as to make immediate settings in the +prototype: + +```python + {"key": lambda: random.choice(("Urfgar", "Rick the smelly", "Blargh the foul", ...)), ...} + +``` + +#### Protfuncs + +Finally, the value can be a *prototype function* (*Protfunc*). These look like simple function calls that you embed in strings and that has a `$` in front, like + +```python + {"key": "$choice(Urfgar, Rick the smelly, Blargh the foul)", + "attrs": {"desc": "This is a large $red(and very red) demon. " + "He has $randint(2,5) skulls in a chain around his neck."} +``` +At execution time, the place of the protfunc will be replaced with the result of that protfunc being called (this is always a string). A protfunc works in much the same way as an +[InlineFunc](https://github.com/evennia/evennia/wiki/TextTags#inline-functions) - they are actually +parsed using the same parser - except protfuncs are run every time the prototype is used to spawn a new object (whereas an inlinefunc is called when a text is returned to the user). + +Here is how a protfunc is defined (same as an inlinefunc). + +```python +# this is a silly example, you can just color the text red with |r directly! +def red(*args, **kwargs): + """ + Usage: $red() + Returns the same text you entered, but red. + """ + if not args or len(args) > 1: + raise ValueError("Must have one argument, the text to color red!") + return "|r{}|n".format(args[0]) +``` + +> Note that we must make sure to validate input and raise `ValueError` if that fails. Also, it is *not* possible to use keywords in the call to the protfunc (so something like `$echo(text, align=left)` is invalid). The `kwargs` requred is for internal evennia use and not used at all for protfuncs (only by inlinefuncs). + +To make this protfunc available to builders in-game, add it to a new module and add the path to that module to `settings.PROT_FUNC_MODULES`: + +```python +# in mygame/server/conf/settings.py + +PROT_FUNC_MODULES += ["world.myprotfuncs"] + +``` +All *global callables* in your added module will be considered a new protfunc. To avoid this (e.g. to have helper functions that are not protfuncs on their own), name your function something starting with `_`. + +The default protfuncs available out of the box are defined in `evennia/prototypes/profuncs.py`. To override the ones available, just add the same-named function in your own protfunc module. + +| Protfunc | Description | +|----------|-------------| +| `$random()` | Returns random value in range [0, 1) | +| `$randint(start, end)` | Returns random value in range [start, end] | +| `$left_justify()` | Left-justify text | +| `$right_justify()` | Right-justify text to screen width | +| `$center_justify()` | Center-justify text to screen width | +| `$full_justify()` | Spread text across screen width by adding spaces | +| `$protkey()` | Returns value of another key in this prototype (self-reference) | +| `$add(, )` | Returns value1 + value2. Can also be lists, dicts etc | +| `$sub(, )` | Returns value1 - value2 | +| `$mult(, )` | Returns value1 * value2 | +| `$div(, )` | Returns value2 / value1 | +| `$toint()` | Returns value converted to integer (or value if not possible) | +| `$eval()` | Returns result of [literal-eval](https://docs.python.org/2/library/ast.html#ast.literal_eval) of code string. Only simple python expressions. | +| `$obj()` | Returns object #dbref searched globally by key, tag or #dbref. Error if more than one found." | +| `$objlist()` | Like `$obj`, except always returns a list of zero, one or more results. | +| `$dbref(dbref)` | Returns argument if it is formed as a #dbref (e.g. #1234), otherwise error. + +For developers with access to Python, using protfuncs in prototypes is generally not useful. Passing real Python functions is a lot more powerful and flexible. Their main use is to allow in-game builders to +do limited coding/scripting for their prototypes without giving them direct access to raw Python. + +## Storing prototypes + +A prototype can be defined and stored in two ways, either in the database or as a dict in a module. + +### Database prototypes + +Stored as [Scripts](Scripts) in the database. These are sometimes referred to as *database-prototypes* This is the only way for in-game builders to modify and add prototypes. They have the advantage of being easily modifiable and sharable between builders but you need to work with them using in-game tools. + +### Module-based prototypes + +These prototypes are defined as dictionaries assigned to global variables in one of the modules defined in `settings.PROTOTYPE_MODULES`. They can only be modified from outside the game so they are are necessarily "read-only" from in-game and cannot be modified (but copies of them could be made into database-prototypes). These were the only prototypes available before Evennia 0.8. Module based prototypes can be useful in order for developers to provide read-only "starting" or "base" prototypes to build from or if they just prefer to work offline in an external code editor. + +By default `mygame/world/prototypes.py` is set up for you to add your own prototypes. *All global dicts* in this module will be considered by Evennia to be a prototype. You could also tell Evennia to look for prototypes in more modules if you want: + +```python +# in mygame/server/conf.py + +PROTOTYPE_MODULES = += ["world.myownprototypes", "combat.prototypes"] + +``` + +Here is an example of a prototype defined in a module: + + ```python + # in a module Evennia looks at for prototypes, + # (like mygame/world/prototypes.py) + + ORC_SHAMAN = {"key":"Orc shaman", + "typeclass": "typeclasses.monsters.Orc", + "weapon": "wooden staff", + "health": 20} + ``` + +> Note that in the example above, `"ORC_SHAMAN"` will become the `prototype_key` of this prototype. +> It's the only case when `prototype_key` can be skipped in a prototype. However, if `prototype_key` +> was given explicitly, that would take precedence. This is a legacy behavior and it's recommended +> that you always add `prototype_key` to be consistent. + + +## Using @spawn + +The spawner can be used from inside the game through the Builder-only `@spawn` command. Assuming the +"goblin" typeclass is available to the system (either as a database-prototype or read from module), +you can spawn a new goblin with + + @spawn goblin + +You can also specify the prototype directly as a valid Python dictionary: + + @spawn {"prototype_key": "shaman", \ + "key":"Orc shaman", \ + "prototype_parent": "goblin", \ + "weapon": "wooden staff", \ + "health": 20} + +> Note: The `@spawn` command is more lenient about the prototype dictionary than shown here. So you can for example skip the `prototype_key` if you are just testing a throw-away prototype. A random hash will be used to please the validation. You could also skip `prototype_parent/typeclass` - then the typeclass given by `settings.BASE_OBJECT_TYPECLASS` will be used. + +## Using evennia.prototypes.spawner() + +In code you access the spawner mechanism directly via the call + +```python + new_objects = evennia.prototypes.spawner.spawn(*prototypes) +``` + +All arguments are prototype dictionaries. The function will return a +matching list of created objects. Example: + +```python + obj1, obj2 = evennia.prototypes.spawner.spawn({"key": "Obj1", "desc": "A test"}, + {"key": "Obj2", "desc": "Another test"}) +``` +> Hint: Same as when using `@spawn`, when spawning from a one-time prototype dict like this, you can skip otherwise required keys, like `prototype_key` or `typeclass`/`prototype_parent`. Defaults will be used. + +Note that no `location` will be set automatically when using `evennia.prototypes.spawner.spawn()`, you +have to specify `location` explicitly in the prototype dict. + +If the prototypes you supply are using `prototype_parent` keywords, the spawner will read prototypes from modules +in `settings.PROTOTYPE_MODULES` as well as those saved to the database to determine the body of available parents. The `spawn` command takes many optional keywords, you can find its definition [in the api docs](https://github.com/evennia/evennia/wiki/evennia.prototypes.spawner#spawn). \ No newline at end of file