A working whitepaper to discuss the soon-to-be-essential event framework, how
it facilitates rich interactions (the "Captain X" mechanism, etc.), and how it
supports the combat and magic systems.
Before beginning with conceptual specifics, some discussion about entity flags
and their relevance to future design elements may be helpful. The original
intent of flags has been to aid fully or partially designer-exposed modules
(tasks, handlers, etc.) in preventing or performing actions on a selective
subset of objects. A good example is the locomotion task, which can key off a
flag to decide which denizens should wander randomly between rooms.
In view of this, it does not seem desirable to expect a certain set of flags
within the core engine. I propose a design guideline that any flag required by
a core engine process should be converted to a specific XML tag.
The event framework, perhaps more than any other pluggable layer, will provide
an excellent opportunity to leverage flags. Entities are more than capable of
introspecting on their flags to decide if an event is relevant to them
specifically. Of course, we also would aim to bind events only to objects of
relevance for efficiency reasons.
It is interesting to note that modules are capable of modifying an entity's own
flags dynamically to record state in a complex action.
Modules are tasked with maintaining and interpreting their relevant flags, but
a libsigma support function should support event-created flags that record
their originator using the delimited concept also employed in area files. This
support should also provide for a key/value arrangement, separated by an equals
sign, to support basic key-value behavior. This means tags could be in various
forms: 'aggressive' (global), 'decay=5' (global, key/value),
'shopkeeper/closed' (module-specific), or 'autospell/cast=boost'
(module-specific, key/value).
Now to the proposal for an events implementation:
From an object standpoint, I propose a "callable chain" class for each event.
These chains are instanced at the lowest level of universal application, with
most events being bound at the Character level, and a narrow set of specialized
events attached to Items and Rooms.
The callable chain is a derivative of the built-in list class that implements
the __call__(...) member function:
class Event(list):
def __init__(self, e=[]):
super(Event, self).__init__()
self.extend(e)
def __call__(self, context=None):
for f in self:
if f(context):
return True
return False
Observations on this:
- The Event is itself a list and therefore provides all the convenient capabilities of a list: slices, appends, prepends, deletes, searches, etc. Virtually no custom support is necessary.
- When the Event is called as a function, the intrinsic list iterates over itself, calling each attached function with a single argument, similar to the context provided to a handler function.
- A function result that returns a True value immediately terminates execution of the rest of the chain. While not to be used commonly, it could be at times useful to prepend a certain event function that short-circuits execution before a built-in or earlier-defined method is invoked. Most functions should simply execute and terminate without returning a value, or returning False.
Similarly to the handler framework, events will only require a decorator in
their declaration which adds them to a mapping object. No further constraint
on organization or implementation should be necessary, aside from the fact that
all events should expect a single context argument.
The XML for this should be quite simple:
<event type="item_offered" function="thank" />
or perhaps more actively-phrased:
<event on="item_offered" call="thank" />
Complex event insertions can be handled by a task module (often only utilizing
the 'init' phase of the module). This should rarely be necessary. To head off
perhaps the greatest risk of a "complex" insertion, we can provide 'prepend' to
manage insertion order:
<event on="item_offered" call="thank" prepend="true" />
One final facility should be a provision for flags within the event
declaration, to wit:
<event on="item_offered" call="bless">
<flag>vocal</flag>
<flag>emote=bow</flag>
</event>
Utilizing the special libsigma support, these flags are automatically converted
to bless/vocal and bless/emote=bow.
This facility provides a means of communicating configuration to the module
without modifying the module code, and at run-time can additionally aid in
maintaining state.
Technically speaking, this XML definition framework with flag capability
eliminates the need for personas, and they will be removed from the codebase
for now. If present eventually, their application under this system would be
to automate the application of a number of events, flags, and other
modification to an entity. This would allow construction of a complex
Character profile ("Captain X") that would carry a payload of operations (item
accepting, shopkeeping, periodic speaking, etc.). The counter-argument would
be a relatively low net decrease in complexity over simply setting up the
entity by hand.
A final note on actual implementation: utilizing a simple set of "system"
events bound mostly to Denizens will vastly simplify operations that otherwise
would be carried in libsigma or handler implementations as special cases for
Denizens.
For example, an 'accept' event added by default to all Denizens would allow the
offer framework to apply universally while making Denizens' acceptance
automatic. 'accept' could either respect an entity flag requesting no
auto-accept of items, or alternatively a special 'stop' event (system-default
or custom) could be prepended to the default chain as a no-op save returning
True and terminating the chain.
Further default events could be useful in combat, and a rich event framework
could significantly simplify a shopkeeper implementation, for example.
Original issue reported on code.google.com by bmcca...@gmail.com on 3 Sep 2010 at 2:15
Original issue reported on code.google.com by
bmcca...@gmail.com
on 3 Sep 2010 at 2:15