shadow/
shadow/data/command/
shadow/data/help/
shadow/data/religion/
shadow/data/skill/
/* Instaroom code by Kyndig
 * Allows resets to be placed on a room, according to current
 * mob/obj/container/exit placement of that room.
 * Syntax: instaroom
 *
 *
 * Additional needed install instructions:
 * Place the following 2 lines in => struct room_index_data{ in merc.h:
 *   RESET_DATA *        last_mob_reset;
 *   RESET_DATA *        last_obj_reset;
 * 
 * Place the following line in => struct  reset_data{ in merc.h
 * RESET_DATA *        prev;
 *
 * You can build an 'instant area reset maker' based off this code
 * Developed by Kyndig from http://www.kyndig.com/  kyndig@kyndig.com
 */
#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#include <sys/time.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <unistd.h>
#include "merc.h"
#include "olc.h"
#include "recycle.h"
#include "const.h"


/* Locals */
void wipe_resets( ROOM_INDEX_DATA *pRoom );
void instaroom( ROOM_INDEX_DATA *pRoom );


/* NOTE: This does not create RANDOM room exits, my exit system was
 * abit to much to port it to stock ROM so I simply removed. It will be up
 * to you to edit these functions to return what you desire
 */


/* Original idea taken from the SMAUG server, the LINK macro is a direct
 * reflection of the outstanding work of Thoric from realms.org
 */

/* double-linked list handling macros -Thoric */
#define LINK(link, first, last, next, prev)                     \
do                                                              \
{                                                               \
    if ( !(first) )                                             \
      (first)                   = (link);                       \
    else                                                        \
      (last)->next              = (link);                       \
    (link)->next                = NULL;                         \
    (link)->prev                = (last);                       \
    (last)                      = (link);                       \
} while(0)



/* called from do_instaroom further below */
void reset_instaroom( CHAR_DATA *ch, char *argument )
{
  ROOM_INDEX_DATA     *pRoom;
  CHAR_DATA           *mob;
  OBJ_DATA            *obj, *inobj;

  pRoom = ch->in_room;

  /* Containers can NOT be closed when doing an 'instaroom'. Thus a builder will have to close
   * the container after the reset is installed
   */

  /* lets go through a mob first */
  for ( mob = ch->in_room->people; mob; mob = mob->next_in_room )
  {
     if ( IS_NPC( mob ) )
     {
         /* only mobs with this areas vnums are allowed */
        if ( mob->pIndexData->area != pRoom->area )
        {
           send_to_char( "There is a mob in this room that is not part of your area, resets not set.\n\r", ch );
           return;
        }
    
        for ( obj = mob->carrying; obj; obj = obj->next_content )
        {
           if( obj->pIndexData->area != pRoom->area )
           {
               send_to_char( "There is an object in a MOB that is not part of your area, resets not set.\n\r", ch );
              return;
           }
      
           if ( IS_SET( obj->value[1], CONT_CLOSED ) )
           {
              send_to_char( "There is a container in a MOB I can't see inside of. Get the container from the mob, open\n\r"
                            "it up, give it _back_ to your mob, then do an instaroom.\n\r" 
                            "AFTER you have set the container to load into the mob,\n\r"
                            "you can then get the container from him again, close/lock it, and return it....In other words\n\r"
                            "A container must be open first, the reset installed, after that, you can close/lock it.\n\r",ch);
              return;
           }

           if ( obj->contains )
           {
               for (inobj = obj->contains; inobj; inobj = obj->next_content )
               {
                   if ( inobj->pIndexData->area != pRoom->area )
                   {
                      send_to_char("There is an object in a container which a MOB in this room has, which is not\n\r"
                                   "a vnum for this area. RESETS NOT SET.\n\r",ch);
                      return;
                   }
               }
           }
        }
     }
  }/* done looking at mobs and their eq/inv */


  /* lets take a look at objects in the room and their contents */ 
  for ( obj = pRoom->contents; obj != NULL; obj = obj->next_content )
  {
      if( obj->pIndexData->area != pRoom->area )
      {
          send_to_char( "There is an object in this room that is not a vnum of your area, resets not set.\n\r", ch );
          return;
      }
  
      if ( IS_SET( obj->value[1], CONT_CLOSED ) )
      {
          send_to_char( "There is a container in this room I can't see inside of. Open it up first, do the\n\r"
                        "instaroom command, THEN you can close/lock the container.\n\r",ch);
          return;
      }

      if ( obj->contains )
      {
         for (inobj = obj->contains; inobj; inobj = obj->next_content )
         {
            if ( inobj->pIndexData->area != pRoom->area )
            {
               send_to_char("There is an object in a container that does not a vnum in this area. No resets set.\n\r",ch);
               return;
            }
         }
      }
  }

  if ( pRoom->reset_first )
    wipe_resets(pRoom);  
  instaroom( pRoom );

  send_to_char( "Room resets installed.\n\r", ch );
  return;
}

/* Separate function for recursive purposes */
void delete_reset( ROOM_INDEX_DATA *pRoom, RESET_DATA *pReset, int insert_loc, bool wipe_all)
{

  if( !pRoom->reset_first )
  {
     return;
  }

  if ( insert_loc-1 <= 0 )
  {
       pReset = pRoom->reset_first;
       pRoom->reset_first = pRoom->reset_first->next;
       if ( !pRoom->reset_first )
           pRoom->reset_last = NULL;
   }
   else
   {
       int iReset = 0;
       RESET_DATA *prev = NULL;

       for ( pReset = pRoom->reset_first;
             pReset;
             pReset = pReset->next )
       {
             if ( ++iReset == insert_loc )
                    break;
             prev = pReset;
       }

       if ( !pReset )
       {
           return;
       }

        if ( prev )
            prev->next = prev->next->next;
        else
            pRoom->reset_first = pRoom->reset_first->next;

        for ( pRoom->reset_last = pRoom->reset_first;
              pRoom->reset_last->next;
              pRoom->reset_last = pRoom->reset_last->next );
   }/*else*/

   if ( pReset == pRoom->last_mob_reset )
    pRoom->last_mob_reset = NULL;
  if ( pReset == pRoom->last_obj_reset )
    pRoom->last_obj_reset = NULL;

   free_reset_data( pReset );

   /* TRUE/FALSE call on delete_reset here, so we can delete all resets or just one */
   if( wipe_all )
   {
     if( ( pReset = pRoom->reset_first)  != '\0')
       delete_reset( pRoom, pReset, 1, TRUE );
   }
}

void wipe_resets( ROOM_INDEX_DATA *pRoom )
{
  RESET_DATA *pReset;
  int i = 1;
  
  for ( pReset = pRoom->reset_first; pReset != NULL; )
  {
    if (pReset)
    {
      delete_reset(pRoom, pReset, i, TRUE);
    }
    i++;
    pReset = pReset->next;
  }
  return;
}

/* make_reset called by add_new_reset in order to create a blank
 * reset to load data into the reset_list 
 */
RESET_DATA *make_reset( char letter, int arg1, int arg2, int arg3, int arg4 )
{
        RESET_DATA *pReset;

        pReset          = new_reset_data();
        pReset->command = letter;
        pReset->arg1    = arg1;
        pReset->arg2    = arg2;
        pReset->arg3    = arg3;
        pReset->arg4    = arg4;
        return pReset;
}


/* add_new_reset called from several places below, it adds in the actual pReset
 * arguments to the reset_list..tricky footwork here 
 */
