Update event typeclasses to use inheritance instead of hook patching

This commit is contained in:
Vincent Le Goff 2017-04-03 14:54:54 -07:00 committed by Griatch
parent 1caf5e988c
commit 08fd37aa98
4 changed files with 258 additions and 157 deletions

View file

@ -80,44 +80,6 @@ def invalidate_event_type(typeclass, event_name):
typeclass_name = typeclass.__module__ + "." + typeclass.__name__ typeclass_name = typeclass.__module__ + "." + typeclass.__name__
event_types.append((typeclass_name, event_name, None, "", None, None)) event_types.append((typeclass_name, event_name, None, "", None, None))
def patch_hook(typeclass, method_name):
"""
Decorator to softly patch a hook in a typeclass.
This decorator should not be used, unless for good reasons, outside
of this contrib. The advantage of using decorated soft patchs is
in allowing users to customize typeclasses without changing the
inheritance tree for a couple of methods.
"""
hook = getattr(typeclass, method_name, None)
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]
def connect_event_types(): def connect_event_types():
""" """
Connect the event types when the script runs. Connect the event types when the script runs.
@ -243,7 +205,7 @@ def create_time_event(obj, event_name, number, parameters):
def keyword_event(events, parameters): def keyword_event(events, parameters):
""" """
Custom call for events with keywords (like say, or push, or pull, or turn...). Custom call for events with keywords (like push, or pull, or turn...).
This function should be imported and added as a custom_call This function should be imported and added as a custom_call
parameter to add the event type when the event supports keywords parameter to add the event type when the event supports keywords
@ -267,3 +229,37 @@ def keyword_event(events, parameters):
to_call.append(event) to_call.append(event)
return to_call return to_call
def phrase_event(events, parameters):
"""
Custom call for events with keywords in sentences (like say or whisper).
This function should be imported and added as a custom_call
parameter to add the event type when the event supports keywords
in phrase as parameters. Keywords in parameters are one or more
words separated by a comma. For instance, a 'say yes, okay' event
can be set to trigger when the player says something containing
either "yes" or "okay" (maybe 'say I don't like it, but okay').
Args:
events (list of dict): the list of events to be called.
parameters (str): the actual parameters entered to trigger the event.
Returns:
A list containing the event dictionaries to be called.
"""
phrase = parameters.strip().lower()
# Remove punctuation marks
punctuations = ',.";?!'
for p in punctuations:
phrase = phrase.replace(p, " ")
words = phrase.split()
words = [w.strip("' ") for w in words if w.strip("' ")]
to_call = []
for event in events:
keys = event["parameters"]
if not keys or any(key.strip().lower() in words for key in keys.split(",")):
to_call.append(event)
return to_call

View file

@ -14,8 +14,7 @@ from evennia import logger
from evennia.utils.create import create_channel from evennia.utils.create import create_channel
from evennia.utils.dbserialize import dbserialize from evennia.utils.dbserialize import dbserialize
from evennia.utils.utils import all_from_module, delay from evennia.utils.utils import all_from_module, delay
from evennia.contrib.events.custom import ( from evennia.contrib.events.custom import connect_event_types, get_next_wait
connect_event_types, get_next_wait, patch_hooks)
from evennia.contrib.events.exceptions import InterruptEvent from evennia.contrib.events.exceptions import InterruptEvent
from evennia.contrib.events.handler import EventsHandler as Handler from evennia.contrib.events.handler import EventsHandler as Handler
from evennia.contrib.events import typeclasses from evennia.contrib.events import typeclasses
@ -36,6 +35,7 @@ class EventHandler(DefaultScript):
""" """
def at_script_creation(self): def at_script_creation(self):
"""Hook called when the script is created."""
self.key = "event_handler" self.key = "event_handler"
self.desc = "Global event handler" self.desc = "Global event handler"
self.persistent = True self.persistent = True
@ -50,10 +50,21 @@ class EventHandler(DefaultScript):
self.db.tasks = {} self.db.tasks = {}
def at_start(self): def at_start(self):
"""Set up the event system.""" """Set up the event system when starting.
Note that this hook is called every time the server restarts
(including when it's reloaded). This hook performs the following
tasks:
- Refresh and re-connect event types.
- Generate locals (individual events' namespace).
- Load event helpers, including user-defined ones.
- Re-schedule tasks that aren't set to fire anymore.
- Effectively connect the handler to the main script.
"""
self.ndb.event_types = {} self.ndb.event_types = {}
connect_event_types() connect_event_types()
patch_hooks()
# Generate locals # Generate locals
self.ndb.current_locals = {} self.ndb.current_locals = {}

