From bc380928e78d00f1b16fe2c1b54a2ef6dbbde15d Mon Sep 17 00:00:00 2001 From: Howard Abrams Date: Mon, 14 Apr 2025 16:39:34 -0700 Subject: [PATCH] Expand conversation with the Door Knocker --- typeclasses/things.py | 222 ++++++++++++++++++++++++++++-------------- world/version1.ev | 4 +- 2 files changed, 151 insertions(+), 75 deletions(-) diff --git a/typeclasses/things.py b/typeclasses/things.py index 090b42e..a8caaa1 100755 --- a/typeclasses/things.py +++ b/typeclasses/things.py @@ -1,6 +1,7 @@ #!/usr/bin/env python from random import choice, random +from enum import Enum from evennia import create_script from evennia.utils import logger, delay @@ -91,6 +92,18 @@ class Puddle(Object): exclude=player) + +class Knocker_Convo(Enum): + "What topics have we covered?" + ANY = 0 + GREETED = 1 + DOOR = 2 + LOCK = 3 + PASS = 4 + HINT = 5 + RIDDLE = 6 + ANSWERED = 7 + class Knocker(Object): """ Knocker @@ -107,62 +120,94 @@ class Knocker(Object): "Umf! Umfmmmf mmm mmmuufffmm mff mmuuummph.", ] + yes_msg = r"\byes|yeah|yah|sure|please\b" + no_msg = r"\bno|nope\b" + question_msg = r"\?$" + greet_msg = r"\bhello|\bgreet|\bhey\b" + + # Each state of conversation is listed: + close_responses = [ + "Oooo...good guess, but we are looking for something more specific.", + ] + answered_responses = [ + "Alright, I'll open the door for yah.", + "I heard my favorite, let's get this door unlocked.", + ] + # Note that the responses are ordered from most specific to least. + knocker_responses = [ + "[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.", + "Why yes, I am hard of hearing.", + ] + password_responses = [ + "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...unless you want a hint?", + "Well, I suppose I could give you a |bhint|n.", + ] + hint_responses = [ + "A hint does sound fair. [Should I|I should] come up with a |briddle|n[|, huh]?" + ] + riddle_responses = [ + "Aged in barrels, smooth and neat, in a glass by the fire, I'm a treat.", + "A scent of oak, a whiff of grain, a drop of expertise from the cask remains.", + """ + In oaken halls, I sleep and bide, + till I'm called to warm your insides... + hrm, that's a little vague, but kinda nice. + """, + """ + Alright, alright, the riddle should be clever. + It should refer to its golden hue, and I should make it obvious + that it isn't gold, and adding a reference to quest of the Argonauts + would be a complete red herring, and quite a mean thing to do, + so I shouldn't add that, + but what about barrels? Yeah, need to include barrels, but not the + way Bilbo road barrels, for that was definitely intended as a misleading + riddle for Smaug, but of course, a smart dragon would always figure such + things out, wait, where was I? + """ + ] + + locked_responses = [ + "Yes, I'm familiar with the door and the fact that it is locked.", + "This locked door is to protect the theft of Dabbler's scones.", + ] all_responses = [ # The password must be the first entry here, as we "act" on it: - ["whiske?y", [ - "Alright, I'll open the door for yah.", - "I heard my favorite, let's get this door unlocked.", - ]], - [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.", - "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 |bhint|n.", - ]], - ["hint", [ - "A hint does sound fair. [Should I|I should] come up with a |briddle|n[|, huh]?" - ]], - ["riddle", [ - "Aged in barrels, smooth and neat, in a glass by the fire, I'm a treat.", - "A scent of oak, a whiff of grain, a drop of expertise from the cask remains.", - """ - In oaken halls, I sleep and bide, - till I'm called to warm your insides... - hrm, that's a little vague, but kinda nice. - """, - """ - Alright, alright, the riddle should be clever. - It should refer to its golden hue, and I should make it obvious - that it isn't gold, and adding a reference to quest of the Argonauts - would be a complete red herring, and quite a mean thing to do, - so I shouldn't add that, - but what about barrels? Yeah, need to include barrels, but not the - way Bilbo road barrels, for that was definitely intended as a misleading - riddle for Smaug, but of course, a smart dragon would always figure such - things out, wait, where was I? - """ - ]], - [r"\bhello|\bgreet|\bhey\b", [ - "How's it going?", - "How's it?", - "'Sup.", - "How are you?", - ]], - [r"\bass\b", [ - "Why yes, I am made of brass." - ]], - [r"\block", [ - "Yes, I'm familiar with the door and the fact that it is locked.", - "This locked door is to protect the theft of Dabbler's scones.", - ]], - [r"\bdoor\b", [ + [r"whiske?y", Knocker_Convo.ANY, answered_responses, Knocker_Convo.ANSWERED], + [r"scotch|bourbon", Knocker_Convo.ANY, [ + "A little more specific than needed, but that will do."], + Knocker_Convo.ANSWERED], + + # A _sequence_ of responses, from what is this password business, to give me a hint, to give me the riddle ... + [question_msg, Knocker_Convo.RIDDLE, [ + "Nope. Would [ya|you] like another riddle?", + "You [really|] think that is the answer?", + "Personally, [|I think] that riddle could not [be|have been] more clear." + ], None], + [yes_msg, Knocker_Convo.HINT, riddle_responses, Knocker_Convo.RIDDLE], + ["riddle", Knocker_Convo.ANY, riddle_responses, Knocker_Convo.RIDDLE], + ["another", Knocker_Convo.RIDDLE, riddle_responses, Knocker_Convo.RIDDLE], + + ["hint", Knocker_Convo.ANY, hint_responses, Knocker_Convo.HINT], + ["\btell\b", Knocker_Convo.PASS, hint_responses, Knocker_Convo.HINT], + [yes_msg, Knocker_Convo.PASS, hint_responses, Knocker_Convo.HINT], + [question_msg, Knocker_Convo.PASS, hint_responses, Knocker_Convo.HINT], + + [yes_msg, Knocker_Convo.DOOR, password_responses, Knocker_Convo.PASS], + ["password", Knocker_Convo.ANY, password_responses, Knocker_Convo.PASS], + ["get in", Knocker_Convo.LOCK, password_responses, Knocker_Convo.PASS], + + ["get in", Knocker_Convo.DOOR, [ + "Well, you see. The door is locked." + ], Knocker_Convo.LOCK], + [question_msg, Knocker_Convo.DOOR, locked_responses, Knocker_Convo.LOCK], + [r"\block", Knocker_Convo.ANY, locked_responses, Knocker_Convo.LOCK], + + [r"\bdoor\b", Knocker_Convo.ANY, [ "What door? I don't see a door. Ha!", "That's right, I am the clever puzzle hanging on a door.", "I'm hanging on a door? Really? Let's see, can I roll my eyes?", @@ -172,18 +217,41 @@ class Knocker(Object): but I like the cut of your [suit|cloak|jib]. So, you see, if you speak the |bpassword|n, wait, I've said too much. """ - ]], - [r"\byes|yeah|yah\b", [ + ], Knocker_Convo.DOOR], + + [greet_msg, Knocker_Convo.GREETED, [ + "Didn't we just exchanged pleasantries?", + "Just so you know, I'm not much into small talk." + ], None], + [greet_msg, Knocker_Convo.ANY, [ + "How's it going?", + "How's it?", + "'Sup.", + "How are you?", + ], Knocker_Convo.GREETED], + + [r"knocker|goblin", Knocker_Convo.ANY, knocker_responses, None], + + [r"\bass\b", Knocker_Convo.ANY, [ + "Why yes, I am made of brass." + ], None], + + [r"thanks|thank you", Knocker_Convo.ANY, [ + "You're [very|] welcome.", + "Glad to be of service." + ], None], + + [yes_msg, Knocker_Convo.ANY, [ "Really? You agree?", - "Excellent", - ]], - [r"\bno|nope\b", [ + "Excellent.", + ], None], + [no_msg, Knocker_Convo.ANY, [ "Well, it's true. Just ask the [raven|trees|gnome].", - ]], - [r"\?$", [ + ], None], + [question_msg, Knocker_Convo.ANY, [ "What about [me|it|'im]?", "I dunno...", - ]], + ], None], ] after_unlocked_responses = [ @@ -248,31 +316,42 @@ class Knocker(Object): else: self.at_say("Doesn't appear that anyone is home.") - def at_heard_say(self, message, from_obj): + def at_heard_say(self, message, talker): """ A simple listener and response. This makes it easy to change for subclasses of NPCs reacting differently to says. - """ + + curr_state = Knocker_Convo(talker.db.knocker_conversation_state or 0) # message will be on the form ` says, "say_text"` # we want to get only say_text without the quotes and any spaces message = message.split('says, ')[1].strip(' "') # Let's see if a keyword gives a good response: - for idx, [regex, responses] in enumerate(self.all_responses): + for [regex, convo_state, responses, new_state] in self.all_responses: full_regex = r".*" + regex + r".*" - if re.match(full_regex, message, re.IGNORECASE): - # 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: - from_obj.tags.add("open_red_door", category="mp") + + if re.match(full_regex, message, re.IGNORECASE) \ + and curr_state.value >= convo_state.value: + logger.info(f"Spoke: {message}, matched: {full_regex} and {curr_state} >= {convo_state}") # If we have the ring in our mouth, we are muffled: if self.has("brass ring"): return choice(self.muffled_responses) else: + if new_state and new_state.value > curr_state.value: + talker.db.knocker_conversation_state = new_state + + # The first match is the password, so we set a tag + # on the character, so that they can go through + # the door: + if talker.db.knocker_conversation_state == Knocker_Convo.ANSWERED: + talker.tags.add("open_red_door", category="mp") + return routput(choice(responses)) + + # We can not do a random response. Of course, we are # pretending we are hard of hearing, so we don't spam the room # _every_ time: @@ -286,7 +365,7 @@ class Knocker(Object): # track and alternate the responses: elif self.db.hard: self.db.hard = False - if from_obj.tags.get(key="open_red_door", category="mp"): + if talker.tags.get(key="open_red_door", category="mp"): return routput(choice(self.after_unlocked_responses)) else: return routput(choice(self.unknown_responses)) @@ -305,9 +384,6 @@ class Knocker(Object): return routput(response) - def at_desc(self, looker): - return "what" # self.get_display_desc(looker) - def get_display_things(self, looker): return "" diff --git a/world/version1.ev b/world/version1.ev index f1f1d6b..cf2ad59 100644 --- a/world/version1.ev +++ b/world/version1.ev @@ -483,7 +483,7 @@ lair # [[file:../../../Dropbox/org/projects/mud.org::*The Lair of the Beast][The Lair of the Beast:9]] -@desc leave = You can see the waterfall cascading just outside the cave entrance. +@desc leave = You can see the waterfall cascading outside the cave entrance. # The Lair of the Beast:9 ends here @@ -1106,7 +1106,7 @@ west # [[file:../../../Dropbox/org/projects/mud.org::*Knocker][Knocker:9]] -@lock ring = tethered:id(#21) +@lock ring = tethered:id(#27) # @set ring/tethered_msg = You put the ring back in the knocker's mouth. "Mmmuufffmm," says the door knocker. # Knocker:9 ends here