Expand conversation with the Door Knocker

This commit is contained in:
Howard Abrams 2025-04-14 16:39:34 -07:00
parent 11785cd534
commit bc380928e7
2 changed files with 151 additions and 75 deletions

View file

@ -1,6 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
from random import choice, random from random import choice, random
from enum import Enum
from evennia import create_script from evennia import create_script
from evennia.utils import logger, delay from evennia.utils import logger, delay
@ -91,6 +92,18 @@ class Puddle(Object):
exclude=player) 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): class Knocker(Object):
""" """
Knocker Knocker
@ -107,62 +120,94 @@ class Knocker(Object):
"Umf! Umfmmmf mmm mmmuufffmm mff mmuuummph.", "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. # 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 = [ all_responses = [
# The password must be the first entry here, as we "act" on it: # The password must be the first entry here, as we "act" on it:
["whiske?y", [ [r"whiske?y", Knocker_Convo.ANY, answered_responses, Knocker_Convo.ANSWERED],
"Alright, I'll open the door for yah.", [r"scotch|bourbon", Knocker_Convo.ANY, [
"I heard my favorite, let's get this door unlocked.", "A little more specific than needed, but that will do."],
]], Knocker_Convo.ANSWERED],
[r"knocker|goblin", [
"[Sorry.|What was that?|Did you say something?] I'm hard of hearing on account of the brass ears.", # A _sequence_ of responses, from what is this password business, to give me a hint, to give me the riddle ...
"Yes, I suppose I'm this amazing puzzle you get to in Chapter Three. Wait, does that mean I'm just an NPC?", [question_msg, Knocker_Convo.RIDDLE, [
"Who me? I thought you were talking to the goblin in the bushes.", "Nope. Would [ya|you] like another riddle?",
"Why yes, I am hard of hearing.", "You [really|] think that is the answer?",
]], "Personally, [|I think] that riddle could not [be|have been] more clear."
["password", [ ], None],
"Of course this door is protected by a super complicated encrypted password.", [yes_msg, Knocker_Convo.HINT, riddle_responses, Knocker_Convo.RIDDLE],
"If I tell you, it wouldn't be a secret now.", ["riddle", Knocker_Convo.ANY, riddle_responses, Knocker_Convo.RIDDLE],
"The password? You just have to guess.", ["another", Knocker_Convo.RIDDLE, riddle_responses, Knocker_Convo.RIDDLE],
"Well, I suppose I could give you a |bhint|n.",
]], ["hint", Knocker_Convo.ANY, hint_responses, Knocker_Convo.HINT],
["hint", [ ["\btell\b", Knocker_Convo.PASS, hint_responses, Knocker_Convo.HINT],
"A hint does sound fair. [Should I|I should] come up with a |briddle|n[|, huh]?" [yes_msg, Knocker_Convo.PASS, hint_responses, Knocker_Convo.HINT],
]], [question_msg, Knocker_Convo.PASS, hint_responses, Knocker_Convo.HINT],
["riddle", [
"Aged in barrels, smooth and neat, in a glass by the fire, I'm a treat.", [yes_msg, Knocker_Convo.DOOR, password_responses, Knocker_Convo.PASS],
"A scent of oak, a whiff of grain, a drop of expertise from the cask remains.", ["password", Knocker_Convo.ANY, password_responses, Knocker_Convo.PASS],
""" ["get in", Knocker_Convo.LOCK, password_responses, Knocker_Convo.PASS],
In oaken halls, I sleep and bide,
till I'm called to warm your insides... ["get in", Knocker_Convo.DOOR, [
hrm, that's a little vague, but kinda nice. "Well, you see. The door is locked."
""", ], Knocker_Convo.LOCK],
""" [question_msg, Knocker_Convo.DOOR, locked_responses, Knocker_Convo.LOCK],
Alright, alright, the riddle should be clever. [r"\block", Knocker_Convo.ANY, locked_responses, Knocker_Convo.LOCK],
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 [r"\bdoor\b", Knocker_Convo.ANY, [
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", [
"What door? I don't see a door. Ha!", "What door? I don't see a door. Ha!",
"That's right, I am the clever puzzle hanging on a door.", "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?", "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]. 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. So, you see, if you speak the |bpassword|n, wait, I've said too much.
""" """
]], ], Knocker_Convo.DOOR],
[r"\byes|yeah|yah\b", [
[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?", "Really? You agree?",
"Excellent", "Excellent.",
]], ], None],
[r"\bno|nope\b", [ [no_msg, Knocker_Convo.ANY, [
"Well, it's true. Just ask the [raven|trees|gnome].", "Well, it's true. Just ask the [raven|trees|gnome].",
]], ], None],
[r"\?$", [ [question_msg, Knocker_Convo.ANY, [
"What about [me|it|'im]?", "What about [me|it|'im]?",
"I dunno...", "I dunno...",
]], ], None],
] ]
after_unlocked_responses = [ after_unlocked_responses = [
@ -248,31 +316,42 @@ class Knocker(Object):
else: else:
self.at_say("Doesn't appear that anyone is home.") 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 A simple listener and response. This makes it easy to change for
subclasses of NPCs reacting differently to says. subclasses of NPCs reacting differently to says.
""" """
curr_state = Knocker_Convo(talker.db.knocker_conversation_state or 0)
# message will be on the form `<Person> says, "say_text"` # message will be on the form `<Person> says, "say_text"`
# we want to get only say_text without the quotes and any spaces # we want to get only say_text without the quotes and any spaces
message = message.split('says, ')[1].strip(' "') message = message.split('says, ')[1].strip(' "')
# Let's see if a keyword gives a good response: # 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".*" 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 if re.match(full_regex, message, re.IGNORECASE) \
# the character, so that they can go through the door: and curr_state.value >= convo_state.value:
if idx == 0: logger.info(f"Spoke: {message}, matched: {full_regex} and {curr_state} >= {convo_state}")
from_obj.tags.add("open_red_door", category="mp")
# If we have the ring in our mouth, we are muffled: # If we have the ring in our mouth, we are muffled:
if self.has("brass ring"): if self.has("brass ring"):
return choice(self.muffled_responses) return choice(self.muffled_responses)
else: 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)) return routput(choice(responses))
# We can not do a random response. Of course, we are # We can not do a random response. Of course, we are
# pretending we are hard of hearing, so we don't spam the room # pretending we are hard of hearing, so we don't spam the room
# _every_ time: # _every_ time:
@ -286,7 +365,7 @@ class Knocker(Object):
# track and alternate the responses: # track and alternate the responses:
elif self.db.hard: elif self.db.hard:
self.db.hard = False 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)) return routput(choice(self.after_unlocked_responses))
else: else:
return routput(choice(self.unknown_responses)) return routput(choice(self.unknown_responses))
@ -305,9 +384,6 @@ class Knocker(Object):
return routput(response) return routput(response)
def at_desc(self, looker):
return "what" # self.get_display_desc(looker)
def get_display_things(self, looker): def get_display_things(self, looker):
return "" return ""

View file

@ -483,7 +483,7 @@ lair
# [[file:../../../Dropbox/org/projects/mud.org::*The Lair of the Beast][The Lair of the Beast:9]] # [[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 # The Lair of the Beast:9 ends here
@ -1106,7 +1106,7 @@ west
# [[file:../../../Dropbox/org/projects/mud.org::*Knocker][Knocker:9]] # [[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. @set ring/tethered_msg = You put the ring back in the knocker's mouth. "Mmmuufffmm," says the door knocker.
# Knocker:9 ends here # Knocker:9 ends here