Create a Janitor to clean rooms if too much debris appears

This commit is contained in:
Howard Abrams 2026-10-15 00:01:08 -07:00
parent d2fda42a32
commit 503dd0301a
7 changed files with 1813 additions and 133 deletions

View file

@ -288,10 +288,7 @@ class CmdGM(MuxCommand):
msg = routput(self.args) msg = routput(self.args)
for o in send_to: for o in send_to:
if o.is_typeclass('typeclasses.rooms.Room'): if o.is_typeclass('typeclasses.rooms.Room'):
# Send the message to all characters and any recording o.msg_contents(msg)
# scribes in attendance:
chars = o.contents_get(None, 'character') + [o.has(Scribe)]
send_emote(me, chars, msg, 'say', None)
elif o.is_typeclass('typeclasses.characters.Character'): elif o.is_typeclass('typeclasses.characters.Character'):
o.msg(msg) o.msg(msg)

View file

@ -10,24 +10,11 @@
#+PROPERTY: header-args:yaml :tangle ~/src/moss-n-puddles/world/hints.yaml #+PROPERTY: header-args:yaml :tangle ~/src/moss-n-puddles/world/hints.yaml
#+PROPERTY: header-args :results none :eval no-export :comments no #+PROPERTY: header-args :results none :eval no-export :comments no
- [ ] The Troll’s gambling den … part of the bar? under the bridge?
- [X] Feed fire and =start fire=a
- [X] Create *Damp Cave* and stream of smelly water
- Create the “Goblin” and its puppets
- Make a rock to sit on. Let’s make it comfortable.
- Clone the teapot? The =make= should not be so extensive … more unique
- Clone the teacup generation? But this time, cracked ones that don’t hold much tea?
- Create the character description file.
- [ ] Create goggles to show /gold tint/, and remove the /tint/ otherwise - [ ] Create goggles to show /gold tint/, and remove the /tint/ otherwise
- [ ] Create /Picker/, the goblin janitor that sweeps through land, deleting all items that /should not/ be there.
- Should I have a YAML file that lists what /should/ be in each room?
- Would the file also show the order and what rooms to follow?
- Perhaps he moves it to tunnels under the Lair?
- [ ] Create /Peddler/, the goblin, and his Emporium and Home Furnishings - [ ] Create /Peddler/, the goblin, and his Emporium and Home Furnishings
- A new =buy= command that exchanges money for an item. - A new =buy= command that exchanges money for an item.
- Need a sign with the items and money … come from a YAML file? - Need a sign with the items and money … come from a YAML file?
- [ ] The Troll’s gambling den … part of the bar? under the bridge?
* Evennia Server * Evennia Server
** Background ** Background
While I believe I could use any MUD server, I’m trying out [[https://www.evennia.com/][Evennia]]. Based on Python, but I won’t hold that against it. Evennia comes up fast, has great features, and good documentation. Python-based projects inspires people, I guess. This file started out as my /notes/ about the process, but grew. Started with writing the Evennia commands I used to create my world (wasn’t sure if when I needed to wipe the database), but realized I could /tangle/ these commands (in org blocks) to an =.ev= file that I could load with the =batchcommands= command. I organized this file mostly around the “rooms” in the game, with “objects” nested inside. Next, I added YAML snippets to create the hint file (since the hints are based on the room location). Then, I included [[Testing the World Experience][Expect/Tcl code]] to have a /test character/ walk through the game testing the features I coded. While I believe I could use any MUD server, I’m trying out [[https://www.evennia.com/][Evennia]]. Based on Python, but I won’t hold that against it. Evennia comes up fast, has great features, and good documentation. Python-based projects inspires people, I guess. This file started out as my /notes/ about the process, but grew. Started with writing the Evennia commands I used to create my world (wasn’t sure if when I needed to wipe the database), but realized I could /tangle/ these commands (in org blocks) to an =.ev= file that I could load with the =batchcommands= command. I organized this file mostly around the “rooms” in the game, with “objects” nested inside. Next, I added YAML snippets to create the hint file (since the hints are based on the room location). Then, I included [[Testing the World Experience][Expect/Tcl code]] to have a /test character/ walk through the game testing the features I coded.
@ -557,7 +544,7 @@ When you first log into the game, you can immediately chat with an unfriendly, o
Then wake it. Then wake it.
#+BEGIN_SRC evennia #+BEGIN_SRC evennia
py tree = me.search("tree") ; tree.sdesc.add("especially large tree") ; tree.backstory("tree") py tree = me.search("tree") ; tree.home=here; tree.sdesc.add("especially large tree") ; tree.backstory("tree")
#+END_SRC #+END_SRC
And his description: And his description:
@ -1696,6 +1683,12 @@ It will wake at 8am …
@set beast/wake_hour = 8 @set beast/wake_hour = 8
#+END_SRC #+END_SRC
Unlike most other creatures, the beast’s circadian rhythm is determined by the internal TICKER that is fully programmed in Python. I noticed that some times, it gets /turned off/, so you might have to do this:
#+BEGIN_SRC evennia :tangle no
@py from evennia import TICKER_HANDLER ; TICKER_HANDLER.add(interval=60, callback=me.search("beast").update_state)
#+END_SRC
Let’s set some behavior levels. For instance, the beast is sad when he doesn’t see its friends: Let’s set some behavior levels. For instance, the beast is sad when he doesn’t see its friends:
#+BEGIN_src evennia #+BEGIN_src evennia
@ -1799,7 +1792,7 @@ And the responses to being /pet/:
@set beast/pet_ecstatic_response = "While << excited ^ ecstatic ^ squirmy >>, the << big, ^ huge, ^ large, ^ tremendous, ^ >> << hairy ^ slobbery ^ horned ^ clawed ^ >> << brute ^ beast ^ monster>> << lays ^ gets >> down so $you() can << pet ^ scratch >> << the top of its head ^ the back of its neck ^ its nose ^ the sides of its face >>. It closes its eyes and purrs. ;; << The squirmy little devil. ^ >> $conj(You) << can ^ >> << barely ^ hardly >> scratch the << nose ^ ears ^ neck ^ top of the head >> of the << big, ^ huge, ^ large, ^ tremendous, ^ >> << hairy ^ slobbery ^ horned ^ clawed ^ >> << brute ^ beast ^ monster>> as it << squirms around ^ wriggles it backend >>. It << can hardly ^ can't >> contain its excitement. ;; The << big, ^ huge, ^ large, ^ tremendous, ^ >> << hairy ^ slobbery ^ horned ^ clawed ^ >> << brute ^ beast ^ monster>> wriggles around before << flopping ^ rolling >> over << in the grass ^>> as $you() $conj(give) it belly rubs. ;; The << big, ^ huge, ^ large, ^ tremendous, ^ >> << hairy ^ slobbery ^ horned ^ clawed ^ >> << brute ^ beast ^ monster>> wriggles and squirms excitedly before it << flops ^ rolls >> over << in the grass ^>> as $you() $conj(rub) its belly." @set beast/pet_ecstatic_response = "While << excited ^ ecstatic ^ squirmy >>, the << big, ^ huge, ^ large, ^ tremendous, ^ >> << hairy ^ slobbery ^ horned ^ clawed ^ >> << brute ^ beast ^ monster>> << lays ^ gets >> down so $you() can << pet ^ scratch >> << the top of its head ^ the back of its neck ^ its nose ^ the sides of its face >>. It closes its eyes and purrs. ;; << The squirmy little devil. ^ >> $conj(You) << can ^ >> << barely ^ hardly >> scratch the << nose ^ ears ^ neck ^ top of the head >> of the << big, ^ huge, ^ large, ^ tremendous, ^ >> << hairy ^ slobbery ^ horned ^ clawed ^ >> << brute ^ beast ^ monster>> as it << squirms around ^ wriggles it backend >>. It << can hardly ^ can't >> contain its excitement. ;; The << big, ^ huge, ^ large, ^ tremendous, ^ >> << hairy ^ slobbery ^ horned ^ clawed ^ >> << brute ^ beast ^ monster>> wriggles around before << flopping ^ rolling >> over << in the grass ^>> as $you() $conj(give) it belly rubs. ;; The << big, ^ huge, ^ large, ^ tremendous, ^ >> << hairy ^ slobbery ^ horned ^ clawed ^ >> << brute ^ beast ^ monster>> wriggles and squirms excitedly before it << flops ^ rolls >> over << in the grass ^>> as $you() $conj(rub) its belly."
#+END_SRC #+END_SRC
I want to make sure that when someone new sees the beast, the see it hiding behind the grass… I want to make sure that when someone new sees the beast, they see it hiding behind the grass…
#+BEGIN_SRC tcl #+BEGIN_SRC tcl
send "leave\n" send "leave\n"
@ -4044,6 +4037,25 @@ Test if it is readable:
expectit "This granny knot" expectit "This granny knot"
#+END_SRC #+END_SRC
*** Pine Tree
Part of the [[Potions and their Ingredients][Alchemist Path]], we use /pine flowers/ to create a potion TBD, and feed the stoat in Dabbler’s House.
#+BEGIN_SRC evennia
@create/drop pine tree;pine;tree;flowers;flower : typeclasses.consumables.Producer
#
@desc pine = Windswept pine leans to shade the dock, protecting it somewhat from the regular occuring rain. Interestingly, the tree has beautiful, yellow |Yflowers|n.
#
@set pine/make_class = "typeclasses.consumables.Herb"
#
@set pine/make_name = "yellow flower"
#
@set pine/make_verb = "$conj(pick) a small"
#
@set pine/make_desc = "Briny smelling flower with purple spots."
#
@lock pine = view:tag(alchemist) # We want the user to be able to "look pine" as a detail?
#+END_SRC
*** Raven *** Raven
Here for hints … and a bit of a distraction. Here for hints … and a bit of a distraction.
@ -4262,7 +4274,7 @@ And testing:
Part of the [[Potions and their Ingredients][Alchemist Path]], we use /pine flowers/ to create a potion TBD, and feed the stoat in Dabbler’s House. Part of the [[Potions and their Ingredients][Alchemist Path]], we use /pine flowers/ to create a potion TBD, and feed the stoat in Dabbler’s House.
#+BEGIN_SRC evennia #+BEGIN_SRC evennia
@create/drop pine tree;pine;tree;flowers;flower = typeclasses.consumables.Producer @create/drop pine tree;pine;tree;flowers;flower : typeclasses.consumables.Producer
# #
@desc pine = Windswept pine leans to shade the shack, protecting it somewhat from the regular occuring rain. Interesting, that the pine has beautiful, yellow |Yflowers|n. @desc pine = Windswept pine leans to shade the shack, protecting it somewhat from the regular occuring rain. Interesting, that the pine has beautiful, yellow |Yflowers|n.
# #
@ -5046,6 +5058,7 @@ With a good description:
@desc books = Shelves at various angles embellish the walls of this small, cozy room. Leatherbound books weigh down each shelf, while some stacks of books support other shelves. @desc books = Shelves at various angles embellish the walls of this small, cozy room. Leatherbound books weigh down each shelf, while some stacks of books support other shelves.
#+end_src #+end_src
And make sure they can’t
Most of the work is actually in the creation of the [[file:~/src/moss-n-puddles/typeclasses/readables.py::class Book(Object):][individual books]], in the Python code in the [[file:~/src/moss-n-puddles/typeclasses/readables.py::class Books(Object):][Books]] class. Most of the work is actually in the creation of the [[file:~/src/moss-n-puddles/typeclasses/readables.py::class Book(Object):][individual books]], in the Python code in the [[file:~/src/moss-n-puddles/typeclasses/readables.py::class Books(Object):][Books]] class.
Can we do the song and dance of getting a book? Should we simplify this? Can we do the song and dance of getting a book? Should we simplify this?
@ -5134,6 +5147,8 @@ Let’s make it hidden for the moment:
And let’s not let anyone pick it up: And let’s not let anyone pick it up:
#+BEGIN_SRC evennia #+BEGIN_SRC evennia
@lock butterfly = get:false()
#
@set butterfly/get_err_msg = "It quickly flutters away, only to land a few feet away." @set butterfly/get_err_msg = "It quickly flutters away, only to land a few feet away."
#+END_SRC #+END_SRC
@ -5221,19 +5236,19 @@ And give him the powers he deserves:
#+BEGIN_src evennia #+BEGIN_src evennia
@set gnome/pose_sleep = "sleeping soundly in a large, overstuffed chair" @set gnome/pose_sleep = "sleeping soundly in a large, overstuffed chair"
# #
pose gnome/default = smoking his pipe @set gnome/pose_default = "smoking his pipe"
#+END_SRC #+END_SRC
And a good description that I can rework: And a good description that I can rework:
#+BEGIN_src evennia #+BEGIN_src evennia
@desc gnome = A small, hunched old man with messy blue hair, a gray vandyke and an eye twinkle.|/|/Spectacles perched precariously on the end of his hooked nose, wobble with his head. His bright crimson jacket contrasts with his dark brown cloak. @desc gnome = A small, hunched old man with messy white hair, a gray vandyke and an eye twinkle.|/|/Spectacles perched precariously on the end of his hooked nose, wobble with his head. His bright crimson jacket contrasts with his dark brown cloak.
#+end_src #+end_src
And an un-puppeted, sleeping, description: And an un-puppeted, sleeping, description:
#+BEGIN_src evennia #+BEGIN_src evennia
@set gnome/desc_unpuppeted = "A small, hunched old man with messy blue hair and a gray vandyke snoring soundly in one of the overstuffed chairs.|/|/Spectacles perched precariously on the end of his hooked nose, wobble with each breath. His bright crimson jacket contrasts with the dark brown blanket covering his legs." @set gnome/desc_unpuppeted = "A small, hunched old man with messy white hair and a gray vandyke snoring soundly in one of the overstuffed chairs.|/|/Spectacles perched precariously on the end of his hooked nose, wobble with each breath. His bright crimson jacket contrasts with the dark brown blanket covering his legs."
#+END_SRC #+END_SRC
And we create a couple of objects: And we create a couple of objects:
@ -5327,7 +5342,7 @@ And a description:
The pipe is little more than messages to the smoker and everyone else in the room. The pipe is little more than messages to the smoker and everyone else in the room.
#+BEGIN_src evennia #+BEGIN_src evennia
@create pipe: typeclasses.things.Pipe @create carved pipe: typeclasses.things.Pipe
#+END_SRC #+END_SRC
And a description: And a description:
@ -6937,6 +6952,14 @@ This should contain a “state” that looks differently for each player based o
@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. @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.
#+END_SRC #+END_SRC
The project shouldn’t be stolen …
#+BEGIN_SRC evennia
@lock project = get:false()
#
@set project/get_err_msg = "You shouldn't steal George's project. Perhaps you can help him with it."
#+END_SRC
Initial default state is unfinished: Initial default state is unfinished:
#+BEGIN_SRC evennia #+BEGIN_SRC evennia
@ -7407,7 +7430,7 @@ Character 3: Twiggy, an automaton or golem made from sticks. When he drinks tea
#+END_SRC #+END_SRC
Get the hell out of dodge: Get the hell out of dodge and back to the Grove:
#+BEGIN_SRC tcl #+BEGIN_SRC tcl
send "leave\n" send "leave\n"
expectit "Dark Tunnel" expectit "Dark Tunnel"
@ -7420,12 +7443,6 @@ Get the hell out of dodge:
send "west\n" send "west\n"
expectit "Grove of the Matriarchs" expectit "Grove of the Matriarchs"
send "west\n"
expectit "Grotto"
send "door\n"
expectit "Cozy House"
#+END_SRC #+END_SRC
** Deep Forest ** Deep Forest
The deep forest is a place where all the Puppets can hang out when not in use. The deep forest is a place where all the Puppets can hang out when not in use.
@ -7741,7 +7758,6 @@ And what he knows:
You know Dabbler, the gnome who claims to have made this land. He makes clever objects, and even gave you a silver ring. You know Dabbler, the gnome who claims to have made this land. He makes clever objects, and even gave you a silver ring.
You know there isn't a lot of action about here, so you need to go to the Bar for a drink. The elf bartender is aloof, but he makes good drinks. You know there isn't a lot of action about here, so you need to go to the Bar for a drink. The elf bartender is aloof, but he makes good drinks.
#+END_SRC #+END_SRC
Finally, let’s awaken him: Finally, let’s awaken him:
@ -7749,18 +7765,759 @@ Finally, let’s awaken him:
#+BEGIN_SRC evennia #+BEGIN_SRC evennia
py rick = me.search("rick") ; rick.backstory("barbarian") py rick = me.search("rick") ; rick.backstory("barbarian")
#+END_SRC #+END_SRC
* A Brave New World
Let’s sail to other islands a part of the way to expand the ingredients for potions as well as to create more “games”.
** The Boat
When the boat is docked, we should have a special /state/ to describe it:
#+BEGIN_SRC evennia
@teleport/quiet Lazy Dock
#
@desc/boat here = The dock you stand on juts into a bay of water the color of lavender flowers. A giant leaf softly bobs in the subtle surf next to the dock. Perhaps one could use this to sail across this sea?
#+END_SRC
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.
#+BEGIN_SRC evennia
@dig/teleport Leaf Boat;gr01 = leaf boat;boat;embark;exit-dock-to-boat,dock;land;disembark;exit-boat-to-dock
#+END_SRC
General description:
#+BEGIN_SRC evennia
@desc here = This leaf, large enough to accommodate a few people comfortably, seems to be a strange, but steady watercraft.
#+END_SRC
Controlling the boat comes from a Script:
#+BEGIN_SRC evennia
@script here = typeclasses.sailing.Boat
#
@set/script sailing/is_sailing = False
#
@set/script sailing/sailing_direction = "port"
#+END_SRC
And store the exits on [[file:~/src/moss-n-puddles/typeclasses/sailing.py::class Boat(Script):][the script]]:
#+BEGIN_SRC evennia
@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)
#+END_SRC
The boat has three /states/:
- =docked= (at *Lazy Dock*)
- =sailing= (at sea)
- =ashore= (at some island below)
#+BEGIN_SRC evennia
@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
#+END_SRC
Toggle that state with:
#+BEGIN_SRC evennia :tangle no
@roomstate docked
#+END_SRC
And describe sailing on it.:
#+BEGIN_SRC evennia
@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. <morning>The morning sun peaks above the colossal trees and the snow-capped mountains beyond.</morning><afternoon>The afternoon sun peaks out briefly from misty clouds.</afternoon><evening>The evening sun begins to set on a watery horizon.</evening><night>The moon peaks out from behind wispy clouds.</night>
#+END_SRC
And the description of the boat when ashore on an island:
#+BEGIN_SRC evennia
@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
#+END_SRC
While on the boat, describe the dock:
#+BEGIN_SRC evennia
@desc dock = A lazy dock with a comfortable-looking chair and a forest of colossal trees behind it on a hill.
#+END_SRC
And describe disembarking to the Lazy Dock:
#+BEGIN_SRC evennia
@set dock/traverse_msg = "You easily disembark from the giant leaf and step onto the dock..."
#+END_SRC
Disembark to describe the exits to the boat:
#+BEGIN_SRC evennia
@teleport/quiet Lazy Dock
#+END_SRC
Describe the leaf boat (as an exit):
#+BEGIN_SRC evennia
@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...
#+END_SRC
And describe embarking:
#+BEGIN_SRC evennia
@set boat/traverse_msg = "You step into the bowed cavity of the leaf. Seems surprisingly steady...for a leaf."
#+END_SRC
The /exit/ to and from the Dock to the *Leaf Boat* comes and goes.
Send the “exit” to the leaf boat into the void to be picked up later:
#+BEGIN_SRC evennia
@teleport/tonone boat
#+END_SRC
Create a script that keeps the boat out at the island:
#+BEGIN_SRC evennia
@script boat-reset:typeclasses.sailing.ResetBoat
#+END_SRC
Let’s blow our horn and board the boat (we begin from the Grove of Matriarches):
#+BEGIN_SRC tcl
send "south\n"
expectit "Lazy Dock"
set timeout 240
send "blow horn\n"
# expectit "You blow your horn"
expectit "The giant leaf slows as it arrives next to the dock."
send "boat\n"
expectit "The giant leaf gently bobs in the surf of this island, allowing you to disembark."
#+END_SRC
** Throne Island
The boat should land on a distant island.
We should come up with a *theme* for the entire island, from the “statue” to the puzzles and answers.
#+BEGIN_SRC evennia
@dig/teleport Lonely Island;gr02:typeclasses.rooms_weather.TimeWeatherRoom
#+END_SRC
And describe it:
#+BEGIN_SRC evennia
@desc here = Vibrant green moss covers the island's gray rock. Wandering around the conifers you encounter a |Ystatue|n of an elegant woman seated on a large throne.
$state(boat, A large leaf bobs invitingly in the surf.)
#+END_SRC
And tell the boat that this is the /default island/:
#+BEGIN_SRC evennia
@set/script sailing/default_island = $search(Lonely Island)
#+END_SRC
Now jump into the boat,
#+BEGIN_SRC evennia
@open leaf boat;boat;embark;exit-isle1-to-boat,shore;disembark;exit-boat-to-isle1 = gr01
#+END_SRC
And describe the exit:
#+BEGIN_SRC evennia
@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.
#+END_SRC
And about getting on the boat:
#+BEGIN_SRC evennia
@set boat/traverse_msg = "You step into the bowed cavity of the giant leaf."
#+END_SRC
And store this exits on the script:
#+BEGIN_SRC evennia
@set/script sailing/island1_to_boat = $search(exit-isle1-to-boat)
#
@set/script sailing/boat_to_island1 = $search(exit-boat-to-isle1)
#+END_SRC
Now, let’s create the other exit:
#+BEGIN_SRC evennia
@teleport gr01
#+END_SRC
And exiting the boat:
#+BEGIN_SRC evennia
@set shore/traverse_msg = "You disembark from the giant leaf to step off into the surf and onto the shore..."
#+END_SRC
Are they going to look before getting off?
#+BEGIN_SRC evennia
@desc shore = You seem to have arrived at some island covered in large conifer trees.
#+END_SRC
And tell the sailing script about this exit, so it can manipulate it:
#+BEGIN_SRC evennia
@set/script sailing/boat_to_island1 = $search(exit-boat-to-isle1)
#+END_SRC
And a bit of clean up to leave the boat at the island.
#+BEGIN_SRC evennia
# This is global, really?
# @teleport/tonone dock
# py me.search('dock', location=here).location = None
#+END_SRC
And the boat’s “room state”:
#+BEGIN_SRC evennia
@roomstate shore
#+END_SRC
Let’s get off of the boat and enjoy the island…
#+BEGIN_SRC tcl
send "disembark\n"
expectit "Lonely Island"
#+END_SRC
*** Moonberries
These berries should be created for making [[file:mud.org::*Trippy Potion][a Trippy Potion]].
#+BEGIN_SRC evennia
@teleport/quiet gr02
#
@create/drop moonberries;moonberry;berry: typeclasses.consumables.Producer
#
@desc moonberries = Growing a slender vines that wrap around the trunks of the pine trees. Laden with small, blue berries, each reflecting a white cresent shape.
#
@set moonberries/make_name = "handful of moonberries"
#
@set moonberries/make_verb = "$conj(collect) a"
#
@set moonberries/make_class = "typeclasses.consumables.Edible"
#
@set moonberries/make_desc = "Blue berries with a white cresent shape."
#
@set moonberries/make_amount = 1
#
@set moonberries/make_eat_msg = "Slightly bitter. Not very good."
#
@set moonberries/make_spell_msgs = "10 ;; $You() $conj(stare) off into space, lost in thought. ;; $You() $conj(study) $pron(your) hands, utterly captivated. ;; 5 ;; $You() $conj(awaken) from a daydream with a start."
#
# And we can't see them until we are an alchemist:
@lock moonberries = view:tag(alchemist)
#+END_SRC
Let’s grab some berries for the road:
#+BEGIN_SRC tcl
send "get moonberry\n"
expectit "You collect a handful of moonberries"
#+END_SRC
*** Seagrass
Part of the [[Potions and their Ingredients][Alchemist Path]], we use /seagrass/ to create a [[file:mud.org::*Water Breathing][Water Breathing potion]].
We put this location hint in the Alchemy Tome:
#+begin_quote
This grass, often called seagrass since it vibrates as if laughing. The herb hunter canfind this in fields and meadows throughout the area.
#+end_quote
And we create it:
#+BEGIN_src evennia
@teleport/quiet gr02
#
@create/drop patch of seagrass;seagrass;sea grass;grass: typeclasses.consumables.Producer
#
@desc seagrass = Patches of this vivid grass vibrates as if giggling.
#
@set seagrass/make_name = "bunch of seagrass"
#
@set seagrass/make_verb = "$conj(harvest) a"
#
@set seagrass/make_class = "typeclasses.consumables.Herb"
#
@set seagrass/make_desc = "This grass vibrates as if giggling."
#
@set seagrass/make_amount = 1
#
# And we can't see them until we are an alchemist:
@lock seagrass = view:tag(alchemist)
#+END_SRC
Until we are an alchemist, we shouldn't see the mushrooms...
#+BEGIN_SRC tcl
dont_expect "You notice" "seagrass"
#+END_SRC
And harvest it:
#+BEGIN_SRC tcl
send "get seagrass\n"
expectit "You harvest a bunch of seagrass"
#+END_SRC
*** The Throne: First Puzzle
Details on the throne and the statues and maybe moss? What about sitting on the throne?
#+BEGIN_SRC evennia
@teleport gr02
#
@detail statue = A massive statue of an elegant woman atop a throne. A |Yshield|n rests against her knee as she hold an upright |Ytrident|n. In her other hand, she holds aloft an |Yorb|n. Eons of moss and mildew adds the appearance of tears streaming down her face. Only a raised eyebrow and subtle smirk hints at an earlier impression. At the base of statue is a |Yplaque|n.
#+END_SRC
#+BEGIN_SRC evennia
@detail trident = Held loosely with one hand. Unclear if the sculptor intended for the middle tine to be missing, or if it broke centuries ago.
#+END_SRC
#+BEGIN_SRC evennia
@detail orb;ball = The sphere resting on the statue's outstretched hand has an ornamental cross on top and the carved letters: |mR E G I N A|n
#+END_SRC
The shield holds a poem:
#+BEGIN_SRC evennia
@detail shield = An ornate shield carved with a poem:
Upon the waves, as ship doeth glide,
Fearful whispers of the sea abide,
We cast our plea, with silver and song
To keep us safe as we sail along.
She stirs the tides with playful ease,
Her whispers ride the ocean's breeze,
Her mischief wrapped in salty foam,
The sea, our mother, the Great Thalome.
#+END_SRC
And the plaque gives the riddle.
#+BEGIN_SRC evennia
@detail plaque = Glory to the seamother. All should |yspeak|n her |yname|n out loud.
#+END_SRC
The plaque contains a /password/ to say aloud. We need another StoryCube to listen for the pass phrase:
#+BEGIN_SRC evennia
@create/drop gr02ctl:typeclasses.puzzles.StoryCube
#
@desc gr02ctl = Opens the door when someone says Melora
#
@lock gr02ctl = view:false()
#
# Even if you can't see it, we don't want anyone taking it:
@lock gr02ctl = get:false()
#+END_SRC
The poem in the shield has the /name, and the plague mentions that someone needs to say the name, which if said aloud, we hear:
#+BEGIN_SRC evennia
@set gr02ctl/say = { r".*\bthalome.*": "gm Upon uttering that phrase, sparks of octarine magic pop the illusion, revealing an open archway at the base of the giant statue. ;; tag_all hidden_archway" }
#+END_SRC
And we need to get rid of it when any one leaves:
#+BEGIN_SRC evennia
@set gr02ctl/leave = "30 ;; @teleport/tonone arched opening"
#+END_SRC
And we test it:
#+BEGIN_SRC tcl
send "look statue\n"
expectit "A massive statue"
send "look trident\n"
expectit "Held loosely with one hand"
send "look ball\n"
expectit "sphere resting"
send "look shield\n"
expectit "An ornate shield"
send "look plaque\n"
expectit "Glory to the seamother"
send "say The answer is Thalome, right?\n"
expectit "revealing an open archway"
#+END_SRC
Which exit would need to be moved from the =None= space and a script to teleport it back after a minute.
*** Antechamber
From the magical archway, let’s create a room under the Throne:
#+BEGIN_SRC evennia
@dig Antechamber;gr03:typeclasses.rooms_dark.DarkRoom = arched opening;opening;arch;archway
#+END_SRC
#+BEGIN_SRC evennia
@desc arched opening = An arch, shaped as a cresent moon, rests on two ornate pillars to frame a short opening at the base of the statue. One pillar shows carved letters: |mF I C K L E|n and the other pillar shows: |mF A I T H|n.
#+END_SRC
And the description of entering:
#+BEGIN_SRC evennia
@set arched opening/traverse_msg = "You slightly stoop to enter into the darkness beyond the archway... As soon as you do, a large stone block slams down behind you, trapping you in the darkness."
#+END_SRC
Let’s hide and lock this down:
#+BEGIN_SRC evennia
@set arched opening/hidden_tag = "hidden_archway"
#
@lock arched opening = view:tag(hidden_archway)
#+END_SRC
But we can enter:
#+BEGIN_SRC tcl
send "look opening\n"
expectit "shaped as a cresent moon"
send "arch\n"
expect {
"The room is completely dark" {
send "light torch\n"
expectit "The room is lit up"
send "look\n"
expectit "Antechamber"
}
"Antechamber" {
send_user "We still have our torch lit from the last time."
}
timeout {
puts "ERROR: Expected response for 'get stick' not received."
send "quit\n"
exit 1
}
}
#+END_SRC
*** Puzzle Two: A Dark Place
Let’s make this a dark room where one has to have two feel around for a splinter to light, or bring a torch.
#+BEGIN_SRC evennia
@create torch:typeclasses.lightables.LightSource
#+END_SRC
Now, we, as a godlike avatar of Light, can go in:
#+BEGIN_SRC evennia
@teleport gr03
#
light torch
#
@desc here = A large room formed from intricately placed |Ystone blocks|n, tapered to form a |Ydome|n above you. In the center you see a large |Yobelisk|n with an empty |Ycavity|n halfway up. There seems to be no visible way to escape.
#+END_SRC
And some details:
#+BEGIN_SRC evennia
@detail cavity = While empty, this opening, about a foot or so deep, looks like it may have held something in the past.
#+END_SRC
Don’t know if the blocks are interesting:
#+BEGIN_SRC evennia
@detail stone blocks;blocks = While the stones are tightly placed, a curious blue fungus grows where moisture seeps between the seams.
#+END_SRC
Dome?
#+BEGIN_SRC evennia
@detail dome = Perhaps 30 feet high, but as shadows from the flame dance above you, you find it difficult to tell.
#+END_SRC
Let’s look at all the fine details:
#+BEGIN_SRC tcl
send "look cavity\n"
expectit "this opening"
send "look blocks\n"
expectit "the stones are tightly placed"
send "look dome\n"
expectit "Perhaps 30 feet high"
#+END_SRC
*** Puzzle Three: The Changing Obelisk
Each time this is “seen”, the response is different, so we use a [[file:~/src/moss-n-puddles/typeclasses/puzzles.py::class Changling(Object):][Changling]]:
#+BEGIN_SRC evennia
@create/drop granite obelisk: typeclasses.puzzles.Changling
#+END_SRC
And pin it down:
#+BEGIN_SRC evennia
@lock obelisk = get:false()
#
@set obelisk/get_err_msg = "That is way too big to lift."
#+END_SRC
The default description will be replaced:
#+BEGIN_SRC evennia
@desc obelisk = A granite obelisk grazes the top of the dome. Each of its four sides show a different image.
#+END_SRC
We build a /changling’s/ description from a =_prefix=, a random =descs=, and a =_postfix=:
#+BEGIN_SRC evennia
@set obelisk/desc_prefix = "A << granite ^ large ^ stone >> obelisk grazes the top of the dome. The surface of the obelisk seem to waver, shift and writhe under your gaze."
#
@set obelisk/desc_postfix = " And << quickly ^ soon ^ swiftly >>, the << image ^ visage ^ vision ^ carving >> << is gone ^ fades ^ disappears ^ vanishes >>."
#+END_SRC
And a list of differences that gives a hint to the password:
#+BEGIN_SRC evennia
@set obelisk/descs = (
"You can briefly make out the image of ocean waves.",
"For the briefest moment you make out an icy engravings of crystals.",
"You think you can see the outline of grass covered in dew drops.",
"The surface for a moment seems to portray clouds passing by.",
"Trees bordering a flowing river show up on the surface.",
)
#+END_SRC
If they say the /magic word/, they get the maguffin.
#+BEGIN_SRC evennia
@set obelisk/say = { r".*\b[Ww]ater\b": "gm Upon uttering that phrase, sparks of octarine magic and mist appear on the opening on the obelisk. ;; scoring_all blue_medal ;; gm A blue medal materializes in the opening. You snatch it, claiming your prize.
;; gift_all blue ;; gm You also notice a secret door! ;; tag_all hidden_antechamber_door" }
#+END_SRC
And let’s test this out:
#+BEGIN_SRC tcl
send "look obelisk\n"
expectit "grazes the top of the dome"
send "get obelisk\n"
expectit "That is way too big"
send "say Is the magic word, Water?\n"
expectit "Upon uttering that phrase"
# Make sure we got a blue medal ... Or whatever reward we should get
send "inv\n"
expectit "blue medal"
#+END_SRC
*** Puzzle Four: The Door Puzzle
Another puzzle, another room:
#+BEGIN_SRC evennia
@dig Escape Hallway;gr04:typeclasses.rooms_dark.DarkRoom = secret door;door
#+END_SRC
And we describe the secret door…
#+BEGIN_SRC evennia
@desc secret door = A door you didn't notice before, looks like stone blocks. Beyond looks like another chamber.
#+END_SRC
And the description of entering:
#+BEGIN_SRC evennia
@set secret door/traverse_msg = "You slightly stoop to enter the dark chamber beyond..."
#+END_SRC
Let’s hide and lock this down:
#+BEGIN_SRC evennia
@set secret door/hidden_tag = "hidden_antechamber_door"
#
@lock secret door = view:tag(hidden_antechamber_door)
#+END_SRC
Let’s go:
#+BEGIN_SRC evennia
@teleport gr04
#
@desc here = This stone hallway ends with an overly |Yornate door|n covered with letters in rows and columns forming a grid matrix. Placed above the door, a bronze |Yplaque|n, shows its age with a coat of patina.
#+END_SRC
Let’s reward our players again:
#+BEGIN_SRC evennia
@set here/arrive = "4 ;; gm ... You hear a distant dice roll ... ;; gmm {0} = With your excellent perception, you notice some gold coins hidden in the floor's dust. ;; gma {0} = $You() $conj(relocate) some gold coins on the floor to $pron(your) pouch. ;; coin 10 to {0}"
#+END_SRC
We need another door in order to escape.
#+BEGIN_SRC evennia
@detail plaque = Through the green patina, you can barely make out the text:|/
A spoken word unseals me.
It starts with a T.
Halfway between twins.
The next character lies.
#+END_SRC
The door leads back out.
#+BEGIN_SRC evennia
@open ornate door;door = Lonely Island
#+END_SRC
The dock is locked:
#+BEGIN_SRC evennia
@lock ornate door = traverse:tag(open_ornate_door)
#+END_SRC
#+BEGIN_SRC evennia
@set ornate door/err_traverse = "The door is locked."
#+END_SRC
Look at the grid to find the two T’s:
#+BEGIN_SRC evennia
@desc ornate door = The grid of letters look like:|/
| Y | B | Q | X | D | Q |
| Y | A | K | D | X | B |
| D | E | L | G | W | X |
| T | S | - | A | X | V |
| L | J | H | E | P | V |
| F | U | I | D | I | P |
| H | X | R | G | R | X |
| K | S | M | U | T | N |
| W | X | M | W | Z | Z |
#+END_SRC
We use a =StoryCube= to lock the door until the password is said:
#+BEGIN_SRC evennia
@create/drop gr04ctl:typeclasses.puzzles.StoryCube
#
@desc gr04ctl = Opens the door when someone says Tidal Surge
#
@lock gr04ctl = view:false()
#
@lock gr04ctl = get:false()
#+END_SRC
If they say, /Tidal Surge/, the *Locker* unlocks the door, and they can go through.
#+BEGIN_SRC evennia
@set gr04ctl/say = {r"^tidal ?surge.*": "gm You hear an audible click from the ornate door! ;; tag_all open_ornate_door"}
#+END_SRC
And the description of passing through:
#+BEGIN_SRC evennia
@set ornate door/traverse_msg = "You open the door, and see that the door exits next to the statue..."
#+END_SRC
Let’s end this back on the Dock:
#+BEGIN_SRC emacs-lisp
teleport Lazy Dock
#+END_SRC
But we’ll have the goblin test this out:
#+BEGIN_SRC tcl
send "look door\n"
expectit "A door you didn't notice before"
send "door\n"
expectit "Escape Hallway"
#+END_SRC
* Scoring
I would like a =score= command that gives them a tally based on the activities they have participated in. This will be a hashtable, stored as an attribute. Perhaps it increments a value? Should I simplify the number of items, and then list them all, so they have goals?
- jump
- throw stick
- catch fish
- feed bhb
- get traveling horn
- make tea
- read a book
- eat candy
- get pipe
- get blue medal
- finish cocktail (all cocktails?)
- make potion (laughing)
Should the score be something be shown when one issues a =look= at another character?
** Bugs ** Bugs
The =TICKER_HANDLER= is a /script/ but fully programmed and configured in the Python code. When I create a /weather room/ or a pet, the handler should be turned on and set to persist, sometimes this fails to get re-instantiated.
The following code is idempotent and can be re-run /just to be sure/:
#+NAME: ticker-handler-script
#+BEGIN_SRC python :tangle no
from evennia import TICKER_HANDLER
from evennia.utils.search import search_typeclass
for pet in search_typeclass("typeclasses.pets.Pet", include_children=True):
TICKER_HANDLER.add(interval=160, callback=pet.update_state)
for baker in search_typeclass("typeclasses.consumables.Trolley"):
TICKER_HANDLER.add(interval=86400, callback=baker.do_bake)
for room in search_typeclass("typeclasses.rooms_weather.TimeWeatherRoom"):
TICKER_HANDLER.add(interval=300, callback=room.update_weather)
#+END_SRC
And run it in an /interactive python/ sub-session:
#+BEGIN_SRC evennia :noweb yes
@py
<<ticker-handler-script>>
quit()
#+END_SRC
Let’s take a look at all the tickers:
#+BEGIN_SRC evennia
@py from evennia import TICKER_HANDLER; print(TICKER_HANDLER.all_display())
#+END_SRC
Which should show us all three pets (BHB, wee beastie and the fireplace) as well as the =do_bake= of the Kitchen’s cabinet and the Goblin’s tea party table and all the rooms with weather:
#+BEGIN_SRC python :tangle no
[(cabinet, 'do_bake', None, 86400, '', True), (fish, 'do_speak', None, 3, '', True), (fireplace, 'update_state', None, 60, '', True), (big hairy beast, 'update_state', None, 60, '', True), (wee beastie, 'update_state', None, 60, '', True)]
#+END_SRC
And now that we are done, say it: And now that we are done, say it:
#+BEGIN_src evennia #+BEGIN_src evennia
@teleport/quiet mp05 @teleport/quiet mp01
# #
say I have finished this, the first version, of my creation. say I have finished this, the first version, of my creation.
#+END_SRC #+END_SRC
** Janitor
The /janitor/ is a =Script= that moves every thing back to where they should be at the end of the day.
#+BEGIN_SRC evennia
@script janitor:typeclasses.janitor.Janitor
#+END_SRC
** Finish Testing ** Finish Testing
Can we quickly walk back through the world, gathering ingredients, and then going back into the Secret Room to make potions? Can we quickly walk back through the world, gathering ingredients, and then going back into the Secret Room to make potions?
@ -7910,17 +8667,6 @@ Let’s try the next potion, the /Somnium Illusorium/:
send "west\n" send "west\n"
expectit "Grove" expectit "Grove"
send "south\n"
expectit "Lazy Dock"
set timeout 240
send "blow horn\n"
# expectit "You blow your horn"
expectit "The giant leaf slows as it arrives next to the dock."
send "boat\n"
expectit "The giant leaf gently bobs in the surf of this island, allowing you to disembark."
send "disembark\n" send "disembark\n"
expectit "Lonely Island" expectit "Lonely Island"
@ -8150,24 +8896,6 @@ You sample from your teacup.
> =throw book in fire= > =throw book in fire=
You drop the book, and it flaps its pages, and flies back to a section on the shelf. Gotta keep the room organized. You drop the book, and it flaps its pages, and flies back to a section on the shelf. Gotta keep the room organized.
* Scoring
I would like a =score= command that gives them a tally based on the activities they have participated in. This will be a hashtable, stored as an attribute. Perhaps it increments a value? Should I simplify the number of items, and then list them all, so they have goals?
- jump
- throw stick
- catch fish
- feed bhb
- get traveling horn
- make tea
- read a book
- eat candy
- get pipe
- get blue medal
- finish cocktail (all cocktails?)
- make potion (laughing)
Should the score be something be shown when one issues a =look= at another character?
* Tutorial Bird * Tutorial Bird
If a person creates a character, instead of giving them a personal letter, let’s have a little bird land on their shoulder, and talk them through the game. I’m thinking a “state machine” triggered by actions that someone does. If a person creates a character, instead of giving them a personal letter, let’s have a little bird land on their shoulder, and talk them through the game. I’m thinking a “state machine” triggered by actions that someone does.

95
typeclasses/janitor.py Executable file
View file

@ -0,0 +1,95 @@
#!/usr/bin/env python
"""
Janitor
Create a service that goes through the world at an interval
moving things that don't belong into a hamper.
"""
from evennia.utils import search
from evennia.utils import logger
from typeclasses.scripts import Script
from typeclasses.exits import Exit
from typeclasses.consumables import Producer
ROOMS_TO_CLEAN = [
"Grove of the Matriarchs",
"Boulder Top",
"Cozy House",
"Grotto",
"Frog Meadow",
"Lazy Dock",
"Lair",
"Mellow Marsh",
"Homey Hut",
"Wyldwood Bar",
"Cramped Kitchen",
"Secret Room",
"Bedroom",
"Prairie",
"Shore",
"Salty Shack", # mp16
"Glittering Glade", # mp19
"Dark Tunnel", # mp20
"Workshop", # mp21
"Damp Cave",
"Leaf Boat",
"Lonely Island",
"Antechamber",
"Escape Hallway",
]
ROOM_EXCEPTIONS = {
"Grove of the Matriarchs": [ "stick" ],
"Cozy House": [ "crystal ball" ],
"Lazy Dock": [ "fishing pole" ],
"Homey Hut": [ "horn" ],
"Bedroom": [ "pair slippers" ],
}
class Janitor(Script):
"""
Should this be an Object or inherit from Script?
"""
def at_script_creation(self):
self.key = "Janitor"
self.desc = "Move trash to a receptacle"
self.interval = 60 * 60 * 24
self.start_delay = False
self.persistent = True
def at_start(self, **kwargs):
self.at_repeat()
def at_repeat(self, **kwargs):
for name in ROOMS_TO_CLEAN:
for room in search.objects(name):
self.clean_room(room)
def clean_room(self, room):
"""
Sweep through the room's contents.
Anything that shouldn't be there is moved to 'mp00'.
Exceptions include objects that are locked to the room,
_exits_ and _producers_ (because producers are not locked),
and actual exceptions in ROOM_EXCEPTIONS.
"""
exceptions = ROOM_EXCEPTIONS.get(room.key, [])
trash = [i for i in room.contents
if not i.is_typeclass(Exit)
and not i.is_typeclass(Producer)
and i.locks.get("get") != "get:false()"
and i.name not in exceptions ]
for t in trash:
to_room = search.objects("mp00")[0]
for room,objs in ROOM_EXCEPTIONS.items():
if t.key in objs:
to_room = search.objects(room)[0]
# Quietly move it to where the object belongs:
logger.info(f"JANITOR: Move '{t.name}' to {to_room.name}")
t.location.msg_contents(f"A rat with beady eyes and a leather satchel appears, picks up the {t.name} and takes off with it.")
t.move_to(to_room, quiet=True)

View file

@ -570,7 +570,12 @@ class WeeBeastie(Friendly, Familiar, Listener, AI):
class BHB(Friendly, Listener, AI): class BHB(Friendly, Listener, AI):
"""
The Big Hairy Beast is a special pet that has a circadian
rhythm, and goes to bed each night and wakes each morning.
"""
def return_appearance(self, looker): def return_appearance(self, looker):
"""The beast looks different when it is sleeping."""
if self.db.is_awake: if self.db.is_awake:
return super().return_appearance(looker) return super().return_appearance(looker)
else: else:

View file

@ -353,7 +353,7 @@ class Wood(Litterable):
])) ]))
class Stick(Consumable): class Stick(Object):
"An object to throw." "An object to throw."
def at_object_creation(self): def at_object_creation(self):
self.cmdset.add_default(CmdSetStick) self.cmdset.add_default(CmdSetStick)

