Merge branch 'vanta' into dice

This commit is contained in:
Alexander Yakovlev 2023-01-17 18:50:36 +06:00
commit bab90c1ba7
9 changed files with 460 additions and 10 deletions

1
.gitignore vendored
View File

@ -14,7 +14,6 @@ var
sdist
develop-eggs
.installed.cfg
lib
lib64
__pycache__

View File

@ -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.

View File

@ -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())

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

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

View File

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

View File

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