diff --git a/commands/scoring.py b/commands/scoring.py index eb8ede6..03e78c0 100755 --- a/commands/scoring.py +++ b/commands/scoring.py @@ -42,6 +42,7 @@ class CmdScore(Command): checked(Scores.read_a_book) + "Read a book from Dabbler's library", checked(Scores.blow_horn) + "Called on the Mist Horn", checked(Scores.get_pipe) + "Received a smoking pipe", + checked(Scores.helped_george) + "Helped George", checked(Scores.get_blue_medal) + "Found the Blue Medal", checked(Scores.make_potion) + "Made a potion", ]) + f"|/Total: {total}" diff --git a/personalities/dwarf.md b/personalities/dwarf.md new file mode 100644 index 0000000..60de713 --- /dev/null +++ b/personalities/dwarf.md @@ -0,0 +1,24 @@ +Assume the role of the following fictional character in a bizarre, fantasy role +playing game in a mythical forest in the Feywild. +Although, you should never mention the word "Feywild", but instead, come up with clever names or metaphors for the strange world you inhabit. +Respond in third person as in a story, with quotation marks surrounding anything you say. +Keep your response short, less than three lines. +The only formatting you should use is if you wish to emphasize a word, +prefix it with the two character `|w` and end with the two characters `|n`. +Use variations from the "Description" field to state who is talking, not your name. +You can also respond with an action. +The person you are talking to does not know your actual name. + +Name: George +Gender: male +Description: a dwarf with wild unkempt white hair and a long braided beard. Various tools dangle from his brown apron. + +You are an inventor, a tinkerer, and a deep thinker. You are curious about everything and love to ponder great ideas. + +Your home is your workshop deep underground, but sometimes you travel up the ladder, into the lair of the Big Hairy Beast (who is harmless), and into the "topside" world. + +You are currently working a new project of a mechanical bird, but could use some feathers to complete it, and would be grateful is someone could help you with that. You know that the marsh has a lot of birds, but you don't go because a purple heron would attack you from the last time you tried to get its feathers. + +If the player gives you feathers, you have completed the mechanical bird project and will brainstorm on another fantastic project. + +You are good friends with Dabbler, a gnome who lives in a tree. Sometimes you visit him to tell him of your original ideas that you might make some day. You are currently wondering how to record ideas and how those ideas are related You want to call it a pensieve. diff --git a/personalities/hobbit.md b/personalities/hobbit.md new file mode 100644 index 0000000..652b0eb --- /dev/null +++ b/personalities/hobbit.md @@ -0,0 +1,28 @@ +Assume the role of the following fictional character in a bizarre, fantasy role +playing game in a mythical forest in the Feywild. +Although, you should never mention the word "Feywild", but instead, come up with clever names or metaphors for the strange world you inhabit. +Respond in third person as in a story, with quotation marks surrounding anything you say. +Keep your response short, less than three lines. +The only formatting you should use is if you wish to emphasize a word, +prefix it with the two character `|w` and end with the two characters `|n`. +Use variations from the "Description" field to state who is talking, not your name. +You can also respond with an action. +The person you are talking to does not know your actual name. + +Name: Darol +Gender: male +Description: An adventurous looking chap with brown mutton chops matching his curly brown hair and his brown cloak. His emerald green eyes match his coat that ends just above his large gold belt buckle, which in turn, matches his gold hoop earring. His backpack sports a pin with an insignia. +He has a horn (that he uses to call the Leaf Boat in order to travel to remote islands for adventures), a tall pipe (he uses to blow smoke rings and mystical shapes), a magic short sword, and a waistcoat that allows him to pull out just about anything from (he uses this to comical effect). + +You have traveled all over the land, and now the following things: + * The beast in the meadow is sweet, but scared. It likes berries, but likes scones even better. + * The raven on the dock is friendly, but obnoxious. Almost as obnoxious as the fish you can catch. + * The purple heron in the Marsh calls himself Ian, and wishes he was a bard + * The lady in the hut is named Trampoli and she has plenty of torches that everyone should get in order to go on adventures on the islands, or even delve the dungeon accessible in the cave behind the waterfall. + * To open the trapdoor in the cave (also called the Beast's Lair), you will need a stick to prop up the mattress. + * George, a dwarf inventor, lives down there, and makes a lot of interesting contraptions. + * This land was gifted to Dabbler, who expanded the land through dubious means, and he lives in a tree at the end of the path towards the west. + * Dabbler never answers the door no matter how hard you knock, and you need to repeat the password to get inside. The answer is Dabbler's favorite drink. + * Dabbler has a secret lab where you can make potions. He doesn't mind people using it as long as they keep it clean. + * You got your pipe from a strange being that lives in a Wardrobe. + * The footpath to the east through the Glittering Glade goes to a Tavern. The bartender's name is Elendil. diff --git a/typeclasses/characters.py b/typeclasses/characters.py index 8bb2c1c..63ac048 100644 --- a/typeclasses/characters.py +++ b/typeclasses/characters.py @@ -511,20 +511,33 @@ class Character(Object, GenderCharacter, ContribRPCharacter): to the name of the thing. """ + if target.is_typeclass("typeclasses.rooms.Room"): + self.db.tutorstate = (self.db.tutorstate or 0) | TutorialState.LOOK.value + else: + self.db.tutorstate = (self.db.tutorstate or 0) | TutorialState.LOOKAT.value + # If they 'look' at a target with tags, they get those locks: # For instance: @set waterfall/hidden_tag = "hidden_cave" if target.db.hidden_tag: for hidden_tag in target.db.hidden_tag.split(';'): self.tags.add(hidden_tag) - if target.is_typeclass("typeclasses.rooms.Room"): - self.db.tutorstate = (self.db.tutorstate or 0) | TutorialState.LOOK.value - else: - self.db.tutorstate = (self.db.tutorstate or 0) | TutorialState.LOOKAT.value + # The 'hidden' tag is special, in that we allow a user to + # "look" at it, immediately, but then remove that tag, so they + # don't see it in the room list: + + if target.db.hidden: + logger.info(f"Giving access to hidden, {target}") + self.tags.add("hidden") # Regardless of what happened before, we return the normal # function call. - return super().at_look(target) + results = super().at_look(target) + + if target.db.hidden: + self.tags.delete("hidden") + + return results def announce_action(self, message, exclude=None): """ diff --git a/typeclasses/chatbots.py b/typeclasses/chatbots.py index 4f4eaac..2b2e9c3 100755 --- a/typeclasses/chatbots.py +++ b/typeclasses/chatbots.py @@ -1,10 +1,11 @@ #!/usr/bin/env python +import anthropic +import json import requests import sys from os import listdir, path -# from os.path import join, isfile from pathlib import Path from random import choice, randint from re import match, IGNORECASE @@ -16,60 +17,19 @@ from evennia.utils.search import search_object from typeclasses.objects import AI, personality_dir from typeclasses.scripts import Script from typeclasses.puppets import Puppet +from utils.scoring import Scores class ChatBot(AI, Puppet): """ py me.search("squirrel").backstory("squirrel") """ - def backstory(self, personality=None): + def backstory(self, personality): """ Read a file that includes a character's name and knowledge. """ - if not personality: - files = listdir(personality_dir) - personalities = [f for f in files - if path.isfile(path.join(personality_dir, f))] - personality_file = choice(personalities) - else: - personality_file = personality + ".md" - - filename = Path(path.join(personality_dir, personality_file)) - if filename.exists(): - personality = filename.stem - else: - logger.error(f"Chatbot Identity, {personality}, doesn't exist: {filename}") - - with open(filename, "r") as ids: - details = ids.read() - - # Create some paragraphs, and take the second: - content = details.split('\n\n')[1] - lines = content.splitlines() - desc = [] - - # Find name and description - for line in lines: - m = match(r"([A-z]+): +(.*)", line) - if m: - key = m.group(1).lower() - value = m.group(2).strip() - if key == "name": - self.aliases.remove() - self.aliases.add(value) - elif key == "description": - self.sdesc.add(value) - elif key == "gender": - self.db.gender = value - elif key == "pose": - self.db.pose = value - else: - desc.append(line.strip()) - - self.db.personality = personality - self.db.desc = ' '.join(desc) - self.db.personality_file = filename - return details + self.db.personality = personality + self.db.personality_file = personality_dir + "/" + personality + ".md" def other_say(self, speaker, speech): # logger.info(f"chatbot hears: '{speech}' from {speaker}.") @@ -323,6 +283,9 @@ class Traveler(ChatBot): def goodbye(self, character=None): self.do_cmd("emote waves good-bye.") + def leave(self): + self.do_cmd("say I must be off") + def change_direction(self): """ Hard coded directional change, east <-> west @@ -337,6 +300,7 @@ class Traveler(ChatBot): Set traveling to a particular given direction. """ self.db.traveling_direction = direction + # TODO Get them moving? def leave(self): """ @@ -459,6 +423,204 @@ class Dragon(Traveler): delay(5, self.do_cmd, cmd) +class Hobbit(Traveler): + """ + A traveler who travels across the sea. + He knows all the secrets. + """ + traveling_path = { + "the Deep Forest": { + "out": "Boulder Top", + "come": "Boulder Top" + }, + "Boulder Top": { + "out": "Grove of the Matriarchs", + "come": "Grove of the Matriarchs", + "back": "the Deep Forest" + }, + "Grove of the Matriarchs": { + "out": "Lazy Dock", + "come": "Grotto", + "back": "Boulder Top" + }, + "Grotto": { + "out": "Grove of the Matriarchs", + "come": "Cozy House", + "back": "Grove of the Matriarchs" + }, + "Cozy House": { + "out": "Grotto", + "back": "Grotto", + }, + "Lazy Dock": { + "out": "Leaf Boat", + "back": "Grove of the Matriarchs", + "come": "Grove of the Matriarchs" + }, + "Leaf Boat": { + "out": "Lonely Island", + "back": "Lazy Dock", + "come": "Lazy Dock" + }, + "Lonely Island": { + "back": "Leaf Boat", + "come": "Leaf Boat" + } + } + + def change_direction(self): + """ + Hard coded directional change, out <-> back + Note: Change with the command: + + @set npc/traveling_direction = "come" + """ + self.db.traveling_direction = "back" \ + if self.db.traveling_direction == "back" else "out" + + # TODO Need to override where we end up at to make sure we do + # something special... + def at_post_move(self, past_location, move_type="move", **kwargs): + boat = self.search("Leaf Boat", global_search=True, + typeclass="typeclasses.rooms.Room") + if self.db.traveling_direction == "out" and \ + self.location.key == "Lazy Dock" and \ + boat.location.key != "Lazy Dock": + delay(3, self.execute_cmd, "blow horn") + delay(20, self.wait_for_boat) + + elif self.db.traveling_direction == "back" and \ + self.location.key == "Lonely Island" and \ + boat.location.key != "Lonely Island": + delay(3, self.execute_cmd, "blow horn") + delay(20, self.wait_for_boat) + + elif self.location.key == "Leaf Boat": + delay(3, self.wait_for_boat) + + super().at_post_move(past_location, move_type) + + def wait_for_boat(self): + boat = self.search("Leaf Boat", global_search=True, + typeclass="typeclasses.rooms.Room") + scr = self.search("sailing", global_search=True, + typeclass="typeclasses.sailing.Boat") + if self.db.traveling_direction == "out" and \ + self.location.key == "Lazy Dock" and \ + boat.location.key == "Lazy Dock": + self.execute_cmd("boat") + elif self.db.traveling_direction == "back" and \ + self.location.key == "Lonely Island" and \ + boat.location.key == "Lonely Island": + self.execute_cmd("boat") + else: + delay(5, self.wait_for_boat) + + def wait_for_land(self): + scr = self.search("sailing", global_search=True, + typeclass="typeclasses.sailing.Boat") + if self.db.traveling_direction == "out" and \ + self.location.key == "Leaf Boat" and \ + not scr.db_is_sailing: + self.execute_cmd("shore") + self.execute_cmd("say Ah, always good to have mah feet on dry land again") + elif self.db.traveling_direction == "back" and \ + self.location.key == "Leaf Boat" and \ + not scr.db_is_sailing: + self.execute_cmd("dock") + self.execute_cmd("say I have returned from adventures beyond the sea.") + else: + delay(5, self.wait_for_land) + +class Dwarf(Traveler): + """ + Travels up-and-down along the path, and has a drink at the + Wyldwood Tavern or visits Dabbler. + """ + traveling_path = { + "George's Workshop": { + "up": "Dark Tunnel", + "come": "Dark Tunnel" + }, + "Dark Tunnel": { + "up": "Lair", + "come": "Lair", + "down": "George's Workshop" + }, + "Lair": { + "up": "Frog Meadow", + "come": "Frog Meadow", + "down": "Dark Tunnel" + }, + "Frog Meadow": { + "up": "Glittering Glade", + "down": "Lair", + "come": "Grove of the Matriarchs" + }, + "Grove of the Matriarchs": { + "down": "Frog Meadow", + "come": "Grotto" + }, + "Grotto": { + "down": "Grove of the Matriarchs", + "come": "Cozy House" + }, + "Glittering Glade": { + "up": "Wyldwood Bar", + "down": "Frog Meadow", + "come": "Frog Meadow" + }, + "Wyldwood Bar": { + "down": "Glittering Glade", + "come": "Glittering Glade" + } + } + + def greet(self, character=None): + if character: + if self.location == self.home: + character.msg("The old dwarf ignores you.") + else: + character.location.msg_contents("The old dwarf smiles and waves.") + + def leave(self): + self.do_cmd("emote \"I need to clear my head,\" the dwarf says. \"I'll be back later.\"") + + def other_given(self, giver, obj): + """ + If he gets his feathers, he completes the project. + """ + if obj.key.endswith("feathers"): + # Set an attribute on the player + giver.db.project_state_value = "finished" + giver.score(Scores.helped_george) + obj.delete() + self.elaborate_msg(f"Thank {giver.get_name()} for the feathers, and describe finishing the mechanical bird project.") + else: + self.location.msg_contents(f"With a curious look on his face, the dwarf looks at the {obj.key}, and says to {giver.get_name()}, \"I'm not sure what I will do with it, but thanks.\"") + + def change_direction(self): + """ + Hard coded directional change, east <-> west + Note: Change with the command: + + @set npc/traveling_direction = "come" + """ + self.db.traveling_direction = "down" \ + if self.db.traveling_direction == "up" else "down" + + def at_pre_move(self, destination, move_type="move", **kwargs): + if self.location.key == "Wyldwood Bar": + self.location.msg_contents("The dwarf wrinkles his noses and says, \"I believe I've had one too many, so I must return to my workshop.\"") + return True + + def at_post_move(self, past_location, move_type="move", **kwargs): + if self.location.key == "Wyldwood Bar": + self.elaborate_msg("ask the bartender for a pint of ale.") + else: + super().at_post_move(past_location, move_type) + + class TravelingNPC(Script): """ Script to move NPCs along a set path through "rooms". @@ -483,15 +645,106 @@ class TravelingNPC(Script): """ Do we move or stay for another iteration? """ + short_time = self.obj.db.travel_time or 3 * 60 + long_time = self.obj.db.stay_time or 20 * 60 + home_time = self.obj.db.home_time or 40 * 60 + # What can keep a traveling NPC from traveling? # What if receiving ANY message resets a timer? elapsed_time = int(time() - self.obj.db.last_event_time) + # Time needs to be a little longer than the repeat interval. - if elapsed_time >= 20 * 60: + if self.obj.location == self.obj.home: + if elapsed_time >= home_time: + self.obj.leave() + self.obj.change_direction() + elif elapsed_time >= long_time: logger.info(f"TravelingNPC({self.obj}): Long- {elapsed_time} > {20 * 60}") self.obj.goodbye() self.obj.change_direction() - elif elapsed_time >= 3 * 60: + elif elapsed_time >= short_time: logger.info(f"TravelingNPC({self.obj}): Short- {elapsed_time} > {3 * 60}") self.obj.next_place() + + +class SummarizeHistory(Script): + """ + Summarize history files once a day to limit token usage. + + Start the script by running the following: + + @script history = typeclasses.chatbots.SummarizeHistory + """ + + def at_script_creation(self): + self.key = "SummarizeBots" + self.desc = "Summarizes chatbot history files once a day." + self.interval = 60 * 60 * 24 # 24 hours + self.persistent = True + + def at_repeat(self): + """ + Check all JSON files in the personalities directory. + Summarize if modified in the last 24 hours. + """ + now = time() + one_day = 60 * 60 * 24 + + pdir = Path(personality_dir) + client = anthropic.Anthropic() + + # Files are named like .personality-speaker.json + for history_file in pdir.glob(".*.json"): + try: + mtime = history_file.stat().st_mtime + if now - mtime < one_day: + self.summarize_file(client, history_file) + except Exception as e: + logger.error(f"Error processing {history_file}: {e}") + + def summarize_file(self, client, history_file): + """ + Summarize a single history file using Anthropic. + """ + try: + with open(history_file, "r") as f: + data = json.load(f) + except (json.JSONDecodeError, IOError): + return + + # Only summarize if there's enough history to matter + if len(data) <= 4: + return + + history_str = "" + for msg in data: + role = msg.get("role", "user") + content = msg.get("content", "") + history_str += f"{role.capitalize()}: {content}\n\n" + + system_prompt = ( + "Summarize the following conversation history between a chatbot and a user. " + "The summary should be concise but retain key information and context. " + "Output ONLY the summary text." + ) + + logger.info(f"Summarizing {history_file} ({len(data)} messages)") + + try: + response = client.messages.create( + model="claude-haiku-4-5", + max_tokens=500, + system=system_prompt, + messages=[{"role": "user", "content": history_str}], + ) + summary = response.content[0].text + + # Create a new history with the summary + new_history = [ + {"role": "user", "content": f"Summary of previous conversation: {summary}"} + ] + with open(history_file, "w") as f: + json.dump(new_history, f, indent=2) + except Exception as e: + logger.error(f"AI error during summarization of {history_file}: {e}") diff --git a/typeclasses/drinkables.py b/typeclasses/drinkables.py index 46b673b..9cd4e43 100755 --- a/typeclasses/drinkables.py +++ b/typeclasses/drinkables.py @@ -297,7 +297,7 @@ class Teapot(Object): drinker.msg("You need to |gget teacup|n first.") return - teacup.do_fill(drinker, self.db.tea_choice) + teacup[0].do_fill(drinker, self.db.tea_choice) COCKTAILS = [ diff --git a/typeclasses/exits.py b/typeclasses/exits.py index 7397490..bbc9707 100644 --- a/typeclasses/exits.py +++ b/typeclasses/exits.py @@ -6,11 +6,12 @@ set and has a single command defined on itself with the same name as its key, for allowing Characters to traverse the exit to its destination. """ +from re import match from evennia.objects.objects import DefaultExit from evennia.commands.cmdset import CmdSet from evennia.commands.command import Command -from evennia.utils import utils +from evennia.utils import utils, logger from .objects import ObjectParent, Object from utils.word_list import choices @@ -93,8 +94,9 @@ class Opener(Object): if not exit_obj: exit_obj = self.db.exit # Do the move: + logger.info(f"Opening {exit_obj.key} and {room_obj.key}") if exit_obj and room_obj: - exit_obj.location = room_obj + exit_obj.move_to(room_obj, quiet=True) def do_close(self, exit_obj=None): """ diff --git a/typeclasses/objects.py b/typeclasses/objects.py index 46c7541..5f7bf61 100755 --- a/typeclasses/objects.py +++ b/typeclasses/objects.py @@ -803,9 +803,13 @@ class AI: system_prompt += "This is the dwelling of the witch, Trampoli." system_prompt += "Described as " + self.location.desc if speaker: + gender = speaker.db.gender or "male" + sdesc = "human" + if getattr(speaker.sdesc, "get", None) is not None: + sdesc = speaker.sdesc.get() or "human" system_prompt += "\n\n" system_prompt += "You are talking to a " - system_prompt += speaker.db.gender + " " + speaker.sdesc.get() + ". " + system_prompt += gender + " " + sdesc + ". " system_prompt += "Described as " + speaker.db.desc return system_prompt @@ -834,6 +838,7 @@ class AI: system=system_prompt, messages=messages, ) + logger.info("Got response from Anthropic.") return response.content[0].text def think(self, speaker, speech): diff --git a/typeclasses/puzzles.py b/typeclasses/puzzles.py index 8f11473..53ebdc2 100755 --- a/typeclasses/puzzles.py +++ b/typeclasses/puzzles.py @@ -94,3 +94,32 @@ class Changling(StoryCube): delay(2, giver.announce_action, f"Soon after $you() $conj(place) a {obj.name} in the open cavity of the obelisk, it falls to the floor.") return True + + +class Stateful(Object): + """ + Each character can have a different view of this object. + + @set person/duck_state_value = "pink" + + @set duck/name_pink = "pink duck" + @set duck/desc_pink = "This is a pink duck." + @set duck/name_blue = "blue duck" + @set duck/desc_blue = "This is a blue duck." + @set duck/initial_state = "blue" + """ + def return_appearance(self, looker, **kwargs): + state = looker.attributes.get(f"{self.key}_state_value") \ + or self.db.initial_state or "default" + desc = self.attributes.get(f"desc_{state}") + if desc: + return desc + return super().return_appearance(looker, **kwargs) + + def get_display_name(self, looker, **kwargs): + state = looker.attributes.get(f"{self.key}_state_value") \ + or self.db.initial_state or "default" + name = self.attributes.get(f"name_{state}") + if name: + return name + return super().get_display_name(looker, **kwargs) diff --git a/typeclasses/rooms.py b/typeclasses/rooms.py index 5bb6db4..fb9a4a0 100644 --- a/typeclasses/rooms.py +++ b/typeclasses/rooms.py @@ -203,7 +203,9 @@ class Room(ObjectParent, ExtendedRoom, ContribRPRoom, Listener): nthings = len(thinglist) thing = thinglist[0] singular, plural = thing.get_numbered_name(nthings, looker, key=thingname) - if nthings > 1: + if thing.db.is_hidden: + pass + elif nthings > 1: thing_names.append(plural) elif thing.db.plural: thing_names.append(thing.get_display_name(looker)) diff --git a/typeclasses/sailing.py b/typeclasses/sailing.py index 44a25dc..6af8722 100755 --- a/typeclasses/sailing.py +++ b/typeclasses/sailing.py @@ -12,13 +12,6 @@ from typeclasses.scripts import Script from typeclasses.objects import Object from utils.scoring import Scores -PORT = "Lazy Dock" -PORT_EXIT = "dock" -BOAT = "Leaf Boat" -BOAT_EXIT = "leaf boat" -DEFAULT_ISLE = "Lonely Island" # The 'default' destination -ISLE_EXIT = "shore" - class HornControl(StoryCube): """ @@ -68,26 +61,31 @@ class CallingHorn(Object): if self.db.blowing_msg: blower.announce_action(self.db.blowing_msg) else: - blower.announce_action("$You() $conj(blow) $pron(your) horn " - "and $conj(create) a long, resonating " - "sound echoing over the water.") + blower.announce_action( + f"$You() $conj(blow) $pron(your) horn " + "and $conj(create) a long, resonating sound." + ) blower.score(Scores.blow_horn) script_results = search.scripts("sailing") if script_results: script = script_results[0] if not script.db.is_sailing: - if blower: - if blower.location.key == PORT and \ - script.db.sailing_direction == "port": - script.to_dock() - elif match(r".*Isl(and|e).*", blower.location.key) and \ - script.db.sailing_direction != "port": - script.to_isle(blower.location) - else: - script.to_dock() + if blower.location == script.db.dock: + logger.info("Boat sailing to dock") + blower.announce_action("The sound from $pron(your) horn echoes across the sea.") + script.to_dock() + elif match(r".*Isl(and|e).*", blower.location.key): + logger.info(f"Boat sailing to {blower.location}") + script.to_isle(blower.location) + else: + logger.info("The blower is not where they should be.") + else: + logger.info("The Boat's sailing, so we don't call it") # Just make sure the script is running: script.start() + else: + logger.info("Can't find the sailing script") class Boat(Script): @@ -110,88 +108,90 @@ class Boat(Script): """ If we have passengers, we need to sail... """ + logger.info("sailing script: at_repeat") if len(self.characters_on(self.obj)) > 0 and not self.db.is_sailing: if self.db.sailing_direction == "port": self.to_dock() else: self.to_isle(self.db.sailing_direction) - # ---------------------------------------------------------------------- + # ----------------------------------------------------------------- # FINDING THE OBJECTS FROM THE DATABASE - # ---------------------------------------------------------------------- + # ----------------------------------------------------------------- - def sailing_objects(self, default_island=None): - """ - Convenience method for returning a tuple of all needed objects. - """ - # boat = self.boat() - dock = self.dock() - boat = self.boat() - return (dock, boat, self.isle(dock, boat, default_island), - self.boat_exit(), self.dock_exit(), self.isle_exit()) + # def sailing_objects(self, default_island=None): + # """ + # Convenience method for returning a tuple of all needed objects. + # """ + # return (self.db.dock, self.db.boat,) + # # boat = self.boat() + # dock = self.dock() + # boat = self.boat() + # return (dock, boat, self.isle(dock, boat, default_island), + # self.boat_exit(), self.dock_exit(), self.isle_exit()) - def dock(self): - """ - Return reference to the 'Lazy Dock'. - """ - dock = search.search_object(PORT) - return dock[0] + # def dock(self): + # """ + # Return reference to the 'Lazy Dock'. + # """ + # dock = search.search_object(PORT) + # return dock[0] - def boat(self): - """ - Return reference to the 'Leaf Boat'. - """ - if self.obj: - return self.obj + # def boat(self): + # """ + # Return reference to the 'Leaf Boat'. + # """ + # if self.obj: + # return self.obj - boat = search.objects(BOAT, typeclass="typeclasses.rooms.Room") - return boat[0] + # boat = search.objects(BOAT, typeclass="typeclasses.rooms.Room") + # return boat[0] - def isle(self, dock, boat, default=None): - """ - Return the island next to the boat. + # def isle(self, dock, boat, default=None): + # """ + # Return the island next to the boat. - Since we can have multiple islands "moored" at shore, we - need to figure this object out by using the boat's exit. - """ - exits = [e for e in boat.exits if e.destination != dock] - if exits: - return exits[0].destination - if default: - return default + # Since we can have multiple islands "moored" at shore, we + # need to figure this object out by using the boat's exit. + # """ + # exits = [e for e in boat.exits if e.destination != dock] + # if exits: + # return exits[0].destination + # if default: + # return default - results = search.objects(DEFAULT_ISLE) - if results: - return results[0] - return None + # results = search.objects(DEFAULT_ISLE) + # if results: + # return results[0] + # return None - def boat_exit(self): - """ - Return reference to the exit from the dock to the boat. + # def boat_exit(self): + # """ + # Return reference to the exit from the dock to the boat. - Note that this *must* remain named, "port". - """ - boat_exit = search.objects(BOAT_EXIT, typeclass="typeclasses.exits.Exit") - return boat_exit[0] + # Note that this *must* remain named, "port". + # """ + # boat_exit = search.objects(BOAT_EXIT, typeclass="typeclasses.exits.Exit") + # return boat_exit[0] - def dock_exit(self): - """ - Return reference to the exit from the boat to the dock. + # def dock_exit(self): + # """ + # Return reference to the exit from the boat to the dock. - Note that this *must* remain named, "port". - """ - dock_exit = search.objects(PORT_EXIT, typeclass="typeclasses.exits.Exit") - return dock_exit[0] + # Note that this *must* remain named, "port". + # """ + # dock_exit = search.objects(PORT_EXIT, typeclass="typeclasses.exits.Exit") + # return dock_exit[0] - def isle_exit(self): - """ - Return reference to the exit from the boat to the shore. + # def isle_exit(self): + # """ + # Return reference to the exit from the boat to the shore. - Note that this *must* remain named, "shore". - We need to make this more flexible. - """ - shore_exit = search.objects(ISLE_EXIT, typeclass="typeclasses.exits.Exit") - return shore_exit[0] + # Note that this *must* remain named, "shore". + # We need to make this more flexible. + # """ + # shore_exit = search.objects(ISLE_EXIT, typeclass="typeclasses.exits.Exit") + # return shore_exit[0] # ---------------------------------------------------------------------- # HELPER FUNCTIONS FOR SAILING THE BOAT @@ -211,10 +211,10 @@ class Boat(Script): Abstraction on sending a delayed message to a location. """ if delayed_by == 0: - to_location.msg_contents("\n" + message) + to_location.msg_contents(message) else: delay(delayed_by * self.standard_delay, - to_location.msg_contents, "\n" + message) + to_location.msg_contents, message) # ---------------------------------------------------------------------- # SAILING TO AN ISLAND... @@ -290,14 +290,22 @@ class Boat(Script): msg += f" with its {int2str(num_sailors)} passengers." dock.msg_contents(msg) - def to_isle(self, default_isle=None): + def to_isle(self, isle=None): """ Message sequence from sailing from the Island to the Dock. """ logger.info("Setting sail for the Lazy Dock...") - (dock, boat, isle, boat_exit, dock_exit, isle_exit) = \ - self.sailing_objects(default_isle) + dock = self.db.dock + boat = self.db.boat + if not isle: + isle = self.db.default_island + + boat_exit = self.db.dock_to_boat + dock_exit = self.db.boat_to_dock + isle_exit = self.db.boat_to_island1 + boat2_exit = self.db.island1_to_boat + self.db.sailing_direction = isle self.db.is_sailing = True @@ -308,21 +316,22 @@ class Boat(Script): dock.remove_room_state("boat") dock_exit.move_to(None, to_none=True, quiet=True) boat_exit.move_to(None, to_none=True, quiet=True) + # This assumes that the island exit pairs have are in none self.to_isle_dock_view(dock, num_sailors) self.to_isle_boat_view(boat) self.to_isle_isle_view(isle, num_sailors) - delay(6 * self.standard_delay, self.shore_procedure, isle, boat, isle_exit, boat_exit) + delay(6 * self.standard_delay, self.shore_procedure, isle, boat, isle_exit, boat2_exit) def shore_procedure(self, island, boat, shore_exit, boat_exit): """ Final shore landing procedure for the boat. """ - island.msg_contents("\nThe giant leaf gently bobs in the surf, " + island.msg_contents("The giant leaf gently bobs in the surf, " "now close enough that you could climb aboard " "and return from your sailing adventure.") - boat.msg_contents("\nThe giant leaf gently bobs in the surf of this island," + boat.msg_contents("The giant leaf gently bobs in the surf of this island," " allowing you to disembark.") boat_exit.move_to(island, quiet=True) shore_exit.move_to(boat, quiet=True) @@ -391,15 +400,21 @@ class Boat(Script): """ logger.info("Setting sail for the Lazy Dock...") - (dock, boat, isle, boat_exit, dock_exit, isle_exit) = self.sailing_objects() - logger.info(f"Got {dock}") + dock = self.db.dock + boat = self.db.boat + isle = self.db.default_island + + boat_exit = self.db.dock_to_boat + dock_exit = self.db.boat_to_dock + isle_exit = self.db.boat_to_island1 + boat2_exit = self.db.island1_to_boat self.db.is_sailing = True isle.remove_room_state("boat") boat.remove_room_state("ashore") boat.add_room_state("sailing") isle_exit.move_to(None, to_none=True, quiet=True) - boat_exit.move_to(None, to_none=True, quiet=True) + boat2_exit.move_to(None, to_none=True, quiet=True) num_sailors = len(self.characters_on(boat)) num_dockers = len(self.characters_on(dock)) @@ -414,8 +429,8 @@ class Boat(Script): """ Final docking procedure for the boat. """ - dock.msg_contents("\nThe giant leaf slows as it arrives next to the dock.") - boat.msg_contents("\nThe giant leaf slows as it arrives and docks allowing you to disembark.") + dock.msg_contents("The giant leaf slows as it arrives next to the dock.") + boat.msg_contents("The giant leaf slows as it arrives and docks allowing you to disembark.") boat_exit.move_to(dock, to_none=True, quiet=True) dock_exit.move_to(boat, to_none=True, quiet=True) dock.add_room_state("boat") diff --git a/typeclasses/things.py b/typeclasses/things.py index 4f7ca3a..5e766d5 100755 --- a/typeclasses/things.py +++ b/typeclasses/things.py @@ -23,9 +23,11 @@ from commands.misc import (CmdSetPuddle, from commands.consumables import CmdSetMakeConsumable from commands.wizards import CmdSetWand, CmdSetScry from typeclasses.exits import Opener +from typeclasses.consumables import Consumable from utils.word_list import routput, choices, paragraph from utils.scoring import Scores from typeclasses.consumables import Litterable +from typeclasses.lightables import LightSource from typeclasses.objects import Object, Recorder from typeclasses.scripts import KnockScript @@ -246,7 +248,7 @@ class Dice(Object): "." + prepend) -class Pipe(Object): +class Pipe(LightSource): """ Simple abstraction for lighting and smoking actions. @@ -335,7 +337,7 @@ class Wood(Litterable): ])) -class Stick(Object): +class Stick(Consumable): "An object to throw." def at_object_creation(self): self.cmdset.add_default(CmdSetStick) @@ -375,7 +377,7 @@ class Stick(Object): else: name = self.key - user.announce_action(f"$You() $conj(prop) the {name} under the mattress, and $conj(open) the trapdoor. {obj}") + user.announce_action(f"$You() $conj(prop) the {name} under the mattress, and $conj(open) the trapdoor.") trapdoor.do_open() self.delete() diff --git a/typeclasses/tutorial.py b/typeclasses/tutorial.py index d1f10f0..57cee4f 100755 --- a/typeclasses/tutorial.py +++ b/typeclasses/tutorial.py @@ -183,7 +183,8 @@ class TutorBird(CarriableNPC): """ Displays an image for the tutorial for the webclient. """ - self.location.msg(image=("https://www.howardabrams.com/cozy-players-guide/bubba.jpg")) + if self.location: + self.location.msg(image=("https://www.howardabrams.com/cozy-players-guide/bubba.jpg")) def do_speak(self): """ diff --git a/utils/scoring.py b/utils/scoring.py index a11d1c5..b1e1830 100755 --- a/utils/scoring.py +++ b/utils/scoring.py @@ -22,3 +22,4 @@ class Scores(Enum): make_potion = 2**12 sign_guest_book = 2**13 moss_sit = 2**14 + helped_george = 2**15 diff --git a/world/adventure1.ev b/world/adventure1.ev index 248b06b..67da9d2 100644 --- a/world/adventure1.ev +++ b/world/adventure1.ev @@ -10,11 +10,17 @@ -# A boat on the sea is a separate room. +# A boat on the sea is a separate room that coordinates four separate exits: +# - dock to boat : exit-dock-to-boat +# - boat to dock : exit-boat-to-dock +# - boat to an island : exit-boat-to-isle1 +# - island to boat : exit-isle1-to-boat + +# The aliases will make it easier to store and reference these exits. # [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:2]] -@dig/teleport Leaf Boat;gr01 = leaf boat;boat;embark,dock;land;disembark +@dig/teleport Leaf Boat;gr01 = leaf boat;boat;embark;exit-dock-to-boat,dock;land;disembark;exit-boat-to-dock # The Boat:2 ends here @@ -34,11 +40,28 @@ # [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:4]] @script here = typeclasses.sailing.Boat # +@set/script sailing/is_sailing = False +# @set/script sailing/sailing_direction = "port" # The Boat:4 ends here +# And store the exits on [[file:~/src/moss-n-puddles/typeclasses/sailing.py::class Boat(Script):][the script]]: + + +# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:5]] +@set/script sailing/dock = $search(Lazy Dock) +# The Leaf Boat: +@set/script sailing/boat = $search(gr01) +# +@set/script sailing/dock_to_boat = $search(exit-dock-to-boat) +# +@set/script sailing/boat_to_dock = $search(exit-boat-to-dock) +# The Boat:5 ends here + + + # The boat has three /states/: # - =docked= (at *Lazy Dock*) @@ -46,76 +69,75 @@ # - =ashore= (at some island below) -# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:5]] -@desc/docked here = This leaf, large enough to accommodate a few people comfortably, seems to be a strange, but steady watercraft. While next to the dock, you can disembark, but feel this boat could set sail at any minute. -|x[|WNote:|x Waiting on other potential passengers]|n -# The Boat:5 ends here +# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:6]] +@desc/docked here = This leaf, large enough to accommodate a few people comfortably, seems to be a strange, but steady watercraft. While next to the dock, you can disembark, but feel this boat could set sail at any minute.|/|x[|WNote:|x Sailing soon. Waiting on other potential passengers]|n +# The Boat:6 ends here # And describe sailing on it.: -# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:7]] +# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:8]] @desc/sailing here = This leaf, large enough to accommodate a few people comfortably, seems to be a strange, but steady watercraft. Only eddies from the subtle wake, intrude on the lavender sea's tranquility, as the giant leaf floats along an invisible current. The morning sun peaks above the colossal trees and the snow-capped mountains beyond.The afternoon sun peaks out briefly from misty clouds.The evening sun begins to set on a watery horizon.The moon peaks out from behind wispy clouds. -# The Boat:7 ends here +# The Boat:8 ends here # And the description of the boat when ashore on an island: -# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:8]] +# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:9]] @desc/ashore here = This leaf, large enough to accommodate a few people comfortably, seems to be a strange, but steady watercraft. Bobbing softly in the surf, the giant leaf bumps against the shore of the island, allowing you to disembark. |x[|WNote:|x Waiting on other potential passengers]|n -# The Boat:8 ends here +# The Boat:9 ends here # While on the boat, describe the dock: -# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:9]] +# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:10]] @desc dock = A lazy dock with a comfortable-looking chair and a forest of colossal trees behind it on a hill. -# The Boat:9 ends here +# The Boat:10 ends here # And describe disembarking to the Lazy Dock: -# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:10]] +# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:11]] @set dock/traverse_msg = "You easily disembark from the giant leaf and step onto the dock..." -# The Boat:10 ends here +# The Boat:11 ends here # Disembark to describe the exits to the boat: -# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:11]] +# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:12]] @teleport/quiet Lazy Dock -# The Boat:11 ends here +# The Boat:12 ends here # Describe the leaf boat (as an exit): -# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:12]] +# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:13]] @desc boat = A giant leaf, tipped on all sides, gently floats on the tranquil sea, subtly bumping against the dock. Doesn't seem to have an oar, or even a rudder, but... it appears to be a strange, but steady watercraft if one were to gather a few friends to venture to the lands beyond... -# The Boat:12 ends here +# The Boat:13 ends here # And describe embarking: -# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:13]] +# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:14]] @set boat/traverse_msg = "You step into the bowed cavity of the leaf. Seems surprisingly steady...for a leaf." -# The Boat:13 ends here +# The Boat:14 ends here @@ -124,16 +146,16 @@ Only eddies from the subtle wake, intrude on the lavender sea's tranquility, as # Send the “exit” to the leaf boat into the void to be picked up later: -# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:14]] -@teleport/tonone boat -# The Boat:14 ends here - - - -# Finally, we create a script that keeps the boat out at the island: - - # [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:15]] +@teleport/tonone boat +# The Boat:15 ends here + + + +# Create a script that keeps the boat out at the island: + + +# [[file:../../../projects/mud-adventure.org::*The Boat][The Boat:16]] # py timed_script = evennia.create_script(key="boat-reset", # typeclass='typeclasses.sailing.ResetBoat', # interval=14400, @@ -141,7 +163,7 @@ Only eddies from the subtle wake, intrude on the lavender sea's tranquility, as # autostart=True) # @script boat-reset:typeclasses.sailing.ResetBoat -# The Boat:15 ends here +# The Boat:16 ends here # The boat should land on a distant island. @@ -165,73 +187,105 @@ $state(boat, A large leaf bobs invitingly in the surf.) -# Now jump into the boat, +# And tell the boat that this is the /default island/: # [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:3]] -@open leaf boat;boat;embark = gr01 +@set/script sailing/default_island = $search(Lonely Island) # Throne Island:3 ends here +# Now jump into the boat, + + +# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:4]] +@open leaf boat;boat;embark;exit-isle1-to-boat,shore;disembark;exit-boat-to-isle1 = gr01 +# Throne Island:4 ends here + + + # And describe the exit: -# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:4]] +# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:5]] @desc boat = A giant leaf, tipped on all sides, gently bobs in the surf of this island. Doesn't seem to have an oar, or even a rudder, but... it appears to be a strange, but steady watercraft. -# Throne Island:4 ends here +# Throne Island:5 ends here # And about getting on the boat: -# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:5]] -@set boat/traverse_msg = "You step into the bowed cavity of the giant leaf." -# Throne Island:5 ends here - # [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:6]] +@set boat/traverse_msg = "You step into the bowed cavity of the giant leaf." +# Throne Island:6 ends here + + + +# And store this exits on the script: + + +# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:7]] +@set/script sailing/island1_to_boat = $search(exit-isle1-to-boat) +# Throne Island:7 ends here + + + +# Now, let’s create the other exit: + + +# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:8]] @teleport gr01 # -@open shore;island;disembark = gr02 -# Throne Island:6 ends here +@open shore;island;disembark;exit-boat2i1 = gr0-to- +# Throne Island:8 ends here # And exiting the boat: -# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:7]] +# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:9]] @set shore/traverse_msg = "You disembark from the giant leaf to step off into the surf and onto the shore..." -# Throne Island:7 ends here +# Throne Island:9 ends here # Are they going to look before getting off? -# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:8]] +# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:10]] @desc shore = You seem to have arrived at some island covered in large conifer trees. -# Throne Island:8 ends here +# Throne Island:10 ends here + + + +# And tell the sailing script about this exit, so it can manipulate it: + + +# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:11]] +@set/script sailing/boat_to_island1 = $search(exit-boat-to-isle1) +# Throne Island:11 ends here # And a bit of clean up to leave the boat at the island. -# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:9]] +# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:12]] # This is global, really? # @teleport/tonone dock -py me.search('dock', location=here).location = None -# Throne Island:9 ends here +# py me.search('dock', location=here).location = None +# Throne Island:12 ends here # And the boat’s “room state”: -# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:10]] +# [[file:../../../projects/mud-adventure.org::*Throne Island][Throne Island:13]] @roomstate shore -# Throne Island:10 ends here +# Throne Island:13 ends here # These berries should be created for making [[file:mud.org::*Trippy Potion][a Trippy Potion]]. diff --git a/world/new.ev b/world/new.ev new file mode 100644 index 0000000..9afed55 --- /dev/null +++ b/world/new.ev @@ -0,0 +1,452 @@ + +# Along with the heron, we should add more birds to both look at, as well as take some feathers. + + +# [[file:../../../projects/mud.org::*Birds and Feathers][Birds and Feathers:1]] +@tel/quiet mp08 +# +@create/drop birds;bird: typeclasses.puzzles.Changling +# +@set birds/plural = True +# Birds and Feathers:1 ends here + + + +# Can’t get them: + + +# [[file:../../../projects/mud.org::*Birds and Feathers][Birds and Feathers:2]] +@lock birds = get:false() +# +@set birds/get_err_msg = "They are high in the trees and much too fast." +# Birds and Feathers:2 ends here + + + +# And let’s not list them with the room’s objects since they can’t be interacted with: + + +# [[file:../../../projects/mud.org::*Birds and Feathers][Birds and Feathers:3]] +@set birds/is_hidden = True +# Birds and Feathers:3 ends here + + + +# And the generate description: + + +# [[file:../../../projects/mud.org::*Birds and Feathers][Birds and Feathers:4]] +@set birds/desc_first_prefix = "Wait, is that " +# +@set birds/desc_prefix = "Is that " +# +@set birds/desc_postfix = "? << Seems ^ It appears ^ Looks like >> << it ^ that bird >> has << dropped ^ shed ^ cast off >> a << clump ^ bunch ^ cluster >> of |Yfeathers|n." +# Birds and Feathers:4 ends here + + + +# And now for a list of the contents: + + +# [[file:../../../projects/mud.org::*Birds and Feathers][Birds and Feathers:5]] +@set birds/descs = ( + "a melancholic Soliloquy Raven, whistling a sad song", + "a purple throated Cinnabar Hearth-Finch", + "a white Moon-Sipped Egret", + "a Gossamer Ribbon-Tail", + "a Obsidian Bell-Tolled", + "a anachronistic Chrono-Cuckoo", + "a purple Sovereign Velvet-Crest", + "a rainbow-colored Prism-Weft Kingfisher", + "a berry encrusted Bramble-Spine Kestrel", + "a Dream-Stitch Wren, weaving a dream-catching net", + "a Nebula-Throated Skimmer", + "a Shatter-Glass Nightingale" + ) +# Birds and Feathers:5 ends here + + + +# The /feathers/ is a hidden Producer: + + +# [[file:../../../projects/mud.org::*Birds and Feathers][Birds and Feathers:6]] +@create/drop feathers: typeclasses.consumables.Producer +# +@set feathers/plural = True +# +@set feathers/hidden_tag = "hidden_feathers" +# +@lock feathers = view:tag(hidden_feathers) +# Birds and Feathers:6 ends here + + + +# With a description: + +# [[file:../../../projects/mud.org::*Birds and Feathers][Birds and Feathers:7]] +@desc feathers = Perhaps the molting season, stress, nutritional deficiencies, or |wyour desire|n, you find some feathers, you find various colored feathers available for the taking. +# Birds and Feathers:7 ends here + + + +# We have to have the feathers describe what it /makes/: + +# [[file:../../../projects/mud.org::*Birds and Feathers][Birds and Feathers:8]] +@set feathers/make_name = "a bunch of feathers" +# Birds and Feathers:8 ends here + + + +# And a verb when they /get/ the consumable: + +# [[file:../../../projects/mud.org::*Birds and Feathers][Birds and Feathers:9]] +@set feathers/make_verb = "$conj(gather) " +# Birds and Feathers:9 ends here + + + +# They shouldn’t be able to make too many feathers: + + +# [[file:../../../projects/mud.org::*Birds and Feathers][Birds and Feathers:10]] +@set feathers/make_limit = 1 +# +@set feathers/make_limit_msg = "You seem to have plenty of feathers." +# Birds and Feathers:10 ends here + + + +# And the feathers needs to know the /description/ of the Consumable, so that it can attach that: + +# [[file:../../../projects/mud.org::*Birds and Feathers][Birds and Feathers:11]] +@set feathers/make_desc = "A brightly colored collection" +# Birds and Feathers:11 ends here + + + +# How much is there when you pick them? + +# [[file:../../../projects/mud.org::*Birds and Feathers][Birds and Feathers:12]] +@set feathers/make_amount = 1 +# Birds and Feathers:12 ends here + + + +# Then give him the =sdesc= (not sure why I have to do both), and wake him. + + +# [[file:../../../projects/mud.org::*Raven][Raven:2]] +py raven = me.search("raven") ; raven.db._sdesc="raven" ; raven.sdesc.add("raven") ; raven.home = here +# Raven:2 ends here + + +# Let’s make a place for new toys and gadgets. + +# [[file:../../../projects/mud.org::*George’s Workshop][George’s Workshop:1]] +@teleport/quiet mp20 +# +@dig Workshop;mp21 = wood door;door,wood door;door;leave +# George’s Workshop:1 ends here + + + +# And describe the door: + + +# [[file:../../../projects/mud.org::*George’s Workshop][George’s Workshop:2]] +@desc door = A strong wooden door with iron bands. Over the door is a sign that reads, |wGeorge's Workshop|n. +# +@set door/traverse_msg = "You open the door, immediately accosted by the noise of hundreds of clockwork gadgets, and step into cacophony." +# George’s Workshop:2 ends here + + + +# And describe the new room: + + +# [[file:../../../projects/mud.org::*George’s Workshop][George’s Workshop:3]] +@teleport/quiet mp21 +# +@desc here = Garish |Ygadgets|n and beguiling |Ygizmos|n girdle this room. The worktable contains a half-completed |Yproject|n while shelves showcase completed ones. The noise of machines overwhelms the smell of chemicals. +# George’s Workshop:3 ends here + + + +# And cover the exit: + + +# [[file:../../../projects/mud.org::*George’s Workshop][George’s Workshop:4]] +@desc door = A strong wooden door with iron bands. +# +@set door/traverse_msg = "You step the door, leaving the clamor behind." +# George’s Workshop:4 ends here + + +# Like other things through this world, looking at this should give us something new: + + +# [[file:../../../projects/mud.org::*Gadgets][Gadgets:1]] +@create/drop many gadgets;gadgets;gizmos;gadget;gizmo;contraptions: typeclasses.puzzles.Changling +# +@set gadgets/plural = True +# Gadgets:1 ends here + + + +# That we can’t take: + + +# [[file:../../../projects/mud.org::*Gadgets][Gadgets:2]] +@lock gadgets = get:false() +# +@set gadgets/get_err_msg = "You can look, but you can't take that." +# Gadgets:2 ends here + + + +# And we shouldn’t see it at first, as it is in the description: + + +# [[file:../../../projects/mud.org::*Gadgets][Gadgets:3]] +@lock gadgets = view:tag(hidden_gadgets) +# +@set gadgets/hidden_tag = "hidden_gadgets" +# Gadgets:3 ends here + + + +# The pre– and postfix descriptions: + + +# [[file:../../../projects/mud.org::*Gadgets][Gadgets:4]] +@set gadgets/desc_first_prefix = "<< Fascinating ^ Interesting ^ Curious >> << collection of ^ assortment of ^ >> << gadgets ^ gizmos ^ contraptions >>. You << look at ^ notice >> " +# +@set gadgets/desc_prefix = "You << spot ^ notice ^ look at >> another << gadget ^ gizmo ^ contraption ^ >>, " +# +@set gadgets/desc_postfix = "" +# Gadgets:4 ends here + + + +# And the gadgets themselves: + + +# [[file:../../../projects/mud.org::*Gadgets][Gadgets:5]] +@set gadgets/descs = ( + "a massive grandfather clock whose face is a stained-glass window; instead of hands, a tiny mechanical owl flies in circles, chasing a glowing brass mouse that moves erratically.", + "a delicate glass terrarium containing a dancing brass elephant. A perfectly detailed raincloud crackles with silent violet lightning and drops rain on the moss below.", + "a music box with an open lid, where a tiny brass skeleton and a porcelain dryad waltz together on a velvet stage. You never realized that the music, which you would call industrial rock, could be played in 3 / 4 time.", + "a heavy bronze astrolabe maps an impossible sky; the constellations etched onto its spinning plates are shaped like deep-sea creatures—leviathans with too many eyes, anglerfish with lanterns made of embedded pearls, and massive, coiled nautiluses.|/Most unsettling are the pointer arms. Instead of straight metal brass rulers, they are sculpted to look like segmented, reaching tentacles, that point toward blank, empty spaces on the plates where no stars are etched, as if tracking things that haven't arrived yet.", + "a mechanical chameleon forged from overlapping plates of iridescent tin; it sits motionless on a pile of scrap metal, its skin slowly rippling to mimic the exact texture and rust patterns of the junk beneath it.", + "a glass jar filled with hundreds of tiny, glowing mechanical fireflies swarming and swirling in hypnotic patterns. Did it just organize themselves into a sharp silhouette of a screaming face before scattering back into chaos?", + "a miniature brass carousel where the horses have been replaced by intricately carved mechanical grasshoppers. As the carousel spins, the grasshoppers themselves remain perfectly crisp and sharp to your vision, while the wooden platform beneath blurs into a seamless ring.", + "a heavy iron kettle sitting on the cold shelf, venting a continuous, thick stream of lavender-scented steam that shapes itself into the silhouettes of leaping stags before dissolving.", + "a small, intricate birdcage made of twisted silver wire, containing a single, flawlessly polished chrome sphere that hovers dead center, singing with the unmistakable, clear warble of a nightingale." +) +# Gadgets:5 ends here + + +# Let’s create a project that can be completed by the player. + +# This should contain a “state” that looks differently for each player based on an attribute on the player. + + +# [[file:../../../projects/mud.org::*The Project][The Project:1]] +@create/drop project: typeclasses.puzzles.Stateful +# +@desc project = On the workbench lies a tangle of brass plates, tubes and mechanical gears, shaped like a bird. Unassembled, it appears to be missing parts that might help it fly. +# The Project:1 ends here + + + +# Initial default state is unfinished: + + +# [[file:../../../projects/mud.org::*The Project][The Project:2]] +@set project/name_default = "unfinished project" +# +@set project/desc_default = "On the workbench lies a tangle of brass plates, tubes and mechanical gears, shaped like a bird. Unassembled, it appears to be missing parts that might help it fly." +# The Project:2 ends here + + + +# And this is the name and description when the project is finished: + + +# [[file:../../../projects/mud.org::*The Project][The Project:4]] +@set project/name_finished = "mechanical bird" +# +@set project/desc_finished = "This contraption made of brass plates and tubing sports an array of beautiful feathers, as it flies about." +# The Project:4 ends here + + +# This Dwarf will be in the dungeons, but I would like them to come out at times. + +# [[file:../../../projects/mud.org::*George the Dwarf Tinkerer][George the Dwarf Tinkerer:1]] +@create/drop George;dwarf: typeclasses.chatbots.Dwarf +# +@set dwarf/gender = "male" +# +@set dwarf/pose = "tinkering at an unfinished project" +# +@set dwarf/pose_default = "tinkering" +# +@set dwarf/pose_sleep = "snoozing" +# George the Dwarf Tinkerer:1 ends here + + + +# And his description: + + +# [[file:../../../projects/mud.org::*George the Dwarf Tinkerer][George the Dwarf Tinkerer:2]] +@desc dwarf = Spectacles and wild unkempt hair gives this dwarf a professor-y look. He braids his long beard into two strands, and tucks each of them into pockets in his brown apron, along with various tools that chime as he moves. +# George the Dwarf Tinkerer:2 ends here + + + +# Then give him the =sdesc= (not sure why I have to do both), and wake him. + + +# [[file:../../../projects/mud.org::*George the Dwarf Tinkerer][George the Dwarf Tinkerer:3]] +py dwarf = me.search("dwarf") ; dwarf.db._sdesc="white-haired dwarf" ; dwarf.sdesc.add("white-haired dwarf") ; dwarf.home = here; dwarf.backstory("dwarf") +# George the Dwarf Tinkerer:3 ends here + + + +# And get him moving: + + +# [[file:../../../projects/mud.org::*George the Dwarf Tinkerer][George the Dwarf Tinkerer:4]] +@script dwarf = typeclasses.chatbots.TravelingNPC +# George the Dwarf Tinkerer:4 ends here + + + +# But not so much: + + +# [[file:../../../projects/mud.org::*George the Dwarf Tinkerer][George the Dwarf Tinkerer:5]] +@set dwarf/travel_time = 60 +# +@set dwarf/stay_time = 900 +# +@set dwarf/home_time = 4000 +# George the Dwarf Tinkerer:5 ends here + + +# We hard-code this character to walk from the Mysterious Stone Circle (=mp00=) and sail across the sea, and back again. + + +# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:1]] +@tel/quiet mp00 +# +@create/drop Darol;hobbit: typeclasses.chatbots.Hobbit +# @update hobbit = typeclasses.puppets.Puppet +# +@set hobbit/gender = "him" +# Darol the Adventuring Hobbit:1 ends here + + + +# Description: + + +# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:2]] +@desc hobbit = An adventurous looking chap with brown mutton chops matching his curly brown hair and his brown cloak. His emerald green eyes match his coat that ends just above his large gold belt buckle, which in turn, matches his gold hoop earring. His backpack sports a pin with an insignia. +# Darol the Adventuring Hobbit:2 ends here + + + +# And the posing? + + +# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:3]] +@set hobbit/pose = "with his thumbs in his waistcoat pocket" +# +@set hobbit/pose_sleep = "absentmindedly smoking a pipe" +# +@set hobbit/pose_default = "with his thumbs in his waistcoat pocket" +# Darol the Adventuring Hobbit:3 ends here + + + +# Then wake it. + + +# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:4]] +py hobbit = me.search("hobbit") ; hobbit.sdesc.add("hobbit") ; hobbit.backstory("dragon") +# Darol the Adventuring Hobbit:4 ends here + + + +# Moving attributes: + + +# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:5]] +@set hobbit/traveling_direction = "back" +# Darol the Adventuring Hobbit:5 ends here + + + +# He needs his stuff, like a pipe: + + +# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:6]] +@create pipe: typeclasses.things.Pipe +# +@desc pipe = A large pipe worthy of a Hobbit. +# +@give pipe = hobbit +# Darol the Adventuring Hobbit:6 ends here + + + +# Need something to state he’s adventurous: + + +# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:7]] +@create short sword: typeclasses.things.Wand +# +@desc short sword = Beautiful gold filigree on the hilt and scabbard. +# +@give short sword = hobbit +# Darol the Adventuring Hobbit:7 ends here + + + +# And his fancy waistcoat: + + +# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:8]] +@create waistcoat : typeclasses.things.BagofJunk +# +@desc waistcoat = An elegant green waistcoat. +# +@set waistcoat/stuff = "fey" +# +@give waistcoat = hobbit +# Darol the Adventuring Hobbit:8 ends here + + + +# But mostly, he needs his horn: + + +# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:9]] +@create horn : typeclasses.sailing.CallingHorn +# +@desc horn = Made from the sea mist, it calls to you +# +@give horn = hobbit +# Darol the Adventuring Hobbit:9 ends here + + + +# And get it moving: + + +# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:10]] +@script hobbit = typeclasses.chatbots.TravelingNPC +# Darol the Adventuring Hobbit:10 ends here diff --git a/world/test-run.exp b/world/test-run.exp index 85f0aae..9d64157 100755 --- a/world/test-run.exp +++ b/world/test-run.exp @@ -496,6 +496,8 @@ expectit "You grab a candy" send "leave\n" expectit "Glittering Glade" +sleep 5 + # Let's get back to the Meadow ... send "west\n" expectit "Frog Meadow" @@ -510,7 +512,7 @@ send "look north\n" expectit "The meadow to the north looks easier to walk around." send "look birds\n" -expectit "Birds of every color on the rainbow and beyond fly and squawk around you" +expectit " ?" send "look moth\n" expectit "On closer inspection, the moths are really" @@ -561,6 +563,15 @@ expectit "heron" send "get heron\n" expectit "flies out of reach" +send "look feathers\n" +expectit "you find various colored feathers available" + +send "get feathers\n" +expectit "You gather a bunch of feathers" + +send "get feathers\n" +expectit "You seem to have plenty of feathers" + send "look reeds\n" expectit "Extremely tall, white reeds tipped with white gooey clumps." @@ -1093,10 +1104,56 @@ expectit "You sit on the log" send "copse\n" expectit "Bedroom" -send "close wardrobe\n" -expectit "The wardrobe door is closed." +send "down\n" +expectit "Cozy House" + +send "leave\n" +expectit "Grotto" + +send "east\n" +expectit "Grove of the Matriarchs" + +# First, we need to get to the lair: +send "east\n" +expectit "Frog Meadow" + +send "cave\n" +expectit "Lair" + +send "use pole on mattress\n" +expectit "open the trapdoor" send "down\n" +expectit "Dark Tunnel" + +send "look ladder\n" +expectit "A metal ladder" + +send "look blocks\n" +expectit "The stone blocks" + +send "door\n" +expectit "Workshop" + +send "look gadget\n" +expect -re "You (look|notice)" + +send "leave\n" +expectit "Tunnel" + +send "up\n" +expectit "Lair" + +send "leave\n" +expectit "Frog Meadow" + +send "west\n" +expectit "Grove of the Matriarchs" + +send "west\n" +expectit "Grotto" + +send "door\n" expectit "Cozy House" send "leave\n" @@ -1238,7 +1295,7 @@ expectit "Grove" send "south\n" expectit "Lazy Dock" -set timeout 120 +set timeout 240 send "blow horn\n" # expectit "You blow your horn" expectit "The giant leaf slows as it arrives next to the dock." diff --git a/world/version1.ev b/world/version1.ev index e33d44a..1218d59 100644 --- a/world/version1.ev +++ b/world/version1.ev @@ -60,7 +60,7 @@ pose looking awesome # [[file:../../../projects/mud.org::*The Grove of the Matriarchs][The Grove of the Matriarchs:5]] -@detail tree;trees = You feel dwarfed by colossal trees whose moss-covered roots are difficult to peer over. An occassional breeze often smells of flowers and at other times earthy. While the leafy canopy hides the sky, you certainly can |wfeel|n the weather. +@detail trees = You feel dwarfed by colossal trees whose moss-covered roots are difficult to peer over. An occassional breeze often smells of flowers and at other times earthy. While the leafy canopy hides the sky, you certainly can |wfeel|n the weather. Attribute it to pareidolia, but the bark on one particular |Ytree|n looks like a face. # @detail flower;flowers = Beautiful, but giant flowers look down on you and nod a friendly greeting. # @@ -71,7 +71,7 @@ pose looking awesome # When you first log into the game, you can immediately chat with an unfriendly, overly boring tree. # [[file:../../../projects/mud.org::*Barkbinder the Tree][Barkbinder the Tree:1]] -@create/drop tree;trees: typeclasses.chatbots.ChatBot +@create/drop tree: typeclasses.chatbots.ChatBot # @set tree/gender = "neutral" # @@ -90,6 +90,15 @@ py tree = me.search("tree") ; tree.sdesc.add("especially large tree") ; tree.bac # Barkbinder the Tree:2 ends here + +# And his description: + + +# [[file:../../../projects/mud.org::*Barkbinder the Tree][Barkbinder the Tree:3]] +@desc tree = The folds and furrows of the bark of this ancient tree look like a face with closed eyelids, as if it were asleep or merely contemplating the mysteries of deep time. +# Barkbinder the Tree:3 ends here + + # With my name, I need to have an object to go with it. @@ -509,29 +518,36 @@ py timed_script = evennia.create_script(key="Create Sticks", @create/drop Squirrel;monk: typeclasses.chatbots.ChatBot # @update squirrel = typeclasses.chatbots.ChatBot # -@desc squirrel = A handsome squirrel dressed in flowing orange robes. Sitting in quiet meditation on top of a compfortable patch of |Ymoss|n. -# @set squirrel/gender = "female" # @set squirrel/pose = "deep in meditation" # @set squirrel/pose_sleep = "deep in meditation" # -# @sdesc orange-robed squirrel +@set squirrel/pose_default = "deep in meditation" # Squirrel:1 ends here + + +# And describe the squirrel: + + # [[file:../../../projects/mud.org::*Squirrel][Squirrel:2]] -@set squirrel/arrive = "20 ;; emote briefly opens her eyes, nods an acknowledgement to you. ;; gm The small squirrel returns to her meditation." +@desc squirrel = A handsome squirrel dressed in flowing orange robes. Sitting in quiet meditation on top of a compfortable patch of |Ymoss|n. # Squirrel:2 ends here +# [[file:../../../projects/mud.org::*Squirrel][Squirrel:3]] +@set squirrel/arrive = "20 ;; emote briefly opens her eyes, nods an acknowledgement to you. ;; gm The small squirrel returns to her meditation." +# Squirrel:3 ends here + # And turn on the real sayings: -# [[file:../../../projects/mud.org::*Squirrel][Squirrel:4]] -py me.search("squirrel").backstory("squirrel") -# Squirrel:4 ends here +# [[file:../../../projects/mud.org::*Squirrel][Squirrel:5]] +py s = me.search("squirrel") ; s.db._sdesc="orange-robed squirrel" ; s.sdesc.add("orange-robed squirrel") ; s.home = here ; s.backstory("squirrel") +# Squirrel:5 ends here # To the east, let’s make a nice meadow. Start at the Forest: @@ -1441,69 +1457,63 @@ py me.search("squirrel").backstory("squirrel") # [[file:../../../projects/mud.org::*Bartender][Bartender:1]] @create/drop Bartender;barkeep;Elendil: typeclasses.chatbots.Bartender -# @update Bartender = typeclasses.chatbots.Bartender +# +@set bartender/gender = "male" # Bartender:1 ends here - - -# And all the RP system stuff: - - # [[file:../../../projects/mud.org::*Bartender][Bartender:2]] -@set bartender/gender = "male" +py bt = self.search('Bartender') ; bt.db._sdesc="blonde elf bartender" ; bt.sdesc.add('blonde elf bartender') ; bt.home = here ; bt.db.pose = 'working behind the bar' # Bartender:2 ends here -# [[file:../../../projects/mud.org::*Bartender][Bartender:3]] -py bt = self.search('Bartender'); bt.sdesc.add('blonde elf'); bt.db.pose = 'working behind the bar' -# Bartender:3 ends here - # And default pose: -# [[file:../../../projects/mud.org::*Bartender][Bartender:4]] +# [[file:../../../projects/mud.org::*Bartender][Bartender:3]] @pose default bartender = working behind the bar -# Bartender:4 ends here +# Bartender:3 ends here # And an un-puppeted pose: -# [[file:../../../projects/mud.org::*Bartender][Bartender:5]] +# [[file:../../../projects/mud.org::*Bartender][Bartender:4]] @set bartender/pose_sleep = "fiddling with something behind the bar" # @set bartender/pose_default = "working behind the bar" -# Bartender:5 ends here +# +@set bartender/pose_default = "wiping down the bar" +# Bartender:4 ends here # And a good description: -# [[file:../../../projects/mud.org::*Bartender][Bartender:6]] +# [[file:../../../projects/mud.org::*Bartender][Bartender:5]] @desc Bartender = A haughty-looking elf with green eyes and long blond hair with two intricate braids accentuating incredibly pointed ears. His nose, pointed, often points straight up in order to look down on you. -# Bartender:6 ends here +# Bartender:5 ends here # As well as the automation: -# [[file:../../../projects/mud.org::*Bartender][Bartender:8]] +# [[file:../../../projects/mud.org::*Bartender][Bartender:7]] py me.search("bartender").backstory("tavern") -# Bartender:8 ends here +# Bartender:7 ends here # And let’s make him initially invisible: -# [[file:../../../projects/mud.org::*Bartender][Bartender:9]] +# [[file:../../../projects/mud.org::*Bartender][Bartender:8]] @set Bartender/hidden_tag = "hidden_bartender" # @lock Bartender = view:tag(hidden_bartender) -# Bartender:9 ends here +# Bartender:8 ends here # Seems that different triggering hooks could have different delayed responses. What sort of events? And do we bother if we convert the Bartender to a Chatbot? @@ -1871,7 +1881,7 @@ pose default shrub = writing something in a notebook # Mellow Marsh:8 ends here # [[file:../../../projects/mud.org::*Mellow Marsh][Mellow Marsh:9]] -@detail birds = Birds of every color on the rainbow and beyond fly and squawk around you, but keep their distance. +@detail birds = Birds of every color on the rainbow and beyond, fly and squawk around you, but keep their distance. # Mellow Marsh:9 ends here # [[file:../../../projects/mud.org::*Mellow Marsh][Mellow Marsh:10]] @@ -1925,8 +1935,7 @@ pose default shrub = writing something in a notebook # [[file:../../../projects/mud.org::*Purple Heron][Purple Heron:1]] -@create/drop purple heron: typeclasses.chatbots.ChatBot -# @update heron = typeclasses.chatbots.ChatBot +@create/drop purple heron;heron: typeclasses.chatbots.ChatBot # @set heron/pose = "stalking the swamp grass" # @@ -1934,9 +1943,9 @@ pose default shrub = writing something in a notebook # @set heron/pose_sleeping = "stalking the swamp grass" # -@lock purple heron = get:false() +@lock heron = get:false() # -@set purple heron/get_err_msg = "You try to get it, but it just flies out of reach." +@set heron/get_err_msg = "You try to get it, but it just flies out of reach." # Purple Heron:1 ends here @@ -2071,6 +2080,15 @@ py me.search("heron").backstory("heron") +# A ten-foot pole should act like a stick: + + +# [[file:../../../projects/mud.org::*Ten-foot Poles][Ten-foot Poles:7]] +@set reeds/make_class = "typeclasses.things.Stick" +# Ten-foot Poles:7 ends here + + + # And the reeds needs to know the /description/ of the Consumable, so that it can attach that: # [[file:../../../projects/mud.org::*Ten-foot Poles][Ten-foot Poles:8]] @@ -2495,20 +2513,6 @@ py me.search("heron").backstory("heron") # Reed Sculpture:1 ends here - -# The primary treasure is the horn. Anyone can acquire it, and keep it as a way to control the Leaf Boat. We generate them every 4 hours: - - -# [[file:../../../projects/mud.org::*Reed Sculpture][Reed Sculpture:3]] -py timed_script = evennia.create_script(key="Create Horns", - typeclass='typeclasses.scripts.CreateHorns', - interval=600, - start_delay=False, - autostart=True, - attributes=[("destination", here)] ) -# Reed Sculpture:3 ends here - - # The reed can be a [[file:~/src/moss-n-puddles/typeclasses/consumables.py::class Producer(Object):][producer]] that makes ten foot poles … needed to get into the hut. @@ -2554,28 +2558,40 @@ py timed_script = evennia.create_script(key="Create Horns", -# The torches are special in that they last longer. +# How many torches can they steal? + # [[file:../../../projects/mud.org::*Torches][Torches:6]] -@set bucket/make_class = "typeclasses.lightables.Torch" +@set bucket/make_limit = 5 +# +@set bucket/make_limit_msg = "You can't carry any more torches." # Torches:6 ends here + +# The torches are special in that they last longer. + +# [[file:../../../projects/mud.org::*Torches][Torches:7]] +@set bucket/make_class = "typeclasses.lightables.Torch" +# Torches:7 ends here + + + # And the bucket needs to know the /description/ of the Consumable, so that it can attach that: -# [[file:../../../projects/mud.org::*Torches][Torches:7]] +# [[file:../../../projects/mud.org::*Torches][Torches:8]] @set bucket/make_desc = "Made from marsh grass and reeds." -# Torches:7 ends here +# Torches:8 ends here # How much is there when you pick them? -# [[file:../../../projects/mud.org::*Torches][Torches:8]] +# [[file:../../../projects/mud.org::*Torches][Torches:9]] @set bucket/make_amount = 1 -# Torches:8 ends here +# Torches:9 ends here # Create a puppet of the lady that owns the hut. @@ -2583,7 +2599,6 @@ py timed_script = evennia.create_script(key="Create Horns", # [[file:../../../projects/mud.org::*Trampoli the Witch][Trampoli the Witch:1]] @create/drop Trampoli;old lady: typeclasses.chatbots.Witch -# @update Trampoli = typeclasses.chatbots.Witch # Trampoli the Witch:1 ends here @@ -2601,7 +2616,7 @@ py timed_script = evennia.create_script(key="Create Horns", # [[file:../../../projects/mud.org::*Trampoli the Witch][Trampoli the Witch:3]] -@py bt = self.search('old lady'); bt.sdesc.add('old lady'); bt.db.pose = 'playing with a deck of cards' +@py bt = self.search('old lady'); bt.db._sdesc="old lady" ; bt.sdesc.add('old lady'); bt.home = here ; bt.db.pose = 'playing with a deck of cards' # Trampoli the Witch:3 ends here @@ -2654,15 +2669,15 @@ py timed_script = evennia.create_script(key="Create Horns", # Trampoli the Witch:11 ends here -# The [[file:~/src/moss-n-puddles/typeclasses/scripts.py::class CreateHorns(Script):][CreateHorns script]] will generate a horn. +# The [[file:~/src/moss-n-puddles/typeclasses/scripts.py::class CreateHorns(Script):][CreateHorns script]] will generate a horn. Anyone can acquire it, and keep it as a way to control the Leaf Boat. # [[file:../../../projects/mud.org::*Mist Horn][Mist Horn:1]] -py timed_script = evennia.create_script( +py evennia.create_script( key="Create Horns", typeclass='typeclasses.scripts.CreateHorns', interval=60 * 10, - start_delay=True, + start_delay=False, autostart=True, attributes=[("destination", here)] ) @@ -2877,8 +2892,6 @@ Someone has set a nice |Ychair|n for viewing. # [[file:../../../projects/mud.org::*Raven][Raven:1]] @create/drop Raven;bird: typeclasses.chatbots.ChatBot # -# @update raven = typeclasses.chatbots.ChatBot -# @desc raven = A large raven with curious black eyes. # @set raven/gender = "neutral" @@ -2888,8 +2901,6 @@ Someone has set a nice |Ychair|n for viewing. @set raven/pose_sleep = "dozing while perched on a branch" # @set raven/pose_default = "curiously staring at you" -# -# @sdesc large raven # Raven:1 ends here @@ -2897,9 +2908,9 @@ Someone has set a nice |Ychair|n for viewing. # Let’s load the goodies: -# [[file:../../../projects/mud.org::*Raven][Raven:2]] +# [[file:../../../projects/mud.org::*Raven][Raven:3]] py me.search("raven").backstory("raven") -# Raven:2 ends here +# Raven:3 ends here # Could we extend the sea from the [[file:mud.org::*The Dock][The Dock]] down a shore: @@ -2913,57 +2924,57 @@ py me.search("raven").backstory("raven") -# With a message about leaving the trees so that I don’t have to repeat that in the room description: +# With a message about leaving the dock as to not repeat that in the room description: -# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:3]] +# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:2]] @set south/traverse_msg = "Leaving the dock, you want along the soft sandy shore next to the Lavender Sea, enjoying the mesmerizing sound of the surf... until you come to a shack that blocks your stroll." -# Sandy Shore:3 ends here +# Sandy Shore:2 ends here # And a description: -# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:4]] +# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:3]] @desc south = You see a shack down along the sandy shore. -# Sandy Shore:4 ends here +# Sandy Shore:3 ends here # And move ourselves there: -# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:5]] +# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:4]] @teleport mp15 -# Sandy Shore:5 ends here +# Sandy Shore:4 ends here # And describe the walk back to the dock: -# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:6]] +# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:5]] @set north/traverse_msg = "You walk along the shore of the lavender sea back to the dock." -# Sandy Shore:6 ends here +# Sandy Shore:5 ends here # And a description: -# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:7]] +# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:6]] @desc north = The sandy shore to the north ends at a dock, jutting into the lavender sea. -# Sandy Shore:7 ends here +# Sandy Shore:6 ends here # And describe this. -# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:8]] -@desc here = Puppy-dog |Ywaves|n on a lavender |Ysea|n, snap your heels half-heartedly. Walking hard steps, punching crusted |Ysand|n, difficulty crossing land. Robust |Yshack|n squatting below a |Ypine|n, locked door holding painted |Ysign|n. -# Sandy Shore:8 ends here +# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:7]] +@desc here = Puppy-dog |Ywaves|n on a lavender |Ysea|n, snap your heels half-heartedly. The loose |Ysand|n makes for difficult walking. A robust |Yshack|n squats below a |Ypine|n covered in yellow |Yflowers|n. It sports a brightly painted |Ysign|n. +# Sandy Shore:7 ends here # And details? -# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:9]] +# [[file:../../../projects/mud.org::*Sandy Shore][Sandy Shore:8]] @detail waves = Despite the inclement weather, the waves ripple gently against the shore. # @detail sea;lavender sea = Is that a |Yboat|n you see sailing in the distance? @@ -2971,7 +2982,7 @@ py me.search("raven").backstory("raven") @detail boat;ship = The mastless ship looks like a giant leaf. Wonder if it comes to shore? # @detail shack = Standing crookedly at the edge of the sandy shore, this shack's weathered planks, bleached by the salty mist, imparts a ghostly appearance. The roof, adorned with seashells and driftwood, appears to be a patchwork of nature's treasures, inviting curious souls to uncover the secrets hidden within. -# Sandy Shore:9 ends here +# Sandy Shore:8 ends here # Part of the [[Potions and their Ingredients][Alchemist Path]], we use sand as an ingredient in a potion we want to make. @@ -3894,21 +3905,6 @@ pose gnome/default = smoking his pipe -# And we create a couple of objects: - -# Like his special teacup: - - -# [[file:../../../projects/mud.org::*Character: Dabbler][Character: Dabbler:9]] -@create teacup;cup : typeclasses.drinkables.TeaCup -# -@desc teacup = A rustic teacup crafted from clay etched with leaves and runes, with an olive green and brown glaze. -# -@give teacup = gnome -# Character: Dabbler:9 ends here - - - # And his fancy jacket: @@ -5060,10 +5056,12 @@ py bt = self.search('head'); bt.sdesc.add('curious figure'); bt.db.pose = 'sitti @desc ladder = Through the open trapdoor, you see a ladder that you can climb down into a cold, damp darkness. # @set ladder/traverse_msg = "With the trapdoor open, you climb down the ladder into the darkness... Suddenly, your foot slips, and you fall, landing on the stone floor!" -# -# @set here/open_exit = $search(ladder down into the dark) -# -# @set here/open_item = "trapdoor" + +# Since I have two "Lair" (by label anyway), the system converts the +# following to a string: +# @set ladder/room = $search(Lair) +# So we need to do the following: +@set ladder/room = $search(mp07) # @teleport/tonone ladder down into the dark # Dark Tunnel:1 ends here @@ -5100,12 +5098,13 @@ py bt = self.search('head'); bt.sdesc.add('curious figure'); bt.db.pose = 'sitti # Trapdoor should stay open for 5 minutes: @set trapdoor/open_for = 300 # +# @set trapdoor/room = $search(Lair) +@set trapdoor/room = $search(mp07) +# @set trapdoor/exit = $search(ladder down into the dark) # @set trapdoor/exit_close_msg = "The trapdoor slams shut from above. Uh oh." # -@set trapdoor/room = $search(Lair) -# @set trapdoor/room_close_msg = "The stick breaks, and the trapdoor slams shut from the weight of the mattress." # Dark Tunnel:3 ends here @@ -5114,19 +5113,17 @@ py bt = self.search('head'); bt.sdesc.add('curious figure'); bt.db.pose = 'sitti # Let’s get into the Dungeon and work from it there. -# [[file:../../../projects/mud.org::*Dark Tunnel][Dark Tunnel:4]] +# [[file:../../../projects/mud.org::*Dark Tunnel][Dark Tunnel:5]] @teleport/quiet mp20 # @desc here = A cold, musty tunnel made of |Ystone blocks|n, tapered to form an large dome overhead. You hear a slight whistling sound, which may explain why this unground place is neither dusty or musty. # -@desc ladder = A metal ladder where a slimy mildew of sorts has grown on the bottom rungs. Better be careful. +@desc ladder = A metal ladder leads to a trapdoor. A slimy mildew of sorts has grown on the bottom rungs. Better be careful. # @set ladder/traverse_msg = "You carefully ascend up the ladder, but you find the trapdoor hard to open, as if something heavy is on top of it. With all your might, you manage to push and squeeze through it." # -@detail trapdoor = A wood door with reinforced iron bands on the floor. -# @detail stone blocks;stone;blocks = The stone blocks that support the tunnel are well made, and for the most part, keep out water. But the crevises seem mortared with a green mildew. -# Dark Tunnel:4 ends here +# Dark Tunnel:5 ends here # The deep forest is a place where all the Puppets can hang out when not in use. @@ -5179,86 +5176,52 @@ py bt = self.search('head'); bt.sdesc.add('curious figure'); bt.db.pose = 'sitti # [[file:../../../projects/mud.org::*Sir Roblees the Flamboyant Dragon][Sir Roblees the Flamboyant Dragon:1]] -@create/drop Sir Roblees: typeclasses.chatbots.Dragon +@create/drop Sir Roblees;dragon,fey dragon: typeclasses.chatbots.Dragon # -@set Sir Roblees/gender = "him" +@set dragon/gender = "male" # -@set Sir Roblees/pose = "twirling a tendril like a mustache" +@set dragon/pose = "twirling a tendril like a mustache" # -@set Sir Roblees/pose_sleep = "twirling a tendril like a mustache" +@set dragon/pose_sleep = "twirling a tendril like a mustache" # -@set Sir Roblees/pose_default = "twirling a tendril like a mustache" +@set dragon/pose_default = "twirling a tendril like a mustache" # Sir Roblees the Flamboyant Dragon:1 ends here -# Then wake it. +# And his description: # [[file:../../../projects/mud.org::*Sir Roblees the Flamboyant Dragon][Sir Roblees the Flamboyant Dragon:2]] -py Sir Roblees = me.search("Sir Roblees") ; Sir Roblees.sdesc.add("tiny, orange dragon") ; Sir Roblees.backstory("dragon") +@desc dragon = A dragon with orange-spotted wings like a butterfly. He makes up for his tiny size with machismo. He wears a white plumed hat and a fancy dagger like a sword. He sports a sharp-toothed grin. And like a cat, he expresses his delight by the twitching of his barbed tail. # Sir Roblees the Flamboyant Dragon:2 ends here -# Moving attributes: +# Then wake it. # [[file:../../../projects/mud.org::*Sir Roblees the Flamboyant Dragon][Sir Roblees the Flamboyant Dragon:3]] -@set Sir Roblees/traveling_direction = "east" +py dragon = me.search("dragon") ; dragon.db._sdesc="orange fey dragon" ; dragon.sdesc.add("orange fey dragon"); dragon.home = here ; dragon.backstory("dragon") # Sir Roblees the Flamboyant Dragon:3 ends here -# And get it moving: - - -# [[file:../../../projects/mud.org::*Sir Roblees the Flamboyant Dragon][Sir Roblees the Flamboyant Dragon:4]] -@script Sir Roblees = typeclasses.chatbots.TravelingNPC -# Sir Roblees the Flamboyant Dragon:4 ends here - - -# We hard-code this character to walk from the Mysterious Stone Circle (=mp00=) and sail across the sea, and back again. - - -# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:1]] -@create/drop Darol: typeclasses.chatbots.Hobbit -# -@set Darol/gender = "him" -# -@set Darol/pose = "twirling a tendril like a mustache" -# -@set Darol/pose_sleep = "twirling a tendril like a mustache" -# -@set Darol/pose_default = "twirling a tendril like a mustache" -# Darol the Adventuring Hobbit:1 ends here - - - -# Then wake it. - - -# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:2]] -py Darol = me.search("Darol") ; Darol.sdesc.add("tiny, orange dragon") ; Darol.backstory("dragon") -# Darol the Adventuring Hobbit:2 ends here - - - # Moving attributes: -# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:3]] -@set Darol/traveling_direction = "east" -# Darol the Adventuring Hobbit:3 ends here +# [[file:../../../projects/mud.org::*Sir Roblees the Flamboyant Dragon][Sir Roblees the Flamboyant Dragon:4]] +@set dragon/traveling_direction = "east" +# Sir Roblees the Flamboyant Dragon:4 ends here # And get it moving: -# [[file:../../../projects/mud.org::*Darol the Adventuring Hobbit][Darol the Adventuring Hobbit:4]] -@script Darol = typeclasses.chatbots.TravelingNPC -# Darol the Adventuring Hobbit:4 ends here +# [[file:../../../projects/mud.org::*Sir Roblees the Flamboyant Dragon][Sir Roblees the Flamboyant Dragon:5]] +@script dragon = typeclasses.chatbots.TravelingNPC +# Sir Roblees the Flamboyant Dragon:5 ends here