/*
 *  Renumber Imm command
 *  Author: Cronel (cronel_kal@hotmail.com)
 *  of FrozenMUD (empire.digiunix.net 4000)
 *
 *  Permission to use and distribute this code is granted provided
 *  this header is retained and unaltered, and the distribution
 *  package contains all the original files unmodified.
 *  If you modify this code and use/distribute modified versions
 *  you must give credit to the original author(s).
 */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "mud.h"

#define NOT_FOUND (-1)
enum
{ REN_ROOM, REN_OBJ, REN_MOB };
struct renumber_data
{
   int old_vnum;
   int new_vnum;

   struct renumber_data *next;
};
typedef struct renumber_data RENUMBER_DATA;

struct renumber_areas
{
   int low_obj, hi_obj;
   RENUMBER_DATA *r_obj;
   int low_mob, hi_mob;
   RENUMBER_DATA *r_mob;
   int low_room, hi_room;
   RENUMBER_DATA *r_room;
};
typedef struct renumber_areas RENUMBER_AREA;

void renumber_area( CHAR_DATA * ch, AREA_DATA * area, RENUMBER_AREA * r_area, bool area_is_proto, bool verbose );

RENUMBER_AREA *gather_renumber_data( AREA_DATA * area, int new_base, bool fill_gaps );
RENUMBER_DATA *gather_one_list( short type, int low, int high, int new_base, bool fill_gaps, int *max_vnum );
void free_renumber_data( RENUMBER_DATA * r_data );

AREA_DATA *find_area( char *filename, bool * p_is_proto );
bool check_vnums( CHAR_DATA * ch, AREA_DATA * tarea, RENUMBER_AREA * r_area );

int find_translation( int vnum, RENUMBER_DATA * r_data );
void translate_reset( RESET_DATA * reset, RENUMBER_AREA * r_data );
void translate_objvals( CHAR_DATA * ch, AREA_DATA * area, RENUMBER_AREA * r_data, bool verbose );
void translate_exits( CHAR_DATA * ch, AREA_DATA * area, RENUMBER_AREA * r_area, bool verbose );
void warn_progs( CHAR_DATA * ch, int low, int high, AREA_DATA * area, RENUMBER_AREA * r_area );
void warn_in_prog( CHAR_DATA * ch, int low, int high, char *where, int vnum, MPROG_DATA * mprog, RENUMBER_AREA * r_area );

/* from db.c */
extern ROOM_INDEX_DATA *room_index_hash[MAX_KEY_HASH];
extern MOB_INDEX_DATA *mob_index_hash[MAX_KEY_HASH];
extern OBJ_INDEX_DATA *obj_index_hash[MAX_KEY_HASH];

