Source code for evennia.contrib.game_systems.gendersub.gendersub

"""
Gendersub

Griatch 2015

This is a simple gender-aware Character class for allowing users to
insert custom markers in their text to indicate gender-aware
messaging. It relies on a modified msg() and is meant as an
inspiration and starting point to how to do stuff like this.

An object can have the following genders:
 - male (he/his)
 - female (her/hers)
 - neutral (it/its)
 - ambiguous (they/them/their/theirs)

Usage

When in use, messages can contain special tags to indicate pronouns gendered
based on the one being addressed. Capitalization will be retained.

- `|s`, `|S`: Subjective form: he, she, it, He, She, It, They
- `|o`, `|O`: Objective form: him, her, it, Him, Her, It, Them
- `|p`, `|P`: Possessive form: his, her, its, His, Her, Its, Their
- `|a`, `|A`: Absolute Possessive form: his, hers, its, His, Hers, Its, Theirs

For example,

```
char.msg("%s falls on |p face with a thud." % char.key)
"Tom falls on his face with a thud"
```

The default gender is "ambiguous" (they/them/their/theirs).

To use, have DefaultCharacter inherit from this, or change
setting.DEFAULT_CHARACTER to point to this class.

The `gender` command is used to set the gender. It needs to be added to the
default cmdset before it becomes available.

"""

import re

from evennia import Command, DefaultCharacter
from evennia.utils import logger

# gender maps

_GENDER_PRONOUN_MAP = {
    "male": {"s": "he", "o": "him", "p": "his", "a": "his"},
    "female": {"s": "she", "o": "her", "p": "her", "a": "hers"},
    "neutral": {"s": "it", "o": "it", "p": "its", "a": "its"},
    "ambiguous": {"s": "they", "o": "them", "p": "their", "a": "theirs"},
}
_RE_GENDER_PRONOUN = re.compile(r"(?<!\|)\|(?!\|)[sSoOpPaA]")

# in-game command for setting the gender


[docs]class SetGender(Command): """ Sets gender on yourself Usage: @gender male || female || neutral || ambiguous """ key = "gender" aliases = "sex" locks = "call:all()"
[docs] def func(self): """ Implements the command. """ caller = self.caller arg = self.args.strip().lower() if arg not in ("male", "female", "neutral", "ambiguous"): caller.msg("Usage: @gender male||female||neutral||ambiguous") return caller.db.gender = arg caller.msg("Your gender was set to %s." % arg)
# Gender-aware character class
[docs]class GenderCharacter(DefaultCharacter): """ This is a Character class aware of gender. """
[docs] def at_object_creation(self): """ Called once when the object is created. """ super().at_object_creation() self.db.gender = "ambiguous"
def _get_pronoun(self, regex_match, source=None): """ Get pronoun from the pronoun marker in the text. This is used as the callable for the re.sub function. Args: regex_match (MatchObject): the regular expression match. Notes: - `|s`, `|S`: Subjective form: he, she, it, He, She, It, They - `|o`, `|O`: Objective form: him, her, it, Him, Her, It, Them - `|p`, `|P`: Possessive form: his, her, its, His, Her, Its, Their - `|a`, `|A`: Absolute Possessive form: his, hers, its, His, Hers, Its, Theirs """ if not source: source = self typ = regex_match.group()[1] # "s", "O" etc gender = source.attributes.get("gender", default="ambiguous") gender = gender if gender in ("male", "female", "neutral") else "ambiguous" pronoun = _GENDER_PRONOUN_MAP[gender][typ.lower()] return pronoun.capitalize() if typ.isupper() else pronoun
[docs] def msg(self, text=None, from_obj=None, session=None, **kwargs): """ Emits something to a session attached to the object. Overloads the default msg() implementation to include gender-aware markers in output. Args: text (str or tuple, optional): The message to send. This is treated internally like any send-command, so its value can be a tuple if sending multiple arguments to the `text` oob command. from_obj (obj, optional): object that is sending. If given, at_msg_send will be called session (Session or list, optional): session or list of sessions to relay to, if any. If set, will force send regardless of MULTISESSION_MODE. Notes: `at_msg_receive` will be called on this Object. All extra kwargs will be passed on to the protocol. """ if text is None: super().msg(from_obj=from_obj, session=session, **kwargs) return try: if text and isinstance(text, tuple): text = ( _RE_GENDER_PRONOUN.sub( lambda x: self._get_pronoun(x, source=from_obj), text[0] ), *text[1:], ) else: text = _RE_GENDER_PRONOUN.sub(lambda x: self._get_pronoun(x, source=from_obj), text) except TypeError: pass except Exception as e: logger.log_trace(e) super().msg(text, from_obj=from_obj, session=session, **kwargs)