"""
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)