Fix merge conflicts

This commit is contained in:
Griatch 2022-09-13 20:14:39 +02:00
commit a974a2843c
2 changed files with 188 additions and 140 deletions

View file

@ -24,7 +24,7 @@ class NameChanger:
class MyObject(DefaultObject): class MyObject(DefaultObject):
@lazy_property: @lazy_property:
def namechange(self): def namechange(self):
return MyHandler(self) return NameChanger(self)
obj = create_object(MyObject, key="test") obj = create_object(MyObject, key="test")
@ -35,13 +35,23 @@ print(obj.key)
>>> "test_extra" >>> "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! What happens here is that we make a new class `NameChanger`. 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. 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 ## 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: 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 ```python
# for example in mygame/world/quests.py # for example in mygame/world/quests.py
@ -103,7 +113,6 @@ class QuestHandler:
self._save() self._save()
def check_progress(self): def check_progress(self):
for quest in self.storage.values():
quest.check_progress() quest.check_progress()
if self.do_save: if self.do_save:
# .do_save is set on handler by Quest if it wants to save progress # .do_save is set on handler by Quest if it wants to save progress
@ -159,9 +168,18 @@ class Quest:
``` ```
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 `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 `Quest.questhandler` property allows to easily
get back to the handler (and the object on which it sits).
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. 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
### Tying it all together ### Tying it all together
@ -184,14 +202,21 @@ class Character(DefaultCharacter):
``` ```
You can now make your Quest classes to describe your quests and add them to characters with You can now make your Quest classes to describe your quests and add them to
characters with
character.quests.add(FindTheRedKey) ```python
character.quests.add(FindTheRedKey)
```
and can later do and can later do
character.quests.check_progress() ```python
character.quests.check_progress()
```
and be sure that quest data is not lost between reloads. 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. You can find a full-fledged quest-handler example as [EvAdventure
quests](evennia.contribs.tutorials.evadventure.quests) contrib in the Evennia
repository.

View file

@ -17,110 +17,133 @@ in the game in various ways:
overhear (for example "s" sounds tend to be audible even when no other overhear (for example "s" sounds tend to be audible even when no other
meaning can be determined). meaning can be determined).
Usage: ## Usage
```python ```python
from evennia.contrib import rplanguage from evennia.contrib import rplanguage
# need to be done once, here we create the "default" lang # need to be done once, here we create the "default" lang
rplanguage.add_language() rplanguage.add_language()
say = "This is me talking." say = "This is me talking."
whisper = "This is me whispering. whisper = "This is me whispering.
print rplanguage.obfuscate_language(say, level=0.0) print rplanguage.obfuscate_language(say, level=0.0)
<<< "This is me talking." <<< "This is me talking."
print rplanguage.obfuscate_language(say, level=0.5) print rplanguage.obfuscate_language(say, level=0.5)
<<< "This is me byngyry." <<< "This is me byngyry."
print rplanguage.obfuscate_language(say, level=1.0) print rplanguage.obfuscate_language(say, level=1.0)
<<< "Daly ly sy byngyry." <<< "Daly ly sy byngyry."
result = rplanguage.obfuscate_whisper(whisper, level=0.0) result = rplanguage.obfuscate_whisper(whisper, level=0.0)
<<< "This is me whispering" <<< "This is me whispering"
result = rplanguage.obfuscate_whisper(whisper, level=0.2) result = rplanguage.obfuscate_whisper(whisper, level=0.2)
<<< "This is m- whisp-ring" <<< "This is m- whisp-ring"
result = rplanguage.obfuscate_whisper(whisper, level=0.5) result = rplanguage.obfuscate_whisper(whisper, level=0.5)
<<< "---s -s -- ---s------" <<< "---s -s -- ---s------"
result = rplanguage.obfuscate_whisper(whisper, level=0.7) result = rplanguage.obfuscate_whisper(whisper, level=0.7)
<<< "---- -- -- ----------" <<< "---- -- -- ----------"
result = rplanguage.obfuscate_whisper(whisper, level=1.0) result = rplanguage.obfuscate_whisper(whisper, level=1.0)
<<< "..." <<< "..."
``` ```
To set up new languages, import and use the `add_language()` ## Custom languages
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: 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 If you want to remember the language definitions, you could put them all
# vowel/consonant grammar possibilities in a module along with the `add_language` call as a quick way to
grammar = ("v vv vvc vcc vvcc cvvc vccv vvccv vcvccv vcvcvcc vvccvvcc " rebuild the language on a db reset:
```python
# a stand-alone module somewhere under mygame. Just import this
# once to automatically add the language!
from evennia.contrib.rpg.rpsystem import rplanguage
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. You can also add your own
dictionary and "fix" random words for a list of input words.
## Example
Below is an example module creating "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") "vcvvccvvc cvcvvcvvcc vcvcvvccvcvv")
# all not in this group is considered a consonant # all not in this group is considered a consonant
vowels = "eaoiuy" vowels = "eaoiuy"
# you need a representative of all of the minimal grammars here, so if a # 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 # grammar v exists, there must be atleast one phoneme available with only
# one vowel in it # one vowel in it
phonemes = ("oi oh ee ae aa eh ah ao aw ay er ey ow ia ih iy " 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 " "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") "ng g m n l r w")
# how much the translation varies in length compared to the original. 0 is # how much the translation varies in length compared to the original. 0 is
# smallest, higher values give ever bigger randomness (including removing # smallest, higher values give ever bigger randomness (including removing
# short words entirely) # short words entirely)
word_length_variance = 1 word_length_variance = 1
# if a proper noun (word starting with capitalized letter) should be # if a proper noun (word starting with capitalized letter) should be
# translated or not. If not (default) it means e.g. names will remain # translated or not. If not (default) it means e.g. names will remain
# unchanged across languages. # unchanged across languages.
noun_translate = False noun_translate = False
# all proper nouns (words starting with a capital letter not at the beginning # 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 # of a sentence) can have either a postfix or -prefix added at all times
noun_postfix = "'la" noun_postfix = "'la"
# words in dict will always be translated this way. The 'auto_translations' # 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 # 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 # bigger dictionary by creating random translations of each word in the
# list *once* and saving the result for subsequent use. # list *once* and saving the result for subsequent use.
manual_translations = {"the":"y'e", "we":"uyi", "she":"semi", "he":"emi", manual_translations = {"the":"y'e", "we":"uyi", "she":"semi", "he":"emi",
"you": "do", 'me':'mi','i':'me', 'be':"hy'e", 'and':'y'} "you": "do", 'me':'mi','i':'me', 'be':"hy'e", 'and':'y'}
rplanguage.add_language(key="elvish", phonemes=phonemes, grammar=grammar, rplanguage.add_language(key="elvish", phonemes=phonemes, grammar=grammar,
word_length_variance=word_length_variance, word_length_variance=word_length_variance,
noun_translate=noun_translate, noun_translate=noun_translate,
noun_postfix=noun_postfix, vowels=vowels, noun_postfix=noun_postfix, vowels=vowels,
manual_translations=manual_translations, manual_translations=manual_translations,
auto_translations="my_word_file.txt") auto_translations="my_word_file.txt")
``` ```
This will produce a decicively more "rounded" and "soft" language This will produce a decicively more "rounded" and "soft" language
than the default one. The few manual_translations also make sure than the default one. The few manual_translations also make sure
to make it at least look superficially "reasonable". to make it at least look superficially "reasonable".
The `auto_translations` keyword is useful, this accepts either a The `auto_translations` keyword is useful, this accepts either a
list or a path to a file of words (one per line) to automatically list or a path to a file of words (one per line) to automatically
create fixed translations for according to the grammatical rules. create fixed translations for according to the grammatical rules.
This allows to quickly build a large corpus of translated words This allows to quickly build a large corpus of translated words
that never change (if this is desired). that never change (if this is desired).
""" """
import re import re
from random import choice, randint
from collections import defaultdict from collections import defaultdict
from random import choice, randint
from evennia import DefaultScript from evennia import DefaultScript
from evennia.utils import logger from evennia.utils import logger
# ------------------------------------------------------------ # ------------------------------------------------------------
# #
# Obfuscate language # Obfuscate language