void do_renumber( CHAR_DATA * ch, char *argument )
{
   RENUMBER_AREA *r_area;
   AREA_DATA *area;
   bool is_proto;
   char arg1[MAX_INPUT_LENGTH];
   int new_base;
   bool fill_gaps, verbose;

   /*
    * parse the first two parameters 
    */
   /*
    * first, area 
    */
   argument = one_argument( argument, arg1 );
   if( arg1[0] == '\0' )
   {
      ch_printf( ch, "What area do you want to renumber?\r\n" );
      return;
   }
   area = find_area( arg1, &is_proto );
   if( area == NULL )
   {
      ch_printf( ch, "No such area '%s'.\r\n", arg1 );
      return;
   }

   /*
    * and new vnum base 
    */
   argument = one_argument( argument, arg1 );
   if( arg1[0] == '\0' )
   {
      ch_printf( ch, "What will be the new vnum base for this area?\r\n" );
      return;
   }
   if( !is_number( arg1 ) )
   {
      ch_printf( ch, "Sorry, '%s' is not a valid vnum base number!\r\n", arg1 );
      return;
   }
   new_base = atoi( arg1 );

   /*
    * parse the flags 
    */
   fill_gaps = FALSE;
   verbose = FALSE;
   for( ;; )
   {
      argument = one_argument( argument, arg1 );
      if( arg1[0] == '\0' )
         break;
      else if( !str_prefix( arg1, "fillgaps" ) )
         fill_gaps = TRUE;
      else if( !str_prefix( arg1, "verbose" ) )
         verbose = TRUE;
      else
      {
         ch_printf( ch, "Invalid flag '%s'.\r\n", arg1 );
         return;
      }
   }

   /*
    * sanity check 
    */
   if( new_base == area->low_r_vnum && new_base == area->low_o_vnum && new_base == area->low_m_vnum && !fill_gaps )
   {
      ch_printf( ch,
                 "You don't want to change the base vnum and you don't want to fill gaps...\r\nSo what DO you wanna do?\r\n" );
      return;
   }

   /*
    * some restrictions 
    */
   if( IS_NPC( ch ) )
   {
      ch_printf( ch, "Yeah, right.\r\n" );
      return;
   }
   if( ch->level < LEVEL_SAVIOR )
   {
      ch_printf( ch, "You don't have enough privileges.\r\n" );
      return;
   }
   if( ch->level == LEVEL_SAVIOR )
   {
      if( area->low_r_vnum < ch->pcdata->r_range_lo || area->hi_r_vnum > ch->pcdata->r_range_hi ||
          area->low_m_vnum < ch->pcdata->m_range_lo || area->hi_m_vnum > ch->pcdata->m_range_hi ||
          area->low_o_vnum < ch->pcdata->o_range_lo || area->hi_o_vnum > ch->pcdata->o_range_hi )
      {
         ch_printf( ch, "You can't renumber that area ('%s').\r\n", area->filename );
         return;
      }
   }

   /*
    * get the renumber data 
    */
   r_area = gather_renumber_data( area, new_base, fill_gaps );

   /*
    * one more restriction 
    */
   if( ch->level == LEVEL_SAVIOR )
   {
      if( r_area->low_room < ch->pcdata->r_range_lo || r_area->hi_room > ch->pcdata->r_range_hi ||
          r_area->low_obj < ch->pcdata->o_range_lo || r_area->hi_obj > ch->pcdata->o_range_hi ||
          r_area->low_mob < ch->pcdata->m_range_lo || r_area->hi_mob > ch->pcdata->m_range_hi )
      {
         ch_printf( ch, "The renumbered area would be outside your assigned vnum range.\r\n" );
         return;
      }
   }
   else if( is_proto )
   {
      if( r_area->low_room < area->low_r_vnum || r_area->hi_room > area->hi_r_vnum ||
          r_area->low_obj < area->low_o_vnum || r_area->hi_obj > area->hi_o_vnum ||
          r_area->low_mob < area->low_m_vnum || r_area->hi_mob > area->hi_m_vnum )
      {
         ch_printf( ch,
                    "Moving a proto area out of its range would create problems.\r\nWait till the area is finished to move it.\r\n" );
         return;
      }
   }

   /*
    * no overwriting of dest vnums 
    */
   if( check_vnums( ch, area, r_area ) )
      return;

   /*
    * another sanity check :) 
    */
   if( r_area == NULL || ( r_area->r_obj == NULL && r_area->r_mob == NULL && r_area->r_room == NULL ) )
   {
      ch_printf( ch, "No changes to make.\r\n" );
      if( r_area != NULL )
         DISPOSE( r_area );
      return;
   }

   /*
    * ok, do it! 
    */
   pager_printf( ch, "Renumbering area '%s' to new base %d, filling gaps: %s\r\n",
                 area->filename, new_base, fill_gaps ? "yes" : "no" );
   renumber_area( ch, area, r_area, is_proto, verbose );
   pager_printf( ch, "Done.\r\n" );

   /*
    * clean up and goodbye 
    */
   if( r_area->r_room != NULL )
      free_renumber_data( r_area->r_room );
   if( r_area->r_obj != NULL )
      free_renumber_data( r_area->r_obj );
   if( r_area->r_mob != NULL )
      free_renumber_data( r_area->r_mob );
   DISPOSE( r_area );
}

bool check_vnums( CHAR_DATA * ch, AREA_DATA * tarea, RENUMBER_AREA * r_area )
{
   int high, low;
   AREA_DATA *area;
   bool proto;

   /*
    * this function assumes all the lows are allways gonna be
    * lower or equal to all the highs .. 
    */
   high = UMAX( r_area->hi_room, UMAX( r_area->hi_obj, r_area->hi_mob ) );
   low = UMIN( r_area->low_room, UMIN( r_area->low_obj, r_area->low_mob ) );

   /*
    * in do_check_vnums they use first_bsort, first_asort but.. i dunno.. 
    */
   area = first_area;
   proto = FALSE;
   while( area )
   {
      if( tarea == area )
         ;
      else if( !( high < area->low_r_vnum || low > area->hi_r_vnum ) ||
               !( high < area->low_o_vnum || low > area->hi_o_vnum ) ||
               !( high < area->low_m_vnum || low > area->hi_m_vnum ) )
      {
         ch_printf( ch, "This operation would overwrite area %s! Use checkvnums first.\r\n", area->filename );
         return TRUE;
      }

      area = area->next;
      if( area == NULL && !proto )
      {
         area = first_build;
         proto = TRUE;
      }
   }
   return FALSE;
}

RENUMBER_AREA *gather_renumber_data( AREA_DATA * area, int new_base, bool fill_gaps )
/* this function actualy gathers all the renumber data for an area */
{
   RENUMBER_AREA *r_area;
   int max;

   CREATE( r_area, RENUMBER_AREA, 1 );

   r_area->r_mob = gather_one_list( REN_MOB, area->low_m_vnum, area->hi_m_vnum, new_base, fill_gaps, &max );
   r_area->low_mob = new_base;
   r_area->hi_mob = max;

   r_area->r_obj = gather_one_list( REN_OBJ, area->low_o_vnum, area->hi_o_vnum, new_base, fill_gaps, &max );
   r_area->low_obj = new_base;
   r_area->hi_obj = max;

   r_area->r_room = gather_one_list( REN_ROOM, area->low_r_vnum, area->hi_r_vnum, new_base, fill_gaps, &max );
   r_area->low_room = new_base;
   r_area->hi_room = max;

   return r_area;
}

RENUMBER_DATA *gather_one_list( short type, int low, int high, int new_base, bool fill_gaps, int *max_vnum )
/* this function builds a list of renumber data for a type (obj, room, or mob) */
{
   int cur_vnum;
   RENUMBER_DATA *r_data, root;
   bool found;
   ROOM_INDEX_DATA *room;
   OBJ_INDEX_DATA *obj;
   MOB_INDEX_DATA *mob;
   int i;
   int highest;

   memset( &root, 0, sizeof( RENUMBER_DATA ) );
   r_data = &root;

   cur_vnum = new_base;
   highest = -1;
   for( i = low; i <= high; i++ )
   {
      found = FALSE;
      switch ( type )
      {
         case REN_ROOM:
            room = get_room_index( i );
            if( room != NULL )
               found = TRUE;
            break;
         case REN_OBJ:
            obj = get_obj_index( i );
            if( obj != NULL )
               found = TRUE;
            break;
         case REN_MOB:
            mob = get_mob_index( i );
            if( mob != NULL )
               found = TRUE;
            break;
      }

      if( found )
      {
         if( cur_vnum > highest )
            highest = cur_vnum;
         if( cur_vnum != i )
         {
            CREATE( r_data->next, RENUMBER_DATA, 1 );
            r_data = r_data->next;
            r_data->old_vnum = i;
            r_data->new_vnum = cur_vnum;
         }
         cur_vnum++;
      }
      else if( !fill_gaps )
         cur_vnum++;
   }
   *max_vnum = highest;
   return root.next;
}

void free_renumber_data( RENUMBER_DATA * r_data )
/* disposes of a list of renumber data items */
{
   RENUMBER_DATA *r_next;

   while( r_data != NULL )
   {
      r_next = r_data->next;
      DISPOSE( r_data );
      r_data = r_next;
   }
}

void renumber_area( CHAR_DATA * ch, AREA_DATA * area, RENUMBER_AREA * r_area, bool area_is_proto, bool verbose )
/* this is the function that actualy does the renumbering of "area" according
   to the renumber data in "r_area". "ch" is to show messages. */
{
   RENUMBER_DATA *r_data;
   ROOM_INDEX_DATA *room, *room_prev, *room_list, *room_next;
   MOB_INDEX_DATA *mob, *mob_prev, *mob_list, *mob_next;
   OBJ_INDEX_DATA *obj, *obj_prev, *obj_list, *obj_next;
   RESET_DATA *preset, *treset;
   int iHash;
   int low, high;

   high = UMAX( area->hi_r_vnum, UMIN( area->hi_o_vnum, area->hi_m_vnum ) );
   low = UMIN( area->low_r_vnum, UMIN( area->low_o_vnum, area->low_m_vnum ) );

   pager_printf( ch, "(Room) Renumbering...\r\n" );

   /*
    * what we do here is, for each list (room/obj/mob) first we
    * * take each element out of the hash array, change the vnum,
    * * and move it to our own list. after everything's moved out
    * * we put it in again. this is to avoid problems in situations
    * * where where room A is being moved to position B, but theres
    * * already a room B wich is also being moved to position C.
    * * a straightforward approach would result in us moving A to
    * * position B first, and then again to position C, and room
    * * B being lost inside the hash array, still there, but not
    * * foundable (its "covered" by A because they'd have the same 
    * * vnum). 
    */

   room_list = NULL;
   for( r_data = r_area->r_room; r_data; r_data = r_data->next )
   {
      if( verbose )
         pager_printf( ch, "(Room) %d -> %d\r\n", r_data->old_vnum, r_data->new_vnum );

      room = get_room_index( r_data->old_vnum );
      if( !room )
      {
         bug( "renumber_area: NULL room %d", r_data->old_vnum );
         continue;
      }

      /*
       * remove it from the hash list 
       */
      iHash = r_data->old_vnum % MAX_KEY_HASH;
      if( room_index_hash[iHash] == room )
         room_index_hash[iHash] = room->next;
      else
      {
         for( room_prev = room_index_hash[iHash]; room_prev && room_prev->next != room; room_prev = room_prev->next )
            ;
         if( room_prev == NULL )
         {
            bug( "renumber_area: Couldn't find a room in the hash table! Skipping it.\r\n" );
            continue;
         }
         room_prev->next = room->next;
         room->next = NULL;
      }

      /*
       * change the vnum 
       */
      room->vnum = r_data->new_vnum;

      /*
       * move it to the temporary list 
       */
      room->next = room_list;
      room_list = room;
   }
   /*
    * now move everything back into the hash array 
    */
   for( room = room_list; room; room = room_next )
   {
      room_next = room->next;
      /*
       * add it to the hash list again (new position) 
       */
      iHash = room->vnum % MAX_KEY_HASH;
      room->next = room_index_hash[iHash];
      room_index_hash[iHash] = room;
   }
   /*
    * if nothing was moved, or if the area is proto, dont change this 
    */
   if( r_area->r_room != NULL && !area_is_proto )
   {
      area->low_r_vnum = r_area->low_room;
      area->hi_r_vnum = r_area->hi_room;
   }

   pager_printf( ch, "(Mobs) Renumbering...\r\n" );
   mob_list = NULL;
   for( r_data = r_area->r_mob; r_data; r_data = r_data->next )
   {
      if( verbose )
         pager_printf( ch, "(Mobs) %d -> %d\r\n", r_data->old_vnum, r_data->new_vnum );

      mob = get_mob_index( r_data->old_vnum );
      if( !mob )
      {
         bug( "renumber_area: NULL mob %d", r_data->old_vnum );
         continue;
      }

      /*
       * fix references to this mob from shops while renumbering this mob 
       */
      if( mob->pShop )
      {
         if( verbose )
            pager_printf( ch, "(Mobs) Fixing shop for mob %d -> %d\r\n", r_data->old_vnum, r_data->new_vnum );
         mob->pShop->keeper = r_data->new_vnum;
      }
      if( mob->rShop )
      {
         if( verbose )
            pager_printf( ch, "(Mobs) Fixing repair shop for mob %d -> %d\r\n", r_data->old_vnum, r_data->new_vnum );
         mob->rShop->keeper = r_data->new_vnum;
      }

      /*
       * remove it from the hash list 
       */
      iHash = r_data->old_vnum % MAX_KEY_HASH;
      if( mob_index_hash[iHash] == mob )
         mob_index_hash[iHash] = mob->next;
      else
      {
         for( mob_prev = mob_index_hash[iHash]; mob_prev && mob_prev->next != mob; mob_prev = mob_prev->next )
            ;
         if( mob_prev == NULL )
         {
            bug( "renumber_area: Couldn't find a mob in the hash table! Skipping it.\r\n" );
            continue;
         }
         mob_prev->next = mob->next;
         mob->next = NULL;
      }

      /*
       * change the vnum 
       */
      mob->vnum = r_data->new_vnum;

      /*
       * move to private list 
       */
      mob->next = mob_list;
      mob_list = mob;
   }
   for( mob = mob_list; mob; mob = mob_next )
   {
      mob_next = mob->next;
      /*
       * add it to the hash list again 
       */
      iHash = mob->vnum % MAX_KEY_HASH;
      mob->next = mob_index_hash[iHash];
      mob_index_hash[iHash] = mob;
   }
   if( r_area->r_mob && !area_is_proto )
   {
      area->low_m_vnum = r_area->low_mob;
      area->hi_m_vnum = r_area->hi_mob;
   }

   pager_printf( ch, "(Objs) Renumbering...\r\n" );
   obj_list = NULL;
   for( r_data = r_area->r_obj; r_data; r_data = r_data->next )
   {
      if( verbose )
         pager_printf( ch, "(Objs) %d -> %d\r\n", r_data->old_vnum, r_data->new_vnum );
      obj = get_obj_index( r_data->old_vnum );
      if( !obj )
      {
         bug( "renumber_area: NULL obj %d", r_data->old_vnum );
         continue;
      }

      /*
       * remove it from the hash list 
       */
      iHash = r_data->old_vnum % MAX_KEY_HASH;
      if( obj_index_hash[iHash] == obj )
         obj_index_hash[iHash] = obj->next;
      else
      {
         for( obj_prev = obj_index_hash[iHash]; obj_prev && obj_prev->next != obj; obj_prev = obj_prev->next )
            ;
         if( obj_prev == NULL )
         {
            bug( "renumber_area: Couldn't find an obj in the hash table! Skipping it.\r\n" );
            continue;
         }
         obj_prev->next = obj->next;
         obj->next = NULL;
      }

      /*
       * change the vnum 
       */
      obj->vnum = r_data->new_vnum;

      /*
       * to our list 
       */
      obj->next = obj_list;
      obj_list = obj;
   }
   for( obj = obj_list; obj; obj = obj_next )
   {
      obj_next = obj->next;
      /*
       * add it to the hash list again 
       */
      iHash = obj->vnum % MAX_KEY_HASH;
      obj->next = obj_index_hash[iHash];
      obj_index_hash[iHash] = obj;
   }
   if( r_area->r_obj && !area_is_proto )
   {
      area->low_o_vnum = r_area->low_obj;
      area->hi_o_vnum = r_area->hi_obj;
   }

   pager_printf( ch, "Fixing references...\r\n" );

   pager_printf( ch, "... fixing objvals...\r\n" );
   translate_objvals( ch, area, r_area, verbose );

   pager_printf( ch, "... fixing exits...\r\n" );
   translate_exits( ch, area, r_area, verbose );

   pager_printf( ch, "... fixing resets...\r\n" );
   for( room = area->first_room; room; room = room->next_aroom )
   {
      for( preset = room->first_reset; preset; preset = preset->next )
      {
         translate_reset( preset, r_area );
         for( treset = preset->first_reset; treset; treset = preset->next_reset )
            translate_reset( treset, r_area );
      }
   }

   if( verbose )
   {
      pager_printf( ch, "Searching progs for references to renumbered vnums...\r\n" );
      warn_progs( ch, low, high, area, r_area );
   }
}

