/*
************************************************************************
*  File: events.doc *
* *
*  Usage: An explanation of how to use mud events *
*  Written by Joseph Arnusch (Vatiken) (Joseph.Arnusch@gmail.com) *
*
*  Usage: An explanation of how to use events *
*  Written by Eric Green (ejg3@cornell.edu) *
************************************************************************
*/

Vatiken's MUD event system
--------------------------

Table of Contents
-----------------
1.  Purpose
2.  Functions Related to MUD Events
3.  Steps to Create a New MUD Event
4.  Differences between the two systems

1. PURPOSE

I scribed a "MUD" event system using the "Death Gate Event" system already
in place to allow for increased ease, and maintainability for both rookie
and experienced programmers.

2. FUNCTIONS RELATED TO MUD EVENTS

 a) See EVENTFUNC() in the Death Gate Events documentation below
 b) void init_events(void)
    "init_events()" creates the global events list and is the allocated location
    for placing any global events, these may include things like AI, Weather,
    and Combat.
 c) struct mud_event_data * char_has_mud_event(struct char_data * ch, event_id iId)
    "char_has_mud_event()" returns an event in the characters event list that matches
    the supplied "event_id", or NULL if none exists.
 d) NEW_EVENT(event_id, struct, var, time)
    "NEW_EVENT" creates a new event of the "event_id" type, with the supplied structure
    (ch, desc, object, etc..), any addtional "var"s, and is set to activate in
    this amount of "time".
 e) struct mud_event_list[]
    The mud_event_list[] is an array of all the events you've designed into your MUD.
    The reason for this excessive step is primarily for organization and troubleshooting,
    and it takes a mere couple seconds to add to mud_events.c.

3. STEPS TO CREATE A NEW MUD EVENT

 a) Add the new event_id to enum list in mud_events.h

  typedef enum {
    eNULL,
    ePROTOCOLS, /* The Protocol Detection Event */
    eWHIRLWIND, /* The Whirlwind Attack */

    eNEWEVENT   /* A NEW EVENT */
  } event_id; 

 b) Create the event

  EVENTFUNC(new_event)
  {
    struct char_data *ch, *tch;
    struct mud_event_data *pMudEvent;
    struct list_data *room_list;
    int count;
	
    /* This is just a dummy check, but we'll do it anyway */
    if (event_obj == NULL)
      return 0;
  
    ...	  

    return 0;
  }

 c) Add the event_id data to mud_event_list[]

  struct mud_event_list mud_event_index[] = {
    { "Null"         , NULL           , -1          },  /* eNULL */
    { "Protocol"     , get_protocols  , EVENT_DESC  },  /* ePROTOCOLS */
    { "Whirlwind"    , event_whirlwind, EVENT_CHAR  }   /* eWHIRLWIND */

    { "A New Event"  , new_event      , EVENT_CHAR  }   /* eNEWEVENT */ 
  };

 d) Place a call for the new event

  if (variableX > variableY)
    NEW_EVENT(eNEWEVENT, ch, NULL, 60 * PASSES_PER_SEC);

 e) Sit back and enjoy your event triggering in 60 seconds.

4. DIFFERENCES BETWEEN THE TWO SYSTEMS

The biggest differences between the two systems is that the MUD Event System 
employs certain functions to make it more "dummy proof" without limiting
functionality.

For example:
 a) all memory allocated for a MUD event will be freed when the event
    is no longer in use.
 b) the mud_event_index[] can/will log when events are called, allowing for an
    easy way to debug any problems.
 c) the mud_event structure allows for easy handling of memory that shouldn't
    be freed with the event like the character him/herself.
 d) when a character leaves the game, all mud_events will be cleared without
    manually having to place checks in free_char().

The "MUD Event" system should be adequate for 99% of all events that a developer
may be interested in creating, and still allows access the dg_event event system
for anything requiring more complicated procedures.

========================================================================

  		  Death Gate Events


Table of Contents
-----------------
1.  Event Functions and Event Objects
2.  Functions for Manipulating Events
3.  Steps to Create a New Event Type
4.  An Example Event Type
5.  Tips for Create Events
  
-----------------------------------------------------------------------------
1.  Event Functions and Event Objects


Each event type needs an:

  Event function:

    An event function is a function with a prototype of:

        long (event_function)(void *event_obj)

    This function is called when the event occurs, and is passed the event
    object (see below).  If the function returns a positive value, the event
    is reenqueued using the return value as the number of pulses in the
    future the event will reoccur.  If the function returns 0 or a negative
    number, the event is not reenqueued.  If the event is not to be
    reenqueued, the event function is responsible for freeing the event
    object.  There is a define:

        #define EVENTFUNC(name) long (name)(void *event_obj)

    to be used when declaring an event function.

  Event object:

    The event object is any structure with the fields to store any data
    needed by the event function.  The event object should not be a game
    object (such as a struct char_data), since this object is freed when
    events are canceled (see cancel_event() below).  All unique data
    contained by the object should be freed by a call of free(event_obj).
    In other words, don't have event_obj include pointers to other structures
    which aren't pointed to elsewhere.  It is also not advisable to have
    pointers in the event object unless the thing they point to has a pointer
    to this event and cancels the event when it is freed.  Passing NULL as an
    event object is valid (providing the event function doesn't need any
    data).

----------------------------------------------------------------------------
2.  Functions for Manipulating Events


The prototypes for the interface functions for events are provided in
events.h.  They are:

void event_init(void);
    This function initializes the event queue for all events.  It is only
    called once, at the initialization of the game.

struct event *event_create(EVENTFUNC(*func), void *event_obj, long when);
    This function creates a new event.  At the current time plus 'when',
    the function call 

    func(event_obj);

    will be made.  A pointer to the created event is returned.  Never free()
  the event returned.  Use event_cancel instead if you want to get rid of it
  prematurely.

void event_cancel(struct event *event);
    This function cancels an event currently in the queue.  The event and the
    event_obj are freed by this call.

void event_process(void);
    This function is called once each pulse to process any pending events.
    It should not be used outside of the main loop.

long event_time(struct event *event);
    Given event, this function returns the number of pulses until the event
    occurs.  One example of a place it is used is to get the pulses left before
    an object timer expires, so its current state can be saved and restored
    later.

-----------------------------------------------------------------------------
3.  Steps to Create a New Event Type

To add a new event type, you do not need to know anything about what's in
events.c, queue.c, or queue.h, the core of the event code.  To create an
event type:

  1.  Declare an event object structure.

  2.  Create your event function.

  3.  Construct your event object, and call event_create() where needed.

  4.  Any place that the 'owner' of the event can be destroyed, call
      event_cancel().

------------------------------------------------------------------------------
4.  An Example Event Type


Example event type:

  /* the event object for the sniff event */
  struct sniff_event_obj {
      struct char_data *ch;
      byte type;
  };


  EVENTFUNC(sniff_event)
  {
    struct sniff_event_obj *seo = (struct sniff_event_obj *) event_obj;
    struct char_data *ch, *victim;

    ch = seo->ch;
    
    GET_CHAR_SNIFF(ch) = NULL;

    if (type == SNIFF_COLD)
      act("$n sniffs loudly.", FALSE, ch, NULL, NULL, TO_ROOM);
    else
      act("$n sniffs some cocaine.", FALSE, ch, NULL, NULL, TO_ROOM);

    act("You sniff.", FALSE, ch, NULL, NULL, TO_CHAR);

    if (--seo->severity <= 0) {
      /* we're done with sniffing */
        free(event_obj);
    }
    else
      return PULSE_SNIFF;
  }


  ACMD(do_sniff)
  {
    struct sniff_event_obj *sniff;

    CREATE(sniff, struct sniff_event_obj, 1);
    sniff->ch = ch;
    sniff->severity = 5;
    if (GET_CLASS(ch) != CLASS_THIEF)
      sniff->type = SNIFF_COLD;
    else
      sniff->type = SNIFF_COCAINE;

    GET_CHAR_SNIFF(ch) = event_create(sniff_event, sniff, PULSE_SNIFF);

    send_to_char(OK, ch);
  }


  void extract_char(struct char_data *ch)
  {

    ...

    if (GET_CHAR_SNIFF(ch)) {
      event_cancel(GET_CHAR_SNIFF(ch));
      GET_CHAR_SNIFF(ch) = NULL;
    }

    ...
  }

-----------------------------------------------------------------------------
5.  Tips for Create Events


Tips for creating events:

 o event_obj should always be freed (or reused) in the EVENTFUNC()

 o Any game object pointed to by event_obj should have a pointer to the
   the event so it can cancel the event if it is extracted.

 o Any game object with pointers to an event should have the event pointer
   set to NULL in EVENTFUNC and immediately following event_cancel().

 o Any place a game object is extracted from the game, any events it points to
   should be canceled and its pointer to the events set to NULL.
