As character do things, they can now get a "score" for everything I think could be interesting/challenging.
561 lines
20 KiB
Python
Executable file
561 lines
20 KiB
Python
Executable file
#!/usr/bin/env python
|
||
|
||
from evennia.prototypes.spawner import spawn
|
||
from evennia.utils import logger
|
||
|
||
from typeclasses.objects import Object
|
||
from commands.consumables import CmdSetTeapot, CmdSetFillable
|
||
from utils.scoring import Scores
|
||
from utils.word_list import routput, choices
|
||
|
||
import re
|
||
import random
|
||
|
||
|
||
def choose_drink(drink_list, name=None, rando=True):
|
||
"""
|
||
Return an entry from 'drink_list'.
|
||
|
||
The 'name' must match an entry with a similar 'title' key.
|
||
The drink_list should be a list of dicts.
|
||
"""
|
||
if name and name != "":
|
||
rx = rf".*{name}.*"
|
||
# logger.info(f"Working on {rx}")
|
||
details = [c for c in drink_list
|
||
if re.match(rx, c.get("title"), re.IGNORECASE) or
|
||
name in c.get("aliases", [])]
|
||
if len(details) > 0:
|
||
return details[0]
|
||
|
||
if rando:
|
||
return random.choice(drink_list)
|
||
return None
|
||
|
||
|
||
TEAS = [
|
||
{"title": "black",
|
||
"desc": "a rich, bold and <<malty ^ fragrant ^ almost spicy>> black tea",
|
||
},
|
||
{"title": "green",
|
||
"desc": "a green tea with <<grassy ^ floral ^ vanilla ^ vegetal>> notes",
|
||
},
|
||
{"title": "oolong",
|
||
"desc": "a complex and <<earthy ^ floral>> oolong",
|
||
},
|
||
{"title": "rooibos",
|
||
"desc": "an almost <<sweet and ^ >> <<woody ^ nutty ^ vanilla-y>> rooibos herbal infusion",
|
||
},
|
||
{"title": "dandelion",
|
||
"desc": "a roasted herbal infusion of dandelions that tastes a bit like coffee",
|
||
},
|
||
{"title": "mint",
|
||
"desc": "a blend of peppermint and spearmint to create the ultimate minty herbal infusion",
|
||
},
|
||
{"title": "chamomile",
|
||
"desc": "a relaxing, yet subtle infusion of chamomile flowers that makes you want to go to sleep",
|
||
},
|
||
{"title": "earl",
|
||
"desc": "a flowery Earl Grey tea",
|
||
},
|
||
{"title": "herbal",
|
||
"desc": "an herbal infusion of <<chamomile ^ mint ^ chicory root ^ lavender>>",
|
||
},
|
||
{"title": "chai",
|
||
"desc": "a spicy chai tea, tempered with milk and sweetness",
|
||
},
|
||
{"title": "hibiscus",
|
||
"desc": "a dark red, tart herbal infusion of roselle flowers",
|
||
},
|
||
{"title": "matcha",
|
||
"desc": "a frothy and aromatic matcha of the most vibrant green"
|
||
},
|
||
{"title": "white",
|
||
"desc": "a subtle tea with notes of <<rose ^ berries ^ chamomile flowers>>",
|
||
},
|
||
{"title": "pu-erh",
|
||
"desc": "an earthy, almost mushroom flavored, pu-erh tea",
|
||
},
|
||
{"title": "puerh",
|
||
"desc": "an earthy, almost mushroom flavored, pu-erh tea",
|
||
},
|
||
{"title": "barley",
|
||
"desc": "a nutty herbal infusion of barley with just a hint of smokiness",
|
||
},
|
||
{"title": "chaga",
|
||
"desc": "an earthy, mushroomy chaga infusion",},
|
||
{
|
||
"title": "",
|
||
"desc": "",
|
||
},
|
||
]
|
||
|
||
|
||
class Container(Object):
|
||
"""
|
||
All re-fillable cups, glasses and bottles inherit from this object.
|
||
|
||
Database settings:
|
||
type: "tea"
|
||
details:
|
||
"""
|
||
# Defaults
|
||
sip_amount = 1
|
||
fill_amount = 1
|
||
details = {"title": "unknown liquid",
|
||
"desc": "Not sure what this is."}
|
||
empty_msgs = ["It is empty."]
|
||
drink_msgs = [
|
||
'You take a <<sip ^ sample ^ swig ^ drink>> << of {0}^ >> << ^ from your {2} >>.',
|
||
'You <<sip ^ sample ^ swig ^ drink>> some of your {0} << ^ from your {2} >>.',
|
||
]
|
||
fill_msgs = ["$You() $conj(fill) $pron(your) {2} with {0}."]
|
||
|
||
def at_object_creation(self):
|
||
"""
|
||
called at creation
|
||
"""
|
||
self.db.amount = 0
|
||
|
||
def do_drink(self, drinker):
|
||
"""
|
||
Drink command results, gives a message to drinker.
|
||
|
||
And lowers the amount by the 'sip_amount'.
|
||
When empty, resets the container description to its
|
||
'empty_desc' setting.
|
||
"""
|
||
|
||
amount = self.db.amount or 0
|
||
details = self.db.details or self.details or {}
|
||
sip_amount = self.db.sip_amount or self.sip_amount or 1
|
||
|
||
if amount == 0:
|
||
drinker.msg(choices(self.empty_msgs, details["title"],
|
||
details["desc"], self.name))
|
||
else:
|
||
self.db.amount = amount - sip_amount
|
||
drinker.msg(choices(self.drink_msgs, details["title"],
|
||
details["desc"], self.name))
|
||
if self.db.amount == 0:
|
||
self.db.desc = self.db.empty_desc
|
||
|
||
def do_empty(self):
|
||
"""
|
||
Return the title of the contents and empty the container.
|
||
|
||
If already empty, return None.
|
||
"""
|
||
if self.db.amount == 0:
|
||
return None
|
||
|
||
details = self.db.details or self.details or {}
|
||
results = (details["title"], self.db.amount, self.db.desc)
|
||
self.db.amount = 0
|
||
if self.db.empty_name:
|
||
self.key = self.db.empty_name
|
||
if self.db.empty_desc:
|
||
self.db.desc = self.db.empty_desc
|
||
return results
|
||
|
||
def do_fill(self, filler, details):
|
||
"""
|
||
Fill this container with 'details'.
|
||
|
||
The containers description will match the contents.
|
||
"""
|
||
if (self.db.amount or 0) > 0:
|
||
already_full = True
|
||
else:
|
||
already_full = False
|
||
|
||
# Render and restore the description:
|
||
desc = routput(details['desc'])
|
||
details['desc'] = desc
|
||
|
||
self.db.details = details
|
||
self.db.amount = self.db.fill_amount or self.fill_amount or 1
|
||
|
||
if already_full:
|
||
msg = f"$You() $conj(empty) $pron(your) {self.name} and then $pron(you) $conj(fill) it with {desc}"
|
||
else:
|
||
msg = choices((self.db.fill_msgs or self.fill_msgs),
|
||
details["title"], desc, self.name)
|
||
|
||
# Save the current description as the "empty" description,
|
||
# for re-using later.
|
||
if not self.db.empty_desc:
|
||
self.db.empty_desc = self.db.desc
|
||
self.db.desc = f"Contains {desc}"
|
||
|
||
filler.announce_action(msg)
|
||
|
||
def at_pre_drop(self, dropper):
|
||
"""
|
||
Let's keep this world tidy.
|
||
"""
|
||
dropper.announce_action(f"$You() $conj(drop) the {self.name}, shattering it.")
|
||
self.delete()
|
||
|
||
|
||
class Bottle(Container):
|
||
"""
|
||
Generic large container that can be filled.
|
||
"""
|
||
fill_amount = 8
|
||
empty_msgs = [
|
||
'Your {2} is empty. Perhaps you can |gfill|n it?',
|
||
'It appears that you need to fill your {2}.'
|
||
]
|
||
def at_object_creation(self):
|
||
"""
|
||
called at creation
|
||
"""
|
||
self.db.amount = 0
|
||
|
||
|
||
class Water(Object):
|
||
"""
|
||
Object that contains water or other fillable liquid.
|
||
|
||
You must set the following:
|
||
|
||
@set this/fill_name = "water"
|
||
@set this/fill_desc = "fizzy water with bubbles trapped from waterfall."
|
||
|
||
"""
|
||
def at_object_creation(self):
|
||
"""
|
||
called at creation
|
||
"""
|
||
self.cmdset.add_default(CmdSetFillable, persistent=True)
|
||
|
||
|
||
class TeaCup(Container):
|
||
fill_amount = 4
|
||
sip_amount = 1
|
||
|
||
drink_msgs = [
|
||
'You take a sip of {0} tea.',
|
||
'You take a sip of {0} tea <<in ^ from>> your <<teacup ^ cup>>.',
|
||
'You <<sip ^ sample ^ drink>> from your <<teacup ^ cup>>.',
|
||
'You savor {1} in your <<teacup ^ cup>>.',
|
||
]
|
||
|
||
empty_msgs = [
|
||
'Your <<teacup ^ cup>> is empty. Perhaps you can |gmake|n some |gtea|n?',
|
||
"Your cup certainly doesn't runneth over, as it be quite empty.",
|
||
'Alas, you find your cup devoid of any sort of watery infusion.',
|
||
'You notice your cup is empty.',
|
||
'It appears that you need to refill your tea cup.'
|
||
]
|
||
|
||
fill_msgs = [
|
||
'You fill your <<teacup ^ cup>> with {1}.',
|
||
'You pour <<some ^ >> {0} tea into your <<teacup ^ cup>>.',
|
||
]
|
||
|
||
def at_pre_drop(self, dropper):
|
||
if dropper.location.key in ("Cozy House", "Cramped Kitchen"):
|
||
dropper.announce_action("$You() $conj(wash) and $conj(replace) the teacup in the cabinet with the others.")
|
||
self.delete()
|
||
else:
|
||
return True
|
||
|
||
|
||
class Teapot(Object):
|
||
def at_object_creation(self):
|
||
"""
|
||
called at creation
|
||
"""
|
||
self.cmdset.add_default(CmdSetTeapot, persistent=True)
|
||
|
||
def do_make_tea(self, drinker, words):
|
||
"""
|
||
Make tea.
|
||
|
||
If 'args', try to make a particular type of tea, otherwise,
|
||
pick one at random.
|
||
"""
|
||
tea_choice = choose_drink(TEAS, words)
|
||
desc = routput(tea_choice['desc'])
|
||
|
||
self.db.tea_choice = tea_choice
|
||
self.db.desc = f"A large, brown teapot full of {desc}."
|
||
drinker.announce_action(f"$You() $conj(make) a teapot of {desc}.")
|
||
drinker.score(Scores.make_tea)
|
||
|
||
def do_fill(self, drinker):
|
||
teatype = self.db.tea_choice
|
||
if not teatype:
|
||
drinker.msg("You need to |gmake tea|n first.")
|
||
return
|
||
|
||
teacup = drinker.has("teacup")
|
||
if not teacup:
|
||
drinker.msg("You need to |gget teacup|n first.")
|
||
return
|
||
|
||
teacup.do_fill(drinker, self.db.tea_choice)
|
||
|
||
|
||
COCKTAILS = [
|
||
{
|
||
"title": "Moonlit Mirage",
|
||
"type": "cocktail",
|
||
"desc": "A shimmering, colorful drink, garnished with glitter and a twist of tart, almost sour fruit.",
|
||
"pre": "The bartender grabs a pixie flying by and shakes the glitter from its wings into the glass.",
|
||
"flavors": ["lemon", "tartness", "sweetness"],
|
||
"effects": [
|
||
"You notice the swirling glitter make a pattern.",
|
||
"Your cocktail just changed color to << red ^ pink ^ orange ^ yellow ^ blue ^ purple >>."
|
||
]
|
||
},
|
||
{
|
||
"title": "Puck's Revenge",
|
||
"type": "cocktail",
|
||
"desc": "A vibrant concoction of fruit juices and sparkling fairy dust, served in a glass shaped like a flower.",
|
||
"flavors": ["fruity", "... fairy dust?"],
|
||
"effects": [
|
||
"Wow, this drink really packs a punch.",
|
||
"You feel a little light-headed.",
|
||
]
|
||
},
|
||
{
|
||
"title": "Glimmering Gossamer",
|
||
"type": "cocktail",
|
||
"desc": "A delicate blend of elderflower liqueur and sparkling wine, adorned with a butterfly-shaped ice cube.",
|
||
"pre": "The bartender uses a butterfly net to catch an ice cube and then places it in the glass.",
|
||
"flavors": ["elderflower"],
|
||
"effects": ["The butterfly shaped ice cube just started fluttering, and flew away."]
|
||
},
|
||
{
|
||
"title": "Whimsical Willow",
|
||
"type": "cocktail",
|
||
"desc": "A herbal infusion of gin, rosemary, and elderberry, served with a sprig of fresh mint and a splash of tonic.",
|
||
"pre": "As the bartender hands over the cocktail, the garnish of a sprig of mint grows a long twisting vine that he casually clips off.",
|
||
"flavors": ["fresh mint", "rosemary", "elderberry"],
|
||
"effects": [
|
||
"Hrm ... did the bartender always have flowers in his hair?",
|
||
"Hrm ... why are vines sprouting from everyone's hair?",
|
||
]
|
||
},
|
||
{
|
||
"title": "Charmed Chalice",
|
||
"type": "cocktail",
|
||
"desc": "A mysterious potion that glows softly, made with enchanted spirits and a hint of honey, served in a chalice that changes shape.",
|
||
"pre": "Ghostly apparitions appear in the air, as the bartender grabs one, twists it, to add a drop of ectoplasm to the drink.",
|
||
"flavors": ["honey", "uhm...ectoplasm"],
|
||
"effects": [
|
||
"Your cocktail glass just changed into a flute for sparkling wine.",
|
||
"Your glass has changed shape into a small vase. Good thing the drink is still in it.",
|
||
"Your glass changes shape into a glass bowl, still holding the contents of your drink.",
|
||
]
|
||
},
|
||
{
|
||
"title": "Enchanted Elixir",
|
||
"type": "cocktail",
|
||
"desc": "A deep blue drink with hints of blueberry and a touch of magic, served with a glowing ice sphere.",
|
||
"pre": "The bartender opens a box that bathes the bar in light. He takes a glowing sphere of ice and garnishes the cocktail before passing it over.",
|
||
"flavors": ["blueberry", "blue"],
|
||
"effects": [
|
||
"The ice sphere in the glass pulses with light and energy.",
|
||
"You see stars everywhere in the room.",
|
||
"The room became brighter...maybe that it just because you can see stars.",
|
||
]
|
||
# - Glowing orbs of light spin about the drinker’s head
|
||
# - Drinker sees stars … SPELL?
|
||
},
|
||
{
|
||
"title": "Sylvan Serenade",
|
||
"type": "cocktail",
|
||
"desc": "A melodic blend of apple cider and spiced rum, garnished with a cinnamon stick and a slice of star fruit.",
|
||
"pre": "The bartender plays a flute as the jars and ingredients line up and pour themselves into a cocktail shaker.",
|
||
"flavors": ["apple", "cider", "spices", "cinnamon"],
|
||
"effects": [
|
||
"You hear the song of a distant choir.",
|
||
"You hear the distant sounds of flutes and tambourines.",
|
||
],
|
||
},
|
||
{
|
||
"title": "Brambleberry Bliss",
|
||
"type": "cocktail",
|
||
"desc": "A sweet and tart mix of brambleberries and gin, served over crushed ice with a sprinkle of edible flowers.",
|
||
"pre": "The bartender garnishes the cocktail with a flower pulled from a pot on the bar. The plant immediately wilts and drops away.",
|
||
"flavors": ["brambleberries", "berry", "floral"],
|
||
"effects": [
|
||
"The flowers floating in the glass whispers to you, \"I'm always getting into trouble. I guess that's because I'm a wild flower.\"",
|
||
"A flower floating in the drink whispers to you, \"I got a promotion. I guess I was just outstanding in my field.\"",
|
||
"The flowers in the cocktail whispers to you, \"I like to drive fast. I guess I'm always putting the petal to the metal.\"",
|
||
],
|
||
# - The flowers talk .. maybe tell jokes.
|
||
},
|
||
{
|
||
"title": "Twilight Tonic",
|
||
"type": "cocktail",
|
||
"desc": "A dark, mysterious drink made with blackcurrant and tonic water, served with a slice of lime and a hint of mint.",
|
||
"pre": "After the cocktail is complete, the branches above the bar part, and light from the moon shines directly on the glass causing bubbles to form.",
|
||
"flavors": ["lime", "blackcurrant", "mint"],
|
||
"effects": [
|
||
"A shadow falls on your face making you look seductively mysterious.",
|
||
"The room briefly grows dim.",
|
||
]
|
||
},
|
||
{
|
||
"title": "Bee Knees",
|
||
"type": "mead",
|
||
"desc": "Slightly fizzy, slightly sweet, served with a wedge of honeycomb.",
|
||
"pre": "After filling the glass, the bartender takes a bee out of jar, gently squeezes it until vomits a drop of honey to the concoction.",
|
||
"flavors": ["honey", "vanilla"],
|
||
"effects": [
|
||
"You feel like a dwarf in a tunnel digging a hole. Diggy, diggy hole!",
|
||
"Just like being in the Mud World."
|
||
]
|
||
},
|
||
{
|
||
"title": "whisky",
|
||
"type": "whisky",
|
||
"desc": "A dram of smokey, amber ambrosia.",
|
||
"flavors": ["tar", "peat", "smoke", "brine", "vanilla", "toffee"],
|
||
"effects": [
|
||
"This reminds you of your travels in the Mud World.",
|
||
"Lovely.",
|
||
"Friendships and conversations distilled in a glass.",
|
||
"A dram to << certainly ^ >> << savor ^ cherish ^ enjoy>>.",
|
||
"Here's to friends that aren't here to share."
|
||
]
|
||
},
|
||
{
|
||
"title": "whiskey",
|
||
"type": "whiskey",
|
||
"desc": "A dram of strong, amber ambrosia.",
|
||
"flavors": ["vanilla", "toffee"],
|
||
"effects": [
|
||
"This reminds you of your travels in the Mud World.",
|
||
"Lovely.",
|
||
"Friendships and conversations distilled in a glass.",
|
||
"A dram to << certainly ^ >> << savor ^ cherish ^ enjoy>>.",
|
||
"Here's to friends that aren't here to share."
|
||
]
|
||
},
|
||
{
|
||
"title": "ale",
|
||
"aliases": ["beer"],
|
||
"type": "beer",
|
||
"desc": "A malty, only slightly carbonated, opaque beverage with overt herbal notes.",
|
||
"flavors": ["rosemary", "bog myrtle", "yarrow"],
|
||
"effects": [
|
||
"Wonder where they got the recipe, twelfth century England?",
|
||
"Tastes like drinking a loaf of bread."
|
||
]
|
||
},
|
||
{
|
||
"title": "red wine",
|
||
"type": "wine",
|
||
"desc": "A rich glass a blood red color with notes of raspberries and toffee.",
|
||
"pre": "After popping a cork from a bottle, the bartender then empties the bottle into the glass.",
|
||
"flavors": ["earthiness", "toffee", "raspberries"],
|
||
"effects": [
|
||
"Ah, what was the name of the land in the Mud World where you first had this vintage?",
|
||
"You swirl your glass and watch the legs return to the bottom.",
|
||
]
|
||
},
|
||
{
|
||
"title": "water",
|
||
"type": "water",
|
||
"desc": "A clean cocktail with absolutely no taste.",
|
||
"flavors": ["water", "blandness"],
|
||
"effects": [
|
||
"Wow, this drink really packs a punch...NOT",
|
||
"Refreshing.",
|
||
"Unintoxicating.",
|
||
"Your liver is thanking you."
|
||
]
|
||
},
|
||
]
|
||
|
||
DRINK_COCKTAIL_MSGS = [
|
||
# 0 -> drink title or name
|
||
# 1 -> type of drink, e.g. cocktail or whiskey
|
||
# 2 -> one of the flavors
|
||
'You take a <<sip ^ sample ^ drink>> of your << |w{0}|n ^ {1} ^ drink >>.',
|
||
'You <<sip ^ sample ^ drink>> your << |w{0}|n ^ {1} ^ drink >>.',
|
||
'You << notice ^ savor ^ relish ^ enjoy >> the {2}.',
|
||
]
|
||
|
||
EMPTY_COCKTAIL_MSGS = [
|
||
'That {1} went down fast.',
|
||
'Hrm ... did you enjoy that {1}?',
|
||
'You drain your glass.',
|
||
"You've finished that concoction.",
|
||
]
|
||
|
||
|
||
class Cocktail(Object):
|
||
fill_amount = 4
|
||
sip_amount = 1
|
||
|
||
def make(owner, shaker, name=None):
|
||
"""
|
||
Create for 'owner', a drink that matches 'name'.
|
||
If name doesn't match, get a random one.
|
||
"""
|
||
details = choose_drink(COCKTAILS, name, rando=False)
|
||
if not details or details == []:
|
||
shaker.msg(f"No match for '{name}'.")
|
||
return
|
||
|
||
drink = spawn({
|
||
"typeclass": "typeclasses.drinkables.Cocktail",
|
||
"key": details.get("title"),
|
||
"aliases": ["drink", "glass", "cocktail"],
|
||
"desc": details.get("desc")
|
||
})[0]
|
||
drink.db.cocktail_type = details.get("type")
|
||
drink.db.flavors = details.get("flavors")
|
||
drink.db.effects = details.get("effects")
|
||
drink.db.amount = Cocktail.fill_amount
|
||
drink.location = owner
|
||
|
||
pre_msg = details.get("pre", "")
|
||
drink.location.msg(routput(pre_msg + " The << bartender ^ barkeep ^ elf >> << passes ^ slides ^ gives ^ hands >> you a |y{0}|n.", drink.name))
|
||
owner.score(Scores.get_cocktail)
|
||
|
||
theroom = drink.location.location
|
||
char = owner.sdesc.get() or owner.get_display_name(theroom)
|
||
msg = routput(pre_msg + " The << bartender ^ barkeep ^ elf >> << passes ^ slides ^ gives ^ hands >> a {0} to {1}.",
|
||
drink.db.cocktail_type, char)
|
||
theroom.msg_contents(msg, exclude=owner)
|
||
|
||
def do_drink(self, drinker):
|
||
"""
|
||
Called when owner calls the 'drink' command.
|
||
"""
|
||
amount = self.db.amount or 0
|
||
cocktail_type = self.db.cocktail_type or "cocktail"
|
||
cocktail_flavors = self.db.flavors
|
||
|
||
if amount > 2:
|
||
flavor = random.choice(cocktail_flavors)
|
||
drinker.msg(choices(DRINK_COCKTAIL_MSGS, self.key, cocktail_type, flavor))
|
||
elif amount > 0:
|
||
drinker.msg(choices(self.db.effects, self.key, cocktail_type))
|
||
drinker.score(Scores.finish_cocktail)
|
||
else:
|
||
self.key = f"{self.db.cocktail_type} glass"
|
||
self.aliases.add('glass')
|
||
self.db.desc = f"An empty {self.db.cocktail_type} glass"
|
||
drinker.msg(choices(EMPTY_COCKTAIL_MSGS, self.key, cocktail_type))
|
||
|
||
self.db.amount = self.db.amount - self.sip_amount
|
||
|
||
def at_pre_drop(self, dropper):
|
||
if dropper.location.key == 'Wyldwood Bar':
|
||
dropper.msg(routput(f"<< A ^ The >> mushroom << man ^ person >> << bounces ^ shimmies over ^ appears >> and takes the {self.name} << from you ^ >>."))
|
||
self.delete()
|
||
elif dropper.location.key in ("Cozy House", "Cramped Kitchen"):
|
||
dropper.msg(routput(f"The {self.db.cocktail_type} << falls ^ drops >> to the << floor ^ ground >> and shatters! A dust ball from under a chair forms into a little impish-looking fellow, who disappears with glass."))
|
||
self.delete()
|
||
else:
|
||
return True
|