diff --git a/docs/0.9.5/api/evennia.commands.default.building.html b/docs/0.9.5/api/evennia.commands.default.building.html
index 55cd43eb22..16d8fb0a56 100644
--- a/docs/0.9.5/api/evennia.commands.default.building.html
+++ b/docs/0.9.5/api/evennia.commands.default.building.html
@@ -496,7 +496,7 @@ You can specify the /force switch to bypass this confirmation.
diff --git a/docs/0.9.5/api/evennia.commands.default.general.html b/docs/0.9.5/api/evennia.commands.default.general.html
index d6d733e26e..c2aa2f5198 100644
--- a/docs/0.9.5/api/evennia.commands.default.general.html
+++ b/docs/0.9.5/api/evennia.commands.default.general.html
@@ -497,7 +497,7 @@ placing it in their inventory.
diff --git a/docs/0.9.5/api/evennia.commands.default.system.html b/docs/0.9.5/api/evennia.commands.default.system.html
index 46eb7cdf44..d9ab832005 100644
--- a/docs/0.9.5/api/evennia.commands.default.system.html
+++ b/docs/0.9.5/api/evennia.commands.default.system.html
@@ -296,7 +296,7 @@ required since whole classes of scripts often have the same name.
diff --git a/docs/0.9.5/api/evennia.contrib.barter.html b/docs/0.9.5/api/evennia.contrib.barter.html
index c0def718e9..aaa45d309f 100644
--- a/docs/0.9.5/api/evennia.contrib.barter.html
+++ b/docs/0.9.5/api/evennia.contrib.barter.html
@@ -653,7 +653,7 @@ try to influence the other part in the deal.
diff --git a/docs/0.9.5/api/evennia.contrib.dice.html b/docs/0.9.5/api/evennia.contrib.dice.html
index 6dc941e2e3..31f97ae1cb 100644
--- a/docs/0.9.5/api/evennia.contrib.dice.html
+++ b/docs/0.9.5/api/evennia.contrib.dice.html
@@ -151,7 +151,7 @@ everyone but the person rolling.
diff --git a/docs/0.9.5/api/evennia.contrib.email_login.html b/docs/0.9.5/api/evennia.contrib.email_login.html
index f315d3c8c2..5150361ef3 100644
--- a/docs/0.9.5/api/evennia.contrib.email_login.html
+++ b/docs/0.9.5/api/evennia.contrib.email_login.html
@@ -76,7 +76,7 @@ the module given by settings.CONNECTION_SCREEN_MODULE.
diff --git a/docs/0.9.5/api/evennia.contrib.turnbattle.tb_basic.html b/docs/0.9.5/api/evennia.contrib.turnbattle.tb_basic.html
index c337ec3963..4383f10009 100644
--- a/docs/0.9.5/api/evennia.contrib.turnbattle.tb_basic.html
+++ b/docs/0.9.5/api/evennia.contrib.turnbattle.tb_basic.html
@@ -562,7 +562,7 @@ if there are still any actions you can take.
diff --git a/docs/0.9.5/api/evennia.contrib.turnbattle.tb_equip.html b/docs/0.9.5/api/evennia.contrib.turnbattle.tb_equip.html
index 76ba8cab38..a9e6929a11 100644
--- a/docs/0.9.5/api/evennia.contrib.turnbattle.tb_equip.html
+++ b/docs/0.9.5/api/evennia.contrib.turnbattle.tb_equip.html
@@ -679,7 +679,7 @@ if there are still any actions you can take.
diff --git a/docs/0.9.5/api/evennia.contrib.turnbattle.tb_items.html b/docs/0.9.5/api/evennia.contrib.turnbattle.tb_items.html
index 5a993996d6..c58f087ca3 100644
--- a/docs/0.9.5/api/evennia.contrib.turnbattle.tb_items.html
+++ b/docs/0.9.5/api/evennia.contrib.turnbattle.tb_items.html
@@ -713,7 +713,7 @@ if there are still any actions you can take.
diff --git a/docs/0.9.5/api/evennia.contrib.turnbattle.tb_magic.html b/docs/0.9.5/api/evennia.contrib.turnbattle.tb_magic.html
index 9d00e31190..45e59490ce 100644
--- a/docs/0.9.5/api/evennia.contrib.turnbattle.tb_magic.html
+++ b/docs/0.9.5/api/evennia.contrib.turnbattle.tb_magic.html
@@ -585,7 +585,7 @@ if there are still any actions you can take.
diff --git a/docs/0.9.5/api/evennia.contrib.turnbattle.tb_range.html b/docs/0.9.5/api/evennia.contrib.turnbattle.tb_range.html
index bbd3dfc46f..91da9c133f 100644
--- a/docs/0.9.5/api/evennia.contrib.turnbattle.tb_range.html
+++ b/docs/0.9.5/api/evennia.contrib.turnbattle.tb_range.html
@@ -997,7 +997,7 @@ if there are still any actions you can take.
diff --git a/docs/0.9.5/api/evennia.contrib.tutorial_examples.cmdset_red_button.html b/docs/0.9.5/api/evennia.contrib.tutorial_examples.cmdset_red_button.html
index d68e87c3ec..5d23da2f25 100644
--- a/docs/0.9.5/api/evennia.contrib.tutorial_examples.cmdset_red_button.html
+++ b/docs/0.9.5/api/evennia.contrib.tutorial_examples.cmdset_red_button.html
@@ -107,7 +107,7 @@ push the lid of the button away.
diff --git a/docs/0.9.5/api/evennia.contrib.tutorial_world.objects.html b/docs/0.9.5/api/evennia.contrib.tutorial_world.objects.html
index f816c7a383..0d48ad222c 100644
--- a/docs/0.9.5/api/evennia.contrib.tutorial_world.objects.html
+++ b/docs/0.9.5/api/evennia.contrib.tutorial_world.objects.html
@@ -353,7 +353,7 @@ of the object. We overload it with our own version.
diff --git a/docs/0.9.5/api/evennia.utils.evmore.html b/docs/0.9.5/api/evennia.utils.evmore.html
index f7ab392c97..ed929be0bf 100644
--- a/docs/0.9.5/api/evennia.utils.evmore.html
+++ b/docs/0.9.5/api/evennia.utils.evmore.html
@@ -77,7 +77,7 @@ the caller.msg() construct every time the page is updated.
diff --git a/docs/1.0-dev/.buildinfo b/docs/1.0-dev/.buildinfo
index 4c06f3bc34..ddc0b608c3 100644
--- a/docs/1.0-dev/.buildinfo
+++ b/docs/1.0-dev/.buildinfo
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
-config: 291cd89ded0c18d1bdd7d7f2567033f4
+config: 8c0d15dc99128e98c4f5cd1b20a15ca9
tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/docs/1.0-dev/Howtos/Tutorial-Persistent-Handler.html b/docs/1.0-dev/Howtos/Tutorial-Persistent-Handler.html
new file mode 100644
index 0000000000..22fc11183e
--- /dev/null
+++ b/docs/1.0-dev/Howtos/Tutorial-Persistent-Handler.html
@@ -0,0 +1,302 @@
+
+
+
+
+
+
+
+
+ Making a Persistent object Handler — Evennia 1.0-dev documentation
+
+
+
+
+
+
+
+
+
+
+
+
A handler is a convenient way to group functionality on an object. This allows you to logically group all actions related to that thing in one place. This tutorial expemplifies how to make your own handlers and make sure data you store in them survives a reload.
+
For example, when you do obj.attributes.get("key") or obj.tags.add('tagname') you are evoking handlers stored as .attributes and tags on the obj. On these handlers are methods (get() and add() in this example).
What happens here is that we make a new class MyHandler. We use the @lazy_property decorator to set it up - this means the handler will not be actually created until someone really wants to use it, by accessing obj.namechange later. The decorated namechange method returns the handler and makes sure to initialize it with self - this becomes the obj inside the handler!
+
We then make a silly method add_to_key that uses the handler to manipulate the key of the object. In this example, the handler is pretty pointless, but grouping functionality this way can both make for an easy-to-remember API and can also allow you cache data for easy access - this is how the AttributeHandler (.attributes) and TagHandler (.tags) works.
Let’s say we want to track ‘quests’ in our handler. A ‘quest’ is a regular class that represents the quest. Let’s make it simple as an example:
+
# for example in mygame/world/quests.py
+
+
+classQuest:
+
+ key="The quest for the red key"
+
+ def__init__(self):
+ self.current_step="start"
+
+ defcheck_progress(self):
+ # uses self.current_step to check
+ # progress of this quest
+ getattr(self,f"step_{self.current_step}")()
+
+ defstep_start(self):
+ # check here if quest-step is complete
+ self.current_step="find_the_red_key"
+ defstep_find_the_red_key(self):
+ # check if step is complete
+ self.current_step="hand_in_quest"
+ defstep_hand_in_quest(self):
+ # check if handed in quest to quest giver
+ self.current_step=None# finished
+
+
+
+
We expect the dev to make subclasses of this to implement different quests. Exactly how this works doesn’t matter, the key is that we want to track self.current_step - a property that should survive a server reload. But so far there is no way for Quest to accomplish this, it’s just a normal Python class with no connection to the database.
Let’s make a QuestHandler that manages a character’s quests.
+
# for example in the same mygame/world/quests.py
+
+
+classQuestHandler:
+ def__init__(self,obj):
+ self.obj=obj
+ self.do_save=False
+ self._load()
+
+ def_load(self):
+ self.storage=self.obj.attributes.get(
+ "quest_storage",default={},category="quests")
+
+ def_save(self):
+ self.obj.attributes.add(
+ "quest_storage",self.storage,category="quests")
+ self._load()# important
+ self.do_save=False
+
+ defadd(self,questclass):
+ self.storage[questclass.key]=questclass(self.obj)
+ self._save()
+
+ defcheck_progress(self):
+ forquestinself.storage.values():
+ quest.check_progress()
+ ifself.do_save:
+ # .do_save is set on handler by Quest if it wants to save progress
+ self._save()
+
+
+
+
The handler is just a normal Python class and has no database-storage on its own. But it has a link to .obj, which is assumed to be a full typeclased entity, on which we can create persistent Attributes to store things however we like!
+
We make two helper methods _load and
+_save that handles local fetches and saves storage to an Attribute on the object. To avoid saving more than necessary, we have a property do_save. This we will set in Quest below.
+
+
Note that once we _save the data, we need to call _load again. This is to make sure the version we store on the handler is properly de-serialized. If you get an error about data being bytes, you probably missed this step.
The handler will save all Quest objects as a dict in an Attribute on obj. We are not done yet though, the Quest object needs access to the obj too - not only will this is important to figure out if the quest is complete (the Quest must be able to check the quester’s inventory to see if they have the red key, for example), it also allows the Quest to tell the handler when its state changed and it should be saved.
The Quest.__init__ now takes obj as argument, to match what we pass to it in QuestHandler.add. We want to monitor the changing of current_step, so we make it into a property. When we edit that value, we set the do_save flag on the handler, which means it will save the status to database once it has checked progress on all its quests.
+
The __serialize__dbobjs__ and __deserialize_dbobjs__ methods are needed because Attributes can’t store ‘hidden’ database objects (the Quest.obj property. The methods help Evennia serialize/deserialize Quest propertly when the handler saves it. For more information, see Storing Single objects in the Attributes documentation.
+
+
+
\ No newline at end of file
diff --git a/docs/1.0-dev/_modules/evennia/contrib/rpg/rpsystem/rplanguage.html b/docs/1.0-dev/_modules/evennia/contrib/rpg/rpsystem/rplanguage.html
index 78d74b7699..738e36cc42 100644
--- a/docs/1.0-dev/_modules/evennia/contrib/rpg/rpsystem/rplanguage.html
+++ b/docs/1.0-dev/_modules/evennia/contrib/rpg/rpsystem/rplanguage.html
@@ -59,110 +59,133 @@
overhear (for example "s" sounds tend to be audible even when no other meaning can be determined).
-Usage:
+## Usage
- ```python
- from evennia.contrib import rplanguage
+```python
+from evennia.contrib import rplanguage
- # need to be done once, here we create the "default" lang
- rplanguage.add_language()
+# need to be done once, here we create the "default" lang
+rplanguage.add_language()
- say = "This is me talking."
- whisper = "This is me whispering.
+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."
+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)
- <<< "..."
+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.
+## Custom languages
- Below is an example of "elvish", using "rounder" vowels and sounds:
+To set up new languages, you need to run `add_language()`
+helper function in this module. The arguments of this function (see below)
+are used to store the new language in the database (in the LanguageHandler,
+which is a type of Script).
- ```python
- # vowel/consonant grammar possibilities
- grammar = ("v vv vvc vcc vvcc cvvc vccv vvccv vcvccv vcvcvcc vvccvvcc "
- "vcvvccvvc cvcvvcvvcc vcvcvvccvcvv")
+If you want to remember the language definitions, you could put them all
+in a module along with the `add_language` call as a quick way to
+rebuild the language on a db reset:
- # all not in this group is considered a consonant
- vowels = "eaoiuy"
+```python
+# a stand-alone module somewhere under mygame. Just import this
+# once to automatically add the language!
- # 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")
+from evennia.contrib.rpg.rpsystem import rplanguage
+grammar = (...)
+vowels = "eaouy"
+# etc
- # 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
+rplanguage.add_language(grammar=grammar, vowels=vowels, ...)
+```
- # 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
+The variables of `add_language` 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. You can also add your own
+dictionary and "fix" random words for a list of input words.
- # 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"
+## Example
- # 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'}
+Below is an example module creating "elvish", using "rounder" vowels and sounds:
- 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")
+```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"
- 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".
+# 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")
- The `auto_translations` keyword is useful, this accepts either a
- list or a path to a file of words (one per line) to automatically
- create fixed translations for according to the grammatical rules.
- This allows to quickly build a large corpus of translated words
- that never change (if this is desired).
+# 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 file of words (one per line) to automatically
+create fixed translations for according to the grammatical rules.
+This allows to quickly build a large corpus of translated words
+that never change (if this is desired)."""importre
-fromrandomimportchoice,randintfromcollectionsimportdefaultdict
+fromrandomimportchoice,randint
+
fromevenniaimportDefaultScriptfromevennia.utilsimportlogger
-
# ------------------------------------------------------------## Obfuscate language
diff --git a/docs/1.0-dev/_sources/Howtos/Tutorial-Persistent-Handler.md.txt b/docs/1.0-dev/_sources/Howtos/Tutorial-Persistent-Handler.md.txt
new file mode 100644
index 0000000000..cf9bd3cc6f
--- /dev/null
+++ b/docs/1.0-dev/_sources/Howtos/Tutorial-Persistent-Handler.md.txt
@@ -0,0 +1,197 @@
+# Making a Persistent object Handler
+
+A _handler_ is a convenient way to group functionality on an object. This allows you to logically group all actions related to that thing in one place. This tutorial expemplifies how to make your own handlers and make sure data you store in them survives a reload.
+
+For example, when you do `obj.attributes.get("key")` or `obj.tags.add('tagname')` you are evoking handlers stored as `.attributes` and `tags` on the `obj`. On these handlers are methods (`get()` and `add()` in this example).
+
+## Base Handler example
+
+Here is a base way to set up an on-object handler:
+
+```python
+
+from evennia import DefaultObject, create_object
+from evennia.utils.utils import lazy_property
+
+class NameChanger:
+ def __init__(self, obj):
+ self.obj = obj
+
+ def add_to_key(self, suffix):
+ self.obj.key = f"self.obj.key_{suffix}"
+
+# make a test object
+class MyObject(DefaultObject):
+ @lazy_property:
+ def namechange(self):
+ return MyHandler(self)
+
+
+obj = create_object(MyObject, key="test")
+print(obj.key)
+>>> "test"
+obj.namechange.add_to_key("extra")
+print(obj.key)
+>>> "test_extra"
+```
+
+What happens here is that we make a new class `MyHandler`. We use the `@lazy_property` decorator to set it up - this means the handler will not be actually created until someone really wants to use it, by accessing `obj.namechange` later. The decorated `namechange` method returns the handler and makes sure to initialize it with `self` - this becomes the `obj` inside the handler!
+
+We then make a silly method `add_to_key` that uses the handler to manipulate the key of the object. In this example, the handler is pretty pointless, but grouping functionality this way can both make for an easy-to-remember API and can also allow you cache data for easy access - this is how the `AttributeHandler` (`.attributes`) and `TagHandler` (`.tags`) works.
+
+## Persistent storage of data in handler
+
+Let's say we want to track 'quests' in our handler. A 'quest' is a regular class that represents the quest. Let's make it simple as an example:
+
+```python
+# for example in mygame/world/quests.py
+
+
+class Quest:
+
+ key = "The quest for the red key"
+
+ def __init__(self):
+ self.current_step = "start"
+
+ def check_progress(self):
+ # uses self.current_step to check
+ # progress of this quest
+ getattr(self, f"step_{self.current_step}")()
+
+ def step_start(self):
+ # check here if quest-step is complete
+ self.current_step = "find_the_red_key"
+ def step_find_the_red_key(self):
+ # check if step is complete
+ self.current_step = "hand_in_quest"
+ def step_hand_in_quest(self):
+ # check if handed in quest to quest giver
+ self.current_step = None # finished
+
+```
+
+
+We expect the dev to make subclasses of this to implement different quests. Exactly how this works doesn't matter, the key is that we want to track `self.current_step` - a property that _should survive a server reload_. But so far there is no way for `Quest` to accomplish this, it's just a normal Python class with no connection to the database.
+
+### Handler with save/load capability
+
+Let's make a `QuestHandler` that manages a character's quests.
+
+```python
+# for example in the same mygame/world/quests.py
+
+
+class QuestHandler:
+ def __init__(self, obj):
+ self.obj = obj
+ self.do_save = False
+ self._load()
+
+ def _load(self):
+ self.storage = self.obj.attributes.get(
+ "quest_storage", default={}, category="quests")
+
+ def _save(self):
+ self.obj.attributes.add(
+ "quest_storage", self.storage, category="quests")
+ self._load() # important
+ self.do_save = False
+
+ def add(self, questclass):
+ self.storage[questclass.key] = questclass(self.obj)
+ self._save()
+
+ def check_progress(self):
+ for quest in self.storage.values():
+ quest.check_progress()
+ if self.do_save:
+ # .do_save is set on handler by Quest if it wants to save progress
+ self._save()
+
+```
+
+The handler is just a normal Python class and has no database-storage on its own. But it has a link to `.obj`, which is assumed to be a full typeclased entity, on which we can create persistent [Attributes](../Components/Attributes.md) to store things however we like!
+
+We make two helper methods `_load` and
+`_save` that handles local fetches and saves `storage` to an Attribute on the object. To avoid saving more than necessary, we have a property `do_save`. This we will set in `Quest` below.
+
+> Note that once we `_save` the data, we need to call `_load` again. This is to make sure the version we store on the handler is properly de-serialized. If you get an error about data being `bytes`, you probably missed this step.
+
+
+### Make quests storable
+
+The handler will save all `Quest` objects as a `dict` in an Attribute on `obj`. We are not done yet though, the `Quest` object needs access to the `obj` too - not only will this is important to figure out if the quest is complete (the `Quest` must be able to check the quester's inventory to see if they have the red key, for example), it also allows the `Quest` to tell the handler when its state changed and it should be saved.
+
+We change the `Quest` such:
+
+```python
+from evennia.utils import dbserialize
+
+
+class Quest:
+
+ def __init__(self, obj):
+ self.obj = obj
+ self._current_step = "start"
+
+ def __serialize_dbobjs__(self):
+ self.obj = dbserialize.dbserialize(self.obj)
+
+ def __deserialize_dbobjs__(self):
+ if isinstance(self.obj, bytes):
+ self.obj = dbserialize.dbunserialize(self.obj)
+
+ @property
+ def questhandler(self):
+ return self.obj.quests
+
+ @property
+ def current_step(self):
+ return self._current_step
+
+ @current_step.setter
+ def current_step(self, value):
+ self._current_step = value
+ self.questhandler.do_save = True # this triggers save in handler!
+
+ # [same as before]
+
+```
+
+The `Quest.__init__` now takes `obj` as argument, to match what we pass to it in `QuestHandler.add`. We want to monitor the changing of `current_step`, so we make it into a `property`. When we edit that value, we set the `do_save` flag on the handler, which means it will save the status to database once it has checked progress on all its quests.
+
+The `__serialize__dbobjs__` and `__deserialize_dbobjs__` methods are needed because `Attributes` can't store 'hidden' database objects (the `Quest.obj` property. The methods help Evennia serialize/deserialize `Quest` propertly when the handler saves it. For more information, see [Storing Single objects](../Components/Attributes.md#storing-single-objects) in the Attributes documentation.
+
+### Tying it all together
+
+The final thing we need to do is to add the quest-handler to the character:
+
+```python
+# in mygame/typeclasses/characters.py
+
+from evennia import DefaultCharacter
+from evennia.utils.utils import lazy_property
+from .world.quests import QuestHandler # as an example
+
+
+class Character(DefaultCharacter):
+ # ...
+ @lazy_property
+ def quests(self):
+ return QuestHandler(self)
+
+```
+
+
+You can now make your Quest classes to describe your quests and add them to characters with
+
+ character.quests.add(FindTheRedKey)
+
+and can later do
+
+ character.quests.check_progress()
+
+and be sure that quest data is not lost between reloads.
+
+You can find a full-fledged quest-handler example as [EvAdventure quests](evennia.contribs.tutorials.evadventure.quests) contrib in the Evennia repository.
\ No newline at end of file
diff --git a/docs/1.0-dev/api/evennia-api.html b/docs/1.0-dev/api/evennia-api.html
index b58a7c3cf4..3c5cefdad9 100644
--- a/docs/1.0-dev/api/evennia-api.html
+++ b/docs/1.0-dev/api/evennia-api.html
@@ -319,7 +319,12 @@
-search_index_entry = {'aliases': 'remit pemit', 'category': 'admin', 'key': 'emit', 'no_prefix': ' remit pemit', 'tags': '', 'text': '\n admin command for emitting message to multiple objects\n\n Usage:\n emit[/switches] [<obj>, <obj>, ... =] <message>\n remit [<obj>, <obj>, ... =] <message>\n pemit [<obj>, <obj>, ... =] <message>\n\n Switches:\n room - limit emits to rooms only (default)\n accounts - limit emits to accounts only\n contents - send to the contents of matched objects too\n\n Emits a message to the selected objects or to\n your immediate surroundings. If the object is a room,\n send to its contents. remit and pemit are just\n limited forms of emit, for sending to rooms and\n to accounts respectively.\n '}¶
+search_index_entry = {'aliases': 'pemit remit', 'category': 'admin', 'key': 'emit', 'no_prefix': ' pemit remit', 'tags': '', 'text': '\n admin command for emitting message to multiple objects\n\n Usage:\n emit[/switches] [<obj>, <obj>, ... =] <message>\n remit [<obj>, <obj>, ... =] <message>\n pemit [<obj>, <obj>, ... =] <message>\n\n Switches:\n room - limit emits to rooms only (default)\n accounts - limit emits to accounts only\n contents - send to the contents of matched objects too\n\n Emits a message to the selected objects or to\n your immediate surroundings. If the object is a room,\n send to its contents. remit and pemit are just\n limited forms of emit, for sending to rooms and\n to accounts respectively.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.commands.default.batchprocess.html b/docs/1.0-dev/api/evennia.commands.default.batchprocess.html
index 63c99c3dbb..66715808ac 100644
--- a/docs/1.0-dev/api/evennia.commands.default.batchprocess.html
+++ b/docs/1.0-dev/api/evennia.commands.default.batchprocess.html
@@ -89,7 +89,7 @@ skipping, reloading etc.
-search_index_entry = {'aliases': 'batchcmd batchcommand', 'category': 'building', 'key': 'batchcommands', 'no_prefix': ' batchcmd batchcommand', 'tags': '', 'text': '\n build from batch-command file\n\n Usage:\n batchcommands[/interactive] <python.path.to.file>\n\n Switch:\n interactive - this mode will offer more control when\n executing the batch file, like stepping,\n skipping, reloading etc.\n\n Runs batches of commands from a batch-cmd text file (*.ev).\n\n '}¶
+search_index_entry = {'aliases': 'batchcommand batchcmd', 'category': 'building', 'key': 'batchcommands', 'no_prefix': ' batchcommand batchcmd', 'tags': '', 'text': '\n build from batch-command file\n\n Usage:\n batchcommands[/interactive] <python.path.to.file>\n\n Switch:\n interactive - this mode will offer more control when\n executing the batch file, like stepping,\n skipping, reloading etc.\n\n Runs batches of commands from a batch-cmd text file (*.ev).\n\n '}¶
diff --git a/docs/1.0-dev/api/evennia.commands.default.building.html b/docs/1.0-dev/api/evennia.commands.default.building.html
index b5e413a34f..8dedda8a0d 100644
--- a/docs/1.0-dev/api/evennia.commands.default.building.html
+++ b/docs/1.0-dev/api/evennia.commands.default.building.html
@@ -543,7 +543,7 @@ You can specify the /force switch to bypass this confirmation.
@@ -584,7 +584,7 @@ You can specify the /force switch to bypass this confirmation.
-search_index_entry = {'aliases': '@del @delete', 'category': 'building', 'key': '@destroy', 'no_prefix': 'destroy del delete', 'tags': '', 'text': '\n permanently delete objects\n\n Usage:\n destroy[/switches] [obj, obj2, obj3, [dbref-dbref], ...]\n\n Switches:\n override - The destroy command will usually avoid accidentally\n destroying account objects. This switch overrides this safety.\n force - destroy without confirmation.\n Examples:\n destroy house, roof, door, 44-78\n destroy 5-10, flower, 45\n destroy/force north\n\n Destroys one or many objects. If dbrefs are used, a range to delete can be\n given, e.g. 4-10. Also the end points will be deleted. This command\n displays a confirmation before destroying, to make sure of your choice.\n You can specify the /force switch to bypass this confirmation.\n '}¶
+search_index_entry = {'aliases': '@delete @del', 'category': 'building', 'key': '@destroy', 'no_prefix': 'destroy delete del', 'tags': '', 'text': '\n permanently delete objects\n\n Usage:\n destroy[/switches] [obj, obj2, obj3, [dbref-dbref], ...]\n\n Switches:\n override - The destroy command will usually avoid accidentally\n destroying account objects. This switch overrides this safety.\n force - destroy without confirmation.\n Examples:\n destroy house, roof, door, 44-78\n destroy 5-10, flower, 45\n destroy/force north\n\n Destroys one or many objects. If dbrefs are used, a range to delete can be\n given, e.g. 4-10. Also the end points will be deleted. This command\n displays a confirmation before destroying, to make sure of your choice.\n You can specify the /force switch to bypass this confirmation.\n '}¶
-search_index_entry = {'aliases': '@swap @parent @typeclasses @update @type', 'category': 'building', 'key': '@typeclass', 'no_prefix': 'typeclass swap parent typeclasses update type', 'tags': '', 'text': "\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] <object> [= typeclass.path]\n typeclass/prototype <object> = prototype_key\n\n typeclasses or typeclass/list/show [typeclass.path]\n swap - this is a shorthand for using /force/reset flags.\n update - this is a shorthand for using the /force/reload flag.\n\n Switch:\n show, examine - display the current typeclass of object (default) or, if\n given a typeclass path, show the docstring of that typeclass.\n update - *only* re-run at_object_creation on this object\n meaning locks or other properties set later may remain.\n reset - clean out *all* the attributes and properties on the\n object - basically making this a new clean object. This will also\n reset cmdsets!\n force - change to the typeclass also if the object\n already has a typeclass of the same name.\n list - show available typeclasses. Only typeclasses in modules actually\n imported or used from somewhere in the code will show up here\n (those typeclasses are still available if you know the path)\n prototype - clean and overwrite the object with the specified\n prototype key - effectively making a whole new object.\n\n Example:\n type button = examples.red_button.RedButton\n type/prototype button=a red button\n\n If the typeclass_path is not given, the current object's typeclass is\n assumed.\n\n View or set an object's typeclass. If setting, the creation hooks of the\n new typeclass will be run on the object. If you have clashing properties on\n the old class, use /reset. By default you are protected from changing to a\n typeclass of the same name as the one you already have - use /force to\n override this protection.\n\n The given typeclass must be identified by its location using python\n dot-notation pointing to the correct module and class. If no typeclass is\n given (or a wrong typeclass is given). Errors in the path or new typeclass\n will lead to the old typeclass being kept. The location of the typeclass\n module is searched from the default typeclass directory, as defined in the\n server settings.\n\n "}¶
+search_index_entry = {'aliases': '@parent @swap @type @typeclasses @update', 'category': 'building', 'key': '@typeclass', 'no_prefix': 'typeclass parent swap type typeclasses update', 'tags': '', 'text': "\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] <object> [= typeclass.path]\n typeclass/prototype <object> = prototype_key\n\n typeclasses or typeclass/list/show [typeclass.path]\n swap - this is a shorthand for using /force/reset flags.\n update - this is a shorthand for using the /force/reload flag.\n\n Switch:\n show, examine - display the current typeclass of object (default) or, if\n given a typeclass path, show the docstring of that typeclass.\n update - *only* re-run at_object_creation on this object\n meaning locks or other properties set later may remain.\n reset - clean out *all* the attributes and properties on the\n object - basically making this a new clean object. This will also\n reset cmdsets!\n force - change to the typeclass also if the object\n already has a typeclass of the same name.\n list - show available typeclasses. Only typeclasses in modules actually\n imported or used from somewhere in the code will show up here\n (those typeclasses are still available if you know the path)\n prototype - clean and overwrite the object with the specified\n prototype key - effectively making a whole new object.\n\n Example:\n type button = examples.red_button.RedButton\n type/prototype button=a red button\n\n If the typeclass_path is not given, the current object's typeclass is\n assumed.\n\n View or set an object's typeclass. If setting, the creation hooks of the\n new typeclass will be run on the object. If you have clashing properties on\n the old class, use /reset. By default you are protected from changing to a\n typeclass of the same name as the one you already have - use /force to\n override this protection.\n\n The given typeclass must be identified by its location using python\n dot-notation pointing to the correct module and class. If no typeclass is\n given (or a wrong typeclass is given). Errors in the path or new typeclass\n will lead to the old typeclass being kept. The location of the typeclass\n module is searched from the default typeclass directory, as defined in the\n server settings.\n\n "}¶
@@ -1482,7 +1482,7 @@ If object is not specified, the current location is examined.
@@ -1750,7 +1750,7 @@ the cases, see the module doc.
-search_index_entry = {'aliases': '@exam @ex', 'category': 'building', 'key': '@examine', 'no_prefix': 'examine exam ex', 'tags': '', 'text': '\n get detailed information about an object\n\n Usage:\n examine [<object>[/attrname]]\n examine [*<account>[/attrname]]\n\n Switch:\n account - examine an Account (same as adding *)\n object - examine an Object (useful when OOC)\n script - examine a Script\n channel - examine a Channel\n\n The examine command shows detailed game info about an\n object and optionally a specific attribute on it.\n If object is not specified, the current location is examined.\n\n Append a * before the search string to examine an account.\n\n '}¶
+search_index_entry = {'aliases': '@ex @exam', 'category': 'building', 'key': '@examine', 'no_prefix': 'examine ex exam', 'tags': '', 'text': '\n get detailed information about an object\n\n Usage:\n examine [<object>[/attrname]]\n examine [*<account>[/attrname]]\n\n Switch:\n account - examine an Account (same as adding *)\n object - examine an Object (useful when OOC)\n script - examine a Script\n channel - examine a Channel\n\n The examine command shows detailed game info about an\n object and optionally a specific attribute on it.\n If object is not specified, the current location is examined.\n\n Append a * before the search string to examine an account.\n\n '}¶
diff --git a/docs/1.0-dev/api/evennia.commands.default.general.html b/docs/1.0-dev/api/evennia.commands.default.general.html
index b904f34aef..33fd31390b 100644
--- a/docs/1.0-dev/api/evennia.commands.default.general.html
+++ b/docs/1.0-dev/api/evennia.commands.default.general.html
@@ -219,7 +219,7 @@ for everyone to use, you need build privileges and the alias command.
@@ -251,7 +251,7 @@ for everyone to use, you need build privileges and the alias command.
-search_index_entry = {'aliases': 'nicks nickname', 'category': 'general', 'key': 'nick', 'no_prefix': ' nicks nickname', 'tags': '', 'text': '\n define a personal alias/nick by defining a string to\n match and replace it with another on the fly\n\n Usage:\n nick[/switches] <string> [= [replacement_string]]\n nick[/switches] <template> = <replacement_template>\n nick/delete <string> or number\n nicks\n\n Switches:\n inputline - replace on the inputline (default)\n object - replace on object-lookup\n account - replace on account-lookup\n list - show all defined aliases (also "nicks" works)\n delete - remove nick by index in /list\n clearall - clear all nicks\n\n Examples:\n nick hi = say Hello, I\'m Sarah!\n nick/object tom = the tall man\n nick build $1 $2 = create/drop $1;$2\n nick tell $1 $2=page $1=$2\n nick tm?$1=page tallman=$1\n nick tm\\=$1=page tallman=$1\n\n A \'nick\' is a personal string replacement. Use $1, $2, ... to catch arguments.\n Put the last $-marker without an ending space to catch all remaining text. You\n can also use unix-glob matching for the left-hand side <string>:\n\n * - matches everything\n ? - matches 0 or 1 single characters\n [abcd] - matches these chars in any order\n [!abcd] - matches everything not among these chars\n \\= - escape literal \'=\' you want in your <string>\n\n Note that no objects are actually renamed or changed by this command - your nicks\n are only available to you. If you want to permanently add keywords to an object\n for everyone to use, you need build privileges and the alias command.\n\n '}¶
+search_index_entry = {'aliases': 'nickname nicks', 'category': 'general', 'key': 'nick', 'no_prefix': ' nickname nicks', 'tags': '', 'text': '\n define a personal alias/nick by defining a string to\n match and replace it with another on the fly\n\n Usage:\n nick[/switches] <string> [= [replacement_string]]\n nick[/switches] <template> = <replacement_template>\n nick/delete <string> or number\n nicks\n\n Switches:\n inputline - replace on the inputline (default)\n object - replace on object-lookup\n account - replace on account-lookup\n list - show all defined aliases (also "nicks" works)\n delete - remove nick by index in /list\n clearall - clear all nicks\n\n Examples:\n nick hi = say Hello, I\'m Sarah!\n nick/object tom = the tall man\n nick build $1 $2 = create/drop $1;$2\n nick tell $1 $2=page $1=$2\n nick tm?$1=page tallman=$1\n nick tm\\=$1=page tallman=$1\n\n A \'nick\' is a personal string replacement. Use $1, $2, ... to catch arguments.\n Put the last $-marker without an ending space to catch all remaining text. You\n can also use unix-glob matching for the left-hand side <string>:\n\n * - matches everything\n ? - matches 0 or 1 single characters\n [abcd] - matches these chars in any order\n [!abcd] - matches everything not among these chars\n \\= - escape literal \'=\' you want in your <string>\n\n Note that no objects are actually renamed or changed by this command - your nicks\n are only available to you. If you want to permanently add keywords to an object\n for everyone to use, you need build privileges and the alias command.\n\n '}¶
@@ -580,7 +580,7 @@ placing it in their inventory.
-search_index_entry = {'aliases': '" \'', 'category': 'general', 'key': 'say', 'no_prefix': ' " \'', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say <message>\n\n Talk to those in your current location.\n '}¶
+search_index_entry = {'aliases': '\' "', 'category': 'general', 'key': 'say', 'no_prefix': ' \' "', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say <message>\n\n Talk to those in your current location.\n '}¶
@@ -660,7 +660,7 @@ automatically begin with your name.
-search_index_entry = {'aliases': 'emote :', 'category': 'general', 'key': 'pose', 'no_prefix': ' emote :', 'tags': '', 'text': "\n strike a pose\n\n Usage:\n pose <pose text>\n pose's <pose text>\n\n Example:\n pose is standing by the wall, smiling.\n -> others will see:\n Tom is standing by the wall, smiling.\n\n Describe an action being taken. The pose text will\n automatically begin with your name.\n "}¶
+search_index_entry = {'aliases': ': emote', 'category': 'general', 'key': 'pose', 'no_prefix': ' : emote', 'tags': '', 'text': "\n strike a pose\n\n Usage:\n pose <pose text>\n pose's <pose text>\n\n Example:\n pose is standing by the wall, smiling.\n -> others will see:\n Tom is standing by the wall, smiling.\n\n Describe an action being taken. The pose text will\n automatically begin with your name.\n "}¶
@@ -724,7 +724,7 @@ which permission groups you are a member of.
@@ -755,7 +755,7 @@ which permission groups you are a member of.
-search_index_entry = {'aliases': 'hierarchy groups', 'category': 'general', 'key': 'access', 'no_prefix': ' hierarchy groups', 'tags': '', 'text': '\n show your current game access\n\n Usage:\n access\n\n This command shows you the permission hierarchy and\n which permission groups you are a member of.\n '}¶
+search_index_entry = {'aliases': 'groups hierarchy', 'category': 'general', 'key': 'access', 'no_prefix': ' groups hierarchy', 'tags': '', 'text': '\n show your current game access\n\n Usage:\n access\n\n This command shows you the permission hierarchy and\n which permission groups you are a member of.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.commands.default.tests.html b/docs/1.0-dev/api/evennia.commands.default.tests.html
index fac04ed30b..8429bbbe3b 100644
--- a/docs/1.0-dev/api/evennia.commands.default.tests.html
+++ b/docs/1.0-dev/api/evennia.commands.default.tests.html
@@ -772,7 +772,7 @@ main test suite started with
Test the batch processor.
-red_button = <module 'evennia.contrib.tutorials.red_button.red_button' from '/tmp/tmpls5hunzf/3f0e630003db86bf45cb61c9287700e0e493fd7e/evennia/contrib/tutorials/red_button/red_button.py'>¶
+red_button = <module 'evennia.contrib.tutorials.red_button.red_button' from '/tmp/tmpfcncuox1/aeab9950617728782ae2a0a12dc4938c480eca87/evennia/contrib/tutorials/red_button/red_button.py'>¶
@@ -108,7 +108,7 @@ there is no object yet before the account has logged in)
-search_index_entry = {'aliases': 'con co conn', 'category': 'general', 'key': 'connect', 'no_prefix': ' con co conn', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect "account name" "pass word"\n\n Use the create command to first create an account before logging in.\n\n If you have spaces in your name, enclose it in double quotes.\n '}¶
+search_index_entry = {'aliases': 'conn con co', 'category': 'general', 'key': 'connect', 'no_prefix': ' conn con co', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect "account name" "pass word"\n\n Use the create command to first create an account before logging in.\n\n If you have spaces in your name, enclose it in double quotes.\n '}¶
-search_index_entry = {'aliases': 'cr cre', 'category': 'general', 'key': 'create', 'no_prefix': ' cr cre', 'tags': '', 'text': '\n create a new account account\n\n Usage (at login screen):\n create <accountname> <password>\n create "account name" "pass word"\n\n This creates a new account account.\n\n If you have spaces in your name, enclose it in double quotes.\n '}¶
+search_index_entry = {'aliases': 'cre cr', 'category': 'general', 'key': 'create', 'no_prefix': ' cre cr', 'tags': '', 'text': '\n create a new account account\n\n Usage (at login screen):\n create <accountname> <password>\n create "account name" "pass word"\n\n This creates a new account account.\n\n If you have spaces in your name, enclose it in double quotes.\n '}¶
@@ -187,7 +187,7 @@ version is a bit more complicated.
@@ -213,7 +213,7 @@ version is a bit more complicated.
-search_index_entry = {'aliases': 'qu q', 'category': 'general', 'key': 'quit', 'no_prefix': ' qu q', 'tags': '', 'text': '\n quit when in unlogged-in state\n\n Usage:\n quit\n\n We maintain a different version of the quit command\n here for unconnected accounts for the sake of simplicity. The logged in\n version is a bit more complicated.\n '}¶
+search_index_entry = {'aliases': 'q qu', 'category': 'general', 'key': 'quit', 'no_prefix': ' q qu', 'tags': '', 'text': '\n quit when in unlogged-in state\n\n Usage:\n quit\n\n We maintain a different version of the quit command\n here for unconnected accounts for the sake of simplicity. The logged in\n version is a bit more complicated.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.base_systems.email_login.email_login.html b/docs/1.0-dev/api/evennia.contrib.base_systems.email_login.email_login.html
index ac01996c82..3871cfbf5a 100644
--- a/docs/1.0-dev/api/evennia.contrib.base_systems.email_login.email_login.html
+++ b/docs/1.0-dev/api/evennia.contrib.base_systems.email_login.email_login.html
@@ -90,7 +90,7 @@ the module given by settings.CONNECTION_SCREEN_MODULE.
@@ -120,7 +120,7 @@ there is no object yet before the account has logged in)
-search_index_entry = {'aliases': 'con co conn', 'category': 'general', 'key': 'connect', 'no_prefix': ' con co conn', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect <email> <password>\n\n Use the create command to first create an account before logging in.\n '}¶
+search_index_entry = {'aliases': 'conn con co', 'category': 'general', 'key': 'connect', 'no_prefix': ' conn con co', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect <email> <password>\n\n Use the create command to first create an account before logging in.\n '}¶
@@ -142,7 +142,7 @@ there is no object yet before the account has logged in)
@@ -223,7 +223,7 @@ version is a bit more complicated.
-search_index_entry = {'aliases': 'qu q', 'category': 'general', 'key': 'quit', 'no_prefix': ' qu q', 'tags': '', 'text': '\n We maintain a different version of the `quit` command\n here for unconnected accounts for the sake of simplicity. The logged in\n version is a bit more complicated.\n '}¶
+search_index_entry = {'aliases': 'q qu', 'category': 'general', 'key': 'quit', 'no_prefix': ' q qu', 'tags': '', 'text': '\n We maintain a different version of the `quit` command\n here for unconnected accounts for the sake of simplicity. The logged in\n version is a bit more complicated.\n '}¶
@@ -142,7 +142,7 @@ aliases to an already joined channel.
-search_index_entry = {'aliases': 'aliaschan chanalias', 'category': 'comms', 'key': 'addcom', 'no_prefix': ' aliaschan chanalias', 'tags': '', 'text': '\n Add a channel alias and/or subscribe to a channel\n\n Usage:\n addcom [alias=] <channel>\n\n Joins a given channel. If alias is given, this will allow you to\n refer to the channel by this alias rather than the full channel\n name. Subsequent calls of this command can be used to add multiple\n aliases to an already joined channel.\n '}¶
+search_index_entry = {'aliases': 'chanalias aliaschan', 'category': 'comms', 'key': 'addcom', 'no_prefix': ' chanalias aliaschan', 'tags': '', 'text': '\n Add a channel alias and/or subscribe to a channel\n\n Usage:\n addcom [alias=] <channel>\n\n Joins a given channel. If alias is given, this will allow you to\n refer to the channel by this alias rather than the full channel\n name. Subsequent calls of this command can be used to add multiple\n aliases to an already joined channel.\n '}¶
-search_index_entry = {'aliases': 'delchanalias delaliaschan', 'category': 'comms', 'key': 'delcom', 'no_prefix': ' delchanalias delaliaschan', 'tags': '', 'text': "\n remove a channel alias and/or unsubscribe from channel\n\n Usage:\n delcom <alias or channel>\n delcom/all <channel>\n\n If the full channel name is given, unsubscribe from the\n channel. If an alias is given, remove the alias but don't\n unsubscribe. If the 'all' switch is used, remove all aliases\n for that channel.\n "}¶
+search_index_entry = {'aliases': 'delaliaschan delchanalias', 'category': 'comms', 'key': 'delcom', 'no_prefix': ' delaliaschan delchanalias', 'tags': '', 'text': "\n remove a channel alias and/or unsubscribe from channel\n\n Usage:\n delcom <alias or channel>\n delcom/all <channel>\n\n If the full channel name is given, unsubscribe from the\n channel. If an alias is given, remove the alias but don't\n unsubscribe. If the 'all' switch is used, remove all aliases\n for that channel.\n "}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.full_systems.evscaperoom.commands.html b/docs/1.0-dev/api/evennia.contrib.full_systems.evscaperoom.commands.html
index da1890d1d0..cb0a573787 100644
--- a/docs/1.0-dev/api/evennia.contrib.full_systems.evscaperoom.commands.html
+++ b/docs/1.0-dev/api/evennia.contrib.full_systems.evscaperoom.commands.html
@@ -162,7 +162,7 @@ the operation will be general or on the room.
-search_index_entry = {'aliases': 'abort q chicken out quit', 'category': 'evscaperoom', 'key': 'give up', 'no_prefix': ' abort q chicken out quit', 'tags': '', 'text': '\n Give up\n\n Usage:\n give up\n\n Abandons your attempts at escaping and of ever winning the pie-eating contest.\n\n '}¶
+search_index_entry = {'aliases': 'abort chicken out q quit', 'category': 'evscaperoom', 'key': 'give up', 'no_prefix': ' abort chicken out q quit', 'tags': '', 'text': '\n Give up\n\n Usage:\n give up\n\n Abandons your attempts at escaping and of ever winning the pie-eating contest.\n\n '}¶
-search_index_entry = {'aliases': 'pose :', 'category': 'general', 'key': 'emote', 'no_prefix': ' pose :', 'tags': '', 'text': '\n Perform a free-form emote. Use /me to\n include yourself in the emote and /name\n to include other objects or characters.\n Use "..." to enact speech.\n\n Usage:\n emote <emote>\n :<emote\n\n Example:\n emote /me smiles at /peter\n emote /me points to /box and /lever.\n\n '}¶
+search_index_entry = {'aliases': ': pose', 'category': 'general', 'key': 'emote', 'no_prefix': ' : pose', 'tags': '', 'text': '\n Perform a free-form emote. Use /me to\n include yourself in the emote and /name\n to include other objects or characters.\n Use "..." to enact speech.\n\n Usage:\n emote <emote>\n :<emote\n\n Example:\n emote /me smiles at /peter\n emote /me points to /box and /lever.\n\n '}¶
@@ -441,7 +441,7 @@ looks and what actions is available.
-search_index_entry = {'aliases': 'unfocus ex e examine', 'category': 'evscaperoom', 'key': 'focus', 'no_prefix': ' unfocus ex e examine', 'tags': '', 'text': '\n Focus your attention on a target.\n\n Usage:\n focus <obj>\n\n Once focusing on an object, use look to get more information about how it\n looks and what actions is available.\n\n '}¶
+search_index_entry = {'aliases': 'examine ex e unfocus', 'category': 'evscaperoom', 'key': 'focus', 'no_prefix': ' examine ex e unfocus', 'tags': '', 'text': '\n Focus your attention on a target.\n\n Usage:\n focus <obj>\n\n Once focusing on an object, use look to get more information about how it\n looks and what actions is available.\n\n '}¶
@@ -600,7 +600,7 @@ to all the variables defined therein.
-search_index_entry = {'aliases': '@dig @open', 'category': 'general', 'key': 'open', 'no_prefix': ' dig open', 'tags': '', 'text': '\n Interact with an object in focus.\n\n Usage:\n <action> [arg]\n\n '}¶
+search_index_entry = {'aliases': '@open @dig', 'category': 'general', 'key': 'open', 'no_prefix': ' open dig', 'tags': '', 'text': '\n Interact with an object in focus.\n\n Usage:\n <action> [arg]\n\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.game_systems.barter.barter.html b/docs/1.0-dev/api/evennia.contrib.game_systems.barter.barter.html
index 1da1e06dc1..86c44920d2 100644
--- a/docs/1.0-dev/api/evennia.contrib.game_systems.barter.barter.html
+++ b/docs/1.0-dev/api/evennia.contrib.game_systems.barter.barter.html
@@ -696,7 +696,7 @@ try to influence the other part in the deal.
@@ -722,7 +722,7 @@ try to influence the other part in the deal.
-search_index_entry = {'aliases': 'deal offers', 'category': 'trading', 'key': 'status', 'no_prefix': ' deal offers', 'tags': '', 'text': "\n show a list of the current deal\n\n Usage:\n status\n deal\n offers\n\n Shows the currently suggested offers on each sides of the deal. To\n accept the current deal, use the 'accept' command. Use 'offer' to\n change your deal. You might also want to use 'say', 'emote' etc to\n try to influence the other part in the deal.\n "}¶
+search_index_entry = {'aliases': 'offers deal', 'category': 'trading', 'key': 'status', 'no_prefix': ' offers deal', 'tags': '', 'text': "\n show a list of the current deal\n\n Usage:\n status\n deal\n offers\n\n Shows the currently suggested offers on each sides of the deal. To\n accept the current deal, use the 'accept' command. Use 'offer' to\n change your deal. You might also want to use 'say', 'emote' etc to\n try to influence the other part in the deal.\n "}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_basic.html b/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_basic.html
index a0cfe3814c..8556688b9e 100644
--- a/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_basic.html
+++ b/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_basic.html
@@ -623,7 +623,7 @@ if there are still any actions you can take.
@@ -649,7 +649,7 @@ if there are still any actions you can take.
-search_index_entry = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'no_prefix': ' hold wait', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}¶
+search_index_entry = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'no_prefix': ' wait hold', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_equip.html b/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_equip.html
index 4917940612..ea6c9eab80 100644
--- a/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_equip.html
+++ b/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_equip.html
@@ -518,7 +518,7 @@ if there are still any actions you can take.
@@ -538,7 +538,7 @@ if there are still any actions you can take.
-search_index_entry = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'no_prefix': ' hold wait', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}¶
+search_index_entry = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'no_prefix': ' wait hold', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_items.html b/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_items.html
index 7f0b2673e0..d352f4e704 100644
--- a/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_items.html
+++ b/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_items.html
@@ -641,7 +641,7 @@ if there are still any actions you can take.
@@ -661,7 +661,7 @@ if there are still any actions you can take.
-search_index_entry = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'no_prefix': ' hold wait', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}¶
+search_index_entry = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'no_prefix': ' wait hold', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_magic.html b/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_magic.html
index 1fbdf077da..069c94c28f 100644
--- a/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_magic.html
+++ b/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_magic.html
@@ -420,7 +420,7 @@ if there are still any actions you can take.
@@ -440,7 +440,7 @@ if there are still any actions you can take.
-search_index_entry = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'no_prefix': ' hold wait', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}¶
+search_index_entry = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'no_prefix': ' wait hold', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_range.html b/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_range.html
index 66421ba3be..47fe6be620 100644
--- a/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_range.html
+++ b/docs/1.0-dev/api/evennia.contrib.game_systems.turnbattle.tb_range.html
@@ -880,7 +880,7 @@ if there are still any actions you can take.
@@ -900,7 +900,7 @@ if there are still any actions you can take.
-search_index_entry = {'aliases': 'hold wait', 'category': 'combat', 'key': 'pass', 'no_prefix': ' hold wait', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}¶
+search_index_entry = {'aliases': 'wait hold', 'category': 'combat', 'key': 'pass', 'no_prefix': ' wait hold', 'tags': '', 'text': '\n Passes on your turn.\n\n Usage:\n pass\n\n When in a fight, you can use this command to end your turn early, even\n if there are still any actions you can take.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.html b/docs/1.0-dev/api/evennia.contrib.html
index 2aeb77a06e..31e8d432a3 100644
--- a/docs/1.0-dev/api/evennia.contrib.html
+++ b/docs/1.0-dev/api/evennia.contrib.html
@@ -290,7 +290,12 @@ useful but are deemed too game-specific to go into the core library.
diff --git a/docs/1.0-dev/api/evennia.contrib.rpg.dice.dice.html b/docs/1.0-dev/api/evennia.contrib.rpg.dice.dice.html
index 426cf798dd..113012046b 100644
--- a/docs/1.0-dev/api/evennia.contrib.rpg.dice.dice.html
+++ b/docs/1.0-dev/api/evennia.contrib.rpg.dice.dice.html
@@ -246,7 +246,7 @@ everyone but the person rolling.
@@ -272,7 +272,7 @@ everyone but the person rolling.
-search_index_entry = {'aliases': '@dice roll', 'category': 'general', 'key': 'dice', 'no_prefix': ' dice roll', 'tags': '', 'text': "\n roll dice\n\n Usage:\n dice[/switch] <nr>d<sides> [modifier] [success condition]\n\n Switch:\n hidden - tell the room the roll is being done, but don't show the result\n secret - don't inform the room about neither roll nor result\n\n Examples:\n dice 3d6 + 4\n dice 1d100 - 2 < 50\n\n This will roll the given number of dice with given sides and modifiers.\n So e.g. 2d6 + 3 means to 'roll a 6-sided die 2 times and add the result,\n then add 3 to the total'.\n Accepted modifiers are +, -, * and /.\n A success condition is given as normal Python conditionals\n (<,>,<=,>=,==,!=). So e.g. 2d6 + 3 > 10 means that the roll will succeed\n only if the final result is above 8. If a success condition is given, the\n outcome (pass/fail) will be echoed along with how much it succeeded/failed\n with. The hidden/secret switches will hide all or parts of the roll from\n everyone but the person rolling.\n "}¶
+search_index_entry = {'aliases': 'roll @dice', 'category': 'general', 'key': 'dice', 'no_prefix': ' roll dice', 'tags': '', 'text': "\n roll dice\n\n Usage:\n dice[/switch] <nr>d<sides> [modifier] [success condition]\n\n Switch:\n hidden - tell the room the roll is being done, but don't show the result\n secret - don't inform the room about neither roll nor result\n\n Examples:\n dice 3d6 + 4\n dice 1d100 - 2 < 50\n\n This will roll the given number of dice with given sides and modifiers.\n So e.g. 2d6 + 3 means to 'roll a 6-sided die 2 times and add the result,\n then add 3 to the total'.\n Accepted modifiers are +, -, * and /.\n A success condition is given as normal Python conditionals\n (<,>,<=,>=,==,!=). So e.g. 2d6 + 3 > 10 means that the roll will succeed\n only if the final result is above 8. If a success condition is given, the\n outcome (pass/fail) will be echoed along with how much it succeeded/failed\n with. The hidden/secret switches will hide all or parts of the roll from\n everyone but the person rolling.\n "}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.rpg.rpsystem.rplanguage.html b/docs/1.0-dev/api/evennia.contrib.rpg.rpsystem.rplanguage.html
index adaddfd835..2f10b4f406 100644
--- a/docs/1.0-dev/api/evennia.contrib.rpg.rpsystem.rplanguage.html
+++ b/docs/1.0-dev/api/evennia.contrib.rpg.rpsystem.rplanguage.html
@@ -76,9 +76,9 @@ meaning can be determined).
-
fromevennia.contribimportrplanguage# need to be done once, here we create the "default" langrplanguage.add_language()
@@ -105,14 +105,36 @@ meaning can be determined).
<<<"..."
-
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
+
To set up new languages, you need to run add_language()
+helper function in this module. The arguments of this function (see below)
+are used to store the new language in the database (in the LanguageHandler,
+which is a type of Script).
+
If you want to remember the language definitions, you could put them all
+in a module along with the add_language call as a quick way to
+rebuild the language on a db reset:
+
# a stand-alone module somewhere under mygame. Just import this
+# once to automatically add the language!
+
+fromevennia.contrib.rpg.rpsystemimportrplanguage
+grammar=(...)
+vowels="eaouy"
+# etc
+
+rplanguage.add_language(grammar=grammar,vowels=vowels,...)
+
+
+
The variables of add_language 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
+words compared to the original. 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:
Below is an example module creating “elvish”, using “rounder” vowels and sounds:
# vowel/consonant grammar possibilitiesgrammar=("v vv vvc vcc vvcc cvvc vccv vvccv vcvccv vcvcvcc vvccvvcc ""vcvvccvvc cvcvvcvvcc vcvcvvccvcvv")
@@ -164,7 +186,6 @@ list or a path to a file of words (one per line) to automatically
create fixed translations for according to the grammatical rules.
This allows to quickly build a large corpus of translated words
that never change (if this is desired).
-
-search_index_entry = {'aliases': '" \'', 'category': 'general', 'key': 'say', 'no_prefix': ' " \'', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say <message>\n\n Talk to those in your current location.\n '}¶
+search_index_entry = {'aliases': '\' "', 'category': 'general', 'key': 'say', 'no_prefix': ' \' "', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say <message>\n\n Talk to those in your current location.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.tutorials.red_button.red_button.html b/docs/1.0-dev/api/evennia.contrib.tutorials.red_button.red_button.html
index 00203df30b..ee79693673 100644
--- a/docs/1.0-dev/api/evennia.contrib.tutorials.red_button.red_button.html
+++ b/docs/1.0-dev/api/evennia.contrib.tutorials.red_button.red_button.html
@@ -96,7 +96,7 @@ such as when closing the lid and un-blinding a character.
-search_index_entry = {'aliases': 'smash lid break lid smash', 'category': 'general', 'key': 'smash glass', 'no_prefix': ' smash lid break lid smash', 'tags': '', 'text': '\n Smash the protective glass.\n\n Usage:\n smash glass\n\n Try to smash the glass of the button.\n\n '}¶
+search_index_entry = {'aliases': 'smash lid smash break lid', 'category': 'general', 'key': 'smash glass', 'no_prefix': ' smash lid smash break lid', 'tags': '', 'text': '\n Smash the protective glass.\n\n Usage:\n smash glass\n\n Try to smash the glass of the button.\n\n '}¶
-search_index_entry = {'aliases': 'get l ex listen examine feel', 'category': 'general', 'key': 'look', 'no_prefix': ' get l ex listen examine feel', 'tags': '', 'text': "\n Looking around in darkness\n\n Usage:\n look <obj>\n\n ... not that there's much to see in the dark.\n\n "}¶
+search_index_entry = {'aliases': 'feel ex get examine listen l', 'category': 'general', 'key': 'look', 'no_prefix': ' feel ex get examine listen l', 'tags': '', 'text': "\n Looking around in darkness\n\n Usage:\n look <obj>\n\n ... not that there's much to see in the dark.\n\n "}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.objects.html b/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.objects.html
index 3d480fae8f..06e83d6c07 100644
--- a/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.objects.html
+++ b/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.objects.html
@@ -507,7 +507,7 @@ shift green root up/down
@@ -756,7 +756,7 @@ parry - forgoes your attack but will make you harder to hit on next
-search_index_entry = {'aliases': 'fight bash parry chop slash stab defend hit pierce kill thrust', 'category': 'tutorialworld', 'key': 'attack', 'no_prefix': ' fight bash parry chop slash stab defend hit pierce kill thrust', 'tags': '', 'text': '\n Attack the enemy. Commands:\n\n stab <enemy>\n slash <enemy>\n parry\n\n stab - (thrust) makes a lot of damage but is harder to hit with.\n slash - is easier to land, but does not make as much damage.\n parry - forgoes your attack but will make you harder to hit on next\n enemy attack.\n\n '}¶
+search_index_entry = {'aliases': 'parry bash kill stab fight pierce defend slash thrust hit chop', 'category': 'tutorialworld', 'key': 'attack', 'no_prefix': ' parry bash kill stab fight pierce defend slash thrust hit chop', 'tags': '', 'text': '\n Attack the enemy. Commands:\n\n stab <enemy>\n slash <enemy>\n parry\n\n stab - (thrust) makes a lot of damage but is harder to hit with.\n slash - is easier to land, but does not make as much damage.\n parry - forgoes your attack but will make you harder to hit on next\n enemy attack.\n\n '}¶
diff --git a/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.rooms.html b/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.rooms.html
index f9b770c667..e24999c8ca 100644
--- a/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.rooms.html
+++ b/docs/1.0-dev/api/evennia.contrib.tutorials.tutorial_world.rooms.html
@@ -919,7 +919,7 @@ to find something.
@@ -947,7 +947,7 @@ random chance of eventually finding a light source.
-search_index_entry = {'aliases': 'l feel around fiddle feel search', 'category': 'tutorialworld', 'key': 'look', 'no_prefix': ' l feel around fiddle feel search', 'tags': '', 'text': '\n Look around in darkness\n\n Usage:\n look\n\n Look around in the darkness, trying\n to find something.\n '}¶
+search_index_entry = {'aliases': 'fiddle feel search feel around l', 'category': 'tutorialworld', 'key': 'look', 'no_prefix': ' fiddle feel search feel around l', 'tags': '', 'text': '\n Look around in darkness\n\n Usage:\n look\n\n Look around in the darkness, trying\n to find something.\n '}¶
diff --git a/docs/1.0-dev/api/evennia.html b/docs/1.0-dev/api/evennia.html
index 37561f728b..62bbcbda21 100644
--- a/docs/1.0-dev/api/evennia.html
+++ b/docs/1.0-dev/api/evennia.html
@@ -425,7 +425,12 @@ with ‘q’, remove the break line and restart server when finished.
-search_index_entry = {'aliases': 'yes abort y a n __nomatch_command no', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' yes abort y a n __nomatch_command no', 'tags': '', 'text': '\n Handle a prompt for yes or no. Press [return] for the default choice.\n\n '}¶
+search_index_entry = {'aliases': '__nomatch_command a abort yes n no y', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' __nomatch_command a abort yes n no y', 'tags': '', 'text': '\n Handle a prompt for yes or no. Press [return] for the default choice.\n\n '}¶
diff --git a/docs/1.0-dev/api/evennia.utils.evmore.html b/docs/1.0-dev/api/evennia.utils.evmore.html
index 44edc6e7d0..198f658110 100644
--- a/docs/1.0-dev/api/evennia.utils.evmore.html
+++ b/docs/1.0-dev/api/evennia.utils.evmore.html
@@ -88,7 +88,7 @@ the caller.msg() construct every time the page is updated.
@@ -114,7 +114,7 @@ the caller.msg() construct every time the page is updated.
-search_index_entry = {'aliases': 'previous e next end abort a quit top n t p q', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' previous e next end abort a quit top n t p q', 'tags': '', 'text': '\n Manipulate the text paging. Catch no-input with aliases.\n '}¶
+search_index_entry = {'aliases': 'q a next abort t quit e end n top previous p', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' q a next abort t quit e end n top previous p', 'tags': '', 'text': '\n Manipulate the text paging. Catch no-input with aliases.\n '}¶