implement pronounsub

This commit is contained in:
lunacb 2023-01-11 05:14:04 -05:00
parent 488595e2b8
commit 8daecbf9fc
4 changed files with 282 additions and 6 deletions

View File

@ -18,11 +18,12 @@ from evennia import default_cmds
from evennia.contrib.base_systems.building_menu import GenericBuildingCmd
from evennia.contrib.game_systems import mail
from evennia.contrib.game_systems.multidescer import CmdMultiDesc
from evennia.contrib.grid.ingame_map_display import MapDisplayCmdSet
from evennia.contrib.grid import simpledoor
from lib.rpsystem.rpsystem import RPSystemCmdSet
from evennia.contrib.game_systems.clothing import ClothedCharacterCmdSet
from evennia.contrib.game_systems.multidescer import CmdMultiDesc
from evennia.contrib.grid import simpledoor
from evennia.contrib.grid.ingame_map_display import MapDisplayCmdSet
from lib.pronounsub import SetPronouns
from lib.rpsystem.rpsystem import RPSystemCmdSet
class CharacterCmdSet(default_cmds.CharacterCmdSet):
@ -65,6 +66,9 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
# Clothing: wear
self.add(ClothedCharacterCmdSet())
# gendersub
self.add(SetPronouns())
class AccountCmdSet(default_cmds.AccountCmdSet):
"""
This is the cmdset available to the Account at all times. It is

View File

@ -0,0 +1,2 @@
from .pronounsub import PronounCharacter
from .pronounsub import SetPronouns

View File

@ -0,0 +1,262 @@
"""
Pronounsub
Griatch 2015
Copyright (c) 2023 lunacb <lunacb@disroot.org>
This is a gender-aware Character class that allows people to set pronouns and
use them in text.
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, like They
- `|o`, `|O`: Objective form, like Them
- `|p`, `|P`: Possessive form, like Their
- `|a`, `|A`: Absolute Possessive form, like Theirs
For example,
```
char.msg("%s falls on |p face with a thud." % char.key)
"Tom falls on their face with a thud"
```
To use, have DefaultCharacter inherit from this, or change
setting.DEFAULT_CHARACTER to point to this class.
The `pronouns` command is used to set pronouns. It needs to be added to the
default cmdset before it becomes available.
"""
import random
import re
from evennia import Command, DefaultCharacter
from evennia.utils import logger
# gender maps
# TODO: Don't make this hardcoded.
_PRONOUN_LIST = [
{"s": "he", "o": "him", "p": "his", "a": "his"},
{"s": "she", "o": "her", "p": "her", "a": "hers"},
{"s": "it", "o": "it", "p": "its", "a": "its"},
{"s": "they", "o": "them", "p": "their", "a": "theirs"},
]
_PRONOUN_FORMS = [ "s", "o", "p", "a" ]
_RE_GENDER_PRONOUN = re.compile(r"(?<!\|)\|(?!\|)[sSoOpPaA]")
# in-game command for setting the gender
class SetPronouns(Command):
"""
Sets your pronouns.
Usage:
@gender
@gender <pronoun1>[;pronoun2 ...]
With no arguments, this command prints your current pronouns.
Pronouns can be specified either in a general form, like "she/they", or a
specific form, like "they,them,their,theirs".
For the general form, each in the list must match up to a grammatical form
of a specific pronoun in a known database, and that specific pronoun will
be used.
The specific format is: <subjective>,<objective>,<possessive>,<absolute>
subjective: *They* went to the store.
objective: You took *them* to the store.
possessive: You took *their* friend to the store.
absolute: The sandwich in the fridge was *theirs*.
When multiple pronouns are given, a random one will be used in each text
substitution.
Examples:
@pronouns he/him
@pronouns she/they/it
@pronouns they,them,their,thers
@pronouns she/they;fae,faer,faer,faers
"""
key = "pronouns"
locks = "call:all()"
def __init__(self):
self.cmd_type = "set"
self.syntax_error = False
def parse(self):
self.cmd_type = "set"
self.syntax_error = True
generals = []
specifics = []
if self.args == '':
self.cmd_type = "get"
self.syntax_error = False
return
pronouns = self.args.strip().lower().split(";")
for pronoun in pronouns:
slash = ('/' in pronoun)
comma = (',' in pronoun)
if slash and comma:
return
if comma:
specific = [ p.strip() for p in pronoun.split(",") ]
if len(specific) != len(_PRONOUN_FORMS):
return
new = {}
for i in range(len(specific)):
new[_PRONOUN_FORMS[i]] = specific[i]
specifics.append(new)
else:
generals += [ p.strip() for p in pronoun.split("/") ]
self.args = (generals, specifics)
self.syntax_error = False
def func(self):
caller = self.caller
if self.syntax_error:
caller.msg(self.get_help(caller, self.cmdset))
return
if self.cmd_type == "get":
caller.msg(f"Your pronouns are \"{caller._print_pronouns()}\"")
return
generals, specifics = self.args
for general in generals:
if not caller._specific_pronoun_from_general(general):
caller.msg(f"Warning: The general pronoun \"{general}\" is not"
"in the server's list of pronouns. It will be"
"ignored.")
caller.db.pronoun_generals = generals
caller.db.pronoun_specifics = specifics
caller.msg(f"Your pronouns have been set to \"{caller._print_pronouns()}\".")
class PronounCharacter(DefaultCharacter):
"""
This is a Character class aware of gender.
"""
def at_object_creation(self):
"""
Called once when the object is created.
"""
super().at_object_creation()
self.db.pronoun_generals = [ "they", "them" ]
self.db.pronoun_specifics = []
def _specific_pronoun_from_general(self, general):
for pronoun in _PRONOUN_LIST:
if general in pronoun.values():
return pronoun
return None
def _print_pronouns(self):
generals = self.attributes.get("pronoun_generals", default=[ "they", "them" ])
specifics = self.attributes.get("pronoun_specifics", default=[])
res = [ "/".join(generals) ] if len(generals) else []
for specific in specifics:
res.append(",".join([ specific[form] for form in _PRONOUN_FORMS ]))
return ";".join(res)
def _get_pronoun(self, regex_match):
logger.log_info("hereee " + str(regex_match))
"""
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, like They
- `|o`, `|O`: Objective form, like Them
- `|p`, `|P`: Possessive form, like Their
- `|a`, `|A`: Absolute Possessive form, like Theirs
"""
typ = regex_match.group()[1] # "s", "O" etc
logger.log_info("hereee " + typ)
dup_specifics = self.attributes.get("pronoun_specifics", default=[])
for general in self.attributes.get("pronoun_generals", default=[ "they", "them" ]):
specific = self._specific_pronoun_from_general(general)
if specific != None:
dup_specifics.append(specific)
specifics = []
for s in dup_specifics:
if not s in specifics:
specifics.append(s)
choice = random.choice(specifics)
gender = self.attributes.get("gender", default="ambiguous")
gender = gender if gender in ("male", "female", "neutral") else "ambiguous"
pronoun = choice[typ.lower()]
logger.log_info("hereee " + pronoun)
return pronoun.capitalize() if typ.isupper() else pronoun
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(self._get_pronoun, text[0]), *text[1:])
else:
text = _RE_GENDER_PRONOUN.sub(self._get_pronoun, text)
except TypeError:
pass
except Exception as e:
logger.log_trace(e)
super().msg(text, from_obj=from_obj, session=session, **kwargs)

View File

@ -8,8 +8,16 @@ creation commands.
"""
from lib.rpsystem import ContribRPCharacter
from lib.pronounsub import PronounCharacter
from evennia.contrib.game_systems.clothing import ClothedCharacter
# rpsystem
class Character(ContribRPCharacter, ClothedCharacter):
pass
class Character(ContribRPCharacter, ClothedCharacter, PronounCharacter):
def at_object_creation(self):
super(PronounCharacter, self).at_object_creation()
super(ContribRPCharacter, self).at_object_creation()
def msg(self, text=None, from_obj=None, session=None, **kwargs):
# TODO: Don't do this. See https://git.disroot.org/vantablack/vantaMOO/issues/29
super(PronounCharacter, self).msg(text, from_obj, session, **kwargs)
#super(ContribRPCharacter, self).msg(text, from_obj, session, **kwargs)