View file

@ -30,6 +30,13 @@ class TestEventHandler(EvenniaTest):
self.handler = create_script( self.handler = create_script(
"evennia.contrib.events.scripts.EventHandler") "evennia.contrib.events.scripts.EventHandler")
# Alter typeclasses
self.char1.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
self.char2.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
self.room1.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
self.room2.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
self.exit.swap_typeclass("evennia.contrib.events.typeclasses.EventExit")
def tearDown(self): def tearDown(self):
"""Stop the event handler.""" """Stop the event handler."""
self.handler.stop() self.handler.stop()
@ -225,28 +232,28 @@ class TestEventHandler(EvenniaTest):
self.assertIsNotNone(self.char1.events) self.assertIsNotNone(self.char1.events)
# Add an event # Add an event
event = self.room1.events.add("say", "pass", author=self.char1, event = self.room1.events.add("dummy", "pass", author=self.char1,
valid=True) valid=True)
self.assertEqual(event.obj, self.room1) self.assertEqual(event.obj, self.room1)
self.assertEqual(event.name, "say") self.assertEqual(event.name, "dummy")
self.assertEqual(event.code, "pass") self.assertEqual(event.code, "pass")
self.assertEqual(event.author, self.char1) self.assertEqual(event.author, self.char1)
self.assertEqual(event.valid, True) self.assertEqual(event.valid, True)
self.assertIn([event], self.room1.events.all().values()) self.assertIn([event], self.room1.events.all().values())
# Edit this very event # Edit this very event
new = self.room1.events.edit("say", 0, "character.db.say = True", new = self.room1.events.edit("dummy", 0, "character.db.say = True",
author=self.char1, valid=True) author=self.char1, valid=True)
self.assertIn([new], self.room1.events.all().values()) self.assertIn([new], self.room1.events.all().values())
self.assertNotIn([event], self.room1.events.all().values()) self.assertNotIn([event], self.room1.events.all().values())
# Try to call this event # Try to call this event
self.assertTrue(self.room1.events.call("say", self.assertTrue(self.room1.events.call("dummy",
locals={"character": self.char2})) locals={"character": self.char2}))
self.assertTrue(self.char2.db.say) self.assertTrue(self.char2.db.say)
# Delete the event # Delete the event
self.room1.events.remove("say", 0) self.room1.events.remove("dummy", 0)
self.assertEqual(self.room1.events.all(), {}) self.assertEqual(self.room1.events.all(), {})
@ -260,6 +267,13 @@ class TestCmdEvent(CommandTest):
self.handler = create_script( self.handler = create_script(
"evennia.contrib.events.scripts.EventHandler") "evennia.contrib.events.scripts.EventHandler")
# Alter typeclasses
self.char1.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
self.char2.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
self.room1.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
self.room2.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
self.exit.swap_typeclass("evennia.contrib.events.typeclasses.EventExit")
def tearDown(self): def tearDown(self):
"""Stop the event handler.""" """Stop the event handler."""
self.handler.stop() self.handler.stop()
@ -412,6 +426,13 @@ class TestDefaultEvents(CommandTest):
self.handler = create_script( self.handler = create_script(
"evennia.contrib.events.scripts.EventHandler") "evennia.contrib.events.scripts.EventHandler")
# Alter typeclasses
self.char1.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
self.char2.swap_typeclass("evennia.contrib.events.typeclasses.EventCharacter")
self.room1.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
self.room2.swap_typeclass("evennia.contrib.events.typeclasses.EventRoom")
self.exit.swap_typeclass("evennia.contrib.events.typeclasses.EventExit")
def tearDown(self): def tearDown(self):
"""Stop the event handler.""" """Stop the event handler."""
self.handler.stop() self.handler.stop()
@ -428,6 +449,9 @@ class TestDefaultEvents(CommandTest):
character.msg("You cannot leave.") character.msg("You cannot leave.")
deny() deny()
""".strip("\n")) """.strip("\n"))
# Enforce self.exit.destination since swapping typeclass lose it
self.exit.destination = self.room2
# Try the can_traverse event # Try the can_traverse event
self.handler.add_event(self.exit, "can_traverse", code, self.handler.add_event(self.exit, "can_traverse", code,
author=self.char1, valid=True) author=self.char1, valid=True)

