Complex event system for DungeonKeeper like game
- by paul424
I am working on opensource GPL3 game.
http://opendungeons.sourceforge.net/ , new coders would be welcome.
Now there's design question regarding Event System:
We want to improve the game logic, that is program a new event system. I will just repost what's settled up already on http://forum.freegamedev.net/viewtopic.php?f=45&t=3033.
From the discussion came the idea of the Publisher / Subscriber pattern + "domains":
My current idea is to use the subscirbers / publishers model.
Its similar to Observable pattern, but instead one subscribes to Events types, not Object's Events.
For each Event would like to have both static and dynamic type.
Static that is its's type would be resolved by belonging to the proper inherited class from Event.
That is from Event we would have EventTile, EventCreature, EvenMapLoader, EventGameMap etc.
From that there are of course subtypes like EventCreature would be EventKobold, EventKnight, EventTentacle etc.
The listeners would collect the event from publishers, and send them subcribers , each of them would be a global singleton.
The Listeners type hierachy would exactly mirror the type hierarchy of Events.
In each constructor of Event type, the created instance would notify the proper listeners. That is when calling EventKnight the proper ctor would notify the Listeners : EventListener, CreatureLisener and KnightListener.
The default action for an listner would be to notify all subscribers, but there would be some exceptions , like EventAttack would notify AttackListener which would dispatch event by the dynamic part ( that is the Creature pointer or hash).
Any comments ?
#include <vector>
class Subscriber;
class SubscriberAttack;
class Event{
private:
int foo;
int bar;
protected:
// static std::vector<Publisher*> publishersList;
static std::vector<Subscriber*> subscribersList;
static std::vector<Event*> eventQueue;
public:
Event(){
eventQueue.push_back(this);
}
static int subscribe(Subscriber* ss);
static int unsubscribe(Subscriber* ss);
//static int reg_publisher(Publisher* pp);
//static int unreg_publisher(Publisher* pp);
};
// class Publisher{
// };
class Subscriber{
public:
int (*newEvent) (Event* ee);
Subscriber( ){
Event::subscribe(this);
}
Subscriber( int (*fp) (Event* ee) ):newEvent(fp){
Subscriber();
}
~Subscriber(){
Event::unsubscribe(this);
}
};
class EventAttack: Event{
private:
int foo;
int bar;
protected:
// static std::vector<Publisher*> publishersList;
static std::vector<SubscriberAttack*> subscribersList;
static std::vector<EventAttack*> eventQueue;
public:
EventAttack(){
eventQueue.push_back(this);
}
static int subscribe(SubscriberAttack* ss);
static int unsubscribe(SubscriberAttack* ss);
//static int reg_publisher(Publisher* pp);
//static int unreg_publisher(Publisher* pp);
};
class AttackSubscriber :Subscriber{
public:
int (*newEvent) (EventAttack* ee);
AttackSubscriber( ){
EventAttack::subscribe(this);
}
AttackSubscriber( int (*fp) (EventAttack* ee) ):newEventAttack(fp){
AttackSubscriber();
}
~AttackSubscriber(){
EventAttack::unsubscribe(this);
}
};
From that point, others wanted the Subject-Observer pattern, that is one would subscribe to all event types produced by particular object. That way it came out to add the domain system :
Huh, to meet the ability to listen to particular game's object events, I though of introducing entity domains .
Domains are trees, which nodes are labeled by unique names for each level. ( like the www addresses ).
Each Entity wanting to participate in our event system ( that is be able to publish / produce events ) should at least now its domain name.
That would end up in Player1/Room/Treasury/#24 or Player1/Creature/Kobold/#3 producing events.
The subscriber picks some part of a tree. For example by specifiing subtree with the root in one of the nodes like Player1/Room/* ,would subscribe us to all Players1's room's event,
and Player1/Creature/Kobold/#3 would subscribe to Players' third kobold's event.
Does such event system make sense to you ?
I have many implementation details to ask as well, but first let's start some general discussion.
Note1: Notice that in the case of a fight between two creatues fight , the creature being attacked would have to throw an event, becuase it is HE/SHE/IT who have its domain address.
So that would be BeingAttackedEvent() etc.
I will edit that post if some other reflections on this would come out.
Note2: the existing class hierarchy might be used to get the domains addresses being build in constructor . In a ctor you would just add + ."className" to domain address.
If you are in a class'es hierarchy leaf constructor one might use nextID , hash or any other charactteristic, just to make the addresses distinguishable .
Note3:subscribing to all entity's Events would require knowledge of all possible events produced by this entity . This could be done in one function call, but information on E produced would have to be handled for every Entity.
SmartNote4 : Finding proper subscribers in a tree would be easy. One would start in particular Leaf for example Player1/Creature/Kobold/#3 and go up one parent a time , notifiying each Subscriber in a Node ie. : Player1/Creature/Kobold/* , Player1/Creature/* , Player1/* etc, , up to a root that is /* .<<<<
Note5: The Event system was needed to have some way of incorporating Angelscript code into application. So the Event dispatcher was to be a gate to A-script functions. But it came out to this one.