forked from vantablack/vantaMOO
Compare commits
15 commits
5f27514eb1
...
bab90c1ba7
Author | SHA1 | Date | |
---|---|---|---|
Alexander Yakovlev | bab90c1ba7 | ||
vanta black | 8a458b21b5 | ||
vanta black | 384c0bacfc | ||
lunacb | 88ba451426 | ||
lunacb | 7e34080666 | ||
lunacb | d1f33e858a | ||
lunacb | fbe670651e | ||
lunacb | 9780740ab9 | ||
lunacb | 125252078a | ||
lunacb | c359ae2f1c | ||
lunacb | d2f6684026 | ||
lunacb | 8daecbf9fc | ||
lunacb | 488595e2b8 | ||
Erin Nova | 0bb41b771c | ||
Erin Nova | b99bc291c3 |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -14,7 +14,6 @@ var
|
|||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
lib
|
||||
lib64
|
||||
__pycache__
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
# Welcome to Evennia!
|
||||
# vantaMOO
|
||||
This is the git repo for all the vantaMOO code.
|
||||
|
||||
Feel free to fork and make your own changes.
|
||||
|
||||
## Welcome to Evennia!
|
||||
|
||||
This is your game directory, set up to let you start with
|
||||
your new game right away. An overview of this directory is found here:
|
||||
|
@ -29,7 +34,7 @@ to your new game using a MUD client on `localhost`, port `4000`. You can
|
|||
also log into the web client by pointing a browser to
|
||||
`http://localhost:4001`.
|
||||
|
||||
# Getting started
|
||||
## Getting started
|
||||
|
||||
From here on you might want to look at one of the beginner tutorials:
|
||||
http://github.com/evennia/evennia/wiki/Tutorials.
|
||||
|
|
|
@ -16,14 +16,16 @@ own cmdsets by inheriting from them or directly from `evennia.CmdSet`.
|
|||
|
||||
from evennia import default_cmds
|
||||
|
||||
from commands.cmdcustomexamine import CmdCustomExamine
|
||||
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 commands.cmdcustomexamine import CmdCustomExamine
|
||||
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 PronounAdminCommand
|
||||
from lib.pronounsub import PronounsCommand
|
||||
from lib.rpsystem.rpsystem import RPSystemCmdSet
|
||||
from evennia.contrib.rpg import dice
|
||||
|
||||
|
||||
|
@ -67,6 +69,10 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
|
|||
# Clothing: wear
|
||||
self.add(ClothedCharacterCmdSet())
|
||||
|
||||
# gendersub
|
||||
self.add(PronounAdminCommand())
|
||||
self.add(PronounsCommand())
|
||||
|
||||
# Overrides @examine
|
||||
self.add(CmdCustomExamine())
|
||||
|
||||
|
|
4
lib/pronounsub/__init__.py
Normal file
4
lib/pronounsub/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
from .pronounsub import PronounCharacter
|
||||
from .pronounsub import PronounsCommand
|
||||
from .pronounsub import PronounAdminCommand
|
||||
from .db import PronounDbScript
|
16
lib/pronounsub/consts.py
Normal file
16
lib/pronounsub/consts.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
import re
|
||||
|
||||
DEFAULT_SPECIFIC_NEUTRAL_PRONOUNS = {"s": "they", "o": "them", "p": "their", "a": "theirs"}
|
||||
|
||||
DEFAULT_SPECIFIC_PRONOUNS = [
|
||||
{"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"}
|
||||
]
|
||||
|
||||
DEFAULT_CHARACTER_PRONOUNS = [ "they", "them" ]
|
||||
|
||||
PRONOUN_FORMS = [ "s", "o", "p", "a" ]
|
||||
|
||||
RE_GENDER_PRONOUN = re.compile(r"(?<!\|)\|(?!\|)[sSoOpPaA]")
|
8
lib/pronounsub/db.py
Normal file
8
lib/pronounsub/db.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
import copy
|
||||
|
||||
from evennia import DefaultScript
|
||||
from .consts import *
|
||||
|
||||
class PronounDbScript(DefaultScript):
|
||||
def at_script_creation(self):
|
||||
self.db.pronouns = copy.deepcopy(DEFAULT_SPECIFIC_PRONOUNS)
|
399
lib/pronounsub/pronounsub.py
Normal file
399
lib/pronounsub/pronounsub.py
Normal file
|
@ -0,0 +1,399 @@
|
|||
"""
|
||||
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
|
||||
|
||||
from evennia import Command, DefaultCharacter
|
||||
from evennia import InterruptCommand
|
||||
from evennia.utils import logger
|
||||
from evennia.utils.create import create_script
|
||||
from evennia.utils.search import search_script
|
||||
from .consts import *
|
||||
|
||||
# in-game command for setting the gender
|
||||
|
||||
def parse_specific_pronoun(pronoun):
|
||||
specific = [ p.strip() for p in pronoun.split(",") ]
|
||||
if len(specific) != len(PRONOUN_FORMS):
|
||||
return None
|
||||
|
||||
new = {}
|
||||
for i in range(len(specific)):
|
||||
new[PRONOUN_FORMS[i]] = specific[i]
|
||||
|
||||
return new
|
||||
|
||||
def get_pronoun_list():
|
||||
search = search_script("pronoun_db")
|
||||
if not any(search):
|
||||
script = create_script("lib.pronounsub.PronounDbScript", key="pronoun_db")
|
||||
else:
|
||||
script = search[0]
|
||||
|
||||
return script.db.pronouns
|
||||
|
||||
def specific_pronoun_from_character(character, caller=None):
|
||||
pronoun_list = get_pronoun_list()[:]
|
||||
if caller != None:
|
||||
# TODO: reorder
|
||||
pronoun_list += caller.db.pronoun_specs
|
||||
for pronoun in pronoun_list:
|
||||
if character in pronoun.values():
|
||||
return pronoun
|
||||
|
||||
return None
|
||||
|
||||
def stringify_specific_pronoun(spec):
|
||||
return ",".join([ spec[form] for form in PRONOUN_FORMS ])
|
||||
|
||||
class PronounAdminCommand(Command):
|
||||
"""
|
||||
Manages the list of known server-wide pronouns.
|
||||
|
||||
Usage:
|
||||
@pronounadmin help
|
||||
@pronounadmin list_known
|
||||
@pronounadmin spec <specific>
|
||||
@pronounadmin unspec <specific>
|
||||
|
||||
On each character and the server are stored character pronouns and specific
|
||||
pronouns. The character pronouns specify what pronouns should actually be
|
||||
used for your character, and the specific pronouns define all the
|
||||
grammatical forms of a pronoun so the character pronouns can be used in
|
||||
text substitution.
|
||||
|
||||
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*.
|
||||
|
||||
The `list_known` command lists all specific pronouns known by the server.
|
||||
|
||||
The `spec` command adds a new specific pronoun.
|
||||
|
||||
The `unspec` command removes an existing specific pronoun.
|
||||
|
||||
Examples:
|
||||
@pronounadmin spec they,them,their,theirs
|
||||
"""
|
||||
key = "pronounadmin"
|
||||
locks = "cmd:id(1) or perm(Admin)"
|
||||
|
||||
def parse(self):
|
||||
caller = self.caller
|
||||
|
||||
args = self.args.strip().lower().split()
|
||||
if len(args) == 0:
|
||||
caller.msg(self.get_help(caller, self.cmdset))
|
||||
raise InterruptCommand()
|
||||
|
||||
command = args[0]
|
||||
|
||||
if command == "list_known" and len(args) == 1:
|
||||
self.args = tuple([command])
|
||||
return
|
||||
|
||||
if len(args) != 2:
|
||||
caller.msg(self.get_help(caller, self.cmdset))
|
||||
raise InterruptCommand()
|
||||
|
||||
if not command in [ "spec", "unspec" ] or command == "help":
|
||||
caller.msg(self.get_help(caller, self.cmdset))
|
||||
raise InterruptCommand()
|
||||
|
||||
pronoun = parse_specific_pronoun(args[1])
|
||||
if pronoun == None:
|
||||
caller.msg(self.get_help(caller, self.cmdset))
|
||||
raise InterruptCommand()
|
||||
|
||||
self.args = ( command, pronoun )
|
||||
|
||||
def func(self):
|
||||
caller = self.caller
|
||||
|
||||
command = self.args[0]
|
||||
|
||||
if command == "list_known":
|
||||
caller.msg("Known server-defined pronouns:")
|
||||
for spec in get_pronoun_list():
|
||||
caller.msg(f" {stringify_specific_pronoun(spec)}")
|
||||
return
|
||||
|
||||
_, pronoun = self.args
|
||||
pronoun_list = get_pronoun_list()
|
||||
|
||||
if command == "spec":
|
||||
if not pronoun in pronoun_list:
|
||||
pronoun_list.append(pronoun)
|
||||
self.caller.msg(f"Added the pronoun {stringify_specific_pronoun(pronoun)}.")
|
||||
elif command == "unspec":
|
||||
if pronoun in pronoun_list:
|
||||
pronoun_list.remove(pronoun)
|
||||
self.caller.msg(f"Removed the pronoun {stringify_specific_pronoun(pronoun)}.")
|
||||
|
||||
class PronounsCommand(Command):
|
||||
"""
|
||||
Manages your pronouns.
|
||||
|
||||
Usage:
|
||||
@pronouns help
|
||||
@pronouns set <pronouns>
|
||||
@pronouns get
|
||||
@pronouns list_known
|
||||
@pronouns spec <specific>
|
||||
@pronouns unspec <specific>
|
||||
|
||||
On each character and the server are stored character pronouns and specific
|
||||
pronouns. The character pronouns specify what pronouns should actually be
|
||||
used for your character, and the specific pronouns define all the
|
||||
grammatical forms of a pronoun so the character pronouns can be used in
|
||||
text substitution.
|
||||
|
||||
The server already has some specific pronouns defined, so it's likely you
|
||||
can just give your character pronouns and be done. If the server doesn't
|
||||
know about your pronouns already then you'll have to tell the server how to
|
||||
use them with the `spec` command.
|
||||
|
||||
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*.
|
||||
|
||||
The `set` command:
|
||||
|
||||
Sets your current character pronouns. Pronouns look the same as how you'd
|
||||
write them anywhere online. They're separated by a slash ("/"), like
|
||||
"she/they/it", or "he/him". Each pronoun should match to the grammatical
|
||||
form of a specific pronoun. When multiple pronouns are given, like both
|
||||
"she" and "they", a random one will be used in each text substitution.
|
||||
|
||||
The `get` command prints your current configured character pronouns.
|
||||
|
||||
The `list_known` command lists all specific pronouns known by the
|
||||
server and specified for your acount.
|
||||
|
||||
The `spec` command adds a new specific pronoun.
|
||||
|
||||
The `unspec` command removes an existing specific pronoun.
|
||||
|
||||
Examples:
|
||||
@pronouns set he/him
|
||||
|
||||
@pronouns set she/they/it
|
||||
|
||||
@pronouns spec fae,faer,faer,faers
|
||||
"""
|
||||
|
||||
key = "pronouns"
|
||||
|
||||
def parse(self):
|
||||
caller = self.caller
|
||||
caller.pronoun_object_init()
|
||||
|
||||
args = self.args.strip().lower().split()
|
||||
if len(args) == 1:
|
||||
command = args[0]
|
||||
if not command in [ "get", "list_known" ] or command == "help":
|
||||
caller.msg(self.get_help(caller, self.cmdset))
|
||||
raise InterruptCommand()
|
||||
|
||||
self.args = tuple([command])
|
||||
return
|
||||
|
||||
if len(args) != 2:
|
||||
caller.msg(self.get_help(caller, self.cmdset))
|
||||
raise InterruptCommand()
|
||||
|
||||
command = args[0]
|
||||
|
||||
if command == "spec" or command == "unspec":
|
||||
new = parse_specific_pronoun(args[1])
|
||||
if new == None:
|
||||
caller.msg(self.get_help(caller, self.cmdset))
|
||||
raise InterruptCommand()
|
||||
else:
|
||||
res = new
|
||||
|
||||
elif command == "set":
|
||||
res = args[1].split('/')
|
||||
|
||||
self.args = (command, res)
|
||||
|
||||
def func(self):
|
||||
caller = self.caller
|
||||
caller.pronoun_object_init()
|
||||
|
||||
command = self.args[0]
|
||||
|
||||
match self.args[0]:
|
||||
case "set":
|
||||
_, pronouns = self.args
|
||||
has_unmatched = False
|
||||
for pronoun in pronouns:
|
||||
if not specific_pronoun_from_character(pronoun, caller):
|
||||
has_unmatched = True
|
||||
caller.msg(f"Warning: The character pronoun \"{pronoun}\" is "
|
||||
"not in any list of specific pronouns. It will "
|
||||
"be ignored. |/")
|
||||
if has_unmatched:
|
||||
caller.msg("You have unkown pronouns set! If you want to "
|
||||
"actually use them, you'll need to specify their "
|
||||
"specific form. Type `pronouns help` for "
|
||||
"details. |/")
|
||||
|
||||
caller.db.pronouns = pronouns
|
||||
caller.msg(f"Your pronouns have been set to {caller._stringify_pronouns()}.")
|
||||
|
||||
case "get":
|
||||
caller.msg(f"Your pronouns are {caller._stringify_pronouns()}.")
|
||||
|
||||
case "list_known":
|
||||
caller.msg("Known user-defined pronouns:")
|
||||
for spec in caller.db.pronoun_specs:
|
||||
caller.msg(f" {stringify_specific_pronoun(spec)}")
|
||||
caller.msg("Known server-defined pronouns:")
|
||||
for spec in get_pronoun_list():
|
||||
caller.msg(f" {stringify_specific_pronoun(spec)}")
|
||||
|
||||
case "spec":
|
||||
_, pronoun = self.args
|
||||
if not pronoun in caller.db.pronoun_specs:
|
||||
caller.db.pronoun_specs.append(pronoun)
|
||||
self.caller.msg(f"Added the pronoun {stringify_specific_pronoun(pronoun)}.")
|
||||
|
||||
case "unspec":
|
||||
_, pronoun = self.args
|
||||
if pronoun in caller.db.pronoun_specs:
|
||||
caller.db.pronoun_specs.remove(pronoun)
|
||||
self.caller.msg(f"Remobed the pronoun {stringify_specific_pronoun(pronoun)}.")
|
||||
|
||||
class PronounCharacter(DefaultCharacter):
|
||||
"""
|
||||
This is a Character class aware of gender.
|
||||
|
||||
"""
|
||||
|
||||
# This should be called before every use of a Character to make sure it's
|
||||
# initialized right.
|
||||
def pronoun_object_init(self):
|
||||
if self.db.pronouns == None:
|
||||
self.db.pronouns = DEFAULT_CHARACTER_PRONOUNS[:]
|
||||
if self.db.pronoun_specs == None:
|
||||
self.db.pronoun_specs = []
|
||||
|
||||
def at_object_creation(self):
|
||||
"""
|
||||
Called once when the object is created.
|
||||
"""
|
||||
super().at_object_creation()
|
||||
self.pronoun_object_init()
|
||||
|
||||
def _stringify_pronouns(self):
|
||||
pronouns = self.attributes.get("pronouns", default=DEFAULT_CHARACTER_PRONOUNS[:])
|
||||
return "/".join(pronouns)
|
||||
|
||||
def _get_pronoun(self, 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
|
||||
dup_specifics = []
|
||||
for pronoun in self.attributes.get("pronouns", default=DEFAULT_CHARACTER_PRONOUNS[:]):
|
||||
specific = specific_pronoun_from_character(pronoun, self)
|
||||
if specific != None:
|
||||
dup_specifics.append(specific)
|
||||
|
||||
specifics = []
|
||||
for s in dup_specifics:
|
||||
if not s in specifics:
|
||||
specifics.append(s)
|
||||
|
||||
if len(specifics) == 0:
|
||||
choice = DEFAULT_SPECIFIC_NEUTRAL_PRONOUNS
|
||||
else:
|
||||
choice = random.choice(specifics)
|
||||
|
||||
pronoun = choice[typ.lower()]
|
||||
return pronoun.capitalize() if typ.isupper() else pronoun
|
||||
|
||||
def msg(self, text=None, from_obj=None, session=None, **kwargs):
|
||||
self.pronoun_object_init()
|
||||
|
||||
"""
|
||||
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)
|
|
@ -8,8 +8,14 @@ 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().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().msg(text, from_obj, session, **kwargs)
|
||||
|
|
|
@ -14,6 +14,13 @@ just overloads its hooks to have it perform its function.
|
|||
|
||||
from evennia.scripts.scripts import DefaultScript
|
||||
|
||||
class TestScript(DefaultScript):
|
||||
def at_script_creation(self):
|
||||
self.key = "testscript"
|
||||
self.interval = 60
|
||||
|
||||
def at_repeat(self):
|
||||
self.obj.msg_contents("hewwo")
|
||||
|
||||
class Script(DefaultScript):
|
||||
"""
|
||||
|
|
Loading…
Reference in a new issue