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,29 +120,37 @@ class Knocker(Object):
"Umf! Umfmmmf mmm mmmuufffmm mff mmuuummph.", "Umf! Umfmmmf mmm mmmuufffmm mff mmuuummph.",
] ]
# Note that the responses are ordered from most specific to least. yes_msg = r"\byes|yeah|yah|sure|please\b"
all_responses = [ no_msg = r"\bno|nope\b"
# The password must be the first entry here, as we "act" on it: question_msg = r"\?$"
["whiske?y", [ 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.", "Alright, I'll open the door for yah.",
"I heard my favorite, let's get this door unlocked.", "I heard my favorite, let's get this door unlocked.",
]], ]
[r"knocker|goblin", [
# 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.", "[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?", "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.", "Why yes, I am hard of hearing.",
]], ]
["password", [ password_responses = [
"Of course this door is protected by a super complicated encrypted password.", "Of course this door is protected by a super complicated encrypted password.",
"If I tell you, it wouldn't be a secret now.", "If I tell you, it wouldn't be a secret now.",
"The password? You just have to guess.", "The password? You just have to guess...unless you want a hint?",
"Well, I suppose I could give you a |bhint|n.", "Well, I suppose I could give you a |bhint|n.",
]], ]
["hint", [ hint_responses = [
"A hint does sound fair. [Should I|I should] come up with a |briddle|n[|, huh]?" "A hint does sound fair. [Should I|I should] come up with a |briddle|n[|, huh]?"
]], ]
["riddle", [ riddle_responses = [
"Aged in barrels, smooth and neat, in a glass by the fire, I'm a treat.", "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.", "A scent of oak, a whiff of grain, a drop of expertise from the cask remains.",
""" """
@ -148,21 +169,45 @@ class Knocker(Object):
riddle for Smaug, but of course, a smart dragon would always figure such riddle for Smaug, but of course, a smart dragon would always figure such
things out, wait, where was I? things out, wait, where was I?
""" """
]], ]
[r"\bhello|\bgreet|\bhey\b", [
"How's it going?", locked_responses = [
"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.", "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.", "This locked door is to protect the theft of Dabbler's scones.",
]], ]
[r"\bdoor\b", [ all_responses = [
# The password must be the first entry here, as we "act" on it:
[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!", "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