Refactoring contribs

This commit is contained in:
Griatch 2021-12-18 18:02:37 +01:00
parent f5f75bd04d
commit 0ab1c30716
103 changed files with 3203 additions and 604 deletions

View file

@ -1,24 +1,25 @@
# AWSstorage system
# AWSstorage system
Contrib by The Right Honourable Reverend (trhr) 2020
## What is this for?
## What is this for?
This plugin migrates the Web-based portion of Evennia, namely images,
javascript, and other items located inside staticfiles into Amazon AWS (S3) for hosting.
javascript, and other items located inside staticfiles into Amazon AWS (S3) for
hosting.
Files hosted on S3 are "in the cloud," and while your personal
server may be sufficient for serving multimedia to a minimal number of users,
the perfect use case for this plugin would be:
- Servers supporting heavy web-based traffic (webclient, etc) ...
- Servers supporting heavy web-based traffic (webclient, etc) ...
- With a sizable number of users ...
- Where the users are globally distributed ...
- Where multimedia files are served to users as a part of gameplay
Bottom line - if you're sending an image to a player every time they traverse a
map, the bandwidth reduction of using this will be substantial. If not,
probably skip this contrib.
map, the bandwidth reduction of using this will be substantial. If not, probably
skip this contrib.
## On costs
@ -35,14 +36,14 @@ pricing structure.
This is a drop-in replacement that operates deeper than all of Evennia's code,
so your existing code does not need to change at all to support it.
For example, when Evennia (or Django), tries to save a file permanently
(say, an image uploaded by a user), the save (or load) communication follows the path:
For example, when Evennia (or Django), tries to save a file permanently (say, an
image uploaded by a user), the save (or load) communication follows the path:
Evennia -> Django
Django -> Storage backend
Storage backend -> file storage location (e.g. hard drive)
Evennia -> Django
Django -> Storage backend
Storage backend -> file storage location (e.g. hard drive)
https://docs.djangoproject.com/en/3.0/ref/settings/#std:setting-STATICFILES_STORAGE
[django docs](https://docs.djangoproject.com/en/3.0/ref/settings/#std:setting-STATICFILES_STORAGE)
This plugin, when enabled, overrides the default storage backend,
which defaults to saving files at mygame/website/, instead,
@ -111,7 +112,7 @@ Advanced Users: The second IAM statement, CreateBucket, is only needed
for initial installation. You can remove it later, or you can
create the bucket and set the ACL yourself before you continue.
## Dependencies
## Dependencies
This package requires the dependency "boto3 >= 1.4.4", the official
@ -120,14 +121,14 @@ extra requirements;
- Activate your `virtualenv`
- `cd` to the root of the Evennia repository. There should be an `requirements_extra.txt`
file here.
- `pip install -r requirements_extra.txt`
file here.
- `pip install -r requirements_extra.txt`
## Configure Evennia
## Configure Evennia
Customize the variables defined below in `secret_settings.py`. No further
configuration is needed. Note the three lines that you need to set to your
actual values.
actual values.
```python
# START OF SECRET_SETTINGS.PY COPY/PASTE >>>
@ -145,7 +146,7 @@ AWS_S3_OBJECT_PARAMETERS = { 'Expires': 'Thu, 31 Dec 2099 20:00:00 GMT',
AWS_DEFAULT_ACL = 'public-read'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % settings.AWS_BUCKET_NAME
AWS_AUTO_CREATE_BUCKET = True
STATICFILES_STORAGE = 'evennia.contrib.awsstorage.aws-s3-cdn.S3Boto3Storage'
STATICFILES_STORAGE = 'evennia.contrib.base_systems.awsstorage.aws-s3-cdn.S3Boto3Storage'
# <<< END OF SECRET_SETTINGS.PY COPY/PASTE
```
@ -153,14 +154,14 @@ STATICFILES_STORAGE = 'evennia.contrib.awsstorage.aws-s3-cdn.S3Boto3Storage'
You may also store these keys as environment variables of the same name.
For advanced configuration, refer to the docs for django-storages.
After copying the above, run `evennia reboot`.
After copying the above, run `evennia reboot`.
## Check that it works
Confirm that web assets are being served from S3 by visiting your website, then
checking the source of any image (for instance, the logo). It should read
`https://your-bucket-name.s3.amazonaws.com/path/to/file`. If so, the system
works and you shouldn't need to do anything else.
works and you shouldn't need to do anything else.
# Uninstallation

View file

@ -1,3 +1,4 @@
"""
Intended to be a collecting folder for Django-specific contribs that do not have observable effects to players.
AWS storage system contrib - trhr 2020
"""

View file

@ -0,0 +1,127 @@
# Building menu
Module containing the building menu system.
Evennia contributor: vincent-lg 2018
Building menus are in-game menus, not unlike `EvMenu` though using a
different approach. Building menus have been specifically designed to edit
information as a builder. Creating a building menu in a command allows
builders quick-editing of a given object, like a room. If you follow the
steps below to add the contrib, you will have access to an `@edit` command
that will edit any default object offering to change its key and description.
## Install
1. Import the `GenericBuildingCmd` class from this contrib in your `mygame/commands/default_cmdset.py` file:
```python
from evennia.base_systems.contrib.building_menu import GenericBuildingCmd
```
2. Below, add the command in the `CharacterCmdSet`:
```python
# ... These lines should exist in the file
class CharacterCmdSet(default_cmds.CharacterCmdSet):
key = "DefaultCharacter"
def at_cmdset_creation(self):
super(CharacterCmdSet, self).at_cmdset_creation()
# ... add the line below
self.add(GenericBuildingCmd())
```
## Usage
The `edit` command will allow you to edit any object. You will need to
specify the object name or ID as an argument. For instance: `edit here`
will edit the current room. However, building menus can perform much more
than this very simple example, read on for more details.
Building menus can be set to edit about anything. Here is an example of
output you could obtain when editing the room:
```
Editing the room: Limbo(#2)
[T]itle: the limbo room
[D]escription
This is the limbo room. You can easily change this default description,
either by using the |y@desc/edit|n command, or simply by entering this
menu (enter |yd|n).
[E]xits:
north to A parking(#4)
[Q]uit this menu
```
From there, you can open the title choice by pressing t. You can then
change the room title by simply entering text, and go back to the
main menu entering @ (all this is customizable). Press q to quit this menu.
The first thing to do is to create a new module and place a class
inheriting from `BuildingMenu` in it.
```python
from evennia.contrib.base_systems.building_menu import BuildingMenu
class RoomBuildingMenu(BuildingMenu):
# ...
```
Next, override the `init` method (not `__init__`!). You can add
choices (like the title, description, and exits choices as seen above) by using
the `add_choice` method.
```python
class RoomBuildingMenu(BuildingMenu):
def init(self, room):
self.add_choice("title", "t", attr="key")
```
That will create the first choice, the title choice. If one opens your menu
and enter t, she will be in the title choice. She can change the title
(it will write in the room's `key` attribute) and then go back to the
main menu using `@`.
`add_choice` has a lot of arguments and offers a great deal of
flexibility. The most useful ones is probably the usage of callbacks,
as you can set almost any argument in `add_choice` to be a callback, a
function that you have defined above in your module. This function will be
called when the menu element is triggered.
Notice that in order to edit a description, the best method to call isn't
`add_choice`, but `add_choice_edit`. This is a convenient shortcut
which is available to quickly open an `EvEditor` when entering this choice
and going back to the menu when the editor closes.
```python
class RoomBuildingMenu(BuildingMenu):
def init(self, room):
self.add_choice("title", "t", attr="key")
self.add_choice_edit("description", key="d", attr="db.desc")
```
When you wish to create a building menu, you just need to import your
class, create it specifying your intended caller and object to edit,
then call `open`:
```python
from <wherever> import RoomBuildingMenu
class CmdEdit(Command):
key = "redit"
def func(self):
menu = RoomBuildingMenu(self.caller, self.caller.location)
menu.open()
```
This is a very short introduction. For more details, see the [online
tutorial](https://github.com/evennia/evennia/wiki/Building-menus) or read the
heavily-documented code of the contrib itself.

View file

@ -0,0 +1,6 @@
"""
Build-menu contrib - vincent-lg 2018
"""
from .building_menu import GenericBuildingCmd # noqa
from .building_menu import BuildingMenu # noqa

View file

@ -10,10 +10,12 @@ builders quick-editing of a given object, like a room. If you follow the
steps below to add the contrib, you will have access to an `@edit` command
that will edit any default object offering to change its key and description.
1. Import the `GenericBuildingCmd` class from this contrib in your `mygame/commands/default_cmdset.py` file:
1. Import the `GenericBuildingCmd` class from this contrib in your
`mygame/commands/default_cmdset.py` file:
```python
from evennia.contrib.building_menu import GenericBuildingCmd
from evennia.contrib.base_systems.building_menu import GenericBuildingCmd
```
2. Below, add the command in the `CharacterCmdSet`:
@ -58,10 +60,11 @@ The first thing to do is to create a new module and place a class
inheriting from `BuildingMenu` in it.
```python
from evennia.contrib.building_menu import BuildingMenu
from evennia.contrib.building_menu.building_menu import BuildingMenu
class RoomBuildingMenu(BuildingMenu):
# ...
```
Next, override the `init` method. You can add choices (like the title,

View file

@ -0,0 +1,57 @@
# Color markups
Contribution, Griatch 2017
Additional color markup styles for Evennia (extending or replacing the default
`|r`, `|234` etc).
## Installation
Import the desired style variables from this module into
mygame/server/conf/settings.py and add them to the settings variables below.
Each are specified as a list, and multiple such lists can be added to each
variable to support multiple formats. Note that list order affects which regexes
are applied first. You must restart both Portal and Server for color tags to
update.
Assign to the following settings variables (see below for example):
COLOR_ANSI_EXTRA_MAP - a mapping between regexes and ANSI colors
COLOR_XTERM256_EXTRA_FG - regex for defining XTERM256 foreground colors
COLOR_XTERM256_EXTRA_BG - regex for defining XTERM256 background colors
COLOR_XTERM256_EXTRA_GFG - regex for defining XTERM256 grayscale foreground colors
COLOR_XTERM256_EXTRA_GBG - regex for defining XTERM256 grayscale background colors
COLOR_ANSI_BRIGHT_BG_EXTRA_MAP = ANSI does not support bright backgrounds; we fake
this by mapping ANSI markup to matching bright XTERM256 backgrounds
COLOR_NO_DEFAULT - Set True/False. If False (default), extend the default
markup, otherwise replace it completely.
## Example
To add the {- "curly-bracket" style, add the following to your settings file,
then reboot both Server and Portal:
```python
from evennia.contrib.base_systems import color_markups
COLOR_ANSI_EXTRA_MAP = color_markups.CURLY_COLOR_ANSI_EXTRA_MAP
COLOR_XTERM256_EXTRA_FG = color_markups.CURLY_COLOR_XTERM256_EXTRA_FG
COLOR_XTERM256_EXTRA_BG = color_markups.CURLY_COLOR_XTERM256_EXTRA_BG
COLOR_XTERM256_EXTRA_GFG = color_markups.CURLY_COLOR_XTERM256_EXTRA_GFG
COLOR_XTERM256_EXTRA_GBG = color_markups.CURLY_COLOR_XTERM256_EXTRA_GBG
COLOR_ANSI_BRIGHT_BG_EXTRA_MAP = color_markups.CURLY_COLOR_ANSI_BRIGHT_BG_EXTRA_MAP
```
To add the `%c-` "mux/mush" style, add the following to your settings file, then
reboot both Server and Portal:
```python
from evennia.contrib import color_markups
COLOR_ANSI_EXTRA_MAP = color_markups.MUX_COLOR_ANSI_EXTRA_MAP
COLOR_XTERM256_EXTRA_FG = color_markups.MUX_COLOR_XTERM256_EXTRA_FG
COLOR_XTERM256_EXTRA_BG = color_markups.MUX_COLOR_XTERM256_EXTRA_BG
COLOR_XTERM256_EXTRA_GFG = color_markups.MUX_COLOR_XTERM256_EXTRA_GFG
COLOR_XTERM256_EXTRA_GBG = color_markups.MUX_COLOR_XTERM256_EXTRA_GBG
COLOR_ANSI_BRIGHT_BGS_EXTRA_MAP = color_markups.CURLY_COLOR_ANSI_BRIGHT_BGS_EXTRA_MAP
```

View file

@ -0,0 +1,6 @@
"""
Color markups contrib - Griatch 2017
"""
from .color_markups import * # noqa

View file

@ -30,7 +30,7 @@ Assign to the following settings variables:
To add the {- "curly-bracket" style, add the following to your settings file, then reboot both
Server and Portal:
from evennia.contrib import color_markups
from evennia.contrib.base_systems import color_markups
COLOR_ANSI_EXTRA_MAP = color_markups.CURLY_COLOR_ANSI_EXTRA_MAP
COLOR_XTERM256_EXTRA_FG = color_markups.CURLY_COLOR_XTERM256_EXTRA_FG
COLOR_XTERM256_EXTRA_BG = color_markups.CURLY_COLOR_XTERM256_EXTRA_BG
@ -42,7 +42,7 @@ COLOR_ANSI_BRIGHT_BG_EXTRA_MAP = color_markups.CURLY_COLOR_ANSI_BRIGHT_BG_EXTRA_
To add the %c- "mux/mush" style, add the following to your settings file, then reboot both Server
and Portal:
from evennia.contrib import color_markups
from evennia.contrib.base_systems import color_markups
COLOR_ANSI_EXTRA_MAP = color_markups.MUX_COLOR_ANSI_EXTRA_MAP
COLOR_XTERM256_EXTRA_FG = color_markups.MUX_COLOR_XTERM256_EXTRA_FG
COLOR_XTERM256_EXTRA_BG = color_markups.MUX_COLOR_XTERM256_EXTRA_BG

View file

@ -0,0 +1,47 @@
# Custom gameime
Contrib - Griatch 2017, vlgeoff 2017
This reimplements the `evennia.utils.gametime` module but supporting a custom
calendar for your game world. It allows for scheduling events to happen at given
in-game times, taking this custom calendar into account.
## Installation
Import and use this in the same way as you would the normal
`evennia.utils.gametime` module.
Customize the calendar by adding a `TIME_UNITS` dict to your settings (see
example below).
## Usage:
```python
from evennia.contrib.base_systems import custom_gametime
gametime = custom_gametime.realtime_to_gametime(days=23)
# scedule an event to fire every in-game 10 hours
custom_gametime.schedule(callback, repeat=True, hour=10)
```
The calendar can be customized by adding the `TIME_UNITS` dictionary to your
settings file. This maps unit names to their length, expressed in the smallest
unit. Here's the default as an example:
TIME_UNITS = {
"sec": 1,
"min": 60,
"hr": 60 * 60,
"hour": 60 * 60,
"day": 60 * 60 * 24,
"week": 60 * 60 * 24 * 7,
"month": 60 * 60 * 24 * 7 * 4,
"yr": 60 * 60 * 24 * 7 * 4 * 12,
"year": 60 * 60 * 24 * 7 * 4 * 12, }
When using a custom calendar, these time unit names are used as kwargs to
the converter functions in this module. Even if your calendar uses other names
for months/weeks etc the system needs the default names internally.

View file

@ -0,0 +1,6 @@
"""
Custom gametime contrib - Griatch, vlgeoff 2017
"""
from .custom_gametime import * # noqa

View file

@ -0,0 +1,30 @@
# Email-based login system
Evennia contrib - Griatch 2012
This is a variant of the login system that requires an email-address
instead of a username to login.
This used to be the default Evennia login before replacing it with a
more standard username + password system (having to supply an email
for some reason caused a lot of confusion when people wanted to expand
on it. The email is not strictly needed internally, nor is any
confirmation email sent out anyway).
## Installation
To your settings file, add/edit the line:
```python
CMDSET_UNLOGGEDIN = "contrib.base_systems.email_login.UnloggedinCmdSet"
CONNECTION_SCREEN_MODULE = "contrib.base_systems.email_login.connection_screens"
```
That's it. Reload the server and reconnect to see it.
## Notes:
If you want to modify the way the connection screen looks, point
`CONNECTION_SCREEN_MODULE` to your own module. Use the default as a
guide (see also Evennia docs).

View file

@ -0,0 +1,7 @@
"""
Email login contrib - Griatch 2012
"""
from .email_login import UnloggedinCmdSet # noqa
from . import connection_screens # noqa

View file

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
"""
Connection screen
This is the text to show the user when they first connect to the game (before
they log in).
To change the login screen in this module, do one of the following:
- Define a function `connection_screen()`, taking no arguments. This will be
called first and must return the full string to act as the connection screen.
This can be used to produce more dynamic screens.
- Alternatively, define a string variable in the outermost scope of this module
with the connection string that should be displayed. If more than one such
variable is given, Evennia will pick one of them at random.
The commands available to the user when the connection screen is shown
are defined in evennia.default_cmds.UnloggedinCmdSet. The parsing and display
of the screen is done by the unlogged-in "look" command.
"""
from django.conf import settings
from evennia import utils
CONNECTION_SCREEN = """
|b==============================================================|n
Welcome to |g{}|n, version {}!
If you have an existing account, connect to it by typing:
|wconnect <email> <password>|n
If you need to create an account, type (without the <>'s):
|wcreate <username> <email> <password>|n
Enter |whelp|n for more info. |wlook|n will re-show this screen.
|b==============================================================|n""".format(
settings.SERVERNAME, utils.get_evennia_version("short")
)

View file

@ -16,10 +16,12 @@ confirmation email sent out anyway).
Installation is simple:
To your settings file, add/edit the line:
To your settings file, add/edit settings as follows:
```python
CMDSET_UNLOGGEDIN = "contrib.email_login.UnloggedinCmdSet"
CMDSET_UNLOGGEDIN = "contrib.base_systems.email_login.email_login.UnloggedinCmdSet"
CONNECTION_SCREEN_MODULE = "contrib.base_systems.email_login.connection_screens"
```
That's it. Reload the server and try to log in to see it.

View file

@ -2,54 +2,62 @@
Vincent Le Goff 2017
This contrib adds the system of in-game Python in Evennia, allowing immortals (or other trusted builders) to
dynamically add features to individual objects. Using custom Python set in-game, every immortal or privileged users
could have a specific room, exit, character, object or something else behave differently from its
"cousins". For these familiar with the use of softcode in MU`*`, like SMAUG MudProgs, the ability to
add arbitrary behavior to individual objects is a step toward freedom. Keep in mind, however, the
warning below, and read it carefully before the rest of the documentation.
This contrib adds the system of in-game Python in Evennia, allowing immortals
(or other trusted builders) to dynamically add features to individual objects.
Using custom Python set in-game, every immortal or privileged users could have a
specific room, exit, character, object or something else behave differently from
its "cousins". For these familiar with the use of softcode in MU`*`, like SMAUG
MudProgs, the ability to add arbitrary behavior to individual objects is a step
toward freedom. Keep in mind, however, the warning below, and read it carefully
before the rest of the documentation.
## A WARNING REGARDING SECURITY
Evennia's in-game Python system will run arbitrary Python code without much restriction. Such a system is as
powerful as potentially dangerous, and you will have to keep in mind these points before deciding to
install it:
Evennia's in-game Python system will run arbitrary Python code without much
restriction. Such a system is as powerful as potentially dangerous, and you
will have to keep in mind these points before deciding to install it:
1. Untrusted people can run Python code on your game server with this system. Be careful about who
can use this system (see the permissions below).
2. You can do all of this in Python outside the game. The in-game Python system is not to replace all your
game feature.
1. Untrusted people can run Python code on your game server with this system.
Be careful about who can use this system (see the permissions below).
2. You can do all of this in Python outside the game. The in-game Python system
is not to replace all your game feature.
## Basic structure and vocabulary
- At the basis of the in-game Python system are **events**. An **event** defines the context in which we
would like to call some arbitrary code. For instance, one event is
defined on exits and will fire every time a character traverses through this exit. Events are described
on a [typeclass](https://github.com/evennia/evennia/wiki/Typeclasses) (like
[exits](https://github.com/evennia/evennia/wiki/Objects#exits) in our example). All objects inheriting
from this typeclass will have access to this event.
- **Callbacks** can be set on individual objects, on events defined in code. These **callbacks**
can contain arbitrary code and describe a specific behavior for an object. When the event fires,
all callbacks connected to this object's event are executed.
- At the basis of the in-game Python system are **events**. An **event**
defines the context in which we would like to call some arbitrary code. For
instance, one event is defined on exits and will fire every time a character
traverses through this exit. Events are described on a [typeclass](Typeclasses)
([exits](Objects#exits) in our example). All objects inheriting from this
typeclass will have access to this event.
- **Callbacks** can be set on individual objects, on events defined in code.
These **callbacks** can contain arbitrary code and describe a specific
behavior for an object. When the event fires, all callbacks connected to this
object's event are executed.
To see the system in context, when an object is picked up (using the default `get` command), a
specific event is fired:
To see the system in context, when an object is picked up (using the default
`get` command), a specific event is fired:
1. The event "get" is set on objects (on the `Object` typeclass).
2. When using the "get" command to pick up an object, this object's `at_get` hook is called.
3. A modified hook of DefaultObject is set by the event system. This hook will execute (or call)
the "get" event on this object.
4. All callbacks tied to this object's "get" event will be executed in order. These callbacks act
as functions containing Python code that you can write in-game, using specific variables that
will be listed when you edit the callback itself.
5. In individual callbacks, you can add multiple lines of Python code that will be fired at this
point. In this example, the `character` variable will contain the character who has picked up
the object, while `obj` will contain the object that was picked up.
2. When using the "get" command to pick up an object, this object's `at_get`
hook is called.
3. A modified hook of DefaultObject is set by the event system. This hook will
execute (or call) the "get" event on this object.
4. All callbacks tied to this object's "get" event will be executed in order.
These callbacks act as functions containing Python code that you can write
in-game, using specific variables that will be listed when you edit the callback
itself.
5. In individual callbacks, you can add multiple lines of Python code that will
be fired at this point. In this example, the `character` variable will
contain the character who has picked up the object, while `obj` will contain the
object that was picked up.
Following this example, if you create a callback "get" on the object "a sword", and put in it:
Following this example, if you create a callback "get" on the object "a sword",
and put in it:
```python
character.msg("You have picked up {} and have completed this quest!".format(obj.get_display_name(character)))
```
When you pick up this object you should see something like:
@ -59,11 +67,13 @@ When you pick up this object you should see something like:
## Installation
Being in a separate contrib, the in-game Python system isn't installed by default. You need to do it
manually, following these steps:
Being in a separate contrib, the in-game Python system isn't installed by
default. You need to do it manually, following these steps:
This is the quick summary. Scroll down for more detailed help on each step.
1. Launch the main script (important!):
```@py evennia.create_script("evennia.contrib.ingame_python.scripts.EventHandler")```
```py evennia.create_script("evennia.contrib.base_systems.ingame_python.scripts.EventHandler")```
2. Set the permissions (optional):
- `EVENTS_WITH_VALIDATION`: a group that can edit callbacks, but will need approval (default to
`None`).
@ -72,7 +82,7 @@ manually, following these steps:
- `EVENTS_VALIDATING`: a group that can validate callbacks (default to `"immortals"`).
- `EVENTS_CALENDAR`: type of the calendar to be used (either `None`, `"standard"` or `"custom"`,
default to `None`).
3. Add the `@call` command.
3. Add the `call` command.
4. Inherit from the custom typeclasses of the in-game Python system.
- `evennia.contrib.ingame_python.typeclasses.EventCharacter`: to replace `DefaultCharacter`.
- `evennia.contrib.ingame_python.typeclasses.EventExit`: to replace `DefaultExit`.
@ -89,7 +99,7 @@ that a 'callback' property is not defined. After performing step `1` the error w
To start the event script, you only need a single command, using `@py`.
@py evennia.create_script("evennia.contrib.ingame_python.scripts.EventHandler")
py evennia.create_script("evennia.contrib.base_systems.ingame_python.scripts.EventHandler")
This command will create a global script (that is, a script independent from any object). This
script will hold basic configuration, individual callbacks and so on. You may access it directly,
@ -107,8 +117,7 @@ By default, callbacks can only be created by immortals: no one except the immort
callbacks, and immortals don't need validation. It can easily be changed, either through settings
or dynamically by changing permissions of users.
The events contrib adds three
[permissions](https://github.com/evennia/evennia/wiki/Locks#permissions) in the settings. You can
The ingame-python contrib adds three [permissions](Permissions)) in the settings. You can
override them by changing the settings into your `server/conf/settings.py` file (see below for an
example). The settings defined in the events contrib are:
@ -142,9 +151,7 @@ calendar you are using. By default, time-related events are disabled. You can
`EVENTS_CALENDAR` to set it to:
- `"standard"`: the standard calendar, with standard days, months, years and so on.
- `"custom"`: a custom calendar that will use the
[custom_gametime](https://github.com/evennia/evennia/blob/master/evennia/contrib/custom_gametime.py)
contrib to schedule events.
- `"custom"`: a custom calendar that will use the `custom_gametime` contrib to schedule events.
This contrib defines two additional permissions that can be set on individual users:
@ -156,17 +163,17 @@ This contrib defines two additional permissions that can be set on individual us
For instance, to give the right to edit callbacks without needing approval to the player 'kaldara',
you might do something like:
@perm *kaldara = events_without_validation
perm *kaldara = events_without_validation
To remove this same permission, just use the `/del` switch:
@perm/del *kaldara = events_without_validation
perm/del *kaldara = events_without_validation
The rights to use the `@call` command are directly related to these permissions: by default, only
The rights to use the `call` command are directly related to these permissions: by default, only
users who have the `events_without_validation` permission or are in (or above) the group defined in
the `EVENTS_WITH_VALIDATION` setting will be able to call the command (with different switches).
### Adding the `@call` command
### Adding the `call` command
You also have to add the `@call` command to your Character CmdSet. This command allows your users
to add, edit and delete callbacks in-game. In your `commands/default_cmdsets, it might look like
@ -199,32 +206,34 @@ classes. For instance, in your `typeclasses/characters.py` module, you should c
like this:
```python
from evennia.contrib.ingame_python.typeclasses import EventCharacter
from evennia.contrib.base_systems.ingame_python.typeclasses import EventCharacter
class Character(EventCharacter):
# ...
```
You should do the same thing for your rooms, exits and objects. Note that the in-game Python system works by
overriding some hooks. Some of these features might not be accessible in your game if you don't
call the parent methods when overriding hooks.
You should do the same thing for your rooms, exits and objects. Note that the
in-game Python system works by overriding some hooks. Some of these features
might not be accessible in your game if you don't call the parent methods when
overriding hooks.
## Using the `@call` command
## Using the `call` command
The in-game Python system relies, to a great extent, on its `@call` command. Who can execute this command,
and who can do what with it, will depend on your set of permissions.
The in-game Python system relies, to a great extent, on its `call` command.
Who can execute this command, and who can do what with it, will depend on your
set of permissions.
The `@call` command allows to add, edit and delete callbacks on specific objects' events. The event
The `call` command allows to add, edit and delete callbacks on specific objects' events. The event
system can be used on most Evennia objects, mostly typeclassed objects (excluding players). The
first argument of the `@call` command is the name of the object you want to edit. It can also be
first argument of the `call` command is the name of the object you want to edit. It can also be
used to know what events are available for this specific object.
### Examining callbacks and events
To see the events connected to an object, use the `@call` command and give the name or ID of the
object to examine. For instance, @call here` to examine the events on your current location. Or
`@call self` to see the events on yourself.
To see the events connected to an object, use the `call` command and give the name or ID of the
object to examine. For instance, `call here` to examine the events on your current location. Or
`call self` to see the events on yourself.
This command will display a table, containing:
@ -233,7 +242,7 @@ This command will display a table, containing:
second column.
- A short help to tell you when the event is triggered in the third column.
If you execute `@call #1` for instance, you might see a table like this:
If you execute `call #1` for instance, you might see a table like this:
```
+------------------+---------+-----------------------------------------------+
@ -292,7 +301,7 @@ If we want to prevent a character from traversing through this exit, the best ev
When we edit the event, we have some more information:
@call/add north = can_traverse
call/add north = can_traverse
Can the character traverse through this exit?
This event is called when a character is about to traverse this
@ -308,7 +317,7 @@ Variables you can use in this event:
The section dedicated to [eventfuncs](#the-eventfuncs) will elaborate on the `deny()` function and
other eventfuncs. Let us say, for the time being, that it can prevent an action (in this case, it
can prevent the character from traversing through this exit). In the editor that opened when you
used `@call/add`, you can type something like:
used `call/add`, you can type something like:
```python
if character.id == 1:
@ -320,11 +329,11 @@ else:
You can now enter `:wq` to leave the editor by saving the callback.
If you enter `@call north`, you should see that "can_traverse" now has an active callback. You can
use `@call north = can_traverse` to see more details on the connected callbacks:
If you enter `call north`, you should see that "can_traverse" now has an active callback. You can
use `call north = can_traverse` to see more details on the connected callbacks:
```
@call north = can_traverse
call north = can_traverse
+--------------+--------------+----------------+--------------+--------------+
| Number | Author | Updated | Param | Valid |
+~~~~~~~~~~~~~~+~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~+~~~~~~~~~~~~~~+
@ -336,7 +345,7 @@ The left column contains callback numbers. You can use them to have even more i
specific event. Here, for instance:
```
@call north = can_traverse 1
call north = can_traverse 1
Callback can_traverse 1 of north:
Created by XXXXX on 2017-04-02 17:58:05.
Updated by XXXXX on 2017-04-02 18:02:50
@ -360,11 +369,11 @@ the name of the object to edit and the equal sign:
1. The name of the event (as seen above).
2. A number, if several callbacks are connected at this location.
You can type `@call/edit <object> = <event name>` to see the callbacks that are linked at this
You can type `call/edit <object> = <event name>` to see the callbacks that are linked at this
location. If there is only one callback, it will be opened in the editor; if more are defined, you
will be asked for a number to provide (for instance, `@call/edit north = can_traverse 2`).
will be asked for a number to provide (for instance, `call/edit north = can_traverse 2`).
The command `@call` also provides a `/del` switch to remove a callback. It takes the same arguments
The command `call` also provides a `/del` switch to remove a callback. It takes the same arguments
as the `/edit` switch.
When removed, callbacks are logged, so an administrator can retrieve its content, assuming the
@ -435,7 +444,7 @@ One example that will illustrate this system is the "msg_leave" event that can b
This event can alter the message that will be sent to other characters when someone leaves through
this exit.
@call/add down = msg_leave
call/add down = msg_leave
Which should display:
@ -467,6 +476,7 @@ If you write something like this in your event:
```python
message = "{character} falls into a hole in the ground!"
```
And if the character Wilfred takes this exit, others in the room will see:
@ -488,18 +498,18 @@ For instance, let's say we want to create a cool voice-operated elevator. You e
elevator and say the floor number... and the elevator moves in the right direction. In this case,
we could create an callback with the parameter "one":
@call/add here = say one
call/add here = say one
This callback will only fire when the user says a sentence that contains "one".
But what if we want to have a callback that would fire if the user says 1 or one? We can provide
several parameters, separated by a comma.
@call/add here = say 1, one
call/add here = say 1, one
Or, still more keywords:
@call/add here = say 1, one, ground
call/add here = say 1, one, ground
This time, the user could say something like "take me to the ground floor" ("ground" is one of our
keywords defined in the above callback).
@ -524,11 +534,12 @@ a mandatory parameter, which is the time you expect this event to fire.
For instance, let's add an event on this room that should trigger every day, at precisely 12:00 PM
(the time is given as game time, not real time):
@call here = time 12:00
call here = time 12:00
```python
# This will be called every MUD day at 12:00 PM
room.msg_contents("It's noon, time to have lunch!")
```
Now, at noon every MUD day, this event will fire and this callback will be executed. You can use
@ -580,7 +591,7 @@ the next at regular times. Connecting exits (opening its doors), waiting a bit,
rolling around and stopping at a different station. That's quite a complex set of callbacks, as it
is, but let's only look at the part that opens and closes the doors:
@call/add here = time 10:00
call/add here = time 10:00
```python
# At 10:00 AM, the subway arrives in the room of ID 22.
@ -617,7 +628,7 @@ This callback will:
And now, what should we have in "chain_1"?
@call/add here = chain_1
call/add here = chain_1
```python
# Close the doors
@ -795,7 +806,7 @@ see a message about a "beautiful ant-hill".
### Adding new eventfuncs
Eventfuncs, like `deny(), are defined in `contrib/events/eventfuncs.py`. You can add your own
Eventfuncs, like `deny()`, are defined in `contrib/events/eventfuncs.py`. You can add your own
eventfuncs by creating a file named `eventfuncs.py` in your `world` directory. The functions
defined in this file will be added as helpers.
@ -814,7 +825,7 @@ EVENTFUNCS_LOCATIONS = [
If you want to create events with parameters (if you create a "whisper" or "ask" command, for
instance, and need to have some characters automatically react to words), you can set an additional
argument in the tuple of events in your typeclass' ```_events``` class variable. This third argument
argument in the tuple of events in your typeclass' `_events` class variable. This third argument
must contain a callback that will be called to filter through the list of callbacks when the event
fires. Two types of parameters are commonly used (but you can define more parameter types, although
this is out of the scope of this documentation).
@ -825,8 +836,9 @@ this is out of the scope of this documentation).
The "say" command uses phrase parameters (you can set a "say" callback to fires if a phrase
contains one specific word).
In both cases, you need to import a function from `evennia.contrib.ingame_python.utils` and use it as third
parameter in your event definition.
In both cases, you need to import a function from
`evennia.contrib.base_systems.ingame_python.utils` and use it as third parameter in your
event definition.
- `keyword_event` should be used for keyword parameters.
- `phrase_event` should be used for phrase parameters.
@ -834,7 +846,7 @@ parameter in your event definition.
For example, here is the definition of the "say" event:
```python
from evennia.contrib.ingame_python.utils import register_events, phrase_event
from evennia.contrib.base_systems.ingame_python.utils import register_events, phrase_event
# ...
@register_events
class SomeTypeclass:
@ -865,5 +877,5 @@ The best way to do this is to use a custom setting, in your setting file
EVENTS_DISABLED = True
```
The in-game Python system will still be accessible (you will have access to the `@call` command, to debug),
The in-game Python system will still be accessible (you will have access to the `call` command, to debug),
but no event will be called automatically.

View file

@ -0,0 +1,12 @@
"""
In-game Python - vlgeoff 2017
"""
from . import callbackhandler
from . import commands
from . import eventfuncs
from . import scripts
from . import tests
from . import typeclasses
from . import utils

View file

@ -5,12 +5,11 @@ Module containing the commands of the in-game Python system.
from datetime import datetime
from django.conf import settings
from evennia import Command
from evennia.utils.ansi import raw
from evennia.utils.eveditor import EvEditor
from evennia.utils.evtable import EvTable
from evennia.utils.utils import class_from_module, time_format
from evennia.contrib.ingame_python.utils import get_event_handler
from evennia.contrib.base_systems.ingame_python.utils import get_event_handler
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)

View file

@ -5,8 +5,8 @@ Eventfuncs are just Python functions that can be used inside of calllbacks.
"""
from evennia import ObjectDB, ScriptDB
from evennia.contrib.ingame_python.utils import InterruptEvent
from evennia import ObjectDB
from evennia.contrib.base_systems.ingame_python.utils import InterruptEvent
def deny():

View file

@ -10,13 +10,13 @@ import traceback
from django.conf import settings
from evennia import DefaultObject, DefaultScript, ChannelDB, ScriptDB
from evennia import logger, ObjectDB
from evennia import logger
from evennia.utils.ansi import raw
from evennia.utils.create import create_channel
from evennia.utils.dbserialize import dbserialize
from evennia.utils.utils import all_from_module, delay, pypath_to_realpath
from evennia.contrib.ingame_python.callbackhandler import CallbackHandler
from evennia.contrib.ingame_python.utils import get_next_wait, EVENTS, InterruptEvent
from evennia.contrib.base_systems.ingame_python.callbackhandler import CallbackHandler
from evennia.contrib.base_systems.ingame_python.utils import get_next_wait, EVENTS, InterruptEvent
# Constants
RE_LINE_ERROR = re.compile(r'^ File "\<string\>", line (\d+)')

View file

@ -12,8 +12,8 @@ from evennia.objects.objects import ExitCommand
from evennia.utils import ansi, utils
from evennia.utils.create import create_object, create_script
from evennia.utils.test_resources import EvenniaTest
from evennia.contrib.ingame_python.commands import CmdCallback
from evennia.contrib.ingame_python.callbackhandler import CallbackHandler
from evennia.contrib.base_systems.ingame_python.commands import CmdCallback
from evennia.contrib.base_systems.ingame_python.callbackhandler import CallbackHandler
# Force settings
settings.EVENTS_CALENDAR = "standard"

View file

@ -10,8 +10,8 @@ default ones in evennia core.
from evennia import DefaultCharacter, DefaultExit, DefaultObject, DefaultRoom
from evennia import ScriptDB
from evennia.utils.utils import delay, inherits_from, lazy_property
from evennia.contrib.ingame_python.callbackhandler import CallbackHandler
from evennia.contrib.ingame_python.utils import register_events, time_event, phrase_event
from evennia.contrib.base_systems.ingame_python.callbackhandler import CallbackHandler
from evennia.contrib.base_systems.ingame_python.utils import register_events, time_event, phrase_event
# Character help
CHARACTER_CAN_DELETE = """

View file

@ -5,17 +5,15 @@ These functions are to be used by developers to customize events and callbacks.
"""
from textwrap import dedent
from django.conf import settings
from evennia import logger
from evennia import ScriptDB
from evennia.utils.create import create_script
from evennia.utils.gametime import real_seconds_until as standard_rsu
from evennia.utils.utils import class_from_module
from evennia.contrib.custom_gametime import UNITS
from evennia.contrib.custom_gametime import gametime_to_realtime
from evennia.contrib.custom_gametime import real_seconds_until as custom_rsu
from evennia.contrib.base_systems.custom_gametime import UNITS
from evennia.contrib.base_systems.custom_gametime import gametime_to_realtime
from evennia.contrib.base_systems.custom_gametime import real_seconds_until as custom_rsu
# Temporary storage for events waiting for the script to be started
EVENTS = []

View file

@ -0,0 +1,22 @@
# Menu-based login system
Contribution - Vincent-lg 2016, Griatch 2019 (rework for modern EvMenu)
This changes the Evennia login to ask for the account name and password in
sequence instead of requiring you to enter both at once. It uses EvMenu under
the hood.
## Installation
To install, add this to `mygame/server/conf/settings.py`:
CMDSET_UNLOGGEDIN = "evennia.contrib.base_systems.menu_login.UnloggedinCmdSet"
CONNECTION_SCREEN_MODULE = "contrib.base_systems.menu_login.connection_screens"
Reload the server and reconnect to see the changes.
## Notes
If you want to modify the way the connection screen looks, point
`CONNECTION_SCREEN_MODULE` to your own module. Use the default as a
guide (see also Evennia docs).

View file

@ -0,0 +1,7 @@
"""
Menu-login - Vinvent-lg 2016, Griatch 2019
"""
from .menu_login import UnloggedinCmdSet # noqa
from .menu_login import connection_screens # noqa

View file

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
"""
Connection screen
This is the text to show the user when they first connect to the game (before
they log in).
To change the login screen in this module, do one of the following:
- Define a function `connection_screen()`, taking no arguments. This will be
called first and must return the full string to act as the connection screen.
This can be used to produce more dynamic screens.
- Alternatively, define a string variable in the outermost scope of this module
with the connection string that should be displayed. If more than one such
variable is given, Evennia will pick one of them at random.
The commands available to the user when the connection screen is shown
are defined in evennia.default_cmds.UnloggedinCmdSet. The parsing and display
of the screen is done by the unlogged-in "look" command.
"""
from django.conf import settings
from evennia import utils
CONNECTION_SCREEN = """
|b==============================================================|n
Welcome to |g{}|n, version {}!
Enter |wh|nelp for more info. |wlook|n will re-show this screen.
|b==============================================================|n""".format(
settings.SERVERNAME, utils.get_evennia_version("short")
)

View file

@ -4,17 +4,17 @@ A login menu using EvMenu.
Contribution - Vincent-lg 2016, Griatch 2019 (rework for modern EvMenu)
This changes the Evennia login to ask for the account name and password in
sequence instead of requiring you to enter both at once.
sequence instead of requiring you to enter both at once.
To install, add this line to the settings file (`mygame/server/conf/settings.py`):
CMDSET_UNLOGGEDIN = "evennia.contrib.menu_login.UnloggedinCmdSet"
CMDSET_UNLOGGEDIN = "evennia.base_systems.contrib.menu_login.UnloggedinCmdSet"
Reload the server and the new connection method will be active. Note that you must
independently change the connection screen to match this login style, by editing
independently change the connection screen to match this login style, by editing
`mygame/server/conf/connection_screens.py`.
This uses Evennia's menu system EvMenu and is triggered by a command that is
This uses Evennia's menu system EvMenu and is triggered by a command that is
called automatically when a new user connects.
"""
@ -32,8 +32,7 @@ _ACCOUNT = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
_GUEST = class_from_module(settings.BASE_GUEST_TYPECLASS)
_ACCOUNT_HELP = (
"Enter the name you used to log into the game before, " "or a new account-name if you are new."
)
"Enter a new or existing login name.")
_PASSWORD_HELP = (
"Password should be a minimum of 8 characters (preferably longer) and "
"can contain a mix of letters, spaces, digits and @/./+/-/_/'/, only."

View file

@ -0,0 +1,42 @@
# Legacy Comms-commands
Contribution - Griatch 2021
In Evennia 1.0, the old Channel commands (originally inspired by MUX) were
replaced by the single `channel` command that performs all these function.
That command is still required to talk on channels. This contrib (extracted
from Evennia 0.9.5) reuses the channel-management of the base Channel command
but breaks out its functionality into separate Commands with MUX-familiar names.
- `allcom` - `channel/all` and `channel`
- `addcom` - `channel/alias`, `channel/sub` and `channel/unmute`
- `delcom` - `channel/unalias`, `alias/unsub` and `channel/mute`
- `cboot` - `channel/boot` (`channel/ban` and `/unban` not supported)
- `cwho` - `channel/who`
- `ccreate` - `channel/create`
- `cdestroy` - `channel/destroy`
- `clock` - `channel/lock`
- `cdesc` - `channel/desc`
## Installation
- Import the `CmdSetLegacyComms` cmdset from this module into `mygame/commands/default_cmdsets.py`
- Add it to the CharacterCmdSet's `at_cmdset_creation` method (see below).
- Reload the server.
```python
# in mygame/commands/default_cmdsets.py
# ..
from evennia.contrib.base_systems.mux_comms_cmds import CmdSetLegacyComms # <----
class CharacterCmdSet(default_cmds.CharacterCmdSet):
# ...
def at_cmdset_creation(self):
# ...
self.add(CmdSetLegacyComms) # <----
```
Note that you will still be able to use the `channel` command; this is actually
still used under the hood by these commands.

View file

@ -0,0 +1,6 @@
"""
Mux-style comms commands - Griatch 2021
"""
from .mux_comms_cmds import CmdSetLegacyComms # noqa

View file

@ -30,8 +30,8 @@ Example:
```python
# in mygame/commands/default_cmdsets.py
# ...
from evennia.contrib.legacy_comms import CmdSetLegacyComms # <----
# ..
from evennia.contrib.base_systems.mux_comms_cmds import CmdSetLegacyComms # <----
class CharacterCmdSet(default_cmds.CharacterCmdSet):
# ...

View file

@ -0,0 +1,65 @@
# Unix-like Command style parent
Evennia contribution, Vincent Le Geoff 2017
This module contains a command class that allows for unix-style command syntax
in-game, using --options, positional arguments and stuff like -n 10 etc
similarly to a unix command. It might not the best syntax for the average player
but can be really useful for builders when they need to have a single command do
many things with many options. It uses the ArgumentParser from Python's standard
library under the hood.
## Installation
To use, inherit `UnixCommand` from this module from your own commands. You need
to override two methods:
- The `init_parser` method, which adds options to the parser. Note that you
should normally *not* override the normal `parse` method when inheriting from
`UnixCommand`.
- The `func` method, called to execute the command once parsed (like any Command).
Here's a short example:
```python
from evennia.contrib.base_systems.unixcommand import UnixCommand
class CmdPlant(UnixCommand):
'''
Plant a tree or plant.
This command is used to plant something in the room you are in.
Examples:
plant orange -a 8
plant strawberry --hidden
plant potato --hidden --age 5
'''
key = "plant"
def init_parser(self):
"Add the arguments to the parser."
# 'self.parser' inherits `argparse.ArgumentParser`
self.parser.add_argument("key",
help="the key of the plant to be planted here")
self.parser.add_argument("-a", "--age", type=int,
default=1, help="the age of the plant to be planted")
self.parser.add_argument("--hidden", action="store_true",
help="should the newly-planted plant be hidden to players?")
def func(self):
"func is called only if the parser succeeded."
# 'self.opts' contains the parsed options
key = self.opts.key
age = self.opts.age
hidden = self.opts.hidden
self.msg("Going to plant '{}', age={}, hidden={}.".format(
key, age, hidden))
```
To see the full power of argparse and the types of supported options, visit
[the documentation of argparse](https://docs.python.org/2/library/argparse.html).

View file

@ -0,0 +1,6 @@
"""
Unix-like Command style - vlgeoff 2017
"""
from .unixcommand import UnixCommand # noqa

View file

@ -19,6 +19,9 @@ to override two methods:
Here's a short example:
```python
from evennia.contrib.base_systems.unixcommand import UnixCommand
class CmdPlant(UnixCommand):
'''

View file

@ -6,19 +6,19 @@ This 'Evennia escaperoom game engine' was created for the MUD Coders Guild game
Jam, April 14-May 15 2019. The theme for the jam was "One Room". This contains the
utilities and base classes and an empty example room.
The original code for the contest is found at https://github.com/Griatch/evscaperoom
but the version on the public Evennia demo is more updated, so if you really
want the latest bug fixes etc you should rather look at
https://github.com/evennia/evdemo/tree/master/evdemo/evscaperoom instead.
A copy of the full game can also be played on the Evennia demo server at
https://demo.evennia.com - just connect to the server and write `evscaperoom`
The original code for the contest is found at
https://github.com/Griatch/evscaperoom but the version on the public Evennia
demo is more updated, so if you really want the latest bug fixes etc you should
rather look at https://github.com/evennia/evdemo/tree/master/evdemo/evscaperoom
instead. A copy of the full game can also be played on the Evennia demo server
at https://demo.evennia.com - just connect to the server and write `evscaperoom`
in the first room to start!
# Introduction
Evscaperoom is, as it sounds, an escaperoom in text form. You start locked into
a room and have to figure out how to get out. This engine contains everything needed
to make a fully-featured puzzle game of this type!
a room and have to figure out how to get out. This engine contains everything
needed to make a fully-featured puzzle game of this type!
# Installation
@ -38,8 +38,9 @@ class CharacterCmdSet(...):
self.add(CmdEvscapeRoomStart())
```
Reload the server and the `evscaperoom` command will be available. The contrib comes
with a small (very small) escape room as an example.
Reload the server and the `evscaperoom` command will be available. The contrib
comes with a small (very small) escape room as an example.
# Making your own evscaperoom
@ -57,21 +58,20 @@ the following to your `mygame/server/conf/settings.py` file:
```
Reload and the example evscaperoom should still work, but you can now modify and expand
it from your game dir!
Reload and the example evscaperoom should still work, but you can now modify and
expand it from your game dir!
## Other useful settings
There are a few other settings that may be useful:
- `EVSCAPEROOM_START_STATE` - default is `state_001_start` and is the name of the
state-module to start from (without `.py`). You can change this if you want some
other naming scheme.
- `EVSCAPEROOM_START_STATE` - default is `state_001_start` and is the name of
the state-module to start from (without `.py`). You can change this if you
want some other naming scheme.
- `HELP_SUMMARY_TEXT` - this is the help blurb shown when entering `help` in
the room without an argument. The original is found at the top of
`evennia/contrib/evscaperoom/commands.py`.
# Playing the game
You should start by `look`ing around and at objects.

View file

@ -0,0 +1,13 @@
"""
Evscaperoom - Griatch 2019
"""
from .evscaperoom import commands # noqa
from .evscaperoom import menu # noqa
from .evscaperoom import objects # noqa
from .evscaperoom import room # noqa
from .evscaperoom import scripts # noqa
from .evscaperoom import state # noqa
from .evscaperoom import tests # noqa
from .evscaperoom import utils # noqa

View file

@ -0,0 +1,128 @@
# Barter system
Evennia contribution - Griatch 2012
This implements a full barter system - a way for players to safely
trade items between each other using code rather than simple free-form
talking. The advantage of this is increased buy/sell safety but it
also streamlines the process and makes it faster when doing many
transactions (since goods are automatically exchanged once both
agree).
This system is primarily intended for a barter economy, but can easily
be used in a monetary economy as well -- just let the "goods" on one
side be coin objects (this is more flexible than a simple "buy"
command since you can mix coins and goods in your trade).
## Installation
Just import the CmdsetTrade command into (for example) the default
cmdset. This will make the trade (or barter) command available
in-game.
```python
# in mygame/commands/default_cmdsets.py
from evennia.contrib.game_systems import barter # <---
# ...
class CharacterCmdSet(default_cmds.CharacterCmdSet):
# ...
def at cmdset_creation(self):
# ...
self.add(barter.CmdsetTrade) # <---
```
## Usage
In this module, a "barter" is generally referred to as a "trade".
Below is an example of a barter sequence. A and B are the parties.
The `A>` and `B>` are their inputs.
1) opening a trade
A> trade B: Hi, I have a nice extra sword. You wanna trade?
B sees:
A says: "Hi, I have a nice extra sword. You wanna trade?"
A wants to trade with you. Enter 'trade A <emote>' to accept.
B> trade A: Hm, I could use a good sword ...
A sees:
B says: "Hm, I could use a good sword ...
B accepts the trade. Use 'trade help' for aid.
B sees:
You are now trading with A. Use 'trade help' for aid.
2) negotiating
A> offer sword: This is a nice sword. I would need some rations in trade.
B sees: A says: "This is a nice sword. I would need some rations in trade."
[A offers Sword of might.]
B> evaluate sword
B sees:
<Sword's description and possibly stats>
B> offer ration: This is a prime ration.
A sees:
B says: "This is a prime ration."
[B offers iron ration]
A> say Hey, this is a nice sword, I need something more for it.
B sees:
A says: "Hey this is a nice sword, I need something more for it."
B> offer sword,apple: Alright. I will also include a magic apple. That's my last offer.
A sees:
B says: "Alright, I will also include a magic apple. That's my last offer."
[B offers iron ration and magic apple]
A> accept: You are killing me here, but alright.
B sees: A says: "You are killing me here, but alright."
[A accepts your offer. You must now also accept.]
B> accept: Good, nice making business with you.
You accept the deal. Deal is made and goods changed hands.
A sees: B says: "Good, nice making business with you."
B accepts the deal. Deal is made and goods changed hands.
At this point the trading system is exited and the negotiated items
are automatically exchanged between the parties. In this example B was
the only one changing their offer, but also A could have changed their
offer until the two parties found something they could agree on. The
emotes are optional but useful for RP-heavy worlds.
## Technical info
The trade is implemented by use of a TradeHandler. This object is a
common place for storing the current status of negotiations. It is
created on the object initiating the trade, and also stored on the
other party once that party agrees to trade. The trade request times
out after a certain time - this is handled by a Script. Once trade
starts, the CmdsetTrade cmdset is initiated on both parties along with
the commands relevant for the trading.
## Ideas for NPC bartering
This module is primarily intended for trade between two players. But
it can also in principle be used for a player negotiating with an
AI-controlled NPC. If the NPC uses normal commands they can use it
directly -- but more efficient is to have the NPC object send its
replies directly through the tradehandler to the player. One may want
to add some functionality to the decline command, so players can
decline specific objects in the NPC offer (decline <object>) and allow
the AI to maybe offer something else and make it into a proper
barter. Along with an AI that "needs" things or has some sort of
personality in the trading, this can make bartering with NPCs at least
moderately more interesting than just plain 'buy'.

View file

@ -0,0 +1,83 @@
# Clothing
Evennia contribution - Tim Ashley Jenkins 2017
Provides a typeclass and commands for wearable clothing,
which is appended to a character's description when worn.
Clothing items, when worn, are added to the character's description
in a list. For example, if wearing the following clothing items:
a thin and delicate necklace
a pair of regular ol' shoes
one nice hat
a very pretty dress
## Installation
To install, import this module and have your default character
inherit from ClothedCharacter in your game's characters.py file:
```python
from evennia.contrib.game_systems.clothing import ClothedCharacter
class Character(ClothedCharacter):
```
And then add `ClothedCharacterCmdSet` in your character set in
`mygame/commands/default_cmdsets.py`:
```python
from evennia.contrib.game_systems.clothing import ClothedCharacterCmdSet <--
class CharacterCmdSet(default_cmds.CharacterCmdSet):
...
at_cmdset_creation(self):
super().at_cmdset_creation()
...
self.add(ClothedCharacterCmdSet) # <--
```
From here, you can use the default builder commands to create clothes
with which to test the system:
create a pretty shirt : evennia.contrib.clothing.Clothing
set shirt/clothing_type = 'top'
wear shirt
A character's description may look like this:
Superuser(#1)
This is User #1.
Superuser is wearing one nice hat, a thin and delicate necklace,
a very pretty dress and a pair of regular ol' shoes.
Characters can also specify the style of wear for their clothing - I.E.
to wear a scarf 'tied into a tight knot around the neck' or 'draped
loosely across the shoulders' - to add an easy avenue of customization.
For example, after entering:
wear scarf draped loosely across the shoulders
The garment appears like so in the description:
Superuser(#1)
This is User #1.
Superuser is wearing a fanciful-looking scarf draped loosely
across the shoulders.
Items of clothing can be used to cover other items, and many options
are provided to define your own clothing types and their limits and
behaviors. For example, to have undergarments automatically covered
by outerwear, or to put a limit on the number of each type of item
that can be worn. The system as-is is fairly freeform - you
can cover any garment with almost any other, for example - but it
can easily be made more restrictive, and can even be tied into a
system for armor or other equipment.

View file

@ -0,0 +1,6 @@
"""
Barter contrib - Griatch 2012
"""
from .barter import CmdsetTrade # noqa

View file

@ -94,7 +94,9 @@ in-game.
"""
from evennia import Command, DefaultScript, CmdSet
from evennia.commands.command import Command
from evennia.commands.cmdset import CmdSet
from evennia.scripts.scripts import DefaultScript
TRADE_TIMEOUT = 60 # timeout for B to accept trade

View file

@ -0,0 +1,84 @@
# Clothing
Evennia contribution - Tim Ashley Jenkins 2017
Provides a typeclass and commands for wearable clothing,
which is appended to a character's description when worn.
Clothing items, when worn, are added to the character's description
in a list. For example, if wearing the following clothing items:
a thin and delicate necklace
a pair of regular ol' shoes
one nice hat
a very pretty dress
## Installation
To install, import this module and have your default character
inherit from ClothedCharacter in your game's characters.py file:
```python
from evennia.contrib.game_systems.clothing import ClothedCharacter
class Character(ClothedCharacter):
```
And then add `ClothedCharacterCmdSet` in your character set in
`mygame/commands/default_cmdsets.py`:
```python
from evennia.contrib.game_systems.clothing import ClothedCharacterCmdSet <--
class CharacterCmdSet(default_cmds.CharacterCmdSet):
# ...
at_cmdset_creation(self):
super().at_cmdset_creation()
...
self.add(ClothedCharacterCmdSet) # <--
```
From here, you can use the default builder commands to create clothes
with which to test the system:
create a pretty shirt : evennia.contrib.clothing.Clothing
set shirt/clothing_type = 'top'
wear shirt
A character's description may look like this:
Superuser(#1)
This is User #1.
Superuser is wearing one nice hat, a thin and delicate necklace,
a very pretty dress and a pair of regular ol' shoes.
Characters can also specify the style of wear for their clothing - I.E.
to wear a scarf 'tied into a tight knot around the neck' or 'draped
loosely across the shoulders' - to add an easy avenue of customization.
For example, after entering:
wear scarf draped loosely across the shoulders
The garment appears like so in the description:
Superuser(#1)
This is User #1.
Superuser is wearing a fanciful-looking scarf draped loosely
across the shoulders.
Items of clothing can be used to cover other items, and many options
are provided to define your own clothing types and their limits and
behaviors. For example, to have undergarments automatically covered
by outerwear, or to put a limit on the number of each type of item
that can be worn. The system as-is is fairly freeform - you
can cover any garment with almost any other, for example - but it
can easily be made more restrictive, and can even be tied into a
system for armor or other equipment.

View file

@ -0,0 +1,7 @@
"""
Clothing contrib - Tim Ashley Jenkins 2017
"""
from .clothing import ClothedCharacter # noqa
from .clothing import ClothedCharacterCmdSet # noqa

View file

@ -0,0 +1,58 @@
# Cooldown contrib module.
Evennia contrib - owllex, 2021
This contrib provides a simple cooldown handler that can be attached to any
typeclassed Object or Account. A cooldown is a lightweight persistent
asynchronous timer that you can query to see if it is ready.
Cooldowns are good for modelling rate-limited actions, like how often a
character can perform a given command.
Cooldowns are completely asynchronous and must be queried to know their
state. They do not fire callbacks, so are not a good fit for use cases
where something needs to happen on a specific schedule (use delay or
a TickerHandler for that instead).
See also the evennia documentation for command cooldowns
(https://github.com/evennia/evennia/wiki/Command-Cooldown) for more information
about the concept.
## Installation
To use, simply add the following property to the typeclass definition of any
object type that you want to support cooldowns. It will expose a new `cooldowns`
property that persists data to the object's attribute storage. You can set this
on your base `Object` typeclass to enable cooldown tracking on every kind of
object, or just put it on your `Character` typeclass.
By default the CooldownHandler will use the `cooldowns` property, but you can
customize this if desired by passing a different value for the `db_attribute`
parameter.
```python
from evennia.game_systems.contrib.cooldowns import Cooldownhandler
from evennia.utils.utils import lazy_property
@lazy_property
def cooldowns(self):
return CooldownHandler(self, db_attribute="cooldowns")
```
# Example
Assuming you've installed cooldowns on your Character typeclasses, you can use a
cooldown to limit how often you can perform a command. The following code
snippet will limit the use of a Power Attack command to once every 10 seconds
per character.
```python
class PowerAttack(Command):
def func(self):
if self.caller.cooldowns.ready("power attack"):
self.do_power_attack()
self.caller.cooldowns.add("power attack", 10)
else:
self.caller.msg("That's not ready yet!")
```

View file

@ -0,0 +1,6 @@
"""
Cooldown contrib - owllex 2021
"""
from .cooldowns import CooldownHandler # noqa

View file

@ -31,7 +31,7 @@ By default the CooldownHandler will use the `cooldowns` property, but you can
customize this if desired by passing a different value for the db_attribute
parameter.
from evennia.contrib.cooldowns import Cooldownhandler
from evennia.game_systems.contrib.cooldowns import Cooldownhandler
from evennia.utils.utils import lazy_property
@lazy_property
@ -52,6 +52,7 @@ class PowerAttack(Command):
self.caller.cooldowns.add("power attack", 10)
else:
self.caller.msg("That's not ready yet!")
"""
import math

View file

@ -44,7 +44,7 @@ in there:
```python
from evennia.contrib.crafting.crafting import CraftingRecipe, CraftingValidationError
from evennia.contrib.game_systems.crafting import CraftingRecipe, CraftingValidationError
class RecipeBread(CraftingRecipe):

View file

@ -0,0 +1,7 @@
"""
Crafting - Griatch 2020
"""
from .crafting import CraftingRecipe # noqa
from .crafting import CraftingValidationError # noqa

View file

@ -73,9 +73,10 @@ from random import random, randint
from evennia.commands.command import Command, InterruptCommand
from .crafting import craft, CraftingRecipe, CraftingValidationError
#------------------------------------------------------------
# ------------------------------------------------------------
# Sword recipe
#------------------------------------------------------------
# ------------------------------------------------------------
class PigIronRecipe(CraftingRecipe):
"""

View file

@ -4,11 +4,10 @@ Unit tests for the crafting system contrib.
"""
from unittest import mock
from anything import Something
from django.test import override_settings
from django.core.exceptions import ObjectDoesNotExist
from evennia.commands.default.tests import CommandTest
from evennia.utils.test_resources import TestCase, EvenniaTest
from evennia.utils.test_resources import TestCase
from evennia.utils.create import create_object
from . import crafting, example_recipes

View file

@ -0,0 +1,49 @@
# Gendersub
Contrib - Griatch 2015
This is a simple gender-aware Character class for allowing users to
insert custom markers in their text to indicate gender-aware
messaging. It relies on a modified msg() and is meant as an
inspiration and starting point to how to do stuff like this.
An object can have the following genders:
- male (he/his)
- female (her/hers)
- neutral (it/its)
- ambiguous (they/them/their/theirs)
## Installation
Import and add the `SetGender` command to your default cmdset in
`mygame/commands/default_cmdset.py`
Make your `Character` inherit from `GenderCharacter`.
## Usage
When in use, messages can contain special tags to indicate pronouns gendered
based on the one being addressed. Capitalization will be retained.
- `|s`, `|S`: Subjective form: he, she, it, He, She, It, They
- `|o`, `|O`: Objective form: him, her, it, Him, Her, It, Them
- `|p`, `|P`: Possessive form: his, her, its, His, Her, Its, Their
- `|a`, `|A`: Absolute Possessive form: his, hers, its, His, Hers, Its, Theirs
For example,
```
char.msg("%s falls on |p face with a thud." % char.key)
"Tom falls on his face with a thud"
```
The default gender is "ambiguous" (they/them/their/theirs).
To use, have DefaultCharacter inherit from this, or change
setting.DEFAULT_CHARACTER to point to this class.
The `gender` command is used to set the gender. It needs to be added to the
default cmdset before it becomes available.

View file

@ -0,0 +1,7 @@
"""
Gendersub - Griatch 2015
"""
from .gendersub import GenderCharacter # noqa
from .gendersub import SetGender # noqa

View file

@ -14,6 +14,8 @@ An object can have the following genders:
- neutral (it/its)
- ambiguous (they/them/their/theirs)
Usage
When in use, messages can contain special tags to indicate pronouns gendered
based on the one being addressed. Capitalization will be retained.
@ -34,7 +36,7 @@ The default gender is "ambiguous" (they/them/their/theirs).
To use, have DefaultCharacter inherit from this, or change
setting.DEFAULT_CHARACTER to point to this class.
The `@gender` command is used to set the gender. It needs to be added to the
The `gender` command is used to set the gender. It needs to be added to the
default cmdset before it becomes available.
"""
@ -66,8 +68,8 @@ class SetGender(Command):
"""
key = "@gender"
aliases = "@sex"
key = "gender"
aliases = "sex"
locks = "call:all()"
def func(self):

View file

@ -0,0 +1,40 @@
# In-Game Mail system
Evennia Contribution - grungies1138 2016
A simple Brandymail style mail system that uses the Msg class from Evennia
Core. It has two Commands, both of which can be used on their own:
- CmdMail - this should sit on the Account cmdset and makes the `mail` command
available both IC and OOC. Mails will always go to Accounts (other players).
- CmdMailCharacter - this should sit on the Character cmdset and makes the `mail`
command ONLY available when puppeting a character. Mails will be sent to other
Characters only and will not be available when OOC.
- If adding *both* commands to their respective cmdsets, you'll get two separate
IC and OOC mailing systems, with different lists of mail for IC and OOC modes.
## Installation:
Install one or both of the following (see above):
- CmdMail (IC + OOC mail, sent between players)
# mygame/commands/default_cmds.py
from evennia.contrib.game_systems import mail
# in AccountCmdSet.at_cmdset_creation:
self.add(mail.CmdMail())
- CmdMailCharacter (optional, IC only mail, sent between characters)
# mygame/commands/default_cmds.py
from evennia.contrib.game_systems import mail
# in CharacterCmdSet.at_cmdset_creation:
self.add(mail.CmdMailCharacter())
Once installed, use `help mail` in game for help with the mail command. Use
ic/ooc to switch in and out of IC/OOC modes.

View file

@ -0,0 +1,7 @@
"""
Mail system - grungies 2016
"""
from .mail import CmdMail # noqa
from .mail import CmdMailCharacter # noqa

View file

@ -5,9 +5,10 @@ Evennia Contribution - grungies1138 2016
A simple Brandymail style @mail system that uses the Msg class from Evennia
Core. It has two Commands, both of which can be used on their own:
- CmdMail - this should sit on the Account cmdset and makes the @mail command
- CmdMail - this should sit on the Account cmdset and makes the `mail` command
available both IC and OOC. Mails will always go to Accounts (other players).
- CmdMailCharacter - this should sit on the Character cmdset and makes the @mail
- CmdMailCharacter - this should sit on the Character cmdset and makes the `mail`
command ONLY available when puppeting a character. Mails will be sent to other
Characters only and will not be available when OOC.
- If adding *both* commands to their respective cmdsets, you'll get two separate
@ -21,7 +22,7 @@ Install one or both of the following (see above):
# mygame/commands/default_cmds.py
from evennia.contrib import mail
from evennia.contrib.game_systems import mail
# in AccountCmdSet.at_cmdset_creation:
self.add(mail.CmdMail())
@ -30,13 +31,13 @@ Install one or both of the following (see above):
# mygame/commands/default_cmds.py
from evennia.contrib import mail
from evennia.contrib.game_systems import mail
# in CharacterCmdSet.at_cmdset_creation:
self.add(mail.CmdMailCharacter())
Once installed, use `help mail` in game for help with the mail command. Use
@ic/@ooc to switch in and out of IC/OOC modes.
ic/ooc to switch in and out of IC/OOC modes.
"""

View file

@ -0,0 +1,24 @@
# Evennia Multidescer
Contrib - Griatch 2016
A "multidescer" is a concept from the MUSH world. It allows for
creating, managing and switching between multiple character
descriptions. This multidescer will not require any changes to the
Character class, rather it will use the `multidescs` Attribute (a
list) and create it if it does not exist.
This contrib also works well together with the rpsystem contrib (which
also adds the short descriptions and the `sdesc` command).
## Installation
Edit `mygame/commands/default_cmdsets.py` and add
`from evennia.contrib.multidescer import CmdMultiDesc` to the top.
Next, look up the `at_cmdset_create` method of the `CharacterCmdSet`
class and add a line `self.add(CmdMultiDesc())` to the end
of it.
Reload the server and you should have the +desc command available (it
will replace the default `desc` command).

View file

@ -0,0 +1,6 @@
"""
Multidescer - Griatch 2016
"""
from .multidescer import CmdMultiDesc # noqa

View file

@ -1,5 +1,5 @@
"""
Evennia Mutltidescer
Evennia Multidescer
Contrib - Griatch 2016
@ -12,11 +12,10 @@ list) and create it if it does not exist.
This contrib also works well together with the rpsystem contrib (which
also adds the short descriptions and the `sdesc` command).
Installation:
Edit `mygame/commands/default_cmdsets.py` and add
`from evennia.contrib.multidescer import CmdMultiDesc` to the top.
`from evennia.contrib.game_system.multidescer import CmdMultiDesc` to the top.
Next, look up the `at_cmdset_create` method of the `CharacterCmdSet`
class and add a line `self.add(CmdMultiDesc())` to the end

View file

@ -0,0 +1,68 @@
# Puzzles System
Evennia contribution - Henddher 2018
Provides a typeclass and commands for objects that can be combined (i.e. 'use'd)
to produce new objects.
A Puzzle is a recipe of what objects (aka parts) must be combined by a player so
a new set of objects (aka results) are automatically created.
## Installation
Add the PuzzleSystemCmdSet to all players (e.g. in their Character typeclass).
Alternatively:
py self.cmdset.add('evennia.contrib.game_systems.puzzles.PuzzleSystemCmdSet')
## Usage
Consider this simple Puzzle:
orange, mango, yogurt, blender = fruit smoothie
As a Builder:
create/drop orange
create/drop mango
create/drop yogurt
create/drop blender
create/drop fruit smoothie
puzzle smoothie, orange, mango, yogurt, blender = fruit smoothie
...
Puzzle smoothie(#1234) created successfuly.
destroy/force orange, mango, yogurt, blender, fruit smoothie
armpuzzle #1234
Part orange is spawned at ...
Part mango is spawned at ...
....
Puzzle smoothie(#1234) has been armed successfully
As Player:
use orange, mango, yogurt, blender
...
Genius, you blended all fruits to create a fruit smoothie!
## Details
Puzzles are created from existing objects. The given
objects are introspected to create prototypes for the
puzzle parts and results. These prototypes become the
puzzle recipe. (See PuzzleRecipe and `puzzle`
command). Once the recipe is created, all parts and result
can be disposed (i.e. destroyed).
At a later time, a Builder or a Script can arm the puzzle
and spawn all puzzle parts in their respective
locations (See armpuzzle).
A regular player can collect the puzzle parts and combine
them (See use command). If player has specified
all pieces, the puzzle is considered solved and all
its puzzle parts are destroyed while the puzzle results
are spawened on their corresponding location.

View file

@ -0,0 +1,7 @@
"""
Puzzles - Henddher 2018
"""
from .puzzles import PuzzleSystemCmdSet # noqa
from .puzzles import PuzzleRecipe # noqa

View file

@ -9,25 +9,35 @@ A Puzzle is a recipe of what objects (aka parts) must
be combined by a player so a new set of objects
(aka results) are automatically created.
Installation:
Add the PuzzleSystemCmdSet to all players (e.g. in their Character typeclass).
Alternatively:
py self.cmdset.add('evennia.contrib.game_systems.puzzles.PuzzleSystemCmdSet')
Usage:
Consider this simple Puzzle:
orange, mango, yogurt, blender = fruit smoothie
As a Builder:
@create/drop orange
@create/drop mango
@create/drop yogurt
@create/drop blender
@create/drop fruit smoothie
create/drop orange
create/drop mango
create/drop yogurt
create/drop blender
create/drop fruit smoothie
@puzzle smoothie, orange, mango, yogurt, blender = fruit smoothie
puzzle smoothie, orange, mango, yogurt, blender = fruit smoothie
...
Puzzle smoothie(#1234) created successfuly.
@destroy/force orange, mango, yogurt, blender, fruit smoothie
destroy/force orange, mango, yogurt, blender, fruit smoothie
@armpuzzle #1234
armpuzzle #1234
Part orange is spawned at ...
Part mango is spawned at ...
....
@ -50,7 +60,7 @@ can be disposed (i.e. destroyed).
At a later time, a Builder or a Script can arm the puzzle
and spawn all puzzle parts in their respective
locations (See @armpuzzle).
locations (See armpuzzle).
A regular player can collect the puzzle parts and combine
them (See use command). If player has specified
@ -58,12 +68,6 @@ all pieces, the puzzle is considered solved and all
its puzzle parts are destroyed while the puzzle results
are spawened on their corresponding location.
Installation:
Add the PuzzleSystemCmdSet to all players.
Alternatively:
@py self.cmdset.add('evennia.contrib.puzzles.PuzzleSystemCmdSet')
"""

View file

@ -16,25 +16,25 @@ implemented and customized:
tb_basic.py - The simplest system, which implements initiative and turn
order, attack rolls against defense values, and damage to hit
points. Only very basic game mechanics are included.
tb_equip.py - Adds weapons and armor to the basic implementation of
the battle system, including commands for wielding weapons and
donning armor, and modifiers to accuracy and damage based on
currently used equipment.
tb_items.py - Adds usable items and conditions/status effects, and gives
a lot of examples for each. Items can perform nearly any sort of
function, including healing, adding or curing conditions, or
being used to attack. Conditions affect a fighter's attributes
and options in combat and persist outside of fights, counting
down per turn in combat and in real time outside combat.
tb_magic.py - Adds a spellcasting system, allowing characters to cast
spells with a variety of effects by spending MP. Spells are
linked to functions, and as such can perform any sort of action
the developer can imagine - spells for attacking, healing and
conjuring objects are included as examples.
tb_items.py - Adds usable items and conditions/status effects, and gives
a lot of examples for each. Items can perform nearly any sort of
function, including healing, adding or curing conditions, or
being used to attack. Conditions affect a fighter's attributes
and options in combat and persist outside of fights, counting
down per turn in combat and in real time outside combat.
tb_magic.py - Adds a spellcasting system, allowing characters to cast
spells with a variety of effects by spending MP. Spells are
linked to functions, and as such can perform any sort of action
the developer can imagine - spells for attacking, healing and
conjuring objects are included as examples.
tb_range.py - Adds a system for abstract positioning and movement, which
tracks the distance between different characters and objects in
combat, as well as differentiates between melee and ranged
@ -49,7 +49,7 @@ meant to closely emulate the experience of playing a tabletop RPG.
Each of these modules contains the full functionality of the battle system
with different customizations added in - the instructions to install each
one is contained in the module itself. It's recommended that you install
and test tb_basic first, so you can better understand how the other
and test `tb_basic` first, so you can better understand how the other
modules expand on it and get a better idea of how you can customize the
system to your liking and integrate the subsystems presented here into
your own combat system.

View file

@ -1 +1,10 @@
"""
Turnbattle system - Tim Ashley Jenkins 2017
"""
from . import tb_basic # noqa
from . import tb_equip # noqa
from . import tb_items # noqa
from . import tb_magic # noqa
from . import tb_range # noqa

View file

@ -0,0 +1,5 @@
"""
Extened Room - Griatch 2012, vincent-lg 2019
"""

View file

@ -0,0 +1,76 @@
# Extended Room
Evennia Contribution - Griatch 2012, vincent-lg 2019
This is an extended Room typeclass for Evennia. It is supported
by an extended `Look` command and an extended `desc` command, also
in this module.
## Installation/testing:
Adding the `ExtendedRoomCmdset` to the default character cmdset will add all
new commands for use.
In more detail, in `mygame/commands/default_cmdsets.py`:
```python
...
from evennia.contrib import extended_room # <---
class CharacterCmdset(default_cmds.Character_CmdSet):
...
def at_cmdset_creation(self):
...
self.add(extended_room.ExtendedRoomCmdSet) # <---
```
Then reload to make the bew commands available. Note that they only work
on rooms with the typeclass `ExtendedRoom`. Create new rooms with the right
typeclass or use the `typeclass` command to swap existing rooms.
## Features
### Time-changing description slots
This allows to change the full description text the room shows
depending on larger time variations. Four seasons (spring, summer,
autumn and winter) are used by default. The season is calculated
on-demand (no Script or timer needed) and updates the full text block.
There is also a general description which is used as fallback if
one or more of the seasonal descriptions are not set when their
time comes.
An updated `desc` command allows for setting seasonal descriptions.
The room uses the `evennia.utils.gametime.GameTime` global script. This is
started by default, but if you have deactivated it, you need to
supply your own time keeping mechanism.
### In-description changing tags
Within each seasonal (or general) description text, you can also embed
time-of-day dependent sections. Text inside such a tag will only show
during that particular time of day. The tags looks like `<timeslot> ...
</timeslot>`. By default there are four timeslots per day - morning,
afternoon, evening and night.
### Details
The Extended Room can be "detailed" with special keywords. This makes
use of a special `Look` command. Details are "virtual" targets to look
at, without there having to be a database object created for it. The
Details are simply stored in a dictionary on the room and if the look
command cannot find an object match for a `look <target>` command it
will also look through the available details at the current location
if applicable. The `detail` command is used to change details.
### Extra commands
- `CmdExtendedRoomLook` - look command supporting room details
- `CmdExtendedRoomDesc` - desc command allowing to add seasonal descs,
- `CmdExtendedRoomDetail` - command allowing to manipulate details in this room
as well as listing them
- `CmdExtendedRoomGameTime` - A simple `time` command, displaying the current
time and season.

View file

@ -0,0 +1,11 @@
"""
Extended Room - Griatch 2012, vincent-lg 2019
"""
from .extended_room import ExtendedRoom # noqa
from .extended_room import ExtendedRoomCmdSet # noqa
from .extended_room import CmdExtendedRoomLook # noqa
from .extended_room import CmdExtendedRoomDesc # noqa
from .extended_room import CmdExtendedRoomDetail # noqa
from .extended_room import CmdExtendedRoomGameTime # noqa

View file

@ -45,7 +45,7 @@ at, without there having to be a database object created for it. The
Details are simply stored in a dictionary on the room and if the look
command cannot find an object match for a `look <target>` command it
will also look through the available details at the current location
if applicable. The `@detail` command is used to change details.
if applicable. The `detail` command is used to change details.
4) Extra commands
@ -67,13 +67,13 @@ In more detail, in mygame/commands/default_cmdsets.py:
```
...
from evennia.contrib import extended_room # <-new
from evennia.contrib import extended_room # <---
class CharacterCmdset(default_cmds.Character_CmdSet):
...
def at_cmdset_creation(self):
...
self.add(extended_room.ExtendedRoomCmdSet) # <-new
self.add(extended_room.ExtendedRoomCmdSet) # <---
```

View file

@ -0,0 +1,278 @@
# Map Builder
Contribution - Cloud_Keeper 2016
Build a map from a 2D ASCII map.
This is a command which takes two inputs:
≈≈≈≈≈
≈♣n♣≈ MAP_LEGEND = {("♣", "♠"): build_forest,
≈∩▲∩≈ ("∩", "n"): build_mountains,
≈♠n♠≈ ("▲"): build_temple}
≈≈≈≈≈
A string of ASCII characters representing a map and a dictionary of functions
containing build instructions. The characters of the map are iterated over and
compared to a list of trigger characters. When a match is found the
corresponding function is executed generating the rooms, exits and objects as
defined by the users build instructions. If a character is not a match to
a provided trigger character (including spaces) it is simply skipped and the
process continues.
For instance, the above map represents a temple (▲) amongst mountains (n,∩)
in a forest (♣,♠) on an island surrounded by water (≈). Each character on the
first line is iterated over but as there is no match with our `MAP_LEGEND`, it
is skipped. On the second line it finds "♣" which is a match and so the
`build_forest` function is called. Next the `build_mountains` function is
called and so on until the map is completed. Building instructions are passed
the following arguments:
x - The rooms position on the maps x axis
y - The rooms position on the maps y axis
caller - The account calling the command
iteration - The current iterations number (0, 1 or 2)
room_dict - A dictionary containing room references returned by build
functions where tuple coordinates are the keys (x, y).
ie room_dict[(2, 2)] will return the temple room above.
Building functions should return the room they create. By default these rooms
are used to create exits between valid adjacent rooms to the north, south,
east and west directions. This behaviour can turned off with the use of switch
arguments. In addition to turning off automatic exit generation the switches
allow the map to be iterated over a number of times. This is important for
something like custom exit building. Exits require a reference to both the
exits location and the exits destination. During the first iteration it is
possible that an exit is created pointing towards a destination that
has not yet been created resulting in error. By iterating over the map twice
the rooms can be created on the first iteration and room reliant code can be
be used on the second iteration. The iteration number and a dictionary of
references to rooms previously created is passed to the build commands.
You then call the command in-game using the path to the MAP and MAP_LEGEND vars
The path you provide is relative to the evennia or mygame folder.
# Installation
Use by importing and including the command in your default_cmdsets module.
For example:
```python
# mygame/commands/default_cmdsets.py
from evennia.contrib.grid import mapbuilder
...
self.add(mapbuilder.CmdMapBuilder())
```
# Usage:
mapbuilder[/switch] <path.to.file.MAPNAME> <path.to.file.MAP_LEGEND>
one - execute build instructions once without automatic exit creation.
two - execute build instructions twice without automatic exit creation.
# Examples
mapbuilder world.gamemap.MAP world.maplegend.MAP_LEGEND
mapbuilder evennia.contrib.grid.mapbuilder.EXAMPLE1_MAP EXAMPLE1_LEGEND
mapbuilder/two evennia.contrib.mapbuilder.EXAMPLE2_MAP EXAMPLE2_LEGEND
(Legend path defaults to map path)
Below are two examples showcasing the use of automatic exit generation and
custom exit generation. Whilst located, and can be used, from this module for
convenience The below example code should be in mymap.py in mygame/world.
## Example One
```python
from django.conf import settings
from evennia.utils import utils
# mapbuilder evennia.contrib.mapbuilder.EXAMPLE1_MAP EXAMPLE1_LEGEND
# -*- coding: utf-8 -*-
# Add the necessary imports for your instructions here.
from evennia import create_object
from typeclasses import rooms, exits
from random import randint
import random
# A map with a temple (▲) amongst mountains (n,∩) in a forest (♣,♠) on an
# island surrounded by water (≈). By giving no instructions for the water
# characters we effectively skip it and create no rooms for those squares.
EXAMPLE1_MAP = '''
≈≈≈≈≈
≈♣n♣≈
≈∩▲∩≈
≈♠n♠≈
≈≈≈≈≈
'''
def example1_build_forest(x, y, **kwargs):
'''A basic example of build instructions. Make sure to include **kwargs
in the arguments and return an instance of the room for exit generation.'''
# Create a room and provide a basic description.
room = create_object(rooms.Room, key="forest" + str(x) + str(y))
room.db.desc = "Basic forest room."
# Send a message to the account
kwargs["caller"].msg(room.key + " " + room.dbref)
# This is generally mandatory.
return room
def example1_build_mountains(x, y, **kwargs):
'''A room that is a little more advanced'''
# Create the room.
room = create_object(rooms.Room, key="mountains" + str(x) + str(y))
# Generate a description by randomly selecting an entry from a list.
room_desc = [
"Mountains as far as the eye can see",
"Your path is surrounded by sheer cliffs",
"Haven't you seen that rock before?",
]
room.db.desc = random.choice(room_desc)
# Create a random number of objects to populate the room.
for i in range(randint(0, 3)):
rock = create_object(key="Rock", location=room)
rock.db.desc = "An ordinary rock."
# Send a message to the account
kwargs["caller"].msg(room.key + " " + room.dbref)
# This is generally mandatory.
return room
def example1_build_temple(x, y, **kwargs):
'''A unique room that does not need to be as general'''
# Create the room.
room = create_object(rooms.Room, key="temple" + str(x) + str(y))
# Set the description.
room.db.desc = (
"In what, from the outside, appeared to be a grand and "
"ancient temple you've somehow found yourself in the the "
"Evennia Inn! It consists of one large room filled with "
"tables. The bardisk extends along the east wall, where "
"multiple barrels and bottles line the shelves. The "
"barkeep seems busy handing out ale and chatting with "
"the patrons, which are a rowdy and cheerful lot, "
"keeping the sound level only just below thunderous. "
"This is a rare spot of mirth on this dread moor."
)
# Send a message to the account
kwargs["caller"].msg(room.key + " " + room.dbref)
# This is generally mandatory.
return room
# Include your trigger characters and build functions in a legend dict.
EXAMPLE1_LEGEND = {
("♣", "♠"): example1_build_forest,
("∩", "n"): example1_build_mountains,
("▲"): example1_build_temple,
}
```
## Example Two
```python
# @mapbuilder/two evennia.contrib.mapbuilder.EXAMPLE2_MAP EXAMPLE2_LEGEND
# -*- coding: utf-8 -*-
# Add the necessary imports for your instructions here.
# from evennia import create_object
# from typeclasses import rooms, exits
# from evennia.utils import utils
# from random import randint
# import random
# This is the same layout as Example 1 but included are characters for exits.
# We can use these characters to determine which rooms should be connected.
EXAMPLE2_MAP = '''
≈ ≈ ≈ ≈ ≈
≈ ♣-♣-♣ ≈
| |
≈ ♣ ♣ ♣ ≈
| | |
≈ ♣-♣-♣ ≈
≈ ≈ ≈ ≈ ≈
'''
def example2_build_forest(x, y, **kwargs):
'''A basic room'''
# If on anything other than the first iteration - Do nothing.
if kwargs["iteration"] > 0:
return None
room = create_object(rooms.Room, key="forest" + str(x) + str(y))
room.db.desc = "Basic forest room."
kwargs["caller"].msg(room.key + " " + room.dbref)
return room
def example2_build_verticle_exit(x, y, **kwargs):
'''Creates two exits to and from the two rooms north and south.'''
# If on the first iteration - Do nothing.
if kwargs["iteration"] == 0:
return
north_room = kwargs["room_dict"][(x, y - 1)]
south_room = kwargs["room_dict"][(x, y + 1)]
# create exits in the rooms
create_object(
exits.Exit, key="south", aliases=["s"], location=north_room, destination=south_room
)
create_object(
exits.Exit, key="north", aliases=["n"], location=south_room, destination=north_room
)
kwargs["caller"].msg("Connected: " + north_room.key + " & " + south_room.key)
def example2_build_horizontal_exit(x, y, **kwargs):
'''Creates two exits to and from the two rooms east and west.'''
# If on the first iteration - Do nothing.
if kwargs["iteration"] == 0:
return
west_room = kwargs["room_dict"][(x - 1, y)]
east_room = kwargs["room_dict"][(x + 1, y)]
create_object(exits.Exit, key="east", aliases=["e"], location=west_room, destination=east_room)
create_object(exits.Exit, key="west", aliases=["w"], location=east_room, destination=west_room)
kwargs["caller"].msg("Connected: " + west_room.key + " & " + east_room.key)
# Include your trigger characters and build functions in a legend dict.
EXAMPLE2_LEGEND = {
("♣", "♠"): example2_build_forest,
("|"): example2_build_verticle_exit,
("-"): example2_build_horizontal_exit,
}
```

View file

@ -0,0 +1,6 @@
"""
Mapbuilder - Cloud Keeper 2016
"""
from .mapbuilder import CmdMapBuilder # noqa

View file

@ -51,43 +51,46 @@ the rooms can be created on the first iteration and room reliant code can be
be used on the second iteration. The iteration number and a dictionary of
references to rooms previously created is passed to the build commands.
You then call the command in-game using the path to the MAP and MAP_LEGEND vars
The path you provide is relative to the evennia or mygame folder.
## Installation:
Use by importing and including the command in your default_cmdsets module.
For example:
```python
# mygame/commands/default_cmdsets.py
from evennia.contrib import mapbuilder
from evennia.contrib.grid import mapbuilder
...
self.add(mapbuilder.CmdMapBuilder())
```
You then call the command in-game using the path to the MAP and MAP_LEGEND vars
The path you provide is relative to the evennia or mygame folder.
Usage:
@mapbuilder[/switch] <path.to.file.MAPNAME> <path.to.file.MAP_LEGEND>
## Usage
Switches:
one - execute build instructions once without automatic exit creation.
two - execute build instructions twice without automatic exit creation.
mapbuilder[/switch] <path.to.file.MAPNAME> <path.to.file.MAP_LEGEND>
Example:
@mapbuilder world.gamemap.MAP world.maplegend.MAP_LEGEND
@mapbuilder evennia.contrib.mapbuilder.EXAMPLE1_MAP EXAMPLE1_LEGEND
@mapbuilder/two evennia.contrib.mapbuilder.EXAMPLE2_MAP EXAMPLE2_LEGEND
one - execute build instructions once without automatic exit creation.
two - execute build instructions twice without automatic exit creation.
## Example
mapbuilder world.gamemap.MAP world.maplegend.MAP_LEGEND
mapbuilder evennia.contrib.grid.mapbuilder.EXAMPLE1_MAP EXAMPLE1_LEGEND
mapbuilder/two evennia.contrib.mapbuilder.EXAMPLE2_MAP EXAMPLE2_LEGEND
(Legend path defaults to map path)
Below are two examples showcasing the use of automatic exit generation and
custom exit generation. Whilst located, and can be used, from this module for
convenience The below example code should be in mymap.py in mygame/world.
"""
# Example One:
from django.conf import settings
from evennia.utils import utils
# ---------- EXAMPLE 1 ---------- #
```python
# @mapbuilder evennia.contrib.mapbuilder.EXAMPLE1_MAP EXAMPLE1_LEGEND
# -*- coding: utf-8 -*-
@ -102,18 +105,17 @@ import random
# A map with a temple (▲) amongst mountains (n,∩) in a forest (♣,♠) on an
# island surrounded by water (≈). By giving no instructions for the water
# characters we effectively skip it and create no rooms for those squares.
EXAMPLE1_MAP = """\
EXAMPLE1_MAP = '''
n
n
"""
'''
def example1_build_forest(x, y, **kwargs):
"""A basic example of build instructions. Make sure to include **kwargs
in the arguments and return an instance of the room for exit generation."""
'''A basic example of build instructions. Make sure to include **kwargs
in the arguments and return an instance of the room for exit generation.'''
# Create a room and provide a basic description.
room = create_object(rooms.Room, key="forest" + str(x) + str(y))
@ -127,7 +129,7 @@ def example1_build_forest(x, y, **kwargs):
def example1_build_mountains(x, y, **kwargs):
"""A room that is a little more advanced"""
'''A room that is a little more advanced'''
# Create the room.
room = create_object(rooms.Room, key="mountains" + str(x) + str(y))
@ -153,7 +155,7 @@ def example1_build_mountains(x, y, **kwargs):
def example1_build_temple(x, y, **kwargs):
"""A unique room that does not need to be as general"""
'''A unique room that does not need to be as general'''
# Create the room.
room = create_object(rooms.Room, key="temple" + str(x) + str(y))
@ -184,8 +186,11 @@ EXAMPLE1_LEGEND = {
("", "n"): example1_build_mountains,
(""): example1_build_temple,
}
```
# ---------- EXAMPLE 2 ---------- #
# Example two
```python
# @mapbuilder/two evennia.contrib.mapbuilder.EXAMPLE2_MAP EXAMPLE2_LEGEND
# -*- coding: utf-8 -*-
@ -199,7 +204,7 @@ EXAMPLE1_LEGEND = {
# This is the same layout as Example 1 but included are characters for exits.
# We can use these characters to determine which rooms should be connected.
EXAMPLE2_MAP = """\
EXAMPLE2_MAP = '''
--
@ -209,11 +214,10 @@ EXAMPLE2_MAP = """\
--
"""
'''
def example2_build_forest(x, y, **kwargs):
"""A basic room"""
'''A basic room'''
# If on anything other than the first iteration - Do nothing.
if kwargs["iteration"] > 0:
return None
@ -227,7 +231,7 @@ def example2_build_forest(x, y, **kwargs):
def example2_build_verticle_exit(x, y, **kwargs):
"""Creates two exits to and from the two rooms north and south."""
'''Creates two exits to and from the two rooms north and south.'''
# If on the first iteration - Do nothing.
if kwargs["iteration"] == 0:
return
@ -248,7 +252,7 @@ def example2_build_verticle_exit(x, y, **kwargs):
def example2_build_horizontal_exit(x, y, **kwargs):
"""Creates two exits to and from the two rooms east and west."""
'''Creates two exits to and from the two rooms east and west.'''
# If on the first iteration - Do nothing.
if kwargs["iteration"] == 0:
return
@ -270,7 +274,14 @@ EXAMPLE2_LEGEND = {
("-"): example2_build_horizontal_exit,
}
# ---------- END OF EXAMPLES ---------- #
```
---
"""
from django.conf import settings
from evennia.utils import utils
from evennia import create_object
from typeclasses import exits
COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)

View file

@ -0,0 +1,44 @@
# SimpleDoor
Contribution - Griatch 2016
A simple two-way exit that represents a door that can be opened and
closed. Can easily be expanded from to make it lockable, destroyable
etc. Note that the simpledoor is based on Evennia locks, so it will
not work for a superuser (which bypasses all locks) - the superuser
will always appear to be able to close/open the door over and over
without the locks stopping you. To use the door, use `@quell` or a
non-superuser account.
## Installation:
Import `SimpleDoorCmdSet` from this module into `mygame/commands/default_cmdsets`
and add it to your `CharacterCmdSet`:
```python
# in mygame/commands/default_cmdsets.py
from evennia.contrib.grid import simpledoor <---
class CharacterCmdSet(default_cmds.CharacterCmdSet):
# ...
def at_cmdset_creation(self):
# ...
self.add(simpledoor.SimpleDoorCmdSet)
```
## Usage:
To try it out, `dig` a new room and then use the (overloaded) `@open`
commmand to open a new doorway to it like this:
@open doorway:contrib.grid.simpledoor.SimpleDoor = otherroom
open doorway
close doorway
Note: This uses locks, so if you are a superuser you will not be blocked by
a locked door - `quell` yourself, if so. Normal users will find that they
cannot pass through either side of the door once it's closed from the other
side.

View file

@ -0,0 +1,7 @@
"""
Simple Door - Griatch 2016
"""
from .simpledoor import SimpleDoorCmdSet # noqa
from .simpledoor import SimpleDoor # noqa

View file

@ -13,14 +13,29 @@ non-superuser account.
Installation:
Import this module in mygame/commands/default_cmdsets and add
the CmdOpen and CmdOpenCloseDoor commands to the CharacterCmdSet;
then reload the server.
To try it out, `@dig` a new room and then use the (overloaded) `@open`
Import this module in mygame/commands/default_cmdsets and add
the SimpleDoorCmdSet to your CharacterCmdSet:
```python
# in mygame/commands/default_cmdsets.py
from evennia.contrib.grid import simpledoor <---
class CharacterCmdSet(default_cmds.CharacterCmdSet):
# ...
def at_cmdset_creation(self):
# ...
self.add(simpledoor.SimpleDoorCmdSet)
```
Usage:
To try it out, `dig` a new room and then use the (overloaded) `@open`
commmand to open a new doorway to it like this:
@open doorway:contrib.simpledoor.SimpleDoor = otherroom
@open doorway:contrib.grid.simpledoor.SimpleDoor = otherroom
You can then use `open doorway' and `close doorway` to change the open
state. If you are not superuser (`@quell` yourself) you'll find you
@ -30,6 +45,7 @@ other side.
"""
from evennia import DefaultExit, default_cmds
from evennia.commands.cmdset import CmdSet
from evennia.utils.utils import inherits_from
@ -170,3 +186,9 @@ class CmdOpenCloseDoor(default_cmds.MuxCommand):
else:
door.setlock("traverse:false()")
self.caller.msg("You close %s." % door.key)
class SimpleDoorCmdSet(CmdSet):
def at_cmdset_creation(self):
self.add(CmdOpen())
self.add(CmdOpenCloseDoor())

View file

@ -0,0 +1,61 @@
# Slow Exit
Contribution - Griatch 2014
This is an example of an Exit-type that delays its traversal. This simulates
slow movement, common in many different types of games. The contrib also
contains two commands, `CmdSetSpeed` and CmdStop for changing the movement speed
and abort an ongoing traversal, respectively.
## Installation:
To try out an exit of this type, you could connect two existing rooms
using something like this:
@open north:contrib.grid.slow_exit.SlowExit = <destination>
To make this your new default exit, modify `mygame/typeclasses/exits.py`
to import this module and change the default `Exit` class to inherit
from `SlowExit` instead.
```
# in mygame/typeclasses/exits.py
from evennia.contrib.grid.slowexit import SlowExit
class Exit(SlowExit):
# ...
```
To get the ability to change your speed and abort your movement, import
```python
# in mygame/commands/default_cmdsets.py
from evennia.contrib.grid import slow_exit <---
class CharacterCmdSet(default_cmds.CharacterCmdSet):
# ...
def at_cmdset_creation(self):
# ...
self.add(slow_exit.SlowDoorCmdSet) <---
```
simply import and add CmdSetSpeed and CmdStop from this module to your
default cmdset (see tutorials on how to do this if you are unsure).
To try out an exit of this type, you could connect two existing rooms using
something like this:
@open north:contrib.grid.slow_exit.SlowExit = <destination>
## Notes:
This implementation is efficient but not persistent; so incomplete
movement will be lost in a server reload. This is acceptable for most
game types - to simulate longer travel times (more than the couple of
seconds assumed here), a more persistent variant using Scripts or the
TickerHandler might be better.

View file

@ -0,0 +1,9 @@
"""
Slow Exit - Griatch 2014
"""
from .slow_exit import SlowExitCmdSet # noqa
from .slow_exit import SlowExit # noqa
from .slow_exit import CmdSetSpeed # noqa
from .slow_exit import CmdStop # noqa

View file

@ -4,28 +4,48 @@ Slow Exit typeclass
Contribution - Griatch 2014
This is an example of an Exit-type that delays its traversal.This
This is an example of an Exit-type that delays its traversal. This
simulates slow movement, common in many different types of games. The
contrib also contains two commands, CmdSetSpeed and CmdStop for changing
contrib also contains two commands, `CmdSetSpeed` and CmdStop for changing
the movement speed and abort an ongoing traversal, respectively.
## Installation:
To try out an exit of this type, you could connect two existing rooms
using something like this:
@open north:contrib.slow_exit.SlowExit = <destination>
@open north:contrib.grid.slow_exit.SlowExit = <destination>
To make this your new default exit, modify `mygame/typeclasses/exits.py`
to import this module and change the default `Exit` class to inherit
from `SlowExit` instead.
Installation:
```
# in mygame/typeclasses/exits.py
To make this your new default exit, modify mygame/typeclasses/exits.py
to import this module and change the default Exit class to inherit
from SlowExit instead.
from evennia.contrib.grid.slowexit import SlowExit
To get the ability to change your speed and abort your movement,
simply import and add CmdSetSpeed and CmdStop from this module to your
default cmdset (see tutorials on how to do this if you are unsure).
class Exit(SlowExit):
# ...
Notes:
```
To get the ability to change your speed and abort your movement, import
```python
# in mygame/commands/default_cmdsets.py
from evennia.contrib.grid import slow_exit <---
class CharacterCmdSet(default_cmds.CharacterCmdSet):
# ...
def at_cmdset_creation(self):
# ...
self.add(slow_exit.SlowDoorCmdSet) <---
```
## Notes:
This implementation is efficient but not persistent; so incomplete
movement will be lost in a server reload. This is acceptable for most
@ -35,7 +55,11 @@ TickerHandler might be better.
"""
from evennia import DefaultExit, utils, Command
from evennia.objects.objects import DefaultExit
from evennia.utils import utils
from evennia.commands.command import Command
from evennia.commands.cmdset import CmdSet
MOVE_DELAY = {"stroll": 6, "walk": 4, "run": 2, "sprint": 1}
@ -142,3 +166,9 @@ class CmdStop(Command):
observer.msg("%s stops." % self.caller.get_display_name(observer))
else:
self.caller.msg("You are not moving.")
class SlowExitCmdSet(CmdSet):
def at_cmdset_creation(self):
self.add(CmdSetSpeed())
self.add(CmdStop())

View file

@ -0,0 +1,113 @@
# Wilderness system
Evennia contrib - titeuf87 2017
This contrib provides a wilderness map without actually creating a large number
of rooms - as you move, your room is instead updated with different
descriptions. This means you can make huge areas with little database use as
long as the rooms are relatively similar (name/desc changing).
## Installation
This contrib does not provide any new commands. Instead the default `py` command
is used to call functions/classes in this contrib directly.
## Usage
A wilderness map needs to created first. There can be different maps, all
with their own name. If no name is provided, then a default one is used. Internally,
the wilderness is stored as a Script with the name you specify. If you don't
specify the name, a script named "default" will be created and used.
@py from evennia.contrib.grid import wilderness; wilderness.create_wilderness()
Once created, it is possible to move into that wilderness map:
@py from evennia.contrib.grid import wilderness; wilderness.enter_wilderness(me)
All coordinates used by the wilderness map are in the format of `(x, y)`
tuples. x goes from left to right and y goes from bottom to top. So `(0, 0)`
is the bottom left corner of the map.
## Customisation
The defaults, while useable, are meant to be customised. When creating a
new wilderness map it is possible to give a "map provider": this is a
python object that is smart enough to create the map.
The default provider, `WildernessMapProvider`, just creates a grid area that
is unlimited in size.
This `WildernessMapProvider` can be subclassed to create more interesting
maps and also to customize the room/exit typeclass used.
There is also no command that allows players to enter the wilderness. This
still needs to be added: it can be a command or an exit, depending on your
needs.
## Example
To give an example of how to customize, we will create a very simple (and
small) wilderness map that is shaped like a pyramid. The map will be
provided as a string: a "." symbol is a location we can walk on.
Let's create a file `world/pyramid.py`:
```python
# mygame/world/pyramid.py
map_str = '''
.
...
.....
.......
'''
from evennia.contrib.grid import wilderness
class PyramidMapProvider(wilderness.WildernessMapProvider):
def is_valid_coordinates(self, wilderness, coordinates):
"Validates if these coordinates are inside the map"
x, y = coordinates
try:
lines = map_str.split("\n")
# The reverse is needed because otherwise the pyramid will be
# upside down
lines.reverse()
line = lines[y]
column = line[x]
return column == "."
except IndexError:
return False
def get_location_name(self, coordinates):
"Set the location name"
x, y = coordinates
if y == 3:
return "Atop the pyramid."
else:
return "Inside a pyramid."
def at_prepare_room(self, coordinates, caller, room):
"Any other changes done to the room before showing it"
x, y = coordinates
desc = "This is a room in the pyramid."
if y == 3 :
desc = "You can see far and wide from the top of the pyramid."
room.db.desc = desc
```
Now we can use our new pyramid-shaped wilderness map. From inside Evennia we
create a new wilderness (with the name "default") but using our new map provider:
py from world import pyramid as p; p.wilderness.create_wilderness(mapprovider=p.PyramidMapProvider())
py from evennia.contrib import wilderness; wilderness.enter_wilderness(me, coordinates=(4, 1))
## Implementation details
When a character moves into the wilderness, they get their own room. If they
move, instead of moving the character, the room changes to match the new
coordinates. If a character meets another character in the wilderness, then
their room merges. When one of the character leaves again, they each get their
own separate rooms. Rooms are created as needed. Unneeded rooms are stored away
to avoid the overhead cost of creating new rooms again in the future.

View file

@ -0,0 +1,14 @@
"""
Wilderness contrib - titeuf87, 2017
"""
from .wilderness import create_wilderness # noqa
from .wilderness import enter_wilderness # noqa
from .wilderness import get_new_coordinates # noqa
from .wilderness import WildernessScript # noqa
from .wilderness import WildernessExit # noqa
from .wilderness import WildernessRoom # noqa
from .wilderness import WildernessMapProvider # noqa
from .wilderness import Wilderness # noqa
from .wilderness import WildernessMap # noqa

View file

@ -7,43 +7,43 @@ This contrib provides a wilderness map. This is an area that can be huge where
the rooms are mostly similar, except for some small cosmetic changes like the
room name.
Usage:
## Usage
This contrib does not provide any commands. Instead the @py command can be
used.
This contrib does not provide any new commands. Instead the default `py` command
is used.
A wilderness map needs to created first. There can be different maps, all
with their own name. If no name is provided, then a default one is used. Internally,
the wilderness is stored as a Script with the name you specify. If you don't
specify the name, a script named "default" will be created and used.
A wilderness map needs to created first. There can be different maps, all
with their own name. If no name is provided, then a default one is used. Internally,
the wilderness is stored as a Script with the name you specify. If you don't
specify the name, a script named "default" will be created and used.
@py from evennia.contrib import wilderness; wilderness.create_wilderness()
@py from evennia.contrib.grid import wilderness; wilderness.create_wilderness()
Once created, it is possible to move into that wilderness map:
Once created, it is possible to move into that wilderness map:
@py from evennia.contrib import wilderness; wilderness.enter_wilderness(me)
@py from evennia.contrib.grid import wilderness; wilderness.enter_wilderness(me)
All coordinates used by the wilderness map are in the format of `(x, y)`
tuples. x goes from left to right and y goes from bottom to top. So `(0, 0)`
is the bottom left corner of the map.
All coordinates used by the wilderness map are in the format of `(x, y)`
tuples. x goes from left to right and y goes from bottom to top. So `(0, 0)`
is the bottom left corner of the map.
Customisation:
## Customisation
The defaults, while useable, are meant to be customised. When creating a
new wilderness map it is possible to give a "map provider": this is a
python object that is smart enough to create the map.
The defaults, while useable, are meant to be customised. When creating a
new wilderness map it is possible to give a "map provider": this is a
python object that is smart enough to create the map.
The default provider, WildernessMapProvider, just creates a grid area that
is unlimited in size.
This WildernessMapProvider can be subclassed to create more interesting
maps and also to customize the room/exit typeclass used.
The default provider, `WildernessMapProvider`, just creates a grid area that
is unlimited in size.
This `WildernessMapProvider` can be subclassed to create more interesting
maps and also to customize the room/exit typeclass used.
There is also no command that allows players to enter the wilderness. This
still needs to be added: it can be a command or an exit, depending on your
needs.
There is also no command that allows players to enter the wilderness. This
still needs to be added: it can be a command or an exit, depending on your
needs.
Customisation example:
## Example
To give an example of how to customize, we will create a very simple (and
small) wilderness map that is shaped like a pyramid. The map will be
@ -51,59 +51,56 @@ Customisation example:
Let's create a file world/pyramid.py:
```python
map_str = \"\"\"
.
...
.....
.......
\"\"\"
```python
map_str = '''
.
...
.....
.......
'''
from evennia.contrib import wilderness
from evennia.contrib.grid import wilderness
class PyramidMapProvider(wilderness.WildernessMapProvider):
class PyramidMapProvider(wilderness.WildernessMapProvider):
def is_valid_coordinates(self, wilderness, coordinates):
"Validates if these coordinates are inside the map"
x, y = coordinates
try:
lines = map_str.split("\n")
# The reverse is needed because otherwise the pyramid will be
# upside down
lines.reverse()
line = lines[y]
column = line[x]
return column == "."
except IndexError:
return False
def is_valid_coordinates(self, wilderness, coordinates):
"Validates if these coordinates are inside the map"
x, y = coordinates
try:
lines = map_str.split("\n")
# The reverse is needed because otherwise the pyramid will be
# upside down
lines.reverse()
line = lines[y]
column = line[x]
return column == "."
except IndexError:
return False
def get_location_name(self, coordinates):
"Set the location name"
x, y = coordinates
if y == 3:
return "Atop the pyramid."
else:
return "Inside a pyramid."
def get_location_name(self, coordinates):
"Set the location name"
x, y = coordinates
if y == 3:
return "Atop the pyramid."
else:
return "Inside a pyramid."
def at_prepare_room(self, coordinates, caller, room):
"Any other changes done to the room before showing it"
x, y = coordinates
desc = "This is a room in the pyramid."
if y == 3 :
desc = "You can see far and wide from the top of the pyramid."
room.db.desc = desc
```
def at_prepare_room(self, coordinates, caller, room):
"Any other changes done to the room before showing it"
x, y = coordinates
desc = "This is a room in the pyramid."
if y == 3 :
desc = "You can see far and wide from the top of the pyramid."
room.db.desc = desc
```
Now we can use our new pyramid-shaped wilderness map. From inside Evennia we
create a new wilderness (with the name "default") but using our new map provider:
Now we can use our new pyramid-shaped wilderness map. From inside Evennia we
create a new wilderness (with the name "default") but using our new map provider:
```
@py from world import pyramid as p; p.wilderness.create_wilderness(mapprovider=p.PyramidMapProvider())
py from world import pyramid as p; p.wilderness.create_wilderness(mapprovider=p.PyramidMapProvider())
py from evennia.contrib import wilderness; wilderness.enter_wilderness(me, coordinates=(4, 1))
@py from evennia.contrib import wilderness; wilderness.enter_wilderness(me, coordinates=(4, 1))
```
Implementation details:
## Implementation details
When a character moves into the wilderness, they get their own room. If
they move, instead of moving the character, the room changes to match the

View file

@ -0,0 +1,15 @@
"""
XYZGrid - Evennia 2021
"""
from . import commands # noqa
from . import example # noqa
from . import launchcmd # noqa
from . import prototypes # noqa
from . import tests # noqa
from . import utils # noqa
from . import xymap # noqa
from . import xymap_legend # noqa
from . import xyzgrid # noqa
from . import xyzroom # noqa

View file

@ -12,8 +12,8 @@ from django.conf import settings
from evennia import InterruptCommand
from evennia import default_cmds, CmdSet
from evennia.commands.default import building
from evennia.contrib.xyzgrid.xyzroom import XYZRoom
from evennia.contrib.xyzgrid.xyzgrid import get_xyzgrid
from evennia.contrib.grid.xyzgrid.xyzroom import XYZRoom
from evennia.contrib.grid.xyzgrid.xyzgrid import get_xyzgrid
from evennia.utils import ansi
from evennia.utils.utils import list_to_string, class_from_module, delay

View file

@ -17,7 +17,7 @@ Then
"""
from evennia.contrib.xyzgrid import xymap_legend
from evennia.contrib.grid.xyzgrid import xymap_legend
# default prototype parent. It's important that
# the typeclass inherits from the XYZRoom (or XYZExit)

View file

@ -20,7 +20,7 @@ from os.path import join as pathjoin
from django.conf import settings
import evennia
from evennia.utils import ansi
from evennia.contrib.xyzgrid.xyzgrid import get_xyzgrid
from evennia.contrib.grid.xyzgrid.xyzgrid import get_xyzgrid
_HELP_SHORT = """

View file

@ -4,7 +4,6 @@ Tests for the XYZgrid system.
"""
from time import time
from random import randint
from parameterized import parameterized
from django.test import TestCase

View file

@ -18,7 +18,7 @@ The grid has three main functions:
"""
from evennia.scripts.scripts import DefaultScript
from evennia.utils import logger
from evennia.utils.utils import variable_from_module, make_iter
from evennia.utils.utils import variable_from_module
from .xymap import XYMap
from .xyzroom import XYZRoom, XYZExit

View file

@ -10,7 +10,6 @@ used as stand-alone XYZ-coordinate-aware rooms.
from django.db.models import Q
from evennia.objects.objects import DefaultRoom, DefaultExit
from evennia.objects.manager import ObjectManager
from evennia.utils.utils import make_iter
# name of all tag categories. Note that the Z-coordinate is
# the `map_name` of the XYZgrid

View file

@ -1,194 +0,0 @@
"""
Contribution - Griatch 2011
> Note - with the advent of MULTISESSION_MODE=2, this is not really as
necessary anymore - the ooclook and @charcreate commands in that mode
replaces this module with better functionality. This remains here for
inspiration.
This is a simple character creation commandset for the Account level.
It shows some more info and gives the Account the option to create a
character without any more customizations than their name (further
options are unique for each game anyway).
In MULTISESSION_MODEs 0 and 1, you will automatically log into an
existing Character. When using `@ooc` you will then end up in this
cmdset.
Installation:
Import this module to `mygame/commands/default_cmdsets.py` and
add `chargen.OOCCMdSetCharGen` to the `AccountCmdSet` class
(it says where to add it). Reload.
"""
from django.conf import settings
from evennia import Command, create_object, utils
from evennia import default_cmds, managers
CHARACTER_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
class CmdOOCLook(default_cmds.CmdLook):
"""
ooc look
Usage:
look
look <character>
This is an OOC version of the look command. Since an Account doesn't
have an in-game existence, there is no concept of location or
"self".
If any characters are available for you to control, you may look
at them with this command.
"""
key = "look"
aliases = ["l", "ls"]
locks = "cmd:all()"
help_category = "General"
def func(self):
"""
Implements the ooc look command
We use an attribute _character_dbrefs on the account in order
to figure out which characters are "theirs". A drawback of this
is that only the CmdCharacterCreate command adds this attribute,
and thus e.g. account #1 will not be listed (although it will work).
Existence in this list does not depend on puppeting rights though,
that is checked by the @ic command directly.
"""
# making sure caller is really an account
self.character = None
if utils.inherits_from(self.caller, "evennia.objects.objects.Object"):
# An object of some type is calling. Convert to account.
self.character = self.caller
if hasattr(self.caller, "account"):
self.caller = self.caller.account
if not self.character:
# ooc mode, we are accounts
avail_chars = self.caller.db._character_dbrefs
if self.args:
# Maybe the caller wants to look at a character
if not avail_chars:
self.caller.msg("You have no characters to look at. Why not create one?")
return
objs = managers.objects.get_objs_with_key_and_typeclass(
self.args.strip(), CHARACTER_TYPECLASS
)
objs = [obj for obj in objs if obj.id in avail_chars]
if not objs:
self.caller.msg("You cannot see this Character.")
return
self.caller.msg(objs[0].return_appearance(self.caller))
return
# not inspecting a character. Show the OOC info.
charnames = []
if self.caller.db._character_dbrefs:
dbrefs = self.caller.db._character_dbrefs
charobjs = [managers.objects.get_id(dbref) for dbref in dbrefs]
charnames = [charobj.key for charobj in charobjs if charobj]
if charnames:
charlist = "The following Character(s) are available:\n\n"
charlist += "\n\r".join(["|w %s|n" % charname for charname in charnames])
charlist += "\n\n Use |w@ic <character name>|n to switch to that Character."
else:
charlist = "You have no Characters."
string = """ You, %s, are an |wOOC ghost|n without form. The world is hidden
from you and besides chatting on channels your options are limited.
You need to have a Character in order to interact with the world.
%s
Use |wcreate <name>|n to create a new character and |whelp|n for a
list of available commands.""" % (
self.caller.key,
charlist,
)
self.caller.msg(string)
else:
# not ooc mode - leave back to normal look
# we have to put this back for normal look to work.
self.caller = self.character
super().func()
class CmdOOCCharacterCreate(Command):
"""
creates a character
Usage:
create <character name>
This will create a new character, assuming
the given character name does not already exist.
"""
key = "create"
locks = "cmd:all()"
def func(self):
"""
Tries to create the Character object. We also put an
attribute on ourselves to remember it.
"""
# making sure caller is really an account
self.character = None
if utils.inherits_from(self.caller, "evennia.objects.objects.Object"):
# An object of some type is calling. Convert to account.
self.character = self.caller
if hasattr(self.caller, "account"):
self.caller = self.caller.account
if not self.args:
self.caller.msg("Usage: create <character name>")
return
charname = self.args.strip()
old_char = managers.objects.get_objs_with_key_and_typeclass(charname, CHARACTER_TYPECLASS)
if old_char:
self.caller.msg("Character |c%s|n already exists." % charname)
return
# create the character
new_character = create_object(CHARACTER_TYPECLASS, key=charname)
if not new_character:
self.caller.msg(
"|rThe Character couldn't be created. This is a bug. Please contact an admin."
)
return
# make sure to lock the character to only be puppeted by this account
new_character.locks.add(
"puppet:id(%i) or pid(%i) or perm(Developer) or pperm(Developer)"
% (new_character.id, self.caller.id)
)
# save dbref
avail_chars = self.caller.db._character_dbrefs
if avail_chars:
avail_chars.append(new_character.id)
else:
avail_chars = [new_character.id]
self.caller.db._character_dbrefs = avail_chars
self.caller.msg("|gThe character |c%s|g was successfully created!" % charname)
class OOCCmdSetCharGen(default_cmds.AccountCmdSet):
"""
Extends the default OOC cmdset.
"""
def at_cmdset_creation(self):
"""Install everything from the default set, then overload"""
self.add(CmdOOCLook())
self.add(CmdOOCCharacterCreate())

View file

@ -0,0 +1,61 @@
# Dice
Rolls dice for roleplaying, in-game gambling or GM:ing
Evennia contribution - Griatch 2012
# Installation:
Add the `CmdDice` command from this module to your character's cmdset
(and then restart the server):
```python
# in mygame/commands/default_cmdsets.py
# ...
from evennia.contrib.rpg import dice <---
class CharacterCmdSet(default_cmds.CharacterCmdSet):
# ...
def at_object_creation(self):
# ...
self.add(dice.CmdDice()) # <---
```
# Usage:
> roll 1d100 + 2
> roll 1d20
> roll 1d20 - 4
The result of the roll will be echoed to the room
One can also specify a standard Python operator in order to specify
eventual target numbers and get results in a fair and guaranteed
unbiased way. For example:
> roll 2d6 + 2 < 8
Rolling this will inform all parties if roll was indeed below 8 or not.
> roll/hidden
Informs the room that the roll is being made without telling what the result
was.
> roll/secret
Is a hidden roll that does not inform the room it happened.
## Rolling dice from code
To roll dice in code, use the `roll` function from this module:
```python
from evennia.contrib.rpg import dice
dice.roll(3, 10, ("+", 2)) # 3d10 + 2
```

View file

@ -0,0 +1,9 @@
"""
Rolling dice - Griatch, 2012
"""
from .dice import roll # noqa
from .dice import roll_dice # noqa
from .dice import CmdDice # noqa
from .dice import DiceCmdSet # noqa

View file

@ -1,13 +1,16 @@
"""
Dice - rolls dice for roleplaying, in-game gambling or GM:ing
# Dice
Rolls dice for roleplaying, in-game gambling or GM:ing
Evennia contribution - Griatch 2012
This module implements a full-fledged dice-roller and a 'dice' command
This module implements a a dice-roller and a `dice`/`roll` command
to go with it. It uses standard RPG 'd'-syntax (e.g. 2d6 to roll two
six-sided die) and also supports modifiers such as 3d6 + 5.
> roll 1d20 + 2
One can also specify a standard Python operator in order to specify
eventual target numbers and get results in a fair and guaranteed
unbiased way. For example a GM could (using the dice command) from
@ -16,17 +19,38 @@ required to succeed. The command will normally echo this result to all
parties (although it also has options for hidden and secret rolls).
Installation:
# Installation:
To use in your code, just import the roll_dice function from this module.
To use the dice/roll command, just import this module in your custom
cmdset module and add the following line to the end of DefaultCmdSet's
at_cmdset_creation():
Add the `CmdDice` command from this module to your character's cmdset
(and then restart the server):
self.add(dice.CmdDice())
```python
# in mygame/commands/default_cmdsets.py
After a reload the dice (or roll) command will be available in-game.
# ...
from evennia.contrib.rpg import dice <---
class CharacterCmdSet(default_cmds.CharacterCmdSet):
# ...
def at_object_creation(self):
# ...
self.add(dice.CmdDice()) # <---
```
# Usage:
roll 1d100 + 10
To roll dice in code, use the `roll` function from this module:
```python
from evennia.contrib.rpg import dice
dice.roll_dice(3, 10, ("+", 2)) # 3d10 + 2
```
"""
import re
@ -34,7 +58,8 @@ from random import randint
from evennia import default_cmds, CmdSet
def roll_dice(dicenum, dicetype, modifier=None, conditional=None, return_tuple=False):
def roll(dicenum, dicetype, modifier=None,
conditional=None, return_tuple=False):
"""
This is a standard dice roller.
@ -116,6 +141,9 @@ def roll_dice(dicenum, dicetype, modifier=None, conditional=None, return_tuple=F
else:
return result
# legacy alias
roll_dice = roll
RE_PARTS = re.compile(r"(d|\+|-|/|\*|<|>|<=|>=|!=|==)")
RE_MOD = re.compile(r"(\+|-|/|\*)")

View file

@ -0,0 +1,36 @@
# Health Bar
Contrib - Tim Ashley Jenkins 2017
The function provided in this module lets you easily display visual
bars or meters - "health bar" is merely the most obvious use for this,
though these bars are highly customizable and can be used for any sort
of appropriate data besides player health.
Today's players may be more used to seeing statistics like health,
stamina, magic, and etc. displayed as bars rather than bare numerical
values, so using this module to present this data this way may make it
more accessible. Keep in mind, however, that players may also be using
a screen reader to connect to your game, which will not be able to
represent the colors of the bar in any way. By default, the values
represented are rendered as text inside the bar which can be read by
screen readers.
## Usage
No installation, just import and use `display_meter` from this
module:
```python
from evennia.contrib.rpg.health_bar import display_meter
# health is 23/100
health_bar = display_meter(23, 100)
caller.msg(prompt=health_bar)
```
The health bar will account for current values above the maximum or
below 0, rendering them as a completely full or empty bar with the
values displayed within.

View file

@ -0,0 +1,6 @@
"""
Health bar - Tim Ashley Jenkins, 2017
"""
from .health_bar import display_meter # noqa

View file

@ -20,6 +20,17 @@ screen readers.
The health bar will account for current values above the maximum or
below 0, rendering them as a completely full or empty bar with the
values displayed within.
No installation, just import and use `display_meter` from this
module:
```python
from evennia.contrib.rpg.health_bar import display_meter
health_bar = display_meter(23, 100)
caller.msg(prompt=health_bar)
```
"""
@ -38,12 +49,12 @@ def display_meter(
"""
Represents a current and maximum value given as a "bar" rendered with
ANSI or xterm256 background colors.
Args:
cur_value (int): Current value to display
max_value (int): Maximum value to display
Options:
Keyword Arguments:
length (int): Length of meter returned, in characters
fill_color (list): List of color codes for the full portion
of the bar, sans any sort of prefix - both ANSI and xterm256
@ -62,6 +73,10 @@ def display_meter(
the bar. It's highly recommended you keep this on, especially if
there's no info given in pre_text or post_text, as players on screen
readers will be unable to read the graphical aspect of the bar.
Returns:
str: The display bar to show.
"""
# Start by building the base string.
num_text = ""

View file

@ -0,0 +1,257 @@
# Roleplaying base system for Evennia
Roleplaying emotes/sdescs - Griatch, 2015
Language/whisper emotes - Griatch, 2015
## Roleplaying emotes
This module contains the ContribRPObject, ContribRPRoom and
ContribRPCharacter typeclasses. If you inherit your
objects/rooms/character from these (or make them the defaults) from
these you will get the following features:
- Objects/Rooms will get the ability to have poses and will report
the poses of items inside them (the latter most useful for Rooms).
- Characters will get poses and also sdescs (short descriptions)
that will be used instead of their keys. They will gain commands
for managing recognition (custom sdesc-replacement), masking
themselves as well as an advanced free-form emote command.
In more detail, This RP base system introduces the following features
to a game, common to many RP-centric games:
- emote system using director stance emoting (names/sdescs).
This uses a customizable replacement noun (/me, @ etc) to
represent you in the emote. You can use /sdesc, /nick, /key or
/alias to reference objects in the room. You can use any
number of sdesc sub-parts to differentiate a local sdesc, or
use /1-sdesc etc to differentiate them. The emote also
identifies nested says and separates case.
- sdesc obscuration of real character names for use in emotes
and in any referencing such as object.search(). This relies
on an SdescHandler `sdesc` being set on the Character and
makes use of a custom Character.get_display_name hook. If
sdesc is not set, the character's `key` is used instead. This
is particularly used in the emoting system.
- recog system to assign your own nicknames to characters, can then
be used for referencing. The user may recog a user and assign
any personal nick to them. This will be shown in descriptions
and used to reference them. This is making use of the nick
functionality of Evennia.
- masks to hide your identity (using a simple lock).
- pose system to set room-persistent poses, visible in room
descriptions and when looking at the person/object. This is a
simple Attribute that modifies how the characters is viewed when
in a room as sdesc + pose.
- in-emote says, including seamless integration with language
obscuration routine (such as contrib/rplanguage.py)
### Installation:
Add `RPSystemCmdSet` from this module to your CharacterCmdSet:
```python
# mygame/commands/default_cmdsets.py
# ...
from evennia.contrib.rpg.rpsystem import RPSystemCmdSet <---
class CharacterCmdSet(default_cmds.CharacterCmdset):
# ...
def at_cmdset_creation(self):
# ...
self.add(RPSystemCmdSet()) # <---
```
You also need to make your Characters/Objects/Rooms inherit from
the typeclasses in this module:
```python
# in mygame/typeclasses/characters.py
from evennia.contrib.rpg import ContribRPCharacter
class Character(ContribRPCharacter):
# ...
```
```python
# in mygame/typeclasses/objects.py
from evennia.contrib.rpg import ContribRPObject
class Object(ContribRPObject):
# ...
```
```python
# in mygame/typeclasses/rooms.py
from evennia.contrib.rpg import ContribRPRoom
class Room(ContribRPRoom):
# ...
```
You will then need to reload the server and potentially force-reload
your objects, if you originally created them without this.
Example for your character:
> type/reset/force me = typeclasses.characters.Character
Examples:
> look
Tavern
The tavern is full of nice people
*A tall man* is standing by the bar.
Above is an example of a player with an sdesc "a tall man". It is also
an example of a static *pose*: The "standing by the bar" has been set
by the player of the tall man, so that people looking at him can tell
at a glance what is going on.
> emote /me looks at /Tall and says "Hello!"
I see:
Griatch looks at Tall man and says "Hello".
Tall man (assuming his name is Tom) sees:
The godlike figure looks at Tom and says "Hello".
Note that by default, the case of the tag matters, so `/tall` will
lead to 'tall man' while `/Tall` will become 'Tall man' and /TALL
becomes /TALL MAN. If you don't want this behavior, you can pass
case_sensitive=False to the `send_emote` function.
## Language and whisper obfuscation system
This module is intented to be used with an emoting system (such as
`contrib/rpg/rpsystem.py`). It offers the ability to obfuscate spoken words
in the game in various ways:
- Language: The language functionality defines a pseudo-language map
to any number of languages. The string will be obfuscated depending
on a scaling that (most likely) will be input as a weighted average of
the language skill of the speaker and listener.
- Whisper: The whisper functionality will gradually "fade out" a
whisper along as scale 0-1, where the fading is based on gradually
removing sections of the whisper that is (supposedly) easier to
overhear (for example "s" sounds tend to be audible even when no other
meaning can be determined).
### Installation
This module adds no new commands; embed it in your say/emote/whisper commands.
### Usage:
```python
from evennia.contrib import rplanguage
# need to be done once, here we create the "default" lang
rplanguage.add_language()
say = "This is me talking."
whisper = "This is me whispering.
print rplanguage.obfuscate_language(say, level=0.0)
<<< "This is me talking."
print rplanguage.obfuscate_language(say, level=0.5)
<<< "This is me byngyry."
print rplanguage.obfuscate_language(say, level=1.0)
<<< "Daly ly sy byngyry."
result = rplanguage.obfuscate_whisper(whisper, level=0.0)
<<< "This is me whispering"
result = rplanguage.obfuscate_whisper(whisper, level=0.2)
<<< "This is m- whisp-ring"
result = rplanguage.obfuscate_whisper(whisper, level=0.5)
<<< "---s -s -- ---s------"
result = rplanguage.obfuscate_whisper(whisper, level=0.7)
<<< "---- -- -- ----------"
result = rplanguage.obfuscate_whisper(whisper, level=1.0)
<<< "..."
```
To set up new languages, import and use the `add_language()`
helper method in this module. This allows you to customize the
"feel" of the semi-random language you are creating. Especially
the `word_length_variance` helps vary the length of translated
words compared to the original and can help change the "feel" for
the language you are creating. You can also add your own
dictionary and "fix" random words for a list of input words.
Below is an example of "elvish", using "rounder" vowels and sounds:
```python
# vowel/consonant grammar possibilities
grammar = ("v vv vvc vcc vvcc cvvc vccv vvccv vcvccv vcvcvcc vvccvvcc "
"vcvvccvvc cvcvvcvvcc vcvcvvccvcvv")
# all not in this group is considered a consonant
vowels = "eaoiuy"
# you need a representative of all of the minimal grammars here, so if a
# grammar v exists, there must be atleast one phoneme available with only
# one vowel in it
phonemes = ("oi oh ee ae aa eh ah ao aw ay er ey ow ia ih iy "
"oy ua uh uw y p b t d f v t dh s z sh zh ch jh k "
"ng g m n l r w")
# how much the translation varies in length compared to the original. 0 is
# smallest, higher values give ever bigger randomness (including removing
# short words entirely)
word_length_variance = 1
# if a proper noun (word starting with capitalized letter) should be
# translated or not. If not (default) it means e.g. names will remain
# unchanged across languages.
noun_translate = False
# all proper nouns (words starting with a capital letter not at the beginning
# of a sentence) can have either a postfix or -prefix added at all times
noun_postfix = "'la"
# words in dict will always be translated this way. The 'auto_translations'
# is instead a list or filename to file with words to use to help build a
# bigger dictionary by creating random translations of each word in the
# list *once* and saving the result for subsequent use.
manual_translations = {"the":"y'e", "we":"uyi", "she":"semi", "he":"emi",
"you": "do", 'me':'mi','i':'me', 'be':"hy'e", 'and':'y'}
rplanguage.add_language(key="elvish", phonemes=phonemes, grammar=grammar,
word_length_variance=word_length_variance,
noun_translate=noun_translate,
noun_postfix=noun_postfix, vowels=vowels,
manual_translations=manual_translations,
auto_translations="my_word_file.txt")
```
This will produce a decicively more "rounded" and "soft" language than the
default one. The few `manual_translations` also make sure to make it at least
look superficially "reasonable".
The `auto_translations` keyword is useful, this accepts either a
list or a path to a text-file (with one word per line). This listing
of words is used to 'fix' translations for those words according to the
grammatical rules. These translations are stored persistently as long as the
language exists.
This allows to quickly build a large corpus of translated words
that never change. This produces a language that seem moderately
consistent, since words like 'the' will always be translated to the same thing.
The disadvantage (or advantage, depending on your game) is that players can
end up learn what words mean even if their characters don't know the
langauge.

View file

@ -0,0 +1,19 @@
"""
Roleplaying emotes and language - Griatch, 2015
"""
from .rpsystem import EmoteError, SdescError, RecogError, LanguageError # noqa
from .rpsystem import ordered_permutation_regex, regex_tuple_from_key_alias # noqa
from .rpsystem import parse_language, parse_sdescs_and_recogs, send_emote # noqa
from .rpsystem import SdescHandler, RecogHandler # noqa
from .rpsystem import RPCommand, CmdEmote, CmdSay, CmdSdesc, CmdPose, CmdRecog, CmdMask # noqa
from .rpsystem import RPSystemCmdSet # noqa
from .rpsystem import ContribRPObject # noqa
from .rpsystem import ContribRPRoom # noqa
from .rpsystem import ContribRPCharacter # noqa
from .rplanguage import LanguageError, LanguageExistsError # noqa
from .rplanguage import LanguageHandler # noqa
from .rplanguage import obfuscate_language, obfuscate_whisper # noqa
from .rplanguage import add_language, available_languages # noqa

View file

@ -3,7 +3,6 @@ Language and whisper obfuscation system
Evennia contrib - Griatch 2015
This module is intented to be used with an emoting system (such as
contrib/rpsystem.py). It offers the ability to obfuscate spoken words
in the game in various ways:

View file

@ -8,44 +8,93 @@ ContribRPCharacter typeclasses. If you inherit your
objects/rooms/character from these (or make them the defaults) from
these you will get the following features:
- Objects/Rooms will get the ability to have poses and will report
the poses of items inside them (the latter most useful for Rooms).
- Characters will get poses and also sdescs (short descriptions)
that will be used instead of their keys. They will gain commands
for managing recognition (custom sdesc-replacement), masking
themselves as well as an advanced free-form emote command.
To use, simply import the typclasses you want from this module and use
them to create your objects, or set them to default.
- Objects/Rooms will get the ability to have poses and will report
the poses of items inside them (the latter most useful for Rooms).
- Characters will get poses and also sdescs (short descriptions)
that will be used instead of their keys. They will gain commands
for managing recognition (custom sdesc-replacement), masking
themselves as well as an advanced free-form emote command.
In more detail, This RP base system introduces the following features
to a game, common to many RP-centric games:
- emote system using director stance emoting (names/sdescs).
This uses a customizable replacement noun (/me, @ etc) to
represent you in the emote. You can use /sdesc, /nick, /key or
/alias to reference objects in the room. You can use any
number of sdesc sub-parts to differentiate a local sdesc, or
use /1-sdesc etc to differentiate them. The emote also
identifies nested says and separates case.
- sdesc obscuration of real character names for use in emotes
and in any referencing such as object.search(). This relies
on an SdescHandler `sdesc` being set on the Character and
makes use of a custom Character.get_display_name hook. If
sdesc is not set, the character's `key` is used instead. This
is particularly used in the emoting system.
- recog system to assign your own nicknames to characters, can then
be used for referencing. The user may recog a user and assign
any personal nick to them. This will be shown in descriptions
and used to reference them. This is making use of the nick
functionality of Evennia.
- masks to hide your identity (using a simple lock).
- pose system to set room-persistent poses, visible in room
descriptions and when looking at the person/object. This is a
simple Attribute that modifies how the characters is viewed when
in a room as sdesc + pose.
- in-emote says, including seamless integration with language
obscuration routine (such as contrib/rplanguage.py)
- emote system using director stance emoting (names/sdescs).
This uses a customizable replacement noun (/me, @ etc) to
represent you in the emote. You can use /sdesc, /nick, /key or
/alias to reference objects in the room. You can use any
number of sdesc sub-parts to differentiate a local sdesc, or
use /1-sdesc etc to differentiate them. The emote also
identifies nested says and separates case.
- sdesc obscuration of real character names for use in emotes
and in any referencing such as object.search(). This relies
on an SdescHandler `sdesc` being set on the Character and
makes use of a custom Character.get_display_name hook. If
sdesc is not set, the character's `key` is used instead. This
is particularly used in the emoting system.
- recog system to assign your own nicknames to characters, can then
be used for referencing. The user may recog a user and assign
any personal nick to them. This will be shown in descriptions
and used to reference them. This is making use of the nick
functionality of Evennia.
- masks to hide your identity (using a simple lock).
- pose system to set room-persistent poses, visible in room
descriptions and when looking at the person/object. This is a
simple Attribute that modifies how the characters is viewed when
in a room as sdesc + pose.
- in-emote says, including seamless integration with language
obscuration routine (such as contrib/rplanguage.py)
Installation:
Add `RPSystemCmdSet` from this module to your CharacterCmdSet:
```python
# mygame/commands/default_cmdsets.py
# ...
from evennia.contrib.rpg.rpsystem import RPSystemCmdSet <---
class CharacterCmdSet(default_cmds.CharacterCmdset):
# ...
def at_cmdset_creation(self):
# ...
self.add(RPSystemCmdSet()) # <---
```
You also need to make your Characters/Objects/Rooms inherit from
the typeclasses in this module:
```python
# in mygame/typeclasses/characters.py
from evennia.contrib.rpg import ContribRPCharacter
class Character(ContribRPCharacter):
# ...
```
```python
# in mygame/typeclasses/objects.py
from evennia.contrib.rpg import ContribRPObject
class Object(ContribRPObject):
# ...
```
```python
# in mygame/typeclasses/rooms.py
from evennia.contrib.rpg import ContribRPRoom
class Room(ContribRPRoom):
# ...
```
Examples:
@ -72,31 +121,31 @@ lead to 'tall man' while `/Tall` will become 'Tall man' and /TALL
becomes /TALL MAN. If you don't want this behavior, you can pass
case_sensitive=False to the `send_emote` function.
Verbose Installation Instructions:
Extra Installation Instructions:
1. In typeclasses/character.py:
Import the `ContribRPCharacter` class:
`from evennia.contrib.rpsystem import ContribRPCharacter`
Inherit ContribRPCharacter:
Change "class Character(DefaultCharacter):" to
`class Character(ContribRPCharacter):`
If you have any overriden calls in `at_object_creation(self)`:
Add `super().at_object_creation()` as the top line.
2. In `typeclasses/rooms.py`:
Import the `ContribRPRoom` class:
`from evennia.contrib.rpsystem import ContribRPRoom`
Inherit `ContribRPRoom`:
Change `class Room(DefaultRoom):` to
`class Room(ContribRPRoom):`
3. In `typeclasses/objects.py`
Import the `ContribRPObject` class:
`from evennia.contrib.rpsystem import ContribRPObject`
Inherit `ContribRPObject`:
Change `class Object(DefaultObject):` to
`class Object(ContribRPObject):`
4. Reload the server (`reload` or from console: "evennia reload")
5. Force typeclass updates as required. Example for your character:
`type/reset/force me = typeclasses.characters.Character`
1. In typeclasses/character.py:
Import the `ContribRPCharacter` class:
`from evennia.contrib.rpsystem import ContribRPCharacter`
Inherit ContribRPCharacter:
Change "class Character(DefaultCharacter):" to
`class Character(ContribRPCharacter):`
If you have any overriden calls in `at_object_creation(self)`:
Add `super().at_object_creation()` as the top line.
2. In `typeclasses/rooms.py`:
Import the `ContribRPRoom` class:
`from evennia.contrib.rpsystem import ContribRPRoom`
Inherit `ContribRPRoom`:
Change `class Room(DefaultRoom):` to
`class Room(ContribRPRoom):`
3. In `typeclasses/objects.py`
Import the `ContribRPObject` class:
`from evennia.contrib.rpsystem import ContribRPObject`
Inherit `ContribRPObject`:
Change `class Object(DefaultObject):` to
`class Object(ContribRPObject):`
4. Reload the server (`reload` or from console: "evennia reload")
5. Force typeclass updates as required. Example for your character:
`type/reset/force me = typeclasses.characters.Character`
"""
import re

View file

@ -0,0 +1,443 @@
# Traits
Whitenoise 2014, Ainneve contributors,
Griatch 2020
A `Trait` represents a modifiable property on (usually) a Character. They can
be used to represent everything from attributes (str, agi etc) to skills
(hunting 10, swords 14 etc) and dynamically changing things like HP, XP etc.
Traits differ from normal Attributes in that they track their changes and limit
themselves to particular value-ranges. One can add/subtract from them easily and
they can even change dynamically at a particular rate (like you being poisoned or
healed).
Traits use Evennia Attributes under the hood, making them persistent (they survive
a server reload/reboot).
## Installation
Traits are always added to a typeclass, such as the Character class.
There are two ways to set up Traits on a typeclass. The first sets up the `TraitHandler`
as a property `.traits` on your class and you then access traits as e.g. `.traits.strength`.
The other alternative uses a `TraitProperty`, which makes the trait available directly
as e.g. `.strength`. This solution also uses the `TraitHandler`, but you don't need to
define it explicitly. You can combine both styles if you like.
### Traits with TraitHandler
Here's an example for adding the TraitHandler to the Character class:
```python
# mygame/typeclasses/objects.py
from evennia import DefaultCharacter
from evennia.utils import lazy_property
from evennia.contrib.traits import TraitHandler
# ...
class Character(DefaultCharacter):
...
@lazy_property
def traits(self):
# this adds the handler as .traits
return TraitHandler(self)
def at_object_creation(self):
# (or wherever you want)
self.traits.add("str", "Strength", trait_type="static", base=10, mod=2)
self.traits.add("hp", "Health", trait_type="gauge", min=0, max=100)
self.traits.add("hunting", "Hunting Skill", trait_type="counter",
base=10, mod=1, min=0, max=100)
```
When adding the trait, you supply the name of the property (`hunting`) along
with a more human-friendly name ("Hunting Skill"). The latter will show if you
print the trait etc. The `trait_type` is important, this specifies which type
of trait this is (see below).
### TraitProperties
Using `TraitProperties` makes the trait available directly on the class, much like Django model
fields. The drawback is that you must make sure that the name of your Traits don't collide with any
other properties/methods on your class.
```python
# mygame/typeclasses/objects.py
from evennia import DefaultObject
from evennia.utils import lazy_property
from evennia.contrib.traits import TraitProperty
# ...
class Object(DefaultObject):
...
strength = TraitProperty("Strength", trait_type="static", base=10, mod=2)
health = TraitProperty("Health", trait_type="gauge", min=0, base=100, mod=2)
hunting = TraitProperty("Hunting Skill", trait_type="counter", base=10, mod=1, min=0, max=100)
```
> Note that the property-name will become the name of the trait and you don't supply `trait_key`
> separately.
> The `.traits` TraitHandler will still be created (it's used under the
> hood. But it will only be created when the TraitProperty has been accessed at least once,
> so be careful if mixing the two styles. If you want to make sure `.traits` is always available,
> add the `TraitHandler` manually like shown earlier - the `TraitProperty` will by default use
> the same handler (`.traits`).
## Using traits
A trait is added to the traithandler (if you use `TraitProperty` the handler is just created under
the hood) after which one can access it as a property on the handler (similarly to how you can do
.db.attrname for Attributes in Evennia).
All traits have a _read-only_ field `.value`. This is only used to read out results, you never
manipulate it directly (if you try, it will just remain unchanged). The `.value` is calculated based
on combining fields, like `.base` and `.mod` - which fields are available and how they relate to
each other depends on the trait type.
```python
> obj.traits.strength.value
12 # base + mod
> obj.traits.strength.base += 5
obj.traits.strength.value
17
> obj.traits.hp.value
102 # base + mod
> obj.traits.hp.base -= 200
> obj.traits.hp.value
0 # min of 0
> obj.traits.hp.reset()
> obj.traits.hp.value
100
# you can also access properties like a dict
> obj.traits.hp["value"]
100
# you can store arbitrary data persistently for easy reference
> obj.traits.hp.effect = "poisoned!"
> obj.traits.hp.effect
"poisoned!"
# with TraitProperties:
> obj.hunting.value
12
> obj.strength.value += 5
> obj.strength.value
17
```
## Trait types
All default traits have a read-only `.value` property that shows the relevant or
'current' value of the trait. Exactly what this means depends on the type of trait.
Traits can also be combined to do arithmetic with their .value, if both have a
compatible type.
```python
> trait1 + trait2
54
> trait1.value
3
> trait1 + 2
> trait1.value
5
```
Two numerical traits can also be compared (bigger-than etc), which is useful in
all sorts of rule-resolution.
```python
if trait1 > trait2:
# do stuff
```
## Static trait
`value = base + mod`
The static trait has a `base` value and an optional `mod`-ifier. A typical use
of a static trait would be a Strength stat or Skill value. That is, something
that varies slowly or not at all, and which may be modified in-place.
```python
> obj.traits.add("str", "Strength", trait_type="static", base=10, mod=2)
> obj.traits.mytrait.value
12 # base + mod
> obj.traits.mytrait.base += 2
> obj.traits.mytrait.mod += 1
> obj.traits.mytrait.value
15
> obj.traits.mytrait.mod = 0
> obj.traits.mytrait.value
12
```
### Counter
min/unset base base+mod max/unset
|--------------|--------|---------X--------X------------|
current value
= current
+ mod
A counter describes a value that can move from a base. The `.current` property
is the thing usually modified. It starts at the `.base`. One can also add a
modifier, which will both be added to the base and to current (forming
`.value`). The min/max of the range are optional, a boundary set to None will
remove it. A suggested use for a Counter Trait would be to track skill values.
```python
> obj.traits.add("hunting", "Hunting Skill", trait_type="counter",
base=10, mod=1, min=0, max=100)
> obj.traits.hunting.value
11 # current starts at base + mod
> obj.traits.hunting.current += 10
> obj.traits.hunting.value
21
# reset back to base+mod by deleting current
> del obj.traits.hunting.current
> obj.traits.hunting.value
11
> obj.traits.hunting.max = None # removing upper bound
# for TraitProperties, pass the args/kwargs of traits.add() to the
# TraitProperty constructor instead.
```
Counters have some extra properties:
#### .descs
The `descs` property is a dict `{upper_bound:text_description}`. This allows for easily
storing a more human-friendly description of the current value in the
interval. Here is an example for skill values between 0 and 10:
{0: "unskilled", 1: "neophyte", 5: "trained", 7: "expert", 9: "master"}
The keys must be supplied from smallest to largest. Any values below the lowest and above the
highest description will be considered to be included in the closest description slot.
By calling `.desc()` on the Counter, you will get the text matching the current `value`.
```python
# (could also have passed descs= to traits.add())
> obj.traits.hunting.descs = {
0: "unskilled", 10: "neophyte", 50: "trained", 70: "expert", 90: "master"}
> obj.traits.hunting.value
11
> obj.traits.hunting.desc()
"neophyte"
> obj.traits.hunting.current += 60
> obj.traits.hunting.value
71
> obj.traits.hunting.desc()
"expert"
```
#### .rate
The `rate` property defaults to 0. If set to a value different from 0, it
allows the trait to change value dynamically. This could be used for example
for an attribute that was temporarily lowered but will gradually (or abruptly)
recover after a certain time. The rate is given as change of the current
`.value` per-second, and this will still be restrained by min/max boundaries,
if those are set.
It is also possible to set a `.ratetarget`, for the auto-change to stop at
(rather than at the min/max boundaries). This allows the value to return to
a previous value.
```python
> obj.traits.hunting.value
71
> obj.traits.hunting.ratetarget = 71
# debuff hunting for some reason
> obj.traits.hunting.current -= 30
> obj.traits.hunting.value
41
> obj.traits.hunting.rate = 1 # 1/s increase
# Waiting 5s
> obj.traits.hunting.value
46
# Waiting 8s
> obj.traits.hunting.value
54
# Waiting 100s
> obj.traits.hunting.value
71 # we have stopped at the ratetarget
> obj.traits.hunting.rate = 0 # disable auto-change
```
Note that when retrieving the `current`, the result will always be of the same
type as the `.base` even `rate` is a non-integer value. So if `base` is an `int`
(default)`, the `current` value will also be rounded the closest full integer.
If you want to see the exact `current` value, set `base` to a float - you
will then need to use `round()` yourself on the result if you want integers.
#### .percent()
If both min and max are defined, the `.percent()` method of the trait will
return the value as a percentage.
```python
> obj.traits.hunting.percent()
"71.0%"
> obj.traits.hunting.percent(formatting=None)
71.0
```
### Gauge
This emulates a [fuel-] gauge that empties from a base+mod value.
min/0 max=base+mod
|-----------------------X---------------------------|
value
= current
The `.current` value will start from a full gauge. The .max property is
read-only and is set by `.base` + `.mod`. So contrary to a `Counter`, the
`.mod` modifier only applies to the max value of the gauge and not the current
value. The minimum bound defaults to 0 if not set explicitly.
This trait is useful for showing commonly depletable resources like health,
stamina and the like.
```python
> obj.traits.add("hp", "Health", trait_type="gauge", base=100)
> obj.traits.hp.value # (or .current)
100
> obj.traits.hp.mod = 10
> obj.traits.hp.value
110
> obj.traits.hp.current -= 30
> obj.traits.hp.value
80
```
The Gauge trait is subclass of the Counter, so you have access to the same
methods and properties where they make sense. So gauges can also have a
`.descs` dict to describe the intervals in text, and can use `.percent()` to
get how filled it is as a percentage etc.
The `.rate` is particularly relevant for gauges - useful for everything
from poison slowly draining your health, to resting gradually increasing it.
### Trait
A single value of any type.
This is the 'base' Trait, meant to inherit from if you want to invent
trait-types from scratch (most of the time you'll probably inherit from some of
the more advanced trait-type classes though).
Unlike other Trait-types, the single `.value` property of the base `Trait` can
be editied. The value can hold any data that can be stored in an Attribute. If
it's an integer/float you can do arithmetic with it, but otherwise this acts just
like a glorified Attribute.
```python
> obj.traits.add("mytrait", "My Trait", trait_type="trait", value=30)
> obj.traits.mytrait.value
30
> obj.traits.mytrait.value = "stringvalue"
> obj.traits.mytrait.value
"stringvalue"
```
## Expanding with your own Traits
A Trait is a class inhering from `evennia.contrib.traits.Trait` (or from one of
the existing Trait classes).
```python
# in a file, say, 'mygame/world/traits.py'
from evennia.contrib.traits import StaticTrait
class RageTrait(StaticTrait):
trait_type = "rage"
default_keys = {
"rage": 0
}
def berserk(self):
self.mod = 100
def sedate(self):
self.mod = 0
```
Above is an example custom-trait-class "rage" that stores a property "rage" on
itself, with a default value of 0. This has all the functionality of a Trait -
for example, if you do del on the `rage` property, it will be set back to its
default (0). Above we also added some helper methods.
To add your custom RageTrait to Evennia, add the following to your settings file
(assuming your class is in mygame/world/traits.py):
TRAIT_CLASS_PATHS = ["world.traits.RageTrait"]
Reload the server and you should now be able to use your trait:
```python
> obj.traits.add("mood", "A dark mood", rage=30, trait_type='rage')
> obj.traits.mood.rage
30
# as TraitProperty
class Character(DefaultCharacter):
rage = TraitProperty("A dark mood", rage=30, trait_type='rage')
```

View file

@ -0,0 +1,10 @@
"""
Traits - Whitenoise 2014, Griatch 2020
"""
from .traits import TraitException # noqa
from .traits import MandatoryTraitKey # noqa
from .traits import TraitHandler # noqa
from .traits import TraitProperty # noqa
from .traits import Trait, StaticTrait, CounterTrait, GaugeTrait # noqa

View file

@ -10,8 +10,7 @@ from copy import copy
from anything import Something
from mock import MagicMock, patch
from django.test import TestCase
from evennia.utils.utils import lazy_property
from evennia.contrib import traits
from . import traits
class _MockObj:

Some files were not shown because too many files have changed in this diff Show more