diff --git a/evennia/contrib/rplanguages.py b/evennia/contrib/rplanguage.py similarity index 87% rename from evennia/contrib/rplanguages.py rename to evennia/contrib/rplanguage.py index 3ee3683e4..f9f22adfa 100644 --- a/evennia/contrib/rplanguages.py +++ b/evennia/contrib/rplanguage.py @@ -106,8 +106,11 @@ from evennia import DefaultScript #------------------------------------------------------------ # default language grammar -_PHONEMES = "ea oh ae aa eh ah ao aw ai 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" +_PHONEMES = "ea oh ae aa eh ah ao aw ai er ey ow ia ih iy oy ua uh uw a e i u y p b t d f v t dh s z sh zh ch jh k ng g m n l r w" _VOWELS = "eaoiuy" +# these must be able to be constructed from phonemes (so for example, +# if you have v here, there must exixt at least one single-character +# vowel phoneme defined above) _GRAMMAR = "v cv vc cvv vcc vcv cvcc vccv cvccv cvcvcc cvccvcv vccvccvc cvcvccvv cvcvcvcvv" _RE_FLAGS = re.MULTILINE + re.IGNORECASE + re.UNICODE @@ -281,37 +284,36 @@ class LanguageHandler(DefaultScript): lword = len(word) if len(word) <= self.level: # below level. Don't translate - self.lastword = word - return word - elif word.istitle() and not self.lastword.istitle(): - # capitalized word inside text - treat as a - # name (don't translate) but don't allow several in a row. - new_word = "%s%s%s" % (self.language["noun_prefix"], word, self.language["noun_postfix"]) - self.lastword = word - return new_word + new_word = word else: # translate the word new_word = self.language["translation"].get(word.lower(), "") if not new_word: - # make up translation on the fly. Length can - # vary from un-translated word. - wlen = max(0, lword + sum(randint(-1,1) for i - in range(self.language["word_length_variance"]))) - grammar = self.language["grammar"] - if wlen not in grammar: - # this word has no direct translation! - return "" - structure = choice(grammar[wlen]) - grammar2phonemes = self.language["grammar2phonemes"] - for match in _RE_GRAMMAR.finditer(structure): - # there are only four combinations: vv,cc,c,v - new_word += choice(grammar2phonemes[match.group()]) - if word.istitle(): - # capitalize words correctly - new_word = new_word.capitalize() - if len(word) > 1 and word.isupper(): - # keep LOUD words loud also when translated - new_word = new_word.upper() + if word.istitle(): + # capitalized word we don't have a translation for - + # treat as a name (don't translate) + print "noun ..." + new_word = "%s%s%s" % (self.language["noun_prefix"], word, self.language["noun_postfix"]) + else: + # make up translation on the fly. Length can + # vary from un-translated word. + wlen = max(0, lword + sum(randint(-1,1) for i + in range(self.language["word_length_variance"]))) + grammar = self.language["grammar"] + if wlen not in grammar: + # this word has no direct translation! + return "" + structure = choice(grammar[wlen]) + grammar2phonemes = self.language["grammar2phonemes"] + for match in _RE_GRAMMAR.finditer(structure): + # there are only four combinations: vv,cc,c,v + new_word += choice(grammar2phonemes[match.group()]) + if word.istitle(): + # capitalize words the same way + new_word = new_word.capitalize() + if len(word) > 1 and word.isupper(): + # keep LOUD words loud also when translated + new_word = new_word.upper() return new_word def translate(self, text, level=0.0, language="default"): @@ -340,8 +342,6 @@ class LanguageHandler(DefaultScript): # configuring the translation self.level = int(10 * (1.0 - max(0, min(level, 1.0)))) - self.lastword = "" - return _RE_WORD.sub(self._translate_sub, text) @@ -355,7 +355,7 @@ def obfuscate_language(text, level=0.0, language="default"): Args: text (str): Text to obfuscate. level (real, optional): A value from 0.0-1.0 determining - the level of obfuscation where 0 means no obfuscation + the level of obfuscation where 0 means no jobfuscation (string returned unchanged) and 1.0 means the entire string is obfuscated. language (str, optional): The identifier of a language @@ -394,6 +394,26 @@ def add_language(**kwargs): _LANGUAGE_HANDLER.add(**kwargs) +def available_languages(): + """ + Returns all available language keys. + + Returns: + languages (list): List of key strings of all available + languages. + """ + global _LANGUAGE_HANDLER + if not _LANGUAGE_HANDLER: + try: + _LANGUAGE_HANDLER = LanguageHandler.objects.get(db_key="language_handler") + except LanguageHandler.DoesNotExist: + if not _LANGUAGE_HANDLER: + from evennia import create_script + _LANGUAGE_HANDLER = create_script(LanguageHandler) + return _LANGUAGE_HANDLER.attributes.get("language_storage", {}).keys() + + + #------------------------------------------------------------ # # Whisper obscuration diff --git a/evennia/contrib/rpsystem.py b/evennia/contrib/rpsystem.py index bc93124ac..2dd3b3df2 100644 --- a/evennia/contrib/rpsystem.py +++ b/evennia/contrib/rpsystem.py @@ -55,7 +55,6 @@ Tall man (assuming his name is Tom) sees: """ import re -from re import match as re_match import itertools from copy import copy from evennia import DefaultObject, DefaultCharacter, DefaultRoom @@ -90,9 +89,6 @@ _EMOTE_MULTIMATCH_ERROR = \ """{{RMultiple possibilities for {ref}: {{r{reflist}{{n""" -_LANGUAGE_NOMATCH_ERROR = \ -"""{{RNo language named {{r{langname}{{n""" - _RE_FLAGS = re.MULTILINE + re.IGNORECASE + re.UNICODE _RE_PREFIX = re.compile(r"^%s" % _PREFIX, re.UNICODE) @@ -127,25 +123,6 @@ _RE_REF_LANG = re.compile(r"\{+\##([0-9]+)\}+") # this regex returns in groups (langname, say), where langname can be empty. _RE_LANGUAGE = re.compile(r"(?:(\w+))*(\".+?\")") - -#TODO -# make this into a pluggable language module for handling -# language errors and translations. -_LANGUAGE_MODULE = None # load code here -#TODO function determining if a given langname exists. Note that -# langname can be None if not specified explicitly. -_LANGUAGE_AVAILABLE = lambda langname: True -#TODO function to translate a string in a given language -_LANGUAGE_TRANSLATE = lambda speaker, listener, language, text: "%s%s" % ("(%s)" % language if language else "", text) -#TODO list available languages -_LANGUAGE_LIST = lambda: [] - -# color markup to use for coloring sdescs/recog strings -# in emotes and spoken language quotes. -_LANGUAGE_COLOR = lambda obj: "{w" -_RECOG_COLOR = lambda obj: "{b" - - # the emote parser works in two steps: # 1) convert the incoming emote into an intermediary # form with all object references mapped to ids. @@ -168,6 +145,12 @@ class LanguageError(Exception): pass +def _dummy_process(text, *args, **kwargs): + "Pass-through processor" + return text + + + def ordered_permutation_regex(sentence): """ Builds a regex that matches 'ordered permutations' of a sentence's @@ -259,10 +242,6 @@ def parse_language(speaker, emote): # in-place without messing up indexes for future matches # note that saytext includes surrounding "...". langname, saytext = say_match.groups() - if not _LANGUAGE_AVAILABLE(langname): - errors.append(_LANGUAGE_NOMATCH_ERROR.format(langname=langname)) - continue - istart, iend = say_match.start(), say_match.end() # the key is simply the running match in the emote key = "##%i" % imatch @@ -465,17 +444,25 @@ def send_emote(sender, receivers, emote, anonymous_add="first"): except AttributeError: pass # handle the language mapping, which always produce different keys ##nn + try: + process_language = receiver.process_language + except AttributeError: + process_language = _dummy_process for key, (langname, saytext) in language_mapping.iteritems(): # color says - mapping[key] = "%s%s{n" % (_LANGUAGE_COLOR(receiver), - _LANGUAGE_TRANSLATE(sender, receiver, langname, saytext)) + mapping[key] = process_language(saytext, sender, langname) # make sure receiver always sees their real name rkey = "#%i" % receiver.id if rkey in mapping: mapping[rkey] = receiver.key - # add color to recog strings - mapping = dict((key, "%s%s{n" % (_RECOG_COLOR(receiver), val)) + # add color to sdesc strings + try: + process_sdesc = receiver.process_sdesc + except AttributeError: + process_sdesc = _dummy_process + + mapping = dict((key, process_sdesc(val, receiver)) for key, val in mapping.iteritems()) # do the template replacement @@ -695,25 +682,6 @@ class CmdRecog(Command): # assign personal alias to object in room caller.msg("You will now remember {w%s{n as {w%s{n." % (sdesc, alias)) -class CmdLanguage(Command): # list available languages - """ - List the available languages. - - Usages: - languages - - This will display a list of all languages available - and the short names needed to speak a given language in - an emote. - - """ - key = "language" - - def func(self): - "simple list" - self.caller.msg("Languages available: %s" % ", ".join(_LANGUAGE_LIST)) - - class RPSystemCmdSet(CmdSet): """ Mix-in for adding rp-commands to default cmdset. @@ -723,7 +691,6 @@ class RPSystemCmdSet(CmdSet): self.add(CmdSdesc()) self.add(CmdPose()) self.add(CmdRecog()) - self.add(CmdLanguage()) #------------------------------------------------------------ @@ -1133,3 +1100,44 @@ class RPCharacter(DefaultCharacter, RPObject): # initializing sdesc self.sdesc.add("A normal person") + def process_sdesc(self, sdesc, obj, **kwargs): + """ + Allows to customize how your sdesc is displayed (primarily by + changing colors). + + Args: + sdesc (str): The sdesc to display. + obj (Object): The object to which the adjoining sdesc + belongs (can be yourself). + + Returns: + sdesc (str): The processed sdesc ready + for display. + + """ + return "{b%s{n" % sdesc + + def process_language(self, text, speaker, language, **kwargs): + """ + Allows to process the spoken text, for example + by obfuscating language based on your and the + speaker's language skills. Also a good place to + put coloring. + + Args: + text (str): The text to process. + speaker (Object): The object delivering the text. + language (str): An identifier string for the language. + + Return: + text (str): The optionally processed text. + + Notes: + This is designed to work together with a string obfuscator + such as the `obfuscate_language` or `obfuscate_whisper` in + the evennia.contrib.rplanguage module. + + """ + from evennia.contrib import rplanguage + #return "{w%s{n" % text + return "{w%s{n" % rplanguage.obfuscate_language(text, level=1.0) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 7d58309fe..bd746a2fb 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -1404,8 +1404,8 @@ class DefaultCharacter(DefaultObject): """ self.msg("\nYou become {c%s{n.\n" % self.name) self.execute_cmd("look") - if self.location: - self.location.msg_contents("%s has entered the game." % self.name, exclude=[self]) + for obj in (obj for obj in self.location.contents if obj != self): + obj.msg("%s has entered the game." % self.get_display_name(obj)) def at_post_unpuppet(self, player, sessid=None): """