317 lines
13 KiB
Python
Executable file
317 lines
13 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
from evennia import create_script
|
|
from evennia.utils import logger
|
|
|
|
from .scripts import KnockScript
|
|
from .objects import Object
|
|
from commands.misc import CmdSetPuddle, CmdSetStick, CmdSetKnock
|
|
from utils.word_list import routput
|
|
|
|
from random import choice, random
|
|
import re
|
|
|
|
|
|
class Stick(Object):
|
|
def at_object_creation(self):
|
|
self.cmdset.add_default(CmdSetStick)
|
|
|
|
def do_throw(self, thrower):
|
|
thrower.db.thrown_times = (thrower.db.thrown_times or 0) + 1
|
|
if thrower.db.jumped_times == 1:
|
|
thrower.msg("The stick flies through the air, and just as you thought, it comes back to you!")
|
|
else:
|
|
thrower.msg(routput(choice([
|
|
f"Yer a wizard, {thrower.name.capitalize()}!",
|
|
"[Did you see that?|] It clipped the leaf on that tree before returning.",
|
|
"This is a [fun|pleasant|nice] [past-time|game].",
|
|
"Maybe we should get a pet [or beast|beastie] in here who would love to play.",
|
|
])))
|
|
self.location.msg_contents(f"{thrower.name} throws a stick.",
|
|
exclude=thrower)
|
|
|
|
|
|
class Puddle(Object):
|
|
def at_object_creation(self):
|
|
self.cmdset.add_default(CmdSetPuddle)
|
|
|
|
def do_jump(self, player):
|
|
player.db.jumped_times = (player.db.jumped_times or 0) + 1
|
|
if player.db.jumped_times == 1:
|
|
player.msg("You jump in the puddle! "
|
|
"This is great fun. You feel childish.")
|
|
else:
|
|
player.msg(routput(choice([
|
|
"You don't care [how muddy|who notices], you jump in again!",
|
|
"Mud? Whatever. You [splash|jump] in[| again].",
|
|
"You splash around in the puddle.",
|
|
"You jump in the puddle again.",
|
|
"This time you dance a little as you kick up your heels.",
|
|
]) + " You feel " + choice([
|
|
"[so much|] better.",
|
|
"carefree.",
|
|
"child-like and free.",
|
|
"irresponsible.",
|
|
"happy.",
|
|
"great.",
|
|
])))
|
|
|
|
self.location.msg_contents(f"{player.name} jumps in the puddle.",
|
|
exclude=player)
|
|
|
|
|
|
class Knocker(Object):
|
|
"""
|
|
Knocker
|
|
|
|
The Object is the class for gatekeepers between rooms.
|
|
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 |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", [
|
|
"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.
|
|
"""
|
|
]],
|
|
[r"\byes|yeah|yah\b", [
|
|
"Really? You agree?",
|
|
"Excellent",
|
|
]],
|
|
[r"\bno|nope\b", [
|
|
"Well, it's true. Just ask the [raven|trees|gnome].",
|
|
]],
|
|
[r"\?$", [
|
|
"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_object_creation(self):
|
|
super().at_object_creation()
|
|
self.cmdset.add(CmdSetKnock)
|
|
|
|
def do_knock(self, knocker):
|
|
if self.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,
|
|
# This is the _character_
|
|
# that does the knocking, not
|
|
# the door knocker:
|
|
attributes=[("knocker", knocker),
|
|
# Waker is the door knocker
|
|
("waker", self),
|
|
("room", self.db.room_to_msg)
|
|
])
|
|
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'.")
|
|
|
|
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.")
|
|
|
|
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):
|
|
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 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 is not 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
|