Quite pleased with the results. This also fixed a utils issue to see if an object has an item. I'm surprised this isn't in the standard library.
251 lines
9.6 KiB
Python
Executable file
251 lines
9.6 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
"""
|
|
Knocker
|
|
|
|
The Object is the class for gatekeepers between rooms.
|
|
|
|
Use the ObjectParent class to implement common features for *all* entities
|
|
with a location in the game world (like Characters, Rooms, Exits).
|
|
|
|
"""
|
|
|
|
from evennia.utils import logger
|
|
|
|
from .objects import Object
|
|
from .characters import Character
|
|
from utils.word_list import routput
|
|
|
|
from random import choice, random
|
|
import re
|
|
|
|
|
|
class Returnable(Object):
|
|
"""
|
|
This object can't go far from one or two locations.
|
|
|
|
"""
|
|
pass
|
|
|
|
class Knocker(Object):
|
|
"""
|
|
Special object that listens to what is said in the room, and
|
|
attempts to a _real_ NPC.
|
|
|
|
"""
|
|
muffled_responses = [
|
|
"Mmmuufffmm",
|
|
"Mmmf?",
|
|
"Mummffmmph",
|
|
"Umfmuummmfff",
|
|
"Umf! Umfmmmf mmm mmmuufffmm mff mmuuummph.",
|
|
]
|
|
|
|
# Note that the responses are ordered from most specific to least.
|
|
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 hint."
|
|
]],
|
|
["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?
|
|
"""
|
|
]],
|
|
["hello|greet", [
|
|
"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!",
|
|
"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?",
|
|
"Just to let you know, the door is locked.",
|
|
"""
|
|
I shouldn['t|be quiet, and not] be telling you this,
|
|
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.
|
|
"""
|
|
]],
|
|
["\byes|yeah|yah\b", [
|
|
"Really? You agree?",
|
|
"Excellent",
|
|
]],
|
|
["\bno|nope\b", [
|
|
"Well, it's true. Just ask the [raven|trees|gnome].",
|
|
]],
|
|
["\?$", [
|
|
"What about [me|it|'im]?",
|
|
"I dunno...",
|
|
]],
|
|
]
|
|
|
|
after_unlocked_responses = [
|
|
"""
|
|
We could [feign|pretend|play make-believe] and
|
|
[carry on|continue|persist] this [conversation|charade],
|
|
but we both know that |byou|n know the [password|secret|secret word|magic],
|
|
and can go through the |bdoor|n now.
|
|
""",
|
|
"""
|
|
[Sure|Why not|Why yes], let's [feign|pretend|play make-believe that] you
|
|
don't know the [password|secret|secret word|magic], and
|
|
[carry on|continue|persist] this conversation.
|
|
""",
|
|
]
|
|
|
|
cant_hear_responses = [
|
|
"[Sorry.|What was that?|Did you say something?] I'm hard of hearing on account of the brass ears.",
|
|
"Brass ears. Yeah, not the best at hearing.",
|
|
"Yeah, These brass ears don't hear much except for the |b[secret|magic|] password|n.",
|
|
]
|
|
|
|
unknown_responses = [
|
|
"Are you talking to me or the goblin [in the bushes|up the tree|behind the rock]?",
|
|
"Knock, knock.",
|
|
"What do you mean?",
|
|
"Of course I like [hard candy|squirrels|apples]. Who [doesn't|wouldn't|do you know that doesn't]?",
|
|
"No thank you, I can't eat [apples|kumquats|figs], what with the brass teeth and a lack of guts.",
|
|
"Tea? While that would be [nice|sweet] of you, I can't really hold a cup.",
|
|
"I'm fine, thanks. How are you?",
|
|
"I'll say, [we have had|that is] a spell of weather.",
|
|
]
|
|
|
|
def at_heard_say(self, message, from_obj):
|
|
"""
|
|
A simple listener and response. This makes it easy to change for
|
|
subclasses of NPCs reacting differently to says.
|
|
|
|
"""
|
|
# message will be on the form `<Person> 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):
|
|
if re.match(r".*" + regex + r".*", 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:
|
|
from_obj.tags.add("open_red_door", category="mp")
|
|
|
|
# If we have the ring in our mouth, we are muffled:
|
|
if self.has("brass ring"):
|
|
return choice(self.muffled_responses)
|
|
else:
|
|
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:
|
|
if random() * 100 < 45:
|
|
if self.has("brass ring"):
|
|
return choice(self.muffled_responses)
|
|
|
|
# If a keyword was not spoken, we want to emphasize that
|
|
# we are hard of hearing, and mention that every other
|
|
# time, so we store in the database a setting to keep
|
|
# track and alternate the responses:
|
|
elif self.db.hard:
|
|
self.db.hard = False;
|
|
if from_obj.tags.get(key="open_red_door", category="mp"):
|
|
return routput(choice(self.after_unlocked_responses))
|
|
else:
|
|
return routput(choice(self.unknown_responses))
|
|
else:
|
|
self.db.hard = True;
|
|
return routput(choice(self.cant_hear_responses))
|
|
|
|
def get_display_desc(self, looker, **kwargs):
|
|
# Use this for random information instead of self.db.desc
|
|
response = "In a shape of a bald goblin, the brass door knocker in the center of the red door"
|
|
if self.has("brass ring"):
|
|
response += " holds a ring in its mouth. "
|
|
response += "[You think it|You are sure it|You could've sworn it|It] [just|] winked at you."
|
|
else:
|
|
response += " [smiles|looks] at you expectantly. "
|
|
|
|
return routput(response)
|
|
|
|
def at_desc(self, looker):
|
|
return "what" # self.get_display_desc(looker)
|
|
|
|
def get_display_things(self, looker):
|
|
return ""
|
|
|
|
|
|
def msg(self, text=None, from_obj=None, **kwargs):
|
|
"Custom msg() method reacting to say."
|
|
if from_obj != self:
|
|
# make sure to not repeat what we ourselves said or we'll create a loop
|
|
is_say = False
|
|
is_whisper = False
|
|
|
|
try:
|
|
# debug(f"text[0]: {text[0]}, text[1]: {text[1]}")
|
|
# if text comes from a say, `text` is `('say_text', {'type': 'say'})`
|
|
say_text, is_say = text[0], text[1]['type'] == 'say'
|
|
is_whisper = text[1]['type'] == 'whisper'
|
|
except Exception:
|
|
pass
|
|
|
|
if is_whisper:
|
|
self.at_say("I'm a little hard of hearing, can you speak up?")
|
|
elif is_say:
|
|
# First get the response (if any)
|
|
response = self.at_heard_say(say_text, from_obj)
|
|
# If there is a response
|
|
if response != None:
|
|
self.at_say(response)
|
|
|
|
# this is needed if anyone ever puppets this NPC - without it you would never
|
|
# get any feedback from the server (not even the results of look)
|
|
super().msg(text=text, from_obj=from_obj, **kwargs)
|
|
|
|
def at_object_leave(self, obj, target_location, **kwargs):
|
|
if obj.key == "brass ring" and target_location != self:
|
|
self.at_say("Oh my, that feels better.")
|
|
else:
|
|
self.at_say("Umph")
|
|
return True
|