Updated HTML docs.

This commit is contained in:
Griatch 2022-11-15 19:43:25 +00:00
parent 59e50f3fa5
commit 06bc3c8bcd
663 changed files with 2 additions and 61705 deletions

View file

@ -1,232 +0,0 @@
# AWSstorage system
Contrib by The Right Honourable Reverend (trhr), 2020
This plugin migrates the Web-based portion of Evennia, namely images,
javascript, and other items located inside staticfiles into Amazon AWS (S3)
cloud hosting. Great for those serving media with the game.
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) ...
- 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.
## On costs
Note that storing and serving files via S3 is not technically free outside of
Amazon's "free tier" offering, which you may or may not be eligible for;
setting up a vanilla evennia server with this contrib currently requires 1.5MB
of storage space on S3, making the current total cost of running this plugin
~$0.0005 per year. If you have substantial media assets and intend to serve
them to many users, caveat emptor on a total cost of ownership - check AWS's
pricing structure.
## Technical details
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:
Evennia -> Django
Django -> Storage backend
Storage backend -> file storage location (e.g. hard drive)
[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,
sending the files to S3 via the storage backend defined herein.
There is no way (or need) to directly access or use the functions here with
other contributions or custom code. Simply work how you would normally, Django
will handle the rest.
## Installation
### Set up AWS account
If you don't have an AWS S3 account, you should create one at
https://aws.amazon.com/ - documentation for AWS S3 is available at:
https://docs.aws.amazon.com/AmazonS3/latest/gsg/GetStartedWithS3.html
Credentials required within the app are AWS IAM Access Key and Secret Keys,
which can be generated/found in the AWS Console.
The following example IAM Control Policy Permissions can be added to
the IAM service inside AWS. Documentation for this can be found here:
https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html
Note that this is only required if you want to tightly secure the roles
that this plugin has access to.
```
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "evennia",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObjectAcl",
"s3:GetObject",
"s3:ListBucket",
"s3:DeleteObject",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::YOUR_BUCKET_NAME/*",
"arn:aws:s3:::YOUR_BUCKET_NAME"
]
}
],
[
{
"Sid":"evennia",
"Effect":"Allow",
"Action":[
"s3:CreateBucket",
],
"Resource":[
"arn:aws:s3:::*"
]
}
]
}
```
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
This package requires the dependency "boto3 >= 1.4.4", the official
AWS python package. To install, it's easiest to just install Evennia's
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`
## 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.
```python
# START OF SECRET_SETTINGS.PY COPY/PASTE >>>
AWS_ACCESS_KEY_ID = 'THIS_IS_PROVIDED_BY_AMAZON'
AWS_SECRET_ACCESS_KEY = 'THIS_IS_PROVIDED_BY_AMAZON'
AWS_STORAGE_BUCKET_NAME = 'mygame-evennia' # CHANGE ME! I suggest yourgamename-evennia
# The settings below need to go in secret_settings,py as well, but will
# not need customization unless you want to do something particularly fancy.
AWS_S3_REGION_NAME = 'us-east-1' # N. Virginia
AWS_S3_OBJECT_PARAMETERS = { 'Expires': 'Thu, 31 Dec 2099 20:00:00 GMT',
'CacheControl': 'max-age=94608000', }
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.base_systems.awsstorage.aws-s3-cdn.S3Boto3Storage'
# <<< END OF SECRET_SETTINGS.PY COPY/PASTE
```
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`.
## 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.
## Uninstallation
If you haven't made changes to your static files (uploaded images, etc),
you can simply remove the lines you added to `secret_settings.py`. If you
have made changes and want to uninstall at a later date, you can export
your files from your S3 bucket and put them in /static/ in the evennia
directory.
## License
Draws heavily from code provided by django-storages, for which these contributors
are authors:
Marty Alchin (S3)
David Larlet (S3)
Arne Brodowski (S3)
Sebastian Serrano (S3)
Andrew McClain (MogileFS)
Rafal Jonca (FTP)
Chris McCormick (S3 with Boto)
Ivanov E. (Database)
Ariel Núñez (packaging)
Wim Leers (SymlinkOrCopy + patches)
Michael Elsdörfer (Overwrite + PEP8 compatibility)
Christian Klein (CouchDB)
Rich Leland (Mosso Cloud Files)
Jason Christa (patches)
Adam Nelson (patches)
Erik CW (S3 encryption)
Axel Gembe (Hash path)
Waldemar Kornewald (MongoDB)
Russell Keith-Magee (Apache LibCloud patches)
Jannis Leidel (S3 and GS with Boto)
Andrei Coman (Azure)
Chris Streeter (S3 with Boto)
Josh Schneier (Fork maintainer, Bugfixes, Py3K)
Anthony Monthe (Dropbox)
EunPyo (Andrew) Hong (Azure)
Michael Barrientos (S3 with Boto3)
piglei (patches)
Matt Braymer-Hayes (S3 with Boto3)
Eirik Martiniussen Sylliaas (Google Cloud Storage native support)
Jody McIntyre (Google Cloud Storage native support)
Stanislav Kaledin (Bug fixes in SFTPStorage)
Filip Vavera (Google Cloud MIME types support)
Max Malysh (Dropbox large file support)
Scott White (Google Cloud updates)
Alex Watt (Google Cloud Storage patch)
Jumpei Yoshimura (S3 docs)
Jon Dufresne
Rodrigo Gadea (Dropbox fixes)
Martey Dodoo
Chris Rink
Shaung Cheng (S3 docs)
Andrew Perry (Bug fixes in SFTPStorage)
The repurposed code from django-storages is released under BSD 3-Clause,
same as Evennia, so for detailed licensing, refer to the Evennia license.
## Versioning
This is confirmed to work for Django 2 and Django 3.
----
<small>This document page is generated from `evennia/contrib/base_systems/awsstorage/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,77 +0,0 @@
# Input/Output Auditing
Contribution by Johnny, 2017
Utility that taps and intercepts all data sent to/from clients and the
server and passes it to a callback of your choosing. This is intended for
quality assurance, post-incident investigations and debugging.
Note that this should be used with care since it can obviously be abused. All
data is recorded in cleartext. Please be ethical, and if you are unwilling to
properly deal with the implications of recording user passwords or private
communications, please do not enable this module.
Some checks have been implemented to protect the privacy of users.
Files included in this module:
outputs.py - Example callback methods. This module ships with examples of
callbacks that send data as JSON to a file in your game/server/logs
dir or to your native Linux syslog daemon. You can of course write
your own to do other things like post them to Kafka topics.
server.py - Extends the Evennia ServerSession object to pipe data to the
callback upon receipt.
tests.py - Unit tests that check to make sure commands with sensitive
arguments are having their PII scrubbed.
## Installation/Configuration:
Deployment is completed by configuring a few settings in server.conf. This line
is required:
SERVER_SESSION_CLASS = 'evennia.contrib.utils.auditing.server.AuditedServerSession'
This tells Evennia to use this ServerSession instead of its own. Below are the
other possible options along with the default value that will be used if unset.
# Where to send logs? Define the path to a module containing your callback
# function. It should take a single dict argument as input
AUDIT_CALLBACK = 'evennia.contrib.utils.auditing.outputs.to_file'
# Log user input? Be ethical about this; it will log all private and
# public communications between players and/or admins (default: False).
AUDIT_IN = False
# Log server output? This will result in logging of ALL system
# messages and ALL broadcasts to connected players, so on a busy game any
# broadcast to all users will yield a single event for every connected user!
AUDIT_OUT = False
# The default output is a dict. Do you want to allow key:value pairs with
# null/blank values? If you're just writing to disk, disabling this saves
# some disk space, but whether you *want* sparse values or not is more of a
# consideration if you're shipping logs to a NoSQL/schemaless database.
# (default: False)
AUDIT_ALLOW_SPARSE = False
# If you write custom commands that handle sensitive data like passwords,
# you must write a regular expression to remove that before writing to log.
# AUDIT_MASKS is a list of dictionaries that define the names of commands
# and the regexes needed to scrub them.
# The system already has defaults to filter out sensitive login/creation
# commands in the default command set. Your list of AUDIT_MASKS will be appended
# to those defaults.
#
# In the regex, the sensitive data itself must be captured in a named group with a
# label of 'secret' (see the Python docs on the `re` module for more info). For
# example: `{'authentication': r"^@auth\s+(?P<secret>[\w]+)"}`
AUDIT_MASKS = []
----
<small>This document page is generated from `evennia/contrib/utils/auditing/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,130 +0,0 @@
# Barter system
Contribution by Griatch, 2012
This implements a full barter system - a way for players to safely
trade items between each other in code rather than simple `give/get`
commands. This increases both safety (at no time will one player have
both goods and payment in-hand) and speed, since agreed goods will
be moved automatically). By just replacing one side with coin objects,
(or a mix of coins and goods), this also works fine for regular money
transactions.
## 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'.
----
<small>This document page is generated from `evennia/contrib/game_systems/barter/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,41 +0,0 @@
# Batch processor examples
Contibution by Griatch, 2012
Simple examples for the batch-processor. The batch processor is used for generating
in-game content from one or more static files. Files can be stored with version
control and then 'applied' to the game to create content.
There are two batch processor types:
- Batch-cmd processor: A list of `#`-separated Evennia commands being executed
in sequence, such as `create`, `dig`, `north` etc. When running a script
of this type (filename ending with `.ev`), the caller of the script will be
the one performing the script's actions.
- Batch-code processor: A full Python script (filename ending with `.py` that
executes Evennia api calls to build, such as `evennia.create_object` or
`evennia.search_object` etc. It can be divided up into comment-separated
chunks so one can execute only parts of the script at a time (in this way it's
a little different than a normal Python file).
## Usage
To test the two example batch files, you need `Developer` or `superuser`
permissions, be logged into the game and run of
> batchcommand/interactive tutorials.batchprocessor.example_batch_cmds
> batchcode/interactive tutorials.batchprocessor.example_batch_code
The `/interactive` drops you in interactive mode so you can follow along what
the scripts do. Skip it to build it all at once.
Both commands produce the same results - they create a red-button object,
a table and a chair. If you run either with the `/debug` switch, the objects will
be deleted afterwards (for quick tests of syntax that you don't want to spam new
objects, for example).
----
<small>This document page is generated from `evennia/contrib/tutorials/batchprocessor/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,22 +0,0 @@
# Script example
Contribution by Griatch, 2012
Example script for testing. This adds a simple timer that has your
character make small verbal observations at irregular intervals.
To test, use (in game)
> script me = contrib.tutorials.bodyfunctions.BodyFunctions
## Notes
Use `scripts me` to see the script running on you. Note that even though
the timer ticks down to 0, you will _not_ see an echo every tick (it's
random if an echo is given on a tick or not).
----
<small>This document page is generated from `evennia/contrib/tutorials/bodyfunctions/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,440 +0,0 @@
# Buffs
Contribution by Tegiminis 2022
A buff is a timed object, attached to a game entity. It is capable of modifying values, triggering code, or both.
It is a common design pattern in RPGs, particularly action games.
Features:
- `BuffHandler`: A buff handler to apply to your objects.
- `BaseBuff`: A buff class to extend from to create your own buffs.
- `BuffableProperty`: A sample property class to show how to automatically check modifiers.
- `CmdBuff`: A command which applies buffs.
- `samplebuffs.py`: Some sample buffs to learn from.
## Quick Start
Assign the handler to a property on the object, like so.
```python
@lazy_property
def buffs(self) -> BuffHandler:
return BuffHandler(self)
```
You may then call the handler to add or manipulate buffs like so: `object.buffs`. See **Using the Handler**.
### Customization
If you want to customize the handler, you can feed the constructor two arguments:
- `dbkey`: The string you wish to use as the attribute key for the buff database. Defaults to "buffs". This allows you to keep separate buff pools - for example, "buffs" and "perks".
- `autopause`: If you want this handler to automatically pause playtime buffs when its owning object is unpuppeted.
> **Note**: If you enable autopausing, you MUST initialize the property in your owning object's
> `at_init` hook. Otherwise, a hot reload can cause playtime buffs to not update properly
> on puppet/unpuppet. You have been warned!
Let's say you want another handler for an object, `perks`, which has a separate database and
respects playtime buffs. You'd assign this new property as so:
```python
class BuffableObject(Object):
@lazy_property
def perks(self) -> BuffHandler:
return BuffHandler(self, dbkey='perks', autopause=True)
def at_init(self):
self.perks
```
## Using the Handler
Here's how to make use of your new handler.
### Apply a Buff
Call the handler's `add` method. This requires a class reference, and also contains a number of
optional arguments to customize the buff's duration, stacks, and so on. You can also store any arbitrary value
in the buff's cache by passing a dictionary through the `to_cache` optional argument. This will not overwrite the normal
values on the cache.
```python
self.buffs.add(StrengthBuff) # A single stack of StrengthBuff with normal duration
self.buffs.add(DexBuff, stacks=3, duration=60) # Three stacks of DexBuff, with a duration of 60 seconds
self.buffs.add(ReflectBuff, to_cache={'reflect': 0.5}) # A single stack of ReflectBuff, with an extra cache value
```
Two important attributes on the buff are checked when the buff is applied: `refresh` and `unique`.
- `refresh` (default: True) determines if a buff's timer is refreshed when it is reapplied.
- `unique` (default: True) determines if this buff is unique; that is, only one of it exists on the object.
The combination of these two booleans creates one of three kinds of keys:
- `Unique is True, Refresh is True/False`: The buff's default key.
- `Unique is False, Refresh is True`: The default key mixed with the applier's dbref. This makes the buff "unique-per-player", so you can refresh through reapplication.
- `Unique is False, Refresh is False`: The default key mixed with a randomized number.
### Get Buffs
The handler has several getter methods which return instanced buffs. You won't need to use these for basic functionality, but if you want to manipulate
buffs after application, they are very useful. The handler's `check`/`trigger` methods utilize some of these getters, while others are just for developer convenience.
`get(key)` is the most basic getter. It returns a single buff instance, or `None` if the buff doesn't exist on the handler. It is also the only getter
that returns a single buff instance, rather than a dictionary.
> **Note**: The handler method `has(buff)` allows you to check if a matching key (if a string) or buff class (if a class) is present on the handler cache, without actually instantiating the buff. You should use this method for basic "is this buff present?" checks.
Group getters, listed below, return a dictionary of values in the format `{buffkey: instance}`. If you want to iterate over all of these buffs,
you should do so via the `dict.values()` method.
- `get_all()` returns all buffs on this handler. You can also use the `handler.all` property.
- `get_by_type(BuffClass)` returns buffs of the specified type.
- `get_by_stat(stat)` returns buffs with a `Mod` object of the specified `stat` string in their `mods` list.
- `get_by_trigger(string)` returns buffs with the specified string in their `triggers` list.
- `get_by_source(Object)` returns buffs applied by the specified `source` object.
- `get_by_cachevalue(key, value)` returns buffs with the matching `key: value` pair in their cache. `value` is optional.
All group getters besides `get_all()` can "slice" an existing dictionary through the optional `to_filter` argument.
```python
dict1 = handler.get_by_type(Burned) # This finds all "Burned" buffs on the handler
dict2 = handler.get_by_source(self, to_filter=dict1) # This filters dict1 to find buffs with the matching source
```
> **Note**: Most of these getters also have an associated handler property. For example, `handler.effects` returns all buffs that can be triggered, which
> is then iterated over by the `get_by_trigger` method.
### Remove Buffs
There are also a number of remover methods. Generally speaking, these follow the same format as the getters.
- `remove(key)` removes the buff with the specified key.
- `clear()` removes all buffs.
- `remove_by_type(BuffClass)` removes buffs of the specified type.
- `remove_by_stat(stat)` removes buffs with a `Mod` object of the specified `stat` string in their `mods` list.
- `remove_by_trigger(string)` removes buffs with the specified string in their `triggers` list.
- `remove_by_source(Object)` removes buffs applied by the specified source
- `remove_by_cachevalue(key, value)` removes buffs with the matching `key: value` pair in their cache. `value` is optional.
You can also remove a buff by calling the instance's `remove` helper method. You can do this on the dictionaries returned by the
getters listed above.
```python
to_remove = handler.get_by_trigger(trigger) # Finds all buffs with the specified trigger
for buff in to_remove.values(): # Removes all buffs in the to_remove dictionary via helper methods
buff.remove()
```
### Check Modifiers
Call the handler `check(value, stat)` method when you want to see the modified value.
This will return the `value`, modified by any relevant buffs on the handler's owner (identified by
the `stat` string).
For example, let's say you want to modify how much damage you take. That might look something like this:
```python
# The method we call to damage ourselves
def take_damage(self, source, damage):
_damage = self.buffs.check(damage, 'taken_damage')
self.db.health -= _damage
```
This method calls the `at_pre_check` and `at_post_check` methods at the relevant points in the process. You can use to this make
buffs that are reactive to being checked; for example, removing themselves, altering their values, or interacting with the game state.
> **Note**: You can also trigger relevant buffs at the same time as you check them by ensuring the optional argument `trigger` is True in the `check` method.
Modifiers are calculated additively - that is, all modifiers of the same type are added together before being applied. They are then
applied through the following formula.
```python
(base + total_add) / max(1, 1.0 + total_div) * max(0, 1.0 + total_mult)
```
#### Multiplicative Buffs (Advanced)
Multiply/divide modifiers in this buff system are additive by default. This means that two +50% modifiers will equal a +100% modifier. But what if you want to apply mods multiplicatively?
First, you should carefully consider if you truly want multiplicative modifiers. Here's some things to consider.
- They are unintuitive to the average user, as two +50% damage buffs equal +125% instead of +100%.
- They lead to "power explosion", where stacking buffs in the right way can turn characters into unstoppable forces
Doing purely-additive multipliers allows you to better control the balance of your game. Conversely, doing multiplicative multipliers enables very fun build-crafting where smart usage of buffs and skills can turn you into a one-shot powerhouse. Each has its place.
The best design practice for multiplicative buffs is to divide your multipliers into "tiers", where each tier is applied separately. You can easily do this with multiple `check` calls.
```python
damage = damage
damage = handler.check(damage, 'damage')
damage = handler.check(damage, 'empower')
damage = handler.check(damage, 'radiant')
damage = handler.check(damage, 'overpower')
```
#### Buff Strength Priority (Advanced)
Sometimes you only want to apply the strongest modifier to a stat. This is supported by the optional `strongest` bool arg in the handler's check method
```python
def take_damage(self, source, damage):
_damage = self.buffs.check(damage, 'taken_damage', strongest=True)
self.db.health -= _damage
```
### Trigger Buffs
Call the handler's `trigger(string)` method when you want an event call. This will call the `at_trigger` hook method on all buffs with the relevant trigger `string`.
For example, let's say you want to trigger a buff to "detonate" when you hit your target with an attack.
You'd write a buff that might look like this:
```python
class Detonate(BaseBuff):
...
triggers = ['take_damage']
def at_trigger(self, trigger, *args, **kwargs)
self.owner.take_damage(100)
self.remove()
```
And then call `handler.trigger('take_damage')` in the method you use to take damage.
> **Note** You could also do this through mods and `at_post_check` if you like, depending on how to want to add the damage.
### Ticking
Ticking buffs are slightly special. They are similar to trigger buffs in that they run code, but instead of
doing so on an event trigger, they do so on a periodic tick. A common use case for a buff like this is a poison,
or a heal over time.
```python
class Poison(BaseBuff):
...
tickrate = 5
def at_tick(self, initial=True, *args, **kwargs):
_dmg = self.dmg * self.stacks
if not initial:
self.owner.location.msg_contents(
"Poison courses through {actor}'s body, dealing {damage} damage.".format(
actor=self.owner.named, damage=_dmg
)
)
```
To make a buff ticking, ensure the `tickrate` is 1 or higher, and it has code in its `at_tick`
method. Once you add it to the handler, it starts ticking!
> **Note**: Ticking buffs always tick on initial application, when `initial` is `True`. If you don't want your hook to fire at that time,
> make sure to check the value of `initial` in your `at_tick` method.
### Context
Every important handler method optionally accepts a `context` dictionary.
Context is an important concept for this handler. Every method which checks, triggers, or ticks a buff passes this
dictionary (default: empty) to the buff hook methods as keyword arguments (`**kwargs`). It is used for nothing else. This allows you to make those
methods "event-aware" by storing relevant data in the dictionary you feed to the method.
For example, let's say you want a "thorns" buff which damages enemies that attack you. Let's take our `take_damage` method
and add a context to the mix.
```python
def take_damage(attacker, damage):
context = {'attacker': attacker, 'damage': damage}
_damage = self.buffs.check(damage, 'taken_damage', context=context)
self.buffs.trigger('taken_damage', context=context)
self.db.health -= _damage
```
Now we use the values that context passes to the buff kwargs to customize our logic.
```python
class ThornsBuff(BaseBuff):
...
triggers = ['taken_damage']
# This is the hook method on our thorns buff
def at_trigger(self, trigger, attacker=None, damage=0, **kwargs):
if not attacker:
return
attacker.db.health -= damage * 0.2
```
Apply the buff, take damage, and watch the thorns buff do its work!
### Viewing
There are two helper methods on the handler that allow you to get useful buff information back.
- `view`: Returns a dictionary of tuples in the format `{buffkey: (buff.name, buff.flavor)}`. Finds all buffs by default, but optionally accepts a dictionary of buffs to filter as well. Useful for basic buff readouts.
- `view_modifiers(stat)`: Returns a nested dictionary of information on modifiers that affect the specified stat. The first layer is the modifier type (`add/mult/div`) and the second layer is the value type (`total/strongest`). Does not return the buffs that cause these modifiers, just the modifiers themselves (akin to using `handler.check` but without actually modifying a value). Useful for stat sheets.
You can also create your own custom viewing methods through the various handler getters, which will always return the entire buff object.
## Creating New Buffs
Creating a new buff is very easy: extend `BaseBuff` into a new class, and fill in all the relevant buff details.
However, there are a lot of individual moving parts to a buff. Here's a step-through of the important stuff.
### Basics
Regardless of any other functionality, all buffs have the following class attributes:
- They have customizable `key`, `name`, and `flavor` strings.
- They have a `duration` (float), and automatically clean-up at the end. Use -1 for infinite duration, and 0 to clean-up immediately. (default: -1)
- They have a `tickrate` (float), and automatically tick if it is greater than 1 (default: 0)
- They can stack, if `maxstacks` (int) is not equal to 1. If it's 0, the buff stacks forever. (default: 1)
- They can be `unique` (bool), which determines if they have a unique namespace or not. (default: True)
- They can `refresh` (bool), which resets the duration when stacked or reapplied. (default: True)
- They can be `playtime` (bool) buffs, where duration only counts down during active play. (default: False)
Buffs also have a few useful properties:
- `owner`: The object this buff is attached to
- `ticknum`: How many ticks the buff has gone through
- `timeleft`: How much time is remaining on the buff
- `ticking`/`stacking`: If this buff ticks/stacks (checks `tickrate` and `maxstacks`)
#### Buff Cache (Advanced)
Buffs always store some useful mutable information about themselves in the cache (what is stored on the owning object's database attribute). A buff's cache corresponds to `{buffkey: buffcache}`, where `buffcache` is a dictionary containing __at least__ the information below:
- `ref` (class): The buff class path we use to construct the buff.
- `start` (float): The timestamp of when the buff was applied.
- `source` (Object): If specified; this allows you to track who or what applied the buff.
- `prevtick` (float): The timestamp of the previous tick.
- `duration` (float): The cached duration. This can vary from the class duration, depending on if the duration has been modified (paused, extended, shortened, etc).
- `tickrate` (float): The buff's tick rate. Cannot go below 0. Altering the tickrate on an applied buff will not cause it to start ticking if it wasn't ticking before. (`pause` and `unpause` to start/stop ticking on existing buffs)
- `stacks` (int): How many stacks they have.
- `paused` (bool): Paused buffs do not clean up, modify values, tick, or fire any hook methods.
Sometimes you will want to dynamically update a buff's cache at runtime, such as changing a tickrate in a hook method, or altering a buff's duration.
You can do so by using the interface `buff.cachekey`. As long as the attribute name matches a key in the cache dictionary, it will update the stored
cache with the new value.
If there is no matching key, it will do nothing. If you wish to add a new key to the cache, you must use the `buff.update_cache(dict)` method,
which will properly update the cache (including adding new keys) using the dictionary provided.
> **Example**: You want to increase a buff's duration by 30 seconds. You use `buff.duration += 30`. This new duration is now reflected on both the instance and the cache.
The buff cache can also store arbitrary information. To do so, pass a dictionary through the handler `add` method (`handler.add(BuffClass, to_cache=dict)`),
set the `cache` dictionary attribute on your buff class, or use the aforementioned `buff.update_cache(dict)` method.
> **Example**: You store `damage` as a value in the buff cache and use it for your poison buff. You want to increase it over time, so you use `buff.damage += 1` in the tick method.
### Modifiers
Mods are stored in the `mods` list attribute. Buffs which have one or more Mod objects in them can modify stats. You can use the handler method to check all
mods of a specific stat string and apply their modifications to the value; however, you are encouraged to use `check` in a getter/setter, for easy access.
Mod objects consist of only four values, assigned by the constructor in this order:
- `stat`: The stat you want to modify. When `check` is called, this string is used to find all the mods that are to be collected.
- `mod`: The modifier. Defaults are `add` (addition/subtraction), `mult` (multiply), and `div` (divide). Modifiers are calculated additively (see `_calculate_mods` for more)
- `value`: How much value the modifier gives regardless of stacks
- `perstack`: How much value the modifier grants per stack, **INCLUDING** the first. (default: 0)
The most basic way to add a Mod to a buff is to do so in the buff class definition, like this:
```python
class DamageBuff(BaseBuff):
mods = [Mod('damage', 'add', 10)]
```
No mods applied to the value are permanent in any way. All calculations are done at runtime, and the mod values are never stored
anywhere except on the buff in question. In other words: you don't need to track the origin of particular stat mods, and you will
never permanently change a stat modified by a buff. To remove the modification, simply remove the buff from the object.
> **Note**: You can add your own modifier types by overloading the `_calculate_mods` method, which contains the basic modifier application logic.
#### Generating Mods (Advanced)
An advanced way to do mods is to generate them when the buff is initialized. This lets you create mods on the fly that are reactive to the game state.
```python
class GeneratedStatBuff(BaseBuff):
...
def at_init(self, *args, **kwargs) -> None:
# Finds our "modgen" cache value, and generates a mod from it
modgen = list(self.cache.get("modgen"))
if modgen:
self.mods = [Mod(*modgen)]
```
### Triggers
Buffs which have one or more strings in the `triggers` attribute can be triggered by events.
When the handler's `trigger` method is called, it searches all buffs on the handler for any with a matchingtrigger,
then calls their `at_trigger` hooks. Buffs can have multiple triggers, and you can tell which trigger was used by
the `trigger` argument in the hook.
```python
class AmplifyBuff(BaseBuff):
triggers = ['damage', 'heal']
def at_trigger(self, trigger, **kwargs):
if trigger == 'damage': print('Damage trigger called!')
if trigger == 'heal': print('Heal trigger called!')
```
### Ticking
A buff which ticks isn't much different than one which triggers. You're still executing arbitrary hooks on
the buff class. To tick, the buff must have a `tickrate` of 1 or higher.
```python
class Poison(BaseBuff):
...
# this buff will tick 6 times between application and cleanup.
duration = 30
tickrate = 5
def at_tick(self, initial, **kwargs):
self.owner.take_damage(10)
```
> **Note**: The buff always ticks once when applied. For this **first tick only**, `initial` will be True in the `at_tick` hook method. `initial` will be False on subsequent ticks.
Ticks utilize a persistent delay, so they should be pickleable. As long as you are not adding new properties to your buff class, this shouldn't be a concern.
If you **are** adding new properties, try to ensure they do not end up with a circular code path to their object or handler, as this will cause pickling errors.
### Extras
Buffs have a grab-bag of extra functionality to let you add complexity to your designs.
#### Conditionals
You can restrict whether or not the buff will `check`, `trigger`, or `tick` through defining the `conditional` hook. As long
as it returns a "truthy" value, the buff will apply itself. This is useful for making buffs dependent on game state - for
example, if you want a buff that makes the player take more damage when they are on fire:
```python
class FireSick(BaseBuff):
...
def conditional(self, *args, **kwargs):
if self.owner.buffs.has(FireBuff):
return True
return False
```
Conditionals for `check`/`trigger` are checked when the buffs are gathered by the handler methods for the respective operations. `Tick`
conditionals are checked each tick.
#### Helper Methods
Buff instances have a number of helper methods.
- `remove`/`dispel`: Allows you to remove or dispel the buff. Calls `at_remove`/`at_dispel`, depending on optional arguments.
- `pause`/`unpause`: Pauses and unpauses the buff. Calls `at_pause`/`at_unpause`.
- `reset`: Resets the buff's start to the current time; same as "refreshing" it.
- `alter_cache`: Updates the buff's cache with the `{key:value}` pairs in the provided dictionary. Can overwrite default values, so be careful!
#### Playtime Duration
If your handler has `autopause` enabled, any buffs with truthy `playtime` value will automatically pause
and unpause when the object the handler is attached to is puppetted or unpuppetted. This even works with ticking buffs,
although if you have less than 1 second of tick duration remaining, it will round up to 1s.
> **Note**: If you want more control over this process, you can comment out the signal subscriptions on the handler and move the autopause logic
> to your object's `at_pre/post_puppet/unpuppet` hooks.
----
<small>This document page is generated from `evennia/contrib/rpg/buffs/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

File diff suppressed because it is too large Load diff

View file

@ -1,125 +0,0 @@
# Character Creator contrib
Commands for managing and initiating an in-game character-creation menu.
Contribution by InspectorCaracal, 2022
## Installation
In your game folder `commands/default_cmdsets.py`, import and add
`ContribCmdCharCreate` to your `AccountCmdSet`.
Example:
```python
from evennia.contrib.rpg.character_creator.character_creator import ContribCmdCharCreate
class AccountCmdSet(default_cmds.AccountCmdSet):
def at_cmdset_creation(self):
super().at_cmdset_creation()
self.add(ContribCmdCharCreate)
```
In your game folder `typeclasses/accounts.py`, import and inherit from `ContribChargenAccount`
on your Account class.
(Alternatively, you can copy the `at_look` method directly into your own class.)
### Example:
```python
from evennia.contrib.rpg.character_creator.character_creator import ContribChargenAccount
class Account(ContribChargenAccount):
# your Account class code
```
In your settings file `server/conf/settings.py`, add the following settings:
```python
AUTO_CREATE_CHARACTER_WITH_ACCOUNT = False
AUTO_PUPPET_ON_LOGIN = False
```
(If you want to allow players to create more than one character, you can
customize that with the setting `MAX_NR_CHARACTERS`.)
By default, the new `charcreate` command will reference the example menu
provided by the contrib, so you can test it out before building your own menu.
You can reference
[the example menu here](github:develop/evennia/contrib/rpg/character_creator/example_menu.py) for
ideas on how to build your own.
Once you have your own menu, just add it to your settings to use it. e.g. if your menu is in
`mygame/word/chargen_menu.py`, you'd add the following to your settings file:
```python
CHARGEN_MENU = "world.chargen_menu"
```
## Usage
### The EvMenu
In order to use the contrib, you will need to create your own chargen EvMenu.
The included `example_menu.py` gives a number of useful menu node techniques
with basic attribute examples for you to reference. It can be run as-is as a
tutorial for yourself/your devs, or used as base for your own menu.
The example menu includes code, tips, and instructions for the following types
of decision nodes:
#### Informational Pages
A small set of nodes that let you page through information on different choices before committing to one.
#### Option Categories
A pair of nodes which let you divide an arbitrary number of options into separate categories.
The base node has a list of categories as the options, and the child node displays the actual character choices.
#### Multiple Choice
Allows players to select and deselect options from the list in order to choose more than one.
#### Starting Objects
Allows players to choose from a selection of starting objects, which are then created on chargen completion.
#### Choosing a Name
The contrib assumes the player will choose their name during character creation,
so the necessary code for doing so is of course included!
### `charcreate` command
The contrib overrides the character creation command - `charcreate` - to use a
character creator menu, as well as supporting exiting/resuming the process. In
addition, unlike the core command, it's designed for the character name to be
chosen later on via the menu, so it won't parse any arguments passed to it.
### Changes to `Account.at_look`
The contrib version works mostly the same as core evennia, but adds an
additional check to recognize an in-progress character. If you've modified your
own `at_look` hook, it's an easy addition to make: just add this section to the
playable character list loop.
```python
for char in characters:
# contrib code starts here
if char.db.chargen_step:
# currently in-progress character; don't display placeholder names
result.append("\n - |Yin progress|n (|wcharcreate|n to continue)")
continue
# the rest of your code continues here
```
----
<small>This document page is generated from `evennia/contrib/rpg/character_creator/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,95 +0,0 @@
# Clothing
Contribution by Tim Ashley Jenkins, 2017
Provides a typeclass and commands for wearable clothing. These
look of these clothes are appended to the 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
Would result in this added description:
Tim is wearing one nice hat, a thin and delicate necklace,
a very pretty dress and a pair of regular ol' shoes.
## 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.game_systems.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.
----
<small>This document page is generated from `evennia/contrib/game_systems/clothing/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,64 +0,0 @@
# Additional Color markups
Contrib by Griatch, 2017
Additional color markup styles for Evennia (extending or replacing the default
`|r`, `|234`). Adds support for MUSH-style (`%cr`, `%c123`) and/or legacy-Evennia
(`{r`, `{123`).
## 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_XTERM256_BRIGHT_BG_EXTRA_MAP = color_markups.CURLY_COLOR_ANSI_XTERM256_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.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
COLOR_XTERM256_EXTRA_GFG = color_markups.MUX_COLOR_XTERM256_EXTRA_GFG
COLOR_XTERM256_EXTRA_GBG = color_markups.MUX_COLOR_XTERM256_EXTRA_GBG
COLOR_ANSI_XTERM256_BRIGHT_BG_EXTRA_MAP = color_markups.MUX_COLOR_ANSI_XTERM256_BRIGHT_BG_EXTRA_MAP
```
----
<small>This document page is generated from `evennia/contrib/base_systems/color_markups/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,198 +0,0 @@
# Components
_Contrib by ChrisLR 2021_
# The Components Contrib
This contrib introduces Components and Composition to Evennia.
Each 'Component' class represents a feature that will be 'enabled' on a typeclass instance.
You can register these components on an entire typeclass or a single object at runtime.
It supports both persisted attributes and in-memory attributes by using Evennia's AttributeHandler.
# Pros
- You can reuse a feature across multiple typeclasses without inheritance
- You can cleanly organize each feature into a self-contained class.
- You can check if your object supports a feature without checking its instance.
# Cons
- It introduces additional complexity.
- A host typeclass instance is required.
# How to install
To enable component support for a typeclass,
import and inherit the ComponentHolderMixin, similar to this
```python
from evennia.contrib.base_systems.components import ComponentHolderMixin
class Character(ComponentHolderMixin, DefaultCharacter):
# ...
```
Components need to inherit the Component class directly and require a name.
```python
from evennia.contrib.components import Component
class Health(Component):
name = "health"
```
Components may define DBFields or NDBFields at the class level.
DBField will store its values in the host's DB with a prefixed key.
NDBField will store its values in the host's NDB and will not persist.
The key used will be 'component_name::field_name'.
They use AttributeProperty under the hood.
Example:
```python
from evennia.contrib.base_systems.components import Component, DBField
class Health(Component):
health = DBField(default=1)
```
Note that default is optional and will default to None.
Adding a component to a host will also a similarly named tag with 'components' as category.
A Component named health will appear as key="health, category="components".
This allows you to retrieve objects with specific components by searching with the tag.
It is also possible to add Component Tags the same way, using TagField.
TagField accepts a default value and can be used to store a single or multiple tags.
Default values are automatically added when the component is added.
Component Tags are cleared from the host if the component is removed.
Example:
```python
from evennia.contrib.base_systems.components import Component, TagField
class Health(Component):
resistances = TagField()
vulnerability = TagField(default="fire", enforce_single=True)
```
The 'resistances' field in this example can be set to multiple times and it will keep the added tags.
The 'vulnerability' field in this example will override the previous tag with the new one.
Each typeclass using the ComponentHolderMixin can declare its components
in the class via the ComponentProperty.
These are components that will always be present in a typeclass.
You can also pass kwargs to override the default values
Example
```python
from evennia.contrib.base_systems.components import ComponentHolderMixin
class Character(ComponentHolderMixin, DefaultCharacter):
health = ComponentProperty("health", hp=10, max_hp=50)
```
You can then use character.components.health to access it.
The shorter form character.cmp.health also exists.
character.health would also be accessible but only for typeclasses that have
this component defined on the class.
Alternatively you can add those components at runtime.
You will have to access those via the component handler.
Example
```python
character = self
vampirism = components.Vampirism.create(character)
character.components.add(vampirism)
...
vampirism_from_elsewhere = character.components.get("vampirism")
```
Keep in mind that all components must be imported to be visible in the listing.
As such, I recommend regrouping them in a package.
You can then import all your components in that package's __init__
Because of how Evennia import typeclasses and the behavior of python imports
I recommend placing the components package inside the typeclass package.
In other words, create a folder named components inside your typeclass folder.
Then, inside the 'typeclasses/__init__.py' file add the import to the folder, like
```python
from typeclasses import components
```
This ensures that the components package will be imported when the typeclasses are imported.
You will also need to import each components inside the package's own 'typeclasses/components/__init__.py' file.
You only need to import each module/file from there but importing the right class is a good practice.
```python
from typeclasses.components.health import Health
```
```python
from typeclasses.components import health
```
Both of the above examples will work.
# Full Example
```python
from evennia.contrib.base_systems import components
# This is the Component class
class Health(components.Component):
name = "health"
# Stores the current and max values as Attributes on the host, defaulting to 100
current = components.DBField(default=100)
max = components.DBField(default=100)
def damage(self, value):
if self.current <= 0:
return
self.current -= value
if self.current > 0:
return
self.current = 0
self.on_death()
def heal(self, value):
hp = self.current
hp += value
if hp >= self.max_hp:
hp = self.max_hp
self.current = hp
@property
def is_dead(self):
return self.current <= 0
def on_death(self):
# Behavior is defined on the typeclass
self.host.on_death()
# This is how the Character inherits the mixin and registers the component 'health'
class Character(ComponentHolderMixin, DefaultCharacter):
health = ComponentProperty("health")
# This is an example of a command that checks for the component
class Attack(Command):
key = "attack"
aliases = ('melee', 'hit')
def at_pre_cmd(self):
caller = self.caller
targets = self.caller.search(args, quiet=True)
valid_target = None
for target in targets:
# Attempt to retrieve the component, None is obtained if it does not exist.
if target.components.health:
valid_target = target
if not valid_target:
caller.msg("You can't attack that!")
return True
```
----
<small>This document page is generated from `evennia/contrib/base_systems/components/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,63 +0,0 @@
# Cooldowns
Contribution by owllex, 2021
Cooldowns are used to model rate-limited actions, like how often a
character can perform a given action; until a certain time has passed their
command can not be used again. This contrib provides a simple cooldown
handler that can be attached to any typeclass. A cooldown is a lightweight persistent
asynchronous timer that you can query to see if a certain time has yet passed.
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.contrib.game_systems.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!")
```
----
<small>This document page is generated from `evennia/contrib/game_systems/cooldowns/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,306 +0,0 @@
# Crafting system
Contribution by Griatch 2020
This implements a full crafting system. The principle is that of a 'recipe',
where you combine items (tagged as ingredients) create something new. The recipe can also
require certain (non-consumed) tools. An example would be to use the 'bread recipe' to
combine 'flour', 'water' and 'yeast' with an 'oven' to bake a 'loaf of bread'.
The recipe process can be understood like this:
ingredient(s) + tool(s) + recipe -> object(s)
Here, 'ingredients' are consumed by the crafting process, whereas 'tools' are
necessary for the process but will not be destroyed by it.
The included `craft` command works like this:
craft <recipe> [from <ingredient>,...] [using <tool>, ...]
## Examples
Using the `craft` command:
craft toy car from plank, wooden wheels, nails using saw, hammer
A recipe does not have to use tools or even multiple ingredients:
snow + snowball_recipe -> snowball
Conversely one could also imagine using tools without consumables, like
spell_book + wand + fireball_recipe -> fireball
The system is generic enough to be used also for adventure-like puzzles (but
one would need to change the command and determine the recipe on based on what
is being combined instead):
stick + string + hook -> makeshift_fishing_rod
makeshift_fishing_rod + storm_drain -> key
See the [sword example](evennia.contrib.game_systems.crafting.example_recipes) for an example
of how to design a recipe tree for crafting a sword from base elements.
## Intallation and Usage
Import the `CmdCraft` command from evennia/contrib/crafting/crafting.py and
add it to your Character cmdset. Reload and the `craft` command will be
available to you:
craft <recipe> [from <ingredient>,...] [using <tool>, ...]
In code, you can craft using the
`evennia.contrib.game_systems.crafting.craft` function:
```python
from evennia.contrib.game_systems.crafting import craft
result = craft(caller, "recipename", *inputs)
```
Here, `caller` is the one doing the crafting and `*inputs` is any combination of
consumables and/or tool Objects. The system will identify which is which by the
[Tags](../Components/Tags.md) on them (see below) The `result` is always a list.
To use crafting you need recipes. Add a new variable to
`mygame/server/conf/settings.py`:
CRAFT_RECIPE_MODULES = ['world.recipes']
All top-level classes in these modules (whose name does not start with `_`) will
be parsed by Evennia as recipes to make available to the crafting system. Using
the above example, create `mygame/world/recipes.py` and add your recipies in
there:
A quick example (read on for more details):
```python
from evennia.contrib.game_systems.crafting import CraftingRecipe, CraftingValidationError
class RecipeBread(CraftingRecipe):
"""
Bread is good for making sandwitches!
"""
name = "bread" # used to identify this recipe in 'craft' command
tool_tags = ["bowl", "oven"]
consumable_tags = ["flour", "salt", "yeast", "water"]
output_prototypes = [
{"key": "Loaf of Bread",
"aliases": ["bread"],
"desc": "A nice load of bread.",
"typeclass": "typeclasses.objects.Food", # assuming this exists
"tags": [("bread", "crafting_material")] # this makes it usable in other recipes ...
}
]
def pre_craft(self, **kwargs):
# validates inputs etc. Raise `CraftingValidationError` if fails
def do_craft(self, **kwargs):
# performs the craft - report errors directly to user and return None (if
# failed) and the created object(s) if successful.
def post_craft(self, result, **kwargs):
# any post-crafting effects. Always called, even if do_craft failed (the
# result would be None then)
```
## Adding new recipes
A *recipe* is a class inheriting from
`evennia.contrib.crafting.crafting.CraftingRecipe`. This class implements the
most common form of crafting - that using in-game objects. Each recipe is a
separate class which gets initialized with the consumables/tools you provide.
For the `craft` command to find your custom recipes, you need to tell Evennia
where they are. Add a new line to your `mygame/server/conf/settings.py` file,
with a list to any new modules with recipe classes.
```python
CRAFT_RECIPE_MODULES = ["world.myrecipes"]
```
(You need to reload after adding this). All global-level classes in these
modules (whose names don't start with underscore) are considered by the system
as viable recipes.
Here we assume you created `mygame/world/myrecipes.py` to match the above
example setting:
```python
# in mygame/world/myrecipes.py
from evennia.contrib.crafting.crafting import CraftingRecipe
class WoodenPuppetRecipe(CraftingRecipe):
"""A puppet""""
name = "wooden puppet" # name to refer to this recipe as
tool_tags = ["knife"]
consumable_tags = ["wood"]
output_prototypes = [
{"key": "A carved wooden doll",
"typeclass": "typeclasses.objects.decorations.Toys",
"desc": "A small carved doll"}
]
```
This specifies which tags to look for in the inputs. It defines a
[Prototype](../Components/Prototypes.md) for the recipe to use to spawn the
result on the fly (a recipe could spawn more than one result if needed).
Instead of specifying the full prototype-dict, you could also just provide a
list of `prototype_key`s to existing prototypes you have.
After reloading the server, this recipe would now be available to use. To try it
we should create materials and tools to insert into the recipe.
The recipe analyzes inputs, looking for [Tags](../Components/Tags.md) with
specific tag-categories. The tag-category used can be set per-recipe using the
(`.consumable_tag_category` and `.tool_tag_category` respectively). The defaults
are `crafting_material` and `crafting_tool`. For
the puppet we need one object with the `wood` tag and another with the `knife`
tag:
```python
from evennia import create_object
knife = create_object(key="Hobby knife", tags=[("knife", "crafting_tool")])
wood = create_object(key="Piece of wood", tags[("wood", "crafting_material")])
```
Note that the objects can have any name, all that matters is the
tag/tag-category. This means if a "bayonet" also had the "knife" crafting tag,
it could also be used to carve a puppet. This is also potentially interesting
for use in puzzles and to allow users to experiment and find alternatives to
know ingredients.
By the way, there is also a simple shortcut for doing this:
```
tools, consumables = WoodenPuppetRecipe.seed()
```
The `seed` class-method will create simple dummy objects that fulfills the
recipe's requirements. This is great for testing.
Assuming these objects were put in our inventory, we could now craft using the
in-game command:
```bash
> craft wooden puppet from wood using hobby knife
```
In code we would do
```python
from evennia.contrib.crafting.crafting import craft
puppet = craft(crafter, "wooden puppet", knife, wood)
```
In the call to `craft`, the order of `knife` and `wood` doesn't matter - the
recipe will sort out which is which based on their tags.
## Deeper customization of recipes
For customizing recipes further, it helps to understand how to use the
recipe-class directly:
```python
class MyRecipe(CraftingRecipe):
# ...
tools, consumables = MyRecipe.seed()
recipe = MyRecipe(crafter, *(tools + consumables))
result = recipe.craft()
```
This is useful for testing and allows you to use the class directly without
adding it to a module in `settings.CRAFTING_RECIPE_MODULES`.
Even without modifying more than the class properties, there are a lot of
options to set on the `CraftingRecipe` class. Easiest is to refer to the
[CraftingRecipe api
documentation](evennia.contrib.game_systems.crafting.crafting.CraftingRecipe). For example,
you can customize the validation-error messages, decide if the ingredients have
to be exactly right, if a failure still consumes the ingredients or not, and
much more.
For even more control you can override hooks in your own class:
- `pre_craft` - this should handle input validation and store its data in `.validated_consumables` and
`validated_tools` respectively. On error, this reports the error to the crafter and raises the
`CraftingValidationError`.
- `craft` - this will only be called if `pre_craft` finished without an exception. This should
return the result of the crafting, by spawnging the prototypes. Or the empty list if crafting
fails for some reason. This is the place to add skill-checks or random chance if you need it
for your game.
- `post_craft` - this receives the result from `craft` and handles error messages and also deletes
any consumables as needed. It may also modify the result before returning it.
- `msg` - this is a wrapper for `self.crafter.msg` and should be used to send messages to the
crafter. Centralizing this means you can also easily modify the sending style in one place later.
The class constructor (and the `craft` access function) takes optional `**kwargs`. These are passed
into each crafting hook. These are unused by default but could be used to customize things per-call.
### Skilled crafters
What the crafting system does not have out of the box is a 'skill' system - the
notion of being able to fail the craft if you are not skilled enough. Just how
skills work is game-dependent, so to add this you need to make your own recipe
parent class and have your recipes inherit from this.
```python
from random import randint
from evennia.contrib.crafting.crafting import CraftingRecipe
class SkillRecipe(CraftingRecipe):
"""A recipe that considers skill"""
difficulty = 20
def craft(self, **kwargs):
"""The input is ok. Determine if crafting succeeds"""
# this is set at initialization
crafter = self.crafte
# let's assume the skill is stored directly on the crafter
# - the skill is 0..100.
crafting_skill = crafter.db.skill_crafting
# roll for success:
if randint(1, 100) <= (crafting_skill - self.difficulty):
# all is good, craft away
return super().craft()
else:
self.msg("You are not good enough to craft this. Better luck next time!")
return []
```
In this example we introduce a `.difficulty` for the recipe and makes a 'dice roll' to see
if we succed. We would of course make this a lot more immersive and detailed in a full game. In
principle you could customize each recipe just the way you want it, but you could also inherit from
a central parent like this to cut down on work.
The [sword recipe example module](evennia.contrib.game_systems.crafting.example_recipes) also shows an example
of a random skill-check being implemented in a parent and then inherited for multiple use.
## Even more customization
If you want to build something even more custom (maybe using different input types of validation logic)
you could also look at the `CraftingRecipe` parent class `CraftingRecipeBase`.
It implements just the minimum needed to be a recipe and for big changes you may be better off starting
from this rather than the more opinionated `CraftingRecipe`.
----
<small>This document page is generated from `evennia/contrib/game_systems/crafting/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,54 +0,0 @@
# Custom gameime
Contrib by vlgeoff, 2017 - based on Griatch's core original
This reimplements the `evennia.utils.gametime` module but with a _custom_
calendar (unusual number of days per week/month/year etc) for your game world.
Like the original, it allows for scheduling events to happen at given
in-game times, but now 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.
----
<small>This document page is generated from `evennia/contrib/base_systems/custom_gametime/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,71 +0,0 @@
# Dice roller
Contribution by Griatch, 2012
A dice roller for any number and side of dice. Adds in-game dice rolling
(`roll 2d10 + 1`) as well as conditionals (roll under/over/equal to a target)
and functions for rolling dice in code. Command also supports hidden or secret
rolls for use by a human game master.
## 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
```
----
<small>This document page is generated from `evennia/contrib/rpg/dice/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,37 +0,0 @@
# Email-based login system
Contrib by Griatch, 2012
This is a variant of the login system that asks for an email-address
instead of a username to login. Note that it does not verify the email,
it just uses it as the identifier rather than a username.
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).
----
<small>This document page is generated from `evennia/contrib/base_systems/email_login/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,43 +0,0 @@
# EvAdventure
Contrib by Griatch 2022
```{warning}
NOTE - this tutorial is WIP and NOT complete! It was put on hold to focus on
releasing Evennia 1.0. You will still learn things from it, but don't expect
perfection.
```
A complete example MUD using Evennia. This is the final result of what is
implemented if you follow the Getting-Started tutorial. It's recommended
that you follow the tutorial step by step and write your own code. But if
you prefer you can also pick apart or use this as a starting point for your
own game.
## Features
- Uses a MUD-version of the [Knave](https://rpggeek.com/rpg/50827/knave) old-school
fantasy ruleset by Ben Milton (classless and overall compatible with early
edition D&D), released under the Creative Commons Attribution (all uses,
including commercial are allowed
as long as attribution is given).
- Character creation using an editable character sheet
- Weapons, effects, healing and resting
- Two alternative combat systems (turn-based and twitch based)
- Magic (three spells)
- NPC/mobs with simple AI.
- Simple Quest system.
- Small game world.
- Coded using best Evennia practices, with unit tests.
## Installation
TODO
----
<small>This document page is generated from `evennia/contrib/tutorials/evadventure/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,130 +0,0 @@
# EvscapeRoom
Contribution by Griatch, 2019
A full engine for creating multiplayer escape-rooms in Evennia. Allows players to
spawn and join puzzle rooms that track their state independently. Any number of players
can join to solve a room together. This is the engine created for 'EvscapeRoom', which won
the MUD Coders Guild "One Room" Game Jam in April-May, 2019. The contrib has no game
content but 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`
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!
## Installation
The Evscaperoom is installed by adding the `evscaperoom` command to your
character cmdset. When you run that command in-game you're ready to play!
In `mygame/commands/default_cmdsets.py`:
```python
from evennia.contrib.full_systems.evscaperoom.commands import CmdEvscapeRoomStart
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.
## Making your own evscaperoom
To do this, you need to make your own states. First make sure you can play the
simple example room installed above.
Copy `evennia/contrib/full_systems/evscaperoom/states` to somewhere in your game folder (let's
assume you put it under `mygame/world/`).
Next you need to re-point Evennia to look for states in this new location. Add
the following to your `mygame/server/conf/settings.py` file:
```python
EVSCAPEROOM_STATE_PACKAGE = "world.states"
```
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.
- `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/full_systems/evscaperoom/commands.py`.
## Playing the game
You should start by `look`ing around and at objects.
The `examine <object>` command allows you to 'focus' on an object. When you do
you'll learn actions you could try for the object you are focusing on, such as
turning it around, read text on it or use it with some other object. Note that
more than one player can focus on the same object, so you won't block anyone
when you focus. Focusing on another object or use `examine` again will remove
focus.
There is also a full hint system.
## Technical
When connecting to the game, the user has the option to join an existing room
(which may already be in some state of ongoing progress), or may create a fresh
room for them to start solving on their own (but anyone may still join them later).
The room will go through a series of 'states' as the players progress through
its challenges. These states are describes as modules in .states/ and the
room will load and execute the State-object within each module to set up
and transition between states as the players progress. This allows for isolating
the states from each other and will hopefully make it easier to track
the logic and (in principle) inject new puzzles later.
Once no players remain in the room, the room and its state will be wiped.
## Design Philosophy
Some basic premises inspired the design of this.
- You should be able to resolve the room alone. So no puzzles should require the
collaboration of multiple players. This is simply because there is no telling
if others will actually be online at a given time (or stay online throughout).
- You should never be held up by the actions/inactions of other players. This
is why you cannot pick up anything (no inventory system) but only
focus/operate on items. This avoids the annoying case of a player picking up
a critical piece of a puzzle and then logging off.
- A room's state changes for everyone at once. My first idea was to have a given
room have different states depending on who looked (so a chest could be open
and closed to two different players at the same time). But not only does this
add a lot of extra complexity, it also defeats the purpose of having multiple
players. This way people can help each other and collaborate like in a 'real'
escape room. For people that want to do it all themselves I instead made it
easy to start "fresh" rooms for them to take on.
All other design decisions flowed from these.
----
<small>This document page is generated from `evennia/contrib/full_systems/evscaperoom/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,83 +0,0 @@
# Extended Room
Contribution - Griatch 2012, vincent-lg 2019
This extends the normal `Room` typeclass to allow its description to change
with time-of-day and/or season. It also adds 'details' for the player to look at
in the room (without having to create a new in-game object for each). The room is
supported by new `look` and `desc` commands.
## 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 new 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.
----
<small>This document page is generated from `evennia/contrib/grid/extended_room/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,165 +0,0 @@
# Easy fillable form
Contribution by Tim Ashley Jenkins, 2018
This module contains a function that generates an `EvMenu` for you - this
menu presents the player with a form of fields that can be filled
out in any order (e.g. for character generation or building). Each field's value can
be verified, with the function allowing easy checks for text and integer input,
minimum and maximum values / character lengths, or can even be verified by a custom
function. Once the form is submitted, the form's data is submitted as a dictionary
to any callable of your choice.
## Usage
The function that initializes the fillable form menu is fairly simple, and
includes the caller, the template for the form, and the callback(caller, result)
to which the form data will be sent to upon submission.
init_fill_field(formtemplate, caller, formcallback)
Form templates are defined as a list of dictionaries - each dictionary
represents a field in the form, and contains the data for the field's name and
behavior. For example, this basic form template will allow a player to fill out
a brief character profile:
PROFILE_TEMPLATE = [
{"fieldname":"Name", "fieldtype":"text"},
{"fieldname":"Age", "fieldtype":"number"},
{"fieldname":"History", "fieldtype":"text"},
]
This will present the player with an EvMenu showing this basic form:
```
Name:
Age:
History:
```
While in this menu, the player can assign a new value to any field with the
syntax <field> = <new value>, like so:
```
> name = Ashley
Field 'Name' set to: Ashley
```
Typing 'look' by itself will show the form and its current values.
```
> look
Name: Ashley
Age:
History:
```
Number fields require an integer input, and will reject any text that can't
be converted into an integer.
```
> age = youthful
Field 'Age' requires a number.
> age = 31
Field 'Age' set to: 31
```
Form data is presented as an EvTable, so text of any length will wrap cleanly.
```
> history = EVERY MORNING I WAKE UP AND OPEN PALM SLAM[...]
Field 'History' set to: EVERY MORNING I WAKE UP AND[...]
> look
Name: Ashley
Age: 31
History: EVERY MORNING I WAKE UP AND OPEN PALM SLAM A VHS INTO THE SLOT.
IT'S CHRONICLES OF RIDDICK AND RIGHT THEN AND THERE I START DOING
THE MOVES ALONGSIDE WITH THE MAIN CHARACTER, RIDDICK. I DO EVERY
MOVE AND I DO EVERY MOVE HARD.
```
When the player types 'submit' (or your specified submit command), the menu
quits and the form's data is passed to your specified function as a dictionary,
like so:
formdata = {"Name":"Ashley", "Age":31, "History":"EVERY MORNING I[...]"}
You can do whatever you like with this data in your function - forms can be used
to set data on a character, to help builders create objects, or for players to
craft items or perform other complicated actions with many variables involved.
The data that your form will accept can also be specified in your form template -
let's say, for example, that you won't accept ages under 18 or over 100. You can
do this by specifying "min" and "max" values in your field's dictionary:
```
PROFILE_TEMPLATE = [
{"fieldname":"Name", "fieldtype":"text"},
{"fieldname":"Age", "fieldtype":"number", "min":18, "max":100},
{"fieldname":"History", "fieldtype":"text"}
]
```
Now if the player tries to enter a value out of range, the form will not acept the
given value.
```
> age = 10
Field 'Age' reqiures a minimum value of 18.
> age = 900
Field 'Age' has a maximum value of 100.
```
Setting 'min' and 'max' for a text field will instead act as a minimum or
maximum character length for the player's input.
There are lots of ways to present the form to the player - fields can have default
values or show a custom message in place of a blank value, and player input can be
verified by a custom function, allowing for a great deal of flexibility. There
is also an option for 'bool' fields, which accept only a True / False input and
can be customized to represent the choice to the player however you like (E.G.
Yes/No, On/Off, Enabled/Disabled, etc.)
This module contains a simple example form that demonstrates all of the included
functionality - a command that allows a player to compose a message to another
online character and have it send after a custom delay. You can test it by
importing this module in your game's `default_cmdsets.py` module and adding
CmdTestMenu to your default character's command set.
## FIELD TEMPLATE KEYS:
### Required:
```
fieldname (str): Name of the field, as presented to the player.
fieldtype (str): Type of value required: 'text', 'number', or 'bool'.
```
### Optional:
- max (int): Maximum character length (if text) or value (if number).
- min (int): Minimum charater length (if text) or value (if number).
- truestr (str): String for a 'True' value in a bool field.
(E.G. 'On', 'Enabled', 'Yes')
- falsestr (str): String for a 'False' value in a bool field.
(E.G. 'Off', 'Disabled', 'No')
- default (str): Initial value (blank if not given).
- blankmsg (str): Message to show in place of value when field is blank.
- cantclear (bool): Field can't be cleared if True.
- required (bool): If True, form cannot be submitted while field is blank.
- verifyfunc (callable): Name of a callable used to verify input - takes
(caller, value) as arguments. If the function returns True,
the player's input is considered valid - if it returns False,
the input is rejected. Any other value returned will act as
the field's new value, replacing the player's input. This
allows for values that aren't strings or integers (such as
object dbrefs). For boolean fields, return '0' or '1' to set
the field to False or True.
----
<small>This document page is generated from `evennia/contrib/utils/fieldfill/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,84 +0,0 @@
# Gendersub
Contribution by 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`:
```python
# mygame/commands/default_cmdsets.py
# ...
from evennia.contrib.game_systems.gendersub import SetGender # <---
# ...
class CharacterCmdSet(default_cmds.CharacterCmdSet):
# ...
def at_cmdset_creation(self):
# ...
self.add(SetGender()) # <---
```
Make your `Character` inherit from `GenderCharacter`.
```python
# mygame/typeclasses/characters.py
# ...
from evennia.contrib.game_systems.gendersub import GenderCharacter # <---
class Character(GenderCharacter): # <---
# ...
```
Reload the server (`evennia reload` or `reload` from inside the game).
## 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.
----
<small>This document page is generated from `evennia/contrib/game_systems/gendersub/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,73 +0,0 @@
# In-game Git Integration
Contribution by helpme (2022)
A module to integrate a stripped-down version of git within the game, allowing developers to view their git status, change branches, and pull updated code of both their local mygame repo and Evennia core. After a successful pull or checkout, the git command will reload the game: Manual restarts may be required to to apply certain changes that would impact persistent scripts etc.
Once the contrib is set up, integrating remote changes is as simple as entering the following into your game:
```
git pull
```
The repositories you want to work with, be it only your local mygame repo, only Evennia core, or both, must be git directories for the command to function. If you are only interested in using this to get upstream Evennia changes, only the Evennia repository needs to be a git repository. [Get started with version control here.](https://www.evennia.com/docs/1.0-dev/Coding/Version-Control.html)
## Dependencies
This package requires the dependency "gitpython", a python library used to interact with git repositories. To install, it's easiest to install Evennia's 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`
## Installation
This utility adds a simple assortment of 'git' commands. Import the module into your commands and add it to your command set to make it available.
Specifically, in `mygame/commands/default_cmdsets.py`:
```python
...
from evennia.contrib.utils.git_integration import GitCmdSet # <---
class CharacterCmdset(default_cmds.Character_CmdSet):
...
def at_cmdset_creation(self):
...
self.add(GitCmdSet) # <---
```
Then `reload` to make the git command available.
## Usage
This utility will only work if the directory you wish to work with is a git directory. If they are not, you will be prompted to initiate your directory as a git repository using the following commands in your terminal:
```
git init
git remote add origin 'link to your repository'
```
By default, the git commands are only available to those with Developer permissions and higher. You can change this by overriding the command and setting its locks from "cmd:pperm(Developer)" to the lock of your choice.
The supported commands are:
* git status: An overview of your git repository, which files have been changed locally, and the commit you're on.
* git branch: What branches are available for you to check out.
* git checkout 'branch': Checkout a branch.
* git pull: Pull the latest code from your current branch.
* All of these commands are also available with 'evennia', to serve the same functionality related to your Evennia directory. So:
* git evennia status
* git evennia branch
* git evennia checkout 'branch'
* git evennia pull: Pull the latest Evennia code.
## Settings Used
The utility uses the existing GAME_DIR and EVENNIA_DIR settings from settings.py. You should not need to alter these if you have a standard directory setup, they ought to exist without any setup required from you.
----
<small>This document page is generated from `evennia/contrib/utils/git_integration/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,42 +0,0 @@
# Health Bar
Contribution by Tim Ashley Jenkins, 2017
The function provided in this module lets you easily display visual
bars or meters as a colorful bar instead of just a number. A "health bar"
is merely the most obvious use for this, but the bar is 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.
----
<small>This document page is generated from `evennia/contrib/rpg/health_bar/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,57 +0,0 @@
# Basic Map
Contribution - helpme 2022
This adds an ascii `map` to a given room which can be viewed with the `map` command.
You can easily alter it to add special characters, room colors etc. The map shown is
dynamically generated on use, and supports all compass directions and up/down. Other
directions are ignored.
If you don't expect the map to be updated frequently, you could choose to save the
calculated map as a .ndb value on the room and render that instead of running mapping
calculations anew each time.
## Installation:
Adding the `MapDisplayCmdSet` to the default character cmdset will add the `map` command.
Specifically, in `mygame/commands/default_cmdsets.py`:
```python
...
from evennia.contrib.grid.ingame_map_display import MapDisplayCmdSet # <---
class CharacterCmdset(default_cmds.Character_CmdSet):
...
def at_cmdset_creation(self):
...
self.add(MapDisplayCmdSet) # <---
```
Then `reload` to make the new commands available.
## Settings:
In order to change your default map size, you can add to `mygame/server/settings.py`:
```python
BASIC_MAP_SIZE = 5 # This changes the default map width/height.
```
## Features:
### ASCII map (and evennia supports UTF-8 characters and even emojis)
This produces an ASCII map for players of configurable size.
### New command
- `CmdMap` - view the map
----
<small>This document page is generated from `evennia/contrib/grid/ingame_map_display/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,245 +0,0 @@
# Dialogues in events
This tutorial will walk you through the steps to create several dialogues with
characters, using the Ingame-Python system. This tutorial assumes the in-game
Python system is installed in your game. If it isn't, you can follow the
installation steps given in [The main In-game Python
docs](./Contrib-Ingame-Python.md) and come back on this tutorial once the
system is installed. **You do not need to read** the entire documentation, it's
a good reference, but not the easiest way to learn about it. Hence these
tutorials.
The in-game Python system allows to run code on individual objects in some
situations. You don't have to modify the source code to add these features,
past the installation. The entire system makes it easy to add specific features
to some objects, but not all. This is why it can be very useful to create a
dialogue system taking advantage of the in-game Python system.
> What will we try to do?
In this tutorial, we are going to create a basic dialogue to have several characters automatically
respond to specific messages said by others.
## A first example with a first character
Let's create a character to begin with.
@charcreate a merchant
This will create a merchant in the room where you currently are. It doesn't have anything, like a
description, you can decorate it a bit if you like.
As said above, the in-game Python system consists in linking objects with arbitrary code. This code
will be executed in some circumstances. Here, the circumstance is "when someone says something in
the same room", and might be more specific like "when someone says hello". We'll decide what code
to run (we'll actually type the code in-game). Using the vocabulary of the in-game Python system,
we'll create a callback: a callback is just a set of lines of code that will run under some
conditions.
You can have an overview of every "conditions" in which callbacks can be created using the `@call`
command (short for `@callback`). You need to give it an object as argument. Here for instance, we
could do:
@call a merchant
You should see a table with three columns, showing the list of events existing on our newly-created
merchant. There are quite a lot of them, as it is, althougn no line of code has been set yet. For
our system, you might be more interested by the line describing the `say` event:
| say | 0 (0) | After another character has said something in |
| | | the character's room. |
We'll create a callback on the `say` event, called when we say "hello" in the merchant's room:
@call/add a merchant = say hello
Before seeing what this command displays, let's see the command syntax itself:
- `@call` is the command name, `/add` is a switch. You can read the help of the command to get the
help of available switches and a brief overview of syntax.
- We then enter the object's name, here "a merchant". You can enter the ID too ("#3" in my case),
which is useful to edit the object when you're not in the same room. You can even enter part of the
name, as usual.
- An equal sign, a simple separator.
- The event's name. Here, it's "say". The available events are displayed when you use `@call`
without switch.
- After a space, we enter the conditions in which this callback should be called. Here, the
conditions represent what the other character should say. We enter "hello". Meaning that if
someone says something containing "hello" in the room, the callback we are now creating will be
called.
When you enter this command, you should see something like this:
```
After another character has said something in the character's room.
This event is called right after another character has said
something in the same location. The action cannot be prevented
at this moment. Instead, this event is ideal to create keywords
that would trigger a character (like a NPC) in doing something
if a specific phrase is spoken in the same location.
To use this event, you have to specify a list of keywords as
parameters that should be present, as separate words, in the
spoken phrase. For instance, you can set a callback that would
fire if the phrase spoken by the character contains "menu" or
"dinner" or "lunch":
@call/add ... = say menu, dinner, lunch
Then if one of the words is present in what the character says,
this callback will fire.
Variables you can use in this event:
speaker: the character speaking in this room.
character: the character connected to this event.
message: the text having been spoken by the character.
```
That's some list of information. What's most important to us now is:
- The "say" event is called whenever someone else speaks in the room.
- We can set callbacks to fire when specific keywords are present in the phrase by putting them as
additional parameters. Here we have set this parameter to "hello". We can have several keywords
separated by a comma (we'll see this in more details later).
- We have three default variables we can use in this callback: `speaker` which contains the
character who speaks, `character` which contains the character who's modified by the in-game Python
system (here, or merchant), and `message` which contains the spoken phrase.
This concept of variables is important. If it makes things more simple to you, think of them as
parameters in a function: they can be used inside of the function body because they have been set
when the function was called.
This command has opened an editor where we can type our Python code.
```
----------Line Editor [Callback say of a merchant]--------------------------------
01|
----------[l:01 w:000 c:0000]------------(:h for help)----------------------------
```
For our first test, let's type something like:
```python
character.location.msg_contents("{character} shrugs and says: 'well, yes, hello to you!'",
mapping=dict(character=character))
```
Once you have entered this line, you can type `:wq` to save the editor and quit it.
And now if you use the "say" command with a message containing "hello":
```
You say, "Hello sir merchant!"
a merchant(#3) shrugs and says: 'well, yes, hello to you!'
```
If you say something that doesn't contain "hello", our callback won't execute.
**In summary**:
1. When we say something in the room, using the "say" command, the "say" event of all characters
(except us) is called.
2. The in-game Python system looks at what we have said, and checks whether one of our callbacks in
the "say" event contains a keyword that we have spoken.
3. If so, call it, defining the event variables as we have seen.
4. The callback is then executed as normal Python code. Here we have called the `msg_contents`
method on the character's location (probably a room) to display a message to the entire room. We
have also used mapping to easily display the character's name. This is not specific to the in-game
Python system. If you feel overwhelmed by the code we've used, just shorten it and use something
more simple, for instance:
```python
speaker.msg("You have said something to me.")
```
## The same callback for several keywords
It's easy to create a callback that will be triggered if the sentence contains one of several
keywords.
@call/add merchant = say trade, trader, goods
And in the editor that opens:
```python
character.location.msg_contents("{character} says: 'Ho well, trade's fine as long as roads are
safe.'", mapping=dict(character=character))
```
Then you can say something with either "trade", "trader" or "goods" in your sentence, which should
call the callback:
```
You say, "and how is your trade going?"
a merchant(#3) says: 'Ho well, trade's fine as long as roads are safe.'
```
We can set several keywords when adding the callback. We just need to separate them with commas.
## A longer callback
So far, we have only set one line in our callbacks. Which is useful, but we often need more. For
an entire dialogue, you might want to do a bit more than that.
@call/add merchant = say bandit, bandits
And in the editor you can paste the following lines:
```python
character.location.msg_contents("{character} says: 'Bandits he?'",
mapping=dict(character=character))
character.location.msg_contents("{character} scratches his head, considering.",
mapping=dict(character=character))
character.location.msg_contents("{character} whispers: 'Aye, saw some of them, north from here. No
trouble o' mine, but...'", mapping=dict(character=character))
speaker.msg("{character} looks at you more
closely.".format(character=character.get_display_name(speaker)))
speaker.msg("{character} continues in a low voice: 'Ain't my place to say, but if you need to find
'em, they're encamped some distance away from the road, I guess near a cave or
something.'.".format(character=character.get_display_name(speaker)))
```
Now try to ask the merchant about bandits:
```
You say, "have you seen bandits?"
a merchant(#3) says: 'Bandits he?'
a merchant(#3) scratches his head, considering.
a merchant(#3) whispers: 'Aye, saw some of them, north from here. No trouble o' mine, but...'
a merchant(#3) looks at you more closely.
a merchant(#3) continues in a low voice: 'Ain't my place to say, but if you need to find 'em,
they're encamped some distance away from the road, I guess near a cave or something.'.
```
Notice here that the first lines of dialogue are spoken to the entire room, but then the merchant is
talking directly to the speaker, and only the speaker hears it. There's no real limit to what you
can do with this.
- You can set a mood system, storing attributes in the NPC itself to tell you in what mood he is,
which will influence the information he will give... perhaps the accuracy of it as well.
- You can add random phrases spoken in some context.
- You can use other actions (you're not limited to having the merchant say something, you can ask
him to move, gives you something, attack if you have a combat system, or whatever else).
- The callbacks are in pure Python, so you can write conditions or loops.
- You can add in "pauses" between some instructions using chained events. This tutorial won't
describe how to do that however. You already have a lot to play with.
## Tutorial F.A.Q.
- **Q:** can I create several characters who would answer to specific dialogue?
- **A:** of course. Te in-game Python system is so powerful because you can set unique code for
various objects. You can have several characters answering to different things. You can even have
different characters in the room answering to greetings. All callbacks will be executed one after
another.
- **Q:** can I have two characters answering to the same dialogue in exactly the same way?
- **A:** It's possible but not so easy to do. Usually, event grouping is set in code, and depends
on different games. However, if it is for some infrequent occurrences, it's easy to do using
[chained
events](https://github.com/evennia/evennia/blob/master/evennia/contrib/ingame_python/README.md#chained-
events).
- **Q:** is it possible to deploy callbacks on all characters sharing the same prototype?
- **A:** not out of the box. This depends on individual settings in code. One can imagine that all
characters of some type would share some events, but this is game-specific. Rooms of the same zone
could share the same events as well. It is possible to do but requires modification of the source
code.
- Next tutorial: [adding a voice-operated elevator with events](A-voice-operated-elevator-using-
events).

View file

@ -1,428 +0,0 @@
# A voice operated elevator using events
This tutorial will walk you through the steps to create a voice-operated elevator, using the [in-
game Python system](./Contrib-Ingame-Python.md). This tutorial assumes the in-game Python
system is installed per the instructions in that doc. **You do not need to read** the entire
documentation, it's a good reference, but not the easiest way to learn about it. Hence these
tutorials.
The in-game Python system allows to run code on individual objects in some situations. You don't
have to modify the source code to add these features, past the installation. The entire system
makes it easy to add specific features to some objects, but not all.
> What will we try to do?
In this tutorial, we are going to create a simple voice-operated elevator. In terms of features, we
will:
- Explore events with parameters.
- Work on more interesting callbacks.
- Learn about chained events.
- Play with variable modification in callbacks.
## Our study case
Let's summarize what we want to achieve first. We would like to create a room that will represent
the inside of our elevator. In this room, a character could just say "1", "2" or "3", and the
elevator will start moving. The doors will close and open on the new floor (the exits leading in
and out of the elevator will be modified).
We will work on basic features first, and then will adjust some, showing you how easy and powerfully
independent actions can be configured through the in-game Python system.
## Creating the rooms and exits we need
We'll create an elevator right in our room (generally called "Limbo", of ID 2). You could easily
adapt the following instructions if you already have some rooms and exits, of course, just remember
to check the IDs.
> Note: the in-game Python system uses IDs for a lot of things. While it is not mandatory, it is
good practice to know the IDs you have for your callbacks, because it will make manipulation much
quicker. There are other ways to identify objects, but as they depend on many factors, IDs are
usually the safest path in our callbacks.
Let's go into limbo (`#2`) to add our elevator. We'll add it to the north. To create this room,
in-game you could type:
tunnel n = Inside of an elevator
The game should respond by telling you:
Created room Inside of an elevator(#3) of type typeclasses.rooms.Room.
Created Exit from Limbo to Inside of an elevator: north(#4) (n).
Created Exit back from Inside of an elevator to Limbo: south(#5) (s).
Note the given IDs:
- `#2` is limbo, the first room the system created.
- `#3` is our room inside of an elevator.
- `#4` is the north exit from Limbo to our elevator.
- `#5` is the south exit from an elevator to Limbo.
Keep these IDs somewhere for the demonstration. You will shortly see why they are important.
> Why have we created exits to our elevator and back to Limbo? Isn't the elevator supposed to move?
It is. But we need to have exits that will represent the way inside the elevator and out. What we
will do, at every floor, will be to change these exits so they become connected to the right room.
You'll see this process a bit later.
We have two more rooms to create: our floor 2 and 3. This time, we'll use `dig`, because we don't
need exits leading there, not yet anyway.
dig The second floor
dig The third floor
Evennia should answer with:
Created room The second floor(#6) of type typeclasses.rooms.Room.
Created room The third floor(#7) of type typeclasses.rooms.Room.
Add these IDs to your list, we will use them too.
## Our first callback in the elevator
Let's go to the elevator (you could use `tel #3` if you have the same IDs I have).
This is our elevator room. It looks a bit empty, feel free to add a prettier description or other
things to decorate it a bit.
But what we want now is to be able to say "1", "2" or "3" and have the elevator move in that
direction.
If you have read
[the other in-game Python tutorial about adding dialogues in events](./Contrib-Ingame-Python-Tutorial-Dialogue.md), you
may remember what we need to do. If not, here's a summary: we need to run some code when somebody
speaks in the room. So we need to create a callback (the callback will contain our lines of code).
We just need to know on which event this should be set. You can enter `call here` to see the
possible events in this room.
In the table, you should see the "say" event, which is called when somebody says something in the
room. So we'll need to add a callback to this event. Don't worry if you're a bit lost, just follow
the following steps, the way they connect together will become more obvious.
call/add here = say 1, 2, 3
1. We need to add a callback. A callback contains the code that will be executed at a given time.
So we use the `call/add` command and switch.
2. `here` is our object, the room in which we are.
3. An equal sign.
4. The name of the event to which the callback should be connected. Here, the event is "say".
Meaning this callback will be executed every time somebody says something in the room.
5. But we add an event parameter to indicate the keywords said in the room that should execute our
callback. Otherwise, our callback would be called every time somebody speaks, no matter what. Here
we limit, indicating our callback should be executed only if the spoken message contains "1", "2" or
"3".
An editor should open, inviting you to enter the Python code that should be executed. The first
thing to remember is to read the text provided (it can contain important information) and, most of
all, the list of variables that are available in this callback:
```
Variables you can use in this event:
character: the character having spoken in this room.
room: the room connected to this event.
message: the text having been spoken by the character.
----------Line Editor [Callback say of Inside of an elevator]---------------------
01|
----------[l:01 w:000 c:0000]------------(:h for help)----------------------------
```
This is important, in order to know what variables we can use in our callback out-of-the-box. Let's
write a single line to be sure our callback is called when we expect it to:
```python
character.msg(f"You just said {message}.")
```
You can paste this line in-game, then type the `:wq` command to exit the editor and save your
modifications.
Let's check. Try to say "hello" in the room. You should see the standard message, but nothing
more. Now try to say "1". Below the standard message, you should see:
You just said 1.
You can try it. Our callback is only called when we say "1", "2" or "3". Which is just what we
want.
Let's go back in our code editor and add something more useful.
call/edit here = say
> Notice that we used the "edit" switch this time, since the callback exists, we just want to edit
it.
The editor opens again. Let's empty it first:
:DD
And turn off automatic indentation, which will help us:
:=
> Auto-indentation is an interesting feature of the code editor, but we'd better not use it at this
point, it will make copy/pasting more complicated.
## Our entire callback in the elevator
So here's the time to truly code our callback in-game. Here's a little reminder:
1. We have all the IDs of our three rooms and two exits.
2. When we say "1", "2" or "3", the elevator should move to the right room, that is change the
exits. Remember, we already have the exits, we just need to change their location and destination.
It's a good idea to try to write this callback yourself, but don't feel bad about checking the
solution right now. Here's a possible code that you could paste in the code editor:
```python
# First let's have some constants
ELEVATOR = get(id=3)
FLOORS = {
"1": get(id=2),
"2": get(id=6),
"3": get(id=7),
}
TO_EXIT = get(id=4)
BACK_EXIT = get(id=5)
# Now we check that the elevator isn't already at this floor
floor = FLOORS.get(message)
if floor is None:
character.msg("Which floor do you want?")
elif TO_EXIT.location is floor:
character.msg("The elevator already is at this floor.")
else:
# 'floor' contains the new room where the elevator should be
room.msg_contents("The doors of the elevator close with a clank.")
TO_EXIT.location = floor
BACK_EXIT.destination = floor
room.msg_contents("The doors of the elevator open to {floor}.",
mapping=dict(floor=floor))
```
Let's review this longer callback:
1. We first obtain the objects of both exits and our three floors. We use the `get()` eventfunc,
which is a shortcut to obtaining objects. We usually use it to retrieve specific objects with an
ID. We put the floors in a dictionary. The keys of the dictionary are the floor number (as str),
the values are room objects.
2. Remember, the `message` variable contains the message spoken in the room. So either "1", "2", or
"3". We still need to check it, however, because if the character says something like "1 2" in the
room, our callback will be executed. Let's be sure what she says is a floor number.
3. We then check if the elevator is already at this floor. Notice that we use `TO_EXIT.location`.
`TO_EXIT` contains our "north" exit, leading inside of our elevator. Therefore, its `location` will
be the room where the elevator currently is.
4. If the floor is a different one, have the elevator "move", changing just the location and
destination of both exits.
- The `BACK_EXIT` (that is "north") should change its location. The elevator shouldn't be
accessible through our old floor.
- The `TO_EXIT` (that is "south", the exit leading out of the elevator) should have a different
destination. When we go out of the elevator, we should find ourselves in the new floor, not the old
one.
Feel free to expand on this example, changing messages, making further checks. Usage and practice
are keys.
You can quit the editor as usual with `:wq` and test it out.
## Adding a pause in our callback
Let's improve our callback. One thing that's worth adding would be a pause: for the time being,
when we say the floor number in the elevator, the doors close and open right away. It would be
better to have a pause of several seconds. More logical.
This is a great opportunity to learn about chained events. Chained events are very useful to create
pauses. Contrary to the events we have seen so far, chained events aren't called automatically.
They must be called by you, and can be called after some time.
- Chained events always have the name `"chain_X"`. Usually, X is a number, but you can give the
chained event a more explicit name.
- In our original callback, we will call our chained events in, say, 15 seconds.
- We'll also have to make sure the elevator isn't already moving.
Other than that, a chained event can be connected to a callback as usual. We'll create a chained
event in our elevator, that will only contain the code necessary to open the doors to the new floor.
call/add here = chain_1
The callback is added to the `"chain_1"` event, an event that will not be automatically called by the
system when something happens. Inside this event, you can paste the code to open the doors at the
new floor. You can notice a few differences:
```python
TO_EXIT.location = floor
TO_EXIT.destination = ELEVATOR
BACK_EXIT.location = ELEVATOR
BACK_EXIT.destination = floor
room.msg_contents("The doors of the elevator open to {floor}.",
mapping=dict(floor=floor))
```
Paste this code into the editor, then use `:wq` to save and quit the editor.
Now let's edit our callback in the "say" event. We'll have to change it a bit:
- The callback will have to check the elevator isn't already moving.
- It must change the exits when the elevator move.
- It has to call the `"chain_1"` event we have defined. It should call it 15 seconds later.
Let's see the code in our callback.
call/edit here = say
Remove the current code and disable auto-indentation again:
:DD
:=
And you can paste instead the following code. Notice the differences with our first attempt:
```python
# First let's have some constants
ELEVATOR = get(id=3)
FLOORS = {
"1": get(id=2),
"2": get(id=6),
"3": get(id=7),
}
TO_EXIT = get(id=4)
BACK_EXIT = get(id=5)
# Now we check that the elevator isn't already at this floor
floor = FLOORS.get(message)
if floor is None:
character.msg("Which floor do you want?")
elif BACK_EXIT.location is None:
character.msg("The elevator is between floors.")
elif TO_EXIT.location is floor:
character.msg("The elevator already is at this floor.")
else:
# 'floor' contains the new room where the elevator should be
room.msg_contents("The doors of the elevator close with a clank.")
TO_EXIT.location = None
BACK_EXIT.location = None
call_event(room, "chain_1", 15)
```
What changed?
1. We added a little test to make sure the elevator wasn't already moving. If it is, the
`BACK_EXIT.location` (the "south" exit leading out of the elevator) should be `None`. We'll remove
the exit while the elevator is moving.
2. When the doors close, we set both exits' `location` to `None`. Which "removes" them from their
room but doesn't destroy them. The exits still exist but they don't connect anything. If you say
"2" in the elevator and look around while the elevator is moving, you won't see any exits.
3. Instead of opening the doors immediately, we call `call_event`. We give it the object containing
the event to be called (here, our elevator), the name of the event to be called (here, "chain_1")
and the number of seconds from now when the event should be called (here, `15`).
4. The `chain_1` callback we have created contains the code to "re-open" the elevator doors. That
is, besides displaying a message, it reset the exits' `location` and `destination`.
If you try to say "3" in the elevator, you should see the doors closing. Look around you and you
won't see any exit. Then, 15 seconds later, the doors should open, and you can leave the elevator
to go to the third floor. While the elevator is moving, the exit leading to it will be
inaccessible.
> Note: we don't define the variables again in our chained event, we just call them. When we
execute `call_event`, a copy of our current variables is placed in the database. These variables
will be restored and accessible again when the chained event is called.
You can use the `call/tasks` command to see the tasks waiting to be executed. For instance, say "2"
in the room, notice the doors closing, and then type the `call/tasks` command. You will see a task
in the elevator, waiting to call the `chain_1` event.
## Changing exit messages
Here's another nice little feature of events: you can modify the message of a single exit without
altering the others. In this case, when someone goes north into our elevator, we'd like to see
something like: "someone walks into the elevator." Something similar for the back exit would be
great too.
Inside of the elevator, you can look at the available events on the exit leading outside (south).
call south
You should see two interesting rows in this table:
```
| msg_arrive | 0 (0) | Customize the message when a character |
| | | arrives through this exit. |
| msg_leave | 0 (0) | Customize the message when a character leaves |
| | | through this exit. |
```
So we can change the message others see when a character leaves, by editing the "msg_leave" event.
Let's do that:
call/add south = msg_leave
Take the time to read the help. It gives you all the information you should need. We'll need to
change the "message" variable, and use custom mapping (between braces) to alter the message. We're
given an example, let's use it. In the code editor, you can paste the following line:
```python
message = "{character} walks out of the elevator."
```
Again, save and quit the editor by entering `:wq`. You can create a new character to see it leave.
charcreate A beggar
tel #8 = here
(Obviously, adapt the ID if necessary.)
py self.search("beggar").move_to(self.search("south"))
This is a crude way to force our beggar out of the elevator, but it allows us to test. You should
see:
A beggar(#8) walks out of the elevator.
Great! Let's do the same thing for the exit leading inside of the elevator. Follow the beggar,
then edit "msg_leave" of "north":
call/add north = msg_leave
```python
message = "{character} walks into the elevator."
```
Again, you can force our beggar to move and see the message we have just set. This modification
applies to these two exits, obviously: the custom message won't be used for other exits. Since we
use the same exits for every floor, this will be available no matter at what floor the elevator is,
which is pretty neat!
## Tutorial F.A.Q.
- **Q:** what happens if the game reloads or shuts down while a task is waiting to happen?
- **A:** if your game reloads while a task is in pause (like our elevator between floors), when the
game is accessible again, the task will be called (if necessary, with a new time difference to take
into account the reload). If the server shuts down, obviously, the task will not be called, but
will be stored and executed when the server is up again.
- **Q:** can I use all kinds of variables in my callback? Whether chained or not?
- **A:** you can use every variable type you like in your original callback. However, if you
execute `call_event`, since your variables are stored in the database, they will need to respect the
constraints on persistent attributes. A callback will not be stored in this way, for instance.
This variable will not be available in your chained event.
- **Q:** when you say I can call my chained events something else than "chain_1", "chain_2" and
such, what is the naming convention?
- **A:** chained events have names beginning by `"chain_"`. This is useful for you and for the
system. But after the underscore, you can give a more useful name, like `"chain_open_doors"` in our
case.
- **Q:** do I have to pause several seconds to call a chained event?
- **A:** no, you can call it right away. Just leave the third parameter of `call_event` out (it
will default to 0, meaning the chained event will be called right away). This will not create a
task.
- **Q:** can I have chained events calling themselves?
- **A:** you can. There's no limitation. Just be careful, a callback that calls itself,
particularly without delay, might be a good recipe for an infinite loop. However, in some cases, it
is useful to have chained events calling themselves, to do the same repeated action every X seconds
for instance.
- **Q:** what if I need several elevators, do I need to copy/paste these callbacks each time?
- **A:** not advisable. There are definitely better ways to handle this situation. One of them is
to consider adding the code in the source itself. Another possibility is to call chained events
with the expected behavior, which makes porting code very easy. This side of chained events will be
shown in the next tutorial.

View file

@ -1,910 +0,0 @@
# Evennia in-game Python system
Contrib by Vincent Le Goff 2017
This contrib adds the ability to script with Python in-game. It allows trusted
staff/builders to dynamically add features and triggers to individual objects
without needing to do it in external Python modules. Using custom Python in-game,
specific rooms, exits, characters, objects etc can be made to behave differently from
its "cousins". This is similar to how softcode works for MU or MudProgs for DIKU.
Keep in mind, however, that allowing Python in-game comes with _severe_
security concerns (you must trust your builders deeply), so read the warnings in
this module carefully before continuing.
## 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:
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.
## Extra tutorials
These tutorials cover examples of using ingame python. Once you have the system
installed (see below) they may be an easier way to learn than reading the full
documentation from beginning to end.
- [Dialogue events](./Contrib-Ingame-Python-Tutorial-Dialogue.md), where
NPCs react to things said.
- [A voice operated elevator](./Contrib-Ingame-Python-Tutorial-Elevator.md)
using ingame-python events.
## 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](../Components/Typeclasses.md)
([exits](../Components/Objects.md#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:
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.
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:
You pick up a sword.
You have picked up a sword and have completed this quest!
## 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:
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.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`).
- `EVENTS_WITHOUT_VALIDATION`: a group with permission to edit callbacks without need of
validation (default to `"immortals"`).
- `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.
4. Inherit from the custom typeclasses of the in-game Python system.
- `evennia.contrib.base_systems.ingame_python.typeclasses.EventCharacter`: to replace `DefaultCharacter`.
- `evennia.contrib.base_systems.ingame_python.typeclasses.EventExit`: to replace `DefaultExit`.
- `evennia.contrib.base_systems.ingame_python.typeclasses.EventObject`: to replace `DefaultObject`.
- `evennia.contrib.base_systems.ingame_python.typeclasses.EventRoom`: to replace `DefaultRoom`.
The following sections describe in details each step of the installation.
> Note: If you were to start the game without having started the main script (such as when
resetting your database) you will most likely face a traceback when logging in, telling you
that a 'callback' property is not defined. After performing step `1` the error will go away.
### Starting the event script
To start the event script, you only need a single command, using `@py`.
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,
but you will probably use the callback handler. Creating this script will also create a `callback`
handler on all objects (see below for details).
### Editing permissions
This contrib comes with its own set of permissions. They define who can edit callbacks without
validation, and who can edit callbacks but needs validation. Validation is a process in which an
administrator (or somebody trusted as such) will check the callbacks produced by others and will
accept or reject them. If accepted, the callbacks are connected, otherwise they are never run.
By default, callbacks can only be created by immortals: no one except the immortals can edit
callbacks, and immortals don't need validation. It can easily be changed, either through settings
or dynamically by changing permissions of users.
The ingame-python contrib adds three [permissions](../Components/Permissions.md)) 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:
- `EVENTS_WITH_VALIDATION`: this defines a permission that can edit callbacks, but will need
approval. If you set this to `"wizards"`, for instance, users with the permission `"wizards"`
will be able to edit callbacks. These callbacks will not be connected, though, and will need to be
checked and approved by an administrator. This setting can contain `None`, meaning that no user is
allowed to edit callbacks with validation.
- `EVENTS_WITHOUT_VALIDATION`: this setting defines a permission allowing editing of callbacks
without needing validation. By default, this setting is set to `"immortals"`. It means that
immortals can edit callbacks, and they will be connected when they leave the editor, without needing
approval.
- `EVENTS_VALIDATING`: this last setting defines who can validate callbacks. By default, this is
set to `"immortals"`, meaning only immortals can see callbacks needing validation, accept or
reject them.
You can override all these settings in your `server/conf/settings.py` file. For instance:
```python
# ... other settings ...
# Event settings
EVENTS_WITH_VALIDATION = "wizards"
EVENTS_WITHOUT_VALIDATION = "immortals"
EVENTS_VALIDATING = "immortals"
```
In addition, there is another setting that must be set if you plan on using the time-related events
(events that are scheduled at specific, in-game times). You would need to specify the type of
calendar you are using. By default, time-related events are disabled. You can change the
`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` contrib to schedule events.
This contrib defines two additional permissions that can be set on individual users:
- `events_without_validation`: this would give this user the rights to edit callbacks but not
require validation before they are connected.
- `events_validating`: this permission allows this user to run validation checks on callbacks
needing to be validated.
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
To remove this same permission, just use the `/del` switch:
perm/del *kaldara = events_without_validation
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
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
this:
```python
from evennia import default_cmds
from evennia.contrib.base_systems.ingame_python.commands import CmdCallback
class CharacterCmdSet(default_cmds.CharacterCmdSet):
"""
The `CharacterCmdSet` contains general in-game commands like `look`,
`get`, etc available on in-game Character objects. It is merged with
the `PlayerCmdSet` when a Player puppets a Character.
"""
key = "DefaultCharacter"
def at_cmdset_creation(self):
"""
Populates the cmdset
"""
super().at_cmdset_creation()
self.add(CmdCallback())
```
### Changing parent classes of typeclasses
Finally, to use the in-game Python system, you need to have your typeclasses inherit from the modified event
classes. For instance, in your `typeclasses/characters.py` module, you should change inheritance
like this:
```python
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.
## 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 `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
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.
This command will display a table, containing:
- The name of each event in the first column.
- The number of callbacks of this name, and the number of total lines of these callbacks in the
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:
```
+------------------+---------+-----------------------------------------------+
| Event name | Number | Description |
+~~~~~~~~~~~~~~~~~~+~~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| can_delete | 0 (0) | Can the character be deleted? |
| can_move | 0 (0) | Can the character move? |
| can_part | 0 (0) | Can the departing character leave this room? |
| delete | 0 (0) | Before deleting the character. |
| greet | 0 (0) | A new character arrives in the location of |
| | | this character. |
| move | 0 (0) | After the character has moved into its new |
| | | room. |
| puppeted | 0 (0) | When the character has been puppeted by a |
| | | player. |
| time | 0 (0) | A repeated event to be called regularly. |
| unpuppeted | 0 (0) | When the character is about to be un- |
| | | puppeted. |
+------------------+---------+-----------------------------------------------+
```
### Creating a new callback
The `/add` switch should be used to add a callback. It takes two arguments beyond the object's
name/DBREF:
1. After an = sign, the name of the event to be edited (if not supplied, will display the list of
possible events, like above).
2. The parameters (optional).
We'll see callbacks with parameters later. For the time being, let's try to prevent a character
from going through the "north" exit of this room:
```
call north
+------------------+---------+-----------------------------------------------+
| Event name | Number | Description |
+~~~~~~~~~~~~~~~~~~+~~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| can_traverse | 0 (0) | Can the character traverse through this exit? |
| msg_arrive | 0 (0) | Customize the message when a character |
| | | arrives through this exit. |
| msg_leave | 0 (0) | Customize the message when a character leaves |
| | | through this exit. |
| time | 0 (0) | A repeated event to be called regularly. |
| traverse | 0 (0) | After the character has traversed through |
| | | this exit. |
+------------------+---------+-----------------------------------------------+
```
If we want to prevent a character from traversing through this exit, the best event for us would be
"can_traverse".
> Why not "traverse"? If you read the description of both events, you will see "traverse" is called
**after** the character has traversed through this exit. It would be too late to prevent it. On
> the other hand, "can_traverse" is obviously checked before the character traverses.
When we edit the event, we have some more information:
call/add north = can_traverse
Can the character traverse through this exit?
This event is called when a character is about to traverse this
exit. You can use the deny() eventfunc to deny the character from
exiting for this time.
Variables you can use in this event:
- character: the character that wants to traverse this exit.
- exit: the exit to be traversed.
- room: the room in which stands the character before moving.
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:
```python
if character.id == 1:
character.msg("You're the superuser, 'course I'll let you pass.")
else:
character.msg("Hold on, what do you think you're doing?")
deny()
```
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:
```
call north = can_traverse
+--------------+--------------+----------------+--------------+--------------+
| Number | Author | Updated | Param | Valid |
+~~~~~~~~~~~~~~+~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~+~~~~~~~~~~~~~~+
| 1 | XXXXX | 5 seconds ago | | Yes |
+--------------+--------------+----------------+--------------+--------------+
```
The left column contains callback numbers. You can use them to have even more information on a
specific event. Here, for instance:
```
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
This callback is connected and active.
Callback code:
if character.id == 1:
character.msg("You're the superuser, 'course I'll let you pass.")
else:
character.msg("Hold on, what do you think you're doing?")
deny()
```
Then try to walk through this exit. Do it with another character if possible, too, to see the
difference.
### Editing and removing a callback
You can use the `/edit` switch to the `@call` command to edit a callback. You should provide, after
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
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`).
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
`/del` was an error.
### The code editor
When adding or editing a callback, the event editor should open in code mode. The additional
options supported by the editor in this mode are describe in [a dedicated section of the EvEditor's
documentation](https://github.com/evennia/evennia/wiki/EvEditor#the-eveditor-to-edit-code).
## Using events
The following sections describe how to use events for various tasks, from the most simple to the
most complex.
### The eventfuncs
In order to make development a little easier, the in-game Python system provides eventfuncs to be used in
callbacks themselves. You don't have to use them, they are just shortcuts. An eventfunc is just a
simple function that can be used inside of your callback code.
Function | Argument | Description | Example
-----------|--------------------------|-----------------------------------|--------
deny | `()` | Prevent an action from happening. | `deny()`
get | `(**kwargs)` | Get a single object. | `char = get(id=1)`
call_event | `(obj, name, seconds=0)` | Call another event. | `call_event(char, "chain_1", 20)`
#### deny
The `deny()` function allows to interrupt the callback and the action that called it. In the
`can_*` events, it can be used to prevent the action from happening. For instance, in `can_say` on
rooms, it can prevent the character from saying something in the room. One could have a `can_eat`
event set on food that would prevent this character from eating this food.
Behind the scenes, the `deny()` function raises an exception that is being intercepted by the
handler of events. The handler will then report that the action was cancelled.
#### get
The `get` eventfunc is a shortcut to get a single object with a specific identity. It's often used
to retrieve an object with a given ID. In the section dedicated to [chained
events](#chained-events), you will see a concrete example of this function in action.
#### call_event
Some callbacks will call other events. It is particularly useful for [chained
events](#chained-events) that are described in a dedicated section. This eventfunc is used to call
another event, immediately or in a defined time.
You need to specify as first parameter the object containing the event. The second parameter is the
name of the event to call. The third parameter is the number of seconds before calling this event.
By default, this parameter is set to 0 (the event is called immediately).
### Variables in callbacks
In the Python code you will enter in individual callbacks, you will have access to variables in your
locals. These variables will depend on the event, and will be clearly listed when you add or edit a
callback. As you've seen in the previous example, when we manipulate characters or character
actions, we often have a `character` variable that holds the character doing the action.
In most cases, when an event is fired, all callbacks from this event are called. Variables are
created for each event. Sometimes, however, the callback will execute and then ask for a variable
in your locals: in other words, some callbacks can alter the actions being performed by changing
values of variables. This is always clearly specified in the help of the event.
One example that will illustrate this system is the "msg_leave" event that can be set on exits.
This event can alter the message that will be sent to other characters when someone leaves through
this exit.
call/add down = msg_leave
Which should display:
```
Customize the message when a character leaves through this exit.
This event is called when a character leaves through this exit.
To customize the message that will be sent to the room where the
character came from, change the value of the variable "message"
to give it your custom message. The character itself will not be
notified. You can use mapping between braces, like this:
message = "{character} falls into a hole!"
In your mapping, you can use {character} (the character who is
about to leave), {exit} (the exit), {origin} (the room in which
the character is), and {destination} (the room in which the character
is heading for). If you need to customize the message with other
information, you can also set "message" to None and send something
else instead.
Variables you can use in this event:
character: the character who is leaving through this exit.
exit: the exit being traversed.
origin: the location of the character.
destination: the destination of the character.
message: the message to be displayed in the location.
mapping: a dictionary containing additional mapping.
```
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:
Wildred falls into a hole in the ground!
In this case, the in-game Python system placed the variable "message" in the callback locals, but will read
from it when the event has been executed.
### Callbacks with parameters
Some callbacks are called without parameter. It has been the case for all examples we have seen
before. In some cases, you can create callbacks that are triggered under only some conditions. A
typical example is the room's "say" event. This event is triggered when somebody says something in
the room. Individual callbacks set on this event can be configured to fire only when some words are
used in the sentence.
For instance, let's say we want to create a cool voice-operated elevator. You enter into the
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
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
Or, still more keywords:
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).
Not all events can take parameters, and these who do have different ways of handling them. There
isn't a single meaning to parameters that could apply to all events. Refer to the event
documentation for details.
> If you get confused between callback variables and parameters, think of parameters as checks
> performed before the callback is run. Event with parameters will only fire some specific
> callbacks, not all of them.
### Time-related events
Events are usually linked to commands, as we saw before. However, this is not always the case.
Events can be triggered by other actions and, as we'll see later, could even be called from inside
other events!
There is a specific event, on all objects, that can trigger at a specific time. It's an event with
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
```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
this event on every kind of typeclassed object, to have a specific action done every MUD day at the
same time.
Time-related events can be much more complex than this. They can trigger every in-game hour or more
often (it might not be a good idea to have events trigger that often on a lot of objects). You can
have events that run every in-game week or month or year. It will greatly vary depending on the
type of calendar used in your game. The number of time units is described in the game
configuration.
With a standard calendar, for instance, you have the following units: minutes, hours, days, months
and years. You will specify them as numbers separated by either a colon (:), a space ( ), or a dash
(-). Pick whatever feels more appropriate (usually, we separate hours and minutes with a colon, the
other units with a dash).
Some examples of syntax:
- `18:30`: every day at 6:30 PM.
- `01 12:00`: every month, the first day, at 12 PM.
- `06-15 09:58`: every year, on the 15th of June (month comes before day), at 9:58 AM.
- `2025-01-01 00:00`: January 1st, 2025 at midnight (obviously, this will trigger only once).
Notice that we specify units in the reverse order (year, month, day, hour and minute) and separate
them with logical separators. The smallest unit that is not defined is going to set how often the
event should fire. That's why, if you use `12:00`, the smallest unit that is not defined is "day":
the event will fire every day at the specified time.
> You can use chained events (see below) in conjunction with time-related events to create more
random or frequent actions in events.
### Chained events
Callbacks can call other events, either now or a bit later. It is potentially very powerful.
To use chained events, just use the `call_event` eventfunc. It takes 2-3 arguments:
- The object containing the event.
- The name of the event to call.
- Optionally, the number of seconds to wait before calling this event.
All objects have events that are not triggered by commands or game-related operations. They are
called "chain_X", like "chain_1", "chain_2", "chain_3" and so on. You can give them more specific
names, as long as it begins by "chain_", like "chain_flood_room".
Rather than a long explanation, let's look at an example: a subway that will go from one place to
the next at regular times. Connecting exits (opening its doors), waiting a bit, closing them,
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
```python
# At 10:00 AM, the subway arrives in the room of ID 22.
# Notice that exit #23 and #24 are respectively the exit leading
# on the platform and back in the subway.
station = get(id=22)
to_exit = get(id=23)
back_exit = get(id=24)
# Open the door
to_exit.name = "platform"
to_exit.aliases = ["p"]
to_exit.location = room
to_exit.destination = station
back_exit.name = "subway"
back_exit.location = station
back_exit.destination = room
# Display some messages
room.msg_contents("The doors open and wind gushes in the subway")
station.msg_contents("The doors of the subway open with a dull clank.")
# Set the doors to close in 20 seconds
call_event(room, "chain_1", 20)
```
This callback will:
1. Be called at 10:00 AM (specify 22:00 to set it to 10:00 PM).
2. Set an exit between the subway and the station. Notice that the exits already exist (you will
not have to create them), but they don't need to have specific location and destination.
3. Display a message both in the subway and on the platform.
4. Call the event "chain_1" to execute in 20 seconds.
And now, what should we have in "chain_1"?
call/add here = chain_1
```python
# Close the doors
to_exit.location = None
to_exit.destination = None
back_exit.location = None
back_exit.destination = None
room.msg_content("After a short warning signal, the doors close and the subway begins moving.")
station.msg_content("After a short warning signal, the doors close and the subway begins moving.")
```
Behind the scenes, the `call_event` function freezes all variables ("room", "station", "to_exit",
"back_exit" in our example), so you don't need to define them again.
A word of caution on callbacks that call chained events: it isn't impossible for a callback to call
itself at some recursion level. If `chain_1` calls `chain_2` that calls `chain_3` that calls
`chain_`, particularly if there's no pause between them, you might run into an infinite loop.
Be also careful when it comes to handling characters or objects that may very well move during your
pause between event calls. When you use `call_event()`, the MUD doesn't pause and commands can be
entered by players, fortunately. It also means that, a character could start an event that pauses
for awhile, but be gone when the chained event is called. You need to check that, even lock the
character into place while you are pausing (some actions should require locking) or at least,
checking that the character is still in the room, for it might create illogical situations if you
don't.
> Chained events are a special case: contrary to standard events, they are created in-game, not
through code. They usually contain only one callback, although nothing prevents you from creating
several chained events in the same object.
## Using events in code
This section describes callbacks and events from code, how to create new events, how to call them in
a command, and how to handle specific cases like parameters.
Along this section, we will see how to implement the following example: we would like to create a
"push" command that could be used to push objects. Objects could react to this command and have
specific events fired.
### Adding new events
Adding new events should be done in your typeclasses. Events are contained in the `_events` class
variable, a dictionary of event names as keys, and tuples to describe these events as values. You
also need to register this class, to tell the in-game Python system that it contains events to be added to
this typeclass.
Here, we want to add a "push" event on objects. In your `typeclasses/objects.py` file, you should
write something like:
```python
from evennia.contrib.base_systems.ingame_python.utils import register_events
from evennia.contrib.base_systems.ingame_python.typeclasses import EventObject
EVENT_PUSH = """
A character push the object.
This event is called when a character uses the "push" command on
an object in the same room.
Variables you can use in this event:
character: the character that pushes this object.
obj: the object connected to this event.
"""
@register_events
class Object(EventObject):
"""
Class representing objects.
"""
_events = {
"push": (["character", "obj"], EVENT_PUSH),
}
```
- Line 1-2: we import several things we will need from the in-game Python system. Note that we use
`EventObject` as a parent instead of `DefaultObject`, as explained in the installation.
- Line 4-12: we usually define the help of the event in a separate variable, this is more readable,
though there's no rule against doing it another way. Usually, the help should contain a short
explanation on a single line, a longer explanation on several lines, and then the list of variables
with explanations.
- Line 14: we call a decorator on the class to indicate it contains events. If you're not familiar
with decorators, you don't really have to worry about it, just remember to put this line just
above the class definition if your class contains events.
- Line 15: we create the class inheriting from `EventObject`.
- Line 20-22: we define the events of our objects in an `_events` class variable. It is a
dictionary. Keys are event names. Values are a tuple containing:
- The list of variable names (list of str). This will determine what variables are needed when
the event triggers. These variables will be used in callbacks (as we'll see below).
- The event help (a str, the one we have defined above).
If you add this code and reload your game, create an object and examine its events with `@call`, you
should see the "push" event with its help. Of course, right now, the event exists, but it's not
fired.
### Calling an event in code
The in-game Python system is accessible through a handler on all objects. This handler is named `callbacks`
and can be accessed from any typeclassed object (your character, a room, an exit...). This handler
offers several methods to examine and call an event or callback on this object.
To call an event, use the `callbacks.call` method in an object. It takes as argument:
- The name of the event to call.
- All variables that will be accessible in the event as positional arguments. They should be
specified in the order chosen when [creating new events](#adding-new-events).
Following the same example, so far, we have created an event on all objects, called "push". This
event is never fired for the time being. We could add a "push" command, taking as argument the name
of an object. If this object is valid, it will call its "push" event.
```python
from commands.command import Command
class CmdPush(Command):
"""
Push something.
Usage:
push <something>
Push something where you are, like an elevator button.
"""
key = "push"
def func(self):
"""Called when pushing something."""
if not self.args.strip():
self.msg("Usage: push <something>")
return
# Search for this object
obj = self.caller.search(self.args)
if not obj:
return
self.msg("You push {}.".format(obj.get_display_name(self.caller)))
# Call the "push" event of this object
obj.callbacks.call("push", self.caller, obj)
```
Here we use `callbacks.call` with the following arguments:
- `"push"`: the name of the event to be called.
- `self.caller`: the one who pushed the button (this is our first variable, `character`).
- `obj`: the object being pushed (our second variable, `obj`).
In the "push" callbacks of our objects, we then can use the "character" variable (containing the one
who pushed the object), and the "obj" variable (containing the object that was pushed).
### See it all work
To see the effect of the two modifications above (the added event and the "push" command), let us
create a simple object:
@create/drop rock
@desc rock = It's a single rock, apparently pretty heavy. Perhaps you can try to push it though.
@call/add rock = push
In the callback you could write:
```python
from random import randint
number = randint(1, 6)
character.msg("You push a rock... is... it... going... to... move?")
if number == 6:
character.msg("The rock topples over to reveal a beautiful ant-hill!")
```
You can now try to "push rock". You'll try to push the rock, and once out of six times, you will
see a message about a "beautiful ant-hill".
### Adding new eventfuncs
Eventfuncs, like `deny()`, are defined in
`contrib/base_systesm/ingame_python/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.
You can also decide to create your eventfuncs in another location, or even in
several locations. To do so, edit the `EVENTFUNCS_LOCATION` setting in your
`server/conf/settings.py` file, specifying either a python path or a list of
Python paths in which your helper functions are defined. For instance:
```python
EVENTFUNCS_LOCATIONS = [
"world.events.functions",
]
```
### Creating events with parameters
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
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).
- Keyword parameters: callbacks of this event will be filtered based on specific keywords. This is
useful if you want the user to specify a word and compare this word to a list.
- Phrase parameters: callbacks will be filtered using an entire phrase and checking all its words.
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.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.
For example, here is the definition of the "say" event:
```python
from evennia.contrib.base_systems.ingame_python.utils import register_events, phrase_event
# ...
@register_events
class SomeTypeclass:
_events = {
"say": (["speaker", "character", "message"], CHARACTER_SAY, phrase_event),
}
```
When you call an event using the `obj.callbacks.call` method, you should also provide the parameter,
using the `parameters` keyword:
```python
obj.callbacks.call(..., parameters="<put parameters here>")
```
It is necessary to specifically call the event with parameters, otherwise the system will not be
able to know how to filter down the list of callbacks.
## Disabling all events at once
When callbacks are running in an infinite loop, for instance, or sending unwanted information to
players or other sources, you, as the game administrator, have the power to restart without events.
The best way to do this is to use a custom setting, in your setting file
(`server/conf/settings.py`):
```python
# Disable all events
EVENTS_DISABLED = True
```
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.
```{toctree}
:hidden:
Contrib-Ingame-Python-Tutorial-Dialogue
Contrib-Ingame-Python-Tutorial-Elevator
```
----
<small>This document page is generated from `evennia/contrib/base_systems/ingame_python/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,49 +0,0 @@
# In-Game Mail system
Contribution by grungies1138 2016
A simple Brandymail style mail system that uses the `Msg` class from Evennia
Core. It has two Commands for either sending mails between Accounts (out of game)
or between Characters (in-game). The two types of mails can be used together or
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)
```python
# 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)
```python
# 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.
----
<small>This document page is generated from `evennia/contrib/game_systems/mail/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,284 +0,0 @@
# Map Builder
Contribution by Cloud_Keeper 2016
Build a game map from the drawing of 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.grid.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.grid.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.grid.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,
}
```
----
<small>This document page is generated from `evennia/contrib/grid/mapbuilder/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,28 +0,0 @@
# Menu-based login system
Contribution by Vincent-lg 2016. Reworked for modern EvMenu by Griatch, 2019.
This changes the Evennia login to ask for the account name and password as a series
of questions instead of requiring you to enter both at once. It uses Evennia's
menu system `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 = "evennia.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).
----
<small>This document page is generated from `evennia/contrib/base_systems/menu_login/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,23 +0,0 @@
# TutorialMirror
Contribution by Griatch, 2017
A simple mirror object to experiment with. It will respond to being looked at.
- echoes back the description of the object looking at it
- echoes back whatever is being sent to its .msg - to the
sender, if given, otherwise to the location of the mirror.
## Installation
Create the mirror with
create/drop mirror:contrib.tutorials.mirror.TutorialMirror
Then look at it.
----
<small>This document page is generated from `evennia/contrib/tutorials/mirror/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,31 +0,0 @@
# Evennia Multidescer
Contribution by Griatch 2016
A "multidescer" is a concept from the MUSH world. It allows for
creating, managing and switching between multiple character
descriptions and is a way for quickly managing your look (such as when
changing clothes) in more free-form roleplaying systems. This will also
work well together with the `rpsystem` contrib.
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.
## Installation
Edit `mygame/commands/default_cmdsets.py` and add
`from evennia.contrib.game_systems.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).
----
<small>This document page is generated from `evennia/contrib/game_systems/multidescer/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,50 +0,0 @@
# Legacy Comms-commands
Contribution by 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 functions.
This contrib (extracted from Evennia 0.9.5) breaks out the functionality into
separate Commands more familiar to MU* users. This is just for show though, the
main `channel` command is still called under the hood.
| Contrib syntax | Default `channel` syntax |
| -------------- | --------------------------------------------------------- |
| `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.
----
<small>This document page is generated from `evennia/contrib/base_systems/mux_comms_cmds/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,282 +0,0 @@
# Random Name Generator
Contribution by InspectorCaracal (2022)
A module for generating random names, both real-world and fantasy. Real-world
names can be generated either as first (personal) names, family (last) names, or
full names (first, optional middles, and last). The name data is from [Behind the Name](https://www.behindthename.com/)
and used under the [CC BY-SA 4.0 license](https://creativecommons.org/licenses/by-sa/4.0/).
Fantasy names are generated from basic phonetic rules, using CVC syllable syntax.
Both real-world and fantasy name generation can be extended to include additional
information via your game's `settings.py`
## Installation
This is a stand-alone utility. Just import this module (`from evennia.contrib.utils import name_generator`) and use its functions wherever you like.
## Usage
Import the module where you need it with the following:
```py
from evennia.contrib.utils.name_generator import namegen
```
By default, all of the functions will return a string with one generated name.
If you specify more than one, or pass `return_list=True` as a keyword argument, the returned value will be a list of strings.
The module is especially useful for naming newly-created NPCs, like so:
```py
npc_name = namegen.full_name()
npc_obj = create_object(key=npc_name, typeclass="typeclasses.characters.NPC")
```
## Available Settings
These settings can all be defined in your game's `server/conf/settings.py` file.
- `NAMEGEN_FIRST_NAMES` adds a new list of first (personal) names.
- `NAMEGEN_LAST_NAMES` adds a new list of last (family) names.
- `NAMEGEN_REPLACE_LISTS` - set to `True` if you want to use only the names defined in your settings.
- `NAMEGEN_FANTASY_RULES` lets you add new phonetic rules for generating entirely made-up names. See the section "Custom Fantasy Name style rules" for details on how this should look.
Examples:
```py
NAMEGEN_FIRST_NAMES = [
("Evennia", 'mf'),
("Green Tea", 'f'),
]
NAMEGEN_LAST_NAMES = [ "Beeblebrox", "Son of Odin" ]
NAMEGEN_FANTASY_RULES = {
"example_style": {
"syllable": "(C)VC",
"consonants": [ 'z','z','ph','sh','r','n' ],
"start": ['m'],
"end": ['x','n'],
"vowels": [ "e","e","e","a","i","i","u","o", ],
"length": (2,4),
}
}
```
## Generating Real Names
The contrib offers three functions for generating random real-world names:
`first_name()`, `last_name()`, and `full_name()`. If you want more than one name
generated at once, you can use the `num` keyword argument to specify how many.
Example:
```
>>> namegen.first_name(num=5)
['Genesis', 'Tali', 'Budur', 'Dominykas', 'Kamau']
>>> namegen.first_name(gender='m')
'Blanchard'
```
The `first_name` function also takes a `gender` keyword argument to filter names
by gender association. 'f' for feminine, 'm' for masculine, 'mf' for feminine
_and_ masculine, or the default `None` to match any gendering.
The `full_name` function also takes the `gender` keyword, as well as `parts` which
defines how many names make up the full name. The minimum is two: a first name and
a last name. You can also generate names with the family name first by setting
the keyword arg `surname_first` to `True`
Example:
```
>>> namegen.full_name()
'Keeva Bernat'
>>> namegen.full_name(parts=4)
'Suzu Shabnam Kafka Baier'
>>> namegen.full_name(parts=3, surname_first=True)
'Ó Muircheartach Torunn Dyson'
>>> namegen.full_name(gender='f')
'Wikolia Ó Deasmhumhnaigh'
```
### Adding your own names
You can add additional names with the settings `NAMEGEN_FIRST_NAMES` and
`NAMEGEN_LAST_NAMES`
`NAMEGEN_FIRST_NAMES` should be a list of tuples, where the first value is the name
and then second value is the gender flag - 'm' for masculine-only, 'f' for feminine-
only, and 'mf' for either one.
`NAMEGEN_LAST_NAMES` should be a list of strings, where each item is an available
surname.
Examples:
```py
NAMEGEN_FIRST_NAMES = [
("Evennia", 'mf'),
("Green Tea", 'f'),
]
NAMEGEN_LAST_NAMES = [ "Beeblebrox", "Son of Odin" ]
```
Set `NAMEGEN_REPLACE_LISTS = True` if you want your custom lists above to entirely replace the built-in lists rather than extend them.
## Generating Fantasy Names
Generating completely made-up names is done with the `fantasy_name` function. The
contrib comes with three built-in styles of names which you can use, or you can
put a dictionary of custom name rules into `settings.py`
Generating a fantasy name takes the ruleset key as the "style" keyword, and can
return either a single name or multiple names. By default, it will return a
single name in the built-in "harsh" style. The contrib also comes with "fluid" and "alien" styles.
```py
>>> namegen.fantasy_name()
'Vhon'
>>> namegen.fantasy_name(num=3, style="harsh")
['Kha', 'Kizdhu', 'Godögäk']
>>> namegen.fantasy_name(num=3, style="fluid")
['Aewalisash', 'Ayi', 'Iaa']
>>> namegen.fantasy_name(num=5, style="alien")
["Qz'vko'", "Xv'w'hk'hxyxyz", "Wxqv'hv'k", "Wh'k", "Xbx'qk'vz"]
```
### Multi-Word Fantasy Names
The `fantasy_name` function will only generate one name-word at a time, so for multi-word names
you'll need to combine pieces together. Depending on what kind of end result you want, there are
several approaches.
#### The simple approach
If all you need is for it to have multiple parts, you can generate multiple names at once and `join` them.
```py
>>> name = " ".join(namegen.fantasy_name(num=2))
>>> name
'Dezhvözh Khäk'
```
If you want a little more variation between first/last names, you can also generate names for
different styles and then combine them.
```py
>>> first = namegen.fantasy_name(style="fluid")
>>> last = namegen.fantasy_name(style="harsh")
>>> name = f"{first} {last}"
>>> name
'Ofasa Käkudhu'
```
#### "Nakku Silversmith"
One common fantasy name practice is profession- or title-based surnames. To achieve this effect,
you can use the `last_name` function with a custom list of last names and combine it with your generated
fantasy name.
Example:
```py
NAMEGEN_LAST_NAMES = [ "Silversmith", "the Traveller", "Destroyer of Worlds" ]
NAMEGEN_REPLACE_LISTS = True
>>> first = namegen.fantasy_name()
>>> last = namegen.last_name()
>>> name = f"{first} {last}"
>>> name
'Tözhkheko the Traveller'
```
#### Elarion d'Yrinea, Thror Obinson
Another common flavor of fantasy names is to use a surname suffix or prefix. For that, you'll
need to add in the extra bit yourself.
Examples:
```py
>>> names = namegen.fantasy_name(num=2)
>>> name = f"{names[0]} za'{names[1]}"
>>> name
"Tithe za'Dhudozkok"
>>> names = namegen.fantasy_name(num=2)
>>> name = f"{names[0]} {names[1]}son"
>>> name
'Kön Ködhöddoson'
```
### Custom Fantasy Name style rules
The style rules are contained in a dictionary of dictionaries, where the style name
is the key and the style rules are the dictionary value.
The following is how you would add a custom style to `settings.py`:
```py
NAMEGEN_FANTASY_RULES = {
"example_style": {
"syllable": "(C)VC",
"consonants": [ 'z','z','ph','sh','r','n' ],
"start": ['m'],
"end": ['x','n'],
"vowels": [ "e","e","e","a","i","i","u","o", ],
"length": (2,4),
}
}
```
Then you could generate names following that ruleset with `namegen.fantasy_name(style="example_style")`.
The keys `syllable`, `consonants`, `vowels`, and `length` must be present, and `length` must be the minimum and maximum syllable counts. `start` and `end` are optional.
#### syllable
The "syllable" field defines the structure of each syllable. C is consonant, V is vowel,
and parentheses mean it's optional. So, the example `(C)VC` means that every syllable
will always have a vowel followed by a consonant, and will *sometimes* have another
consonant at the beginning. e.g. `en`, `bak`
*Note:* While it's not standard, the contrib lets you nest parentheses, with each layer
being less likely to show up. Additionally, any other characters put into the syllable
structure - e.g. an apostrophe - will be read and inserted as written. The
"alien" style rules in the module gives an example of both: the syllable structure is `C(C(V))(')(C)`
which results in syllables such as `khq`, `xho'q`, and `q'` with a much lower frequency of vowels than
`C(C)(V)(')(C)` would have given.
#### consonants
A simple list of consonant phonemes that can be chosen from. Multi-character strings are
perfectly acceptable, such as "th", but each one will be treated as a single consonant.
The function uses a naive form of weighting, where you make a phoneme more likely to
occur by putting more copies of it into the list.
#### start and end
These are **optional** lists for the first and last letters of a syllable, if they're
a consonant. You can add on additional consonants which can only occur at the beginning
or end of a syllable, or you can add extra copies of already-defined consonants to
increase the frequency of them at the start/end of syllables.
For example, in the `example_style` above, we have a `start` of m, and `end` of x and n.
Taken with the rest of the consonants/vowels, this means you can have the syllables of `mez`
but not `zem`, and you can have `phex` or `phen` but not `xeph` or `neph`.
They can be left out of custom rulesets entirely.
#### vowels
Vowels is a simple list of vowel phonemes - exactly like consonants, but instead used for the
vowel selection. Single-or multi-character strings are equally fine. It uses the same naive weighting system
as consonants - you can increase the frequency of any given vowel by putting it into the list multiple times.
#### length
A tuple with the minimum and maximum number of syllables a name can have.
When setting this, keep in mind how long your syllables can get! 4 syllables might
not seem like very many, but if you have a (C)(V)VC structure with one- and
two-letter phonemes, you can get up to eight characters per syllable.
----
<small>This document page is generated from `evennia/contrib/utils/name_generator/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,77 +0,0 @@
# Puzzles System
Contribution by Henddher 2018
Intended for adventure-game style combination puzzles, such as combining fruits
and a blender to create a smoothie. Provides a typeclass and commands for objects
that can be combined (i.e. used together). Unlike the `crafting` contrib, each
puzzle is built from unique objects rather than using tags and a builder can create
the puzzle entirely from in-game.
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 (for quick testing):
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.
----
<small>This document page is generated from `evennia/contrib/game_systems/puzzles/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,273 +0,0 @@
# Roleplaying base system for Evennia
Contribution by Griatch, 2015
A full roleplaying emote system. Short-descriptions and recognition (only
know people by their looks until you assign a name to them). Room poses. Masks/disguises
(hide your description). Speak directly in emote, with optional language obscuration
(words get garbled if you don't know the language, you can also have different languages
with different 'sounding' garbling). Whispers can be partly overheard from a distance. A
very powerful in-emote reference system, for referencing and differentiate targets
(including objects).
The system contains of two main modules - the roleplaying emote system and the language
obscuration module.
## 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.
----
<small>This document page is generated from `evennia/contrib/rpg/rpsystem/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,60 +0,0 @@
# Pseudo-random generator and registry
Contribution by Vincent Le Goff (vlgeoff), 2017
This utility can be used to generate pseudo-random strings of information
with specific criteria. You could, for instance, use it to generate
phone numbers, license plate numbers, validation codes, in-game security
passwords and so on. The strings generated will be stored and won't be repeated.
## Usage Example
Here's a very simple example:
```python
from evennia.contrib.utils.random_string_generator import RandomStringGenerator
# Create a generator for phone numbers
phone_generator = RandomStringGenerator("phone number", r"555-[0-9]{3}-[0-9]{4}")
# Generate a phone number (555-XXX-XXXX with X as numbers)
number = phone_generator.get()
# `number` will contain something like: "555-981-2207"
# If you call `phone_generator.get`, it won't give the same anymore.phone_generator.all()
# Will return a list of all currently-used phone numbers
phone_generator.remove("555-981-2207")
# The number can be generated again
```
## Importing
1. Import the `RandomStringGenerator` class from the contrib.
2. Create an instance of this class taking two arguments:
- The name of the gemerator (like "phone number", "license plate"...).
- The regular expression representing the expected results.
3. Use the generator's `all`, `get` and `remove` methods as shown above.
To understand how to read and create regular expressions, you can refer to
[the documentation on the re module](https://docs.python.org/2/library/re.html).
Some examples of regular expressions you could use:
- `r"555-\d{3}-\d{4}"`: 555, a dash, 3 digits, another dash, 4 digits.
- `r"[0-9]{3}[A-Z][0-9]{3}"`: 3 digits, a capital letter, 3 digits.
- `r"[A-Za-z0-9]{8,15}"`: between 8 and 15 letters and digits.
- ...
Behind the scenes, a script is created to store the generated information
for a single generator. The `RandomStringGenerator` object will also
read the regular expression you give to it to see what information is
required (letters, digits, a more restricted class, simple characters...)...
More complex regular expressions (with branches for instance) might not be
available.
----
<small>This document page is generated from `evennia/contrib/utils/random_string_generator/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,40 +0,0 @@
# Red Button example
Contribution by Griatch, 2011
A red button that you can press to have an effect. This is a more advanced example
object with its own functionality and state tracking.
Create the button with
create/drop button:tutorials.red_button.RedButton
Note that you must drop the button before you can see its messages! It's
imperative that you press the red button. You know you want to.
Use `del button` to destroy/stop the button when you are done playing.
## Technical
The button's functionality is controlled by CmdSets that gets added and removed
depending on the 'state' the button is in.
- Lid-closed state: In this state the button is covered by a glass cover and
trying to 'push' it will fail. You can 'nudge', 'smash' or 'open' the lid.
- Lid-open state: In this state the lid is open but will close again after a
certain time. Using 'push' now will press the button and trigger the
Blind-state.
- Blind-state: In this mode you are blinded by a bright flash. This will affect
your normal commands like 'look' and help until the blindness wears off after
a certain time.
Timers are handled by persistent delays on the button. These are examples of
`evennia.utils.utils.delay` calls that wait a certain time before calling a
method - such as when closing the lid and un-blinding a character.
----
<small>This document page is generated from `evennia/contrib/tutorials/red_button/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,52 +0,0 @@
# SimpleDoor
Contribution by Griatch, 2016
A simple two-way exit that represents a door that can be opened and
closed from both sides. Can easily be expanded 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.
----
<small>This document page is generated from `evennia/contrib/grid/simpledoor/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,67 +0,0 @@
# Slow Exit
Contribution by Griatch 2014
An example of an Exit-type that delays its traversal. This simulates
slow movement, common in many games. The contrib also
contains two commands, `setspeed` and `stop` 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.
----
<small>This document page is generated from `evennia/contrib/grid/slow_exit/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,27 +0,0 @@
# Talkative NPC example
Contribution by Griatch 2011. Updated by grungies1138, 2016
This is an example of a static NPC object capable of holding a simple menu-driven
conversation. Suitable for example as a quest giver or merchant.
## Installation
Create the NPC by creating an object of typeclass `contrib.tutorials.talking_npc.TalkingNPC`,
For example:
create/drop John : contrib.tutorials.talking_npc.TalkingNPC
Use `talk` in the same room as the NPC to start a conversation.
If there are many talkative npcs in the same room you will get to choose which
one's talk command to call (Evennia handles this automatically).
This use of EvMenu is very simplistic; See EvMenu for a lot more complex
possibilities.
----
<small>This document page is generated from `evennia/contrib/tutorials/talking_npc/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,447 +0,0 @@
# Traits
Contribution by Griatch 2020, based on code by Whitenoise and Ainneve contribs, 2014
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.rpg.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.rpg.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.rpg.traits.Trait` (or from one of
the existing Trait classes).
```python
# in a file, say, 'mygame/world/traits.py'
from evennia.contrib.rpg.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')
```
----
<small>This document page is generated from `evennia/contrib/rpg/traits/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,169 +0,0 @@
# Easy menu selection tree
Contribution by Tim Ashley Jenkins, 2017
This utility allows you to create and initialize an entire branching EvMenu
instance from a multi-line string passed to one function.
> Note: Since the time this contrib was created, EvMenu itself got its own templating
> language that has more features and is not compatible with the style used in
> this contrib. Both can still be used in parallel.
`EvMenu` is incredibly powerful and flexible but it can be a little overwhelming
and offers a lot of power that may not be needed for a simple multiple-choice menu.
This module provides a function, `init_tree_selection`, which acts as a frontend
for EvMenu, dynamically sourcing the options from a multi-line string you
provide. For example, if you define a string as such:
TEST_MENU = '''Foo
Bar
Baz
Qux'''
And then use `TEST_MENU` as the 'treestr' source when you call
`init_tree_selection` on a player:
init_tree_selection(TEST_MENU, caller, callback)
The player will be presented with an EvMenu, like so:
___________________________
Make your selection:
___________________________
Foo
Bar
Baz
Qux
Making a selection will pass the selection's key to the specified callback as a
string along with the caller, as well as the index of the selection (the line
number on the source string) along with the source string for the tree itself.
In addition to specifying selections on the menu, you can also specify
categories. Categories are indicated by putting options below it preceded with
a '-' character. If a selection is a category, then choosing it will bring up a
new menu node, prompting the player to select between those options, or to go
back to the previous menu. In addition, categories are marked by default with a
'[+]' at the end of their key. Both this marker and the option to go back can be
disabled.
Categories can be nested in other categories as well - just go another '-'
deeper. You can do this as many times as you like. There's no hard limit to the
number of categories you can go down.
For example, let's add some more options to our menu, turning 'Bar' into a
category.
TEST_MENU = '''Foo
Bar
-You've got to know
--When to hold em
--When to fold em
--When to walk away
Baz
Qux'''
Now when we call the menu, we can see that 'Bar' has become a category instead of a
selectable option.
_______________________________
Make your selection:
_______________________________
Foo
Bar [+]
Baz
Qux
Note the [+] next to 'Bar'. If we select 'Bar', it'll show us the option listed
under it.
________________________________________________________________
Bar
________________________________________________________________
You've got to know [+]
<< Go Back: Return to the previous menu.
Just the one option, which is a category itself, and the option to go back,
which will take us back to the previous menu. Let's select 'You've got to know'.
________________________________________________________________
You've got to know
________________________________________________________________
When to hold em
When to fold em
When to walk away
<< Go Back: Return to the previous menu.
Now we see the three options listed under it, too. We can select one of them or
use 'Go Back' to return to the 'Bar' menu we were just at before. It's very
simple to make a branching tree of selections!
One last thing - you can set the descriptions for the various options simply by
adding a ':' character followed by the description to the option's line. For
example, let's add a description to 'Baz' in our menu:
TEST_MENU = '''Foo
Bar
-You've got to know
--When to hold em
--When to fold em
--When to walk away
Baz: Look at this one: the best option.
Qux'''
Now we see that the Baz option has a description attached that's separate from its key:
_______________________________________________________________
Make your selection:
_______________________________________________________________
Foo
Bar [+]
Baz: Look at this one: the best option.
Qux
Once the player makes a selection - let's say, 'Foo' - the menu will terminate
and call your specified callback with the selection, like so:
callback(caller, TEST_MENU, 0, "Foo")
The index of the selection is given along with a string containing the
selection's key. That way, if you have two selections in the menu with the same
key, you can still differentiate between them.
And that's all there is to it! For simple branching-tree selections, using this
system is much easier than manually creating EvMenu nodes. It also makes
generating menus with dynamic options much easier - since the source of the menu
tree is just a string, you could easily generate that string procedurally before
passing it to the `init_tree_selection` function. For example, if a player casts
a spell or does an attack without specifying a target, instead of giving them an
error, you could present them with a list of valid targets to select by
generating a multi-line string of targets and passing it to
`init_tree_selection`, with the callable performing the maneuver once a
selection is made.
This selection system only works for simple branching trees - doing anything
really complicated like jumping between categories or prompting for arbitrary
input would still require a full EvMenu implementation. For simple selections,
however, I'm sure you will find using this function to be much easier!
Included in this module is a sample menu and function which will let a player
change the color of their name - feel free to mess with it to get a feel for how
this system works by importing this module in your game's `default_cmdsets.py`
module and adding `CmdNameColor` to your default character's command set.
----
<small>This document page is generated from `evennia/contrib/utils/tree_select/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,61 +0,0 @@
# Turn based battle system framework
Contribution by Tim Ashley Jenkins, 2017
This is a framework for a simple turn-based combat system, similar
to those used in D&D-style tabletop role playing games. It allows
any character to start a fight in a room, at which point initiative
is rolled and a turn order is established. Each participant in combat
has a limited time to decide their action for that turn (30 seconds by
default), and combat progresses through the turn order, looping through
the participants until the fight ends.
This folder contains multiple examples of how such a system can be
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_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
attacks.
This system is meant as a basic framework to start from, and is modeled
after the combat systems of popular tabletop role playing games rather than
the real-time battle systems that many MMOs and some MUDs use. As such, it
may be better suited to role-playing or more story-oriented games, or games
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
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.
----
<small>This document page is generated from `evennia/contrib/game_systems/turnbattle/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,114 +0,0 @@
# Evennia Tutorial World
Contribution by Griatch 2011, 2015
A stand-alone tutorial area for an unmodified Evennia install.
Think of it as a sort of single-player adventure rather than a
full-fledged multi-player game world. The various rooms and objects
are designed to show off features of Evennia, not to be a
very challenging (nor long) gaming experience. As such it's of course
only skimming the surface of what is possible. Taking this apart
is a great way to start learning the system.
The tutorial world also includes a game tutor menu example, exemplifying
Evmenu.
## Installation
Log in as superuser (#1), then run
batchcommand tutorials.tutorial_world.build
Wait a little while for building to complete and don't run the command
again even if it's slow. This builds the world and connect it to Limbo
and creates a new exit `tutorial`.
If you are a superuser (User `#1`), use the `quell` command to play
the tutorial as intended.
## Comments
The tutorial world is intended to be explored and analyzed. It will help you
learn how to accomplish some more advanced effects and might give some good
ideas along the way.
It's suggested you play it through (as a normal user, NOT as Superuser!) and
explore it a bit, then come back here and start looking into the (heavily
documented) build/source code to find out how things tick - that's the
"tutorial" in Tutorial world after all.
Please report bugs in the tutorial to the Evennia issue tracker.
**Spoilers below - don't read on unless you already played the
tutorial game**
## Tutorial World Room map
?
|
+---+----+ +-------------------+ +--------+ +--------+
| | | | |gate | |corner |
| cliff +----+ bridge +----+ +---+ |
| | | | | | | |
+---+---\+ +---------------+---+ +---+----+ +---+----+
| \ | | castle |
| \ +--------+ +----+---+ +---+----+ +---+----+
| \ |under- | |ledge | |along | |court- |
| \|ground +--+ | |wall +---+yard |
| \ | | | | | | |
| +------\-+ +--------+ +--------+ +---+----+
| \ |
++---------+ \ +--------+ +--------+ +---+----+
|intro | \ |cell | |trap | |temple |
o--+ | \| +----+ | | |
L | | \ | /| | | |
I +----+-----+ +--------+ / ---+-+-+-+ +---+----+
M | / | | | |
B +----+-----+ +--------+/ +--+-+-+---------+----+
O |outro | |tomb | |antechamber |
o--+ +----------+ | | |
| | | | | |
+----------+ +--------+ +---------------------+
## Hints/Notes:
* o-- connections to/from Limbo
* intro/outro areas are rooms that automatically sets/cleans the
Character of any settings assigned to it during the
tutorial game.
* The Cliff is a good place to get an overview of the surroundings.
* The Bridge may seem like a big room, but it is really only one room
with custom move commands to make it take longer to cross. You can
also fall off the bridge if you are unlucky or take your time to
take in the view too long.
* In the Castle areas an aggressive mob is patrolling. It implements
rudimentary AI but packs quite a punch unless you have
found yourself a weapon that can harm it. Combat is only
possible once you find a weapon.
* The Antechamber features a puzzle for finding the correct Grave
chamber.
* The Cell is your reward if you fail in various ways. Finding a
way out of it is a small puzzle of its own.
* The Tomb is a nice place to find a weapon that can hurt the
castle guardian. This is the goal of the tutorial.
Explore on, or take the exit to finish the tutorial.
* ? - look into the code if you cannot find this bonus area!
----
<small>This document page is generated from `evennia/contrib/tutorials/tutorial_world/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,71 +0,0 @@
# Unix-like Command style
Contribution by Vincent Le Geoff (vlgeoff), 2017
This module contains a command class with an alternate syntax parser implementing
Unix-style command syntax in-game. This means `--options`, positional arguments
and stuff like `-n 10`. 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).
----
<small>This document page is generated from `evennia/contrib/base_systems/unixcommand/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

View file

@ -1,119 +0,0 @@
# Wilderness system
Contribution by titeuf87, 2017
This contrib provides a wilderness map without actually creating a large number
of rooms - as you move, you instead end up back in the same room but its description
changes. 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.
----
<small>This document page is generated from `evennia/contrib/grid/wilderness/README.md`. Changes to this
file will be overwritten, so edit that file rather than this one.</small>

File diff suppressed because it is too large Load diff

View file

@ -1,773 +0,0 @@
# Contribs
_Contribs_ are optional code snippets and systems contributed by
the Evennia community. They vary in size and complexity and
may be more specific about game types and styles than 'core' Evennia.
This page is auto-generated and summarizes all contribs currently included.
All contrib categories are imported from `evennia.contrib`, such as
from evennia.contrib.base_systems import building_menu
Each contrib contains installation instructions for how to integrate it
with your other code. If you want to tweak the code of a contrib, just
copy its entire folder to your game directory and modify/use it from there.
> Hint: Additional (potentially un-maintained) code snippets from the community can be found
in our discussion forum's [Community Contribs & Snippets](https://github.com/evennia/evennia/discussions/categories/community-contribs-snippets) category.
If you want to contribute yourself, see [here](../Contributing.md)!
## base_systems
_This category contains systems that are not necessarily tied to a specific
in-game mechanic but is useful for the game as a whole. Examples include
login systems, new command syntaxes, and build helpers._
```{toctree}
:maxdepth: 1
Contrib-AWSStorage.md
Contrib-Building-Menu.md
Contrib-Color-Markups.md
Contrib-Components.md
Contrib-Custom-Gametime.md
Contrib-Email-Login.md
Contrib-Ingame-Python.md
Contrib-Menu-Login.md
Contrib-Mux-Comms-Cmds.md
Contrib-Unixcommand.md
```
### Contrib: `awsstorage`
_Contrib by The Right Honourable Reverend (trhr), 2020_
This plugin migrates the Web-based portion of Evennia, namely images,
javascript, and other items located inside staticfiles into Amazon AWS (S3)
cloud hosting. Great for those serving media with the game.
[Read the documentation](./Contrib-AWSStorage.md) - [Browse the Code](evennia.contrib.base_systems.awsstorage)
### Contrib: `building_menu`
_Contrib by 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 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.
[Read the documentation](./Contrib-Building-Menu.md) - [Browse the Code](evennia.contrib.base_systems.building_menu)
### Contrib: `color_markups`
_Contrib by Griatch, 2017_
Additional color markup styles for Evennia (extending or replacing the default
`|r`, `|234`). Adds support for MUSH-style (`%cr`, `%c123`) and/or legacy-Evennia
(`{r`, `{123`).
[Read the documentation](./Contrib-Color-Markups.md) - [Browse the Code](evennia.contrib.base_systems.color_markups)
### Contrib: `components`
__Contrib by ChrisLR 2021__
# The Components Contrib
[Read the documentation](./Contrib-Components.md) - [Browse the Code](evennia.contrib.base_systems.components)
### Contrib: `custom_gametime`
_Contrib by vlgeoff, 2017 - based on Griatch's core original_
This reimplements the `evennia.utils.gametime` module but with a _custom_
calendar (unusual number of days per week/month/year etc) for your game world.
Like the original, it allows for scheduling events to happen at given
in-game times, but now taking this custom calendar into account.
[Read the documentation](./Contrib-Custom-Gametime.md) - [Browse the Code](evennia.contrib.base_systems.custom_gametime)
### Contrib: `email_login`
_Contrib by Griatch, 2012_
This is a variant of the login system that asks for an email-address
instead of a username to login. Note that it does not verify the email,
it just uses it as the identifier rather than a username.
[Read the documentation](./Contrib-Email-Login.md) - [Browse the Code](evennia.contrib.base_systems.email_login)
### Contrib: `ingame_python`
_Contrib by Vincent Le Goff 2017_
This contrib adds the ability to script with Python in-game. It allows trusted
staff/builders to dynamically add features and triggers to individual objects
without needing to do it in external Python modules. Using custom Python in-game,
specific rooms, exits, characters, objects etc can be made to behave differently from
its "cousins". This is similar to how softcode works for MU or MudProgs for DIKU.
Keep in mind, however, that allowing Python in-game comes with _severe_
security concerns (you must trust your builders deeply), so read the warnings in
this module carefully before continuing.
[Read the documentation](./Contrib-Ingame-Python.md) - [Browse the Code](evennia.contrib.base_systems.ingame_python)
### Contrib: `menu_login`
_Contribution by Vincent-lg 2016. Reworked for modern EvMenu by Griatch, 2019._
This changes the Evennia login to ask for the account name and password as a series
of questions instead of requiring you to enter both at once. It uses Evennia's
menu system `EvMenu` under the hood.
[Read the documentation](./Contrib-Menu-Login.md) - [Browse the Code](evennia.contrib.base_systems.menu_login)
### Contrib: `mux_comms_cmds`
_Contribution by 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 functions.
This contrib (extracted from Evennia 0.9.5) breaks out the functionality into
separate Commands more familiar to MU* users. This is just for show though, the
main `channel` command is still called under the hood.
[Read the documentation](./Contrib-Mux-Comms-Cmds.md) - [Browse the Code](evennia.contrib.base_systems.mux_comms_cmds)
### Contrib: `unixcommand`
_Contribution by Vincent Le Geoff (vlgeoff), 2017_
This module contains a command class with an alternate syntax parser implementing
Unix-style command syntax in-game. This means `--options`, positional arguments
and stuff like `-n 10`. 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.
[Read the documentation](./Contrib-Unixcommand.md) - [Browse the Code](evennia.contrib.base_systems.unixcommand)
## full_systems
_This category contains 'complete' game engines that can be used directly
to start creating content without no further additions (unless you want to)._
```{toctree}
:maxdepth: 1
Contrib-Evscaperoom.md
```
### Contrib: `evscaperoom`
_Contribution by Griatch, 2019_
A full engine for creating multiplayer escape-rooms in Evennia. Allows players to
spawn and join puzzle rooms that track their state independently. Any number of players
can join to solve a room together. This is the engine created for 'EvscapeRoom', which won
the MUD Coders Guild "One Room" Game Jam in April-May, 2019. The contrib has no game
content but contains the utilities and base classes and an empty example room.
[Read the documentation](./Contrib-Evscaperoom.md) - [Browse the Code](evennia.contrib.full_systems.evscaperoom)
## game_systems
_This category holds code implementing in-game gameplay systems like
crafting, mail, combat and more. Each system is meant to be adopted
piecemeal and adopted for your game. This does not include
roleplaying-specific systems, those are found in the `rpg` folder._
```{toctree}
:maxdepth: 1
Contrib-Barter.md
Contrib-Clothing.md
Contrib-Cooldowns.md
Contrib-Crafting.md
Contrib-Gendersub.md
Contrib-Mail.md
Contrib-Multidescer.md
Contrib-Puzzles.md
Contrib-Turnbattle.md
```
### Contrib: `barter`
_Contribution by Griatch, 2012_
This implements a full barter system - a way for players to safely
trade items between each other in code rather than simple `give/get`
commands. This increases both safety (at no time will one player have
both goods and payment in-hand) and speed, since agreed goods will
be moved automatically). By just replacing one side with coin objects,
(or a mix of coins and goods), this also works fine for regular money
transactions.
[Read the documentation](./Contrib-Barter.md) - [Browse the Code](evennia.contrib.game_systems.barter)
### Contrib: `clothing`
_Contribution by Tim Ashley Jenkins, 2017_
Provides a typeclass and commands for wearable clothing. These
look of these clothes are appended to the character's description when worn.
[Read the documentation](./Contrib-Clothing.md) - [Browse the Code](evennia.contrib.game_systems.clothing)
### Contrib: `cooldowns`
_Contribution by owllex, 2021_
Cooldowns are used to model rate-limited actions, like how often a
character can perform a given action; until a certain time has passed their
command can not be used again. This contrib provides a simple cooldown
handler that can be attached to any typeclass. A cooldown is a lightweight persistent
asynchronous timer that you can query to see if a certain time has yet passed.
[Read the documentation](./Contrib-Cooldowns.md) - [Browse the Code](evennia.contrib.game_systems.cooldowns)
### Contrib: `crafting`
_Contribution by Griatch 2020_
This implements a full crafting system. The principle is that of a 'recipe',
where you combine items (tagged as ingredients) create something new. The recipe can also
require certain (non-consumed) tools. An example would be to use the 'bread recipe' to
combine 'flour', 'water' and 'yeast' with an 'oven' to bake a 'loaf of bread'.
[Read the documentation](./Contrib-Crafting.md) - [Browse the Code](evennia.contrib.game_systems.crafting)
### Contrib: `gendersub`
_Contribution by 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.
[Read the documentation](./Contrib-Gendersub.md) - [Browse the Code](evennia.contrib.game_systems.gendersub)
### Contrib: `mail`
_Contribution by grungies1138 2016_
A simple Brandymail style mail system that uses the `Msg` class from Evennia
Core. It has two Commands for either sending mails between Accounts (out of game)
or between Characters (in-game). The two types of mails can be used together or
on their own.
[Read the documentation](./Contrib-Mail.md) - [Browse the Code](evennia.contrib.game_systems.mail)
### Contrib: `multidescer`
_Contribution by Griatch 2016_
A "multidescer" is a concept from the MUSH world. It allows for
creating, managing and switching between multiple character
descriptions and is a way for quickly managing your look (such as when
changing clothes) in more free-form roleplaying systems. This will also
work well together with the `rpsystem` contrib.
[Read the documentation](./Contrib-Multidescer.md) - [Browse the Code](evennia.contrib.game_systems.multidescer)
### Contrib: `puzzles`
_Contribution by Henddher 2018_
Intended for adventure-game style combination puzzles, such as combining fruits
and a blender to create a smoothie. Provides a typeclass and commands for objects
that can be combined (i.e. used together). Unlike the `crafting` contrib, each
puzzle is built from unique objects rather than using tags and a builder can create
the puzzle entirely from in-game.
[Read the documentation](./Contrib-Puzzles.md) - [Browse the Code](evennia.contrib.game_systems.puzzles)
### Contrib: `turnbattle`
_Contribution by Tim Ashley Jenkins, 2017_
This is a framework for a simple turn-based combat system, similar
to those used in D&D-style tabletop role playing games. It allows
any character to start a fight in a room, at which point initiative
is rolled and a turn order is established. Each participant in combat
has a limited time to decide their action for that turn (30 seconds by
default), and combat progresses through the turn order, looping through
the participants until the fight ends.
[Read the documentation](./Contrib-Turnbattle.md) - [Browse the Code](evennia.contrib.game_systems.turnbattle)
## grid
_Systems related to the game world's topology and structure. This has
contribs related to rooms, exits and map building._
```{toctree}
:maxdepth: 1
Contrib-Extended-Room.md
Contrib-Ingame-Map-Display.md
Contrib-Mapbuilder.md
Contrib-Simpledoor.md
Contrib-Slow-Exit.md
Contrib-Wilderness.md
Contrib-XYZGrid.md
```
### Contrib: `extended_room`
_Contribution - Griatch 2012, vincent-lg 2019_
This extends the normal `Room` typeclass to allow its description to change
with time-of-day and/or season. It also adds 'details' for the player to look at
in the room (without having to create a new in-game object for each). The room is
supported by new `look` and `desc` commands.
[Read the documentation](./Contrib-Extended-Room.md) - [Browse the Code](evennia.contrib.grid.extended_room)
### Contrib: `ingame_map_display`
_Contribution - helpme 2022_
This adds an ascii `map` to a given room which can be viewed with the `map` command.
You can easily alter it to add special characters, room colors etc. The map shown is
dynamically generated on use, and supports all compass directions and up/down. Other
directions are ignored.
[Read the documentation](./Contrib-Ingame-Map-Display.md) - [Browse the Code](evennia.contrib.grid.ingame_map_display)
### Contrib: `mapbuilder`
_Contribution by Cloud_Keeper 2016_
Build a game map from the drawing of a 2D ASCII map.
[Read the documentation](./Contrib-Mapbuilder.md) - [Browse the Code](evennia.contrib.grid.mapbuilder)
### Contrib: `simpledoor`
_Contribution by Griatch, 2016_
A simple two-way exit that represents a door that can be opened and
closed from both sides. Can easily be expanded to make it lockable,
destroyable etc.
[Read the documentation](./Contrib-Simpledoor.md) - [Browse the Code](evennia.contrib.grid.simpledoor)
### Contrib: `slow_exit`
_Contribution by Griatch 2014_
An example of an Exit-type that delays its traversal. This simulates
slow movement, common in many games. The contrib also
contains two commands, `setspeed` and `stop` for changing the movement speed
and abort an ongoing traversal, respectively.
[Read the documentation](./Contrib-Slow-Exit.md) - [Browse the Code](evennia.contrib.grid.slow_exit)
### Contrib: `wilderness`
_Contribution by titeuf87, 2017_
This contrib provides a wilderness map without actually creating a large number
of rooms - as you move, you instead end up back in the same room but its description
changes. This means you can make huge areas with little database use as
long as the rooms are relatively similar (name/desc changing).
[Read the documentation](./Contrib-Wilderness.md) - [Browse the Code](evennia.contrib.grid.wilderness)
### Contrib: `xyzgrid`
_Contribution by Griatch 2021_
Places Evennia's game world on an xy (z being different maps) coordinate grid.
Grid is created and maintained externally by drawing and parsing 2D ASCII maps,
including teleports, map transitions and special markers to aid pathfinding.
Supports very fast shortest-route pathfinding on each map. Also includes a
fast view function for seeing only a limited number of steps away from your
current location (useful for displaying the grid as an in-game, updating map).
[Read the documentation](./Contrib-XYZGrid.md) - [Browse the Code](evennia.contrib.grid.xyzgrid)
## rpg
_These are systems specifically related to roleplaying
and rule implementation like character traits, dice rolling and emoting._
```{toctree}
:maxdepth: 1
Contrib-Buffs.md
Contrib-Character-Creator.md
Contrib-Dice.md
Contrib-Health-Bar.md
Contrib-RPSystem.md
Contrib-Traits.md
```
### Contrib: `buffs`
_Contribution by Tegiminis 2022_
A buff is a timed object, attached to a game entity. It is capable of modifying values, triggering code, or both.
It is a common design pattern in RPGs, particularly action games.
[Read the documentation](./Contrib-Buffs.md) - [Browse the Code](evennia.contrib.rpg.buffs)
### Contrib: `character_creator`
_Commands for managing and initiating an in-game character-creation menu._
Contribution by InspectorCaracal, 2022
[Read the documentation](./Contrib-Character-Creator.md) - [Browse the Code](evennia.contrib.rpg.character_creator)
### Contrib: `dice`
_Contribution by Griatch, 2012_
A dice roller for any number and side of dice. Adds in-game dice rolling
(`roll 2d10 + 1`) as well as conditionals (roll under/over/equal to a target)
and functions for rolling dice in code. Command also supports hidden or secret
rolls for use by a human game master.
[Read the documentation](./Contrib-Dice.md) - [Browse the Code](evennia.contrib.rpg.dice)
### Contrib: `health_bar`
_Contribution by Tim Ashley Jenkins, 2017_
The function provided in this module lets you easily display visual
bars or meters as a colorful bar instead of just a number. A "health bar"
is merely the most obvious use for this, but the bar is highly customizable
and can be used for any sort of appropriate data besides player health.
[Read the documentation](./Contrib-Health-Bar.md) - [Browse the Code](evennia.contrib.rpg.health_bar)
### Contrib: `rpsystem`
_Contribution by Griatch, 2015_
A full roleplaying emote system. Short-descriptions and recognition (only
know people by their looks until you assign a name to them). Room poses. Masks/disguises
(hide your description). Speak directly in emote, with optional language obscuration
(words get garbled if you don't know the language, you can also have different languages
with different 'sounding' garbling). Whispers can be partly overheard from a distance. A
very powerful in-emote reference system, for referencing and differentiate targets
(including objects).
[Read the documentation](./Contrib-RPSystem.md) - [Browse the Code](evennia.contrib.rpg.rpsystem)
### Contrib: `traits`
_Contribution by Griatch 2020, based on code by Whitenoise and Ainneve contribs, 2014_
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).
[Read the documentation](./Contrib-Traits.md) - [Browse the Code](evennia.contrib.rpg.traits)
## tutorials
_Helper resources specifically meant to teach a development concept or
to exemplify an Evennia system. Any extra resources tied to documentation
tutorials are found here. Also the home of the Tutorial World demo adventure._
```{toctree}
:maxdepth: 1
Contrib-Batchprocessor.md
Contrib-Bodyfunctions.md
Contrib-Evadventure.md
Contrib-Mirror.md
Contrib-Red-Button.md
Contrib-Talking-Npc.md
Contrib-Tutorial-World.md
```
### Contrib: `batchprocessor`
_Contibution by Griatch, 2012_
Simple examples for the batch-processor. The batch processor is used for generating
in-game content from one or more static files. Files can be stored with version
control and then 'applied' to the game to create content.
[Read the documentation](./Contrib-Batchprocessor.md) - [Browse the Code](evennia.contrib.tutorials.batchprocessor)
### Contrib: `bodyfunctions`
_Contribution by Griatch, 2012_
Example script for testing. This adds a simple timer that has your
character make small verbal observations at irregular intervals.
[Read the documentation](./Contrib-Bodyfunctions.md) - [Browse the Code](evennia.contrib.tutorials.bodyfunctions)
### Contrib: `evadventure`
_Contrib by Griatch 2022_
```{warning}
NOTE - this tutorial is WIP and NOT complete! It was put on hold to focus on
releasing Evennia 1.0. You will still learn things from it, but don't expect
perfection.
```
[Read the documentation](./Contrib-Evadventure.md) - [Browse the Code](evennia.contrib.tutorials.evadventure)
### Contrib: `mirror`
_Contribution by Griatch, 2017_
A simple mirror object to experiment with. It will respond to being looked at.
[Read the documentation](./Contrib-Mirror.md) - [Browse the Code](evennia.contrib.tutorials.mirror)
### Contrib: `red_button`
_Contribution by Griatch, 2011_
A red button that you can press to have an effect. This is a more advanced example
object with its own functionality and state tracking.
[Read the documentation](./Contrib-Red-Button.md) - [Browse the Code](evennia.contrib.tutorials.red_button)
### Contrib: `talking_npc`
_Contribution by Griatch 2011. Updated by grungies1138, 2016_
This is an example of a static NPC object capable of holding a simple menu-driven
conversation. Suitable for example as a quest giver or merchant.
[Read the documentation](./Contrib-Talking-Npc.md) - [Browse the Code](evennia.contrib.tutorials.talking_npc)
### Contrib: `tutorial_world`
_Contribution by Griatch 2011, 2015_
A stand-alone tutorial area for an unmodified Evennia install.
Think of it as a sort of single-player adventure rather than a
full-fledged multi-player game world. The various rooms and objects
are designed to show off features of Evennia, not to be a
very challenging (nor long) gaming experience. As such it's of course
only skimming the surface of what is possible. Taking this apart
is a great way to start learning the system.
[Read the documentation](./Contrib-Tutorial-World.md) - [Browse the Code](evennia.contrib.tutorials.tutorial_world)
## utils
_Miscellaneous, optional tools for manipulating text, auditing connections
and more._
```{toctree}
:maxdepth: 1
Contrib-Auditing.md
Contrib-Fieldfill.md
Contrib-Git-Integration.md
Contrib-Name-Generator.md
Contrib-Random-String-Generator.md
Contrib-Tree-Select.md
```
### Contrib: `auditing`
_Contribution by Johnny, 2017_
Utility that taps and intercepts all data sent to/from clients and the
server and passes it to a callback of your choosing. This is intended for
quality assurance, post-incident investigations and debugging.
[Read the documentation](./Contrib-Auditing.md) - [Browse the Code](evennia.contrib.utils.auditing)
### Contrib: `fieldfill`
_Contribution by Tim Ashley Jenkins, 2018_
This module contains a function that generates an `EvMenu` for you - this
menu presents the player with a form of fields that can be filled
out in any order (e.g. for character generation or building). Each field's value can
be verified, with the function allowing easy checks for text and integer input,
minimum and maximum values / character lengths, or can even be verified by a custom
function. Once the form is submitted, the form's data is submitted as a dictionary
to any callable of your choice.
[Read the documentation](./Contrib-Fieldfill.md) - [Browse the Code](evennia.contrib.utils.fieldfill)
### Contrib: `git_integration`
_Contribution by helpme (2022)_
A module to integrate a stripped-down version of git within the game, allowing developers to view their git status, change branches, and pull updated code of both their local mygame repo and Evennia core. After a successful pull or checkout, the git command will reload the game: Manual restarts may be required to to apply certain changes that would impact persistent scripts etc.
[Read the documentation](./Contrib-Git-Integration.md) - [Browse the Code](evennia.contrib.utils.git_integration)
### Contrib: `name_generator`
_Contribution by InspectorCaracal (2022)_
A module for generating random names, both real-world and fantasy. Real-world
names can be generated either as first (personal) names, family (last) names, or
full names (first, optional middles, and last). The name data is from [Behind the Name](https://www.behindthename.com/)
and used under the [CC BY-SA 4.0 license](https://creativecommons.org/licenses/by-sa/4.0/).
[Read the documentation](./Contrib-Name-Generator.md) - [Browse the Code](evennia.contrib.utils.name_generator)
### Contrib: `random_string_generator`
_Contribution by Vincent Le Goff (vlgeoff), 2017_
This utility can be used to generate pseudo-random strings of information
with specific criteria. You could, for instance, use it to generate
phone numbers, license plate numbers, validation codes, in-game security
passwords and so on. The strings generated will be stored and won't be repeated.
[Read the documentation](./Contrib-Random-String-Generator.md) - [Browse the Code](evennia.contrib.utils.random_string_generator)
### Contrib: `tree_select`
_Contribution by Tim Ashley Jenkins, 2017_
This utility allows you to create and initialize an entire branching EvMenu
instance from a multi-line string passed to one function.
[Read the documentation](./Contrib-Tree-Select.md) - [Browse the Code](evennia.contrib.utils.tree_select)
----
<small>This document page is auto-generated. Manual changes
will be overwritten.</small>