Add the first event on exit.at_traverse
This commit is contained in:
parent
74ab1ed030
commit
51bc9ac65a
3 changed files with 173 additions and 3 deletions
|
|
@ -9,6 +9,8 @@ and are designed to be used more by developers to add event types.
|
||||||
from evennia import logger
|
from evennia import logger
|
||||||
from evennia import ScriptDB
|
from evennia import ScriptDB
|
||||||
|
|
||||||
|
hooks = []
|
||||||
|
|
||||||
def create_event_type(typeclass, event_name, variables, help_text):
|
def create_event_type(typeclass, event_name, variables, help_text):
|
||||||
"""
|
"""
|
||||||
Create a new event type for a specific typeclass.
|
Create a new event type for a specific typeclass.
|
||||||
|
|
@ -37,9 +39,9 @@ def create_event_type(typeclass, event_name, variables, help_text):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get the event types for this typeclass
|
# Get the event types for this typeclass
|
||||||
event_types = script.db.event_types.get(typeclass_name, {})
|
if typeclass_name not in script.db.event_types:
|
||||||
if not event_types:
|
script.db.event_types[typeclass_name] = {}
|
||||||
script.db.event_types[typeclass_name] = event_types
|
event_types = script.db.event_types[typeclass_name]
|
||||||
|
|
||||||
# Add or replace the event
|
# Add or replace the event
|
||||||
event_types[event_name] = (variables, help_text)
|
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, {})
|
event_types = script.db.event_types.get(typeclass_name, {})
|
||||||
if event_name in event_types:
|
if event_name in event_types:
|
||||||
del event_types[event_name]
|
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]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,13 @@
|
||||||
Scripts for the event system.
|
Scripts for the event system.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from Queue import Queue
|
||||||
|
|
||||||
from evennia import DefaultScript
|
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):
|
class EventHandler(DefaultScript):
|
||||||
|
|
||||||
|
|
@ -16,3 +22,95 @@ class EventHandler(DefaultScript):
|
||||||
# Permanent data to be stored
|
# Permanent data to be stored
|
||||||
self.db.event_types = {}
|
self.db.event_types = {}
|
||||||
self.db.events = {}
|
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)
|
||||||
|
|
|
||||||
40
evennia/contrib/events/typeclasses.py
Normal file
40
evennia/contrib/events/typeclasses.py
Normal 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""")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue