Improved generation of Player Characters using charcreate

This commit is contained in:
Andrew Bastien 2023-05-05 18:31:07 -04:00
parent f6b43b0416
commit dfd5ee3f8e
3 changed files with 80 additions and 56 deletions

View file

@ -12,16 +12,16 @@ instead for most things).
"""
import re
import time
import typing
from random import getrandbits
import evennia
from django.conf import settings
from django.contrib.auth import authenticate, password_validation
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.utils import timezone
from django.utils.module_loading import import_string
from django.utils.translation import gettext as _
import evennia
from evennia.accounts.manager import AccountManager
from evennia.accounts.models import AccountDB
from evennia.commands.cmdsethandler import CmdSetHandler
@ -272,6 +272,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
objects = AccountManager()
# Used by account.create_character() to choose default typeclass for characters.
player_character_typeclass = settings.BASE_CHARACTER_TYPECLASS
# properties
@lazy_property
def cmdset(self):
@ -315,7 +318,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
"""
pass
def at_post_remove_character(self, character: "DefaultAccount"):
def at_post_remove_character(self, character: "DefaultCharacter"):
"""
Called after a character is removed from this account's list of playable characters.
@ -790,6 +793,40 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
logger.log_sec(f"Password successfully changed for {self}.")
self.at_password_change()
def get_character_slots(self) -> typing.Optional[int]:
"""
Returns the number of character slots this account has.
By default, that's settings.MAX_NR_CHARACTERS but this makes it easy to override.
Maybe for your game, players can be rewarded with more slots, somehow.
If it returns None, then there are no limits on character slots.
"""
return settings.MAX_NR_CHARACTERS
def get_available_character_slots(self) -> typing.Optional[int]:
"""
Returns the number of character slots this account has available.
"""
if (slots := self.get_character_slots()) is None:
return None
return max(0, slots - len(self.characters))
def check_available_slots(self, **kwargs) -> typing.Optional[str]:
"""
Helper method used to determine if an account can create additional characters using
the character slot system.
Returns:
str (optional): An error message regarding the status of slots. If present, this
will halt character creation. If not, character creation can proceed.
"""
if (slots := self.get_available_character_slots()) is not None:
if slots <= 0:
if not (self.is_superuser or self.check_permstring("Developer")):
plural = "" if (max_slots := self.get_character_slots()) == 1 else "s"
return f"You may only have a maximum of {max_slots} character{plural}."
def create_character(self, *args, **kwargs):
"""
Create a character linked to this account.
@ -807,16 +844,17 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
list or None: A list of errors, or None.
"""
# check character slot usage.
if (slot_check := self.check_available_slots()):
return None, [slot_check]
# parse inputs
character_key = kwargs.pop("key", self.key)
character_ip = kwargs.pop("ip", self.db.creator_ip)
character_permissions = kwargs.pop("permissions", self.permissions)
# Load the appropriate Character class
character_typeclass = kwargs.pop("typeclass", None)
character_typeclass = (
character_typeclass if character_typeclass else settings.BASE_CHARACTER_TYPECLASS
)
character_typeclass = kwargs.pop("typeclass", self.player_character_typeclass)
Character = class_from_module(character_typeclass)
if "location" not in kwargs:
@ -832,12 +870,29 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
**kwargs,
)
if character:
# Update playable character list
self.at_post_create_character(character, ip=character_ip)
return character, errs
def at_post_create_character(self, character, **kwargs):
"""
An overloadable hook method that allows for further customization of newly created characters.
"""
if character not in self.characters:
self.characters.add(character)
# We need to set this to have @ic auto-connect to this character
# We need to set this to have @ic auto-connect to this character
if len(self.characters) == 1:
self.db._last_puppet = character
return character, errs
character.locks.add(
f"puppet:id({character.id}) or pid({self.id}) or perm(Developer) or pperm(Developer);delete:id({self.id}) or"
" perm(Admin)"
)
logger.log_sec(
f"Character Created: {character} (Caller: {self}, IP: {kwargs.get('ip', None)})."
)
@classmethod
def create(cls, *args, **kwargs):
@ -960,10 +1015,10 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
if account and _AUTO_CREATE_CHARACTER_WITH_ACCOUNT:
# Auto-create a character to go with this account
character, errs = account.create_character(
typeclass=kwargs.get("character_typeclass")
)
call_kwargs = {}
if "character_typeclass" in kwargs:
call_kwargs["character_typeclass"] = kwargs["character_typeclass"]
character, errs = account.create_character(**call_kwargs)
if errs:
errors.extend(errs)