A character can acquire a horn (from stealing it from the witch) and own the horn (traveling at will). The puzzle now gifts the players who win a blue medal for their effort.
439 lines
15 KiB
Python
Executable file
439 lines
15 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
from random import choice
|
|
from re import match
|
|
|
|
from evennia import CmdSet
|
|
from evennia.utils import logger, delay, int2str, search
|
|
|
|
from typeclasses.puzzles import StoryCube
|
|
from typeclasses.scripts import Script
|
|
from typeclasses.objects import Object
|
|
from commands.command import Command
|
|
|
|
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):
|
|
"""
|
|
An invisible puppet to take care of horn's location.
|
|
|
|
@create/drop hornctl:typeclasses.puppets.StoryCube
|
|
@desc hornctl = Moves horn to room when character arrives.
|
|
@lock hornctl = view:false() # to make it invisible
|
|
@set hornctl/horn_object = mist horn
|
|
|
|
@set hornctl/arrive = "15 ;; gm The mists gather over the sea. ;; 18 ;; gm The swirling mists coalesce into a shape of a curved horn. ;;
|
|
20 ;; The curved horn, now physical, drops to the dock. ;; @teleport mist horn = here
|
|
"""
|
|
|
|
|
|
class CmdBlow(Command):
|
|
"""
|
|
Blow a horn.
|
|
|
|
Usage:
|
|
|
|
blow [ object ]
|
|
"""
|
|
key = "blow"
|
|
locks = "holds()"
|
|
|
|
def func(self):
|
|
self.obj.do_blow(self.caller)
|
|
|
|
|
|
class CmdSetHorn(CmdSet):
|
|
def at_cmdset_creation(self):
|
|
self.add(CmdBlow)
|
|
|
|
|
|
class CallingHorn(Object):
|
|
"""
|
|
A thing to move the ship to the caller's location.
|
|
"""
|
|
def at_object_creation(self):
|
|
self.cmdset.add_default(CmdSetHorn)
|
|
|
|
def do_blow(self, blower):
|
|
"""
|
|
The act of blowing a horn, calls the ship.
|
|
"""
|
|
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.")
|
|
|
|
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()
|
|
# Just make sure the script is running:
|
|
script.start()
|
|
|
|
|
|
class Boat(Script):
|
|
"""
|
|
Start the script by running the following:
|
|
|
|
@teleport gr01
|
|
@script here = typeclasses.sailing.Boat
|
|
"""
|
|
def at_script_creation(self):
|
|
self.key = "sailing"
|
|
self.desc = "Sailing the Boat"
|
|
self.interval = 60 # seconds
|
|
self.start_delay = True # wait self.interval until first call
|
|
self.persistent = True
|
|
self.db.sailing_direction = "port"
|
|
self.db.is_sailing = False
|
|
|
|
def at_repeat(self, **kwargs):
|
|
"""
|
|
If we have passengers, we need to sail...
|
|
"""
|
|
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 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
|
|
|
|
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.
|
|
|
|
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
|
|
|
|
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]
|
|
|
|
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]
|
|
|
|
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]
|
|
|
|
# ----------------------------------------------------------------------
|
|
# HELPER FUNCTIONS FOR SAILING THE BOAT
|
|
# ----------------------------------------------------------------------
|
|
|
|
standard_delay = 7
|
|
|
|
def characters_on(self, location):
|
|
"""
|
|
Return a list of players on the location.
|
|
"""
|
|
return [item for item in location.contents
|
|
if item.is_typeclass("typeclasses.characters.Character")]
|
|
|
|
def send_to(self, to_location, message, delayed_by=0):
|
|
"""
|
|
Abstraction on sending a delayed message to a location.
|
|
"""
|
|
if delayed_by == 0:
|
|
to_location.msg_contents("\n" + message)
|
|
else:
|
|
delay(delayed_by * self.standard_delay,
|
|
to_location.msg_contents, "\n" + message)
|
|
|
|
# ----------------------------------------------------------------------
|
|
# SAILING TO AN ISLAND...
|
|
# ----------------------------------------------------------------------
|
|
|
|
sailing_events = [
|
|
"Suddenly, the air is filled with fish with glowing fins, flying over the waves!",
|
|
"Suddenly, you see a huge sea serpent swimming towards the fragile leaf. " +
|
|
"As it gets closer, it dives, and swims under the boat. " +
|
|
"The colorful patterns of its scales form mesmerizing patterns as it passes.",
|
|
"Suddenly, you see a pair of horses, pulling a chariot with two warriors. " +
|
|
"They look at you confused, as you sail past.",
|
|
"Suddenly, a pod of dolphins swim briefly alongside your makeshift boat. " +
|
|
"As they begin to leave, one turns and waves.",
|
|
]
|
|
|
|
def to_isle_boat_view(self, boat):
|
|
"""
|
|
Delayed messages sent to passengers on the boat.
|
|
|
|
- Start with a message about leaving.
|
|
- Then a message about the peaceful journey.
|
|
- Followed by a random event.
|
|
- Finished with a view of a distant island.
|
|
"""
|
|
boat.msg_contents("The leaf boat sets sail onto the lavender sea.")
|
|
|
|
# Nice stuff
|
|
tod = boat.get_time_of_day()
|
|
second_msg = "Only eddies from the subtle wake, intrude on the " \
|
|
"lavender sea's tranquility, as the giant leaf floats along invisible currents. "
|
|
if tod == "morning":
|
|
second_msg += "The morning sun peaks above the colossal trees and " \
|
|
"the snow-capped mountains beyond."
|
|
elif tod == "afternoon":
|
|
second_msg += "The afternoon sun peaks out briefly from misty clouds."
|
|
elif tod == "afternoon":
|
|
second_msg += "The evening sun begins to set on a watery horizon."
|
|
else:
|
|
second_msg += "The moon peaks out from behind wispy clouds."
|
|
self.send_to(boat, second_msg, 1)
|
|
|
|
# Random event
|
|
third_msg = choice(self.sailing_events)
|
|
self.send_to(boat, third_msg, 3)
|
|
|
|
self.send_to(boat, "In the distance, you see an island.", 5)
|
|
|
|
def to_isle_isle_view(self, isle, num_sailors):
|
|
"""
|
|
Delayed messages sent to anyone on the island.
|
|
"""
|
|
self.send_to(isle, "You see a giant leaf sailing on the lavender sea.", 4)
|
|
|
|
msg = "The leaf sails ever closer."
|
|
if num_sailors == 1:
|
|
msg = msg + " You can see it carries a passenger."
|
|
elif num_sailors > 1:
|
|
msg = msg + \
|
|
f" You can see it carries {int2str(num_sailors)} passengers."
|
|
self.send_to(isle, msg, 5)
|
|
|
|
def to_isle_dock_view(self, dock, num_sailors):
|
|
"""
|
|
Message sent to anyone standing on the dock when the boat leaves.
|
|
"""
|
|
msg = "The leaf boat sets sail"
|
|
if num_sailors == 0:
|
|
msg += ", leaving you behind."
|
|
elif num_sailors == 1:
|
|
msg += " with its passenger."
|
|
else:
|
|
msg += f" with its {int2str(num_sailors)} passengers."
|
|
dock.msg_contents(msg)
|
|
|
|
def to_isle(self, default_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)
|
|
self.db.sailing_direction = isle
|
|
self.db.is_sailing = True
|
|
|
|
num_sailors = len(self.characters_on(boat))
|
|
|
|
boat.remove_room_state("docked")
|
|
boat.add_room_state("sailing")
|
|
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)
|
|
|
|
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)
|
|
|
|
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, "
|
|
"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,"
|
|
" allowing you to disembark.")
|
|
boat_exit.move_to(island, quiet=True)
|
|
shore_exit.move_to(boat, quiet=True)
|
|
island.add_room_state("boat")
|
|
boat.add_room_state("ashore")
|
|
|
|
# For the next time we need to sail, we go to:
|
|
self.db.sailing_direction = "port"
|
|
self.db.is_sailing = False
|
|
|
|
# ----------------------------------------------------------------------
|
|
# SAILING BACK TO THE DOCK...
|
|
# ----------------------------------------------------------------------
|
|
|
|
def to_dock_boat_view(self, boat, num_dockers):
|
|
"""
|
|
Delayed messages from the point of view of the boat's passengers.
|
|
"""
|
|
self.send_to(boat, "The leaf boat sets sail into the lavender sea.")
|
|
|
|
self.send_to(boat, "Soon you see dark shapes of "
|
|
"colossal trees on the horizon.", 1)
|
|
|
|
self.send_to(boat, "In the shadows of the trees sits a dock.", 2)
|
|
|
|
if num_dockers == 0:
|
|
msg = "You can make out an inviting chair perched on the dock."
|
|
elif num_dockers == 1:
|
|
msg = "You can see a figure on the dock."
|
|
else:
|
|
msg = f"You can see {int2str(num_dockers)} figures standing on the dock."
|
|
self.send_to(boat, msg, 3)
|
|
|
|
def to_dock_dock_view(self, dock, num_sailors):
|
|
"""
|
|
Delayed messages from the point of view of people on the dock.
|
|
"""
|
|
self.send_to(dock, "In the distance, you notice a large leaf floating on the lavender sea.", 1)
|
|
|
|
self.send_to(dock, "The leaf seems to be floating towards the dock.", 2)
|
|
|
|
msg = "The leaf seems to be large enough to carry passengers."
|
|
if num_sailors == 1:
|
|
msg = msg + " In fact, it does carry a passenger."
|
|
elif num_sailors > 1:
|
|
msg = msg + f" In fact, it carries {int2str(num_sailors)} passengers."
|
|
self.send_to(dock, msg, 3)
|
|
|
|
def to_dock_isle_view(self, isle, num_sailors):
|
|
"""
|
|
Delayed messages from the point of view of people left on the island.
|
|
"""
|
|
msg = "The leaf boat sets sail"
|
|
if num_sailors == 0:
|
|
msg += ", leaving you behind."
|
|
elif num_sailors == 1:
|
|
msg += " with its passenger."
|
|
else:
|
|
msg += f" with its {int2str(num_sailors)} passengers."
|
|
self.send_to(isle, msg)
|
|
|
|
def to_dock(self):
|
|
"""
|
|
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()
|
|
logger.info(f"Got {dock}")
|
|
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)
|
|
|
|
num_sailors = len(self.characters_on(boat))
|
|
num_dockers = len(self.characters_on(dock))
|
|
|
|
self.to_dock_dock_view(dock, num_sailors)
|
|
self.to_dock_boat_view(boat, num_dockers)
|
|
self.to_dock_isle_view(isle, num_sailors)
|
|
|
|
delay(4 * self.standard_delay, self.docking_procedure, dock, boat, dock_exit, boat_exit)
|
|
|
|
def docking_procedure(self, dock, boat, dock_exit, boat_exit):
|
|
"""
|
|
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.")
|
|
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")
|
|
boat.add_room_state("docked")
|
|
|
|
# For the next time we need to sail, we go to a default island:
|
|
self.db.sailing_direction = None
|
|
self.db.is_sailing = False
|
|
|
|
|
|
class ResetBoat(Script):
|
|
"""
|
|
Script to pull the boat back out to sea and dock it at an island.
|
|
"""
|
|
def at_repeat(self, **kwargs):
|
|
script_results = search.scripts("sailing")
|
|
if script_results:
|
|
script = script_results[0]
|
|
if not script.db.is_sailing:
|
|
script.to_isle()
|
|
|
|
# Just make sure the script is running:
|
|
script.start()
|