Merging mainline changes.

This commit is contained in:
lagos 2012-09-27 23:27:45 -07:00
commit fe54326d5f
10 changed files with 525 additions and 366 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
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
# 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
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):
"This is called when the object is first created."
@ -35,11 +35,11 @@ class Mob(tut_objects.TutorialObject):
self.scripts.add(tut_scripts.IrregularEvent)
# this is a good attribute for exits to look for, to block
# a mob from entering certain exits.
self.db.is_mob = True
self.db.last_location = None
# only when True will the mob move.
self.db.roam_mode = True
# a mob from entering certain exits.
self.db.is_mob = True
self.db.last_location = None
# only when True will the mob move.
self.db.roam_mode = True
def announce_move_from(self, destination):
"Called just before moving"
@ -53,8 +53,8 @@ class Mob(tut_objects.TutorialObject):
"Called at irregular intervals. Moves the mob."
if self.roam_mode:
exits = [ex for ex in self.location.exits if ex.access(self, "traverse")]
if exits:
# Try to make it so the mob doesn't backtrack.
if exits:
# Try to make it so the mob doesn't backtrack.
new_exits = [ex for ex in exits if ex.destination != self.db.last_location]
if 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
# in its vicinity. An enemy will try to attack characters
# An enemy is a mobile that is aggressive against players
# in its vicinity. An enemy will try to attack characters
# 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
# attack.
# An enemy needs to have a Weapon object in order to
# attack.
#
# This particular tutorial enemy is a ghostly apparition that can 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):
"This sets up the script"
self.key = "AttackTimer"
self.key = "AttackTimer"
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.persistent = True
self.persistent = True
def at_repeat(self):
"Called every self.interval seconds."
"Called every self.interval seconds."
if self.obj.db.inactive:
return
return
#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()
#return
elif self.obj.db.battle_mode:
#print "attack"
self.obj.attack()
return
return
elif self.obj.db.pursue_mode:
#print "pursue"
self.obj.pursue()
#return
else:
#dead mode. Wait for respawn.
#dead mode. Wait for respawn.
dead_at = self.db.dead_at
if not dead_at:
self.db.dead_at = time.time()
if (time.time() - self.db.dead_at) > self.db.dead_timer:
self.obj.reset()
class Enemy(Mob):
"""
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.
An enemy can be in four modes:
An enemy can be in four modes:
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
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
win_text - text to show player when defeating 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):
"Called at object creation."
super(Enemy, self).at_object_creation()
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.battle_mode = False
self.db.pursue_mode = False
self.db.pursue_mode = False
self.db.dead_mode = False
# health (change this at creation time)
self.db.full_health = 20
@ -159,8 +159,8 @@ class Enemy(Mob):
self.db.dead_at = time.time()
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
# store the last player to hit
self.db.last_attacker = None
# store the last player to hit
self.db.last_attacker = None
# where to take defeated enemies
self.db.defeat_location = "darkcell"
self.scripts.add(AttackTimer)
@ -169,26 +169,26 @@ class Enemy(Mob):
"the irregular event is inherited from Mob class"
strings = self.db.irregular_echoes
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):
"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
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 players:
# we found players in the room. Attack.
self.db.roam_mode = False
if players:
# we found players in the room. Attack.
self.db.roam_mode = False
self.db.pursue_mode = False
self.db.battle_mode = True
self.db.battle_mode = True
elif random.random() < 0.2:
# no players to attack, move about randomly.
exits = [ex.destination for ex in self.location.exits if ex.access(self, "traverse")]
if exits:
# Try to make it so the mob doesn't backtrack.
if exits:
# Try to make it so the mob doesn't backtrack.
new_exits = [ex for ex in exits if ex.destination != self.db.last_location]
if new_exits:
exits = new_exits
@ -198,7 +198,7 @@ class Enemy(Mob):
else:
# no exits - a dead end room. Respawn back to start.
self.move_to(self.home)
def attack(self):
"""
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.
"""
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:
# find a target
# find a target
if last_attacker in players:
# prefer to attack the player last attacking.
target = last_attacker
@ -226,7 +226,7 @@ class Enemy(Mob):
# analyze result.
if target.db.health <= 0:
# 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
if not tstring:
tstring = "You feel your conciousness slip away ... you fall to the ground as "
@ -240,24 +240,24 @@ class Enemy(Mob):
target.location = tloc[0]
tloc[0].at_object_receive(target, self.location)
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])
else:
# no players found, this could mean they have fled. Switch to pursue mode.
self.battle_mode = False
self.roam_mode = False
self.pursue_mode = True
def pursue(self):
"""
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
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:
# 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.roam_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")]
# 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,
# causing the enemy to teleport out of bounds.
# causing the enemy to teleport out of bounds.
players = {}
for dest in destinations:
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:
# we found targets. Move to intercept.
if last_attacker in players:
# preferably the one that last attacked us
self.move_to(players[last_attacker])
else:
# otherwise randomly.
# otherwise randomly.
key = players.keys()[random.randint(0, len(players) - 1)]
self.move_to(players[key])
else:
# we found no players nearby. Return to roam mode.
# we found no players nearby. Return to roam mode.
self.battle_mode = False
self.roam_mode = True
self.pursue_mode = False
def at_hit(self, weapon, attacker, damage):
"""
Called when this object is hit by an enemy's weapon
Should return True if enemy is defeated, False otherwise.
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:
# we were attacked, so switch to battle mode.
self.db.roam_mode = False
self.db.pursue_mode = False
self.db.battle_mode = True
self.db.pursue_mode = False
self.db.battle_mode = True
#self.scripts.add(AttackTimer)
if not weapon.db.magic:
# In the tutorial, the enemy is a ghostly apparition, so
# only magical weapons can harm it.
@ -310,7 +310,7 @@ class Enemy(Mob):
if not string:
string = "Your weapon just passes through your enemy, causing no effect!"
attacker.msg(string)
return
return
else:
# an actual hit
health = float(self.db.health)
@ -319,10 +319,10 @@ class Enemy(Mob):
if health <= 0:
string = self.db.win_text
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 += "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)
string = self.db.win_text_room
if not string:
@ -331,28 +331,28 @@ class Enemy(Mob):
string += "feeling it is only temporarily weakened. "
string += "You fear it's only a matter of time before it materializes somewhere again."
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.pursue_mode = False
self.db.battle_mode = False
self.db.dead_mode = True
self.db.pursue_mode = False
self.db.battle_mode = False
self.db.dead_mode = True
self.db.dead_at = time.time()
self.location = None
self.location = None
else:
self.location.msg_contents("%s wails, shudders and writhes." % self.key)
return False
return False
def reset(self):
"If the mob was 'dead', respawn it to its home position and reset all modes and damage."
if self.db.dead_mode:
self.db.health = self.db.full_health
self.db.roam_mode = True
self.db.pursue_mode = False
self.db.battle_mode = False
self.db.dead_mode = False
self.location = self.home
self.db.pursue_mode = False
self.db.battle_mode = False
self.db.dead_mode = False
self.location = self.home
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
self.location.msg_contents(string)

View file

@ -1,32 +1,32 @@
"""
TutorialWorld - basic objects - Griatch 2011
This module holds all "dead" object definitions for
the tutorial world. Object-commands and -cmdsets
This module holds all "dead" object definitions for
the tutorial world. Object-commands and -cmdsets
are also defined here, together with the object.
Objects:
Objects:
TutorialObject
Readable
Readable
Climbable
Obelisk
LightSource
Obelisk
LightSource
CrumblingWall
Weapon
Weapon
WeaponRack
"""
import time, random
import time, random
from ev import utils, create_object
from ev import Object, Exit, Command, CmdSet, Script
#------------------------------------------------------------
#
# TutorialObject
# TutorialObject
#
# The TutorialObject is the base class for all items
# 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
# 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.
#
#------------------------------------------------------------
@ -51,7 +51,7 @@ class TutorialObject(Object):
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
@ -59,13 +59,13 @@ class TutorialObject(Object):
#------------------------------------------------------------
#
# Readable - an object one can "read".
# Readable - an object one can "read".
#
#------------------------------------------------------------
class CmdRead(Command):
"""
Usage:
Usage:
read [obj]
Read some text.
@ -82,8 +82,8 @@ class CmdRead(Command):
else:
obj = self.obj
if not obj:
return
# we want an attribute read_text to be defined.
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)
@ -100,7 +100,7 @@ class CmdSetReadable(CmdSet):
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()
@ -116,14 +116,14 @@ class Readable(TutorialObject):
#
# 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.
# command 'climb' handles the actual climbing.
#
#------------------------------------------------------------
class CmdClimb(Command):
"""
Usage:
climb <object>
Usage:
climb <object>
"""
key = "climb"
locks = "cmd:all()"
@ -140,7 +140,7 @@ class CmdClimb(Command):
return
if obj != self.obj:
self.caller.msg("Try as you might, you cannot climb that.")
return
return
ostring = self.obj.db.climb_text
if not ostring:
ostring = "You climb %s. Having looked around, you climb down again." % self.obj.name
@ -153,10 +153,10 @@ class CmdSetClimbable(CmdSet):
"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)
@ -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
# 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
# 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.
# 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.",
"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."
"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
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
# 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
# a limited burn-time.
# When burned out, it will remove itself from the carrying
@ -228,7 +228,7 @@ class StateLightSourceOn(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.repeats = 1 # only run once.
self.persistent = True # survive a server reboot.
def at_start(self):
@ -237,15 +237,15 @@ class StateLightSourceOn(Script):
self.db.script_started = time.time()
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.reset()
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
burntime.
burntime.
"""
# calculate remaining burntime, if object is not
# already deleted (because it burned out)
@ -264,7 +264,7 @@ class StateLightSourceOn(Script):
class CmdLightSourceOn(Command):
"""
Switches on the lightsource.
Switches on the lightsource.
"""
key = "on"
aliases = ["switch on", "turn on", "light"]
@ -273,16 +273,16 @@ class CmdLightSourceOn(Command):
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
self.obj.db.is_active = True
# 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.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.
self.caller.location.scripts.validate()
# look around
@ -313,7 +313,7 @@ class CmdLightSourceOff(Command):
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"
@ -322,69 +322,69 @@ class CmdSetLightSource(CmdSet):
"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.
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.is_active = False
self.db.burntime = 60*3 # 3 minutes
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)
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.
"""
"""
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.
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
self.db.is_active = False
try:
# validate in holders current room, if possible
self.location.location.scripts.validate()
# validate in holders current room, if possible
self.location.location.scripts.validate()
except AttributeError:
# maybe it was dropped, try validating at current location.
# maybe it was dropped, try validating at current location.
try:
self.location.scripts.validate()
except AttributeError,e:
pass
pass
self.delete()
#------------------------------------------------------------
#
#
# 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.
#
# 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.
# 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
# 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?)
@ -396,35 +396,35 @@ class CmdShiftRoot(Command):
shift blue root left/right
shift red root left/right
shift yellow root up/down
shift green root up/down
shift green root up/down
"""
key = "shift"
"""
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):
def parse(self):
"custom parser; split input by spaces"
self.arglist = self.args.strip().split()
def func(self):
"""
Implement the command.
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
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
return
color = self.arglist[0].lower()
direction = self.arglist[1].lower()
# get current root positions dict
@ -432,7 +432,7 @@ class CmdShiftRoot(Command):
if not color in root_pos:
self.caller.msg("No such root to move.")
return
return
# first, vertical roots (red/blue) - can be moved left/right
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.")
else:
self.caller.msg("You cannot move the root in that direction.")
# store new position
# 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.db.crumbling_wall_found_button = True
self.caller.msg("Holding aside the root you think you notice something behind it ...")
class CmdPressButton(Command):
@ -511,19 +511,19 @@ class CmdPressButton(Command):
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
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."
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
@ -531,7 +531,7 @@ class CmdPressButton(Command):
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
return
self.obj.destination = eloc
self.caller.msg(string)
@ -539,22 +539,22 @@ class CmdSetCrumblingWall(CmdSet):
"Group the commands for crumblingWall"
key = "crumblingwall_cmdset"
def at_cmdset_creation(self):
"called when object is first created."
"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
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)
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."
@ -567,17 +567,17 @@ class CrumblingWall(TutorialObject, Exit):
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".
# 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.
# 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 ",
@ -595,10 +595,10 @@ class CrumblingWall(TutorialObject, Exit):
string = rootnames[root] + hpos[ipos]
else:
string = rootnames[root] + vpos[ipos]
return string
return string
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:
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 "
@ -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 += "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
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
@ -621,7 +621,7 @@ class CrumblingWall(TutorialObject, Exit):
self.reset()
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)
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.")
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.
# 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},
@ -639,31 +639,31 @@ class CrumblingWall(TutorialObject, Exit):
{"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
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
# 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.
#
# to hit with it, and how much damage it can do.
#
#------------------------------------------------------------
class CmdAttack(Command):
"""
Attack the enemy. Commands:
Attack the enemy. Commands:
stab <enemy>
slash <enemy>
parry
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.
@ -672,7 +672,7 @@ class CmdAttack(Command):
"""
# 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"
aliases = ["hit","kill", "fight", "thrust", "pierce", "stab", "slash", "chop", "parry", "defend"]
@ -683,50 +683,50 @@ class CmdAttack(Command):
"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
return
# parry mode
if cmdstring in ("parry", "defend"):
# 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.db.combat_parry_mode = True
self.caller.location.msg_contents("%s takes a defensive stance" % self.caller, exclude=[self.caller])
return
return
if not self.args:
self.caller.msg("Who do you attack?")
return
return
target = self.caller.search(self.args.strip())
if not target:
return
return
string = ""
tstring = ""
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
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
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
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
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
return
if target.db.combat_parry_mode:
# target is defensive; even harder to hit!
@ -737,17 +737,17 @@ class CmdAttack(Command):
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
target.db.health -= damage
else:
# sorry, impossible to fight this enemy ...
self.caller.msg("The enemy seems unaffacted.")
return False
return False
else:
self.caller.msg(string + "{rYou miss.{n")
target.msg(tstring + "{gThey miss you.{n")
@ -775,9 +775,9 @@ class Weapon(TutorialObject):
self.db.hit = 0.4 # hit chance
self.db.parry = 0.8 # parry chance
self.damage = 8.0
self.magic = False
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:
@ -787,8 +787,8 @@ class Weapon(TutorialObject):
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
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)
else:
dmg, name, aliases, desc, magic = self.obj.randomize_type()
@ -834,8 +834,8 @@ class CmdSetWeaponRack(CmdSet):
key = "weaponrack_cmdset"
mergemode = "Replace"
def at_cmdset_creation(self):
self.add(CmdGetWeapon())
self.add(CmdGetWeapon())
class WeaponRack(TutorialObject):
"""
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.db.min_dmg = 1.0
self.db.max_dmg = 4.0
self.db.magic = False
self.db.magic = False
def randomize_type(self):
"""
@ -867,26 +867,26 @@ class WeaponRack(TutorialObject):
name = "Knife"
desc = "A rusty kitchen knife. Better than nothing."
elif dmg < 2.0:
name = "Rusty dagger"
name = "Rusty dagger"
desc = "A double-edged dagger with nicked edge. It has a wooden handle."
elif dmg < 3.0:
name = "Sword"
name = "Sword"
desc = "A rusty shortsword. It has leather wrapped around the handle."
elif dmg < 4.0:
name = "Club"
elif dmg < 4.0:
name = "Club"
desc = "A heavy wooden club with some rusty spikes in it."
elif dmg < 5.0:
name = "Ornate Longsword"
name = "Ornate Longsword"
aliases.extend(["longsword","ornate"])
desc = "A fine longsword."
elif dmg < 6.0:
name = "Runeaxe"
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."
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")
@ -901,8 +901,8 @@ class WeaponRack(TutorialObject):
desc = "This massive sword is large as you are tall. Its metal shine with a bluish glow."
else:
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."
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

View file

@ -1,5 +1,5 @@
"""
Room Typeclasses for the TutorialWorld.
"""
@ -12,11 +12,11 @@ from contrib.tutorial_world.objects import LightSource, TutorialObject
#------------------------------------------------------------
#
# Tutorial room - parent room class
#
# This room is the parent of all rooms in the tutorial.
# Tutorial room - parent room class
#
# This room is the parent of all rooms in the tutorial.
# It defines a tutorial command on itself (available to
# all who is in a tutorial room).
# all who is in a tutorial room).
#
#------------------------------------------------------------
@ -39,9 +39,9 @@ class CmdTutorial(Command):
def func(self):
"""
All we do is to scan the current location for an attribute
called `tutorial_info` and display that.
called `tutorial_info` and display that.
"""
caller = self.caller
if not self.args:
@ -49,11 +49,11 @@ class CmdTutorial(Command):
else:
target = caller.search(self.args.strip())
if not target:
return
return
helptext = target.db.tutorial_info
if helptext:
if helptext:
caller.msg("{G%s{n" % helptext)
else:
else:
caller.msg("{RSorry, there is no tutorial help available here.{n")
class TutorialRoomCmdSet(CmdSet):
@ -65,7 +65,7 @@ class TutorialRoomCmdSet(CmdSet):
class TutorialRoom(Room):
"""
This is the base room type for all rooms in the tutorial world.
This is the base room type for all rooms in the tutorial world.
It defines a cmdset on itself for reading tutorial info about the location.
"""
def at_object_creation(self):
@ -82,14 +82,14 @@ class TutorialRoom(Room):
#------------------------------------------------------------
#
# Weather room - scripted room
#
# The weather room is called by a script at
#
# The weather room is called by a script at
# irregular intervals. The script is generally useful
# and so is split out into tutorialworld.scripts.
#
#------------------------------------------------------------
class WeatherRoom(TutorialRoom):
"""
This should probably better be called a rainy room...
@ -99,7 +99,7 @@ class WeatherRoom(TutorialRoom):
inherit from this.
"""
def at_object_creation(self):
def at_object_creation(self):
"Called when object is first created."
super(WeatherRoom, self).at_object_creation()
@ -133,7 +133,7 @@ class WeatherRoom(TutorialRoom):
#
# Dark Room - a scripted room
#
# This room limits the movemenets of its denizens unless they carry a and active
# This room limits the movemenets of its denizens unless they carry a and active
# LightSource object (LightSource is defined in tutorialworld.objects.LightSource)
#
#-----------------------------------------------------------------------------------
@ -141,7 +141,7 @@ class WeatherRoom(TutorialRoom):
class CmdLookDark(Command):
"""
Look around in darkness
Usage:
look
@ -155,7 +155,7 @@ class CmdLookDark(Command):
def func(self):
"Implement the command."
caller = self.caller
# we don't have light, grasp around blindly.
# we don't have light, grasp around blindly.
messages = ("It's pitch black. You fumble around but cannot find anything.",
"You don't see a thing. You feel around, managing to bump your fingers hard against something. Ouch!",
"You don't see a thing! Blindly grasping the air around you, you find nothing.",
@ -163,25 +163,25 @@ class CmdLookDark(Command):
"You are completely blind. For a moment you think you hear someone breathing nearby ... \n ... surely you must be mistaken.",
"Blind, you think you find some sort of object on the ground, but it turns out to be just a stone.",
"Blind, you bump into a wall. The wall seems to be covered with some sort of vegetation, but its too damp to burn.",
"You can't see anything, but the air is damp. It feels like you are far underground.")
"You can't see anything, but the air is damp. It feels like you are far underground.")
irand = random.randint(0, 10)
if irand < len(messages):
caller.msg(messages[irand])
else:
# check so we don't already carry a lightsource.
# check so we don't already carry a lightsource.
carried_lights = [obj for obj in caller.contents if utils.inherits_from(obj, LightSource)]
if carried_lights:
string = "You don't want to stumble around in blindness anymore. You already found what you need. Let's get light already!"
caller.msg(string)
return
#if we are lucky, we find the light source.
return
#if we are lucky, we find the light source.
lightsources = [obj for obj in self.obj.contents if utils.inherits_from(obj, LightSource)]
if lightsources:
lightsource = lightsources[0]
else:
# create the light source from scratch.
lightsource = create_object(LightSource, key="torch")
lightsource.location = caller
lightsource = create_object(LightSource, key="torch")
lightsource.location = caller
string = "Your fingers bump against a piece of wood in a corner. Smelling it you sense the faint smell of tar. A {c%s{n!"
string += "\nYou pick it up, holding it firmly. Now you just need to {wlight{n it using the flint and steel you carry with you."
caller.msg(string % lightsource.key)
@ -199,7 +199,7 @@ class CmdDarkHelp(Command):
string += " You cannot give up even if you don't find anything right away."
self.caller.msg(string)
# the nomatch system command will give a suitable error when we cannot find the normal commands.
# the nomatch system command will give a suitable error when we cannot find the normal commands.
from src.commands.default.syscommands import CMD_NOMATCH
from src.commands.default.general import CmdSay
class CmdDarkNoMatch(Command):
@ -222,37 +222,37 @@ class DarkCmdSet(CmdSet):
self.add(CmdDarkNoMatch())
self.add(CmdSay)
#
# Darkness room two-state system
# Darkness room two-state system
#
class DarkState(Script):
"""
The darkness state is a script that keeps tabs on when
a player in the room carries an active light source. It places
The darkness state is a script that keeps tabs on when
a player in the room carries an active light source. It places
a new, very restrictive cmdset (DarkCmdSet) on all the players
in the room whenever there is no light in it. Upon turning on
a light, the state switches off and moves to LightState.
in the room whenever there is no light in it. Upon turning on
a light, the state switches off and moves to LightState.
"""
def at_script_creation(self):
"This setups the script"
self.key = "tutorial_darkness_state"
self.desc = "A dark room"
self.persistent = True
def at_start(self):
self.persistent = True
def at_start(self):
"called when the script is first starting up."
for char in [char for char in self.obj.contents if char.has_player]:
if char.is_superuser:
char.msg("You are Superuser, so you are not affected by the dark state.")
else:
else:
char.cmdset.add(DarkCmdSet)
char.msg("The room is pitch dark! You are likely to be eaten by a Grue.")
def is_valid(self):
"is valid only as long as noone in the room has lit the lantern."
"is valid only as long as noone in the room has lit the lantern."
return not self.obj.is_lit()
def at_stop(self):
"Someone turned on a light. This state dies. Switch to LightState."
for char in [char for char in self.obj.contents if char.has_player]:
char.cmdset.delete(DarkCmdSet)
for char in [char for char in self.obj.contents if char.has_player]:
char.cmdset.delete(DarkCmdSet)
self.obj.db.is_dark = False
self.obj.scripts.add(LightState)
@ -264,29 +264,29 @@ class LightState(Script):
"Called when script is first created."
self.key = "tutorial_light_state"
self.desc = "A room lit up"
self.persistent = True
self.persistent = True
def is_valid(self):
"This state is only valid as long as there is an active light source in the room."
"This state is only valid as long as there is an active light source in the room."
return self.obj.is_lit()
def at_stop(self):
"Light disappears. This state dies. Return to DarknessState."
"Light disappears. This state dies. Return to DarknessState."
self.obj.db.is_dark = True
self.obj.scripts.add(DarkState)
class DarkRoom(TutorialRoom):
"""
A dark room. This tries to start the DarkState script on all
objects entering. The script is responsible for making sure it is
objects entering. The script is responsible for making sure it is
valid (that is, that there is no light source shining in the room).
"""
def is_lit(self):
"""
Helper method to check if the room is lit up. It checks all
characters in room to see if they carry an active object of
type LightSource.
"""
return any([any([True for obj in char.contents
if utils.inherits_from(obj, LightSource) and obj.is_active])
characters in room to see if they carry an active object of
type LightSource.
"""
return any([any([True for obj in char.contents
if utils.inherits_from(obj, LightSource) and obj.is_active])
for char in self.contents if char.has_player])
def at_object_creation(self):
@ -309,34 +309,34 @@ class DarkRoom(TutorialRoom):
health = character.db.health_max
if not health:
health = 20
character.db.health = health
character.db.health = health
self.scripts.validate()
def at_object_leave(self, character, target_location):
"In case people leave with the light, we make sure to update the states accordingly."
character.cmdset.delete(DarkCmdSet) # in case we are teleported away
self.scripts.validate()
#------------------------------------------------------------
#
# Teleport room - puzzle room
# Teleport room - puzzle room
#
# This is a sort of puzzle room that requires a certain
# attribute on the entering character to be the same as
# an attribute of the room. If not, the character will
# be teleported away to a target location. This is used
# This is a sort of puzzle room that requires a certain
# attribute on the entering character to be the same as
# an attribute of the room. If not, the character will
# be teleported away to a target location. This is used
# by the Obelisk - grave chamber puzzle, where one must
# have looked at the obelisk to get an attribute set on
# oneself, and then pick the grave chamber with the
# oneself, and then pick the grave chamber with the
# matching imagery for this attribute.
#
#------------------------------------------------------------
class TeleportRoom(TutorialRoom):
"""
Teleporter - puzzle room.
Teleporter - puzzle room.
Important attributes (set at creation):
Important attributes (set at creation):
puzzle_key - which attr to look for on character
puzzle_value - what char.db.puzzle_key must be set to
teleport_to - where to teleport to in case of failure to match
@ -347,7 +347,7 @@ class TeleportRoom(TutorialRoom):
super(TeleportRoom, self).at_object_creation()
# what character.db.puzzle_clue must be set to, to avoid teleportation.
self.db.puzzle_value = 1
# the target of the success teleportation. Can be a dbref or a unique room name.
# the target of the success teleportation. Can be a dbref or a unique room name.
self.db.success_teleport_to = "treasure room"
# the target of the failure teleportation.
self.db.failure_teleport_to = "dark cell"
@ -355,33 +355,33 @@ class TeleportRoom(TutorialRoom):
def at_object_receive(self, character, source_location):
"This hook is called by the engine whenever the player is moved into this room."
if not character.has_player:
# only act on player characters.
# only act on player characters.
return
#print character.db.puzzle_clue, self.db.puzzle_value
if character.db.puzzle_clue != self.db.puzzle_value:
# we didn't pass the puzzle. See if we can teleport.
teleport_to = self.db.failure_teleport_to # this is a room name
# we didn't pass the puzzle. See if we can teleport.
teleport_to = self.db.failure_teleport_to # this is a room name
else:
# passed the puzzle
teleport_to = self.db.success_teleport_to # this is a room name
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:
# 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
return
return
if character.player.is_superuser:
# superusers don't get teleported
# superusers don't get teleported
character.msg("Superuser block: You would have been teleported to %s." % results[0])
return
return
# teleport
character.execute_cmd("look")
character.location = results[0] # stealth move
character.location = results[0] # stealth move
character.location.at_object_receive(character, self)
#------------------------------------------------------------
#
# Bridge - unique room
# Bridge - unique room
#
# Defines a special west-eastward "bridge"-room, a large room it takes
# several steps to cross. It is complete with custom commands and a
@ -389,10 +389,10 @@ class TeleportRoom(TutorialRoom):
# instead the exiting are handled by custom commands set on the player
# upon first entering the room.
#
# Since one can enter the bridge room from both ends, it is
# Since one can enter the bridge room from both ends, it is
# divided into five steps:
# westroom <- 0 1 2 3 4 -> eastroom
#
#
#------------------------------------------------------------
@ -404,25 +404,25 @@ class CmdEast(Command):
aliases = ["e"]
locks = "cmd:all()"
help_category = "TutorialWorld"
def func(self):
"move forward"
caller = self.caller
bridge_step = min(5, caller.db.tutorial_bridge_position + 1)
if bridge_step > 4:
# we have reached the far east end of the bridge. Move to the east room.
# we have reached the far east end of the bridge. Move to the east room.
eexit = search_object(self.obj.db.east_exit)
if eexit:
caller.move_to(eexit[0])
else:
caller.msg("No east exit was found for this room. Contact an admin.")
return
return
caller.db.tutorial_bridge_position = bridge_step
caller.location.msg_contents("%s steps eastwards across the bridge." % caller.name, exclude=caller)
caller.execute_cmd("look")
# go back across the bridge
class CmdWest(Command):
"""
@ -432,11 +432,11 @@ class CmdWest(Command):
aliases = ["w"]
locks = "cmd:all()"
help_category = "TutorialWorld"
def func(self):
"move forward"
caller = self.caller
bridge_step = max(-1, caller.db.tutorial_bridge_position - 1)
if bridge_step < 0:
@ -446,14 +446,14 @@ class CmdWest(Command):
caller.move_to(wexit[0])
else:
caller.msg("No west exit was found for this room. Contact an admin.")
return
return
caller.db.tutorial_bridge_position = bridge_step
caller.location.msg_contents("%s steps westwartswards across the bridge." % caller.name, exclude=caller)
caller.execute_cmd("look")
class CmdLookBridge(Command):
"""
looks around at the bridge.
looks around at the bridge.
"""
key = 'look'
aliases = ["l"]
@ -463,14 +463,14 @@ class CmdLookBridge(Command):
def func(self):
"Looking around, including a chance to fall."
bridge_position = self.caller.db.tutorial_bridge_position
messages =("You are standing {wvery close to the the bridge's western foundation{n. If you go west you will be back on solid ground ...",
"The bridge slopes precariously where it extends eastwards towards the lowest point - the center point of the hang bridge.",
"You are {whalfways{n out on the unstable bridge.",
"The bridge slopes precariously where it extends westwards towards the lowest point - the center point of the hang bridge.",
"You are standing {wvery close to the bridge's eastern foundation{n. If you go east you will be back on solid ground ...")
moods = ("The bridge sways in the wind.", "The hanging bridge creaks dangerously.",
moods = ("The bridge sways in the wind.", "The hanging bridge creaks dangerously.",
"You clasp the ropes firmly as the bridge sways and creaks under you.",
"From the castle you hear a distant howling sound, like that of a large dog or other beast.",
"The bridge creaks under your feet. Those planks does not seem very sturdy.",
@ -481,27 +481,27 @@ class CmdLookBridge(Command):
"The section of rope you hold onto crumble in your hands, parts of it breaking apart. You sway trying to regain balance.")
message = "{c%s{n\n" % self.obj.key + messages[bridge_position] + "\n" + moods[random.randint(0, len(moods) - 1)]
chars = [obj for obj in self.obj.contents if obj != self.caller and obj.has_player]
if chars:
if chars:
message += "\n You see: %s" % ", ".join("{c%s{n" % char.key for char in chars)
self.caller.msg(message)
# there is a chance that we fall if we are on the western or central part of the bridge.
# there is a chance that we fall if we are on the western or central part of the bridge.
if bridge_position < 3 and random.random() < 0.05 and not self.caller.is_superuser:
# we fall on 5% of the times.
# we fall on 5% of the times.
fexit = search_object(self.obj.db.fall_exit)
if fexit:
if fexit:
string = "\n Suddenly the plank you stand on gives way under your feet! You fall!"
string += "\n You try to grab hold of an adjoining plank, but all you manage to do is to "
string += "divert your fall westwards, towards the cliff face. This is going to hurt ... "
string += "\n ... The world goes dark ...\n"
# note that we move silently so as to not call look hooks (this is a little trick to leave
string += "\n ... The world goes dark ...\n"
# note that we move silently so as to not call look hooks (this is a little trick to leave
# the player with the "world goes dark ..." message, giving them ample time to read it. They
# have to manually call look to find out their new location). Thus we also call the
# at_object_leave hook manually (otherwise this is done by move_to()).
# have to manually call look to find out their new location). Thus we also call the
# at_object_leave hook manually (otherwise this is done by move_to()).
self.caller.msg("{r%s{n" % string)
self.obj.at_object_leave(self.caller, fexit)
self.caller.location = fexit[0] # stealth move, without any other hook calls.
self.caller.location = fexit[0] # stealth move, without any other hook calls.
self.obj.msg_contents("A plank gives way under %s's feet and they fall from the bridge!" % self.caller.key)
# custom help command
@ -522,7 +522,7 @@ class CmdBridgeHelp(Command):
self.caller.msg(string)
class BridgeCmdSet(CmdSet):
"This groups the bridge commands. We will store it on the room."
"This groups the bridge commands. We will store it on the room."
key = "Bridge commands"
priority = 1 # this gives it precedence over the normal look/help commands.
def at_cmdset_creation(self):
@ -531,14 +531,14 @@ class BridgeCmdSet(CmdSet):
self.add(CmdWest())
self.add(CmdLookBridge())
self.add(CmdBridgeHelp())
class BridgeRoom(TutorialRoom):
"""
The bridge room implements an unsafe bridge. It also enters the player into a
The bridge room implements an unsafe bridge. It also enters the player into a
state where they get new commands so as to try to cross the bridge.
We want this to result in the player getting a special set of
commands related to crossing the bridge. The result is that it will take several
We want this to result in the player getting a special set of
commands related to crossing the bridge. The result is that it will take several
steps to cross it, despite it being represented by only a single room.
We divide the bridge into steps:
@ -547,7 +547,7 @@ class BridgeRoom(TutorialRoom):
0 1 2 3 4
The position is handled by a variable stored on the player when entering and giving
special move commands will increase/decrease the counter until the bridge is crossed.
special move commands will increase/decrease the counter until the bridge is crossed.
"""
def at_object_creation(self):
@ -557,7 +557,7 @@ class BridgeRoom(TutorialRoom):
# at irregular intervals, this will call self.update_irregular()
self.scripts.add(tut_scripts.IrregularEvent)
# this identifies the exits from the room (should be the command
# needed to leave through that exit). These are defaults, but you
# needed to leave through that exit). These are defaults, but you
# could of course also change them after the room has been created.
self.db.west_exit = "cliff"
self.db.east_exit = "gate"
@ -583,12 +583,12 @@ class BridgeRoom(TutorialRoom):
"Some sort of large bird sweeps by overhead, giving off an eery screech. Soon it has disappeared in the gloom.",
"The bridge sways from side to side in the wind.")
self.msg_contents("{w%s{n" % strings[random.randint(0, 7)])
def at_object_receive(self, character, source_location):
"""
This hook is called by the engine whenever the player is moved
into this room.
"""
"""
if character.has_player:
# we only run this if the entered object is indeed a player object.
# check so our east/west exits are correctly defined.
@ -598,8 +598,8 @@ class BridgeRoom(TutorialRoom):
if not wexit or not eexit or not fexit:
character.msg("The bridge's exits are not properly configured. Contact an admin. Forcing west-end placement.")
character.db.tutorial_bridge_position = 0
return
if source_location == eexit[0]:
return
if source_location == eexit[0]:
character.db.tutorial_bridge_position = 4
else:
character.db.tutorial_bridge_position = 0
@ -618,13 +618,13 @@ class BridgeRoom(TutorialRoom):
# Intro Room - unique room
#
# This room marks the start of the tutorial. It sets up properties on the player char
# that is needed for the tutorial.
# that is needed for the tutorial.
#
#------------------------------------------------------------
class IntroRoom(TutorialRoom):
"""
Intro room
Intro room
properties to customize:
char_health - integer > 0 (default 20)
@ -634,15 +634,15 @@ class IntroRoom(TutorialRoom):
"""
Assign properties on characters
"""
# setup
# setup
health = self.db.char_health
if not health:
health = 20
if character.has_player:
character.db.health = health
character.db.health_max = health
character.db.health = health
character.db.health_max = health
if character.is_superuser:
string = "-"*78
@ -655,7 +655,7 @@ class IntroRoom(TutorialRoom):
#------------------------------------------------------------
#
# Outro room - unique room
#
#
# Cleans up the character from all tutorial-related properties.
#
#------------------------------------------------------------
@ -664,16 +664,16 @@ class OutroRoom(TutorialRoom):
"""
Outro room.
"""
def at_object_receive(self, character, source_location):
"""
Do cleanup.
"""
"""
if character.has_player:
del character.db.health
del character.db.health
del character.db.has_climbed
del character.db.puzzle_clue
del character.db.combat_parry_mode
del character.db.combat_parry_mode
del character.db.tutorial_bridge_position
for tut_obj in [obj for obj in character.contents if utils.inherits_from(obj, TutorialObject)]:
tut_obj.reset()

1
ev.py
View file

@ -236,3 +236,4 @@ class SystemCmds(object):
del cmdhandler
syscmdkeys = SystemCmds()
del SystemCmds

View file

@ -65,15 +65,15 @@ for inum in range(len(commands)):
print "leaving run ..."
"""
_PROCPOOL_BATCHCODE_SOURCE = """
from src.commands.default.batchprocess import batch_cmd_exec, step_pointer, BatchSafeCmdSet
caller.ndb.batch_stack = commands
from src.commands.default.batchprocess import batch_code_exec, step_pointer, BatchSafeCmdSet
caller.ndb.batch_stack = codes
caller.ndb.batch_stackptr = 0
caller.ndb.batch_batchmode = "batch_commands"
caller.ndb.batch_batchmode = "batch_code"
caller.cmdset.add(BatchSafeCmdSet)
for inum in range(len(commands)):
print "command:", inum
for inum in range(len(codes)):
print "code:", inum
caller.cmdset.add(BatchSafeCmdSet)
if not batch_cmd_exec(caller):
if not batch_code_exec(caller):
break
step_pointer(caller, 1)
print "leaving run ..."
@ -272,7 +272,14 @@ class CmdBatchCommands(MuxCommand):
else:
caller.msg("Running Batch-command processor - Automatic mode for %s (this might take some time) ..." % python_path)
if False:#TODO - need to add a procpool solution. settings.PROCPOOL_ENABLED:
procpool = False
if "PythonProcPool" in utils.server_services():
if utils.uses_database("sqlite3"):
caller.msg("Batchprocessor disabled ProcPool under SQLite3.")
else:
procpool=True
if procpool:
# run in parallel process
def callback(r):
caller.msg(" {GBatchfile '%s' applied." % python_path)
@ -366,7 +373,13 @@ class CmdBatchCode(MuxCommand):
else:
caller.msg("Running Batch-code processor - Automatic mode for %s ..." % python_path)
if False: #TODO Add procpool solution. settings.PROCPOOL_ENABLED:
procpool = False
if "PythonProcPool" in utils.server_services():
if utils.uses_database("sqlite3"):
caller.msg("Batchprocessor disabled ProcPool under SQLite3.")
else:
procpool=True
if procpool:
# run in parallel process
def callback(r):
caller.msg(" {GBatchfile '%s' applied." % python_path)
@ -374,10 +387,10 @@ class CmdBatchCode(MuxCommand):
def errback(e):
caller.msg(" {RError from processor: '%s'" % e)
purge_processor(caller)
utils.run_async(_PROCPOOL_BATCHCODE_SOURCE, commands=commands, caller=caller, at_return=callback, at_err=errback)
utils.run_async(_PROCPOOL_BATCHCODE_SOURCE, codes=codes, caller=caller, at_return=callback, at_err=errback)
else:
# un in-process (will block)
for inum in range(len(commands)):
for inum in range(len(codes)):
# loop through the batch file
if not batch_cmd_exec(caller):
return

View file

@ -118,7 +118,7 @@ class CmdSetObjAlias(MuxCommand):
objname = self.lhs
# Find the object to receive aliases
obj = caller.search(objname, global_search=True)
obj = caller.search(objname)
if not obj:
return
if self.rhs == None:
@ -1232,7 +1232,7 @@ class CmdSetAttribute(ObjManipCommand):
# Use literal_eval to parse python structure exactly.
try:
return _LITERAL_EVAL(strobj)
except ValueError:
except (SyntaxError, ValueError):
# treat as string
string = "{RNote: Value was converted to string. If you don't want this, "
string += "use proper Python syntax, like enclosing strings in quotes.{n"

View file

@ -1,6 +1,8 @@
"""
Custom manager for Objects.
"""
try: import cPickle as pickle
except ImportError: import pickle
from django.db.models import Q
from django.conf import settings
#from django.contrib.auth.models import User
@ -8,10 +10,11 @@ from django.db.models.fields import exceptions
from src.typeclasses.managers import TypedObjectManager
from src.typeclasses.managers import returns_typeclass, returns_typeclass_list
from src.utils import utils
from src.utils.utils import to_unicode, make_iter, string_partial_matching
from src.utils.utils import to_unicode, make_iter, string_partial_matching, to_str
__all__ = ("ObjectManager",)
_GA = object.__getattribute__
_DUMPS = lambda inp: to_unicode(pickle.dumps(inp))
# Try to use a custom way to parse id-tagged multimatches.
@ -126,8 +129,13 @@ class ObjectManager(TypedObjectManager):
not make sense to offer an "exact" type matching for this.
"""
cand_restriction = candidates and Q(db_obj__pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
attrs= self.model.objattribute_set.related.model.objects.select_related("db_obj").filter(cand_restriction & Q(db_key=attribute_name))
return [attr.db_obj for attr in attrs if attribute_value == attr.value]
if type(attribute_value) in (basestring, int, float):
# simple attribute_value - do direct lookup
return self.model.objattribute_set.related.model.objects.select_related("db_obj").filter(cand_restriction & Q(db_key=attribute_name) & Q(db_value=_DUMPS(("simple", attribute_value))))
else:
# go via attribute conversion
attrs= self.model.objattribute_set.related.model.objects.select_related("db_obj").filter(cand_restriction & Q(db_key=attribute_name))
return [attr.db_obj for attr in attrs if attribute_value == attr.value]
@returns_typeclass_list
def get_objs_with_db_property(self, property_name, candidates=None):
@ -137,7 +145,7 @@ class ObjectManager(TypedObjectManager):
candidates - list of candidate objects to search
"""
property_name = "db_%s" % property_name.lstrip('db_')
cand_restriction = candidates and Q(pk__in=[_GA(obj, "in") for obj in make_iter(candidates) if obj]) or Q()
cand_restriction = candidates and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
try:
return self.filter(cand_restriction).exclude(Q(property_name=None))
except exceptions.FieldError:
@ -151,7 +159,7 @@ class ObjectManager(TypedObjectManager):
if isinstance(property_value, basestring):
property_value = to_unicode(property_value)
property_name = "db_%s" % property_name.lstrip('db_')
cand_restriction = candidates and Q(pk__in=[_GA(obj, "in") for obj in make_iter(candidates) if obj]) or Q()
cand_restriction = candidates and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
try:
return self.filter(cand_restriction & Q(property_name=property_value))
except exceptions.FieldError:
@ -178,7 +186,7 @@ class ObjectManager(TypedObjectManager):
candidates_id = [_GA(obj, "id") for obj in make_iter(candidates) if obj]
cand_restriction = candidates and Q(pk__in=candidates_id) or Q()
if exact:
return self.filter(cand_restriction & (Q(db_key__iexact=ostring) | Q(alias__db_key__iexact=ostring)))
return self.filter(cand_restriction & (Q(db_key__iexact=ostring) | Q(alias__db_key__iexact=ostring))).distinct()
else:
if candidates:
# fuzzy matching - only check the candidates
@ -196,7 +204,7 @@ class ObjectManager(TypedObjectManager):
return []
else:
# fuzzy matching - first check with keys, then with aliases
key_candidates = self.filter(Q(db_key__istartswith=ostring) | Q(alias__db_key__istartswith=ostring))
key_candidates = self.filter(Q(db_key__istartswith=ostring) | Q(alias__db_key__istartswith=ostring)).distinct()
key_strings = key_candidates.values_list("db_key", flat=True)
matches = string_partial_matching(key_candidates, ostring, reg_index=False)
if matches:
@ -209,7 +217,6 @@ class ObjectManager(TypedObjectManager):
@returns_typeclass_list
def object_search(self, ostring, caller=None,
global_search=False,
attribute_name=None,
candidates=None,
exact=True):
@ -241,16 +248,16 @@ class ObjectManager(TypedObjectManager):
"""
def _searcher(ostring, exact=False):
"Helper method for searching objects"
if ostring.startswith("*"):
# Player search - try to find obj by its player's name
player_match = self.get_object_with_player(ostring, candidates=candidates)
if player_match is not None:
return [player_match]
if attribute_name:
# attribute/property search (always exact).
matches = self.get_objs_with_db_property_value(attribute_name, ostring, candidates=candidates)
if not matches:
return self.get_objs_with_attr_value(attribute_name, ostring, candidates=candidates)
if ostring.startswith("*"):
# Player search - try to find obj by its player's name
player_match = self.get_object_with_player(ostring, candidates=candidates)
if player_match is not None:
return [player_match]
else:
# normal key/alias search
return self.get_objs_with_key_or_alias(ostring, exact=exact, candidates=candidates)

View file

@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding index on 'ObjAttribute', fields ['db_value']
db.create_index('objects_objattribute', ['db_value'])
def backwards(self, orm):
# Removing index on 'ObjAttribute', fields ['db_value']
db.delete_index('objects_objattribute', ['db_value'])
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'objects.alias': {
'Meta': {'object_name': 'Alias'},
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objattribute': {
'Meta': {'object_name': 'ObjAttribute'},
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'db_value': ('django.db.models.fields.TextField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objectdb': {
'Meta': {'object_name': 'ObjectDB'},
'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objectnick': {
'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'ObjectNick'},
'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'db_real': ('django.db.models.fields.TextField', [], {}),
'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerdb': {
'Meta': {'object_name': 'PlayerDB'},
'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True', 'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
}
}
complete_apps = ['objects']

View file

@ -268,7 +268,7 @@ class Attribute(SharedMemoryModel):
db_key = models.CharField('key', max_length=255, db_index=True)
# access through the value property
db_value = models.TextField('value', blank=True, null=True)
db_value = models.TextField('value', blank=True, null=True, db_index=True)
# Lock storage
db_lock_storage = models.CharField('locks', max_length=512, blank=True)
# references the object the attribute is linked to (this is set

View file

@ -461,6 +461,35 @@ def format_table(table, extra_space=1):
for icol, col in enumerate(table)])
return ftable
def server_services():
"""
Lists all services active on the Server. Observe that
since services are launced in memory, this function will
only return any results if called from inside the game.
"""
from src.server.sessionhandler import SESSIONS
if hasattr(SESSIONS, "server") and hasattr(SESSIONS.server, "services"):
server = SESSIONS.server.services.namedServices
else:
print "This function must be called from inside the evennia process."
server = {}
del SESSIONS
return server
def uses_database(name="sqlite3"):
"""
Checks if the game is currently using a given database. This is a
shortcut to having to use the full backend name
name - one of 'sqlite3', 'mysql', 'postgresql_psycopg2' or 'oracle'
"""
try:
engine = settings.DATABASES["default"]["ENGINE"]
except KeyError:
engine = settings.DATABASE_ENGINE
return engine == "django.db.backends.%s" % name
_FROM_MODEL_MAP = None
_TO_DBOBJ = lambda o: (hasattr(o, "dbobj") and o.dbobj) or o