diff --git a/commands/take.py b/commands/take.py index c28589a..93bcbd1 100755 --- a/commands/take.py +++ b/commands/take.py @@ -10,7 +10,7 @@ class CmdTake(Command): Added to the default_cmdsets. """ key = "take" - aliases = ["get", "remove"] + aliases = ["remove"] # only allow this command if command.obj is carried by caller. # locks = "cmd:holds()" @@ -19,5 +19,23 @@ class CmdTake(Command): Implements the take command. Since this command is designed to work on the object, we operate only on self.obj. """ - logger.log_info(f"Dealing with {self.caller.key}") + # logger.log_info(f"Dealing with {self.caller.key}") self.caller.do_take(self.args) + +class CmdKnock(Command): + """ + The ability to knock on something, like a door. + + While it would be great if it could be attached to a door or some + other exit, we need to attach this to the room. + + """ + key = "knock" + + def func(self): + logger.log_info(f"Seems like {self.caller.key} wants to knock on me.") + self.obj.do_knock(self.caller) + +class CmdSetKnock(CmdSet): + def at_cmdset_creation(self): + self.add(CmdKnock) diff --git a/typeclasses/characters.py b/typeclasses/characters.py index f5a21f4..53a202b 100644 --- a/typeclasses/characters.py +++ b/typeclasses/characters.py @@ -11,10 +11,10 @@ creation commands. from evennia.objects.objects import DefaultCharacter from utils.word_list import Token, routput -from .objects import ObjectParent +from .objects import Object -class Character(ObjectParent, DefaultCharacter): +class Character(Object, DefaultCharacter): """ The Character just re-implements some of the Object's methods and hooks to represent a Character entity in-game. diff --git a/typeclasses/exits.py b/typeclasses/exits.py index 8e3b476..4c3c5d2 100644 --- a/typeclasses/exits.py +++ b/typeclasses/exits.py @@ -10,7 +10,7 @@ for allowing Characters to traverse the exit to its destination. from evennia.objects.objects import DefaultExit from .objects import ObjectParent - +from commands.take import CmdSetKnock class Exit(ObjectParent, DefaultExit): """ @@ -30,3 +30,12 @@ class Exit(ObjectParent, DefaultExit): return super().at_traverse(traveler, destination) else: return False + +class KnockableExit(Exit): + def at_object_creation(self): + self.cmdset.add_default(CmdSetKnock) + + def do_knock(self, knocker): + knocker.msg("You knock.") + self.location.msg_contents(f"{knocker.name} knocks on {self.key}.", + exclude=knocker) diff --git a/typeclasses/readables.py b/typeclasses/readables.py index 875c446..41a9283 100755 --- a/typeclasses/readables.py +++ b/typeclasses/readables.py @@ -4,7 +4,7 @@ from typeclasses.objects import Object from typeclasses.rooms import DabblersRoom from evennia import Command, CmdSet from evennia.prototypes.spawner import spawn -from utils.word_list import routput, character_has, Token +from utils.word_list import routput, Token import random diff --git a/typeclasses/rooms_weather.py b/typeclasses/rooms_weather.py index cb60864..92920e7 100755 --- a/typeclasses/rooms_weather.py +++ b/typeclasses/rooms_weather.py @@ -3,6 +3,9 @@ import random from .rooms import Room +from commands.take import CmdSetKnock +from .scripts import Script, KnockScript + from evennia import ( TICKER_HANDLER, CmdSet, @@ -14,10 +17,9 @@ from evennia import ( search_object, syscmdkeys, utils, + create_script, ) -print("Loading the weather...") - # ------------------------------------------------------------- # # Weather room - room with a ticker @@ -153,3 +155,27 @@ class TimeWeatherRoom(Room): if self.db.previous_weather != msg: self.db.previous_weather = msg self.msg_contents(f"|w{msg}|n\n") + + +class KnockableOutsideRoom(TimeWeatherRoom): + def at_object_creation(self): + super().at_object_creation() + self.cmdset.add_default(CmdSetKnock) + + def do_knock(self, knocker): + door = self.search("knocker") + if door.has("ring"): + knock_script = create_script(key="knocking", + typeclass=KnockScript, + interval=10, # seconds <=0 means off + start_delay=True, # wait interval before first call + autostart=True, + attributes=[("knocker", knocker)]) + knock_script.db.waker = door + knock_script.db.knocker = knocker + + knocker.msg("You grab the ring and knock firmly on the door.") + self.msg_contents(f"{knocker.name} grabs the ring and knocks firmly on the door.", + exclude=knocker) + else: + knocker.msg("This door knocker is defective, as it doesn't have a ring to...er, do the knockin'.") diff --git a/typeclasses/scripts.py b/typeclasses/scripts.py index 06e2385..24099ff 100644 --- a/typeclasses/scripts.py +++ b/typeclasses/scripts.py @@ -13,6 +13,8 @@ just overloads its hooks to have it perform its function. """ from evennia.scripts.scripts import DefaultScript +from evennia.utils import logger +from utils.support import god_msg class Script(DefaultScript): @@ -99,5 +101,18 @@ class Script(DefaultScript): at_server_start() """ - pass + +class KnockScript(Script): + """ + A script to wake the dead. + """ + def at_start(self): + knocker = self.attributes.get("knocker") + if knocker: + god_msg(f"{knocker.key} stands at the door, and knocks.") + + def at_repeat(self): + if self.db.waker: + self.db.waker.knocked_timed_out() + self.stop() diff --git a/typeclasses/sittables.py b/typeclasses/sittables.py index 819d61d..be6c13e 100755 --- a/typeclasses/sittables.py +++ b/typeclasses/sittables.py @@ -71,12 +71,12 @@ class Sittables(Sittable): def sit_msg(self): adjective = self.db.adjective or "on" article = self.db.article or "the" - default = f"{article} {self.db.name}" + name = aliases[-1] # Last alias is singular + default = f"{article} {name}" singular = self.db.singular or default extra = self.db.extra or "" aliases = self.aliases.all() - name = aliases[1] # Second alias is singular return routput(f"You sit {adjective} {singular}. {extra}") diff --git a/typeclasses/things.py b/typeclasses/things.py index de93515..69755b3 100755 --- a/typeclasses/things.py +++ b/typeclasses/things.py @@ -51,14 +51,14 @@ class Knocker(Object): [r"knocker|goblin", [ "[Sorry.|What was that?|Did you say something?] I'm hard of hearing on account of the brass ears.", "Yes, I suppose I'm this amazing puzzle you get to in Chapter Three. Wait, does that mean I'm just an NPC?", - "Who me? I thought you were talking to the goblin in the bushes." + "Who me? I thought you were talking to the goblin in the bushes.", "Why yes, I am hard of hearing.", ]], ["password", [ "Of course this door is protected by a super complicated encrypted password.", "If I tell you, it wouldn't be a secret now.", "The password? You just have to guess.", - "Well, I suppose I could give you a hint." + "Well, I suppose I could give you a |bhint|n.", ]], ["hint", [ "A hint does sound fair. [Should I|I should] come up with a |briddle|n[|, huh]?" @@ -83,7 +83,7 @@ class Knocker(Object): things out, wait, where was I? """ ]], - ["hello|greet", [ + [r"\bhello|\bgreet|\bhey\b", [ "How's it going?", "How's it?", "'Sup.", @@ -107,14 +107,14 @@ class Knocker(Object): So, you see, if you speak the |bpassword|n, wait, I've said too much. """ ]], - ["\byes|yeah|yah\b", [ + [r"\byes|yeah|yah\b", [ "Really? You agree?", "Excellent", ]], - ["\bno|nope\b", [ + [r"\bno|nope\b", [ "Well, it's true. Just ask the [raven|trees|gnome].", ]], - ["\?$", [ + [r"\?$", [ "What about [me|it|'im]?", "I dunno...", ]], @@ -163,7 +163,8 @@ class Knocker(Object): # Let's see if a keyword gives a good response: for idx, [regex, responses] in enumerate(self.all_responses): - if re.match(r".*" + regex + r".*", message): + full_regex = r".*" + regex + r".*" + if re.match(full_regex, message): # The first match is the password, so we set a tag on # the character, so that they can go through the door: if idx == 0: @@ -248,3 +249,9 @@ class Knocker(Object): else: self.at_say("Umph") return True + + def knocked_timed_out(self): + if self.has("brass ring"): + self.at_say(choice(self.muffled_responses)) + else: + self.at_say("Doesn't appear that anyone is home.") diff --git a/utils/support.py b/utils/support.py index b1cdd1b..211990e 100755 --- a/utils/support.py +++ b/utils/support.py @@ -20,12 +20,11 @@ def character_has(holder, item): elif i == item: return i +def god_msg(msg, sender=None, location=None): + dabbler = Character.objects.search_object("#1")[0] + dabbler.msg(f"{msg} {'from ' + sender if sender else ''} {'at ' + location if location else ''}") + def debug(msg, sender=None, location=None): - dabbler = Character.objects.search_object("Dabbler")[0] - full_msg = "DEBUG:" - if sender: - full_msg += f" (from {sender})" - if location: - full_msg += f" (at {location})" - full_msg += f" {msg}" - dabbler.msg(full_msg) + god_msg(f"DEBUG: {msg}", sender, location) + +# py from typeclasses.characters import Character; god = Character.objects.search_object("#1")[0]