Add the first event on exit.at_traverse

This commit is contained in:
Vincent Le Goff 2017-03-09 21:17:41 -08:00 committed by Griatch
parent 74ab1ed030
commit 51bc9ac65a
3 changed files with 173 additions and 3 deletions

View file

@ -9,6 +9,8 @@ and are designed to be used more by developers to add event types.
from evennia import logger
from evennia import ScriptDB
hooks = []
def create_event_type(typeclass, event_name, variables, help_text):
"""
Create a new event type for a specific typeclass.
@ -37,9 +39,9 @@ def create_event_type(typeclass, event_name, variables, help_text):
return
# Get the event types for this typeclass
event_types = script.db.event_types.get(typeclass_name, {})
if not event_types:
script.db.event_types[typeclass_name] = event_types
if typeclass_name not in script.db.event_types:
script.db.event_types[typeclass_name] = {}
event_types = script.db.event_types[typeclass_name]
# Add or replace the event
event_types[event_name] = (variables, help_text)
@ -70,3 +72,33 @@ def del_event_type(typeclass, event_name):
event_types = script.db.event_types.get(typeclass_name, {})
if event_name in event_types:
del event_types[event_name]
def patch_hook(typeclass, method_name):
"""Decorator to softly patch a hook in a typeclass."""
hook = getattr(typeclass, method_name)
def wrapper(method):
"""Wrapper around the hook."""
def overridden_hook(*args, **kwargs):
"""Function to call the new hook."""
# Enforce the old hook as a keyword argument
kwargs["hook"] = hook
ret = method(*args, **kwargs)
return ret
hooks.append((typeclass, method_name, overridden_hook))
return overridden_hook
return wrapper
def patch_hooks():
"""
Patch all the configured hooks.
This function should be called only once when the event system
has loaded, is set and has defined its patched typeclasses.
It will be called internally by the event system, you shouldn't
call this function in your game.
"""
while hooks:
typeclass, method_name, new_hook = hooks[0]
setattr(typeclass, method_name, new_hook)
del hooks[0]

View file

@ -2,7 +2,13 @@
Scripts for the event system.
"""
from datetime import datetime
from Queue import Queue
from evennia import DefaultScript
from evennia import logger
from evennia.contrib.events.extend import patch_hooks
from evennia.contrib.events import typeclasses
class EventHandler(DefaultScript):
@ -16,3 +22,95 @@ class EventHandler(DefaultScript):
# Permanent data to be stored
self.db.event_types = {}
self.db.events = {}
def at_start(self):
"""Set up the event system."""
patch_hooks()
def add_event(self, obj, event_name, code, author=None, valid=True):
"""
Add the specified event.
Args:
obj (Object): the Evennia typeclassed object to be modified.
event_name (str): the name of the event to add.
code (str): the Python code associated with this event.
author (optional, Character, Player): the author of the event.
valid (optional, bool): should the event be connected?
This method doesn't check that the event type exists.
"""
obj_events = self.db.events.get(obj, {})
if not obj_events:
self.db.events[obj] = {}
obj_events = self.db.events[obj]
events = obj_events.get(event_name, [])
if not events:
obj_events[event_name] = []
events = obj_events[event_name]
# Add the event in the list
events.append({
"created_on": datetime.now(),
"author": author,
"valid": valid,
"code": code,
})
def call_event(self, obj, event_name, *args):
"""
Call the event.
Args:
obj (Object): the Evennia typeclassed object.
event_name (str): the event name to call.
*args: additional variables for this event.
Returns:
True to report the event was called without interruption,
False otherwise.
"""
# First, look for the event type corresponding to this name
# To do so, go back the inheritance tree
event_type = None
event_types = self.db.event_types
classes = Queue()
classes.put(type(obj))
while not classes.empty():
typeclass = classes.get()
typeclass_name = typeclass.__module__ + "." + typeclass.__name__
event_type = event_types.get(typeclass_name, {}).get(event_name)
if event_type:
break
else:
# Look for the parent classes
for parent in typeclass.__bases__:
classes.put(parent)
# If there is still no event_type
if not event_type:
logger.log_err("The event {} for the object {} (typeclass " \
"{}) can't be found".format(event_name, obj, type(obj)))
return False
# Prepare the locals
locals = {}
for i, variable in enumerate(event_type[0]):
try:
locals[variable] = args[i]
except IndexError:
logger.log_err("event {} of {} ({}): need variable " \
"{} in position {}".format(event_name, obj,
type(obj), variable, i))
return False
# Now execute all the valid events linked at this address
events = self.db.events.get(obj, {}).get(event_name, [])
for event in events:
if not event["valid"]:
continue
exec(event["code"], locals, locals)

View file

@ -0,0 +1,40 @@
"""
Patched typeclasses for Evennia.
"""
from evennia import DefaultCharacter, DefaultExit
from evennia import ScriptDB
from evennia.contrib.events.extend import create_event_type, patch_hook
from evennia.utils.utils import inherits_from
class PatchedExit(object):
"""Patched exit to patch some hooks of DefaultExit."""
@staticmethod
@patch_hook(DefaultExit, "at_traverse")
def at_traverse(exit, traversing_object, target_location, hook=None):
"""
This hook is responsible for handling the actual traversal,
normally by calling
`traversing_object.move_to(target_location)`. It is normally
only implemented by Exit objects. If it returns False (usually
because `move_to` returned False), `at_after_traverse` below
should not be called and instead `at_failed_traverse` should be
called.
Args:
traversing_object (Object): Object traversing us.
target_location (Object): Where target is going.
"""
if inherits_from(traversing_object, DefaultCharacter):
script = ScriptDB.objects.get(db_key="event_handler")
script.call_event(exit, "at_traverse", traversing_object,
exit, exit.location)
hook(exit, traversing_object, target_location)
# Default events
create_event_type(DefaultExit, "at_traverse", ["character", "exit", "room"],
"""When traversing""")