Reworked object command tables.

Object commands used to require re-adding every call in the script parent's __init__ or factory functions, adding the commands to a new command table directly on the object. Since all other attributes can be set up in at_object_creation(), this was both inconsistent and a bit confusing to work with. There is now a method add_commands() directly defined on all objects. It takes the same arguments as the normal add_command()o but use a reserved attribute to create and update a command table on the object. This has the advantange of completely removing the __init__ call in the script parent, all definitions can now be kept in at_object_creation() and are, more importantly, persistent without having to be recreated every call.
- I updated the examine command to show all the commands defined on an object (if any).
- I updated gamesrc/parents/examples/red_button.py considerably using the new command methodology and also using the updated Events.
.
Griatch
This commit is contained in:
Griatch 2009-12-03 00:41:53 +00:00
parent 5f6454ea1e
commit c7cbc4854e
7 changed files with 252 additions and 53 deletions

View file

@ -6,18 +6,21 @@ the red_button parent to see its effects (e.g. @create button=examples/red_butto
Technically the event don't contain any game logics, all it does is locate all
objects inheriting to a particular script parent and calls one of its functions
at a regular interval.
Note that this type of event will cause *all* red buttons to blink at the same
time, regardless when they were created. This is a very efficient way
to do it (it is also very useful for global events like weather patterns
and day-night cycles), but you can also add events directly to individual objecs
(see the example event in gamesrc/parents/examples/red_button)
"""
import sys
import traceback
from src.events import IntervalEvent
from src.scheduler import add_event
from src import scheduler
from src.objects.models import Object
#the logger is useful for debugging
from src.logger import log_errmsg
#Example of the event system. This example adds an event to the red_button parent
#in parents/examples. It makes the button blink temptingly at a regular interval.
from src import logger
class EventBlinkButton(IntervalEvent):
"""
@ -25,8 +28,9 @@ class EventBlinkButton(IntervalEvent):
"""
def __init__(self):
"""
A custom init method also storing the source object.
Note that we do NOT make this event persistent across
reboots since we are actually creating it (i.e. restarting it)
every time the module is reloaded.
"""
super(EventBlinkButton, self).__init__()
self.name = 'event_blink_red_button'
@ -34,13 +38,16 @@ class EventBlinkButton(IntervalEvent):
self.interval = 30
#the description is seen when you run @ps in-game.
self.description = "Blink red buttons regularly."
def event_function(self):
"""
This stub function is automatically fired every self.interval seconds.
In this case we do a search for all objects inheriting from the correct
parent and call a function on them.
Note that we must make sure to handle all tracebacks in this
function to avoid trouble.
"""
#find all objects inheriting from red_button (parents are per definition
#stored with the gamesrc/parent/ drawer as a base)
@ -51,11 +58,10 @@ class EventBlinkButton(IntervalEvent):
try:
b.scriptlink.blink()
except:
#show other tracebacks to log and owner of object.
#This is important, we must handle these exceptions gracefully!
b.get_owner().emit_to(sys.exc_info()[1])
log_errmsg(sys.exc_info()[1])
# Print all tracebacks to the log instead of letting them by.
# This is important, we must handle these exceptions gracefully!
logger.log_errmsg(traceback.print_exc())
#create and add the event to the global handler
blink_event = EventBlinkButton()
add_event(blink_event)
scheduler.add_event(blink_event)

View file

@ -8,41 +8,134 @@ Assuming this script remains in gamesrc/parents/examples, create an object
of this type using @create button:examples.red_button
This file also shows the use of the Event system to make the button
send a message to the players at regular intervals. Note that if you create a
test button you must drop it before you will see its messages!
send a message to the players at regular intervals. To show the use of
Events, we are tying two types of events to the red button, one which cause ALL
red buttons in the game to blink in sync (gamesrc/events/example.py) and one
event which cause the protective glass lid over the button to close
again some time after it was opened.
Note that if you create a test button you must drop it before you can
see its messages!
"""
import traceback
from game.gamesrc.parents.base.basicobject import BasicObject
from src.objects.models import Object
from src.events import IntervalEvent
from src import scheduler
from src import logger
# you have to import the event definition(s) from somewhere
# covered by @reload, and this is as good a place as any.
# Doing this will start the event ticking.
import game.gamesrc.events.example
#
# commands for using the button object. These are added to
# Events
#
# Importing this will start the blink event ticking, only one
# blink event is used for all red buttons.
import game.gamesrc.events.example
# We also create an object-specific event.
class EventCloselid(IntervalEvent):
"""
This event closes the glass lid over the button
some time after it was opened.
"""
def __init__(self, obj):
"""
Note how we take an object as an argument,
this will allow instances of this event to
operate on this object only.
"""
# we must call super to make sure things work!
super(EventCloselid, self).__init__()
# store the object reference
self.obj_dbref = obj.dbref()
# This is used in e.g. @ps to show what the event does
self.description = "Close lid on %s" % obj
# We make sure that this event survives a reboot
self.persistent = True
# How many seconds from event creation to closing
# the lid
self.interval = 20
# We only run the event one time before it deletes itself.
self.repeats = 1
def event_function(self):
"""
This function is called every self.interval seconds.
Note that we must make sure to handle all errors from
this call to avoid trouble.
"""
try:
# if the lid is open, close it. We have to find the object
# again since it might have changed.
obj = Object.objects.get_object_from_dbref(self.obj_dbref)
if obj.has_flag("LID_OPEN"):
obj.scriptlink.close_lid()
retval = "The glass cover over the button silently closes by itself."
obj.get_location().emit_to_contents(retval)
except:
# send the traceback to the log instead of letting it by.
# It is important that we handle exceptions gracefully here!
logger.log_errmsg(traceback.print_exc())
#
# Object commands
#
# Commands for using the button object. These are added to
# the object in the class_factory function at the
# bottom of this module.
#
def cmd_open_lid(command):
"""
Open the glass lid cover over the button.
"""
# In the case of object commands, you can use this to
# get the object the command is defined on.
obj = command.scripted_obj
if obj.has_flag("LID_OPEN"):
retval = "The lid is already open."
else:
retval = "You lift the lid, exposing the tempting button."
obj.scriptlink.open_lid()
command.source_object.emit_to(retval)
def cmd_close_lid(command):
"""
Close the lid again.
"""
obj = command.scripted_obj
if not obj.has_flag("LID_OPEN"):
retval = "The lid is already open."
else:
retval = "You secure the glass cover over the button."
obj.scriptlink.close_lid()
command.source_object.emit_to(retval)
def cmd_push_button(command):
"""
This is a simple command that handles a user pressing the
button by returning a message.
button by returning a message. The button can only be
"""
retval = "There is a loud bang: BOOOM!"
command.source_object.emit_to(retval)
def cmd_pull_button(command):
"""
An example of a second defined command (for those who
don't know how a button works ... ;) )
"""
retval = "A button is meant to be pushed, not pulled!"
obj = command.scripted_obj
if obj.has_flag("LID_OPEN"):
retval = "You press the button ..."
retval += "\n ..."
retval += "\n BOOOOOM!"
obj.scriptlink.close_lid()
else:
retval = "There is a glass lid covering "
retval += "the button as a safety measure. If you "
retval += "want to press the button you need to open "
retval += "the lid first."
command.source_object.emit_to(retval)
#
# Definition of the object itself
#
@ -53,7 +146,8 @@ class RedButton(BasicObject):
It will use the event definition in
game/gamesrc/events/example.py to blink
at regular intervals until the lightbulb
breaks.
breaks. It also use the EventCloselid event defined
above to close the lid
"""
def at_object_creation(self):
"""
@ -63,10 +157,31 @@ class RedButton(BasicObject):
#get stored object related to this class
obj = self.scripted_obj
obj.set_attribute('desc', "This is your standard big red button.")
obj.set_attribute("breakpoint", 10)
obj.set_attribute('desc', "This is a big red button. It has a glass cover.")
obj.set_attribute("breakpoint", 5)
obj.set_attribute("count", 0)
# add the object-based commands to the button
obj.add_command("open lid", cmd_open_lid)
obj.add_command("lift lid", cmd_open_lid)
obj.add_command("close lid", cmd_close_lid)
obj.add_command("push button", cmd_push_button)
obj.add_command("push the button", cmd_push_button)
def open_lid(self):
"""
Open the glass lid and start the timer so it will
soon close again.
"""
self.scripted_obj.set_flag("LID_OPEN")
scheduler.add_event(EventCloselid(self.scripted_obj))
def close_lid(self):
"""
Close the glass lid
"""
self.scripted_obj.unset_flag("LID_OPEN")
def blink(self):
"""
If the event system is active, it will regularly call this
@ -102,8 +217,4 @@ def class_factory(source_obj):
This is a good place for adding new commands to the button since this is
where it is actually instantiated.
"""
button = RedButton(source_obj)
# add the object-based commands to the button
button.command_table.add_command("push button", cmd_push_button)
button.command_table.add_command("pull button", cmd_pull_button)
return button
return RedButton(source_obj)