mirror of
https://github.com/evennia/evennia.git
synced 2026-03-17 05:16:31 +01:00
Refactoring contribs
This commit is contained in:
parent
f5f75bd04d
commit
0ab1c30716
103 changed files with 3203 additions and 604 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
"""
|
||||
|
|
|
|||
127
evennia/contrib/base_systems/building_menu/README.md
Normal file
127
evennia/contrib/base_systems/building_menu/README.md
Normal 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.
|
||||
6
evennia/contrib/base_systems/building_menu/__init__.py
Normal file
6
evennia/contrib/base_systems/building_menu/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"""
|
||||
Build-menu contrib - vincent-lg 2018
|
||||
|
||||
"""
|
||||
from .building_menu import GenericBuildingCmd # noqa
|
||||
from .building_menu import BuildingMenu # noqa
|
||||
|
|
@ -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,
|
||||
57
evennia/contrib/base_systems/color_markups/README.md
Normal file
57
evennia/contrib/base_systems/color_markups/README.md
Normal 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
|
||||
```
|
||||
6
evennia/contrib/base_systems/color_markups/__init__.py
Normal file
6
evennia/contrib/base_systems/color_markups/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"""
|
||||
Color markups contrib - Griatch 2017
|
||||
|
||||
"""
|
||||
|
||||
from .color_markups import * # noqa
|
||||
|
|
@ -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
|
||||
47
evennia/contrib/base_systems/custom_gametime/README.md
Normal file
47
evennia/contrib/base_systems/custom_gametime/README.md
Normal 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.
|
||||
6
evennia/contrib/base_systems/custom_gametime/__init__.py
Normal file
6
evennia/contrib/base_systems/custom_gametime/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"""
|
||||
Custom gametime contrib - Griatch, vlgeoff 2017
|
||||
|
||||
"""
|
||||
|
||||
from .custom_gametime import * # noqa
|
||||
30
evennia/contrib/base_systems/email_login/README.md
Normal file
30
evennia/contrib/base_systems/email_login/README.md
Normal 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).
|
||||
7
evennia/contrib/base_systems/email_login/__init__.py
Normal file
7
evennia/contrib/base_systems/email_login/__init__.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""
|
||||
Email login contrib - Griatch 2012
|
||||
|
||||
"""
|
||||
|
||||
from .email_login import UnloggedinCmdSet # noqa
|
||||
from . import connection_screens # noqa
|
||||
|
|
@ -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")
|
||||
)
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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+)')
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 = """
|
||||
|
|
|
|||
|
|
@ -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 = []
|
||||
|
|
|
|||
22
evennia/contrib/base_systems/menu_login/README.md
Normal file
22
evennia/contrib/base_systems/menu_login/README.md
Normal 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).
|
||||
7
evennia/contrib/base_systems/menu_login/__init__.py
Normal file
7
evennia/contrib/base_systems/menu_login/__init__.py
Normal 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
|
||||
|
|
@ -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")
|
||||
)
|
||||
|
|
@ -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."
|
||||
42
evennia/contrib/base_systems/mux_comms_cmds/README.md
Normal file
42
evennia/contrib/base_systems/mux_comms_cmds/README.md
Normal 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.
|
||||
6
evennia/contrib/base_systems/mux_comms_cmds/__init__.py
Normal file
6
evennia/contrib/base_systems/mux_comms_cmds/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"""
|
||||
Mux-style comms commands - Griatch 2021
|
||||
|
||||
"""
|
||||
|
||||
from .mux_comms_cmds import CmdSetLegacyComms # noqa
|
||||
|
|
@ -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):
|
||||
# ...
|
||||
65
evennia/contrib/base_systems/unixcommand/README.md
Normal file
65
evennia/contrib/base_systems/unixcommand/README.md
Normal 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).
|
||||
6
evennia/contrib/base_systems/unixcommand/__init__.py
Normal file
6
evennia/contrib/base_systems/unixcommand/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"""
|
||||
Unix-like Command style - vlgeoff 2017
|
||||
|
||||
"""
|
||||
|
||||
from .unixcommand import UnixCommand # noqa
|
||||
|
|
@ -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):
|
||||
|
||||
'''
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
128
evennia/contrib/game_systems/barter/README.md
Normal file
128
evennia/contrib/game_systems/barter/README.md
Normal 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'.
|
||||
83
evennia/contrib/game_systems/barter/]
Normal file
83
evennia/contrib/game_systems/barter/]
Normal 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.
|
||||
6
evennia/contrib/game_systems/barter/__init__.py
Normal file
6
evennia/contrib/game_systems/barter/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"""
|
||||
Barter contrib - Griatch 2012
|
||||
|
||||
"""
|
||||
|
||||
from .barter import CmdsetTrade # noqa
|
||||
|
|
@ -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
|
||||
|
||||
84
evennia/contrib/game_systems/clothing/README.md
Normal file
84
evennia/contrib/game_systems/clothing/README.md
Normal 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.
|
||||
|
||||
7
evennia/contrib/game_systems/clothing/__init__.py
Normal file
7
evennia/contrib/game_systems/clothing/__init__.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""
|
||||
Clothing contrib - Tim Ashley Jenkins 2017
|
||||
|
||||
"""
|
||||
|
||||
from .clothing import ClothedCharacter # noqa
|
||||
from .clothing import ClothedCharacterCmdSet # noqa
|
||||
58
evennia/contrib/game_systems/cooldowns/README.md
Normal file
58
evennia/contrib/game_systems/cooldowns/README.md
Normal 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!")
|
||||
|
||||
```
|
||||
6
evennia/contrib/game_systems/cooldowns/__init__.py
Normal file
6
evennia/contrib/game_systems/cooldowns/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"""
|
||||
Cooldown contrib - owllex 2021
|
||||
|
||||
"""
|
||||
|
||||
from .cooldowns import CooldownHandler # noqa
|
||||
|
|
@ -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
|
||||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
"""
|
||||
Crafting - Griatch 2020
|
||||
|
||||
"""
|
||||
|
||||
from .crafting import CraftingRecipe # noqa
|
||||
from .crafting import CraftingValidationError # noqa
|
||||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
49
evennia/contrib/game_systems/gendersub/README.md
Normal file
49
evennia/contrib/game_systems/gendersub/README.md
Normal 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.
|
||||
|
||||
7
evennia/contrib/game_systems/gendersub/__init__.py
Normal file
7
evennia/contrib/game_systems/gendersub/__init__.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""
|
||||
Gendersub - Griatch 2015
|
||||
|
||||
"""
|
||||
|
||||
from .gendersub import GenderCharacter # noqa
|
||||
from .gendersub import SetGender # noqa
|
||||
|
|
@ -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):
|
||||
40
evennia/contrib/game_systems/mail/README.md
Normal file
40
evennia/contrib/game_systems/mail/README.md
Normal 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.
|
||||
|
||||
7
evennia/contrib/game_systems/mail/__init__.py
Normal file
7
evennia/contrib/game_systems/mail/__init__.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""
|
||||
Mail system - grungies 2016
|
||||
|
||||
"""
|
||||
|
||||
from .mail import CmdMail # noqa
|
||||
from .mail import CmdMailCharacter # noqa
|
||||
|
|
@ -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.
|
||||
|
||||
"""
|
||||
|
||||
24
evennia/contrib/game_systems/multidescer/README.md
Normal file
24
evennia/contrib/game_systems/multidescer/README.md
Normal 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).
|
||||
6
evennia/contrib/game_systems/multidescer/__init__.py
Normal file
6
evennia/contrib/game_systems/multidescer/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"""
|
||||
Multidescer - Griatch 2016
|
||||
|
||||
"""
|
||||
|
||||
from .multidescer import CmdMultiDesc # noqa
|
||||
|
|
@ -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
|
||||
68
evennia/contrib/game_systems/puzzles/README.md
Normal file
68
evennia/contrib/game_systems/puzzles/README.md
Normal 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.
|
||||
7
evennia/contrib/game_systems/puzzles/__init__.py
Normal file
7
evennia/contrib/game_systems/puzzles/__init__.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""
|
||||
Puzzles - Henddher 2018
|
||||
|
||||
"""
|
||||
|
||||
from .puzzles import PuzzleSystemCmdSet # noqa
|
||||
from .puzzles import PuzzleRecipe # noqa
|
||||
|
|
@ -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')
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
5
evennia/contrib/grid/__init__.py
Normal file
5
evennia/contrib/grid/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
"""
|
||||
Extened Room - Griatch 2012, vincent-lg 2019
|
||||
|
||||
"""
|
||||
|
||||
76
evennia/contrib/grid/extended_room/README.md
Normal file
76
evennia/contrib/grid/extended_room/README.md
Normal 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.
|
||||
11
evennia/contrib/grid/extended_room/__init__.py
Normal file
11
evennia/contrib/grid/extended_room/__init__.py
Normal 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
|
||||
|
|
@ -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) # <---
|
||||
|
||||
```
|
||||
|
||||
278
evennia/contrib/grid/mapbuilder/README.md
Normal file
278
evennia/contrib/grid/mapbuilder/README.md
Normal 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,
|
||||
}
|
||||
|
||||
```
|
||||
6
evennia/contrib/grid/mapbuilder/__init__.py
Normal file
6
evennia/contrib/grid/mapbuilder/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"""
|
||||
Mapbuilder - Cloud Keeper 2016
|
||||
|
||||
"""
|
||||
|
||||
from .mapbuilder import CmdMapBuilder # noqa
|
||||
|
|
@ -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)
|
||||
|
||||
44
evennia/contrib/grid/simpledoor/README.md
Normal file
44
evennia/contrib/grid/simpledoor/README.md
Normal 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.
|
||||
7
evennia/contrib/grid/simpledoor/__init__.py
Normal file
7
evennia/contrib/grid/simpledoor/__init__.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""
|
||||
Simple Door - Griatch 2016
|
||||
|
||||
"""
|
||||
|
||||
from .simpledoor import SimpleDoorCmdSet # noqa
|
||||
from .simpledoor import SimpleDoor # noqa
|
||||
|
|
@ -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())
|
||||
61
evennia/contrib/grid/slow_exit/README.md
Normal file
61
evennia/contrib/grid/slow_exit/README.md
Normal 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.
|
||||
9
evennia/contrib/grid/slow_exit/__init__.py
Normal file
9
evennia/contrib/grid/slow_exit/__init__.py
Normal 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
|
||||
|
|
@ -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())
|
||||
113
evennia/contrib/grid/wilderness/README.md
Normal file
113
evennia/contrib/grid/wilderness/README.md
Normal 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.
|
||||
14
evennia/contrib/grid/wilderness/__init__.py
Normal file
14
evennia/contrib/grid/wilderness/__init__.py
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 = """
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
61
evennia/contrib/rpg/dice/README.md
Normal file
61
evennia/contrib/rpg/dice/README.md
Normal 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
|
||||
|
||||
```
|
||||
9
evennia/contrib/rpg/dice/__init__.py
Normal file
9
evennia/contrib/rpg/dice/__init__.py
Normal 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
|
||||
|
|
@ -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"(\+|-|/|\*)")
|
||||
36
evennia/contrib/rpg/health_bar/README.md
Normal file
36
evennia/contrib/rpg/health_bar/README.md
Normal 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.
|
||||
|
||||
6
evennia/contrib/rpg/health_bar/__init__.py
Normal file
6
evennia/contrib/rpg/health_bar/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"""
|
||||
Health bar - Tim Ashley Jenkins, 2017
|
||||
|
||||
"""
|
||||
|
||||
from .health_bar import display_meter # noqa
|
||||
|
|
@ -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 = ""
|
||||
257
evennia/contrib/rpg/rpsystem/README.md
Normal file
257
evennia/contrib/rpg/rpsystem/README.md
Normal 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.
|
||||
19
evennia/contrib/rpg/rpsystem/__init__.py
Normal file
19
evennia/contrib/rpg/rpsystem/__init__.py
Normal 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
|
||||
|
|
@ -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:
|
||||
|
|
@ -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
|
||||
443
evennia/contrib/rpg/traits/README.md
Normal file
443
evennia/contrib/rpg/traits/README.md
Normal 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')
|
||||
|
||||
```
|
||||
10
evennia/contrib/rpg/traits/__init__.py
Normal file
10
evennia/contrib/rpg/traits/__init__.py
Normal 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
|
||||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue