Reworked the build script and made the default tutorial_room more clever, using details and custom cmdsets.
This commit is contained in:
parent
f0770da672
commit
c63ae1742f
11 changed files with 767 additions and 449 deletions
|
|
@ -16,6 +16,11 @@ from evennia import utils, create_object, search_object
|
|||
from evennia import syscmdkeys, default_cmds
|
||||
from evennia.contrib.tutorial_world.objects import LightSource, TutorialObject
|
||||
|
||||
# the system error-handling module is defined in the settings. We load the
|
||||
# given setting here using utils.object_from_module. This way we can use
|
||||
# it regardless of if we change settings later.
|
||||
from django.conf import settings
|
||||
_SEARCH_AT_RESULT = utils.object_from_module(settings.SEARCH_AT_RESULT)
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
|
|
@ -68,15 +73,139 @@ class CmdTutorial(Command):
|
|||
caller.msg("{RSorry, there is no tutorial help available here.{n")
|
||||
|
||||
|
||||
# for the @detail command we inherit from MuxCommand, since
|
||||
# we want to make use of MuxCommand's pre-parsing of '=' in the
|
||||
# argument.
|
||||
class CmdTutorialSetDetail(default_cmds.MuxCommand):
|
||||
"""
|
||||
sets a detail on a room
|
||||
|
||||
Usage:
|
||||
@detail <key> = <description>
|
||||
@detail <key>;<alias>;... = description
|
||||
|
||||
Example:
|
||||
@detail walls = The walls are covered in ...
|
||||
@detail castle;ruin;tower = The distant ruin ...
|
||||
|
||||
This sets a "detail" on the object this command is defined on
|
||||
(TutorialRoom for this tutorial). This detail can be accessed with
|
||||
the TutorialRoomLook command sitting on TutorialRoom objects (details
|
||||
are set as a simple dictionary on the room). This is a Builder command.
|
||||
|
||||
We custom parse the key for the ;-separator in order to create
|
||||
multiple aliases to the detail all at once.
|
||||
"""
|
||||
key = "@detail"
|
||||
locks = "cmd:perm(Builders)"
|
||||
help_category = "TutorialWorld"
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
All this does is to check if the object has
|
||||
the set_detail method and uses it.
|
||||
"""
|
||||
if not self.args or not self.rhs:
|
||||
self.caller.msg("Usage: @detail key = description")
|
||||
return
|
||||
if not hasattr(self.obj, "set_detail"):
|
||||
self.caller.msg("Details cannot be set on %s." % self.obj)
|
||||
return
|
||||
for key in self.args.split(";"):
|
||||
# loop over all aliases, if any (if not, this will just be
|
||||
# the one key to loop over)
|
||||
self.obj.set_detail(key, self.rhs)
|
||||
self.caller.msg("Detail set: '%s': '%s'" % (self.lhs, self.rhs))
|
||||
|
||||
|
||||
class CmdTutorialLook(default_cmds.CmdLook):
|
||||
"""
|
||||
looks at the room and on details
|
||||
|
||||
Usage:
|
||||
look <obj>
|
||||
look <room detail>
|
||||
look *<player>
|
||||
|
||||
Observes your location, details at your location or objects
|
||||
in your vicinity.
|
||||
|
||||
Tutorial: This is a child of the default Look command, that also
|
||||
allows us to look at "details" in the room. These details are
|
||||
things to examine and offers some extra description without
|
||||
actually having to be actual database objects. It uses the
|
||||
return_detail() hook on TutorialRooms for this.
|
||||
"""
|
||||
# we don't need to specify key/locks etc, this is already
|
||||
# set by the parent.
|
||||
help_category = "TutorialWorld"
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
Handle the looking. This is a copy of the default look
|
||||
code except for adding in the details.
|
||||
"""
|
||||
caller = self.caller
|
||||
args = self.args
|
||||
print "tutorial look"
|
||||
if args:
|
||||
# we use quiet=True to turn off automatic error reporting.
|
||||
# This tells search that we want to handle error messages
|
||||
# ourself. This also means the search function will always
|
||||
# return a list (with 0, 1 or more elements) rather than
|
||||
# result/None.
|
||||
looking_at_obj = caller.search(args, use_nicks=True, quiet=True)
|
||||
if len(looking_at_obj) != 1:
|
||||
# no target found or more than one target found (multimatch)
|
||||
# look for a detail that may match
|
||||
detail = self.obj.return_detail(args)
|
||||
print "look detail:", detail, self.obj, self.obj.db.details
|
||||
if detail:
|
||||
self.caller.msg(detail)
|
||||
return
|
||||
else:
|
||||
# no detail found, delegate our result to the normal
|
||||
# error message handler.
|
||||
_SEARCH_AT_RESULT(caller, args, looking_at_obj)
|
||||
return
|
||||
else:
|
||||
# we found a match, extract it from the list and carry on
|
||||
# normally with the look handling.
|
||||
looking_at_obj = looking_at_obj[0]
|
||||
|
||||
else:
|
||||
looking_at_obj = caller.location
|
||||
if not looking_at_obj:
|
||||
caller.msg("You have no location to look at!")
|
||||
return
|
||||
|
||||
if not hasattr(looking_at_obj, 'return_appearance'):
|
||||
# this is likely due to us having a player instead
|
||||
looking_at_obj = looking_at_obj.character
|
||||
if not looking_at_obj.access(caller, "view"):
|
||||
caller.msg("Could not find '%s'." % args)
|
||||
return
|
||||
# get object's appearance
|
||||
caller.msg(looking_at_obj.return_appearance(caller))
|
||||
# the object's at_desc() method.
|
||||
looking_at_obj.at_desc(looker=caller)
|
||||
|
||||
|
||||
|
||||
class TutorialRoomCmdSet(CmdSet):
|
||||
"""
|
||||
Implements the simple tutorial cmdset
|
||||
Implements the simple tutorial cmdset. This will overload the look
|
||||
command in the default CharacterCmdSet since it has a higher
|
||||
priority (ChracterCmdSet has prio 0)
|
||||
"""
|
||||
key = "tutorial_cmdset"
|
||||
priority = 1
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
"add the tutorial cmd"
|
||||
"add the tutorial-room commands"
|
||||
self.add(CmdTutorial())
|
||||
self.add(CmdTutorialSetDetail())
|
||||
self.add(CmdTutorialLook())
|
||||
|
||||
|
||||
class TutorialRoom(DefaultRoom):
|
||||
|
|
@ -95,6 +224,11 @@ class TutorialRoom(DefaultRoom):
|
|||
the room about it by trying to call a hook on them. The Mob object
|
||||
uses this to cheaply get notified of enemies without having
|
||||
to constantly scan for them.
|
||||
|
||||
Args:
|
||||
new_arrival (Object): the object that just entered this room.
|
||||
source_location (Object): the previous location of new_arrival.
|
||||
|
||||
"""
|
||||
if new_arrival.has_player and not new_arrival.is_superuser:
|
||||
# this is a character
|
||||
|
|
@ -102,6 +236,36 @@ class TutorialRoom(DefaultRoom):
|
|||
if hasattr(obj, "at_new_arrival"):
|
||||
obj.at_new_arrival(new_arrival)
|
||||
|
||||
def return_detail(self, detailkey):
|
||||
"""
|
||||
This looks for an Attribute "obj_details" and possibly
|
||||
returns the value of it.
|
||||
|
||||
Args:
|
||||
detailkey (str): The detail being looked at
|
||||
|
||||
"""
|
||||
details = self.db.details
|
||||
if details:
|
||||
return details.get(detailkey, None)
|
||||
|
||||
def set_detail(self, detailkey, description):
|
||||
"""
|
||||
This sets a new detail, using an Attribute "details".
|
||||
|
||||
Args:
|
||||
detailkey (str): The detail identifier to add (for
|
||||
aliases you need to add multiple keys to the
|
||||
same description).
|
||||
description (str): The text to return when looking
|
||||
at the given detailkey.
|
||||
|
||||
"""
|
||||
if self.db.details:
|
||||
self.db.details[detailkey] = description
|
||||
else:
|
||||
self.db.details = {detailkey: description}
|
||||
|
||||
def reset(self):
|
||||
"Can be called by the tutorial runner."
|
||||
pass
|
||||
|
|
@ -155,11 +319,13 @@ class WeatherRoom(TutorialRoom):
|
|||
self.db.tutorial_info = \
|
||||
"This room has a Script running that has it echo a weather-related message at irregular intervals."
|
||||
|
||||
def update_weather(self):
|
||||
def update_weather(self, *args, **kwargs):
|
||||
"""
|
||||
Called by the tickerhandler at regular intervals. Even so, we
|
||||
only update 20% of the time, picking a random weather message
|
||||
when we do.
|
||||
when we do. The tickerhandler requires that this hook accepts
|
||||
any arguments and keyword arguments (hence the *args, **kwargs
|
||||
even though we don't actually use them in this example)
|
||||
"""
|
||||
if random.random() < 0.2:
|
||||
# only update 20 % of the time
|
||||
|
|
@ -168,7 +334,7 @@ class WeatherRoom(TutorialRoom):
|
|||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Dark Room - a scripted room
|
||||
# Dark Room - a room with states
|
||||
#
|
||||
# This room limits the movemenets of its denizens unless they carry an active
|
||||
# LightSource object (LightSource is defined in
|
||||
|
|
@ -395,7 +561,10 @@ class TeleportRoom(TutorialRoom):
|
|||
Important attributes (set at creation):
|
||||
puzzle_key - which attr to look for on character
|
||||
puzzle_value - what char.db.puzzle_key must be set to
|
||||
teleport_to - where to teleport to in case of failure to match
|
||||
success_teleport_to - where to teleport in case if success
|
||||
success_teleport_msg - message to echo while teleporting to success
|
||||
failure_teleport_to - where to teleport to in case of failure
|
||||
failure_teleport_msg - message to echo while teleporting to failure
|
||||
|
||||
"""
|
||||
def at_object_creation(self):
|
||||
|
|
@ -405,8 +574,10 @@ class TeleportRoom(TutorialRoom):
|
|||
self.db.puzzle_value = 1
|
||||
# target of successful teleportation. Can be a dbref or a
|
||||
# unique room name.
|
||||
self.db.success_teleport_msg = "You are successful!"
|
||||
self.db.success_teleport_to = "treasure room"
|
||||
# the target of the failure teleportation.
|
||||
self.db.failure_teleport_msg = "You fail!"
|
||||
self.db.failure_teleport_to = "dark cell"
|
||||
|
||||
def at_object_receive(self, character, source_location):
|
||||
|
|
@ -417,14 +588,10 @@ class TeleportRoom(TutorialRoom):
|
|||
if not character.has_player:
|
||||
# only act on player characters.
|
||||
return
|
||||
#print character.db.puzzle_clue, self.db.puzzle_value
|
||||
if character.db.puzzle_clue != self.db.puzzle_value:
|
||||
# we didn't pass the puzzle. See if we can teleport.
|
||||
teleport_to = self.db.failure_teleport_to # this is a room name
|
||||
else:
|
||||
# passed the puzzle
|
||||
teleport_to = self.db.success_teleport_to # this is a room name
|
||||
|
||||
# determine if the puzzle is a success or not
|
||||
is_success = character.db.puzzle_clue == self.db.puzzle_value
|
||||
teleport_to = self.db.success_teleport_to if is_success else self.db.failure_teleport_to
|
||||
# note that this returns a list
|
||||
results = search_object(teleport_to)
|
||||
if not results or len(results) > 1:
|
||||
# we cannot move anywhere since no valid target was found.
|
||||
|
|
@ -434,8 +601,11 @@ class TeleportRoom(TutorialRoom):
|
|||
# superusers don't get teleported
|
||||
character.msg("Superuser block: You would have been teleported to %s." % results[0])
|
||||
return
|
||||
# the teleporter room's desc should give the 'teleporting message'.
|
||||
character.execute_cmd("look")
|
||||
# perform the teleport
|
||||
if is_success:
|
||||
character.msg(self.db.success_teleport_msg)
|
||||
else:
|
||||
character.msg(self.db.failure_teleport_msg)
|
||||
# teleport quietly to the new place
|
||||
character.move_to(results[0], quiet=True)
|
||||
|
||||
|
|
@ -468,7 +638,7 @@ class CmdEast(Command):
|
|||
when exiting east.
|
||||
- west_exit: a unique name or dbref to the room to go to
|
||||
when exiting west.
|
||||
The room must also have the following property:
|
||||
The room must also have the following Attributes
|
||||
- tutorial_bridge_posistion: the current position on
|
||||
on the bridge, 0 - 4.
|
||||
|
||||
|
|
@ -687,13 +857,8 @@ class BridgeRoom(WeatherRoom):
|
|||
self.db.fall_exit = "cliffledge"
|
||||
# add the cmdset on the room.
|
||||
self.cmdset.add_default(BridgeCmdSet)
|
||||
# information for those using the tutorial command
|
||||
self.db.tutorial_info = \
|
||||
"The bridge seems large but is actually only a " \
|
||||
"single room that assigns custom west/east commands " \
|
||||
"and a counter to determine how far across you are."
|
||||
|
||||
def update_weather(self):
|
||||
def update_weather(self, *args, **kwargs):
|
||||
"""
|
||||
This is called at irregular intervals and makes the passage
|
||||
over the bridge a little more interesting.
|
||||
|
|
@ -801,7 +966,7 @@ class OutroRoom(TutorialRoom):
|
|||
"""
|
||||
Called when the room is first created.
|
||||
"""
|
||||
super(IntroRoom, self).at_object_creation()
|
||||
super(OutroRoom, self).at_object_creation()
|
||||
self.db_tutorial_info = "The last room of the tutorial. " \
|
||||
"This cleans up all temporary Attributes " \
|
||||
"the tutorial may have assigned to the "\
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue