pdirt/data/
pdirt/data/HELP/
pdirt/data/HELP/0/
pdirt/data/HELP/F/
pdirt/data/HELP/G/
pdirt/data/HELP/H/
pdirt/data/HELP/J/
pdirt/data/HELP/K/
pdirt/data/HELP/O/
pdirt/data/HELP/Q/
pdirt/data/HELP/R/
pdirt/data/HELP/U/
pdirt/data/HELP/V/
pdirt/data/HELP/Y/
pdirt/data/HELP/Z/
pdirt/data/MESSAGES/
pdirt/data/POWERINFO/
pdirt/data/WIZ_ZONES/
pdirt/drv/
pdirt/drv/bin/
pdirt/drv/compiler/converter/
pdirt/drv/compiler/libs/
pdirt/drv/compiler/scripts/
pdirt/drv/include/AberChat/
pdirt/drv/include/InterMud/
pdirt/drv/include/machine/
pdirt/drv/src/InterMud/
pdirt/drv/src/Players/
pdirt/drv/utils/UAFPort/
pdirt/drv/utils/dnsresolv/
pdirt/drv/utils/gdbm/
#define SPECIAL_C
/****************************************************************************
 ** Some more functionality for the world.
 ** This allows things to happen on special events, rather then hardcode them
 ** into the core engine.
 ** 
 ** the functions should have the following type:
 ** void functionname (int event);
 ** 
 ** You can have as many events as you want to a mobile/room/object. See
 ** special.h to see what events we have
 **
 ** Project    : pDirt (Aber IV MUD daemon)
 ** Module     : special.c
 ** Author     : Peter Eussen (Marty)
 ** Description: Special events Initialisation routines.
 ** Version    : 1.0
 ** Date       : 1 Juli 1996
 **
 ** Additions  : 
 ** [Marty/19 Juli]
 **     Moved switch statements to 3 arrays (one for objects, one for rooms,
 **     and one for mobiles). Now it just clears all functions, and then 
 **     works the table to set all the right functions.
 ** [Marty/7-9 Sept]
 **     Added special events for players. See Players directory, for tables,
 **     and code.
 ***************************************************************************/
 
/** DOCUMENTATION

 This file includes almost all special events code of the world. This is done
 so the actual driver and the world code are seperated from eachother. This 
 has the following advantages.
 - The driver code remains seperate, and is not affected by additions to the
   world.
 - Regenerating the world will not result in a remake of the whole driver.
 - The events work in a standardised way, making it easier to add things.
 - Less checks are needed on an average command.
 
 HOW DOES IT WORK
 
 In the old dyrt code, from which this code originates, all special events
 where coded into the driver. I moved those to seperate files and made a 
 small routine which checks if something needs to be done. 
 
 Because of this routine, only the objects/rooms that are involved with that
 command are checked, which results in more direct checks: Instead of checking
 all objects which have a special event, it now only check the object that is
 being handled.
 
 Lets take a look at a simple example. Lets say you want to 'get pick'. The 
 driver will jump to the getcom, which will then check if the object (pick)
 has some special event. If it does, it will call the function which handles
 the events for that object with a unique number (E_ONGET here).
 
 HOW TO MAKE AN EVENT FOR AN OBJECT/ROOM/MOBILE
 
 All event handlers have the same prototype. It is defined as this:
      (void)spec_case(int); 
 So a special event handler for an object will look something like this:
       void obj_fooland_foo(int event);
       {  switch (event) {
          E_ONINIT: setoloc(param_s.ob,LOC_FOO_INTOWN,IN_ROOM); break;
          E_ONTIMER: sendf(param_s.plx,"Hello"); break;
          E_ONGET: destroy(param_s.ob); break;
          E_ONMISC:
            if (param_s.misc == VERB_WAVE)
            {  bprintf("Foo waves as well\n"); 
            }
            break;
          default: break;
          }
       }
 
 The integer parameter is needed to specify which event is being called.
 Besides the int parameter I use an global struct which holds some data
 on the call. The struct is called param_s and has the following fields.
    plx     : Current player (same as mynum most of the time, but NOT ALWAYS)
    ob      : object that is being handled/manipulated
    pl      : Second char in the event (example: give .. to fooman, pl will
              point to fooman)
    misc    : Misc values. Used by E_ONMISC, to specify which special event
              of E_ONMISC is called.
    ret     : return value, if 1 then the function that called the event will
              continue (default), otherwise it will fail and return.

 If we look at the example you will see what a typical event handler looks 
 like. On initialisation it will set the location of the object to somewhere
 in fooland. When someone gets the object it will be destroyed. Once every
 2 seconds it will print an hello message and when someone waves to the foo,
 it will wave back.
 
 To see what events are know look at include/special.h.
 
 Once you have made such an event, it must be included in this file and then
 the event handlers must be assigned to the right object on startup. To add the
 event handler to a function, edit the header file (example: 
 objects/objtable.h). Add your ID in the table with the name of the function.

 Work in progress: 
 - Moving remaining code out of the driver and into special events
 - Add special events for a player.

 Done work:
 - moved include list for rooms and objects to a seperate headerfile.
 
 END OF DOCUMENTATION **/
/****************************************************************************
 ** SOURCE CODE
 ****************************************************************************/
/* Include all prototypes of functions which can be used in the special event
 * handlers.
 */
#include "kernel.h"
#include <stdlib.h>
#include <string.h>
#ifdef RS6000
#include <strings.h>
#endif
#include "special.h"
#include "mobile.h"
#include "objsys.h"
#include "mobiles.h"
#include "locations.h"
#include "log.h"
#include "objects.h"
#include "parse.h"
#include "cflags.h"
#include "oflags.h"
#include "sflags.h"
#include "lflags.h"
#include "mflags.h"
#include "pflags.h"
#include "sendsys.h"
#include "bprintf.h"
#include "rooms.h"
#include "mud.h"
#include "utils.h"
#include "communicate.h"
#include "commands.h"
#include "clone.h"
#include "verbs.h"
#include "quests.h"
#include "quest.h"
#include "questnames.h"
#include "questpoints.h"
#include "magic.h"
#include "atsys.h"
#include "uaf.h"
#include "board.h"
#include "fight.h"
#include "tables.h"
#include "zones.h"
#include "zonetab.h"
/*****************************************************************************
 ** Now include special event code files
 ** Put your special zone code files in here as well and also add them to the
 ** correct setting list below. Now this is what i call including some files!
 *****************************************************************************/
#include "prototypes.h"
#include "authors.h"
/*********************************
 ** Include the code
 *********************************/
#include "Objects/includeobjects.h"
#include "NPC/includemobs.h"
#include "Rooms/includerooms.h"
#include "Players/Players.h"
/*********************************
 ** Include the tables 
 *********************************/
#include "NPC/mobtable.h"
#include "Rooms/loctable.h"
#include "Objects/objtable.h"
#include "Players/PlayerTable.h"

#include "Players/Pets.c"
#include "Players/Players.c"

/*********************************
 ** Prototype functions
 *********************************/
PRIVATE int set_mob_funcs(void);
PRIVATE int set_obj_funcs(void);
PRIVATE int set_rom_funcs(void);

/**************************************************************************
 ** FUNCTION   : clearfunctions
 ** DESCRIPTION: Clear all pointers to functions related to an object, mobile
 **              or room in the world.
 **************************************************************************/
PUBLIC void clearfunctions(void)
{  int n;
   for (n=max_players; n < numchars; n++)
      clear_mob_fun(n);
   for (n=0; n < numobs; n++)
      clear_obj_fun(n);
   for (n=0; n < numloc; n++)
      clear_rom_fun(n);
}

PUBLIC void init_zone(int zone)
{   int i;
 
    for (i=zfirst_mob(zone); i != SET_END; i = znext_mob(zone))
    {   if (mob_fun(i) != NULL)
        {   param_s.pl = i;
            mob_fun(i)(E_ONINIT);
        }
    }
    for (i=zfirst_obj(zone); i != SET_END; i = znext_obj(zone))
    {   if (obj_fun(i) != NULL)
        {   param_s.ob = i;
            obj_fun(i)(E_ONINIT);
        }
    }
    for (i=zfirst_loc(zone); i != SET_END; i = znext_loc(zone))
    {   if (rom_fun(i) != NULL)
        {   param_s.loc = i;
            rom_fun(i)(E_ONINIT);
        }
    }
}
/*****************************************************************************
 ** FUNCTION   : init_all
 ** DESCRIPTION: Call every special event function with E_ONINIT, to initialise
 **              the code.
 *****************************************************************************/
PUBLIC void init_all(void)
{  int n;

   for (n = 0; n < numloc; n++)
      if (rom_fun(convroom(n)) != NULL)
      { param_s.loc = convroom(n);
        rom_fun(convroom(n))(E_ONINIT);
      }
   for (n = 0; n < numobs; n++)
     if (obj_fun(n) != NULL)
     {  param_s.ob = n;
        obj_fun(n)(E_ONINIT);
     }
   for (n = max_players; n < numchars; n++)
      if (mob_fun(n) != NULL)
      {   param_s.pl = n;
          mob_fun(n)(E_ONINIT);
      }
}

/*****************************************************************************
 ** Set all functions using the routines set_mob_funcs, set_obj_funcs and 
 ** set_rom_funcs.
 *****************************************************************************/
PUBLIC int setfunctions(void)
{  clearfunctions();

   if (set_mob_funcs() && set_obj_funcs() && set_rom_funcs())
     return 1;
   else 
    return 0;
}

PRIVATE int set_rom_funcs(void)
{  int count;

   /* Set all functions for the entries in the location table */
   for (count = 0; count < MAPSIZE(loc_table); count ++)
   { if (loc_table[count].id >= numloc)
     {  mudlog("INIT: Illegal Indexing in Location Table (mob_table[%d] = %d)",count,loc_table[count].id);
     }
     else if (loc_table[count].id != -1)
        set_rom_fun(convroom(loc_table[count].id),loc_table[count].func);
   }
   return 1;
}

PRIVATE int set_obj_funcs(void)
{   int count;
    
    for (count = 0; count < MAPSIZE(obj_table); count++)
    {  if (obj_table[count].id >= numobs)
       {  mudlog("INIT: Illegal Indexing in Object Table (obj_table[%d] = %d)",count,obj_table[count].id);
       }
       else if (obj_table[count].id != -1)
          set_obj_fun(obj_table[count].id,obj_table[count].func);
    }
    return 1;
}

PRIVATE int set_mob_funcs(void)
{  int count;

   for (count = 0; count < MAPSIZE(mob_table); count ++)
   {  if (mob_table[count].id >= numchars)
        mudlog("INIT: Illegal Indexing in Mobile Table (mob_table[%d] = %d)",count,mob_table[count].id);
      else if (mob_table[count].id != -1)
        set_mob_fun((mob_table[count].id+max_players), mob_table[count].func);
   }
   return 1;
}

PUBLIC void on_timer_events(void)
{  int count,i,pl;

   /* only do special stuff in rooms where people are */
   for (count = 0; count < max_players; count ++)
   {   if (!is_in_game(count))   /* Player slot is empty */
          continue;

       if (room_data[convroom(ploc(count))].spec_case != NULL)
       {  
          room_data[convroom(ploc(count))].spec_case(E_ONTIMER);
       }

       /* Do stuff for all mobiles in the room with a player */
       for (i = 0; i < lnumchars(ploc(count)); i ++)
       {  pl = lmob_nr(i,ploc(count));
          if (mob_fun(pl) != NULL && alive(pl) != -1)
          {  param_s.plx = count;
             param_s.pl  = pl;
             mob_fun(pl)(E_ONTIMER);
          }
       }
   }
}  

/* Do all objects event handlers checking for special events */
PUBLIC void obj_on_timer()
{  int i;

   for (i=0; i < numobs; i++)
   {  if (objects[i].spec_case != NULL)
      {  param_s.plx = i;
         param_s.loc = oloc(i);
         objects[i].spec_case(E_ONTIMER);
      }
   }
}

PUBLIC void check_obj_in_room(int player,int event)
{  register int i,ob;
   int lnum = lnumobs(ploc(player));

   for (i=0; i < lnum; i++)
   {   ob = lobj_nr(i,ploc(player));
       if (objects[(ob)].spec_case != NULL)
       {
          param_s.plx = player;
          param_s.loc = oloc(ob);
          objects[(ob)].spec_case(event);
       }
   }
}

PUBLIC void check_obj_on_plx(int player, int event)
{  register int i,ob;
   int pnum = pnumobs(player);

   for (i=0; i < pnum; i++)
   {   ob = pobj_nr(i,player);
       if (objects[(ob)].spec_case != NULL)
       {  param_s.plx = player;
          param_s.loc = oloc(ob);
          param_s.ob  = ob;
          param_s.ret = 1;
          objects[(ob)].spec_case(event);
          if (param_s.ret == -1)
             return;
       }
   }
}

PUBLIC void check_pl_in_room(int player,int event)
{  register int i,pl;
   int lnum = lnumchars(ploc(player));

   for (i = 0; i < lnum; i ++)
   {  pl = lmob_nr(i,ploc(player));
          if (mob_fun(pl) != NULL && alive(pl) != -1)
          {  param_s.plx = player;
             param_s.pl  = pl;
             mob_fun(pl)(event);
          }
   }
}

/****************************************************************************
 ** Give a player a function, if he/she is in the table PlayerFunc, otherwise
 ** clear the function (NULLify)
 ***************************************************************************/
PUBLIC void setplayerfunc(int plx)
{
  int i,l = CHMAPSIZE(PlayerFuncs);

/*  clear_mob_fun(plx);*/
  set_mob_fun(plx,Default_Handler);

  for (i=0;i < l;i++)
  {  if (strcasecmp(pname(plx),PlayerFuncs[i].id) == 0)
     {  set_mob_fun(plx,PlayerFuncs[i].func);
        break;
     }
  }
  param_s.plx = plx;
  param_s.ret = 1;
  mob_fun(plx)(E_ONINIT);
}


/*****************************************************************************
 ** FUNCTION   : is_start_loc
 ** DESCRIPTION: Check if a location is one of the starting places on the MUD.
 **              This is needed for commands like become and defrob.
 ***************************************************************************/
Boolean is_start_loc(int loc)
{   return (loc < LOCMIN_START && loc >= LOCMAX_START);
}

/*****************************************************************************
 ** FUNCTION    : get_rand_start
 ** DESCRIPTION : Get a random start location.
 *****************************************************************************/
int get_rand_start(void)
{   int r = randperc() % 2;

    if (the_world->w_start_loc != 0 && exists(the_world->w_start_loc) &&
        plev(mynum) < LVL_APPREN)
       return the_world->w_start_loc;

    if (plev(mynum) < LVL_FOUR)
       return LOC_INTRO_WELCOME;

    switch (r) {
    case 0 : return LOC_START_TEMPLE;
    case 1 : return LOC_START_CHURCH;
#ifdef LOCMIN_THENEWWORLD
    case 2 : return LOC_START_ENTRYPOINT;
#endif
    default: break;
    }
    return LOC_START_TEMPLE;
}


Boolean in_table(int val, const int *table)
{  int i;

   for (i=0; table[i] != -1; i++)
       if (table[i] == val)
          return True;

   return False;
}

void specials_on_timer(void)
{   /* Place your timed specials here */   
}