evennia/contrib/tutorial_world/objects.py

902 lines
36 KiB
Python

"""
TutorialWorld - basic objects - Griatch 2011
This module holds all "dead" object definitions for
the tutorial world. Object-commands and -cmdsets
are also defined here, together with the object.
Objects:
TutorialObject
Readable
Obelisk
LightSource
CrumblingWall
Weapon
"""
import time, random
from src.utils import utils, create
from game.gamesrc.objects.baseobjects import Object, Exit
from game.gamesrc.commands.basecommand import Command
from game.gamesrc.commands.basecmdset import CmdSet
from game.gamesrc.scripts.basescript import Script
#------------------------------------------------------------
#
# TutorialObject
#
# The TutorialObject is the base class for all items
# in the tutorial. They have an attribute "tutorial_info"
# on them that a global tutorial command can use to extract
# interesting behind-the scenes information about the object.
#
# TutorialObjects may also be "reset". What the reset means
# is up to the object. It can be the resetting of the world
# itself, or the removal of an inventory item from a
# character's inventory when leaving the tutorial, for example.
#
#------------------------------------------------------------
class TutorialObject(Object):
"""
This is the baseclass for all objects in the tutorial.
"""
def at_object_creation(self):
"Called when the object is first created."
super(TutorialObject, self).at_object_creation()
self.db.tutorial_info = "No tutorial info is available for this object."
#self.db.last_reset = time.time()
def reset(self):
"Resets the object, whatever that may mean."
self.location = self.home
#------------------------------------------------------------
#
# Readable - an object one can "read".
#
#------------------------------------------------------------
class CmdRead(Command):
"""
Usage:
read [obj]
Read some text.
"""
key = "read"
locks = "cmd:all()"
help_category = "TutorialWorld"
def func(self):
"Implement the read command."
if self.args:
obj = self.caller.search(self.args.strip())
else:
obj = self.obj
if not obj:
return
# we want an attribute read_text to be defined.
readtext = obj.db.readable_text
if readtext:
string = "You read {C%s{n:\n %s" % (obj.key, readtext)
else:
string = "There is nothing to read on %s." % obj.key
self.caller.msg(string)
class CmdSetReadable(CmdSet):
"CmdSet for readables"
def at_cmdset_creation(self):
"called when object is created."
self.add(CmdRead())
class Readable(TutorialObject):
"""
This object defines some attributes and defines a read method on itself.
"""
def at_object_creation(self):
"Called when object is created"
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.readable_text = "There is no text written on %s." % self.key
# define a command on the object.
self.cmdset.add_default(CmdSetReadable, permanent=True)
#------------------------------------------------------------
#
# Climbable object
#
# The climbable object works so that once climbed, it sets
# a flag on the climber to show that it was climbed. A simple
# command 'climb' handles the actual climbing.
#
#------------------------------------------------------------
class CmdClimb(Command):
"""
Usage:
climb <object>
"""
key = "climb"
locks = "cmd:all()"
help_category = "TutorialWorld"
def func(self):
"Implements function"
if not self.args:
self.caller.msg("What do you want to climb?")
return
obj = self.caller.search(self.args.strip())
if not obj:
return
if obj != self.obj:
self.caller.msg("Try as you might, you cannot climb that.")
return
ostring = self.obj.db.climb_text
if not ostring:
ostring = "You climb %s. Having looked around, you climb down again." % self.obj.name
self.caller.msg(ostring)
self.caller.db.last_climbed = self.obj
class CmdSetClimbable(CmdSet):
"Climbing cmdset"
def at_cmdset_creation(self):
"populate set"
self.add(CmdClimb())
class Climbable(TutorialObject):
"A climbable object."
def at_object_creation(self):
"Called at initial creation only"
self.cmdset.add_default(CmdSetClimbable, permanent=True)
#------------------------------------------------------------
#
# Obelisk - a unique item
#
# The Obelisk is an object with a modified return_appearance
# method that causes it to look slightly different every
# time one looks at it. Since what you actually see
# is a part of a game puzzle, the act of looking also
# stores a key attribute on the looking object for later
# reference.
#
#------------------------------------------------------------
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.",
"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.",
"The surface for a moment seems to portray {ba woman fighting a beast{n."]
class Obelisk(TutorialObject):
"""
This object changes its description randomly.
"""
def at_object_creation(self):
"Called when object is created."
super(Obelisk, self).at_object_creation()
self.db.tutorial_info = "This object changes its desc randomly, and makes sure to remember which one you saw."
# make sure this can never be picked up
self.locks.add("get:false()")
def return_appearance(self, caller):
"Overload the default version of this hook."
clueindex = random.randint(0, len(OBELISK_DESCS)-1)
# set this description
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. "
self.db.desc = string + OBELISK_DESCS[clueindex]
# remember that this was the clue we got.
caller.db.puzzle_clue = clueindex
# call the parent function as normal (this will use db.desc we just set)
return super(Obelisk, self).return_appearance(caller)
#------------------------------------------------------------
#
# LightSource
#
# This object that emits light and can be
# turned on or off. It must be carried to use and has only
# a limited burn-time.
# When burned out, it will remove itself from the carrying
# character's inventory.
#
#------------------------------------------------------------
class StateLightSourceOn(Script):
"""
This script controls how long the light source is burning. When
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_stop(self):
"""
Since this script stops after only 1 "repeat", we can use this hook
instead of at_repeat(). Since the user may also turn off the light
prematurely, this hook will also be called in that case.
"""
# calculate remaining burntime
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
if burntime <= 0:
# no burntime left. Reset the object.
self.obj.reset()
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"
aliases = ["switch on", "turn on", "light"]
locks = "cmd:holds()" # only allow if command.obj is carried by caller.
help_category = "TutorialWorld"
def func(self):
"Implements the command"
if self.obj.db.is_active:
self.caller.msg("%s is already burning." % 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])
# we 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:
# set lightsource to inactive
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")
# we run script validation on the room to make light/dark states tick.
class CmdSetLightSource(CmdSet):
"CmdSet for the lightsource commands"
key = "lightsource_cmdset"
def at_cmdset_creation(self):
"called at cmdset creation"
self.add(CmdLightSourceOn())
self.add(CmdLightSourceOff())
class LightSource(TutorialObject):
"""
This implements a light source object.
When burned out, lightsource will be moved to its home - which by default is the
location it was first created at.
"""
def at_object_creation(self):
"Called when object is first created."
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.is_active = False
self.db.burntime = 60 # 1 minute
self.db.desc = "A splinter of wood with remnants of resin on it, enough for burning."
# add commands
self.cmdset.add_default(CmdSetLightSource, permanent=True)
def reset(self):
"""
Can be called by tutorial world runner, or by the script when the lightsource
has burned out.
"""
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:
# validate in holders current room, if possible
self.location.location.scripts.validate()
except AttributeError:
# maybe it was dropped, try validating at current location.
try:
self.location.scripts.validate()
except AttributeError,e:
pass
self.delete()
#------------------------------------------------------------
#
# Crumbling wall - unique exit
#
# This implements a simple puzzle exit that needs to be
# accessed with commands before one can get to traverse it.
#
# The puzzle is currently simply to move roots (that have
# presumably covered the wall) aside until a button for a
# secret door is revealed. The original position of the
# roots blocks the button, so they have to be moved to a certain
# position - when they have, the "press button" command
# is made available and the Exit is made traversable.
#
#------------------------------------------------------------
# There are four roots - two horizontal and two vertically
# running roots. Each can have three positions: top/middle/bottom
# and left/middle/right respectively. There can be any number of
# roots hanging through the middle position, but only one each
# 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
# to each side to "win". This is just a tutorial, remember?)
class CmdShiftRoot(Command):
"""
Shifts roots around.
shift blue root left/right
shift red root left/right
shift yellow root up/down
shift green root up/down
"""
key = "shift"
aliases = ["move"]
# the locattr() lock looks for the attribute is_dark on the current room.
locks = "cmd:not locattr(is_dark)"
help_category = "TutorialWorld"
def parse(self):
"custom parser; split input by spaces"
self.arglist = self.args.strip().split()
def func(self):
"""
Implement the command.
blue/red - vertical roots
yellow/green - horizontal roots
"""
if not self.arglist:
self.caller.msg("What do you want to move, and in what direction?")
return
if "root" in self.arglist:
self.arglist.remove("root")
# we accept arguments on the form <color> <direction>
if not len(self.arglist) > 1:
self.caller.msg("You must define which colour of root you want to move, and in which direction.")
return
color = self.arglist[0].lower()
direction = self.arglist[1].lower()
# get current root positions dict
root_pos = self.obj.db.root_pos
if not color in root_pos:
self.caller.msg("No such root to move.")
return
# first, vertical roots (red/blue) - can be moved left/right
if color == "red":
if direction == "left":
root_pos[color] = max(-1, root_pos[color] - 1)
self.caller.msg("You shift the reddish root to the left.")
if root_pos[color] != 0 and root_pos[color] == root_pos["blue"]:
root_pos["blue"] += 1
self.caller.msg("The root with blue flowers gets in the way and is pushed to the right.")
elif direction == "right":
root_pos[color] = min(1, root_pos[color] + 1)
self.caller.msg("You shove the reddish root to the right.")
if root_pos[color] != 0 and root_pos[color] == root_pos["blue"]:
root_pos["blue"] -= 1
self.caller.msg("The root with blue flowers gets in the way and is pushed to the left.")
else:
self.caller.msg("You cannot move the root in that direction.")
elif color == "blue":
if direction == "left":
root_pos[color] = max(-1, root_pos[color] - 1)
self.caller.msg("You shift the root with small blue flowers to the left.")
if root_pos[color] != 0 and root_pos[color] == root_pos["red"]:
root_pos["red"] += 1
self.caller.msg("The reddish root is to big to fit as well, so that one falls away to the left.")
elif direction == "right":
root_pos[color] = min(1, root_pos[color] + 1)
self.caller.msg("You shove the root adorned with small blue flowers to the right.")
if root_pos[color] != 0 and root_pos[color] == root_pos["red"]:
root_pos["red"] -= 1
self.caller.msg("The thick reddish root gets in the way and is pushed back to the left.")
else:
self.caller.msg("You cannot move the root in that direction.")
# now the horizontal roots (yellow/green). They can be moved up/down
elif color == "yellow":
if direction == "up":
root_pos[color] = max(-1, root_pos[color] - 1)
self.caller.msg("You shift the root with small yellow flowers upwards.")
if root_pos[color] != 0 and root_pos[color] == root_pos["green"]:
root_pos["green"] += 1
self.caller.msg("The green weedy root falls down.")
elif direction == "down":
root_pos[color] = min(1, root_pos[color] +1)
self.caller.msg("You shove the root adorned with small yellow flowers downwards.")
if root_pos[color] != 0 and root_pos[color] == root_pos["green"]:
root_pos["green"] -= 1
self.caller.msg("The weedy green root is shifted upwards to make room.")
else:
self.caller.msg("You cannot move the root in that direction.")
elif color == "green":
if direction == "up":
root_pos[color] = max(-1, root_pos[color] - 1)
self.caller.msg("You shift the weedy green root upwards.")
if root_pos[color] != 0 and root_pos[color] == root_pos["yellow"]:
root_pos["yellow"] += 1
self.caller.msg("The root with yellow flowers falls down.")
elif direction == "down":
root_pos[color] = min(1, root_pos[color] + 1)
self.caller.msg("You shove the weedy green root downwards.")
if root_pos[color] != 0 and root_pos[color] == root_pos["yellow"]:
root_pos["yellow"] -= 1
self.caller.msg("The root with yellow flowers gets in the way and is pushed upwards.")
else:
self.caller.msg("You cannot move the root in that direction.")
# store new position
self.obj.db.root_pos = root_pos
# check victory condition
if root_pos.values().count(0) == 0: # no roots in middle position
self.caller.db.crumbling_wall_found_button = True
self.caller.msg("Holding aside the root you think you notice something behind it ...")
class CmdPressButton(Command):
"""
Presses a button.
"""
key = "press"
aliases = ["press button", "button", "push", "push button"]
locks = "cmd:attr(crumbling_wall_found_button) and not locattr(is_dark)" # only accessible if the button was found and there is light.
help_category = "TutorialWorld"
def func(self):
"Implements the command"
if self.caller.db.crumbling_wall_found_exit:
# we already pushed the button
self.caller.msg("The button folded away when the secret passage opened. You cannot push it again.")
return
# pushing the button
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 "
string += "{wpassage{n opens, dust and pebbles rumbling as part of the wall moves aside."
# 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)
class CmdSetCrumblingWall(CmdSet):
"Group the commands for crumblingWall"
key = "crumblingwall_cmdset"
def at_cmdset_creation(self):
"called when object is first created."
self.add(CmdShiftRoot())
self.add(CmdPressButton())
class CrumblingWall(TutorialObject, Exit):
"""
The CrumblingWall can be examined in various
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
allows passage if a certain attribute is set on the trying
player.
Important attribute
destination - this property must be set to make this a valid exit
whenever the button is pushed (this hides it as an exit
until it actually is)
"""
def at_object_creation(self):
"called when the object is first created."
super(CrumblingWall, self).at_object_creation()
self.aliases = ["secret passage", "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, V1/V2 the
# vertically hanging ones. Each can have three positions: (-1, 0, 1) where
# 0 means the middle position. yellow/green are horizontal roots and red/blue vertical.
# all may have value 0, but never any other identical value.
self.db.root_pos = {"yellow":0, "green":0, "red":0, "blue":0}
def _translate_position(self, root, ipos):
"Translates the position into words"
rootnames = {"red": "The {rreddish{n vertical-hanging root ",
"blue": "The thick vertical root with {bblue{n flowers ",
"yellow": "The thin horizontal-hanging root with {yyellow{n flowers ",
"green": "The weedy {ggreen{n horizontal root "}
vpos = {-1: "hangs far to the {wleft{n on the wall.",
0: "hangs straight down the {wmiddle{n of the wall.",
1: "hangs far to the {wright{n of the wall."}
hpos = {-1: "covers the {wupper{n part of the wall.",
0: "passes right over the {wmiddle{n of the wall.",
1: "nearly touches the floor, near the {wbottom{n of the wall."}
if root in ("yellow", "green"):
string = rootnames[root] + hpos[ipos]
else:
string = rootnames[root] + vpos[ipos]
return string
def return_appearance(self, caller):
"This is called when someone looks at the wall. We need to echo the current root positions."
if caller.db.crumbling_wall_found_button:
string = "Having moved all the roots aside, you find that the center of the wall, "
string += "previously hidden by the vegetation, hid a curious square depression. It was maybe once "
string += "concealed and made to look a part of the wall, but with the crumbling of stone around it,"
string += "it's now easily identifiable as some sort of button."
else:
string = "The wall is old and covered with roots that here and there have permeated the stone. "
string += "The roots (or whatever they are - some of them are covered in small non-descript flowers) "
string += "crisscross the wall, making it hard to clearly see its stony surface.\n"
for key, pos in self.db.root_pos.items():
string += "\n" + self._translate_position(key, pos)
self.db.desc = string
# call the parent to continue execution (will use desc we just set)
return super(CrumblingWall, self).return_appearance(caller)
def at_after_traverse(self, traverser, source_location):
"This is called after we traversed this exit. Cleans up and resets the puzzle."
del traverser.db.crumbling_wall_found_button
del traverser.db.crumbling_wall_found_exit
self.reset()
def at_failed_traverse(self, traverser):
"This is called if the player fails to pass the Exit."
traverser.msg("No matter how you try, you cannot force yourself through %s." % self.key)
def reset(self):
"Called by tutorial world runner, or whenever someone successfully traversed the Exit."
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 get out in time. They
# have to try again.
del obj.db.crumbling_wall_found_exit
# Reset the roots with some random starting positions for the roots:
start_pos = [{"yellow":1, "green":0, "red":0, "blue":0},
{"yellow":0, "green":0, "red":0, "blue":0},
{"yellow":0, "green":1, "red":-1, "blue":0},
{"yellow":1, "green":0, "red":0, "blue":0},
{"yellow":0, "green":0, "red":0, "blue":1}]
self.db.root_pos = start_pos[random.randint(0, 4)]
self.destination = None
#------------------------------------------------------------
#
# Weapon - object type
#
# A weapon is necessary in order to fight in the tutorial
# world. A weapon (which here is assumed to be a bladed
# melee weapon for close combat) has three commands,
# stab, slash and defend. Weapons also have a property "magic"
# to determine if they are usable against certain enemies.
#
# Since Characters don't have special skills in the tutorial,
# we let the weapon itself determine how easy/hard it is
# to hit with it, and how much damage it can do.
#
#------------------------------------------------------------
class CmdAttack(Command):
"""
Attack the enemy. Commands:
stab <enemy>
slash <enemy>
parry
stab - (thrust) makes a lot of damage but is harder to hit with.
slash - is easier to land, but does not make as much damage.
parry - forgoes your attack but will make you harder to hit on next enemy attack.
"""
# this is an example of implementing many commands as a single command class,
# using the given command alias to separate between them.
key = "attack"
aliases = ["hit","kill", "fight", "thrust", "pierce", "stab", "slash", "chop", "parry", "defend"]
locks = "cmd:all()"
help_category = "TutorialWorld"
def func(self):
"Implements the stab"
cmdstring = self.cmdstring
if cmdstring in ("attack", "fight"):
string = "How do you want to fight? Choose one of 'stab', 'slash' or 'defend'."
self.caller.msg(string)
return
# parry mode
if cmdstring in ("parry", "defend"):
string = "You raise your weapon in a defensive pose, ready to block the next enemy attack."
self.caller.msg(string)
self.caller.db.combat_parry_mode = True
self.caller.location.msg_contents("%s takes a defensive stance" % self.caller, exclude=[self.caller])
return
if not self.args:
self.caller.msg("Who do you attack?")
return
target = self.caller.search(self.args.strip())
if not target:
return
string = ""
tstring = ""
ostring = ""
if cmdstring in ("thrust", "pierce", "stab"):
hit = float(self.obj.db.hit) * 0.7 # modified due to stab
damage = self.obj.db.damage * 2 # modified due to stab
string = "You stab with %s. " % self.obj.key
tstring = "%s stabs at you with %s. " % (self.caller.key, self.obj.key)
ostring = "%s stabs at %s with %s. " % (self.caller.key, target.key, self.obj.key)
self.caller.db.combat_parry_mode = False
elif cmdstring in ("slash", "chop"):
hit = float(self.obj.db.hit) # un modified due to slash
damage = self.obj.db.damage # un modified due to slash
string = "You slash with %s. " % self.obj.key
tstring = "%s slash at you with %s. " % (self.caller.key, self.obj.key)
ostring = "%s slash at %s with %s. " % (self.caller.key, target.key, self.obj.key)
self.caller.db.combat_parry_mode = False
else:
self.caller.msg("You fumble with your weapon, unable to choose an appropriate action...")
self.caller.location.msg_contents("%s fumbles with their weapon." % self.obj.key)
self.caller.db.combat_parry_mode = False
return
if target.db.combat_parry_mode:
# target is defensive; even harder to hit!
hit *= 0.5
if random.random() <= hit:
self.caller.msg(string + "{gIt's a hit!{n")
target.msg(tstring + "{rIt's a hit!{n")
self.caller.location.msg_contents(ostring + "It's a hit!", exclude=[target,self.caller])
# call enemy hook
if hasattr(target, "at_hit"):
# should return True if target is defeated, False otherwise.
return target.at_hit(self.obj, self.caller, damage)
elif target.db.health:
target.db.health -= damage
else:
# sorry, impossible to fight this enemy ...
self.caller.msg("The enemy seems unaffacted.")
return False
else:
self.caller.msg(string + "{rYou miss.{n")
target.msg(tstring + "{gThey miss you.{n")
self.caller.location.msg_contents(ostring + "They miss.", exclude=[target, self.caller])
class CmdSetWeapon(CmdSet):
"Holds the attack command."
def at_cmdset_creation(self):
"called at first object creation."
self.add(CmdAttack())
class Weapon(TutorialObject):
"""
This defines a bladed weapon.
Important attributes (set at creation):
hit - chance to hit (0-1)
parry - chance to parry (0-1)
damage - base damage given (modified by hit success and type of attack) (0-10)
"""
def at_object_creation(self):
"Called at first creation of the object"
super(Weapon, self).at_object_creation()
self.db.hit = 0.4 # hit chance
self.db.parry = 0.8 # parry chance
self.damage = 8.0
self.magic = False
self.cmdset.add_default(CmdSetWeapon, permanent=True)
def reset(self):
"When reset, the weapon is simply deleted, unless it has a place to return to."
if self.location.has_player and self.home == self.location:
self.location.msg_contents("%s suddenly and magically fades into nothingness, as if it was never there ..." % self.key)
self.delete()
else:
self.location = self.home
#------------------------------------------------------------
#
# Weapon rack - spawns weapons
#
#------------------------------------------------------------
class CmdGetWeapon(Command):
"""
Usage:
get weapon
This will try to obtain a weapon from the container.
"""
key = "get"
aliases = "get weapon"
locks = "cmd:all()"
help_cateogory = "TutorialWorld"
def func(self):
"Implement the command"
rack_id = self.obj.db.rack_id
if eval("self.caller.db.%s" % rack_id):
# we don't allow to take more than one weapon from rack.
self.caller.msg("%s has no more to offer." % self.obj.name)
else:
dmg, name, aliases, desc, magic = self.obj.randomize_type()
new_weapon = create.create_object(Weapon, key=name, aliases=aliases,location=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.
exec("self.caller.db.%s = True" % rack_id)
class CmdSetWeaponRack(CmdSet):
"group the rack cmd"
key = "weaponrack_cmdset"
mergemode = "Replace"
def at_cmdset_creation(self):
self.add(CmdGetWeapon())
class WeaponRack(TutorialObject):
"""
This will spawn a new weapon for the player unless the player already has one from this rack.
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):
"called at creation"
self.cmdset.add_default(CmdSetWeaponRack, permanent=True)
self.rack_id = "weaponrack_1"
self.db.min_dmg = 1.0
self.db.max_dmg = 4.0
self.db.magic = False
def randomize_type(self):
"""
this returns a random weapon
"""
min_dmg = float(self.db.min_dmg)
max_dmg = float(self.db.max_dmg)
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:
name = "The Hawkblade"
aliases.append("hawkblade")
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."
if dmg < 9 and magic:
desc += "\nThe metal seems to glow faintly, as if imbued with more power than what is immediately apparent."
return dmg, name, aliases, desc, magic