mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Improved generation of Player Characters using charcreate
This commit is contained in:
parent
f6b43b0416
commit
dfd5ee3f8e
3 changed files with 80 additions and 56 deletions
|
|
@ -12,16 +12,16 @@ instead for most things).
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
import typing
|
||||||
from random import getrandbits
|
from random import getrandbits
|
||||||
|
|
||||||
|
import evennia
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import authenticate, password_validation
|
from django.contrib.auth import authenticate, password_validation
|
||||||
from django.core.exceptions import ImproperlyConfigured, ValidationError
|
from django.core.exceptions import ImproperlyConfigured, ValidationError
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.module_loading import import_string
|
from django.utils.module_loading import import_string
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
import evennia
|
|
||||||
from evennia.accounts.manager import AccountManager
|
from evennia.accounts.manager import AccountManager
|
||||||
from evennia.accounts.models import AccountDB
|
from evennia.accounts.models import AccountDB
|
||||||
from evennia.commands.cmdsethandler import CmdSetHandler
|
from evennia.commands.cmdsethandler import CmdSetHandler
|
||||||
|
|
@ -272,6 +272,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
|
|
||||||
objects = AccountManager()
|
objects = AccountManager()
|
||||||
|
|
||||||
|
# Used by account.create_character() to choose default typeclass for characters.
|
||||||
|
player_character_typeclass = settings.BASE_CHARACTER_TYPECLASS
|
||||||
|
|
||||||
# properties
|
# properties
|
||||||
@lazy_property
|
@lazy_property
|
||||||
def cmdset(self):
|
def cmdset(self):
|
||||||
|
|
@ -315,7 +318,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
"""
|
"""
|
||||||
pass
|
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.
|
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}.")
|
logger.log_sec(f"Password successfully changed for {self}.")
|
||||||
self.at_password_change()
|
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):
|
def create_character(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Create a character linked to this account.
|
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.
|
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
|
# parse inputs
|
||||||
character_key = kwargs.pop("key", self.key)
|
character_key = kwargs.pop("key", self.key)
|
||||||
character_ip = kwargs.pop("ip", self.db.creator_ip)
|
character_ip = kwargs.pop("ip", self.db.creator_ip)
|
||||||
character_permissions = kwargs.pop("permissions", self.permissions)
|
character_permissions = kwargs.pop("permissions", self.permissions)
|
||||||
|
|
||||||
# Load the appropriate Character class
|
# Load the appropriate Character class
|
||||||
character_typeclass = kwargs.pop("typeclass", None)
|
character_typeclass = kwargs.pop("typeclass", self.player_character_typeclass)
|
||||||
character_typeclass = (
|
|
||||||
character_typeclass if character_typeclass else settings.BASE_CHARACTER_TYPECLASS
|
|
||||||
)
|
|
||||||
Character = class_from_module(character_typeclass)
|
Character = class_from_module(character_typeclass)
|
||||||
|
|
||||||
if "location" not in kwargs:
|
if "location" not in kwargs:
|
||||||
|
|
@ -832,12 +870,29 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
if character:
|
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)
|
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
|
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
|
@classmethod
|
||||||
def create(cls, *args, **kwargs):
|
def create(cls, *args, **kwargs):
|
||||||
|
|
@ -960,10 +1015,10 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
|
|
||||||
if account and _AUTO_CREATE_CHARACTER_WITH_ACCOUNT:
|
if account and _AUTO_CREATE_CHARACTER_WITH_ACCOUNT:
|
||||||
# Auto-create a character to go with this account
|
# Auto-create a character to go with this account
|
||||||
|
call_kwargs = {}
|
||||||
character, errs = account.create_character(
|
if "character_typeclass" in kwargs:
|
||||||
typeclass=kwargs.get("character_typeclass")
|
call_kwargs["character_typeclass"] = kwargs["character_typeclass"]
|
||||||
)
|
character, errs = account.create_character(**call_kwargs)
|
||||||
if errs:
|
if errs:
|
||||||
errors.extend(errs)
|
errors.extend(errs)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -146,53 +146,20 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
|
||||||
self.msg("Usage: charcreate <charname> [= description]")
|
self.msg("Usage: charcreate <charname> [= description]")
|
||||||
return
|
return
|
||||||
key = self.lhs
|
key = self.lhs
|
||||||
desc = self.rhs
|
description = self.rhs or "This is a character."
|
||||||
|
|
||||||
if _MAX_NR_CHARACTERS is not None:
|
new_character, errors = self.account.create_character(key=key, description=description, ip=self.session.address)
|
||||||
if (
|
|
||||||
not account.is_superuser
|
|
||||||
and not account.check_permstring("Developer")
|
|
||||||
and account.characters
|
|
||||||
and len(account.characters) >= _MAX_NR_CHARACTERS
|
|
||||||
):
|
|
||||||
plural = "" if _MAX_NR_CHARACTERS == 1 else "s"
|
|
||||||
self.msg(f"You may only have a maximum of {_MAX_NR_CHARACTERS} character{plural}.")
|
|
||||||
return
|
|
||||||
from evennia.objects.models import ObjectDB
|
|
||||||
|
|
||||||
typeclass = settings.BASE_CHARACTER_TYPECLASS
|
if errors:
|
||||||
|
self.msg(errors)
|
||||||
if ObjectDB.objects.filter(db_typeclass_path=typeclass, db_key__iexact=key):
|
if not new_character:
|
||||||
# check if this Character already exists. Note that we are only
|
|
||||||
# searching the base character typeclass here, not any child
|
|
||||||
# classes.
|
|
||||||
self.msg(f"|rA character named '|w{key}|r' already exists.|n")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# create the character
|
|
||||||
start_location = ObjectDB.objects.get_id(settings.START_LOCATION)
|
|
||||||
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
|
|
||||||
permissions = settings.PERMISSION_ACCOUNT_DEFAULT
|
|
||||||
new_character = create.create_object(
|
|
||||||
typeclass, key=key, location=start_location, home=default_home, permissions=permissions
|
|
||||||
)
|
|
||||||
# only allow creator (and developers) to puppet this char
|
|
||||||
new_character.locks.add(
|
|
||||||
"puppet:id(%i) or pid(%i) or perm(Developer) or pperm(Developer);delete:id(%i) or"
|
|
||||||
" perm(Admin)" % (new_character.id, account.id, account.id)
|
|
||||||
)
|
|
||||||
account.characters.add(new_character)
|
|
||||||
if desc:
|
|
||||||
new_character.db.desc = desc
|
|
||||||
elif not new_character.db.desc:
|
|
||||||
new_character.db.desc = "This is a character."
|
|
||||||
self.msg(
|
self.msg(
|
||||||
f"Created new character {new_character.key}. Use |wic {new_character.key}|n to enter"
|
f"Created new character {new_character.key}. Use |wic {new_character.key}|n to enter"
|
||||||
" the game as this character."
|
" the game as this character."
|
||||||
)
|
)
|
||||||
logger.log_sec(
|
|
||||||
f"Character Created: {new_character} (Caller: {account}, IP: {self.session.address})."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CmdCharDelete(COMMAND_DEFAULT_CLASS):
|
class CmdCharDelete(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
|
||||||
|
|
@ -2548,8 +2548,9 @@ class DefaultCharacter(DefaultObject):
|
||||||
try:
|
try:
|
||||||
# Check to make sure account does not have too many chars
|
# Check to make sure account does not have too many chars
|
||||||
if account:
|
if account:
|
||||||
if len(account.characters) >= settings.MAX_NR_CHARACTERS:
|
avail = account.check_available_slots()
|
||||||
errors.append(_("There are too many characters associated with this account."))
|
if avail:
|
||||||
|
errors.append(avail)
|
||||||
return obj, errors
|
return obj, errors
|
||||||
|
|
||||||
# Create the Character
|
# Create the Character
|
||||||
|
|
@ -2604,7 +2605,8 @@ class DefaultCharacter(DefaultObject):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_name(cls, name):
|
def validate_name(cls, name):
|
||||||
"""Validate the character name prior to creating. Overload this function to add custom validators
|
"""
|
||||||
|
Validate the character name prior to creating. Overload this function to add custom validators
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name (str) : The name of the character
|
name (str) : The name of the character
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue