Docs
Ok, this may look as a bad joke ;-). Though, I'll try to place here some usefull informations.
Basic concepts
Rooms, livings and items
Our environment consits of three basic objects. Every object has its description and some other properties accessible throug the mudObject class' methods.
Rooms contains everything what can be seen on the MUD. They are connected with exits, so user can go through them. Most of exits are visible in room's description, but there may be hidden exits or even special exits, which aren't shown at all or uses special command for passing them. Every room should have title (i.e. very short description). There can be two types of objects in rooms: characters (livings - NPCs or entites - players) and items. Rooms are represented by mudRoom class.
Items are objects user can manipulate with - doors (open/close, lock/unlock), food and drink (eat, drink), weapons and armours (wear/remove, wield/unwiled) etc. Items can by owned by room or by other items.
Characters are objects controlled by computer (NPCs) or by user. They're always in some room (accessible via mudObject::getParentRoom() and can possess items. Users can interact with them using ask command (ask <who> [to|for|about] <something>)
If everything goes well, complete room description can look like this one:
Small square
Nice fountain is located in the center. Ground is paved with grey paving stones. You see a shop in the northeastern corner and very magnificent house on the west. Through the narrow street on the north you can be seen trees. To the south direction leads wide street and other one leads to the east.
Exits: [east, house[#], north, shop]
Items: [bread, milk]
citizen is standing here.
Extending existing objects
However it is possible to extend objects (i.e. room, items, characters) using standard C++ mechanism, it isn't very pretty (save/load problems). Because of this reason, we're using events and actions. Events reacts on some thing that happend. They are collected in actions.
When new behaviour is needed, programmer inherits some event class, add it to some action and assigns that action to some object. This information is stored as string-string map, so it is very easy to save such data. Actions are managed by action manager.
Events can be divided by many aspects, so there are listed by class:
- mudLivingEvent - catches events invoked in room; obviously added to the mudAction; this event can be catched by anybody in room
- mudRoomEvent - like the above one, but it is catched by room and added to the mudRoomAction
- mudGlobalEvent - invoked globally by mudServer::globalInvoke(); registered by mudServer::addGlobalEvent() and unregistered by mudServer::removeGlobalEvent()
- mudHook - directed to one character in the room; obviously added to the mudAction
- mudItemHook - directed to one item in the room; obviously added to the mudItemAction
- mudAlgorithm - invoked before the command is executed in room where the player is standing; it can disable executing of command; obviously added to the mudAction
Some examples
Let's say that we want npc, which will greet player, which enters the room, and says something some period of tie. This can be done by implementing two events - mudLivingEvent for greeting and mudGlobalEvent for saying. But the object for second event must contain information about relevant living. This can be inefficient and undesirable. Luckily, mudServer provides mechanism for translating global events to local events (mudLivingEvent).
#include <mud_events.h> #include <mud_server.h> // just implement new class based on mudLivingEvent class greetEvent : public mudLivingEvent { public: // standard constructor and destructor greetEvent() : mudLivingEvent() {}; virtual ~greetEvent() {}; // this method is called when the event is invoked virtual void invoke(mudLiving *receiver, mudLiving *from, mudItem *item = 0, const mudString ¶ms = mudString::null); } void greetEvent::invoke(mudLiving *receiver, mudLiving *from, mudItem *, mudString &) { // this will add command to the server command queue // the source of the command is receiver (first argument) and the command // itself is passed as the second server->addCommandToExec(receiver, "say to " + from->getName() + "welcome here, stranger")); } // time-based event is invoked globally, implement new one class sayEvent : public mudLivingEvent { public: // standard constructor and destructor sayEvent() : mudGlobalEvent() {}; virtual ~sayEvent() {}; // like the above one virtual void invoke(mudLiving *receiver, mudLiving *from, mudItem *item = 0, const mudString ¶ms = mudString::null); } void sayEvent::invoke(mudLiving *receiver, mudLiving *from, mudItem *, mudString &) { server->addCommandToExec(receiver, "say i should say something right now"); }
These classes should be initialized somewhere. Obvious place for doing such thing should be module's constructor, where the action is created and events are added.
// include necessary header file #include <mud_action.h> // ... // somewhere in module initialization cycle (module's constructor) // create events greet = new greetEvent(); say = new sayEvent(); action = new mudAction("custom"); // for greeting action->addEvent("new living added", greet); // for saying action->addEvent("rt-tick", say); // don't forget to delete created objects in destructor
Last thing that should be done is creation of living. Again, this place can be module's constructor. Living can be also completely defined by XML file, which describes such living.
// include necessary header file #include <mud_living.h> #include <mud_server.h> // ... living = new mudLiving("Testing living"); living->assignAction("custom"); // rt-tick can be replaced by rt-minute, rt-hour or rt-day server->receiveGlobals("rt-tick", living); // don't forget to delete objects :-)