View file

@ -1204,11 +1204,95 @@ expectit "Frog Meadow"
send "west\n" send "west\n"
expectit "Grove of the Matriarchs" expectit "Grove of the Matriarchs"
send "west\n" send "south\n"
expectit "Grotto" expectit "Lazy Dock"
set timeout 240
send "blow horn\n"
# expectit "You blow your horn"
expectit "The giant leaf slows as it arrives next to the dock."
send "boat\n"
expectit "The giant leaf gently bobs in the surf of this island, allowing you to disembark."
send "disembark\n"
expectit "Lonely Island"
send "get moonberry\n"
expectit "You collect a handful of moonberries"
dont_expect "You notice" "seagrass"
send "get seagrass\n"
expectit "You harvest a bunch of seagrass"
send "look statue\n"
expectit "A massive statue"
send "look trident\n"
expectit "Held loosely with one hand"
send "look ball\n"
expectit "sphere resting"
send "look shield\n"
expectit "An ornate shield"
send "look plaque\n"
expectit "Glory to the seamother"
send "say The answer is Thalome, right?\n"
expectit "revealing an open archway"
send "look opening\n"
expectit "shaped as a cresent moon"
send "arch\n"
expect {
"The room is completely dark" {
send "light torch\n"
expectit "The room is lit up"
send "look\n"
expectit "Antechamber"
}
"Antechamber" {
send_user "We still have our torch lit from the last time."
}
timeout {
puts "ERROR: Expected response for 'get stick' not received."
send "quit\n"
exit 1
}
}
send "look cavity\n"
expectit "this opening"
send "look blocks\n"
expectit "the stones are tightly placed"
send "look dome\n"
expectit "Perhaps 30 feet high"
send "look obelisk\n"
expectit "grazes the top of the dome"
send "get obelisk\n"
expectit "That is way too big"
send "say Is the magic word, Water?\n"
expectit "Upon uttering that phrase"
# Make sure we got a blue medal ... Or whatever reward we should get
send "inv\n"
expectit "blue medal"
send "look door\n"
expectit "A door you didn't notice before"
send "door\n" send "door\n"
expectit "Cozy House" expectit "Escape Hallway"
send "leave\n" send "leave\n"
expectit "Grotto" expectit "Grotto"
@ -1346,17 +1430,6 @@ expectit "Frog Meadow"
send "west\n" send "west\n"
expectit "Grove" expectit "Grove"
send "south\n"
expectit "Lazy Dock"
set timeout 240
send "blow horn\n"
# expectit "You blow your horn"
expectit "The giant leaf slows as it arrives next to the dock."
send "boat\n"
expectit "The giant leaf gently bobs in the surf of this island, allowing you to disembark."
send "disembark\n" send "disembark\n"
expectit "Lonely Island" expectit "Lonely Island"

File diff suppressed because it is too large Load diff