Notification Engine

From Hero of Allacrost Wiki
Jump to: navigation, search

Introduction[edit]

When some sort of condition or state is detected in running code, sometimes we want to let other parts of the code know what transpired so they can act accordingly. Take as an example a sprite that has a collision with some object or unwalkable area of a map. Depending on what the collision was with and where it was on the map, we may want to do things such as moving to a new map, starting a dialogue, or playing a sound. But the collision detection and resolution logic occurs in the core internals of MapMode )C++), while determining what action to take happens in the map script (Lua). How can the map script know that a collision happened in a completely different layer of the code?

The answer is with the notification engine. Any area of the code (except for utilities) is able to publish a message that says "this event happened, do with it what you will". Another area of the code may be continually checking for that type of event. The NotificationEngine class maintains a list of all notifications that were generated on each game update loop and deletes the list right before the next loop begins. Notifications can include not only a description of the event that happened, but various pieces of data describing the state and circumstances that generated the event.

Notifications are typically generated during the Update() call to the active game mode, either directly through the game mode or some component of the engine or other code that is called from the game mode. The notifications must be processed by the listening code before the Update() call ends, as all notifications created are destroyed immediately after the Update() call ends.

NotificationEvent Class[edit]

The NotificationEvent class is the lowest level type of notification that can be made. It contains two member strings which are public for convenience, but should not be modified after the class object is instantiated.

  • category identifies the area of the code that generated the notification. This is typically the outer namespace of the code without the "hoa_" prefix. So notifications in the "hoa_battle" or "private_battle" namespaces would have a category name of "battle".
  • event describes the type of event that generated the notification. A sprite collision in map mode might choose the event name "collision".

Custom Notifications[edit]

For many types of notifications, NotificationEvent provides enough information for other code to take action. Other types of notifications may need to provide more information about the particular state of something that occurred, such as X/Y coordinates on a map. In this case, a subclass of NotificationEvent should be defined and include the extra data as members of the class. The NotificationEngine doesn't care what type of notification object you give it so long as it is derived from NotificationEvent.

Below is an example of a custom notification that was defined for map collisions.

/** ****************************************************************************
*** \brief A notification event class describing sprite collisions
***
*** Whenever a sprite of any type moves on the map and has a collision, one of these
*** notification events is generated to describe the type and particulars about the
*** collision. This can be used by a map script to determine whether to play a sound,
*** switch the context of the sprite, or take some other action.
***
*** \note Because collision resolution changes the position of the sprite, you can
*** not rely on the position of the sprite when the notification event is being processed.
*** This is why this class has members that retain the position of the sprite as the collision
*** happened.
*** ***************************************************************************/
class MapCollisionNotificationEvent : public hoa_notification::NotificationEvent {
public:
	/** \param type The type of collision that occurred
	*** \param sprite The sprite that had the collision
	*** \note You should \b not use this constructor for object-type collisions
	**/
	MapCollisionNotificationEvent(COLLISION_TYPE type, VirtualSprite* sprite) :
		NotificationEvent("map", "collision"), collision_type(type), sprite(sprite), object(NULL) { _CopySpritePosition(); }

	/** \param type The type of collision that occurred (should be COLLISION_OBJECT)
	*** \param sprite The sprite that had the collision
	*** \param object The object that the sprite collided with
	*** \note You should \b only use this constructor for object-type collisions
	**/
	MapCollisionNotificationEvent(COLLISION_TYPE type, VirtualSprite* sprite, MapObject* object) :
		NotificationEvent("map", "collision"), collision_type(type), sprite(sprite), object(object) { _CopySpritePosition(); }

	//! \brief Returns a string representation of the collision data stored in this object
	const std::string DEBUG_PrintInfo();

	//! \brief The type of collision that caused the notification to be generated
	COLLISION_TYPE collision_type;

	//! \brief The sprite that had the collision
	VirtualSprite* sprite;

	//! \brief Saved position data from the sprite at the time of the collision
	uint16 x_position, y_position;
	float x_offset, y_offset;

	//! \brief The object that the sprite collided with, if it was an object type collision. Otherwise will be NULL
	MapObject* object;

private:
	//! \brief Retains the state of the sprite's position data in the class members
	void _CopySpritePosition();
}; // class MapCollisionNotificationEvent : public hoa_notification::NotificationEvent

NotificationEngine Class[edit]

NotificationEngine is a singleton class with a simple job of maintaining a list of all notifications that have been generated. There are two types of interactions that other code make with the notification engine.

  • Store created notifications in a list
  • Iterate through all stored notifications and retrieve their data

There are two functions that allow you to store a created notification. The first takes a pointer to the created NotificationEvent class (or subclass, if defining a custom notification). The notification engine assumes responsibility for making sure the objects gets deleted, so you should never attempt to delete a notification that you passed to the engine, otherwise you can expect a segmentation fault to occur. The second function is a short-cut that creates the object internally in the engine.

	/** \brief Sends a trigger object that has already been created
	*** \param notification A pointer to the NotificationEvent (or subclass) object that was created
	**/
	void Notify(NotificationEvent* notification);

	/** \brief Creates a new NotificationEvent object and immediately adds it to the trigger list
	*** \param category The category identifier of the code generating this trigger
	*** \param event The identifier string for the type of trigger that occurred
	**/
	void CreateAndNotify(const std::string& category, const std::string& event);

Iterating through the stored notifications can either be done by returning a reference to the list, or retrieving each notification individually. It is not possible to retrieve the notification list in Lua code.

	//! \brief Returns the number of notification events currently stored by the class
	uint32 GetNotificationCount() const
		{ return _notification_events.size(); }

	/** \brief Retrieves a pointer to the NotificationEvent object stored at a particular index in the trigger list
	*** \param index The list index to retrieve the object from
	*** \return A pointer to the object, or NULL if there is no object stored at this index
	**/
	NotificationEvent* GetNotificationEvent(uint32 index) const;

	//! \brief Returns a reference to the list of notification events stored by the class
	std::vector<NotificationEvent*>& GetAllNotificationEvents()
		{ return _notification_events; }


Example Uses[edit]

Below are several examples of how notifications can be used throughout disparate areas of the code. Not all of these examples are actively used by the code. This is just a list of possible ideas to get you thinking about how you can make use of the notification engine. Feel free to add your own ideas to the examples table below.

Example # Source Origin Reason for Notification
Source Destination Response Action Taken
A Audio Engine Finished playing a sound or piece of music
Battle Mode Instruct the video engine to draw a special effect in the background
B Global Management Announce when the amount of drunes (currency) held by the party changes
Shop Mode Animate the drunes amount changing
C Map Mode Declare that a collision occurred
Map Mode (script) Examine what type of collision and where it occurred, then enact any appropriate action, such as moving the sprite into a building if they collided with a door

As one final example, the notification engine could be used as the means to implement an achievement system in Allacrost. You could define your own achievement conditions, such as "Reached XP Level 10", "10,000 Drunes Held", or "Played for 5 hours". Then you could add notifications to the relevant areas of the code that can cause those conditions to be triggered. Another part of the code will have to continually run on every game update loop and examine these notifications to determine if they cause any achievements to be earned.