mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
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
|
|
@ -88,7 +88,9 @@ except (IOError, CalledProcessError):
|
|||
|
||||
def init():
|
||||
"""
|
||||
This is called only after Evennia has fully initialized all its models.
|
||||
This is called by the launcher only after Evennia has fully
|
||||
initialized all its models. It sets up the API in a safe
|
||||
environment where all models are available already.
|
||||
"""
|
||||
def imp(path, variable=True):
|
||||
"Helper function"
|
||||
|
|
@ -97,14 +99,14 @@ def init():
|
|||
mod, fromlist = path.rsplit('.', 1)
|
||||
return __import__(mod, fromlist=[fromlist])
|
||||
|
||||
global DefaultPlayer, DefaultObject, DefaultGuest, DefaultCharacter, \
|
||||
DefaultRoom, DefaultExit, DefaultChannel, DefaultScript
|
||||
global DefaultPlayer, DefaultObject, DefaultGuest, DefaultCharacter
|
||||
global DefaultRoom, DefaultExit, DefaultChannel, DefaultScript
|
||||
global ObjectDB, PlayerDB, ScriptDB, ChannelDB, Msg
|
||||
global Command, CmdSet, default_cmds, syscmdkeys
|
||||
global search_object, search_script, search_player, search_channel, search_help
|
||||
global create_object, create_script, create_player, create_channel, create_message
|
||||
global lockfuncs, tickerhandler, logger, utils, gametime, ansi, spawn, managers
|
||||
global contrib
|
||||
global lockfuncs, logger, utils, gametime, ansi, spawn, managers
|
||||
global contrib, TICKER_HANDLER, OOB_HANDLER, SESSION_HANDLER, CHANNEL_HANDLER
|
||||
|
||||
from players.players import DefaultPlayer
|
||||
from players.players import DefaultGuest
|
||||
|
|
|
|||
|
|
@ -146,6 +146,7 @@ def get_and_merge_cmdsets(caller, session, player, obj,
|
|||
yield [lobj.cmdset.current for lobj in local_objlist
|
||||
if (lobj.cmdset.current and
|
||||
lobj.locks.check(caller, 'call', no_superuser_bypass=True))]
|
||||
print "local_obj_cmdsets:", [c.key for c in local_obj_cmdsets]
|
||||
for cset in local_obj_cmdsets:
|
||||
#This is necessary for object sets, or we won't be able to
|
||||
# separate the command sets from each other in a busy room.
|
||||
|
|
@ -209,6 +210,7 @@ def get_and_merge_cmdsets(caller, session, player, obj,
|
|||
if cmdsets:
|
||||
# faster to do tuple on list than to build tuple directly
|
||||
mergehash = tuple([id(cmdset) for cmdset in cmdsets])
|
||||
print "mergehash:", mergehash, mergehash in _CMDSET_MERGE_CACHE, [(c.key, c.priority) for c in cmdsets]
|
||||
if mergehash in _CMDSET_MERGE_CACHE:
|
||||
# cached merge exist; use that
|
||||
cmdset = _CMDSET_MERGE_CACHE[mergehash]
|
||||
|
|
|
|||
|
|
@ -694,11 +694,11 @@ class CmdStateCC(MuxCommand):
|
|||
|
||||
class CmdStateJJ(MuxCommand):
|
||||
"""
|
||||
j <command number>
|
||||
jj <command number>
|
||||
|
||||
Jump to specific command number
|
||||
"""
|
||||
key = "j"
|
||||
key = "jj"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
|
|
|
|||
|
|
@ -54,65 +54,33 @@ class CmdLook(MuxCommand):
|
|||
locks = "cmd:all()"
|
||||
arg_regex = r"\s|$"
|
||||
|
||||
# we split up the functionality of Look a little
|
||||
# since this is a command which is very common to
|
||||
# overload; this makes it easy to overload different
|
||||
# sections of it without overloading all.
|
||||
def at_found_target(self, target):
|
||||
"""
|
||||
Called when a target object has been found to look at.
|
||||
|
||||
Args:
|
||||
target (Object): object found to look at (args have
|
||||
been parsed and searched for already at this point)
|
||||
|
||||
The default implementation calls the return_appearance hook on
|
||||
the observed target object to have it describe itself (and
|
||||
possibly its contents) as well as format the description into
|
||||
a suitable form.
|
||||
"""
|
||||
caller = self.caller
|
||||
if not hasattr(target, 'return_appearance'):
|
||||
# this is likely due to us having a player instead
|
||||
target = target.character
|
||||
if not target.access(caller, "view"):
|
||||
# no permission to view this object - act as if
|
||||
# it was not found at all.
|
||||
caller.msg("Could not find '%s'." % self.args)
|
||||
return
|
||||
# get object's appearance
|
||||
self.caller.msg(target.return_appearance(caller))
|
||||
# the object's at_desc() method.
|
||||
target.at_desc(looker=caller)
|
||||
|
||||
def at_not_found_target(self):
|
||||
"""
|
||||
Called when no target object was found to look at.
|
||||
"""
|
||||
if not self.args:
|
||||
# this means we tried to look at location but failed. It
|
||||
# usually means we are OOC.
|
||||
return self.caller.msg("You have no location to look at!")
|
||||
# otherwise we just return quietly.
|
||||
return
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
Handle the looking.
|
||||
"""
|
||||
caller = self.caller
|
||||
if self.args:
|
||||
target = caller.search(self.args, use_nicks=True)
|
||||
if target:
|
||||
return self.at_found_target(target)
|
||||
else:
|
||||
return self.at_not_found_target()
|
||||
args = self.args
|
||||
if args:
|
||||
# Use search to handle duplicate/nonexistant results.
|
||||
looking_at_obj = caller.search(args, use_nicks=True)
|
||||
if not looking_at_obj:
|
||||
return
|
||||
else:
|
||||
target = caller.location
|
||||
if target:
|
||||
return self.at_found_target(target)
|
||||
else:
|
||||
return self.at_not_found_target()
|
||||
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 CmdNick(MuxCommand):
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -9,13 +9,55 @@ import random
|
|||
|
||||
from evennia import TICKER_HANDLER
|
||||
from evennia import search_object
|
||||
from evennia import Command, CmdSet
|
||||
from evennia.contrib.tutorial_world import objects as tut_objects
|
||||
|
||||
|
||||
class CmdMobOnOff(Command):
|
||||
"""
|
||||
Activates/deactivates Mob
|
||||
|
||||
Usage:
|
||||
mobon <mob>
|
||||
moboff <mob>
|
||||
|
||||
This turns the mob from active (alive) mode
|
||||
to inactive (dead) mode. It is used during
|
||||
building to activate the mob once it's
|
||||
prepared.
|
||||
"""
|
||||
key = "mobon"
|
||||
aliases = "moboff"
|
||||
locks = "cmd:superuser()"
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
Uses the mob's set_alive/set_dead methods
|
||||
to turn on/off the mob."
|
||||
"""
|
||||
if not self.args:
|
||||
self.caller.msg("Usage: mobon|moboff <mob>")
|
||||
return
|
||||
mob = self.caller.search(self.args)
|
||||
if not mob:
|
||||
return
|
||||
if self.cmdname == "mobon":
|
||||
mob.set_alive()
|
||||
else:
|
||||
mob.set_dead()
|
||||
|
||||
|
||||
class MobCmdSet(CmdSet):
|
||||
"""
|
||||
Holds the admin command controlling the mob
|
||||
"""
|
||||
def at_cmdset_creation(self):
|
||||
self.add(CmdMobOnOff())
|
||||
|
||||
class Mob(tut_objects.TutorialObject):
|
||||
"""
|
||||
This is a state-machine AI mobile. It has several states which
|
||||
are controlled from setting various Attributes:
|
||||
This is a state-machine AI mobile. It has several states which are
|
||||
controlled from setting various Attributes. All default to True:
|
||||
|
||||
patrolling: if set, the mob will move randomly
|
||||
from room to room, but preferring to not return
|
||||
|
|
@ -30,18 +72,23 @@ class Mob(tut_objects.TutorialObject):
|
|||
to flee from it, so it can enter combat. If unset,
|
||||
it will return to patrolling/idling if fled from.
|
||||
immortal: If set, the mob cannot take any damage.
|
||||
It also has several states,
|
||||
is_patrolling - set when the mob is patrolling.
|
||||
is_attacking - set when the mob is in combat
|
||||
is_hunting - set when the mob is pursuing an enemy.
|
||||
is_immortal - is currently immortal
|
||||
is_dead: if set, the Mob is set to immortal, non-patrolling
|
||||
and non-aggressive mode. Its description is
|
||||
turned into that of a corpse-description.
|
||||
Other important properties:
|
||||
home - the home location should set to someplace inside
|
||||
the patrolling area. The mob will use this if it should
|
||||
happen to roam into a room with no exits.
|
||||
irregular_echoes: list of strings the mob generates at irregular intervals.
|
||||
desc_alive: the physical description while alive
|
||||
desc_dead: the physical descripion while dead
|
||||
send_defeated_to: unique key/alias for location to send defeated enemies to
|
||||
defeat_msg: message to echo to defeated opponent
|
||||
defeat_msg_room: message to echo to room. Accepts %s as the name of the defeated.
|
||||
hit_msg: message to echo when this mob is hit. Accepts %s for the mob's key.
|
||||
weapon_ineffective_msg: message to echo for useless attacks
|
||||
death_msg: message to echo to room when this mob dies.
|
||||
patrolling_pace: how many seconds per tick, when patrolling
|
||||
aggressive_pace: -"- attacking
|
||||
hunting_pace: -"- hunting
|
||||
death_pace: -"- returning to life when dead
|
||||
|
||||
field 'home' - the home location should set to someplace inside
|
||||
the patrolling area. The mob will use this if it should
|
||||
happen to roam into a room with no exits.
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
|
|
@ -60,10 +107,11 @@ class Mob(tut_objects.TutorialObject):
|
|||
Called the first time the object is created.
|
||||
We set up the base properties and flags here.
|
||||
"""
|
||||
self.cmdsets.add(MobCmdSet, permanent=True)
|
||||
# Main AI flags. We start in dead mode so we don't have to
|
||||
# chase the mob around when building.
|
||||
self.db.patrolling = False
|
||||
self.db.aggressive = False
|
||||
self.db.patrolling = True
|
||||
self.db.aggressive = True
|
||||
self.db.immortal = True
|
||||
# db-store if it is dead or not
|
||||
self.db.is_dead = True
|
||||
|
|
@ -99,10 +147,11 @@ class Mob(tut_objects.TutorialObject):
|
|||
# text to echo to the defeated foe.
|
||||
self.db.defeat_msg = "You fall to the ground."
|
||||
self.db.defeat_msg_room = "%s falls to the ground."
|
||||
self.db.weapon_ineffective_text = "Your weapon just passes through your enemy, causing almost no effect!"
|
||||
self.db.weapon_ineffective_msg = "Your weapon just passes through your enemy, causing almost no effect!"
|
||||
|
||||
self.db.death_msg = "After the last hit %s evaporates." % self.key
|
||||
self.db.hit_msg = "%s wails, shudders and writhes." % self.key
|
||||
self.db.irregular_msgs = ["the enemy looks about.", "the enemy changes stance."]
|
||||
|
||||
self.db.tutorial_info = "This is an object with simple state AI, using a ticker to move."
|
||||
|
||||
|
|
@ -127,7 +176,7 @@ class Mob(tut_objects.TutorialObject):
|
|||
previous ticker subscription so that we can
|
||||
easily find and stop it before setting a
|
||||
new one. The tickerhandler is persistent so
|
||||
we need to remmeber this across reloads.
|
||||
we need to remember this across reloads.
|
||||
|
||||
"""
|
||||
idstring = "tutorial_mob" # this doesn't change
|
||||
|
|
@ -155,7 +204,7 @@ class Mob(tut_objects.TutorialObject):
|
|||
if obj.has_player and not obj.is_superuser]
|
||||
return targets[0] if targets else None
|
||||
|
||||
def set_alive(self):
|
||||
def set_alive(self, *args, **kwargs):
|
||||
"""
|
||||
Set the mob to "alive" mode. This effectively
|
||||
resurrects it from the dead state.
|
||||
|
|
@ -232,7 +281,7 @@ class Mob(tut_objects.TutorialObject):
|
|||
self.ndb.is_hunting = False
|
||||
self.ndb.is_attacking = True
|
||||
|
||||
def do_patrol(self):
|
||||
def do_patrol(self, *args, **kwargs):
|
||||
"""
|
||||
Called repeatedly during patrolling mode. In this mode, the
|
||||
mob scans its surroundings and randomly chooses a viable exit.
|
||||
|
|
@ -240,6 +289,8 @@ class Mob(tut_objects.TutorialObject):
|
|||
order to block the mob from moving outside its area while
|
||||
allowing player-controlled characters to move normally.
|
||||
"""
|
||||
if random.random() < 0.01:
|
||||
self.location.msg_contents(random.choice(self.db.irregular_msgs))
|
||||
if self.db.aggressive:
|
||||
# first check if there are any targets in the room.
|
||||
target = self._find_target(self.location)
|
||||
|
|
@ -263,12 +314,14 @@ class Mob(tut_objects.TutorialObject):
|
|||
# no exits! teleport to home to get away.
|
||||
self.move_to(self.home)
|
||||
|
||||
def do_hunting(self):
|
||||
def do_hunting(self, *args, **kwargs):
|
||||
"""
|
||||
Called regularly when in hunting mode. In hunting mode the mob
|
||||
scans adjacent rooms for enemies and moves towards them to
|
||||
attack if possible.
|
||||
"""
|
||||
if random.random() < 0.01:
|
||||
self.location.msg_contents(random.choice(self.db.irregular_msgs))
|
||||
if self.db.aggressive:
|
||||
# first check if there are any targets in the room.
|
||||
target = self._find_target(self.location)
|
||||
|
|
@ -293,12 +346,14 @@ class Mob(tut_objects.TutorialObject):
|
|||
# no exits! teleport to home to get away.
|
||||
self.move_to(self.home)
|
||||
|
||||
def do_attacking(self):
|
||||
def do_attacking(self, *args, **kwargs):
|
||||
"""
|
||||
Called regularly when in attacking mode. In attacking mode
|
||||
the mob will bring its weapons to bear on any targets
|
||||
in the room.
|
||||
"""
|
||||
if random.random() < 0.01:
|
||||
self.location.msg_contents(random.choice(self.db.irregular_msgs))
|
||||
# first make sure we have a target
|
||||
target = self._find_target(self.location)
|
||||
if not target:
|
||||
|
|
|
|||
|
|
@ -19,11 +19,9 @@ WeaponRack
|
|||
|
||||
"""
|
||||
|
||||
import time
|
||||
import random
|
||||
|
||||
from evennia import create_object
|
||||
from evennia import DefaultObject, DefaultExit, Command, CmdSet, DefaultScript
|
||||
from evennia import DefaultObject, DefaultExit, Command, CmdSet
|
||||
from evennia import utils
|
||||
from evennia.utils.spawner import spawn
|
||||
|
||||
|
|
@ -171,8 +169,8 @@ class CmdClimb(Command):
|
|||
if not ostring:
|
||||
ostring = "You climb %s. Having looked around, you climb down again." % self.obj.name
|
||||
self.caller.msg(ostring)
|
||||
# store this object to remember what we last climbed
|
||||
self.caller.db.last_climbed = self.obj
|
||||
# set a tag on the caller to remember that we climbed.
|
||||
self.caller.tags.add("tutorial_climbed_tree")
|
||||
|
||||
|
||||
class CmdSetClimbable(CmdSet):
|
||||
|
|
@ -611,8 +609,6 @@ class CrumblingWall(TutorialObject, DefaultExit):
|
|||
# if its location is lit and only traverse it once the Attribute
|
||||
# exit_open is set to True.
|
||||
self.locks.add("cmd:locattr(is_lit);traverse:objattr(exit_open)")
|
||||
|
||||
self.db.tutorial_info = "This is an Exit with a conditional traverse-lock. Try to shift the roots around."
|
||||
# set cmdset
|
||||
self.cmdset.add(CmdSetCrumblingWall, permanent=True)
|
||||
|
||||
|
|
@ -671,7 +667,8 @@ class CrumblingWall(TutorialObject, DefaultExit):
|
|||
# puzzle not solved yet.
|
||||
string = "The wall is old and covered with roots that here and there have permeated the stone. " \
|
||||
"The roots (or whatever they are - some of them are covered in small non-descript flowers) " \
|
||||
"crisscross the wall, making it hard to clearly see its stony surface.\n"
|
||||
"crisscross the wall, making it hard to clearly see its stony surface. Maybe you could " \
|
||||
"try to {wshift{n or {wmove{n them.\n"
|
||||
# display the root positions to help with the puzzle
|
||||
for key, pos in self.db.root_pos.items():
|
||||
string += "\n" + self._translate_position(key, pos)
|
||||
|
|
@ -851,7 +848,7 @@ class Weapon(TutorialObject):
|
|||
super(Weapon, self).at_object_creation()
|
||||
self.db.hit = 0.4 # hit chance
|
||||
self.db.parry = 0.8 # parry chance
|
||||
self.db.damage = 8.0
|
||||
self.db.damage = 1.0
|
||||
self.db.magic = False
|
||||
self.cmdset.add_default(CmdSetWeapon, permanent=True)
|
||||
|
||||
|
|
@ -1007,6 +1004,13 @@ class WeaponRack(TutorialObject):
|
|||
on it. This will also set a property on the character
|
||||
to make sure they can't get more than one at a time.
|
||||
|
||||
Attributes to set on this object:
|
||||
available_weapons: list of prototype-keys from
|
||||
WEAPON_PROTOTYPES, the weapons available in this rack.
|
||||
no_more_weapons_msg - error message to return to players
|
||||
who already got one weapon from the rack and tries to
|
||||
grab another one.
|
||||
|
||||
"""
|
||||
def at_object_creation(self):
|
||||
"""
|
||||
|
|
@ -1016,21 +1020,27 @@ class WeaponRack(TutorialObject):
|
|||
self.db.rack_id = "weaponrack_1"
|
||||
# these are prototype names from the prototype
|
||||
# dictionary above.
|
||||
self.db.get_weapon_msg = "You pull %s from the rack."
|
||||
self.db.no_more_weapons_msg = "%s has no more to offer you." % self.key
|
||||
self.db.available_weapons = ["knife", "rusty_dagger",
|
||||
"sword", "club"]
|
||||
|
||||
def produce_weapon(self, caller):
|
||||
"""
|
||||
This will produce a new weapon from the rack,
|
||||
assuming the caller hasn't already gotten one.
|
||||
assuming the caller hasn't already gotten one. When
|
||||
doing so, the caller will get Tagged with the id
|
||||
of this rack, to make sure they cannot keep
|
||||
pulling weapons from it indefinitely.
|
||||
"""
|
||||
if caller.attributes.get(self.db.rack_id):
|
||||
rack_id = self.db.rack_id
|
||||
if caller.tags.get(rack_id):
|
||||
caller.msg("%s has no more to offer you." % self.key)
|
||||
else:
|
||||
prototype = random.choice(self.db.available_weapons)
|
||||
# use the spawner to create a new Weapon from the
|
||||
# spawner dictionary
|
||||
# spawner dictionary, tag the caller
|
||||
wpn = spawn(WEAPON_PROTOTYPES[prototype], prototype_parents=WEAPON_PROTOTYPES)
|
||||
caller.attributes.add(self.db.rack_id, True)
|
||||
caller.tags.add(rack_id)
|
||||
wpn.location = caller
|
||||
caller.msg("You grab %s - %s." % (wpn.key, wpn.db.desc))
|
||||
caller.msg(self.db.weapon_msg % wpn.key)
|
||||
|
|
|
|||
|
|
@ -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 "\
|
||||
|
|
|
|||
|
|
@ -422,6 +422,27 @@ def attr_ne(accessing_obj, accessed_obj, *args, **kwargs):
|
|||
"""
|
||||
return attr(accessing_obj, accessed_obj, *args, **{'compare': 'ne'})
|
||||
|
||||
def tag(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
tag(tagkey)
|
||||
tag(tagkey, category)
|
||||
|
||||
Only true if accessing_obj has the specified tag and optional
|
||||
category
|
||||
"""
|
||||
return accessing_obj.tags.get(*args)
|
||||
|
||||
def objtag(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
objtag(tagkey)
|
||||
objtag(tagkey, category)
|
||||
|
||||
Only true if accessed_obj has the specified tag and optional
|
||||
category.
|
||||
"""
|
||||
return accessed_obj.tags.get(*args)
|
||||
|
||||
def inside(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -268,7 +268,8 @@ class TickerHandler(object):
|
|||
the same time interval
|
||||
hook_key (str, optional): The name of the hook method
|
||||
on `obj` to call every `interval` seconds. Defaults to
|
||||
`at_tick()`.
|
||||
`at_tick(*args, **kwargs`. All hook methods must
|
||||
always accept *args, **kwargs.
|
||||
args, kwargs (optional): These will be passed into the
|
||||
method given by `hook_key` every time it is called.
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import textwrap
|
|||
import datetime
|
||||
import random
|
||||
import traceback
|
||||
from subprocess import check_output
|
||||
from importlib import import_module
|
||||
from inspect import ismodule, trace
|
||||
from collections import defaultdict
|
||||
|
|
@ -967,7 +966,8 @@ def class_from_module(path, defaultpaths=None):
|
|||
err += "."
|
||||
raise ImportError(err)
|
||||
return cls
|
||||
|
||||
# alias
|
||||
object_from_module = class_from_module
|
||||
|
||||
def init_new_player(player):
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue