Fixing tutorial world usage of search.

This commit is contained in:
Griatch 2012-09-27 21:36:20 +02:00
parent 59ccd3eb38
commit 1b0544c261
3 changed files with 227 additions and 227 deletions

View file

@ -1,7 +1,7 @@
""" """
This module implements a simple mobile object with This module implements a simple mobile object with
a very rudimentary AI as well as an aggressive enemy a very rudimentary AI as well as an aggressive enemy
object based on that mobile class. object based on that mobile class.
""" """
@ -16,10 +16,10 @@ BASE_CHARACTER_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
#------------------------------------------------------------ #------------------------------------------------------------
# #
# Mob - mobile object # Mob - mobile object
# #
# This object utilizes exits and moves about randomly from # This object utilizes exits and moves about randomly from
# room to room. # room to room.
# #
#------------------------------------------------------------ #------------------------------------------------------------
@ -27,7 +27,7 @@ class Mob(tut_objects.TutorialObject):
""" """
This type of mobile will roam from exit to exit at This type of mobile will roam from exit to exit at
random intervals. Simply lock exits against the is_mob attribute random intervals. Simply lock exits against the is_mob attribute
to block them from the mob (lockstring = "traverse:not attr(is_mob)"). to block them from the mob (lockstring = "traverse:not attr(is_mob)").
""" """
def at_object_creation(self): def at_object_creation(self):
"This is called when the object is first created." "This is called when the object is first created."
@ -35,11 +35,11 @@ class Mob(tut_objects.TutorialObject):
self.scripts.add(tut_scripts.IrregularEvent) self.scripts.add(tut_scripts.IrregularEvent)
# this is a good attribute for exits to look for, to block # this is a good attribute for exits to look for, to block
# a mob from entering certain exits. # a mob from entering certain exits.
self.db.is_mob = True self.db.is_mob = True
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
def announce_move_from(self, destination): def announce_move_from(self, destination):
"Called just before moving" "Called just before moving"
@ -53,8 +53,8 @@ class Mob(tut_objects.TutorialObject):
"Called at irregular intervals. Moves the mob." "Called at irregular intervals. Moves the mob."
if self.roam_mode: if self.roam_mode:
exits = [ex for ex in self.location.exits if ex.access(self, "traverse")] exits = [ex for ex in self.location.exits if ex.access(self, "traverse")]
if exits: if exits:
# Try to make it so the mob doesn't backtrack. # Try to make it so the mob doesn't backtrack.
new_exits = [ex for ex in exits if ex.destination != self.db.last_location] new_exits = [ex for ex in exits if ex.destination != self.db.last_location]
if new_exits: if new_exits:
exits = new_exits exits = new_exits
@ -67,15 +67,15 @@ class Mob(tut_objects.TutorialObject):
#------------------------------------------------------------ #------------------------------------------------------------
# #
# Enemy - mobile attacking object # Enemy - mobile attacking object
# #
# An enemy is a mobile that is aggressive against players # An enemy is a mobile that is aggressive against players
# in its vicinity. An enemy will try to attack characters # in its vicinity. An enemy will try to attack characters
# in the same location. It will also pursue enemies through # in the same location. It will also pursue enemies through
# exits if possible. # exits if possible.
# #
# An enemy needs to have a Weapon object in order to # An enemy needs to have a Weapon object in order to
# attack. # attack.
# #
# This particular tutorial enemy is a ghostly apparition that can only # This particular tutorial enemy is a ghostly apparition that can only
# be hurt by magical weapons. It will also not truly "die", but only # be hurt by magical weapons. It will also not truly "die", but only
@ -90,44 +90,44 @@ class AttackTimer(Script):
""" """
def at_script_creation(self): def at_script_creation(self):
"This sets up the script" "This sets up the script"
self.key = "AttackTimer" self.key = "AttackTimer"
self.desc = "Drives an Enemy's combat." self.desc = "Drives an Enemy's combat."
self.interval = random.randint(2, 3) # how fast the Enemy acts self.interval = random.randint(2, 3) # how fast the Enemy acts
self.start_delay = True # wait self.interval before first call self.start_delay = True # wait self.interval before first call
self.persistent = True self.persistent = True
def at_repeat(self): def at_repeat(self):
"Called every self.interval seconds." "Called every self.interval seconds."
if self.obj.db.inactive: if self.obj.db.inactive:
return return
#print "attack timer: at_repeat", self.dbobj.id, self.ndb.twisted_task, id(self.ndb.twisted_task) #print "attack timer: at_repeat", self.dbobj.id, self.ndb.twisted_task, id(self.ndb.twisted_task)
if self.obj.db.roam_mode: if self.obj.db.roam_mode:
self.obj.roam() self.obj.roam()
#return #return
elif self.obj.db.battle_mode: elif self.obj.db.battle_mode:
#print "attack" #print "attack"
self.obj.attack() self.obj.attack()
return return
elif self.obj.db.pursue_mode: elif self.obj.db.pursue_mode:
#print "pursue" #print "pursue"
self.obj.pursue() self.obj.pursue()
#return #return
else: else:
#dead mode. Wait for respawn. #dead mode. Wait for respawn.
dead_at = self.db.dead_at dead_at = self.db.dead_at
if not dead_at: if not dead_at:
self.db.dead_at = time.time() self.db.dead_at = time.time()
if (time.time() - self.db.dead_at) > self.db.dead_timer: if (time.time() - self.db.dead_at) > self.db.dead_timer:
self.obj.reset() self.obj.reset()
class Enemy(Mob): class Enemy(Mob):
""" """
This is a ghostly enemy with health (hit points). Their chance to hit, damage etc is This is a ghostly enemy with health (hit points). Their chance to hit, damage etc is
determined by the weapon they are wielding, same as characters. determined by the weapon they are wielding, same as characters.
An enemy can be in four modes: An enemy can be in four modes:
roam (inherited from Mob) - where it just moves around randomly roam (inherited from Mob) - where it just moves around randomly
battle - where it stands in one place and attacks players battle - where it stands in one place and attacks players
pursue - where it follows a player, trying to enter combat again pursue - where it follows a player, trying to enter combat again
dead - passive and invisible until it is respawned dead - passive and invisible until it is respawned
@ -139,19 +139,19 @@ class Enemy(Mob):
defeat_text_room - text to show other players in room when a player is defeated defeat_text_room - text to show other players in room when a player is defeated
win_text - text to show player when defeating the enemy win_text - text to show player when defeating the enemy
win_text_room - text to show room when a player defeates the enemy win_text_room - text to show room when a player defeates the enemy
respawn_text - text to echo to room when the mob is reset/respawn in that room. respawn_text - text to echo to room when the mob is reset/respawn in that room.
""" """
def at_object_creation(self): def at_object_creation(self):
"Called at object creation." "Called at object creation."
super(Enemy, self).at_object_creation() super(Enemy, self).at_object_creation()
self.db.tutorial_info = "This moving object will attack players in the same room." self.db.tutorial_info = "This moving object will attack players in the same room."
# state machine modes # state machine modes
self.db.roam_mode = True self.db.roam_mode = True
self.db.battle_mode = False self.db.battle_mode = False
self.db.pursue_mode = False self.db.pursue_mode = False
self.db.dead_mode = False self.db.dead_mode = False
# health (change this at creation time) # health (change this at creation time)
self.db.full_health = 20 self.db.full_health = 20
@ -159,8 +159,8 @@ class Enemy(Mob):
self.db.dead_at = time.time() self.db.dead_at = time.time()
self.db.dead_timer = 100 # how long to stay dead self.db.dead_timer = 100 # how long to stay dead
self.db.inactive = True # this is used during creation to make sure the mob doesn't move away self.db.inactive = True # this is used during creation to make sure the mob doesn't move away
# store the last player to hit # store the last player to hit
self.db.last_attacker = None self.db.last_attacker = None
# where to take defeated enemies # where to take defeated enemies
self.db.defeat_location = "darkcell" self.db.defeat_location = "darkcell"
self.scripts.add(AttackTimer) self.scripts.add(AttackTimer)
@ -169,26 +169,26 @@ class Enemy(Mob):
"the irregular event is inherited from Mob class" "the irregular event is inherited from Mob class"
strings = self.db.irregular_echoes strings = self.db.irregular_echoes
if strings: if strings:
self.location.msg_contents(strings[random.randint(0, len(strings) - 1)]) self.location.msg_contents(strings[random.randint(0, len(strings) - 1)])
def roam(self): def roam(self):
"Called by Attack timer. Will move randomly as long as exits are open." "Called by Attack timer. Will move randomly as long as exits are open."
# in this mode, the mob is healed. # in this mode, the mob is healed.
self.db.health = self.db.full_health self.db.health = self.db.full_health
players = [obj for obj in self.location.contents players = [obj for obj in self.location.contents
if utils.inherits_from(obj, BASE_CHARACTER_TYPECLASS) and not obj.is_superuser] if utils.inherits_from(obj, BASE_CHARACTER_TYPECLASS) and not obj.is_superuser]
if players: if players:
# we found players in the room. Attack. # we found players in the room. Attack.
self.db.roam_mode = False self.db.roam_mode = False
self.db.pursue_mode = False self.db.pursue_mode = False
self.db.battle_mode = True self.db.battle_mode = True
elif random.random() < 0.2: elif random.random() < 0.2:
# no players to attack, move about randomly. # no players to attack, move about randomly.
exits = [ex.destination for ex in self.location.exits if ex.access(self, "traverse")] exits = [ex.destination for ex in self.location.exits if ex.access(self, "traverse")]
if exits: if exits:
# Try to make it so the mob doesn't backtrack. # Try to make it so the mob doesn't backtrack.
new_exits = [ex for ex in exits if ex.destination != self.db.last_location] new_exits = [ex for ex in exits if ex.destination != self.db.last_location]
if new_exits: if new_exits:
exits = new_exits exits = new_exits
@ -198,7 +198,7 @@ class Enemy(Mob):
else: else:
# no exits - a dead end room. Respawn back to start. # no exits - a dead end room. Respawn back to start.
self.move_to(self.home) self.move_to(self.home)
def attack(self): def attack(self):
""" """
This is the main mode of combat. It will try to hit players in This is the main mode of combat. It will try to hit players in
@ -206,11 +206,11 @@ class Enemy(Mob):
to the defeat location. to the defeat location.
""" """
last_attacker = self.db.last_attacker last_attacker = self.db.last_attacker
players = [obj for obj in self.location.contents players = [obj for obj in self.location.contents
if utils.inherits_from(obj, BASE_CHARACTER_TYPECLASS) and not obj.is_superuser] if utils.inherits_from(obj, BASE_CHARACTER_TYPECLASS) and not obj.is_superuser]
if players: if players:
# find a target # find a target
if last_attacker in players: if last_attacker in players:
# prefer to attack the player last attacking. # prefer to attack the player last attacking.
target = last_attacker target = last_attacker
@ -226,7 +226,7 @@ class Enemy(Mob):
# analyze result. # analyze result.
if target.db.health <= 0: if target.db.health <= 0:
# we reduced enemy to 0 health. Whisp them off to the prison room. # we reduced enemy to 0 health. Whisp them off to the prison room.
tloc = search_object(self.db.defeat_location, global_search=True) tloc = search_object(self.db.defeat_location)
tstring = self.db.defeat_text tstring = self.db.defeat_text
if not tstring: if not tstring:
tstring = "You feel your conciousness slip away ... you fall to the ground as " tstring = "You feel your conciousness slip away ... you fall to the ground as "
@ -240,24 +240,24 @@ class Enemy(Mob):
target.location = tloc[0] target.location = tloc[0]
tloc[0].at_object_receive(target, self.location) tloc[0].at_object_receive(target, self.location)
elif not ostring: elif not ostring:
ostring = "%s falls to the ground!" % target.key ostring = "%s falls to the ground!" % target.key
self.location.msg_contents(ostring, exclude=[target]) self.location.msg_contents(ostring, exclude=[target])
else: else:
# no players found, this could mean they have fled. Switch to pursue mode. # no players found, this could mean they have fled. Switch to pursue mode.
self.battle_mode = False self.battle_mode = False
self.roam_mode = False self.roam_mode = False
self.pursue_mode = True self.pursue_mode = True
def pursue(self): def pursue(self):
""" """
In pursue mode, the enemy tries to find players in adjoining rooms, preferably In pursue mode, the enemy tries to find players in adjoining rooms, preferably
those that previously attacked it. those that previously attacked it.
""" """
last_attacker = self.db.last_attacker last_attacker = self.db.last_attacker
players = [obj for obj in self.location.contents if utils.inherits_from(obj, BASE_CHARACTER_TYPECLASS) and not obj.is_superuser] players = [obj for obj in self.location.contents if utils.inherits_from(obj, BASE_CHARACTER_TYPECLASS) and not obj.is_superuser]
if players: if players:
# we found players in the room. Maybe we caught up with some, or some walked in on us # we found players in the room. Maybe we caught up with some, or some walked in on us
# before we had time to pursue them. Switch to battle mode. # before we had time to pursue them. Switch to battle mode.
self.battle_mode = True self.battle_mode = True
self.roam_mode = False self.roam_mode = False
self.pursue_mode = False self.pursue_mode = False
@ -266,43 +266,43 @@ class Enemy(Mob):
destinations = [ex.destination for ex in self.location.exits if ex.access(self, "traverse")] destinations = [ex.destination for ex in self.location.exits if ex.access(self, "traverse")]
# find all players in the possible destinations. OBS-we cannot just use the player's # find all players in the possible destinations. OBS-we cannot just use the player's
# current position to move the Enemy; this might have changed when the move is performed, # current position to move the Enemy; this might have changed when the move is performed,
# causing the enemy to teleport out of bounds. # causing the enemy to teleport out of bounds.
players = {} players = {}
for dest in destinations: for dest in destinations:
for obj in [o for o in dest.contents if utils.inherits_from(o, BASE_CHARACTER_TYPECLASS)]: for obj in [o for o in dest.contents if utils.inherits_from(o, BASE_CHARACTER_TYPECLASS)]:
players[obj] = dest players[obj] = dest
if players: if players:
# we found targets. Move to intercept. # we found targets. Move to intercept.
if last_attacker in players: if last_attacker in players:
# preferably the one that last attacked us # preferably the one that last attacked us
self.move_to(players[last_attacker]) self.move_to(players[last_attacker])
else: else:
# otherwise randomly. # otherwise randomly.
key = players.keys()[random.randint(0, len(players) - 1)] key = players.keys()[random.randint(0, len(players) - 1)]
self.move_to(players[key]) self.move_to(players[key])
else: else:
# we found no players nearby. Return to roam mode. # we found no players nearby. Return to roam mode.
self.battle_mode = False self.battle_mode = False
self.roam_mode = True self.roam_mode = True
self.pursue_mode = False self.pursue_mode = False
def at_hit(self, weapon, attacker, damage): def at_hit(self, weapon, attacker, damage):
""" """
Called when this object is hit by an enemy's weapon Called when this object is hit by an enemy's weapon
Should return True if enemy is defeated, False otherwise. Should return True if enemy is defeated, False otherwise.
In the case of players attacking, we handle all the events In the case of players attacking, we handle all the events
and information from here, so the return value is not used. and information from here, so the return value is not used.
""" """
self.db.last_attacker = attacker self.db.last_attacker = attacker
if not self.db.battle_mode: if not self.db.battle_mode:
# we were attacked, so switch to battle mode. # we were attacked, so switch to battle mode.
self.db.roam_mode = False self.db.roam_mode = False
self.db.pursue_mode = False self.db.pursue_mode = False
self.db.battle_mode = True self.db.battle_mode = True
#self.scripts.add(AttackTimer) #self.scripts.add(AttackTimer)
if not weapon.db.magic: if not weapon.db.magic:
# In the tutorial, the enemy is a ghostly apparition, so # In the tutorial, the enemy is a ghostly apparition, so
# only magical weapons can harm it. # only magical weapons can harm it.
@ -310,7 +310,7 @@ class Enemy(Mob):
if not string: if not string:
string = "Your weapon just passes through your enemy, causing no effect!" string = "Your weapon just passes through your enemy, causing no effect!"
attacker.msg(string) attacker.msg(string)
return return
else: else:
# an actual hit # an actual hit
health = float(self.db.health) health = float(self.db.health)
@ -319,10 +319,10 @@ class Enemy(Mob):
if health <= 0: if health <= 0:
string = self.db.win_text string = self.db.win_text
if not string: if not string:
string = "After your last hit, %s folds in on itself, it seems to fade away into nothingness. " % self.key string = "After your last hit, %s folds in on itself, it seems to fade away into nothingness. " % self.key
string += "In a moment there is nothing left but the echoes of its screams. But you have a " string += "In a moment there is nothing left but the echoes of its screams. But you have a "
string += "feeling it is only temporarily weakened. " string += "feeling it is only temporarily weakened. "
string += "You fear it's only a matter of time before it materializes somewhere again." string += "You fear it's only a matter of time before it materializes somewhere again."
attacker.msg(string) attacker.msg(string)
string = self.db.win_text_room string = self.db.win_text_room
if not string: if not string:
@ -331,28 +331,28 @@ class Enemy(Mob):
string += "feeling it is only temporarily weakened. " string += "feeling it is only temporarily weakened. "
string += "You fear it's only a matter of time before it materializes somewhere again." string += "You fear it's only a matter of time before it materializes somewhere again."
self.location.msg_contents(string, exclude=[attacker]) self.location.msg_contents(string, exclude=[attacker])
# put enemy in dead mode and hide it from view. IrregularEvent will bring it back later. # put enemy in dead mode and hide it from view. IrregularEvent will bring it back later.
self.db.roam_mode = False self.db.roam_mode = False
self.db.pursue_mode = False self.db.pursue_mode = False
self.db.battle_mode = False self.db.battle_mode = False
self.db.dead_mode = True self.db.dead_mode = True
self.db.dead_at = time.time() self.db.dead_at = time.time()
self.location = None self.location = None
else: else:
self.location.msg_contents("%s wails, shudders and writhes." % self.key) self.location.msg_contents("%s wails, shudders and writhes." % self.key)
return False return False
def reset(self): def reset(self):
"If the mob was 'dead', respawn it to its home position and reset all modes and damage." "If the mob was 'dead', respawn it to its home position and reset all modes and damage."
if self.db.dead_mode: if self.db.dead_mode:
self.db.health = self.db.full_health self.db.health = self.db.full_health
self.db.roam_mode = True self.db.roam_mode = True
self.db.pursue_mode = False self.db.pursue_mode = False
self.db.battle_mode = False self.db.battle_mode = False
self.db.dead_mode = False self.db.dead_mode = False
self.location = self.home self.location = self.home
string = self.db.respawn_text string = self.db.respawn_text
if not string: if not string:
string = "%s fades into existence from out of thin air. It's looking pissed." % self.key string = "%s fades into existence from out of thin air. It's looking pissed." % self.key
self.location.msg_contents(string) self.location.msg_contents(string)

View file

@ -1,32 +1,32 @@
""" """
TutorialWorld - basic objects - Griatch 2011 TutorialWorld - basic objects - Griatch 2011
This module holds all "dead" object definitions for This module holds all "dead" object definitions for
the tutorial world. Object-commands and -cmdsets the tutorial world. Object-commands and -cmdsets
are also defined here, together with the object. are also defined here, together with the object.
Objects: Objects:
TutorialObject TutorialObject
Readable Readable
Climbable Climbable
Obelisk Obelisk
LightSource LightSource
CrumblingWall CrumblingWall
Weapon Weapon
WeaponRack WeaponRack
""" """
import time, random import time, random
from ev import utils, create_object from ev import utils, create_object
from ev import Object, Exit, Command, CmdSet, Script from ev import Object, Exit, Command, CmdSet, Script
#------------------------------------------------------------ #------------------------------------------------------------
# #
# TutorialObject # TutorialObject
# #
# 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"
@ -35,7 +35,7 @@ from ev import Object, Exit, Command, CmdSet, Script
# #
# TutorialObjects may also be "reset". What the reset means # TutorialObjects may also be "reset". What the reset means
# is up to the object. It can be the resetting of the world # is up to the object. It can be the resetting of the world
# itself, or the removal of an inventory item from a # itself, or the removal of an inventory item from a
# character's inventory when leaving the tutorial, for example. # character's inventory when leaving the tutorial, for example.
# #
#------------------------------------------------------------ #------------------------------------------------------------
@ -51,7 +51,7 @@ class TutorialObject(Object):
super(TutorialObject, self).at_object_creation() super(TutorialObject, self).at_object_creation()
self.db.tutorial_info = "No tutorial info is available for this object." self.db.tutorial_info = "No tutorial info is available for this object."
#self.db.last_reset = time.time() #self.db.last_reset = time.time()
def reset(self): def reset(self):
"Resets the object, whatever that may mean." "Resets the object, whatever that may mean."
self.location = self.home self.location = self.home
@ -59,13 +59,13 @@ class TutorialObject(Object):
#------------------------------------------------------------ #------------------------------------------------------------
# #
# Readable - an object one can "read". # Readable - an object one can "read".
# #
#------------------------------------------------------------ #------------------------------------------------------------
class CmdRead(Command): class CmdRead(Command):
""" """
Usage: Usage:
read [obj] read [obj]
Read some text. Read some text.
@ -82,8 +82,8 @@ class CmdRead(Command):
else: else:
obj = self.obj obj = self.obj
if not obj: if not obj:
return return
# we want an attribute read_text to be defined. # we want an attribute read_text to be defined.
readtext = obj.db.readable_text readtext = obj.db.readable_text
if readtext: if readtext:
string = "You read {C%s{n:\n %s" % (obj.key, readtext) string = "You read {C%s{n:\n %s" % (obj.key, readtext)
@ -100,7 +100,7 @@ class CmdSetReadable(CmdSet):
class Readable(TutorialObject): class Readable(TutorialObject):
""" """
This object defines some attributes and defines a read method on itself. This object defines some attributes and defines a read method on itself.
""" """
def at_object_creation(self): def at_object_creation(self):
"Called when object is created" "Called when object is created"
super(Readable, self).at_object_creation() super(Readable, self).at_object_creation()
@ -116,14 +116,14 @@ 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.
# #
#------------------------------------------------------------ #------------------------------------------------------------
class CmdClimb(Command): class CmdClimb(Command):
""" """
Usage: Usage:
climb <object> climb <object>
""" """
key = "climb" key = "climb"
locks = "cmd:all()" locks = "cmd:all()"
@ -140,7 +140,7 @@ class CmdClimb(Command):
return return
if obj != self.obj: if obj != self.obj:
self.caller.msg("Try as you might, you cannot climb that.") self.caller.msg("Try as you might, you cannot climb that.")
return return
ostring = self.obj.db.climb_text ostring = self.obj.db.climb_text
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
@ -153,10 +153,10 @@ class CmdSetClimbable(CmdSet):
"populate set" "populate set"
self.add(CmdClimb()) self.add(CmdClimb())
class Climbable(TutorialObject): class Climbable(TutorialObject):
"A climbable object." "A climbable object."
def at_object_creation(self): def at_object_creation(self):
"Called at initial creation only" "Called at initial creation only"
self.cmdset.add_default(CmdSetClimbable, permanent=True) self.cmdset.add_default(CmdSetClimbable, permanent=True)
@ -165,14 +165,14 @@ 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 that causes it to look slightly different every # method that causes it to look slightly different every
# time one looks at it. Since what you actually see # time one looks at it. Since what you actually see
# is a part of a game puzzle, the act of looking also # is a part of a game puzzle, the act of looking also
# stores a key attribute on the looking object for later # stores a key attribute on the looking object for later
# reference. # reference.
# #
#------------------------------------------------------------ #------------------------------------------------------------
@ -181,36 +181,36 @@ OBELISK_DESCS = ["You can briefly make out the image of {ba woman with a blue bi
"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):
""" """
This object changes its description randomly. This object changes its description randomly.
""" """
def at_object_creation(self): def at_object_creation(self):
"Called when object is created." "Called when object is created."
super(Obelisk, self).at_object_creation() 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." 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 # make sure this can never be picked up
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." "Overload the default version of this hook."
clueindex = random.randint(0, len(OBELISK_DESCS)-1) clueindex = random.randint(0, len(OBELISK_DESCS)-1)
# set this description # set this description
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. " string += "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.
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 db.desc we just set)
return super(Obelisk, self).return_appearance(caller) return super(Obelisk, self).return_appearance(caller)
#------------------------------------------------------------ #------------------------------------------------------------
# #
# LightSource # LightSource
# #
# This object that emits light and can be # This object that emits light and can be
# turned on or off. It must be carried to use and has only # turned on or off. It must be carried to use and has only
# a limited burn-time. # a limited burn-time.
# When burned out, it will remove itself from the carrying # When burned out, it will remove itself from the carrying
@ -228,7 +228,7 @@ class StateLightSourceOn(Script):
self.key = "lightsourceBurn" self.key = "lightsourceBurn"
self.desc = "Keeps lightsources burning." self.desc = "Keeps lightsources burning."
self.start_delay = True # only fire after self.interval s. self.start_delay = True # only fire after self.interval s.
self.repeats = 1 # only run once. self.repeats = 1 # only run once.
self.persistent = True # survive a server reboot. self.persistent = True # survive a server reboot.
def at_start(self): def at_start(self):
@ -237,15 +237,15 @@ class StateLightSourceOn(Script):
self.db.script_started = time.time() self.db.script_started = time.time()
def at_repeat(self): def at_repeat(self):
# this is only called when torch has burnt out # this is only called when torch has burnt out
self.obj.db.burntime = -1 self.obj.db.burntime = -1
self.obj.reset() self.obj.reset()
def at_stop(self): def at_stop(self):
""" """
Since the user may also turn off the light Since the user may also turn off the light
prematurely, this hook will store the current prematurely, this hook will store the current
burntime. burntime.
""" """
# calculate remaining burntime, if object is not # calculate remaining burntime, if object is not
# already deleted (because it burned out) # already deleted (because it burned out)
@ -264,7 +264,7 @@ class StateLightSourceOn(Script):
class CmdLightSourceOn(Command): class CmdLightSourceOn(Command):
""" """
Switches on the lightsource. Switches on the lightsource.
""" """
key = "on" key = "on"
aliases = ["switch on", "turn on", "light"] aliases = ["switch on", "turn on", "light"]
@ -273,16 +273,16 @@ class CmdLightSourceOn(Command):
def func(self): def func(self):
"Implements the command" "Implements the command"
if self.obj.db.is_active: if self.obj.db.is_active:
self.caller.msg("%s is already burning." % self.obj.key) self.caller.msg("%s is already burning." % self.obj.key)
else: else:
# set lightsource to active # set lightsource to active
self.obj.db.is_active = True self.obj.db.is_active = True
# activate the script to track burn-time. # activate the script to track burn-time.
self.obj.scripts.add(StateLightSourceOn) self.obj.scripts.add(StateLightSourceOn)
self.caller.msg("{gYou light {C%s.{n" % self.obj.key) 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])
# we run script validation on the room to make light/dark states tick. # we run script validation on the room to make light/dark states tick.
self.caller.location.scripts.validate() self.caller.location.scripts.validate()
# look around # look around
@ -313,7 +313,7 @@ class CmdLightSourceOff(Command):
self.caller.location.scripts.validate() self.caller.location.scripts.validate()
self.caller.execute_cmd("look") self.caller.execute_cmd("look")
# we run script validation on the room to make light/dark states tick. # we run script validation on the room to make light/dark states tick.
class CmdSetLightSource(CmdSet): class CmdSetLightSource(CmdSet):
"CmdSet for the lightsource commands" "CmdSet for the lightsource commands"
@ -322,69 +322,69 @@ class CmdSetLightSource(CmdSet):
"called at cmdset creation" "called at cmdset creation"
self.add(CmdLightSourceOn()) self.add(CmdLightSourceOn())
self.add(CmdLightSourceOff()) 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 default is the When burned out, lightsource will be moved to its home - which by default is the
location it was first created at. location it was first created at.
""" """
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 turned on off and has a timed script controlling it."
self.db.is_active = False self.db.is_active = False
self.db.burntime = 60*3 # 3 minutes self.db.burntime = 60*3 # 3 minutes
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 commands
self.cmdset.add_default(CmdSetLightSource, permanent=True) self.cmdset.add_default(CmdSetLightSource, permanent=True)
def reset(self): def reset(self):
""" """
Can be called by tutorial world runner, or by the script when the lightsource Can be called by tutorial world runner, or by the script when the lightsource
has burned out. has burned out.
""" """
if self.db.burntime <= 0: if self.db.burntime <= 0:
# light burned out. Since the lightsources's "location" should be # light burned out. Since the lightsources's "location" should be
# a character, notify them this way. # a character, notify them this way.
try: try:
loc = self.location.location loc = self.location.location
except AttributeError: except AttributeError:
loc = self.location loc = self.location
loc.msg_contents("{c%s{n {Rburns out.{n" % self.key) loc.msg_contents("{c%s{n {Rburns out.{n" % self.key)
self.db.is_active = False self.db.is_active = False
try: try:
# validate in holders current room, if possible # validate in holders current room, if possible
self.location.location.scripts.validate() self.location.location.scripts.validate()
except AttributeError: except AttributeError:
# maybe it was dropped, try validating at current location. # maybe it was dropped, try validating at current location.
try: try:
self.location.scripts.validate() self.location.scripts.validate()
except AttributeError,e: except AttributeError,e:
pass pass
self.delete() self.delete()
#------------------------------------------------------------ #------------------------------------------------------------
# #
# Crumbling wall - unique exit # Crumbling wall - unique exit
# #
# 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 is currently 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
# position - when they have, the "press button" command # position - when they have, the "press button" command
# is made available and the Exit is made traversable. # is made available and the Exit is made traversable.
# #
#------------------------------------------------------------ #------------------------------------------------------------
# There are four roots - two horizontal and two vertically # There are four roots - two horizontal and two vertically
# running roots. Each can have three positions: top/middle/bottom # running roots. Each can have three positions: top/middle/bottom
# and left/middle/right respectively. There can be any number of # and left/middle/right respectively. There can be any number of
# roots hanging through the middle position, but only one each # roots hanging through the middle position, but only one each
# 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?)
@ -396,35 +396,35 @@ class CmdShiftRoot(Command):
shift blue root left/right shift blue root left/right
shift red root left/right shift red root left/right
shift yellow root up/down shift yellow root up/down
shift green root up/down shift green root up/down
""" """
key = "shift" key = "shift"
aliases = ["move"] aliases = ["move"]
# the locattr() lock looks for the attribute is_dark on the current room. # the locattr() lock looks for the attribute is_dark on the current room.
locks = "cmd:not locattr(is_dark)" locks = "cmd:not locattr(is_dark)"
help_category = "TutorialWorld" help_category = "TutorialWorld"
def parse(self): def parse(self):
"custom parser; split input by spaces" "custom parser; split input by spaces"
self.arglist = self.args.strip().split() self.arglist = self.args.strip().split()
def func(self): def func(self):
""" """
Implement the command. Implement the command.
blue/red - vertical roots blue/red - vertical roots
yellow/green - horizontal roots yellow/green - horizontal roots
""" """
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:
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
@ -432,7 +432,7 @@ class CmdShiftRoot(Command):
if not color in root_pos: if not color in root_pos:
self.caller.msg("No such root to move.") self.caller.msg("No such root to move.")
return return
# first, vertical roots (red/blue) - can be moved left/right # first, vertical roots (red/blue) - can be moved left/right
if color == "red": if color == "red":
@ -496,11 +496,11 @@ 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 # 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 self.caller.db.crumbling_wall_found_button = 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 ...")
class CmdPressButton(Command): class CmdPressButton(Command):
@ -511,19 +511,19 @@ class CmdPressButton(Command):
aliases = ["press button", "button", "push", "push button"] 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. 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" help_category = "TutorialWorld"
def func(self): def func(self):
"Implements the command" "Implements the command"
if self.caller.db.crumbling_wall_found_exit: if self.caller.db.crumbling_wall_found_exit:
# we already pushed the button # we already pushed the button
self.caller.msg("The button folded away when the secret passage opened. You cannot push it again.") self.caller.msg("The button folded away when the secret passage opened. You cannot push it again.")
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 " 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." string += "{wpassage{n opens, dust and pebbles rumbling as part of the wall moves aside."
# we are done - this will make the exit traversable! # we are done - this will make the exit traversable!
self.caller.db.crumbling_wall_found_exit = True self.caller.db.crumbling_wall_found_exit = True
@ -531,7 +531,7 @@ class CmdPressButton(Command):
eloc = self.caller.search(self.obj.db.destination, global_search=True) eloc = self.caller.search(self.obj.db.destination, global_search=True)
if not eloc: if not eloc:
self.caller.msg("The exit leads nowhere, there's just more stone behind it ...") self.caller.msg("The exit leads nowhere, there's just more stone behind it ...")
return return
self.obj.destination = eloc self.obj.destination = eloc
self.caller.msg(string) self.caller.msg(string)
@ -539,22 +539,22 @@ class CmdSetCrumblingWall(CmdSet):
"Group the commands for crumblingWall" "Group the commands for crumblingWall"
key = "crumblingwall_cmdset" key = "crumblingwall_cmdset"
def at_cmdset_creation(self): def at_cmdset_creation(self):
"called when object is first created." "called when object is first created."
self.add(CmdShiftRoot()) self.add(CmdShiftRoot())
self.add(CmdPressButton()) self.add(CmdPressButton())
class CrumblingWall(TutorialObject, Exit): class CrumblingWall(TutorialObject, Exit):
""" """
The CrumblingWall can be examined in various The CrumblingWall can be examined in various
ways, but only if a lit light source is in the room. The traversal 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 itself is blocked by a traverse: lock on the exit that only
allows passage if a certain attribute is set on the trying allows passage if a certain attribute is set on the trying
player. 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 at_object_creation(self): def at_object_creation(self):
"called when the object is first created." "called when the object is first created."
@ -567,17 +567,17 @@ class CrumblingWall(TutorialObject, Exit):
self.locks.add("cmd:not locattr(is_dark)") 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." 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". # the lock is important for this exit; we only allow passage if we "found exit".
self.locks.add("traverse:attr(crumbling_wall_found_exit)") self.locks.add("traverse:attr(crumbling_wall_found_exit)")
# set cmdset # set cmdset
self.cmdset.add(CmdSetCrumblingWall, permanent=True) self.cmdset.add(CmdSetCrumblingWall, permanent=True)
# starting root positions. H1/H2 are the horizontally hanging roots, V1/V2 the # 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 # 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. # 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. # all may have value 0, but never 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}
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 ",
@ -595,10 +595,10 @@ class CrumblingWall(TutorialObject, Exit):
string = rootnames[root] + hpos[ipos] string = rootnames[root] + hpos[ipos]
else: else:
string = rootnames[root] + vpos[ipos] string = rootnames[root] + vpos[ipos]
return string return string
def return_appearance(self, caller): def return_appearance(self, caller):
"This is called when someone looks at the wall. We need to echo the current root positions." "This is called when someone looks at the wall. We need to echo the current root positions."
if caller.db.crumbling_wall_found_button: if caller.db.crumbling_wall_found_button:
string = "Having moved all the roots aside, you find that the center of the wall, " 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 += "previously hidden by the vegetation, hid a curious square depression. It was maybe once "
@ -609,11 +609,11 @@ class CrumblingWall(TutorialObject, Exit):
string += "The roots (or whatever they are - some of them are covered in small non-descript flowers) " 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" string += "crisscross the wall, making it hard to clearly see its stony surface.\n"
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 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):
"This is called after we traversed this exit. Cleans up and resets the puzzle." "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_button
@ -621,7 +621,7 @@ class CrumblingWall(TutorialObject, Exit):
self.reset() self.reset()
def at_failed_traverse(self, traverser): def at_failed_traverse(self, traverser):
"This is called if the player fails to pass the Exit." "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) traverser.msg("No matter how you try, you cannot force yourself through %s." % self.key)
def reset(self): def reset(self):
@ -629,9 +629,9 @@ class CrumblingWall(TutorialObject, 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: for obj in self.location.contents:
# clear eventual puzzle-solved attribues on everyone that didn't get out in time. They # clear eventual puzzle-solved attribues on everyone that didn't get out in time. They
# have to try again. # have to try again.
del obj.db.crumbling_wall_found_exit del obj.db.crumbling_wall_found_exit
# 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},
{"yellow":0, "green":0, "red":0, "blue":0}, {"yellow":0, "green":0, "red":0, "blue":0},
@ -639,31 +639,31 @@ class CrumblingWall(TutorialObject, Exit):
{"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 = start_pos[random.randint(0, 4)]
self.destination = None self.destination = None
#------------------------------------------------------------ #------------------------------------------------------------
# #
# Weapon - object type # Weapon - object type
# #
# A weapon is necessary in order to fight in the tutorial # A weapon is necessary in order to fight in the tutorial
# world. A weapon (which here is assumed to be a bladed # world. A weapon (which here is assumed to be a bladed
# melee weapon for close combat) has three commands, # melee weapon for close combat) has three commands,
# stab, slash and defend. Weapons also have a property "magic" # stab, slash and defend. Weapons also have a property "magic"
# to determine if they are usable against certain enemies. # to determine if they are usable against certain enemies.
# #
# Since Characters don't have special skills in the tutorial, # Since Characters don't have special skills in the tutorial,
# we let the weapon itself determine how easy/hard it is # we let the weapon itself determine how easy/hard it is
# to hit with it, and how much damage it can do. # to hit with it, and how much damage it can do.
# #
#------------------------------------------------------------ #------------------------------------------------------------
class CmdAttack(Command): class CmdAttack(Command):
""" """
Attack the enemy. Commands: Attack the enemy. Commands:
stab <enemy> stab <enemy>
slash <enemy> slash <enemy>
parry parry
stab - (thrust) makes a lot of damage but is harder to hit with. 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. slash - is easier to land, but does not make as much damage.
@ -672,7 +672,7 @@ class CmdAttack(Command):
""" """
# this is an example of implementing many commands as a single command class, # this is an example of implementing many commands as a single command class,
# using the given command alias to separate between them. # using the given command alias to separate between them.
key = "attack" key = "attack"
aliases = ["hit","kill", "fight", "thrust", "pierce", "stab", "slash", "chop", "parry", "defend"] aliases = ["hit","kill", "fight", "thrust", "pierce", "stab", "slash", "chop", "parry", "defend"]
@ -683,50 +683,50 @@ class CmdAttack(Command):
"Implements the stab" "Implements the stab"
cmdstring = self.cmdstring cmdstring = self.cmdstring
if cmdstring in ("attack", "fight"): if cmdstring in ("attack", "fight"):
string = "How do you want to fight? Choose one of 'stab', 'slash' or 'defend'." string = "How do you want to fight? Choose one of 'stab', 'slash' or 'defend'."
self.caller.msg(string) self.caller.msg(string)
return return
# parry mode # parry mode
if cmdstring in ("parry", "defend"): if cmdstring in ("parry", "defend"):
string = "You raise your weapon in a defensive pose, ready to block the next enemy attack." string = "You raise your weapon in a defensive pose, ready to block the next enemy attack."
self.caller.msg(string) self.caller.msg(string)
self.caller.db.combat_parry_mode = True self.caller.db.combat_parry_mode = True
self.caller.location.msg_contents("%s takes a defensive stance" % self.caller, exclude=[self.caller]) self.caller.location.msg_contents("%s takes a defensive stance" % self.caller, exclude=[self.caller])
return return
if not self.args: if not self.args:
self.caller.msg("Who do you attack?") self.caller.msg("Who do you attack?")
return return
target = self.caller.search(self.args.strip()) target = self.caller.search(self.args.strip())
if not target: if not target:
return return
string = "" string = ""
tstring = "" tstring = ""
ostring = "" ostring = ""
if cmdstring in ("thrust", "pierce", "stab"): if cmdstring in ("thrust", "pierce", "stab"):
hit = float(self.obj.db.hit) * 0.7 # modified due to stab hit = float(self.obj.db.hit) * 0.7 # modified due to stab
damage = self.obj.db.damage * 2 # modified due to stab damage = self.obj.db.damage * 2 # modified due to stab
string = "You stab with %s. " % self.obj.key string = "You stab with %s. " % self.obj.key
tstring = "%s stabs at you with %s. " % (self.caller.key, 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) ostring = "%s stabs at %s with %s. " % (self.caller.key, target.key, self.obj.key)
self.caller.db.combat_parry_mode = False self.caller.db.combat_parry_mode = False
elif cmdstring in ("slash", "chop"): elif cmdstring in ("slash", "chop"):
hit = float(self.obj.db.hit) # un modified due to slash hit = float(self.obj.db.hit) # un modified due to slash
damage = self.obj.db.damage # un modified due to slash damage = self.obj.db.damage # un modified due to slash
string = "You slash with %s. " % self.obj.key string = "You slash with %s. " % self.obj.key
tstring = "%s slash at you with %s. " % (self.caller.key, 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) ostring = "%s slash at %s with %s. " % (self.caller.key, target.key, self.obj.key)
self.caller.db.combat_parry_mode = False self.caller.db.combat_parry_mode = False
else: else:
self.caller.msg("You fumble with your weapon, unable to choose an appropriate action...") 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.location.msg_contents("%s fumbles with their weapon." % self.obj.key)
self.caller.db.combat_parry_mode = False self.caller.db.combat_parry_mode = False
return return
if target.db.combat_parry_mode: if target.db.combat_parry_mode:
# target is defensive; even harder to hit! # target is defensive; even harder to hit!
@ -737,17 +737,17 @@ class CmdAttack(Command):
self.caller.msg(string + "{gIt's a hit!{n") self.caller.msg(string + "{gIt's a hit!{n")
target.msg(tstring + "{rIt'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]) self.caller.location.msg_contents(ostring + "It's a hit!", exclude=[target,self.caller])
# call enemy hook # call enemy hook
if hasattr(target, "at_hit"): if hasattr(target, "at_hit"):
# should return True if target is defeated, False otherwise. # should return True if target is defeated, False otherwise.
return target.at_hit(self.obj, self.caller, damage) return target.at_hit(self.obj, self.caller, damage)
elif target.db.health: elif target.db.health:
target.db.health -= damage target.db.health -= damage
else: else:
# sorry, impossible to fight this enemy ... # sorry, impossible to fight this enemy ...
self.caller.msg("The enemy seems unaffacted.") self.caller.msg("The enemy seems unaffacted.")
return False return False
else: else:
self.caller.msg(string + "{rYou miss.{n") self.caller.msg(string + "{rYou miss.{n")
target.msg(tstring + "{gThey miss you.{n") target.msg(tstring + "{gThey miss you.{n")
@ -775,9 +775,9 @@ class Weapon(TutorialObject):
self.db.hit = 0.4 # hit chance self.db.hit = 0.4 # hit chance
self.db.parry = 0.8 # parry chance self.db.parry = 0.8 # parry chance
self.damage = 8.0 self.damage = 8.0
self.magic = False self.magic = False
self.cmdset.add_default(CmdSetWeapon, permanent=True) self.cmdset.add_default(CmdSetWeapon, permanent=True)
def reset(self): def reset(self):
"When reset, the weapon is simply deleted, unless it has a place to return to." "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: if self.location.has_player and self.home == self.location:
@ -787,8 +787,8 @@ class Weapon(TutorialObject):
self.location = self.home self.location = self.home
#------------------------------------------------------------ #------------------------------------------------------------
# #
# Weapon rack - spawns weapons # Weapon rack - spawns weapons
# #
#------------------------------------------------------------ #------------------------------------------------------------
@ -809,7 +809,7 @@ class CmdGetWeapon(Command):
rack_id = self.obj.db.rack_id rack_id = self.obj.db.rack_id
if eval("self.caller.db.%s" % rack_id): if eval("self.caller.db.%s" % rack_id):
# we don't allow to take more than one weapon from rack. # we don't allow to take more than one weapon from rack.
self.caller.msg("%s has no more to offer." % self.obj.name) self.caller.msg("%s has no more to offer." % self.obj.name)
else: else:
dmg, name, aliases, desc, magic = self.obj.randomize_type() dmg, name, aliases, desc, magic = self.obj.randomize_type()
@ -834,8 +834,8 @@ class CmdSetWeaponRack(CmdSet):
key = "weaponrack_cmdset" key = "weaponrack_cmdset"
mergemode = "Replace" mergemode = "Replace"
def at_cmdset_creation(self): def at_cmdset_creation(self):
self.add(CmdGetWeapon()) self.add(CmdGetWeapon())
class WeaponRack(TutorialObject): class WeaponRack(TutorialObject):
""" """
This will spawn a new weapon for the player unless the player already has one from this rack. This will spawn a new weapon for the player unless the player already has one from this rack.
@ -852,7 +852,7 @@ class WeaponRack(TutorialObject):
self.rack_id = "weaponrack_1" self.rack_id = "weaponrack_1"
self.db.min_dmg = 1.0 self.db.min_dmg = 1.0
self.db.max_dmg = 4.0 self.db.max_dmg = 4.0
self.db.magic = False self.db.magic = False
def randomize_type(self): def randomize_type(self):
""" """
@ -867,26 +867,26 @@ class WeaponRack(TutorialObject):
name = "Knife" name = "Knife"
desc = "A rusty kitchen knife. Better than nothing." desc = "A rusty kitchen knife. Better than nothing."
elif dmg < 2.0: elif dmg < 2.0:
name = "Rusty dagger" name = "Rusty dagger"
desc = "A double-edged dagger with nicked edge. It has a wooden handle." desc = "A double-edged dagger with nicked edge. It has a wooden handle."
elif dmg < 3.0: elif dmg < 3.0:
name = "Sword" name = "Sword"
desc = "A rusty shortsword. It has leather wrapped around the handle." desc = "A rusty shortsword. It has leather wrapped around the handle."
elif dmg < 4.0: elif dmg < 4.0:
name = "Club" name = "Club"
desc = "A heavy wooden club with some rusty spikes in it." desc = "A heavy wooden club with some rusty spikes in it."
elif dmg < 5.0: elif dmg < 5.0:
name = "Ornate Longsword" name = "Ornate Longsword"
aliases.extend(["longsword","ornate"]) aliases.extend(["longsword","ornate"])
desc = "A fine longsword." desc = "A fine longsword."
elif dmg < 6.0: elif dmg < 6.0:
name = "Runeaxe" name = "Runeaxe"
aliases.extend(["rune","axe"]) aliases.extend(["rune","axe"])
desc = "A single-bladed axe, heavy but yet easy to use." desc = "A single-bladed axe, heavy but yet easy to use."
elif dmg < 7.0: elif dmg < 7.0:
name = "Broadsword named Thruning" name = "Broadsword named Thruning"
aliases.extend(["thruning","broadsword"]) aliases.extend(["thruning","broadsword"])
desc = "This heavy bladed weapon is marked with the name 'Thruning'. It is very powerful in skilled hands." desc = "This heavy bladed weapon is marked with the name 'Thruning'. It is very powerful in skilled hands."
elif dmg < 8.0: elif dmg < 8.0:
name = "Silver Warhammer" name = "Silver Warhammer"
aliases.append("warhammer") aliases.append("warhammer")
@ -901,8 +901,8 @@ class WeaponRack(TutorialObject):
desc = "This massive sword is large as you are tall. Its metal shine with a bluish glow." desc = "This massive sword is large as you are tall. Its metal shine with a bluish glow."
else: else:
name = "The Hawkblade" name = "The Hawkblade"
aliases.append("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." 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: if dmg < 9 and magic:
desc += "\nThe metal seems to glow faintly, as if imbued with more power than what is immediately apparent." desc += "\nThe metal seems to glow faintly, as if imbued with more power than what is immediately apparent."
return dmg, name, aliases, desc, magic return dmg, name, aliases, desc, magic

View file

@ -365,7 +365,7 @@ class TeleportRoom(TutorialRoom):
# passed the puzzle # passed the puzzle
teleport_to = self.db.success_teleport_to # this is a room name teleport_to = self.db.success_teleport_to # this is a room name
results = search_object(teleport_to, global_search=True) results = search_object(teleport_to)
if not results or len(results) > 1: if not results or len(results) > 1:
# we cannot move anywhere since no valid target was found. # we cannot move anywhere since no valid target was found.
print "no valid teleport target for %s was found." % teleport_to print "no valid teleport target for %s was found." % teleport_to