View file

@ -1,33 +1,23 @@
""" """
Patched typeclasses for Evennia. Typeclasses for the event system.
These typeclasses are not inherited from DefaultObject and other To use thm, one should inherit from these classes (EventObject,
Evennia default types. They softly "patch" some of these object hooks EventRoom, EventCharacter and EventExit).
however. While this adds a new layer in this module, it's (normally)
more simple to use from game designers, since it doesn't require a
new inheritance. These replaced hooks are only active if the event
system is active. You shouldn't need to change this module, just
override the hooks as you usually do in your custom typeclasses.
Calling super() would call the Default hooks (which would call the
event hook without further ado).
""" """
from evennia import DefaultCharacter, DefaultExit, DefaultObject, DefaultRoom from evennia import DefaultCharacter, DefaultExit, DefaultObject, DefaultRoom
from evennia import ScriptDB from evennia import ScriptDB
from evennia.utils.utils import inherits_from, lazy_property from evennia.utils.utils import delay, inherits_from, lazy_property
from evennia.contrib.events.custom import ( from evennia.contrib.events.custom import (
create_event_type, invalidate_event_type, patch_hook, create_time_event) create_event_type, invalidate_event_type, create_time_event, phrase_event)
from evennia.contrib.events.handler import EventsHandler from evennia.contrib.events.handler import EventsHandler
class EventCharacter: class EventCharacter(DefaultCharacter):
"""Patched typeclass for DefaultCharcter.""" """Typeclass to represent a character and call event types."""
@staticmethod def announce_move_from(self, destination, msg=None, mapping=None):
@patch_hook(DefaultCharacter, "announce_move_from")
def announce_move_from(character, destination, msg=None, mapping=None,
hook=None):
""" """
Called if the move is to be announced. This is Called if the move is to be announced. This is
called while we are still standing in the old called while we are still standing in the old
@ -47,21 +37,21 @@ class EventCharacter:
destination: the location of the object after moving. destination: the location of the object after moving.
""" """
if not character.location: if not self.location:
return return
string = msg or "{object} is leaving {origin}, heading for {destination}." string = msg or "{object} is leaving {origin}, heading for {destination}."
# Get the exit from location to destination # Get the exit from location to destination
location = character.location location = self.location
exits = [o for o in location.contents if o.location is location and o.destination is destination] exits = [o for o in location.contents if o.location is location and o.destination is destination]
mapping = mapping or {} mapping = mapping or {}
mapping.update({ mapping.update({
"character": character, "character": self,
}) })
if exits: if exits:
exits[0].events.call("msg_leave", character, exits[0], exits[0].events.call("msg_leave", self, exits[0],
location, destination, string, mapping) location, destination, string, mapping)
string = exits[0].events.get_variable("message") string = exits[0].events.get_variable("message")
mapping = exits[0].events.get_variable("mapping") mapping = exits[0].events.get_variable("mapping")
@ -71,13 +61,9 @@ class EventCharacter:
if not string: if not string:
return return
if hook: super(EventCharacter, self).announce_move_from(destination, msg=string, mapping=mapping)
hook(character, destination, msg=string, mapping=mapping)
@staticmethod def announce_move_to(self, source_location, msg=None, mapping=None):
@patch_hook(DefaultCharacter, "announce_move_to")
def announce_move_to(character, source_location, msg=None, mapping=None,
hook=None):
""" """
Called after the move if the move was not quiet. At this point Called after the move if the move was not quiet. At this point
we are standing in the new location. we are standing in the new location.
@ -97,11 +83,11 @@ class EventCharacter:
""" """
if not source_location and character.location.has_player: if not source_location and self.location.has_player:
# This was created from nowhere and added to a player's # This was created from nowhere and added to a player's
# inventory; it's probably the result of a create command. # inventory; it's probably the result of a create command.
string = "You now have %s in your possession." % self.get_display_name(self.location) string = "You now have %s in your possession." % self.get_display_name(self.location)
character.location.msg(string) self.location.msg(string)
return return
if source_location: if source_location:
@ -110,17 +96,17 @@ class EventCharacter:
string = "{character} arrives to {destination}." string = "{character} arrives to {destination}."
origin = source_location origin = source_location
destination = character.location destination = self.location
exits = [] exits = []
mapping = mapping or {} mapping = mapping or {}
mapping.update({ mapping.update({
"character": character, "character": self,
}) })
if origin: if origin:
exits = [o for o in destination.contents if o.location is destination and o.destination is origin] exits = [o for o in destination.contents if o.location is destination and o.destination is origin]
if exits: if exits:
exits[0].events.call("msg_arrive", character, exits[0], exits[0].events.call("msg_arrive", self, exits[0],
origin, destination, string, mapping) origin, destination, string, mapping)
string = exits[0].events.get_variable("message") string = exits[0].events.get_variable("message")
mapping = exits[0].events.get_variable("mapping") mapping = exits[0].events.get_variable("mapping")
@ -130,12 +116,9 @@ class EventCharacter:
if not string: if not string:
return return
if hook: super(EventCharacter, self).announce_move_to(source_location, msg=string, mapping=mapping)
hook(character, source_location, msg=string, mapping=mapping)
@staticmethod def at_before_move(self, destination):
@patch_hook(DefaultCharacter, "at_before_move")
def at_before_move(character, destination, hook=None):
""" """
Called just before starting to move this object to Called just before starting to move this object to
destination. destination.
@ -151,18 +134,18 @@ class EventCharacter:
before it is even started. before it is even started.
""" """
origin = character.location origin = self.location
Room = DefaultRoom Room = DefaultRoom
if isinstance(origin, Room) and isinstance(destination, Room): if isinstance(origin, Room) and isinstance(destination, Room):
can = character.events.call("can_move", character, can = self.events.call("can_move", self,
origin, destination) origin, destination)
if can: if can:
can = origin.events.call("can_move", character, origin) can = origin.events.call("can_move", self, origin)
if can: if can:
# Call other character's 'can_part' event # Call other character's 'can_part' event
for present in [o for o in origin.contents if isinstance( for present in [o for o in origin.contents if isinstance(
o, DefaultCharacter) and o is not character]: o, DefaultCharacter) and o is not self]:
can = present.events.call("can_part", present, character) can = present.events.call("can_part", present, self)
if not can: if not can:
break break
@ -173,9 +156,7 @@ class EventCharacter:
return True return True
@staticmethod def at_after_move(self, source_location):
@patch_hook(DefaultCharacter, "at_after_move")
def at_after_move(character, source_location, hook=None):
""" """
Called after move has completed, regardless of quiet mode or Called after move has completed, regardless of quiet mode or
not. Allows changes to the object due to the location it is not. Allows changes to the object due to the location it is
@ -185,39 +166,34 @@ class EventCharacter:
source_location (Object): Wwhere we came from. This may be `None`. source_location (Object): Wwhere we came from. This may be `None`.
""" """
if hook: super(EventCharacter, self).at_after_move(source_location)
hook(character, source_location)
origin = source_location origin = source_location
destination = character.location destination = self.location
Room = DefaultRoom Room = DefaultRoom
if isinstance(origin, Room) and isinstance(destination, Room): if isinstance(origin, Room) and isinstance(destination, Room):
character.events.call("move", character, origin, destination) self.events.call("move", self, origin, destination)
destination.events.call("move", character, origin, destination) destination.events.call("move", self, origin, destination)
# Call the 'greet' event of characters in the location # Call the 'greet' event of characters in the location
for present in [o for o in destination.contents if isinstance( for present in [o for o in destination.contents if isinstance(
o, DefaultCharacter)]: o, DefaultCharacter) and o is not self]:
present.events.call("greet", present, character) present.events.call("greet", present, self)
@staticmethod def at_object_delete(self):
@patch_hook(DefaultCharacter, "at_object_delete")
def at_object_delete(character, hook=None):
""" """
Called just before the database object is permanently Called just before the database object is permanently
delete()d from the database. If this method returns False, delete()d from the database. If this method returns False,
deletion is aborted. deletion is aborted.
""" """
if not character.events.call("can_delete", character): if not self.events.call("can_delete", self):
return False return False
character.events.call("delete", character) self.events.call("delete", self)
return True return True
@staticmethod def at_post_puppet(self):
@patch_hook(DefaultCharacter, "at_post_puppet")
def at_post_puppet(character, hook=None):
""" """
Called just after puppeting has been completed and all Called just after puppeting has been completed and all
Player<->Object links have been established. Player<->Object links have been established.
@ -229,19 +205,16 @@ class EventCharacter:
puppeting this Object. puppeting this Object.
""" """
if hook: super(EventCharacter, self).at_post_puppet()
hook(character)
character.events.call("puppeted", character) self.events.call("puppeted", self)
# Call the room's puppeted_in event # Call the room's puppeted_in event
location = character.location location = self.location
if location and isinstance(location, DefaultRoom): if location and isinstance(location, DefaultRoom):
location.events.call("puppeted_in", character, location) location.events.call("puppeted_in", self, location)
@staticmethod def at_pre_unpuppet(self):
@patch_hook(DefaultCharacter, "at_pre_unpuppet")
def at_pre_unpuppet(character, hook=None):
""" """
Called just before beginning to un-connect a puppeting from Called just before beginning to un-connect a puppeting from
this Player. this Player.
@ -253,24 +226,21 @@ class EventCharacter:
puppeting this Object. puppeting this Object.
""" """
character.events.call("unpuppeted", character) self.events.call("unpuppeted", self)
if hook:
hook(character)
# Call the room's unpuppeted_in event # Call the room's unpuppeted_in event
location = character.location location = self.location
if location and isinstance(location, DefaultRoom): if location and isinstance(location, DefaultRoom):
location.events.call("unpuppeted_in", character, location) location.events.call("unpuppeted_in", self, location)
super(EventCharacter, self).at_pre_unpuppet()
class EventExit(object): class EventExit(DefaultExit):
"""Patched exit to patch some hooks of DefaultExit.""" """Modified exit including management of events."""
@staticmethod def at_traverse(self, traversing_object, target_location):
@patch_hook(DefaultExit, "at_traverse")
def at_traverse(exit, traversing_object, target_location, hook=None):
""" """
This hook is responsible for handling the actual traversal, This hook is responsible for handling the actual traversal,
normally by calling normally by calling
@ -287,51 +257,91 @@ class EventExit(object):
""" """
is_character = inherits_from(traversing_object, DefaultCharacter) is_character = inherits_from(traversing_object, DefaultCharacter)
if is_character: if is_character:
allow = exit.events.call("can_traverse", traversing_object, allow = self.events.call("can_traverse", traversing_object,
exit, exit.location) self, self.location)
if not allow: if not allow:
return return
if hook: super(EventExit, self).at_traverse(traversing_object, target_location)
hook(exit, traversing_object, target_location)
# After traversing # After traversing
if is_character: if is_character:
exit.events.call("traverse", traversing_object, self.events.call("traverse", traversing_object,
exit, exit.location, exit.destination) self, self.location, self.destination)
class EventRoom: class EventRoom(DefaultRoom):
"""Soft-patching of room's default hooks.""" """Default room with management of events."""
@staticmethod def at_object_delete(self):
@patch_hook(DefaultRoom, "at_object_delete")
def at_object_delete(room, hook=None):
""" """
Called just before the database object is permanently Called just before the database object is permanently
delete()d from the database. If this method returns False, delete()d from the database. If this method returns False,
deletion is aborted. deletion is aborted.
""" """
if not room.events.call("can_delete", room): if not self.events.call("can_delete", self):
return False return False
room.events.call("delete", room) self.events.call("delete", self)
return True return True
def at_say(self, speaker, message):
"""
Called on this object if an object inside this object speaks.
The string returned from this method is the final form of the
speech.
class EventObject(object): Args:
speaker (Object): The object speaking.
message (str): The words spoken.
"""Patched default object.""" Notes:
You should not need to add things like 'you say: ' or
similar here, that should be handled by the say command before
this.
"""
allow = self.events.call("can_say", speaker, self, message,
parameters=message)
if not allow:
return
message = self.events.get_variable("message")
# Call the event "can_say" of other characters in the location
for present in [o for o in self.contents if isinstance(
o, DefaultCharacter) and o is not speaker]:
allow = present.events.call("can_say", speaker, present,
message, parameters=message)
if not allow:
return
message = present.events.get_variable("message")
# We force the next event to be called after the message
# This will have to change when the Evennia API adds new hooks
delay(0, self.events.call, "say", speaker, self, message,
parameters=message)
for present in [o for o in self.contents if isinstance(
o, DefaultCharacter) and o is not speaker]:
delay(0, present.events.call, "say", speaker, present, message,
parameters=message)
return message
class EventObject(DefaultObject):
"""Default object with management of events."""
@lazy_property @lazy_property
def events(self): def events(self):
"""Return the EventsHandler.""" """Return the EventsHandler."""
return EventsHandler(self) return EventsHandler(self)
@staticmethod
@patch_hook(DefaultObject, "at_get") def at_get(self, getter):
def at_get(obj, getter, hook=None):
""" """
Called by the default `get` command when this object has been Called by the default `get` command when this object has been
picked up. picked up.
@ -344,14 +354,10 @@ class EventObject(object):
permissions for that. permissions for that.
""" """
if hook: super(EventObject, self).at_get(getter)
hook(obj, getter) self.events.call("get", getter, self)
obj.events.call("get", getter, obj) def at_drop(self, dropper):
@staticmethod
@patch_hook(DefaultObject, "at_drop")
def at_drop(obj, dropper, hook=None):
""" """
Called by the default `drop` command when this object has been Called by the default `drop` command when this object has been
dropped. dropped.
@ -364,10 +370,8 @@ class EventObject(object):
permissions from that. permissions from that.
""" """
if hook: super(EventObject, self).at_drop(dropper)
hook(obj, dropper) self.events.call("drop", dropper, self)
obj.events.call("drop", dropper, obj)
## Default events ## Default events
# Character events # Character events
@ -406,6 +410,19 @@ create_event_type(DefaultCharacter, "can_part", ["character", "departing"], """
departing: the character who wants to leave this room. departing: the character who wants to leave this room.
character: the character connected to this event. character: the character connected to this event.
""") """)
create_event_type(DefaultCharacter, "can_say", ["speaker", "character", "message"], """
Before another character can say something in the same location.
This event is called before another character says something in the
character's location. The "something" in question can be modified,
or the action can be prevented by using 'deny()'. To change the
content of what the character says, simply change the variable
'message' to another string of characters.
Variables you can use in this event:
speaker: the character who is using the say command.
character: the character connected to this event.
message: the text spoken by the character.
""", custom_call=phrase_event)
create_event_type(DefaultCharacter, "delete", ["character"], """ create_event_type(DefaultCharacter, "delete", ["character"], """
Before deleting the character. Before deleting the character.
This event is called just before deleting this character. It shouldn't This event is called just before deleting this character. It shouldn't
@ -450,6 +467,27 @@ create_event_type(DefaultCharacter, "puppeted", ["character"], """
Variables you can use in this event: Variables you can use in this event:
character: the character connected to this event. character: the character connected to this event.
""") """)
create_event_type(DefaultCharacter, "say", ["speaker", "character", "message"], """
After another character has said something in the character's room.
This event is called right after another character has said
something in the same location.. The action cannot be prevented
at this moment. Instead, this event is ideal to create keywords
that would trigger a character (like a NPC) in doing something
if a specific phrase is spoken in the same location.
To use this event, you have to specify a list of keywords as
parameters that should be present, as separate words, in the
spoken phrase. For instance, you can set an event tthat would
fire if the phrase spoken by the character contains "menu" or
"dinner" or "lunch":
@event/add ... = say menu, dinner, lunch
Then if one of the words is present in what the character says,
this event will fire.
Variables you can use in this event:
speaker: the character speaking in this room.
character: the character connected to this event.
message: the text having been spoken by the character.
""", custom_call=phrase_event)
create_event_type(DefaultCharacter, "time", ["character"], """ create_event_type(DefaultCharacter, "time", ["character"], """
A repeated event to be called regularly. A repeated event to be called regularly.
This event is scheduled to repeat at different times, specified This event is scheduled to repeat at different times, specified
@ -631,6 +669,19 @@ create_event_type(DefaultRoom, "can_move", ["character", "room"], """
character: the character who wants to move in this room. character: the character who wants to move in this room.
room: the room connected to this event. room: the room connected to this event.
""") """)
create_event_type(DefaultRoom, "can_say", ["character", "room", "message"], """
Before a character can say something in this room.
This event is called before a character says something in this
room. The "something" in question can be modified, or the action
can be prevented by using 'deny()'. To change the content of what
the character says, simply change the variable 'message' to another
string of characters.
Variables you can use in this event:
character: the character who is using the say command.
room: the room connected to this event.
message: the text spoken by the character.
""", custom_call=phrase_event)
create_event_type(DefaultRoom, "delete", ["room"], """ create_event_type(DefaultRoom, "delete", ["room"], """
Before deleting the room. Before deleting the room.
This event is called just before deleting this room. It shouldn't This event is called just before deleting this room. It shouldn't
@ -665,6 +716,25 @@ create_event_type(DefaultRoom, "puppeted_in", ["character", "room"], """
character: the character who have just been puppeted in this room. character: the character who have just been puppeted in this room.
room: the room connected to this event. room: the room connected to this event.
""") """)
create_event_type(DefaultRoom, "say", ["character", "room", "message"], """
After the character has said something in the room.
This event is called right after a character has said something
in this room. The action cannot be prevented at this moment.
Instead, this event is ideal to create actions that will respond
to something being said aloud. To use this event, you have to
specify a list of keywords as parameters that should be present,
as separate words, in the spoken phrase. For instance, you can
set an event tthat would fire if the phrase spoken by the character
contains "menu" or "dinner" or "lunch":
@event/add ... = say menu, dinner, lunch
Then if one of the words is present in what the character says,
this event will fire.
Variables you can use in this event:
character: the character having spoken in this room.
room: the room connected to this event.
message: the text having been spoken by the character.
""", custom_call=phrase_event)
create_event_type(DefaultRoom, "time", ["room"], """ create_event_type(DefaultRoom, "time", ["room"], """
A repeated event to be called regularly. A repeated event to be called regularly.
This event is scheduled to repeat at different times, specified This event is scheduled to repeat at different times, specified