RESET_DATA *add_new_reset( ROOM_INDEX_DATA *pRoom, char letter, int arg1, int arg2, int arg3, int arg4 )
{
    RESET_DATA *pReset;

    if ( !pRoom )
    {
        bug( "add_reset: NULL area!", 0 );
        return NULL;
    }

    letter = UPPER(letter);
    pReset = make_reset( letter, arg1, arg2, arg3, arg4 );
    switch( letter )
    {
        case 'M':  pRoom->last_mob_reset = pReset;      break;
        case 'E':  case 'G':  case 'P':
        case 'O':  pRoom->last_obj_reset = pReset;      break;
            break;
    }

    /* LINK code from the SMAUG server */
    LINK( pReset, pRoom->reset_first, pRoom->reset_last, next, prev );
    return pReset;
}


/* Called from instaroom */
/* The below function debugged by Chalc on the Romlist, thanx again Chalc */
void add_obj_reset( ROOM_INDEX_DATA *pRoom, char cm, OBJ_DATA *obj, int v2, int v3, int v4 )
{
  add_new_reset( pRoom, cm, obj->pIndexData->vnum, v2, v3, v4 );

  if(obj->contains)
  {
    OBJ_INDEX_DATA **objList;
    OBJ_DATA *inObj;
    int *objCount;
    int count;
    int itemCount;

    for(inObj = obj->contains, itemCount = 0; inObj; inObj = inObj->next_content) itemCount++;

    /* Now have count objects in obj, allocate space for lists */
    objList = (OBJ_INDEX_DATA **)alloc_mem(itemCount * sizeof(OBJ_INDEX_DATA *));
    objCount = (int *)alloc_mem(itemCount * sizeof(int));
    
    /* Initialize Memory */
    memset(objList, 0, itemCount * sizeof(OBJ_INDEX_DATA *));
    memset(objCount, 0, itemCount * sizeof(int));

    /* Figure out how many of each obj is in the container */
    for(inObj = obj->contains; inObj; inObj = inObj->next_content)
    {
      for(count = 0; objList[count] && objList[count] != inObj->pIndexData; count++);
      if(!objList[count]) objList[count] = inObj->pIndexData;
      objCount[count]++;
    }    

    /* Create the resets */
    for(count = 0; objList[count]; count++)
      add_new_reset(pRoom, 'P', objList[count]->vnum, objCount[count], obj->pIndexData->vnum, objCount[count]);

    /* Free the memory */
    free_mem(objList, itemCount * sizeof(OBJ_INDEX_DATA *));
    free_mem(objCount, itemCount * sizeof(int));
  }
  /* And Done */
  return;
}

void instaroom( ROOM_INDEX_DATA *pRoom )
{
  CHAR_DATA *rch;
  OBJ_DATA *obj;
  
  for ( rch = pRoom->people; rch; rch = rch->next_in_room )
  {
    if ( !IS_NPC(rch) )
      continue;
    add_new_reset( pRoom, 'M',rch->pIndexData->vnum, rch->pIndexData->count, pRoom->vnum, 3 );
    for ( obj = rch->carrying; obj; obj = obj->next_content )
    {
      if ( obj->wear_loc == WEAR_NONE )
        add_obj_reset( pRoom, 'G', obj, 1, 0, 3 );
      else
        add_obj_reset( pRoom, 'E', obj, 1, obj->wear_loc, 3 );
    }
  }
  for ( obj = pRoom->contents; obj; obj = obj->next_content )
  {
    add_obj_reset( pRoom, 'O', obj, 1, pRoom->vnum, 1 );
  }
  return;
}

void do_instaroom( CHAR_DATA *ch, char *argument )
{
  ROOM_INDEX_DATA *pRoom = ch->in_room;
  char arg[MAX_INPUT_LENGTH];

  argument = one_argument(argument, arg);

  if(!strstr( ch->in_room->area->builders, ch->name ) )
  {  send_to_char("You aren't the builder of this area!\n\r",ch);
     return;
  }

  if(ch->pcdata->security < ch->in_room->area->security )
  {     
        send_to_char("You don't have the security to edit this area.\n\r",ch);
	return;
  }

  if ( pRoom->reset_first )
    wipe_resets(pRoom);  
  instaroom( pRoom );

  send_to_char( "Room resets installed.\n\r", ch );
}