void translate_exits( CHAR_DATA * ch, AREA_DATA * area, RENUMBER_AREA * r_area, bool verbose )
{
   int i, new_vnum;
   EXIT_DATA *pexit, *rv_exit;
   ROOM_INDEX_DATA *room;
   int old_vnum;

   for( i = area->low_r_vnum; i <= area->hi_r_vnum; i++ )
   {
      room = get_room_index( i );
      if( !room )
         continue;
      for( pexit = room->first_exit; pexit; pexit = pexit->next )
      {
         /*
          * translate the exit destination, if it was moved 
          */
         new_vnum = find_translation( pexit->vnum, r_area->r_room );
         if( new_vnum != NOT_FOUND )
            pexit->vnum = new_vnum;
         /*
          * if this room was moved 
          */
         if( pexit->rvnum != i )
         {
            old_vnum = pexit->rvnum;
            pexit->rvnum = i;
            /*
             * all reverse exits in other areas will be wrong 
             */
            rv_exit = get_exit_to( pexit->to_room, rev_dir[pexit->vdir], old_vnum );
            if( rv_exit && pexit->to_room->area != area )
            {
               if( rv_exit->vnum != i )
               {
                  pager_printf( ch, "...    fixing reverse exit in area %s.\r\n", pexit->to_room->area->filename );
                  rv_exit->vnum = i;
               }
            }
         }

         /*
          * translate the key 
          */
         if( pexit->key != -1 )
         {
            new_vnum = find_translation( pexit->key, r_area->r_obj );
            if( new_vnum == NOT_FOUND )
               continue;
            pexit->key = new_vnum;
         }
      }
   }
}

