""" Characters Characters are (by default) Objects setup to be puppeted by Accounts. They are what you "see" in game. The Character class in this module is setup to be the "default" character type created by the default creation commands. """ from datetime import datetime, timedelta from re import match, compile, sub from evennia.commands.command import InterruptCommand from evennia.contrib.game_systems.gendersub import GenderCharacter from evennia.contrib.rpg.rpsystem import ContribRPCharacter, send_emote from evennia.prototypes.spawner import spawn from evennia.utils import delay, logger, int2str from evennia.utils.search import search_object, search_objects_by_typeclass from utils.word_list import routput, choices, fix_msg from .objects import Object from .tutorial import TutorBird, TutorialState INTRO = """ As the surrounding mists dissipate, you find yourself in an ancient, halcyon forest dripping with moss. You see an envelope of parchment wedged under a scaly protrusion of bark...inside, a letter in familiar penmanship, personally addressed to you, which you pick up. A little gray bird flies by you, almost grazing your ear!""" READ_LETTER = """You read a letter with an oddly familiar penmanship: My dear {0}, If you are reading this, you've found the world I was overly excited in relaying to you over drinks in Marsivan. Most excellent. Enjoy this halcyon world, unspoiled and idyllic. I would suggest seeing/doing these: - Jump in a puddle - Sit on some moss - Feed the beast until it is friendly - Catch an obnoxious fish - Get and read a book - Have some tea and scones Oh, I also suggest checking out |wWyldwood|n, a fabulous bar that doesn't open very often, but is quite fun when it does. I'm here, so join me in a cup of tea and we can reconnect and reminisce of glorious days gone by, and the utter curiosity that surrounds us. Your friend, Dabbler (Type 'help start' for details on playing this game)""" class Character(Object, GenderCharacter, ContribRPCharacter): """ The Character just re-implements some of the Object's methods and hooks to represent a Character entity in-game. See mygame/typeclasses/objects.py for a list of properties and methods available on all Object child classes like this. """ pose = True def at_object_creation(self): "called when a character is first created." self.db.tutorstate = 0 if self.dbref != "#1" and not self.is_typeclass('typeclasses.puppets.Puppet'): self.create_letter() self.create_ticket() self.create_pouch() def utf(self): """ Return True if character's user encoding is UTF-8. """ session = self.sessions.get()[0] if session: flags = session.protocol_flags return flags.get("ENCODING") == 'utf-8' return False def delete_inv(self, typeclass): for obj in self.contents: if obj.is_typeclass(typeclass): obj.delete() def at_post_puppet(self, **kwargs): if self.db.visited and not self.db.guest_account: self.msg(f"""\n“Welcome back, {self.key.capitalize()}.”\n""") if self.location.key == "Wyldwood Bar": self.msg("You wake up in a meadow with a strange dream of a bar...") self.delete_inv("typeclasses.drinkables.Cocktail") meadow = self.search("Frog Meadow", global_search=True, quiet=True) if isinstance(meadow, list): meadow = meadow[0] self.move_to(meadow, quiet=True, use_destination=True) else: self.execute_cmd("look") else: self.db.visited = True self.db.tutorstate = 0 TutorBird.do_start_tutorial(self) self.msg(INTRO) self.fix_letter() self.account.db._last_puppet = self def at_pre_unpuppet(self, **kwargs): """ Make sure we aren't left sitting down when logging out. """ if self.db.is_sitting: chair = self.db.is_sitting chair.db.sitter = None self.db.is_sitting = None def msg(self, text=None, from_obj=None, session=None, **kwargs): """ Capitalizes messages sent to the user. This just looks better to me. """ text = fix_msg(text) super().msg(text, from_obj=from_obj, session=session, **kwargs) def create_pouch(self, name="pouch", desc="leather pouch", giver=None): """ Create a leather pouch of coins. Fill it with ten gold coins, so they can play the games. """ pouch = spawn({ "typeclass": "typeclasses.things.CoinPurse", "key": name, "desc": f"A {desc} containing coins.", })[0] pouch.db.gold_amount = 10 pouch.location = self if giver: self.announce_action(f"The {giver.get_display_name(self)} tosses a {desc} to $you().") self.msg(f"You now have a {name} with {int2str(pouch.db.gold_amount)} coins.") def get_pouch(self): """ Return a pouch object. Throws InterruptCommand exception if the character has no pouch, and therefore, no money. """ pouches = [item for item in self.contents if item.is_typeclass("typeclasses.things.CoinPurse")] if pouches: return pouches[0] raise InterruptCommand("No coin purse") def how_many_coins(self): "Return the amount of money in the coin purse." return self.get_pouch().how_much() def adjust_coins(self, amount): "Increase or decrease the amount of money in the coin purse." return self.get_pouch().adjust_amount(amount) def has_coins(self, at_least=1): "Return True if character has 'at_least' that many coins." return self.get_pouch().has_amount(at_least) def create_letter(self): "create a welcome letter in a character's inventory" letter = spawn({ "typeclass": "typeclasses.readables.Letter", "key": "letter", "desc": "An envelope with a letter of familiar penmanship.", })[0] letter.db.inside = READ_LETTER.format(self.name.capitalize()) letter.location = self def fix_letter(self): """ Adjust the letter initially created for the character. This replaces the weird, auto-generated name with the character's actual name. """ letter = self.search('letter', location=self, quiet=True) if letter and isinstance(letter, list) and len(letter) > 0: letter = letter[0] if letter: text = sub(r"My dear [^,]*", f"My dear {self.name}", letter.db.inside) letter.db.inside = text def create_ticket(self): """ Create a dated ticket. """ today = datetime.now() days_ahead = (1 - today.weekday() + 7) % 7 # 1 is Tuesday next_tuesday_date = today + timedelta(days=days_ahead) tuesday = next_tuesday_date.date() day = tuesday.day month = tuesday.strftime("%B") # Full month name if 10 <= day % 100 <= 20: suffix = 'th' else: suffix = {1: 'st', 2: 'nd', 3: 'rd'}.get(day % 10, 'th') ticket = spawn({ "typeclass": "typeclasses.readables.Letter", "key": "ticket", "desc": "A ticket to |wWyldwood Bar|n.", })[0] ticket.db.inside = f""" The ticket is dated, {day}{suffix} of {month} ... It also reads that the portal opens in the field at seven. """ ticket.location = self def get_display_things(self, looker, *args, **kwargs): return super().get_display_things(looker, pose=False) def do_take(self, to_take, victim): """ A character has a _steal_command. What are the limitations? """ if victim: thing = None if hasattr(victim, 'has') and callable(getattr(victim, 'has')): thing = victim.has(to_take) if thing and thing.db.can_take: self.msg(f"You take {thing.key} from {victim.key}.") thing.move_to(self, quiet=True, use_destination=True) return self.msg(f"{victim.key} doesn't have a {to_take} you can take.") def pronoun_subjective(self, uppercase=False): gender = self.attributes.get('gender') if gender == "male": results = 'He' elif gender == "female": results = 'She' elif gender == "neutral": results = 'It' else: results = 'They' return results if uppercase else results.lower() def gendered_text(self, text): """ Replace entries, like |s and |p with pronouns. Like 'he' and 'her' """ gender_rx = compile(r"(? 0: return targets for target in self.location.contents: logger.info(f"Looking for books at {target}") inv_items = self.search(item_str, quiet=True, location=target) if len(inv_items) > 0: return inv_items return None # Local Variables: # jinx-local-words: "Marsivan" # End: