""" 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 evennia.objects.objects import DefaultCharacter from evennia.prototypes.spawner import spawn from utils.word_list import Token, routput from .objects import Object from .tutorial import TutorBird, TutorialState from re import match 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 blue bird flies by you, almost grazing your ear!""" READ_LETTER = """You read a letter with an oddly familiar penmanship: My dearest {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'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, DefaultCharacter): """ 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. """ def at_object_creation(self): "called when a character is first created." self.db.tutorstate = 0 if self.dbref != "#1": self.create_letter() TutorBird.do_start_tutorial(self) def at_post_puppet(self): if self.db.visited: self.msg(f"""\n“Welcome back, {self.key.capitalize()}.”\n""") self.execute_cmd("look") else: self.db.visited = True self.db.tutorstate = 0 self.msg(INTRO) self.account.db._last_puppet = self def create_letter(self): "create a welcome letter in a character's inventory" letter = spawn({ "typeclass": "typeclasses.readables.Letter", "key": "letter", "desc": "A letter of familiar penmanship stuffed in an envelope.", })[0] letter.db.inside = READ_LETTER.format(self.name.capitalize()) letter.location = self def do_take(self, to_take, from_whom): """ A character has a _steal_command. What are the limitations? """ victim = self.search(from_whom) if victim: thing = victim.has(to_take) if thing and thing.db.can_take: self.msg(f"You take {thing.key} from {victim.key}.") self.location.msg_contents( f"{self.key} takes {thing.key} from {victim.key}!", exclude=self) thing.move_to(self, quiet=True, use_destination=True) return self.msg(f"{victim.key} doesn't have a {to_take} you can take.") else: self.msg(f"You don't see {from_whom}.") def at_pre_move(self, destination, **kwargs): """ Called by self.move_to when trying to move somewhere. If this returns False, the move is immediately canceled. """ self.db.tutorstate = self.db.tutorstate | TutorialState.MOVE.value if self.db.is_sitting: self.msg("You stand up first...") self.db.is_sitting = False; # @lock thing = tethered:id(#19) # @set thing/tethered_msg = "Let's put that back" for thing in self.contents: to = thing.locks.get('tethered') if to: m = match(r".*:id\(#?(.*)\)", to) if m: id_num = m.group(1) dest = self.global_search(f"#{id_num}") msg = thing.db.tethered_msg if dest and msg: thing.location = dest self.msg(msg) else: print(f"Found tethered, {thing} to #{id_num}, but dest is {dest} and msg is '{msg}'. Set both.") else: print(f"Found tethered, {thing} to #{to}, but that needs to be 'tethered:id(num) where num is the ID# of an object/place.") return super().at_pre_move(destination) def at_pre_say(self, message, **kwargs): "While we could/should do 'at_say', this should be easier." self.db.tutorstate = self.db.tutorstate | TutorialState.SAY.value return super().at_pre_say(message) def at_look(self, target, **kwargs): """ When we look at something that _might_ be hidden, we check if we are looking at something that _is_ hidden, i.e. has a view lock of a particular tag, and if so, we interpret this as 'you are looking at something by name', therefore you see it. So we give ourselves the right to see it from now on. To use this, simply add the following lock: @lock thing = view:tag(hidden_thing, mp) Where thing is the single name of the hidden object. """ hidden_tag = f"hidden_{target}" lock_string = f"view:tag({hidden_tag}, mp)" view_lock = target.locks.get("view") if view_lock == lock_string: self.tags.add(hidden_tag, category="mp") if target.is_typeclass("typeclasses.rooms.Room"): self.db.tutorstate = self.db.tutorstate | TutorialState.LOOK.value else: self.db.tutorstate = self.db.tutorstate | TutorialState.LOOKAT.value # Regardless of what happened before, we return the normal # function call. return super().at_look(target)