void translate_objvals( CHAR_DATA * ch, AREA_DATA * area, RENUMBER_AREA * r_area, bool verbose )
{
   int i, new_vnum;
   OBJ_INDEX_DATA *obj;

   for( i = area->low_o_vnum; i <= area->hi_o_vnum; i++ )
   {
      obj = get_obj_index( i );
      if( !obj )
         continue;

      if( obj->item_type == ITEM_CONTAINER )
      {
         new_vnum = find_translation( obj->value[2], r_area->r_obj );
         if( new_vnum != NOT_FOUND )
         {
            if( verbose )
               pager_printf( ch, "...    container %d; fixing objval2 (key vnum) %d -> %d\r\n", i, obj->value[2], new_vnum );
            obj->value[2] = new_vnum;
         }
         else if( verbose )
            pager_printf( ch, "...    container %d; no need to fix.\r\n", i );
      }
      else if( obj->item_type == ITEM_SWITCH || obj->item_type == ITEM_LEVER ||
               obj->item_type == ITEM_PULLCHAIN || obj->item_type == ITEM_BUTTON )
      {
         /*
          * levers might have room vnum references in their objvals 
          */
         if( IS_SET( obj->value[0], TRIG_TELEPORT )
             || IS_SET( obj->value[0], TRIG_TELEPORTALL )
             || IS_SET( obj->value[0], TRIG_TELEPORTPLUS )
             || IS_SET( obj->value[0], TRIG_RAND4 )
             || IS_SET( obj->value[0], TRIG_RAND6 ) || IS_SET( obj->value[0], TRIG_DOOR ) )
         {
            new_vnum = find_translation( obj->value[1], r_area->r_room );
            if( new_vnum != NOT_FOUND )
            {
               if( verbose )
                  pager_printf( ch, "...    lever %d: fixing source room (%d -> %d)\r\n", i, obj->value[1], new_vnum );
               obj->value[1] = new_vnum;
            }
            if( IS_SET( obj->value[0], TRIG_DOOR ) && IS_SET( obj->value[0], TRIG_PASSAGE ) )
            {
               new_vnum = find_translation( obj->value[2], r_area->r_room );
               if( new_vnum != NOT_FOUND )
               {
                  if( verbose )
                     pager_printf( ch, "...    lever %d: fixing dest room (passage) (%d -> %d)\r\n",
                                   i, obj->value[2], new_vnum );
                  obj->value[2] = new_vnum;
               }
            }
         }
      }
   }
}

void warn_progs( CHAR_DATA * ch, int low, int high, AREA_DATA * area, RENUMBER_AREA * r_area )
{
   ROOM_INDEX_DATA *room;
   OBJ_INDEX_DATA *obj;
   MOB_INDEX_DATA *mob;
   MPROG_DATA *mprog;
   int i;

   for( i = area->low_r_vnum; i <= area->hi_r_vnum; i++ )
   {
      room = get_room_index( i );
      if( !room )
         continue;
      mprog = room->mudprogs;
      while( mprog )
      {
         warn_in_prog( ch, low, high, "room", i, mprog, r_area );
         mprog = mprog->next;
      }
   }

   for( i = area->low_o_vnum; i <= area->hi_o_vnum; i++ )
   {
      obj = get_obj_index( i );
      if( !obj )
         continue;
      mprog = obj->mudprogs;
      while( mprog )
      {
         warn_in_prog( ch, low, high, "obj", i, mprog, r_area );
         mprog = mprog->next;
      }
   }

   for( i = area->low_m_vnum; i <= area->hi_m_vnum; i++ )
   {
      mob = get_mob_index( i );
      if( !mob )
         continue;
      mprog = mob->mudprogs;
      while( mprog )
      {
         warn_in_prog( ch, low, high, "mob", i, mprog, r_area );
         mprog = mprog->next;
      }
   }
}



void warn_in_prog( CHAR_DATA * ch, int low, int high, char *where, int vnum, MPROG_DATA * mprog, RENUMBER_AREA * r_area )
{
   char *p, *start_number, cTmp;
   int num;

   p = mprog->comlist;
   while( *p )
   {
      if( isdigit( *p ) )
      {
         start_number = p;
         while( isdigit( *p ) && *p )
            p++;
         cTmp = *p;
         *p = 0;
         num = atoi( start_number );
         *p = cTmp;
         if( num >= low && num <= high )
         {
            pager_printf( ch,
                          "Warning! %s prog in %s vnum %d might contain a reference to %d.\r\n(Translation: Room %d, Obj %d, Mob %d)\r\n",
                          mprog_type_to_name( mprog->type ), where, vnum, num, find_translation( num, r_area->r_room ),
                          find_translation( num, r_area->r_obj ), find_translation( num, r_area->r_mob ) );
         }
         if( *p == '\0' )
            break;
      }
      p++;
   }
}


void translate_reset( RESET_DATA * reset, RENUMBER_AREA * r_data )
/* this function translates a reset according to the renumber data in r_data */
{
   /*
    * a list based approach to fixing the resets. instead
    * of having a bunch of several instances of very 
    * similar code, i just made this array that tells the
    * code what to do. it's pretty straightforward 
    */
   char *action_table[] = { "Mm1r3", "Oo1r3", "Ho1", "Po1o3",
      "Go1", "Eo1", "Dr1", "Rr1", NULL
   };
   char *p;
   RENUMBER_DATA *r_table;
   int *parg, new_vnum, i;

   /*
    * T is a special case 
    */
   if( reset->command == 'T' )
   {
      if( IS_SET( reset->extra, TRAP_ROOM ) )
         r_table = r_data->r_room;
      else if( IS_SET( reset->extra, TRAP_OBJ ) )
         r_table = r_data->r_obj;
      else
      {
         bug( "translate_reset: Invalid 'T' reset found.\r\n" );
         return;
      }
      new_vnum = find_translation( reset->arg3, r_table );
      if( new_vnum != NOT_FOUND )
         reset->arg3 = new_vnum;
      return;
   }

   /*
    * B is another special case 
    */
   if( reset->command == 'B' )
   {
      bug( "translate_reset: B command found." );
      if( ( reset->arg2 & BIT_RESET_TYPE_MASK ) == BIT_RESET_DOOR ||
          ( reset->arg2 & BIT_RESET_TYPE_MASK ) == BIT_RESET_ROOM )
      {
         new_vnum = find_translation( reset->arg1, r_data->r_obj );
         if( new_vnum != NOT_FOUND )
            reset->arg1 = new_vnum;
      }
      return;
   }

   for( i = 0; action_table[i] != NULL; i++ )
   {
      if( reset->command == action_table[i][0] )
      {
         p = action_table[i] + 1;
         while( *p )
         {
            if( *p == 'm' )
               r_table = r_data->r_mob;
            else if( *p == 'o' )
               r_table = r_data->r_obj;
            else if( *p == 'r' )
               r_table = r_data->r_room;
            else
            {
               bug( "translate_reset: Invalid action found in action table.\r\n" );
               p += 2;
               continue;
            }
            p++;

            if( *p == '1' )
               parg = &( reset->arg1 );
            else if( *p == '2' )
               parg = &( reset->arg2 );
            else if( *p == '3' )
               parg = &( reset->arg3 );
            else
            {
               bug( "translate_reset: Invalid argument number found in action table.\r\n" );
               p++;
               continue;
            }
            p++;

            new_vnum = find_translation( *parg, r_table );
            if( new_vnum != NOT_FOUND )
               *parg = new_vnum;

         }
         return;
      }
   }

   if( action_table[i] == NULL )
      bug( "translate_reset: Invalid reset '%c' found.\r\n", reset->command );
}

int find_translation( int vnum, RENUMBER_DATA * r_data )
/* returns the new vnum for the old vnum "vnum" according to the info in
 * r_data 
 */
{
   RENUMBER_DATA *r_temp;

   for( r_temp = r_data; r_temp; r_temp = r_temp->next )
   {
      if( r_temp->old_vnum == vnum )
         return r_temp->new_vnum;
   }
   return NOT_FOUND;
}


AREA_DATA *find_area( char *filename, bool * p_is_proto )
/* simply returns a pointer to a "filename" or NULL if no such area. stores
   TRUE in *p_is_proto if the area is proto */
{
   bool found;
   AREA_DATA *area;

   found = FALSE;

   for( area = first_area; area; area = area->next )
   {
      if( !str_cmp( area->filename, filename ) )
      {
         found = TRUE;
         break;
      }
   }

   if( found )
   {
      *p_is_proto = FALSE;
      return area;
   }

   for( area = first_build; area; area = area->next )
   {
      if( !str_cmp( area->filename, filename ) )
      {
         found = TRUE;
         break;
      }
   }

   if( found )
   {
      *p_is_proto = TRUE;
      return area;
   }
   else
      return NULL;
}
