Add the time-related events and events with parameters

This commit is contained in:
Vincent Le Goff 2017-03-14 20:56:55 -07:00 committed by Griatch
parent 1f4095c625
commit e898ee0ec2
4 changed files with 165 additions and 17 deletions

View file

@ -292,7 +292,7 @@ class CmdEvent(COMMAND_DEFAULT_CLASS):
# Open the editor
event = self.handler.add_event(obj, event_name, "",
self.caller, False)
self.caller, False, parameters=self.parameters)
self.caller.db._event = event
EvEditor(self.caller, loadfunc=_ev_load, savefunc=_ev_save,
quitfunc=_ev_quit, key="Event {} of {}".format(

View file

@ -8,8 +8,13 @@ and are designed to be used more by developers to add event types.
from textwrap import dedent
from django.conf import settings
from evennia import logger
from evennia import ScriptDB
from evennia.contrib.custom_gametime import UNITS
from evennia.contrib.custom_gametime import real_seconds_until as custom_rsu
from evennia.utils.create import create_script
from evennia.utils.gametime import real_seconds_until as standard_rsu
hooks = []
event_types = []
@ -24,7 +29,8 @@ def get_event_handler():
return script
def create_event_type(typeclass, event_name, variables, help_text):
def create_event_type(typeclass, event_name, variables, help_text,
custom_add=None):
"""
Create a new event type for a specific typeclass.
@ -33,6 +39,8 @@ def create_event_type(typeclass, event_name, variables, help_text):
event_name (str): the name of the event to be added.
variables (list of str): a list of variable names.
help_text (str): a help text of the event.
custom_add (function, default None): a callback to call when adding
the new event.
Events obey the inheritance hierarchy: if you set an event on
DefaultRoom, for instance, and if your Room typeclass inherits
@ -44,7 +52,8 @@ def create_event_type(typeclass, event_name, variables, help_text):
"""
typeclass_name = typeclass.__module__ + "." + typeclass.__name__
event_types.append((typeclass_name, event_name, variables, help_text))
event_types.append((typeclass_name, event_name, variables, help_text,
custom_add))
def del_event_type(typeclass, event_name):
"""
@ -118,7 +127,8 @@ def connect_event_types():
"cannot be found.")
return
for typeclass_name, event_name, variables, help_text in event_types:
for typeclass_name, event_name, variables, help_text, \
custom_add in event_types:
# Get the event types for this typeclass
if typeclass_name not in script.ndb.event_types:
script.ndb.event_types[typeclass_name] = {}
@ -126,4 +136,80 @@ def connect_event_types():
# Add or replace the event
help_text = dedent(help_text.strip("\n"))
types[event_name] = (variables, help_text)
types[event_name] = (variables, help_text, custom_add)
# Custom callbacks for specific events
def get_next_wait(format):
"""
Get the length of time in seconds before format.
Args:
format (str): a time format matching the set calendar.
The time format could be something like "2018-01-08 12:00". The
number of units set in the calendar affects the way seconds are
calculated.
"""
calendar = getattr(settings, "EVENTS_CALENDAR", None)
if calendar is None:
logger.log_err("A time-related event has been set whereas " \
"the gametime calendar has not been set in the settings.")
return
elif calendar == "standard":
rsu = standard_rsu
units = ["min", "hour", "day", "month", "year"]
elif calendar == "custom":
rsu = custom_rsu
back = dict([(value, name) for name, value in UNITS.items()])
sorted_units = sorted(back.items())
del sorted_units[0]
units = [n for v, n in sorted_units]
params = {}
for delimiter in ("-", ":"):
format = format.replace(delimiter, " ")
pieces = list(reversed(format.split()))
details = []
i = 0
for uname in units:
try:
piece = pieces[i]
except IndexError:
break
if not piece.isdigit():
logger.log_err("The time specified '{}' in {} isn't " \
"a valid number".format(piece, format))
return
# Convert the piece to int
piece = int(piece)
params[uname] = piece
details.append("{}={}".format(uname, piece))
i += 1
params["sec"] = 0
details = " ".join(details)
seconds = rsu(**params)
return seconds, details
def create_time_event(obj, event_name, number, parameters):
"""
Create an time-related event.
args:
obj (Object): the object on which stands the event.
event_name (str): the event's name.
number (int): the number of the event.
parameter (str): the parameter of the event.
"""
print "parameters", repr(parameters)
seconds, key = get_next_wait(parameters)
script = create_script("evennia.contrib.events.scripts.TimeEventScript", interval=seconds, obj=obj)
script.key = key
script.desc = "time event called regularly on {}".format(key)
script.db.time_format = parameters
script.db.number = number

View file

@ -5,10 +5,12 @@ Scripts for the event system.
from datetime import datetime
from Queue import Queue
from evennia import DefaultScript
from django.conf import settings
from evennia import DefaultScript, ScriptDB
from evennia import logger
from evennia.contrib.events.custom import connect_event_types, \
get_next_wait, patch_hooks
from evennia.contrib.events.exceptions import InterruptEvent
from evennia.contrib.events.custom import connect_event_types, patch_hooks
from evennia.contrib.events import typeclasses
from evennia.utils.utils import all_from_module
@ -64,7 +66,8 @@ class EventHandler(DefaultScript):
return types
def add_event(self, obj, event_name, code, author=None, valid=False):
def add_event(self, obj, event_name, code, author=None, valid=False,
parameters=""):
"""
Add the specified event.
@ -74,6 +77,7 @@ class EventHandler(DefaultScript):
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?
parameters (str, optional): optional parameters.
This method doesn't check that the event type exists.
@ -100,6 +104,13 @@ class EventHandler(DefaultScript):
if not valid:
self.db.to_valid.append((obj, event_name, len(events) - 1))
# Call the custom_add if needed
custom_add = self.get_event_types(obj).get(
event_name, [None, None, None])[2]
print "custom_add", custom_add
if custom_add:
custom_add(obj, event_name, len(events) - 1, parameters)
# Build the definition to return (a dictionary)
definition = dict(events[-1])
definition["obj"] = obj
@ -163,7 +174,7 @@ class EventHandler(DefaultScript):
if (obj, event_name, number) in self.db.to_valid:
self.db.to_valid.remove((obj, event_name, number))
def call_event(self, obj, event_name, *args):
def call_event(self, obj, event_name, number=None, *args):
"""
Call the event.
@ -171,6 +182,7 @@ class EventHandler(DefaultScript):
obj (Object): the Evennia typeclassed object.
event_name (str): the event name to call.
*args: additional variables for this event.
number (int, default None): call just a specific event.
Returns:
True to report the event was called without interruption,
@ -198,13 +210,64 @@ class EventHandler(DefaultScript):
# Now execute all the valid events linked at this address
events = self.db.events.get(obj, {}).get(event_name, [])
for event in events:
for i, event in enumerate(events):
if not event["valid"]:
continue
if number is not None and i != number:
continue
try:
exec(event["code"], locals, locals)
except InterruptEvent:
return False
return True
# Script to call time-related events
class TimeEventScript(DefaultScript):
"""Gametime-sensitive script."""
def at_script_creation(self):
"""The script is created."""
self.start_delay = True
self.persistent = True
# Script attributes
self.db.time_format = None
self.db.event_name = "time"
self.db.number = None
def at_repeat(self):
"""Call the event and reset interval."""
# Get the event handler and call the script
try:
script = ScriptDB.objects.get(db_key="event_handler")
except ScriptDB.DoesNotExist:
logger.log_err("Can't get the event handler.")
return
if self.db.event_name and self.db.number is not None:
obj = self.obj
event_name = self.db.event_name
number = self.db.number
events = script.db.events.get(obj, {}).get(event_name)
if events is None:
logger.log_err("Cannot find the event {} on {}".format(
event_name, obj))
return
try:
event = events[number]
except IndexError:
logger.log_err("Cannot find the event {} {} on {}".format(
event_name, number, obj))
return
script.call_event(obj, event_name, number, obj)
if self.db.time_format:
seconds, details = get_next_wait(self.db.time_format)
self.restart(interval=seconds)

View file

@ -4,7 +4,8 @@ Patched typeclasses for Evennia.
from evennia import DefaultCharacter, DefaultExit, DefaultObject, DefaultRoom
from evennia import ScriptDB
from evennia.contrib.events.custom import create_event_type, patch_hook
from evennia.contrib.events.custom import create_event_type, patch_hook, \
create_time_event
from evennia.utils.utils import inherits_from
class PatchedExit(object):
@ -31,7 +32,7 @@ class PatchedExit(object):
is_character = inherits_from(traversing_object, DefaultCharacter)
script = ScriptDB.objects.get(db_key="event_handler")
if is_character:
allow = script.call_event(exit, "can_traverse", traversing_object,
allow = script.call_event(exit, "can_traverse", None, traversing_object,
exit, exit.location)
if not allow:
return
@ -40,7 +41,7 @@ class PatchedExit(object):
# After traversing
if is_character:
script.call_event(exit, "traverse", traversing_object,
script.call_event(exit, "traverse", None, traversing_object,
exit, exit.location, exit.destination)
@ -71,7 +72,7 @@ create_event_type(DefaultExit, "traverse", ["character", "exit",
""")
# Room events
create_event_type(DefaultRoom, "time", ["room", "time"], """
create_event_type(DefaultRoom, "time", ["room"], """
A repeated event to be called regularly.
This event is scheduled to repeat at different times, specified
as parameters. You can set it to run every day at 8:00 AM (game
@ -87,6 +88,4 @@ create_event_type(DefaultRoom, "time", ["room", "time"], """
Variables you can use in this event:
room: the room connected to this event.
time: a string containing the current time.
""")
""", create_time_event)