Reworked tutorial world objects, starting with mob.
This commit is contained in:
parent
0029232ab0
commit
32fd9d2a4d
3 changed files with 386 additions and 296 deletions
|
|
@ -41,6 +41,9 @@ class Mob(tut_objects.TutorialObject):
|
||||||
self.db.last_location = None
|
self.db.last_location = None
|
||||||
# only when True will the mob move.
|
# only when True will the mob move.
|
||||||
self.db.roam_mode = True
|
self.db.roam_mode = True
|
||||||
|
#
|
||||||
|
self.db.move_from
|
||||||
|
self.location.msg_contents("With a cold breeze, %s drifts in the direction of %s." % (self.key, destination.key))
|
||||||
|
|
||||||
def announce_move_from(self, destination):
|
def announce_move_from(self, destination):
|
||||||
"Called just before moving"
|
"Called just before moving"
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ import random
|
||||||
|
|
||||||
from evennia import create_object
|
from evennia import create_object
|
||||||
from evennia import DefaultObject, DefaultExit, Command, CmdSet, DefaultScript
|
from evennia import DefaultObject, DefaultExit, Command, CmdSet, DefaultScript
|
||||||
|
from evennia import utils
|
||||||
|
from evennia.utils.spawner import spawn
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -31,7 +33,7 @@ from evennia import DefaultObject, DefaultExit, Command, CmdSet, DefaultScript
|
||||||
#
|
#
|
||||||
# The TutorialObject is the base class for all items
|
# The TutorialObject is the base class for all items
|
||||||
# in the tutorial. They have an attribute "tutorial_info"
|
# in the tutorial. They have an attribute "tutorial_info"
|
||||||
# on them that a global tutorial command can use to extract
|
# on them that the global tutorial command can use to extract
|
||||||
# interesting behind-the scenes information about the object.
|
# interesting behind-the scenes information about the object.
|
||||||
#
|
#
|
||||||
# TutorialObjects may also be "reset". What the reset means
|
# TutorialObjects may also be "reset". What the reset means
|
||||||
|
|
@ -60,16 +62,20 @@ class TutorialObject(DefaultObject):
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Readable - an object one can "read".
|
# Readable - an object that can be "read"
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
#
|
||||||
|
# Read command
|
||||||
|
#
|
||||||
|
|
||||||
class CmdRead(Command):
|
class CmdRead(Command):
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
read [obj]
|
read [obj]
|
||||||
|
|
||||||
Read some text.
|
Read some text of a readable object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "read"
|
key = "read"
|
||||||
|
|
@ -77,7 +83,11 @@ class CmdRead(Command):
|
||||||
help_category = "TutorialWorld"
|
help_category = "TutorialWorld"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implement the read command."
|
"""
|
||||||
|
Implements the read command. This simply looks for an
|
||||||
|
Attribute "readable_text" on the object and displays that.
|
||||||
|
"""
|
||||||
|
|
||||||
if self.args:
|
if self.args:
|
||||||
obj = self.caller.search(self.args.strip())
|
obj = self.caller.search(self.args.strip())
|
||||||
else:
|
else:
|
||||||
|
|
@ -94,18 +104,25 @@ class CmdRead(Command):
|
||||||
|
|
||||||
|
|
||||||
class CmdSetReadable(CmdSet):
|
class CmdSetReadable(CmdSet):
|
||||||
"CmdSet for readables"
|
"""
|
||||||
|
A CmdSet for readables.
|
||||||
|
"""
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"called when object is created."
|
"""
|
||||||
|
Called when the cmdset is created.
|
||||||
|
"""
|
||||||
self.add(CmdRead())
|
self.add(CmdRead())
|
||||||
|
|
||||||
|
|
||||||
class Readable(TutorialObject):
|
class Readable(TutorialObject):
|
||||||
"""
|
"""
|
||||||
This object defines some attributes and defines a read method on itself.
|
This simple object defines some attributes and
|
||||||
"""
|
"""
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"Called when object is created"
|
"""
|
||||||
|
Called when object is created. We make sure to set the needed
|
||||||
|
Attribute and add the readable cmdset.
|
||||||
|
"""
|
||||||
super(Readable, self).at_object_creation()
|
super(Readable, self).at_object_creation()
|
||||||
self.db.tutorial_info = "This is an object with a 'read' command defined in a command set on itself."
|
self.db.tutorial_info = "This is an object with a 'read' command defined in a command set on itself."
|
||||||
self.db.readable_text = "There is no text written on %s." % self.key
|
self.db.readable_text = "There is no text written on %s." % self.key
|
||||||
|
|
@ -119,14 +136,20 @@ class Readable(TutorialObject):
|
||||||
#
|
#
|
||||||
# The climbable object works so that once climbed, it sets
|
# The climbable object works so that once climbed, it sets
|
||||||
# a flag on the climber to show that it was climbed. A simple
|
# a flag on the climber to show that it was climbed. A simple
|
||||||
# command 'climb' handles the actual climbing.
|
# command 'climb' handles the actual climbing. The memory
|
||||||
|
# of what was last climbed is used in a simple puzzle in the
|
||||||
|
# tutorial.
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
||||||
class CmdClimb(Command):
|
class CmdClimb(Command):
|
||||||
"""
|
"""
|
||||||
|
Climb an object
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
climb <object>
|
climb <object>
|
||||||
|
|
||||||
|
This allows you to climb.
|
||||||
"""
|
"""
|
||||||
key = "climb"
|
key = "climb"
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
|
|
@ -148,6 +171,7 @@ class CmdClimb(Command):
|
||||||
if not ostring:
|
if not ostring:
|
||||||
ostring = "You climb %s. Having looked around, you climb down again." % self.obj.name
|
ostring = "You climb %s. Having looked around, you climb down again." % self.obj.name
|
||||||
self.caller.msg(ostring)
|
self.caller.msg(ostring)
|
||||||
|
# store this object to remember what we last climbed
|
||||||
self.caller.db.last_climbed = self.obj
|
self.caller.db.last_climbed = self.obj
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -159,7 +183,10 @@ class CmdSetClimbable(CmdSet):
|
||||||
|
|
||||||
|
|
||||||
class Climbable(TutorialObject):
|
class Climbable(TutorialObject):
|
||||||
"A climbable object."
|
"""
|
||||||
|
A climbable object. All that is special about it is that it has
|
||||||
|
the "climb" command available on it.
|
||||||
|
"""
|
||||||
|
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"Called at initial creation only"
|
"Called at initial creation only"
|
||||||
|
|
@ -171,21 +198,19 @@ class Climbable(TutorialObject):
|
||||||
#
|
#
|
||||||
# Obelisk - a unique item
|
# Obelisk - a unique item
|
||||||
#
|
#
|
||||||
# The Obelisk is an object with a modified return_appearance
|
# The Obelisk is an object with a modified return_appearance method
|
||||||
# method that causes it to look slightly different every
|
# that causes it to look slightly different every time one looks at it.
|
||||||
# time one looks at it. Since what you actually see
|
# Since what you actually see is a part of a game puzzle, the act of
|
||||||
# is a part of a game puzzle, the act of looking also
|
# looking also stores a key attribute on the looking object (different
|
||||||
# stores a key attribute on the looking object for later
|
# depending on which text you saw) for later reference.
|
||||||
# reference.
|
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
||||||
OBELISK_DESCS = ["You can briefly make out the image of {ba woman with a blue bird{n.",
|
OBELISK_DESCS = ("You can briefly make out the image of {ba woman with a blue bird{n.",
|
||||||
"You for a moment see the visage of {ba woman on a horse{n.",
|
"You for a moment see the visage of {ba woman on a horse{n.",
|
||||||
"For the briefest moment you make out an engraving of {ba regal woman wearing a crown{n.",
|
"For the briefest moment you make out an engraving of {ba regal woman wearing a crown{n.",
|
||||||
"You think you can see the outline of {ba flaming shield{n in the stone.",
|
"You think you can see the outline of {ba flaming shield{n in the stone.",
|
||||||
"The surface for a moment seems to portray {ba woman fighting a beast{n."]
|
"The surface for a moment seems to portray {ba woman fighting a beast{n.")
|
||||||
|
|
||||||
|
|
||||||
class Obelisk(TutorialObject):
|
class Obelisk(TutorialObject):
|
||||||
"""
|
"""
|
||||||
|
|
@ -200,15 +225,22 @@ class Obelisk(TutorialObject):
|
||||||
self.locks.add("get:false()")
|
self.locks.add("get:false()")
|
||||||
|
|
||||||
def return_appearance(self, caller):
|
def return_appearance(self, caller):
|
||||||
"Overload the default version of this hook."
|
"""
|
||||||
|
This hook is called by the look command to get the description
|
||||||
|
of the object. We overload it with our own version.
|
||||||
|
"""
|
||||||
|
# randomly get the index for one of the descriptions
|
||||||
clueindex = random.randint(0, len(OBELISK_DESCS) - 1)
|
clueindex = random.randint(0, len(OBELISK_DESCS) - 1)
|
||||||
# set this description
|
# set this description, with the random extra
|
||||||
string = "The surface of the obelisk seem to waver, shift and writhe under your gaze, with "
|
string = "The surface of the obelisk seem to waver, shift and writhe under your gaze, with " \
|
||||||
string += "different scenes and structures appearing whenever you look at it. "
|
"different scenes and structures appearing whenever you look at it. "
|
||||||
self.db.desc = string + OBELISK_DESCS[clueindex]
|
self.db.desc = string + OBELISK_DESCS[clueindex]
|
||||||
# remember that this was the clue we got.
|
# remember that this was the clue we got. The Puzzle room will
|
||||||
|
# look for this later to determine if you should be teleported
|
||||||
|
# or not.
|
||||||
caller.db.puzzle_clue = clueindex
|
caller.db.puzzle_clue = clueindex
|
||||||
# call the parent function as normal (this will use db.desc we just set)
|
# call the parent function as normal (this will use
|
||||||
|
# the new desc Attribute we just set)
|
||||||
return super(Obelisk, self).return_appearance(caller)
|
return super(Obelisk, self).return_appearance(caller)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -216,164 +248,118 @@ class Obelisk(TutorialObject):
|
||||||
#
|
#
|
||||||
# LightSource
|
# LightSource
|
||||||
#
|
#
|
||||||
# This object that emits light and can be
|
# This object emits light. Once it has been turned on it
|
||||||
# turned on or off. It must be carried to use and has only
|
# cannot be turned off. When it burns out it will delete
|
||||||
# a limited burn-time.
|
# itself.
|
||||||
# When burned out, it will remove itself from the carrying
|
#
|
||||||
# character's inventory.
|
# This could be implemented using a single-repeat Script or by
|
||||||
|
# registering with the TickerHandler. We do it simpler by
|
||||||
|
# using the delay() utility function. This is very simple
|
||||||
|
# to use but does not survive a server @reload. Because of
|
||||||
|
# where the light matters (in the Dark Room where you can
|
||||||
|
# find new light sources easily), this is okay here.
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
||||||
class StateLightSourceOn(DefaultScript):
|
class CmdLight(Command):
|
||||||
"""
|
"""
|
||||||
This script controls how long the light source is burning. When
|
Creates light where there was none. Something to burn.
|
||||||
it runs out of fuel, the lightsource goes out.
|
|
||||||
"""
|
|
||||||
def at_script_creation(self):
|
|
||||||
"Called at creation of script."
|
|
||||||
self.key = "lightsourceBurn"
|
|
||||||
self.desc = "Keeps lightsources burning."
|
|
||||||
self.start_delay = True # only fire after self.interval s.
|
|
||||||
self.repeats = 1 # only run once.
|
|
||||||
self.persistent = True # survive a server reboot.
|
|
||||||
|
|
||||||
def at_start(self):
|
|
||||||
"Called at script start - this can also happen if server is restarted."
|
|
||||||
self.interval = self.obj.db.burntime
|
|
||||||
self.db.script_started = time.time()
|
|
||||||
|
|
||||||
def at_repeat(self):
|
|
||||||
"Called at self.interval seconds"
|
|
||||||
# this is only called when torch has burnt out
|
|
||||||
self.obj.db.burntime = -1
|
|
||||||
self.obj.reset()
|
|
||||||
|
|
||||||
def at_stop(self):
|
|
||||||
"""
|
|
||||||
Since the user may also turn off the light
|
|
||||||
prematurely, this hook will store the current
|
|
||||||
burntime.
|
|
||||||
"""
|
|
||||||
# calculate remaining burntime, if object is not
|
|
||||||
# already deleted (because it burned out)
|
|
||||||
if self.obj:
|
|
||||||
try:
|
|
||||||
time_burnt = time.time() - self.db.script_started
|
|
||||||
except TypeError:
|
|
||||||
# can happen if script_started is not defined
|
|
||||||
time_burnt = self.interval
|
|
||||||
burntime = self.interval - time_burnt
|
|
||||||
self.obj.db.burntime = burntime
|
|
||||||
|
|
||||||
def is_valid(self):
|
|
||||||
"This script is only valid as long as the lightsource burns."
|
|
||||||
return self.obj.db.is_active
|
|
||||||
|
|
||||||
|
|
||||||
class CmdLightSourceOn(Command):
|
|
||||||
"""
|
|
||||||
Switches on the lightsource.
|
|
||||||
"""
|
"""
|
||||||
key = "on"
|
key = "on"
|
||||||
aliases = ["switch on", "turn on", "light"]
|
aliases = ["light", "burn"]
|
||||||
locks = "cmd:holds()" # only allow if command.obj is carried by caller.
|
# only allow this command if command.obj is carried by caller.
|
||||||
|
locks = "cmd:holds()"
|
||||||
help_category = "TutorialWorld"
|
help_category = "TutorialWorld"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implements the command"
|
"""
|
||||||
|
Implements the light command. Since this command is designed
|
||||||
|
to sit on a "lightable" object, we operate only on self.obj.
|
||||||
|
"""
|
||||||
|
|
||||||
if self.obj.db.is_active:
|
if self.obj.light():
|
||||||
self.caller.msg("%s is already burning." % self.obj.key)
|
self.caller("You light %s." % self.obj.key)
|
||||||
else:
|
|
||||||
# set lightsource to active
|
|
||||||
self.obj.db.is_active = True
|
|
||||||
# activate the script to track burn-time.
|
|
||||||
self.obj.scripts.add(StateLightSourceOn)
|
|
||||||
self.caller.msg("{gYou light {C%s.{n" % self.obj.key)
|
|
||||||
self.caller.location.msg_contents("%s lights %s!" % (self.caller, self.obj.key), exclude=[self.caller])
|
self.caller.location.msg_contents("%s lights %s!" % (self.caller, self.obj.key), exclude=[self.caller])
|
||||||
# run script validation on the room to make light/dark states tick.
|
|
||||||
self.caller.location.scripts.validate()
|
|
||||||
# look around
|
|
||||||
self.caller.execute_cmd("look")
|
|
||||||
|
|
||||||
|
|
||||||
class CmdLightSourceOff(Command):
|
|
||||||
"""
|
|
||||||
Switch off the lightsource.
|
|
||||||
"""
|
|
||||||
key = "off"
|
|
||||||
aliases = ["switch off", "turn off", "dowse"]
|
|
||||||
locks = "cmd:holds()" # only allow if command.obj is carried by caller.
|
|
||||||
help_category = "TutorialWorld"
|
|
||||||
|
|
||||||
def func(self):
|
|
||||||
"Implements the command "
|
|
||||||
|
|
||||||
if not self.obj.db.is_active:
|
|
||||||
self.caller.msg("%s is not burning." % self.obj.key)
|
|
||||||
else:
|
else:
|
||||||
# set lightsource to inactive
|
self.caller.msg("%s is already burning." % self.obj.key)
|
||||||
self.obj.db.is_active = False
|
|
||||||
# validating the scripts will kill it now that is_active=False.
|
|
||||||
self.obj.scripts.validate()
|
|
||||||
self.caller.msg("{GYou dowse {C%s.{n" % self.obj.key)
|
|
||||||
self.caller.location.msg_contents("%s dowses %s." % (self.caller, self.obj.key), exclude=[self.caller])
|
|
||||||
self.caller.location.scripts.validate()
|
|
||||||
self.caller.execute_cmd("look")
|
|
||||||
|
|
||||||
|
|
||||||
class CmdSetLightSource(CmdSet):
|
class CmdSetLight(CmdSet):
|
||||||
"CmdSet for the lightsource commands"
|
"CmdSet for the lightsource commands"
|
||||||
key = "lightsource_cmdset"
|
key = "lightsource_cmdset"
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"called at cmdset creation"
|
"called at cmdset creation"
|
||||||
self.add(CmdLightSourceOn())
|
self.add(CmdLight())
|
||||||
self.add(CmdLightSourceOff())
|
|
||||||
|
|
||||||
|
|
||||||
class LightSource(TutorialObject):
|
class LightSource(TutorialObject):
|
||||||
"""
|
"""
|
||||||
This implements a light source object.
|
This implements a light source object.
|
||||||
|
|
||||||
When burned out, lightsource will be moved to its home - which by
|
When burned out, the object will be deleted.
|
||||||
default is the location it was first created at.
|
|
||||||
"""
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
If this is called with the Attribute is_giving_light already
|
||||||
|
set, we know that the timer got killed by a server
|
||||||
|
reload/reboot before it had time to finish. So we kill it here
|
||||||
|
instead. This is the price we pay for the simplicity of the
|
||||||
|
non-persistent delay() method.
|
||||||
|
"""
|
||||||
|
if self.db.is_giving_light:
|
||||||
|
self.delete()
|
||||||
|
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"Called when object is first created."
|
"Called when object is first created."
|
||||||
super(LightSource, self).at_object_creation()
|
super(LightSource, self).at_object_creation()
|
||||||
self.db.tutorial_info = "This object can be turned on off and has a timed script controlling it."
|
self.db.tutorial_info = "This object can be lit to create light. It has a timeout for how long it burns."
|
||||||
self.db.is_active = False
|
self.db.is_giving_light = False
|
||||||
self.db.burntime = 60 * 3 # 3 minutes
|
self.db.burntime = 60 * 3 # 3 minutes
|
||||||
|
# this is the default desc, it can of course be customized
|
||||||
|
# when created.
|
||||||
self.db.desc = "A splinter of wood with remnants of resin on it, enough for burning."
|
self.db.desc = "A splinter of wood with remnants of resin on it, enough for burning."
|
||||||
# add commands
|
# add the Light command
|
||||||
self.cmdset.add_default(CmdSetLightSource, permanent=True)
|
self.cmdset.add_default(CmdSetLight, permanent=True)
|
||||||
|
|
||||||
def reset(self):
|
def _burnout(self, ret):
|
||||||
"""
|
"""
|
||||||
Can be called by tutorial world runner, or by the script when
|
This is called when this light source burns out. We make no
|
||||||
the lightsource has burned out.
|
use of the return value.
|
||||||
"""
|
"""
|
||||||
if self.db.burntime <= 0:
|
|
||||||
# light burned out. Since the lightsources's "location" should be
|
|
||||||
# a character, notify them this way.
|
|
||||||
try:
|
|
||||||
loc = self.location.location
|
|
||||||
except AttributeError:
|
|
||||||
loc = self.location
|
|
||||||
loc.msg_contents("{c%s{n {Rburns out.{n" % self.key)
|
|
||||||
self.db.is_active = False
|
|
||||||
try:
|
try:
|
||||||
# validate in holders current room, if possible
|
# our location is usually a Character (their inventory), we try
|
||||||
self.location.location.scripts.validate()
|
# to send to -their- location so everyone else also notices the
|
||||||
|
# light goes out.
|
||||||
|
self.location.location.msg_contents("%s's %s flickers and dies." %
|
||||||
|
(self.location, self.key), exclude=self.location)
|
||||||
|
self.location.msg("Your %s flickers and dies." % self.key)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# maybe it was dropped, try validating at current location.
|
# we are not in someone's inventory (maybe we were dropped.)
|
||||||
try:
|
self.location.msg_contents("A %s on the floor flickers and dies." % self.key)
|
||||||
self.location.scripts.validate()
|
# delete ourselves.
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
self.delete()
|
self.delete()
|
||||||
|
|
||||||
|
def light(self):
|
||||||
|
"""
|
||||||
|
Light this object - this is called by Light command.
|
||||||
|
"""
|
||||||
|
if self.db.is_giving_light:
|
||||||
|
return False
|
||||||
|
# burn for 3 minutes before calling _burnout
|
||||||
|
self.db.is_giving_light = True
|
||||||
|
# if we are in a dark room, trigger its light check
|
||||||
|
try:
|
||||||
|
self.location.check_light_state()
|
||||||
|
except Exception:
|
||||||
|
# if we are not in a dark room, never mind
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
# start the burn timer. When it runs out, self._burnout
|
||||||
|
# will be called.
|
||||||
|
utils.delay(60 * 3, self._burnout)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -382,7 +368,7 @@ class LightSource(TutorialObject):
|
||||||
# This implements a simple puzzle exit that needs to be
|
# This implements a simple puzzle exit that needs to be
|
||||||
# accessed with commands before one can get to traverse it.
|
# accessed with commands before one can get to traverse it.
|
||||||
#
|
#
|
||||||
# The puzzle is currently simply to move roots (that have
|
# The puzzle-part is simply to move roots (that have
|
||||||
# presumably covered the wall) aside until a button for a
|
# presumably covered the wall) aside until a button for a
|
||||||
# secret door is revealed. The original position of the
|
# secret door is revealed. The original position of the
|
||||||
# roots blocks the button, so they have to be moved to a certain
|
# roots blocks the button, so they have to be moved to a certain
|
||||||
|
|
@ -398,25 +384,34 @@ class LightSource(TutorialObject):
|
||||||
# along the sides. The goal is to make the center position clear.
|
# along the sides. The goal is to make the center position clear.
|
||||||
# (yes, it's really as simple as it sounds, just move the roots
|
# (yes, it's really as simple as it sounds, just move the roots
|
||||||
# to each side to "win". This is just a tutorial, remember?)
|
# to each side to "win". This is just a tutorial, remember?)
|
||||||
|
#
|
||||||
|
# The ShiftRoot command depends on the root object having an
|
||||||
|
# Attribute root_pos (a dictionary) to describe the current
|
||||||
|
# position of the roots.
|
||||||
|
|
||||||
class CmdShiftRoot(Command):
|
class CmdShiftRoot(Command):
|
||||||
"""
|
"""
|
||||||
Shifts roots around.
|
Shifts roots around.
|
||||||
|
|
||||||
shift blue root left/right
|
Usage:
|
||||||
shift red root left/right
|
shift blue root left/right
|
||||||
shift yellow root up/down
|
shift red root left/right
|
||||||
shift green root up/down
|
shift yellow root up/down
|
||||||
|
shift green root up/down
|
||||||
|
|
||||||
"""
|
"""
|
||||||
key = "shift"
|
key = "shift"
|
||||||
aliases = ["move"]
|
aliases = ["shiftroot", "push", "pull", "move"]
|
||||||
# the locattr() lock looks for the attribute is_dark on the current room.
|
# we only allow to use this command while the
|
||||||
locks = "cmd:not locattr(is_dark)"
|
# room is properly lit, so we lock it to the
|
||||||
|
# setting of Attribute "is_lit" on our location.
|
||||||
|
locks = "cmd:locattr(is_lit)"
|
||||||
help_category = "TutorialWorld"
|
help_category = "TutorialWorld"
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
"custom parser; split input by spaces"
|
"""
|
||||||
|
Custom parser; split input by spaces for simplicity.
|
||||||
|
"""
|
||||||
self.arglist = self.args.strip().split()
|
self.arglist = self.args.strip().split()
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
|
|
@ -429,14 +424,20 @@ class CmdShiftRoot(Command):
|
||||||
if not self.arglist:
|
if not self.arglist:
|
||||||
self.caller.msg("What do you want to move, and in what direction?")
|
self.caller.msg("What do you want to move, and in what direction?")
|
||||||
return
|
return
|
||||||
|
|
||||||
if "root" in self.arglist:
|
if "root" in self.arglist:
|
||||||
|
# we clean out the use of the word "root"
|
||||||
self.arglist.remove("root")
|
self.arglist.remove("root")
|
||||||
|
|
||||||
# we accept arguments on the form <color> <direction>
|
# we accept arguments on the form <color> <direction>
|
||||||
|
|
||||||
if not len(self.arglist) > 1:
|
if not len(self.arglist) > 1:
|
||||||
self.caller.msg("You must define which colour of root you want to move, and in which direction.")
|
self.caller.msg("You must define which colour of root you want to move, and in which direction.")
|
||||||
return
|
return
|
||||||
|
|
||||||
color = self.arglist[0].lower()
|
color = self.arglist[0].lower()
|
||||||
direction = self.arglist[1].lower()
|
direction = self.arglist[1].lower()
|
||||||
|
|
||||||
# get current root positions dict
|
# get current root positions dict
|
||||||
root_pos = self.obj.db.root_pos
|
root_pos = self.obj.db.root_pos
|
||||||
|
|
||||||
|
|
@ -475,6 +476,7 @@ class CmdShiftRoot(Command):
|
||||||
self.caller.msg("The thick reddish root gets in the way and is pushed back to the left.")
|
self.caller.msg("The thick reddish root gets in the way and is pushed back to the left.")
|
||||||
else:
|
else:
|
||||||
self.caller.msg("You cannot move the root in that direction.")
|
self.caller.msg("You cannot move the root in that direction.")
|
||||||
|
|
||||||
# now the horizontal roots (yellow/green). They can be moved up/down
|
# now the horizontal roots (yellow/green). They can be moved up/down
|
||||||
elif color == "yellow":
|
elif color == "yellow":
|
||||||
if direction == "up":
|
if direction == "up":
|
||||||
|
|
@ -506,11 +508,14 @@ class CmdShiftRoot(Command):
|
||||||
self.caller.msg("The root with yellow flowers gets in the way and is pushed upwards.")
|
self.caller.msg("The root with yellow flowers gets in the way and is pushed upwards.")
|
||||||
else:
|
else:
|
||||||
self.caller.msg("You cannot move the root in that direction.")
|
self.caller.msg("You cannot move the root in that direction.")
|
||||||
# store new position
|
|
||||||
|
# we have moved the root. Store new position
|
||||||
self.obj.db.root_pos = root_pos
|
self.obj.db.root_pos = root_pos
|
||||||
# check victory condition
|
|
||||||
|
# Check victory condition
|
||||||
if root_pos.values().count(0) == 0: # no roots in middle position
|
if root_pos.values().count(0) == 0: # no roots in middle position
|
||||||
self.caller.db.crumbling_wall_found_button = True
|
# This will affect the cmd: lock of CmdPressButton
|
||||||
|
self.obj.db.button_exposed = True
|
||||||
self.caller.msg("Holding aside the root you think you notice something behind it ...")
|
self.caller.msg("Holding aside the root you think you notice something behind it ...")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -520,8 +525,11 @@ class CmdPressButton(Command):
|
||||||
"""
|
"""
|
||||||
key = "press"
|
key = "press"
|
||||||
aliases = ["press button", "button", "push", "push button"]
|
aliases = ["press button", "button", "push", "push button"]
|
||||||
# only accessible if the button was found and there is light.
|
# only accessible if the button was found and there is light. This checks
|
||||||
locks = "cmd:attr(crumbling_wall_found_button) and not locattr(is_dark)"
|
# the Attribute button_exposed on the Wall object so that
|
||||||
|
# you can only push the button when the puzzle is solved. It also
|
||||||
|
# checks the is_lit Attribute on the location.
|
||||||
|
locks = "cmd:objattr(button_exposed) and locattr(is_lit)"
|
||||||
help_category = "TutorialWorld"
|
help_category = "TutorialWorld"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
|
|
@ -533,18 +541,16 @@ class CmdPressButton(Command):
|
||||||
return
|
return
|
||||||
|
|
||||||
# pushing the button
|
# pushing the button
|
||||||
string = "You move your fingers over the suspicious depression, then gives it a "
|
string = "You move your fingers over the suspicious depression, then gives it a " \
|
||||||
string += "decisive push. First nothing happens, then there is a rumble and a hidden "
|
"decisive push. First nothing happens, then there is a rumble and a hidden " \
|
||||||
string += "{wpassage{n opens, dust and pebbles rumbling as part of the wall moves aside."
|
"{wpassage{n opens, dust and pebbles rumbling as part of the wall moves aside."
|
||||||
|
self.caller.msg(string)
|
||||||
|
string = "%s moves their fingers over the suspicious depression, then gives it a " \
|
||||||
|
"decisive push. First nothing happens, then there is a rumble and a hidden " \
|
||||||
|
"{wpassage{n opens, dust and pebbles rumbling as part of the wall moves aside."
|
||||||
|
self.caller.location.msg_contents(string % self.caller.key, exclude=self.caller)
|
||||||
|
self.obj.open_wall()
|
||||||
|
|
||||||
# we are done - this will make the exit traversable!
|
|
||||||
self.caller.db.crumbling_wall_found_exit = True
|
|
||||||
# this will make it into a proper exit
|
|
||||||
eloc = self.caller.search(self.obj.db.destination, global_search=True)
|
|
||||||
if not eloc:
|
|
||||||
self.caller.msg("The exit leads nowhere, there's just more stone behind it ...")
|
|
||||||
return
|
|
||||||
self.obj.destination = eloc
|
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -560,36 +566,30 @@ class CmdSetCrumblingWall(CmdSet):
|
||||||
|
|
||||||
class CrumblingWall(TutorialObject, DefaultExit):
|
class CrumblingWall(TutorialObject, DefaultExit):
|
||||||
"""
|
"""
|
||||||
The CrumblingWall can be examined in various
|
This is a custom Exit.
|
||||||
ways, but only if a lit light source is in the room. The traversal
|
|
||||||
itself is blocked by a traverse: lock on the exit that only
|
The CrumblingWall can be examined in various ways, but only if a
|
||||||
allows passage if a certain attribute is set on the trying
|
lit light source is in the room. The traversal itself is blocked
|
||||||
player.
|
by a traverse: lock on the exit that only allows passage if a
|
||||||
|
certain attribute is set on the trying player.
|
||||||
|
|
||||||
Important attribute
|
Important attribute
|
||||||
destination - this property must be set to make this a valid exit
|
destination - this property must be set to make this a valid exit
|
||||||
whenever the button is pushed (this hides it as an exit
|
whenever the button is pushed (this hides it as an exit
|
||||||
until it actually is)
|
until it actually is)
|
||||||
"""
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
We make sure to reset the puzzle after a server reload/reboot.
|
||||||
|
"""
|
||||||
|
self.reset()
|
||||||
|
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"called when the object is first created."
|
"called when the object is first created."
|
||||||
super(CrumblingWall, self).at_object_creation()
|
super(CrumblingWall, self).at_object_creation()
|
||||||
|
|
||||||
self.aliases.add(["secret passage", "passage",
|
self.aliases.add(["secret passage", "passage",
|
||||||
"crack", "opening", "secret door"])
|
"crack", "opening", "secret door"])
|
||||||
# this is assigned first when pushing button, so assign
|
|
||||||
# this at creation time!
|
|
||||||
|
|
||||||
self.db.destination = 2
|
|
||||||
# locks on the object directly transfer to the exit "command"
|
|
||||||
self.locks.add("cmd:not locattr(is_dark)")
|
|
||||||
|
|
||||||
self.db.tutorial_info = "This is an Exit with a conditional traverse-lock. Try to shift the roots around."
|
|
||||||
# the lock is important for this exit; we only allow passage
|
|
||||||
# if we "found exit".
|
|
||||||
self.locks.add("traverse:attr(crumbling_wall_found_exit)")
|
|
||||||
# set cmdset
|
|
||||||
self.cmdset.add(CmdSetCrumblingWall, permanent=True)
|
|
||||||
|
|
||||||
# starting root positions. H1/H2 are the horizontally hanging roots,
|
# starting root positions. H1/H2 are the horizontally hanging roots,
|
||||||
# V1/V2 the vertically hanging ones. Each can have three positions:
|
# V1/V2 the vertically hanging ones. Each can have three positions:
|
||||||
|
|
@ -598,6 +598,40 @@ class CrumblingWall(TutorialObject, DefaultExit):
|
||||||
# ever any other identical value.
|
# ever any other identical value.
|
||||||
self.db.root_pos = {"yellow": 0, "green": 0, "red": 0, "blue": 0}
|
self.db.root_pos = {"yellow": 0, "green": 0, "red": 0, "blue": 0}
|
||||||
|
|
||||||
|
# flags controlling the puzzle victory conditions
|
||||||
|
self.db.button_exposed = False
|
||||||
|
self.db.exit_open = False
|
||||||
|
|
||||||
|
# this is not even an Exit until it has a proper destination, and we won't assign
|
||||||
|
# that until it is actually open. Until then we store the destination here. This
|
||||||
|
# should be given a reasonable value at creation!
|
||||||
|
self.db.destination = 2
|
||||||
|
|
||||||
|
# we lock this Exit so that one can only execute commands on it
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
"""
|
||||||
|
This method is called by the push button command once the puzzle
|
||||||
|
is solved. It opens the wall and sets a timer for it to reset
|
||||||
|
itself.
|
||||||
|
"""
|
||||||
|
# this will make it into a proper exit
|
||||||
|
eloc = self.caller.search(self.obj.db.destination, global_search=True)
|
||||||
|
if not eloc:
|
||||||
|
self.caller.msg("The exit leads nowhere, there's just more stone behind it ...")
|
||||||
|
else:
|
||||||
|
self.obj.destination = eloc
|
||||||
|
self.exit_open = True
|
||||||
|
# start a 45 second timer before closing again
|
||||||
|
utils.delay(45, self.reset)
|
||||||
|
|
||||||
def _translate_position(self, root, ipos):
|
def _translate_position(self, root, ipos):
|
||||||
"Translates the position into words"
|
"Translates the position into words"
|
||||||
rootnames = {"red": "The {rreddish{n vertical-hanging root ",
|
rootnames = {"red": "The {rreddish{n vertical-hanging root ",
|
||||||
|
|
@ -622,19 +656,28 @@ class CrumblingWall(TutorialObject, DefaultExit):
|
||||||
This is called when someone looks at the wall. We need to echo the
|
This is called when someone looks at the wall. We need to echo the
|
||||||
current root positions.
|
current root positions.
|
||||||
"""
|
"""
|
||||||
if caller.db.crumbling_wall_found_button:
|
if self.db.button_exposed:
|
||||||
string = "Having moved all the roots aside, you find that the center of the wall, "
|
# we found the button by moving the roots
|
||||||
string += "previously hidden by the vegetation, hid a curious square depression. It was maybe once "
|
string = "Having moved all the roots aside, you find that the center of the wall, " \
|
||||||
string += "concealed and made to look a part of the wall, but with the crumbling of stone around it,"
|
"previously hidden by the vegetation, hid a curious square depression. It was maybe once " \
|
||||||
string += "it's now easily identifiable as some sort of button."
|
"concealed and made to look a part of the wall, but with the crumbling of stone around it," \
|
||||||
|
"it's now easily identifiable as some sort of button."
|
||||||
|
elif self.db.exit_open:
|
||||||
|
# we pressed the button; the exit is open
|
||||||
|
string = "With the button pressed, a crack has opened in the root-covered wall, just wide enough " \
|
||||||
|
"to squeeze through. A cold draft is coming from the hole and you get the feeling the " \
|
||||||
|
"opening may close again soon."
|
||||||
else:
|
else:
|
||||||
string = "The wall is old and covered with roots that here and there have permeated the stone. "
|
# puzzle not solved yet.
|
||||||
string += "The roots (or whatever they are - some of them are covered in small non-descript flowers) "
|
string = "The wall is old and covered with roots that here and there have permeated the stone. " \
|
||||||
string += "crisscross the wall, making it hard to clearly see its stony surface.\n"
|
"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"
|
||||||
|
# display the root positions to help with the puzzle
|
||||||
for key, pos in self.db.root_pos.items():
|
for key, pos in self.db.root_pos.items():
|
||||||
string += "\n" + self._translate_position(key, pos)
|
string += "\n" + self._translate_position(key, pos)
|
||||||
self.db.desc = string
|
self.db.desc = string
|
||||||
# call the parent to continue execution (will use desc we just set)
|
|
||||||
|
# call the parent to continue execution (will use the desc we just set)
|
||||||
return super(CrumblingWall, self).return_appearance(caller)
|
return super(CrumblingWall, self).return_appearance(caller)
|
||||||
|
|
||||||
def at_after_traverse(self, traverser, source_location):
|
def at_after_traverse(self, traverser, source_location):
|
||||||
|
|
@ -642,7 +685,7 @@ class CrumblingWall(TutorialObject, DefaultExit):
|
||||||
This is called after we traversed this exit. Cleans up and resets
|
This is called after we traversed this exit. Cleans up and resets
|
||||||
the puzzle.
|
the puzzle.
|
||||||
"""
|
"""
|
||||||
del traverser.db.crumbling_wall_found_button
|
del traverser.db.crumbling_wall_found_buttothe
|
||||||
del traverser.db.crumbling_wall_found_exit
|
del traverser.db.crumbling_wall_found_exit
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
|
|
@ -656,10 +699,11 @@ class CrumblingWall(TutorialObject, DefaultExit):
|
||||||
traversed the Exit.
|
traversed the Exit.
|
||||||
"""
|
"""
|
||||||
self.location.msg_contents("The secret door closes abruptly, roots falling back into place.")
|
self.location.msg_contents("The secret door closes abruptly, roots falling back into place.")
|
||||||
for obj in self.location.contents:
|
|
||||||
# clear eventual puzzle-solved attribues on everyone that didn't
|
# reset the flags and remove the exit destination
|
||||||
# get out in time. They have to try again.
|
self.db.button_exposed = False
|
||||||
del obj.db.crumbling_wall_found_exit
|
self.db.exit_open = False
|
||||||
|
self.destination = None
|
||||||
|
|
||||||
# Reset the roots with some random starting positions for the roots:
|
# Reset the roots with some random starting positions for the roots:
|
||||||
start_pos = [{"yellow":1, "green":0, "red":0, "blue":0},
|
start_pos = [{"yellow":1, "green":0, "red":0, "blue":0},
|
||||||
|
|
@ -667,8 +711,7 @@ class CrumblingWall(TutorialObject, DefaultExit):
|
||||||
{"yellow":0, "green":1, "red":-1, "blue":0},
|
{"yellow":0, "green":1, "red":-1, "blue":0},
|
||||||
{"yellow":1, "green":0, "red":0, "blue":0},
|
{"yellow":1, "green":0, "red":0, "blue":0},
|
||||||
{"yellow":0, "green":0, "red":0, "blue":1}]
|
{"yellow":0, "green":0, "red":0, "blue":1}]
|
||||||
self.db.root_pos = start_pos[random.randint(0, 4)]
|
self.db.root_pos = random.choice(start_pos)
|
||||||
self.destination = None
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
@ -828,8 +871,104 @@ class Weapon(TutorialObject):
|
||||||
#
|
#
|
||||||
# Weapon rack - spawns weapons
|
# Weapon rack - spawns weapons
|
||||||
#
|
#
|
||||||
|
# This is a spawner mechanism that creates custom weapons from a
|
||||||
|
# spawner prototype dictionary. Note that we only create a single typeclass
|
||||||
|
# (Weapon) yet customize all these different weapons using the spawner.
|
||||||
|
# The spawner dictionaries could easily sit in separate modules and be
|
||||||
|
# used to create unique and interesting variations of typeclassed
|
||||||
|
# objects.
|
||||||
|
#
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
WEAPON_PROTOTYPES = {
|
||||||
|
"weapon": {
|
||||||
|
"typeclass": "tutorial_world.objects.Weapon",
|
||||||
|
"key": "Weapon",
|
||||||
|
"hit": 0.2,
|
||||||
|
"parry": 0.2,
|
||||||
|
"damage": 1.0,
|
||||||
|
"magic": False,
|
||||||
|
"desc": "A generic blade."},
|
||||||
|
"knife": {
|
||||||
|
"prototype": "weapon",
|
||||||
|
"aliases": "sword",
|
||||||
|
"key": "Kitchen knife",
|
||||||
|
"desc":"A rusty kitchen knife. Better than nothing.",
|
||||||
|
"damage": 3},
|
||||||
|
"rusty dagger": {
|
||||||
|
"prototype": "knife",
|
||||||
|
"key": "Rusty dagger",
|
||||||
|
"aliases": ["knife", "dagger"],
|
||||||
|
"desc": "A double-edged dagger with a nicked edge and a wooden handle.",
|
||||||
|
"hit": 0.25},
|
||||||
|
"sword": {
|
||||||
|
"prototype": "weapon",
|
||||||
|
"key": "Rusty sword",
|
||||||
|
"aliases": ["sword"],
|
||||||
|
"desc": "A rusty shortsword. It has a leather-wrapped handle covered i food grease.",
|
||||||
|
"hit": 0.3,
|
||||||
|
"damage": 5,
|
||||||
|
"parry": 0.5},
|
||||||
|
"club": {
|
||||||
|
"prototype": "weapon",
|
||||||
|
"key":"Club",
|
||||||
|
"desc": "A heavy wooden club, little more than a heavy branch.",
|
||||||
|
"hit": 0.4,
|
||||||
|
"damage": 6,
|
||||||
|
"parry": 0.2},
|
||||||
|
"ornate longsword": {
|
||||||
|
"prototype":"sword",
|
||||||
|
"key": "Ornate longsword",
|
||||||
|
"desc": "A fine longsword with some swirling patterns on the handle.",
|
||||||
|
"hit": 0.5,
|
||||||
|
"damage": 5},
|
||||||
|
"rune axe": {
|
||||||
|
"prototype": "club",
|
||||||
|
"key": "Runeaxe",
|
||||||
|
"aliases": ["axe"],
|
||||||
|
"hit": 0.4,
|
||||||
|
"damage": 6},
|
||||||
|
"thruning": {
|
||||||
|
"prototype": "sword",
|
||||||
|
"key": "Broadsword named Thruning",
|
||||||
|
"desc": "This heavy bladed weapon is marked with the name 'Thruning'. It is very powerful in skilled hands.",
|
||||||
|
"hit": 0.6,
|
||||||
|
"parry": 0.6,
|
||||||
|
"damage": 7},
|
||||||
|
"warhammer": {
|
||||||
|
"prototype": "club",
|
||||||
|
"key": "Silver Warhammer",
|
||||||
|
"aliases": ["hammer", "warhammer", "war"],
|
||||||
|
"desc": "A heavy war hammer with silver ornaments. This huge weapon causes massive damage - if you can hit.",
|
||||||
|
"hit": 0.4,
|
||||||
|
"damage": 10},
|
||||||
|
"slayer waraxe": {
|
||||||
|
"prototype": "axe",
|
||||||
|
"key": "Slayer waraxe",
|
||||||
|
"aliases": ["waraxe", "war", "slayer"],
|
||||||
|
"desc": "A huge double-bladed axe marked with the runes for 'Slayer'. It has more runic inscriptions on its head, which you cannot decipher.",
|
||||||
|
"magic": True,
|
||||||
|
"hit": 0.7,
|
||||||
|
"damage": 8},
|
||||||
|
"ghostblade": {
|
||||||
|
"prototype": "slayer waraxe",
|
||||||
|
"key": "The Ghostblade",
|
||||||
|
"aliases": ["blade", "ghost"],
|
||||||
|
"desc": "This massive sword is large as you are tall, yet seems to weigh almost nothing. It's almost like it's not really there.",
|
||||||
|
"hit": 0.9,
|
||||||
|
"parry": 0.8,
|
||||||
|
"damage": 10},
|
||||||
|
"hawkblade": {
|
||||||
|
"prototype": "ghostblade",
|
||||||
|
"key": "The Hawblade",
|
||||||
|
"aliases": ["hawk", "blade"],
|
||||||
|
"desc": "The weapon of a long-dead heroine and a more civilized age, the hawk-shaped hilt of this blade almost has a life of its own.",
|
||||||
|
"hit": 0.95,
|
||||||
|
"parry": 0.8,
|
||||||
|
"damage": 12}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class CmdGetWeapon(Command):
|
class CmdGetWeapon(Command):
|
||||||
"""
|
"""
|
||||||
Usage:
|
Usage:
|
||||||
|
|
@ -837,40 +976,23 @@ class CmdGetWeapon(Command):
|
||||||
|
|
||||||
This will try to obtain a weapon from the container.
|
This will try to obtain a weapon from the container.
|
||||||
"""
|
"""
|
||||||
key = "get"
|
key = "get weapon"
|
||||||
aliases = "get weapon"
|
aliases = "get weapon"
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
help_cateogory = "TutorialWorld"
|
help_cateogory = "TutorialWorld"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implement the command"
|
"""
|
||||||
|
Get a weapon from the container. It will
|
||||||
rack_id = self.obj.db.rack_id
|
itself handle all messages.
|
||||||
if self.caller.attributes.get(rack_id):
|
"""
|
||||||
# we don't allow a player to take more than one weapon from rack.
|
self.obj.produce_weapon(self.caller)
|
||||||
self.caller.msg("%s has no more to offer you." % self.obj.name)
|
|
||||||
else:
|
|
||||||
dmg, name, aliases, desc, magic = self.obj.randomize_type()
|
|
||||||
new_weapon = create_object(Weapon, key=name, aliases=aliases,location=self.caller, home=self.caller)
|
|
||||||
new_weapon.db.rack_id = rack_id
|
|
||||||
new_weapon.db.damage = dmg
|
|
||||||
new_weapon.db.desc = desc
|
|
||||||
new_weapon.db.magic = magic
|
|
||||||
ostring = self.obj.db.get_text
|
|
||||||
if not ostring:
|
|
||||||
ostring = "You pick up %s."
|
|
||||||
if '%s' in ostring:
|
|
||||||
self.caller.msg(ostring % name)
|
|
||||||
else:
|
|
||||||
self.caller.msg(ostring)
|
|
||||||
# tag the caller so they cannot keep taking objects from the rack.
|
|
||||||
self.caller.attributes.add(rack_id, True)
|
|
||||||
|
|
||||||
|
|
||||||
class CmdSetWeaponRack(CmdSet):
|
class CmdSetWeaponRack(CmdSet):
|
||||||
"group the rack cmd"
|
"""
|
||||||
|
The cmdset for the rack.
|
||||||
|
"""
|
||||||
key = "weaponrack_cmdset"
|
key = "weaponrack_cmdset"
|
||||||
mergemode = "Replace"
|
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"Called at first creation of cmdset"
|
"Called at first creation of cmdset"
|
||||||
|
|
@ -879,73 +1001,36 @@ class CmdSetWeaponRack(CmdSet):
|
||||||
|
|
||||||
class WeaponRack(TutorialObject):
|
class WeaponRack(TutorialObject):
|
||||||
"""
|
"""
|
||||||
This will spawn a new weapon for the player unless the player already has
|
This object represents a weapon store. When people use the
|
||||||
one from this rack.
|
"get weapon" command on this rack, it will produce one
|
||||||
|
random weapon from among those registered to exist
|
||||||
|
on it. This will also set a property on the character
|
||||||
|
to make sure they can't get more than one at a time.
|
||||||
|
|
||||||
attribute to set at creation:
|
|
||||||
min_dmg - the minimum damage of objects from this rack
|
|
||||||
max_dmg - the maximum damage of objects from this rack
|
|
||||||
magic - if weapons should be magical (have the magic flag set)
|
|
||||||
get_text - the echo text to return when getting the weapon. Give '%s'
|
|
||||||
to include the name of the weapon.
|
|
||||||
"""
|
"""
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
"called at creation"
|
"""
|
||||||
|
called at creation
|
||||||
|
"""
|
||||||
self.cmdset.add_default(CmdSetWeaponRack, permanent=True)
|
self.cmdset.add_default(CmdSetWeaponRack, permanent=True)
|
||||||
self.db.rack_id = "weaponrack_1"
|
self.db.rack_id = "weaponrack_1"
|
||||||
self.db.min_dmg = 1.0
|
# these are prototype names from the prototype
|
||||||
self.db.max_dmg = 4.0
|
# dictionary above.
|
||||||
self.db.magic = False
|
self.db.available_weapons = ["knife", "rusty_dagger",
|
||||||
|
"sword", "club"]
|
||||||
|
|
||||||
def randomize_type(self):
|
def produce_weapon(self, caller):
|
||||||
"""
|
"""
|
||||||
this returns a random weapon
|
This will produce a new weapon from the rack,
|
||||||
|
assuming the caller hasn't already gotten one.
|
||||||
"""
|
"""
|
||||||
min_dmg = float(self.db.min_dmg)
|
if caller.attributes.get(self.db.rack_id):
|
||||||
max_dmg = float(self.db.max_dmg)
|
caller.msg("%s has no more to offer you." % self.key)
|
||||||
magic = bool(self.db.magic)
|
|
||||||
dmg = min_dmg + random.random()*(max_dmg - min_dmg)
|
|
||||||
aliases = [self.db.rack_id, "weapon"]
|
|
||||||
if dmg < 1.5:
|
|
||||||
name = "Knife"
|
|
||||||
desc = "A rusty kitchen knife. Better than nothing."
|
|
||||||
elif dmg < 2.0:
|
|
||||||
name = "Rusty dagger"
|
|
||||||
desc = "A double-edged dagger with nicked edge. It has a wooden handle."
|
|
||||||
elif dmg < 3.0:
|
|
||||||
name = "Sword"
|
|
||||||
desc = "A rusty shortsword. It has leather wrapped around the handle."
|
|
||||||
elif dmg < 4.0:
|
|
||||||
name = "Club"
|
|
||||||
desc = "A heavy wooden club with some rusty spikes in it."
|
|
||||||
elif dmg < 5.0:
|
|
||||||
name = "Ornate Longsword"
|
|
||||||
aliases.extend(["longsword","ornate"])
|
|
||||||
desc = "A fine longsword."
|
|
||||||
elif dmg < 6.0:
|
|
||||||
name = "Runeaxe"
|
|
||||||
aliases.extend(["rune","axe"])
|
|
||||||
desc = "A single-bladed axe, heavy but yet easy to use."
|
|
||||||
elif dmg < 7.0:
|
|
||||||
name = "Broadsword named Thruning"
|
|
||||||
aliases.extend(["thruning","broadsword"])
|
|
||||||
desc = "This heavy bladed weapon is marked with the name 'Thruning'. It is very powerful in skilled hands."
|
|
||||||
elif dmg < 8.0:
|
|
||||||
name = "Silver Warhammer"
|
|
||||||
aliases.append("warhammer")
|
|
||||||
desc = "A heavy war hammer with silver ornaments. This huge weapon causes massive damage."
|
|
||||||
elif dmg < 9.0:
|
|
||||||
name = "Slayer Waraxe"
|
|
||||||
aliases.extend(["waraxe","slayer"])
|
|
||||||
desc = "A huge double-bladed axe marked with the runes for 'Slayer'. It has more runic inscriptions on its head, which you cannot decipher."
|
|
||||||
elif dmg < 10.0:
|
|
||||||
name = "The Ghostblade"
|
|
||||||
aliases.append("ghostblade")
|
|
||||||
desc = "This massive sword is large as you are tall. Its metal shine with a bluish glow."
|
|
||||||
else:
|
else:
|
||||||
name = "The Hawkblade"
|
prototype = random.choice(self.db.available_weapons)
|
||||||
aliases.append("hawkblade")
|
# use the spawner to create a new Weapon from the
|
||||||
desc = "White surges of magical power runs up and down this runic blade. The hawks depicted on its hilt almost seems to have a life of their own."
|
# spawner dictionary
|
||||||
if dmg < 9 and magic:
|
wpn = spawn(WEAPON_PROTOTYPES[prototype], prototype_parents=WEAPON_PROTOTYPES)
|
||||||
desc += "\nThe metal seems to glow faintly, as if imbued with more power than what is immediately apparent."
|
caller.attributes.add(self.db.rack_id, True)
|
||||||
return dmg, name, aliases, desc, magic
|
wpn.location = caller
|
||||||
|
caller.msg("You grab %s - %s." % (wpn.key, wpn.db.desc))
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@
|
||||||
Room Typeclasses for the TutorialWorld.
|
Room Typeclasses for the TutorialWorld.
|
||||||
|
|
||||||
This defines special types of Rooms available in the tutorial. To keep
|
This defines special types of Rooms available in the tutorial. To keep
|
||||||
everything in one place we define them together with custom commands
|
everything in one place we define them together with the custom
|
||||||
to control them, those commands could also have been in a separate
|
commands needed to control them. Those commands could also have been
|
||||||
module (e.g. if they could have been re-used elsewhere.)
|
in a separate module (e.g. if they could have been re-used elsewhere.)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -301,9 +301,11 @@ class DarkRoom(TutorialRoom):
|
||||||
|
|
||||||
Note that we do NOT look for a specific LightSource typeclass,
|
Note that we do NOT look for a specific LightSource typeclass,
|
||||||
but for the Attribute is_giving_light - this makes it easy to
|
but for the Attribute is_giving_light - this makes it easy to
|
||||||
later add other types of light-giving items.
|
later add other types of light-giving items. We also accept
|
||||||
|
if there is a light-giving object in the room overall (like if
|
||||||
|
a lantern was dropped in the room)
|
||||||
"""
|
"""
|
||||||
return any(obj for obj in obj.contents if obj.db.is_giving_light)
|
return obj.db.is_giving_light or any(obj for obj in obj.contents if obj.db.is_giving_light)
|
||||||
|
|
||||||
def _heal(self, character):
|
def _heal(self, character):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue