mirror of
https://github.com/evennia/evennia.git
synced 2026-04-06 07:57:16 +02:00
Made sure tickerhandler cleans itself of alrady-deleted objects. Some more bug fixes to reworked tutorialworld.
This commit is contained in:
parent
ca69c5aaec
commit
56104d9a1d
6 changed files with 79 additions and 54 deletions
|
|
@ -342,6 +342,7 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
|
|||
caller.ndb.last_cmd = None
|
||||
# return result to the deferred
|
||||
returnValue(ret)
|
||||
|
||||
except Exception:
|
||||
string = "%s\nAbove traceback is from an untrapped error."
|
||||
string += " Please file a bug report."
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ start
|
|||
tickerhandler to regularly 'tick and randomly display various
|
||||
weather-related messages.
|
||||
|
||||
The room also has 'details' set on it (such as the old well), those
|
||||
The room also has 'details' set on it (such as the ruin in the distance), those
|
||||
are snippets of text stored on the room that the custom look command
|
||||
used for all tutorial rooms can display.
|
||||
#
|
||||
|
|
@ -620,8 +620,9 @@ hole
|
|||
#
|
||||
@detail hole;above =
|
||||
Whereas the lower edges of the hole seem jagged and natural you can
|
||||
faintly make out that it turns into a man-made circular shaft higher
|
||||
up. It looks like an old well.
|
||||
faintly make out it turning into a man-made circular shaft higher up.
|
||||
It looks like an old well. There must have been much more water
|
||||
here once.
|
||||
#
|
||||
@detail passages;dark =
|
||||
Those dark passages seem to criss-cross the cliff. No need to
|
||||
|
|
@ -1062,7 +1063,9 @@ stairs down
|
|||
the tomb of some sort of ancient heroine - it must be the goal you
|
||||
have been looking for!
|
||||
#
|
||||
@dig Tomb of woman on horse
|
||||
@tel tut#14
|
||||
#
|
||||
@dig/teleport Tomb of woman on horse
|
||||
: tutorial_world.rooms.TeleportRoom
|
||||
= Tomb with statue of riding woman;horse;riding;
|
||||
#
|
||||
|
|
@ -1099,7 +1102,9 @@ stairs down
|
|||
the tomb of some sort of ancient heroine - it must be the goal you
|
||||
have been looking for!
|
||||
#
|
||||
@dig Tomb of the crowned queen
|
||||
@tel tut#14
|
||||
#
|
||||
@dig/teleport Tomb of the crowned queen
|
||||
: tutorial_world.rooms.TeleportRoom
|
||||
= Tomb with statue of a crowned queen;crown;queen
|
||||
#
|
||||
|
|
@ -1136,7 +1141,9 @@ stairs down
|
|||
the tomb of some sort of ancient heroine - it must be the goal you
|
||||
have been looking for!
|
||||
#
|
||||
@dig Tomb of the shield
|
||||
@tel tut#14
|
||||
#
|
||||
@dig/teleport Tomb of the shield
|
||||
: tutorial_world.rooms.TeleportRoom
|
||||
= Tomb with shield of arms;shield
|
||||
#
|
||||
|
|
@ -1173,7 +1180,9 @@ stairs down
|
|||
the tomb of some sort of ancient heroine - it must be the goal you
|
||||
have been looking for!
|
||||
#
|
||||
@dig Tomb of the hero
|
||||
@tel tut#14
|
||||
#
|
||||
@dig/teleport Tomb of the hero
|
||||
: tutorial_world.rooms.TeleportRoom
|
||||
= Tomb depicting a heroine fighting a monster;knight;hero;monster;beast
|
||||
#
|
||||
|
|
@ -1217,10 +1226,11 @@ stairs down
|
|||
#
|
||||
# The ancient tomb
|
||||
#
|
||||
# This is the real tomb, the goal of the adventure.
|
||||
# This is the real tomb, the goal of the adventure. It is not
|
||||
# directly accessible from the Antechamber but you are
|
||||
# teleported here only if you solve the puzzle of the Obelisk.
|
||||
#
|
||||
#------------------------------------------------------------
|
||||
# Create the real tomb
|
||||
#
|
||||
@dig/teleport Ancient tomb;tut#16
|
||||
: tutorial_world.rooms.TutorialRoom
|
||||
|
|
|
|||
|
|
@ -113,11 +113,11 @@ class Mob(tut_objects.TutorialObject):
|
|||
# chase the mob around when building.
|
||||
self.db.patrolling = True
|
||||
self.db.aggressive = True
|
||||
self.db.immortal = True
|
||||
self.db.immortal = False
|
||||
# db-store if it is dead or not
|
||||
self.db.is_dead = True
|
||||
# specifies how much damage we remove from non-magic weapons
|
||||
self.db.magic_resistance = 0.01
|
||||
# specifies how much damage we divide away from non-magic weapons
|
||||
self.db.damage_resistance = 100.0
|
||||
# pace (number of seconds between ticks) for
|
||||
# the respective modes.
|
||||
self.db.patrolling_pace = 6
|
||||
|
|
@ -388,12 +388,13 @@ class Mob(tut_objects.TutorialObject):
|
|||
Someone landed a hit on us. Check our status
|
||||
and start attacking if not already doing so.
|
||||
"""
|
||||
if not self.db.immortal:
|
||||
if not self.ndb.is_immortal:
|
||||
if not weapon.db.magic:
|
||||
# not a magic weapon - scale damage with magical
|
||||
# resistance
|
||||
damage = self.db.damage_resistance * damage
|
||||
# not a magic weapon - divide away magic resistance
|
||||
damage /= self.db.damage_resistance
|
||||
attacker.msg(self.db.weapon_ineffective_msg)
|
||||
else:
|
||||
self.location.msg_contents(self.db.hit_msg)
|
||||
self.db.health -= damage
|
||||
|
||||
# analyze the result
|
||||
|
|
@ -403,7 +404,6 @@ class Mob(tut_objects.TutorialObject):
|
|||
self.set_dead()
|
||||
else:
|
||||
# still alive, start attack if not already attacking
|
||||
attacker.msg(self.db.hit_msg)
|
||||
if self.db.aggressive and not self.ndb.is_attacking:
|
||||
self.start_attacking()
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import random
|
|||
|
||||
from evennia import DefaultObject, DefaultExit, Command, CmdSet
|
||||
from evennia import utils
|
||||
from evennia.utils import search
|
||||
from evennia.utils.spawner import spawn
|
||||
|
||||
#------------------------------------------------------------
|
||||
|
|
@ -276,7 +277,7 @@ class CmdLight(Command):
|
|||
"""
|
||||
|
||||
if self.obj.light():
|
||||
self.caller("You light %s." % self.obj.key)
|
||||
self.caller.msg("You light %s." % self.obj.key)
|
||||
self.caller.location.msg_contents("%s lights %s!" % (self.caller, self.obj.key), exclude=[self.caller])
|
||||
else:
|
||||
self.caller.msg("%s is already burning." % self.obj.key)
|
||||
|
|
@ -285,6 +286,8 @@ class CmdLight(Command):
|
|||
class CmdSetLight(CmdSet):
|
||||
"CmdSet for the lightsource commands"
|
||||
key = "lightsource_cmdset"
|
||||
# this is higher than the dark cmdset - important!
|
||||
priority = 3
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
"called at cmdset creation"
|
||||
|
|
@ -320,24 +323,27 @@ class LightSource(TutorialObject):
|
|||
# add the Light command
|
||||
self.cmdset.add_default(CmdSetLight, permanent=True)
|
||||
|
||||
def _burnout(self, ret):
|
||||
def _burnout(self):
|
||||
"""
|
||||
This is called when this light source burns out. We make no
|
||||
use of the return value.
|
||||
"""
|
||||
# delete ourselves from the database
|
||||
self.db.is_giving_light = False
|
||||
try:
|
||||
# our location is usually a Character (their inventory), we try
|
||||
# to send to -their- location so everyone else also notices the
|
||||
# light goes out.
|
||||
self.location.location.msg_contents("%s's %s flickers and dies." %
|
||||
(self.location, self.key), exclude=self.location)
|
||||
self.location.msg("Your %s flickers and dies." % self.key)
|
||||
self.location.location.check_light_state()
|
||||
except AttributeError:
|
||||
# we are not in someone's inventory (maybe we were dropped.)
|
||||
self.location.msg_contents("A %s on the floor flickers and dies." % self.key)
|
||||
# delete ourselves.
|
||||
try:
|
||||
self.location.msg_contents("A %s on the floor flickers and dies." % self.key)
|
||||
self.location.location.check_light_state()
|
||||
except AttributeError:
|
||||
pass
|
||||
self.delete()
|
||||
|
||||
|
||||
def light(self):
|
||||
"""
|
||||
Light this object - this is called by Light command.
|
||||
|
|
@ -348,10 +354,13 @@ class LightSource(TutorialObject):
|
|||
self.db.is_giving_light = True
|
||||
# if we are in a dark room, trigger its light check
|
||||
try:
|
||||
self.location.check_light_state()
|
||||
except Exception:
|
||||
# if we are not in a dark room, never mind
|
||||
pass
|
||||
self.location.location.check_light_state()
|
||||
except AttributeError:
|
||||
try:
|
||||
# maybe we are directly in the room
|
||||
self.location.check_light_state()
|
||||
except AttributeError:
|
||||
pass
|
||||
finally:
|
||||
# start the burn timer. When it runs out, self._burnout
|
||||
# will be called.
|
||||
|
|
@ -549,12 +558,11 @@ class CmdPressButton(Command):
|
|||
self.caller.location.msg_contents(string % self.caller.key, exclude=self.caller)
|
||||
self.obj.open_wall()
|
||||
|
||||
self.caller.msg(string)
|
||||
|
||||
|
||||
class CmdSetCrumblingWall(CmdSet):
|
||||
"Group the commands for crumblingWall"
|
||||
key = "crumblingwall_cmdset"
|
||||
priority = 2
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
"called when object is first created."
|
||||
|
|
@ -612,18 +620,18 @@ class CrumblingWall(TutorialObject, DefaultExit):
|
|||
# set cmdset
|
||||
self.cmdset.add(CmdSetCrumblingWall, permanent=True)
|
||||
|
||||
def open(self):
|
||||
def open_wall(self):
|
||||
"""
|
||||
This method is called by the push button command once the puzzle
|
||||
is solved. It opens the wall and sets a timer for it to reset
|
||||
itself.
|
||||
"""
|
||||
# this will make it into a proper exit
|
||||
eloc = self.caller.search(self.obj.db.destination, global_search=True)
|
||||
# this will make it into a proper exit (this returns a list)
|
||||
eloc = search.search_object(self.db.destination)
|
||||
if not eloc:
|
||||
self.caller.msg("The exit leads nowhere, there's just more stone behind it ...")
|
||||
else:
|
||||
self.obj.destination = eloc
|
||||
self.destination = eloc[0]
|
||||
self.exit_open = True
|
||||
# start a 45 second timer before closing again
|
||||
utils.delay(45, self.reset)
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ class CmdTutorialSetDetail(default_cmds.MuxCommand):
|
|||
if not hasattr(self.obj, "set_detail"):
|
||||
self.caller.msg("Details cannot be set on %s." % self.obj)
|
||||
return
|
||||
for key in self.args.split(";"):
|
||||
for key in self.lhs.split(";"):
|
||||
# loop over all aliases, if any (if not, this will just be
|
||||
# the one key to loop over)
|
||||
self.obj.set_detail(key, self.rhs)
|
||||
|
|
@ -341,7 +341,7 @@ class WeatherRoom(TutorialRoom):
|
|||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
DARK_MESSAGES = ("It is pitch black. You are likely to be eaten by a grue."
|
||||
DARK_MESSAGES = ("It is pitch black. You are likely to be eaten by a grue.",
|
||||
"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.",
|
||||
|
|
@ -475,6 +475,12 @@ class DarkRoom(TutorialRoom):
|
|||
self.db.is_lit = False
|
||||
self.cmdsets.add(DarkCmdSet, permanent=True)
|
||||
|
||||
def at_init(self):
|
||||
"""
|
||||
Called when room is first recached (such as after a reload)
|
||||
"""
|
||||
self.check_light_state()
|
||||
|
||||
def _carries_light(self, obj):
|
||||
"""
|
||||
Checks if the given object carries anything that gives light.
|
||||
|
|
@ -483,9 +489,9 @@ class DarkRoom(TutorialRoom):
|
|||
but for the Attribute is_giving_light - this makes it easy to
|
||||
later add other types of light-giving items. We also accept
|
||||
if there is a light-giving object in the room overall (like if
|
||||
a lantern was dropped in the room)
|
||||
a splinter was dropped in the room)
|
||||
"""
|
||||
return obj.db.is_giving_light or any(obj for obj in obj.contents if obj.db.is_giving_light)
|
||||
return obj.is_superuser or obj.db.is_giving_light or obj.is_superuser or any(o for o in obj.contents if o.db.is_giving_light)
|
||||
|
||||
def _heal(self, character):
|
||||
"""
|
||||
|
|
@ -502,18 +508,15 @@ class DarkRoom(TutorialRoom):
|
|||
the room and also by the Light sources when they turn on.
|
||||
"""
|
||||
if any(self._carries_light(obj) for obj in self.contents):
|
||||
# people are carrying lights
|
||||
if not self.db.is_lit:
|
||||
self.cmdset.remove(DarkCmdSet)
|
||||
self.db.is_lit = True
|
||||
for char in (obj for obj in self.contents if obj.has_player):
|
||||
# this won't do anything if it is already removed
|
||||
char.msg("The room is lit up.")
|
||||
self.cmdset.remove(DarkCmdSet)
|
||||
self.db.is_lit = True
|
||||
for char in (obj for obj in self.contents if obj.has_player):
|
||||
# this won't do anything if it is already removed
|
||||
char.msg("The room is lit up.")
|
||||
else:
|
||||
# noone is carrying light - darken the room
|
||||
if self.db.is_lit:
|
||||
self.db.is_lit = False
|
||||
self.cmdset.add(DarkCmdSet, permanent=True)
|
||||
self.db.is_lit = False
|
||||
self.cmdset.add(DarkCmdSet, permanent=True)
|
||||
for char in (obj for obj in self.contents if obj.has_player):
|
||||
if char.is_superuser:
|
||||
char.msg("You are Superuser, so you are not affected by the dark state.")
|
||||
|
|
@ -529,7 +532,7 @@ class DarkRoom(TutorialRoom):
|
|||
# a puppeted object, that is, a Character
|
||||
self._heal(obj)
|
||||
# in case the new guy carries light with them
|
||||
self.check_light_state()
|
||||
self.check_light_state()
|
||||
|
||||
def at_object_leave(self, obj, target_location):
|
||||
"""
|
||||
|
|
@ -538,7 +541,6 @@ class DarkRoom(TutorialRoom):
|
|||
teleported away.
|
||||
"""
|
||||
self.check_light_state()
|
||||
obj.cmdset.delete(DarkCmdSet)
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
|
|
@ -773,7 +775,7 @@ class CmdLookBridge(Command):
|
|||
fall_exit = search_object(self.obj.db.fall_exit)
|
||||
if fall_exit:
|
||||
self.caller.msg("{r%s{n" % FALL_MESSAGE)
|
||||
self.caller.move_to(fall_exit, quiet=True)
|
||||
self.caller.move_to(fall_exit[0], quiet=True)
|
||||
# inform others on the bridge
|
||||
self.obj.msg_contents("A plank gives way under %s's feet and " \
|
||||
"they fall from the bridge!" % self.caller.key)
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ call the handler's save() and restore() methods when the server reboots.
|
|||
|
||||
"""
|
||||
from twisted.internet.defer import inlineCallbacks
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from evennia.scripts.scripts import ExtendedLoopingCall
|
||||
from evennia.server.models import ServerConfig
|
||||
from evennia.utils.logger import log_trace, log_err
|
||||
|
|
@ -84,14 +85,17 @@ class Ticker(object):
|
|||
The callback should ideally work under @inlineCallbacks so it can yield
|
||||
appropriately.
|
||||
"""
|
||||
for key, (obj, args, kwargs) in self.subscriptions.items():
|
||||
for store_key, (obj, args, kwargs) in self.subscriptions.items():
|
||||
hook_key = yield kwargs.get("_hook_key", "at_tick")
|
||||
if not obj:
|
||||
# object was deleted between calls
|
||||
self.validate()
|
||||
self.remove(store_key)
|
||||
continue
|
||||
try:
|
||||
yield _GA(obj, hook_key)(*args, **kwargs)
|
||||
except ObjectDoesNotExist:
|
||||
log_trace()
|
||||
self.remove(store_key)
|
||||
except Exception:
|
||||
log_trace()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue