/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

objsave.c				Object saving to/loading from
					disk.  Force saves and normal
					(cryo) saves.  Houses, players,
					etc.  No longer uses binary files
					btw, now strictly text.

		******** Heavily modified and expanded ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** Heavily modified and expanded ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
/* 
   RoA, totally overhauled object save code, saves in text file now
   easier on the conversions we'll be going thru and have gone thru...
   easier to add stuff to objects now, without having to convert obect files..
   no rent code crap, no 30 day limit anymore, no nothing, but saves yer damn
   obects to disk, thats it, thats all :)
   oh yeah, loads every object off a proto save for a FEW things that are
   written to disk, like object hits left on the object and what not
   all the autowear stuff is also saved to disk, loads off the PROTO now
   so everytime a character logs in, their objects will be up to date
   with the current versions of the protos, no more having to sift thru
   every object file and pull out of whack objects 

   of course, this has it's disadvantages as well, but the benes won here

   -jtrhone
*/

#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "comm.h"
#include "handler.h"
#include "db.h"
#include "interpreter.h"
#include "acmd.h"
#include "utils.h"
#include "magic.h"
#include "objsave.h"
#include "lists.h"
#include "nature.h"
#include "darkenelf.h"

// defines
#define NAME(x) ((temp = get_name_by_id(x)) == NULL ? "<UNDEF>" : temp)

// protos
extern char *get_name_by_id(long id);

// global vars
extern struct str_app_type str_app[];
extern char *inst_names[];

BOOL  delete_object_file(chdata *ch)
{
   char	filename[50];

   if (IS_NPC(ch))
     return FALSE;

   if (!get_pdata_filename(GET_NAME(ch), "roa", filename))
      return FALSE;

   if (unlink(filename) < 0) {
     if (errno != ENOENT) {	/* if it fails, NOT because of no file */
       sprintf(buf1, "SYSERR: deleting object file %s (2)", filename);
       perror(buf1);
     }
   }

   return(TRUE);
}

/* hack way to delete player reimb file */
BOOL  delete_reimb_file(chdata *ch)
{
   char	filename[50];

   if (IS_NPC(ch))
     return FALSE;

   if (!get_pdata_filename(GET_NAME(ch), "rmb", filename))
      return FALSE;

   if (unlink(filename) < 0) {
     if (errno != ENOENT) {	/* if it fails, NOT because of no file */
       sprintf(buf1, "SYSERR: deleting reimb file %s (2)", filename);
       perror(buf1);
     }
   }
   return(TRUE);
}

void	listrent(chdata *ch, char *name) 
{
   FILE * fl;
   char	fname[MAX_INPUT_LENGTH], buf[20000];
   obdata *obj = NULL;
   int tmp;

   if (!get_pdata_filename(name, "roa", fname))
   {
      send_to_char("Object file unlocatable.\n\r",ch);
      return;
   }

   if (!(fl = fopen(fname, "rt"))) {
      sprintf(buf, "%s has no object file.\n\r", name);
      S2C();
      return;
   }

   sprintf(buf, "%%5%s%%0\n\r", fname);
   while (!feof(fl)) 
   {
     if(fscanf(fl, "%d", &tmp) != 1)
	break;
     obj = read_object(tmp, VIRTUAL);
     if (obj)
     {
       sprintf(buf2, "  [%%b%d%%0] %s\n\r",tmp,obj->shdesc);
       strcat(buf, buf2);
       extract_obj(obj);
     }
     fgets(buf1, 80, fl); /* skip the rest man */
   }
   fclose(fl);
   page_string(ch->desc, buf, 1);
}

void	listreimb(chdata *ch, char *name) 
{
   FILE * fl;
   char	fname[MAX_INPUT_LENGTH], buf[20000];
   obdata *obj = NULL;
   int tmp;

   if (!get_pdata_filename(name, "rmb", fname))
   {
      send_to_char("Reimbursement file unlocatable.\n\r",ch);
      return;
   }
   if (!(fl = fopen(fname, "rt"))) {
      sprintf(buf, "%s has no reimbursement file.\n\r", name);
      S2C();
      return;
   }

   sprintf(buf, "%%5%s%%0\n\r", fname);
   while (!feof(fl)) 
   {
     if(fscanf(fl, "%d", &tmp) != 1)
	break;
     obj = read_object(tmp, VIRTUAL);
     if (obj)
     {
       sprintf(buf2, "  [%%b%d%%0] %s\n\r",tmp,obj->shdesc);
       strcat(buf, buf2);
       extract_obj(obj);
     }
     fgets(buf1, 80, fl); /* skip the rest man */
   }
   fclose(fl);
   page_string(ch->desc, buf, 1);
}

BOOL	reimburse(chdata *ch) 
{
   FILE *fl;
   char	fname[MAX_INPUT_LENGTH];
   obdata *obj = NULL;
   int tmp;

   if (IS_NPC(ch)) return FALSE;

   if (!get_pdata_filename(GET_NAME(ch), "rmb", fname))
      return FALSE;

   if (!(fl = fopen(fname, "rt"))) 
      return FALSE;

   while (!feof(fl)) 
   {
     if(fscanf(fl, "%d", &tmp) != 1)
	break;
     obj = read_object(tmp, VIRTUAL);
     if (obj)
	obj_to_char(obj, ch);
     fgets(buf1, 80, fl); /* skip the rest man */
   }
   fclose(fl);

   if (unlink(fname) < 0) {
     if (errno != ENOENT) {	/* if it fails, NOT because of no file */
       sprintf(buf1, "SYSERR: deleting reimb file %s (3)", fname);
       perror(buf1);
     }
   }

   return TRUE;
}

void	write_reimb_file(chdata *ch) 
{
   FILE * fl;
   char	fname[MAX_INPUT_LENGTH];
   int j;

   if (IS_NPC(ch)) return;

   if (!get_pdata_filename(GET_NAME(ch), "rmb", fname))
      return;

   if (!(fl = fopen(fname, "wt"))) 
      return;

   if (!save_object(ch->carrying, fl)) {
      fclose(fl);
      return;
   }

   for (j = 0; j < MAX_WEAR; j++)
    if (EQ(ch, j) && !save_object(EQ(ch, j), fl)) 
    {
      fclose(fl);
      return;
    }
   fclose(fl);
}

void	extract_objects(obdata *obj)
{
   if (obj) {
      extract_objects(obj->contains);
      extract_objects(obj->next_content);
      extract_obj(obj);
   }
}

BOOL is_unrentable(obdata *obj)
{
   if (!obj)
      return FALSE;
   if (IS_SET(OBJ_EXTRAS(obj), ITEM_NORENT) || obj->item_number <= -1 || ITEM_TYPE(obj) == ITEM_KEY)
      return TRUE;
   return FALSE;
}

void	extract_norents(obdata *obj)
{
   if (obj) 
   {
     extract_norents(obj->contains);
     extract_norents(obj->next_content);
     if (is_unrentable(obj))
       extract_obj(obj);
   }
}

void	extract_cheapest(obdata *obj)
{
   obdata *tobj = NULL, *min = NULL;

   min = obj;
   for (tobj = obj; tobj; tobj = tobj->next_content)
      if ((tobj->cost < min->cost) && ITEM_TYPE(tobj) != ITEM_CONTAINER)
	 min = tobj;
   extract_obj(min);
}

/* Find all the unrentables inside an object */
BOOL find_unrentables(chdata *ch, obdata *obj)
{
   if (obj) 
   {
      if (is_unrentable(obj)) 
	return TRUE;
      if(find_unrentables(ch, obj->contains)) 
	return TRUE;
      if(find_unrentables(ch, obj->next_content)) 
	return TRUE;
   }
   return FALSE;
}

/* Test whether char has unrentables */
BOOL has_norents(chdata *ch)
{
  int i;

  for(i = 0;i < MAX_WEAR;i++)
   if(find_unrentables(ch, EQ(ch, i))) 
     return TRUE;

  if(find_unrentables(ch, ch->carrying)) 
    return TRUE;

  return FALSE;
}

// make a common routine to load obj from file  5/30/98 -jtrhone
void fill_obj_from_file(obdata *obj, FILE *fl)
{
  int tmp1, tmp2, tmp3, tmp4;
  char *temp;

  fscanf(fl, "%d %d %d %d %d %d %d %d %d %d %d %d\n",
	 &tmp1, &tmp2, &tmp3, &tmp4,
	 &obj->objsave_parent, &obj->tmp1, &obj->objsave_self,
	 &obj->tmp2, &obj->objsave_where, &obj->obj_hits,
	 &obj->owner_of, &obj->obj_id);
 
  // restore everything of these 4 values except weapons -roa
  if (!IS_WEAPON(obj))
  {
    obj->value[0] = tmp1;
    obj->value[1] = tmp2;
    obj->value[2] = tmp3;
    obj->value[3] = tmp4;
  }

  /* restore instrument name based on player who owns it */
  if (OBJ_FLAGGED(obj, ITEM_INSTRUM) && OBJ_ID(obj))
  {
    if (GET_OBJ_VNUM(obj) == 115)
    {
      sprintf(buf, "instrument inst %s", inst_names[OBJ_ID(obj)]);
      obj->name = STR_DUP(buf);

      strcpy(buf2, NAME(obj->owner_of));
      CAP(buf2);
      sprintf(buf, "%s's %s is lying here.", buf2, inst_names[OBJ_ID(obj)]);
      obj->description = STR_DUP(buf);

      sprintf(buf, "%s's %s", buf2, inst_names[OBJ_ID(obj)]);
      obj->shdesc = STR_DUP(buf);

      // hit up the actdesc too :)
      obj->actdesc = STR_DUP(buf);
    }
    else
    if (GET_OBJ_VNUM(obj) == 116)
    {
      sprintf(buf, "instrument inst magic magical %s", inst_names[OBJ_ID(obj)]);
      obj->name = STR_DUP(buf);
	
      strcpy(buf2, NAME(obj->owner_of));
      CAP(buf2);
      sprintf(buf, "%s's %%Bmagical%%0 %s is lying here.", buf2, inst_names[OBJ_ID(obj)]);
      obj->description = STR_DUP(buf);

      sprintf(buf, "%s's %%Bmagical%%0 %s", buf2, inst_names[OBJ_ID(obj)]);
      obj->shdesc = STR_DUP(buf);

      // hit up the actdesc too :)
      obj->actdesc = STR_DUP(buf);
    }
  }
}

// uses common fill obj from file  5/30/98 -jtrhone
BOOL	load_room_objects(int hvnum)
{
   FILE *fl;
   char	fname[MAX_STRING_LENGTH];
   obdata *obj = NULL;
   int tmp, hrnum;

   if (!(hrnum = real_room(hvnum)))
   {
      sprintf(buf, "Room #%d does not exist.", hvnum);
      mudlog(buf, NRM, LEV_IMM, TRUE);
      return FALSE;
   }

   sprintf(fname, "rmobjs/%d.roa", hvnum);
   if (!(fl = fopen(fname, "rt"))) 
   {
     if (errno != ENOENT)
     {
      sprintf(buf, "Room #%d object loading error.", hvnum);
      mudlog(buf, NRM, LEV_IMM, TRUE);
     }
     return FALSE;
   }

   /* read objects here, restore saved stats, give em to room */
   while (!feof(fl)) 
   {
      fscanf(fl, "%d", &tmp);
      if (!(obj = read_object(tmp, VIRTUAL)))
      {
	fgets(buf, 80, fl); /* skip this line */
	continue;
      }
      else  /* lets read this puppy */
      {
        fill_obj_from_file(obj, fl);
	obj_to_room(obj, hrnum);
      }
   }
   fclose(fl);
   return TRUE;
}

// uses common fill obj from file  5/30/98 -jtrhone
BOOL	load_house_objects(int hvnum)
{
   FILE *fl;
   char	fname[MAX_STRING_LENGTH];
   obdata *obj = NULL;
   int tmp, hrnum;

   if (!(hrnum = real_room(hvnum)))
   {
      sprintf(buf, "House #%d does not exist.", hvnum);
      mudlog(buf, NRM, LEV_IMM, TRUE);
      return FALSE;
   }

   sprintf(fname, "house/%d.house", hvnum);
   if (!(fl = fopen(fname, "rt"))) 
   {
     if (errno != ENOENT)
     {
      sprintf(buf, "House #%d object loading error.", hvnum);
      mudlog(buf, NRM, LEV_IMM, TRUE);
     }
     return FALSE;
   }

   /* read objects here, restore saved stats, give em to room */
   while (!feof(fl)) 
   {
      fscanf(fl, "%d", &tmp);
      if (!(obj = read_object(tmp, VIRTUAL)))
      {
	fgets(buf, 80, fl); /* skip this line */
	continue;
      }
      else  /* lets read this puppy */
      {
        fill_obj_from_file(obj, fl);
	obj_to_room(obj, hrnum);
      }
   }
   fclose(fl);
   return TRUE;
}

// updated name of instrument to use owner_of for name ref  5/29/98 -jtrhone
// uses common fill obj from file  5/30/98 -jtrhone
BOOL	load_char_objects(chdata *ch)
{
   FILE * fl;
   char	fname[MAX_STRING_LENGTH];
   obdata *obj = NULL;
   int tmp;

   if (!get_pdata_filename(GET_NAME(ch), "roa", fname))
      return FALSE;

   if (!(fl = fopen(fname, "rt"))) 
   {
      if (errno != ENOENT)  /* failed other than not found */
      send_to_char("\n\r********************* NOTICE *********************\n\r"
		   "There was a problem loading your objects from disk.\n\r"
                   "Notify an Immortal for assistance.\n\r", ch);
      sprintf(buf, "%s entering game with no equipment.", GET_NAME(ch));
      mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(ch)), TRUE);
      return FALSE;
   }

   if (PLR_FLAGGED(ch, PLR_CRYO))
     sprintf(buf1, "cryo-saved ");
   else
   if (PLR_FLAGGED(ch, PLR_FORCED))
     sprintf(buf1, "force-saved ");
   else
   if (PLR_FLAGGED(ch, PLR_CRASH))
     sprintf(buf1, "crash-saved ");
   else
     strcpy(buf1, "");

   sprintf(buf, "%s retrieving %sitems and entering game.", GET_NAME(ch), buf1);
   mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(ch)), TRUE);

   REMOVE_BIT(PLR_FLAGS(ch), PLR_CRYO | PLR_CRASH | PLR_FORCED);

   /* read objects here, restore saved stats, give em to character */
   while (!feof(fl)) 
   {
      fscanf(fl, "%d", &tmp);
      if (!(obj = read_object(tmp, VIRTUAL)))
      {
	fgets(buf, 80, fl); /* skip this line */
	continue;
      }
      else
      {
        fill_obj_from_file(obj, fl);
	obj_to_char(obj, ch);
      }
   }
   fclose(fl);
   return TRUE;
}

/* write an object list to disk along with everything in it */
/* uses recursion to save everything inside of everything else */
BOOL save_object(obdata *obj, FILE *fp)
{
  if (obj)
  {
   save_object(obj->contains, fp);
   save_object(obj->next_content, fp);

   /* now lets write out this object to disk */
   if (obj->item_number >= 0)  /* only write if a real object */
     fprintf(fp, "%d %d %d %d %d %d %d %d %d %d %d %d %d\n",
	     GET_OBJ_VNUM(obj), obj->value[0], obj->value[1], obj->value[2], obj->value[3],
	     obj->objsave_parent, obj->tmp1, obj->objsave_self, obj->tmp2,
	     obj->objsave_where, obj->obj_hits, obj->owner_of, obj->obj_id); 
  }
  return TRUE;
}

/* normal save here, but dont wax em */
void	save_char_objects(chdata *ch)
{
   char	buf[MAX_INPUT_LENGTH];
   FILE * fp;
   int j;

   if (IS_NPC(ch)) 
      return;

   if (check_num(ch) <= 0)
   {
     delete_object_file(ch);
     return;
   }

   reduce_num(ch);
   assign_inventory(ch);

   if (!get_pdata_filename(GET_NAME(ch), "roa", buf))
     return;

   if (!(fp = fopen(buf, "w")))
      return;

   if (!save_object(ch->carrying, fp)) 
   {
      fclose(fp);
      return;
   }

   for (j = 0; j < MAX_WEAR; j++)
    if (EQ(ch, j) && !save_object(EQ(ch, j), fp)) 
    {
      fclose(fp);
      return;
    }

   fclose(fp);
   REMOVE_BIT(PLR_FLAGS(ch), PLR_CRASH);
}

/* save everybody's objects to disk, and save players */
void  mortal_save(void)
{
  dsdata *d;
 
  Descriptors(d)
    if (D_CHECK(d) && IS_PC(d->character) && PLR_FLAGGED(d->character, PLR_CRASH)) 
    {
      save_char_objects(d->character);
      save_char(d->character, NOWHERE);
    }
}

/* now, save em to disk, then wax em and set cryo flag */
void	cryo_save(chdata *ch)
{
   char	buf[MAX_INPUT_LENGTH];
   int	j;
   FILE * fp;
   obdata *obj = NULL;

   if (IS_NPC(ch)) 
      return;

   if (check_num(ch) <= 0)
   {
     delete_object_file(ch);
     return;
   }

   // extract all norentables from inventory and equipment -roa
   if (has_norents(ch)) 
   {
     extract_norents(ch->carrying);
     for (j = 0; j < MAX_WEAR; j++)
       if ((obj = EQ(ch, j)))
       {
	 if (is_unrentable(obj))
	   obj = unequip_char(ch, j, FALSE);
         extract_norents(obj);
       }
   }

   if (check_num(ch) > MAX_RENT_S)
   {
     sprintf(buf,"Please reduce your inventory to %%6%d%%0 items.\n\r",MAX_RENT_S);
     send_to_char(buf, ch);
     sprintf(buf, "%%6Failure%%0 to do so will result in EQ %%6LOSS%%0.");
     send_to_char(buf, ch);
     return;
   }

   // assign inventory for autowear numbers
   assign_inventory(ch);

   // now remove all to inventory
   for (j = 0; j < MAX_WEAR; j++)
    if (EQ(ch, j))
     obj_to_char(unequip_char(ch, j, FALSE), ch);

   if (!get_pdata_filename(GET_NAME(ch), "roa", buf))
     return;

   if (!(fp = fopen(buf, "w")))
     return;

   if (!save_object(ch->carrying, fp)) 
   {
     fclose(fp);
     return;
   }
   fclose(fp);

   send_to_char("Objects saved.\n\r", ch);
   extract_objects(ch->carrying);
   REMOVE_BIT(PLR_FLAGS(ch), PLR_CRASH | PLR_FORCED);
   SET_BIT(PLR_FLAGS(ch), PLR_CRYO);
}

/* now, save em to disk, then wax em and set forced flag */
void	force_save(chdata *ch)
{
   char	buf[MAX_INPUT_LENGTH];
   int	j;
   FILE * fp;
   obdata *obj = NULL;

   if (IS_NPC(ch)) 
      return;

   if (check_num(ch) <= 0)
   {
     delete_object_file(ch);
     return;
   }

   // extract all norentables from inventory and equipment -roa
   if (has_norents(ch)) 
   {
     extract_norents(ch->carrying);
     for (j = 0; j < MAX_WEAR; j++)
       if ((obj = EQ(ch, j)))
       {
	 if (is_unrentable(obj))
	   obj = unequip_char(ch, j, FALSE);
         extract_norents(obj);
       }
   }

   if (!get_pdata_filename(GET_NAME(ch), "roa", buf))
     return;

   if (!(fp = fopen(buf, "w")))
     return;

   reduce_num(ch);  /* force to drop everything over 50 items */

   // assign inventory for autowear numbers
   assign_inventory(ch);

   for (j = 0; j < MAX_WEAR; j++)
    if (EQ(ch, j))
     obj_to_char(unequip_char(ch, j, FALSE), ch);

   if (!save_object(ch->carrying, fp)) 
   {
      fclose(fp);
      return;
   }
   fclose(fp);

   send_to_char("Objects force-saved.\n\r", ch);
   extract_objects(ch->carrying);
   REMOVE_BIT(PLR_FLAGS(ch), PLR_CRASH | PLR_CRYO);
   SET_BIT(PLR_FLAGS(ch), PLR_FORCED);
   sprintf(buf, "%s force-saved and extracted.", GET_NAME(ch));
   mudlog(buf, BRF, LEV_IMM, FALSE);
}

/* Procedures for automatic eq positions saving begin here - SL */
/* modified jtrhone -roa */
/* 
   assign_num - assigns a (unique) number to the object and
   all of its containees, returns the smallest number still unassigned 
   this_obj_id is the first avaliable number for assigning 
*/

int assign_num(chdata *ch, obdata *obj, int this_obj_id)
{
  int next_obj_id;
  obdata *ob = NULL;

  if (!ch || !obj) 
    return this_obj_id;
  
  /* (re) assign the object itself */
  obj->objsave_self    = this_obj_id;
  obj->objsave_parent  = -1;
  obj->objsave_where   = -1;

  next_obj_id = this_obj_id + 1;
  
  /* Assign containees */
  for(ob = obj->contains;ob;ob = ob->next_content) 
    next_obj_id = assign_num(ch, ob, next_obj_id);
  return next_obj_id;
}

/* Now, assign the numbers to all the objects in the char's inventory */
void assign_position_ids(chdata *ch)
{
  int current_id, position;
  obdata *ob = NULL;

  /* this is the FIRST object in restore list */
  current_id = 0;

  /* Assign values to eq slots and their containees */
  /* only IF there is an obj in particular eq slot */
  for(position = 0; position < MAX_WEAR; position++) 
    if (EQ(ch, position)) 
    {
      current_id = assign_num(ch, EQ(ch, position), current_id);
      EQ(ch, position)->objsave_where = position;
    }
  
  /* Assign values to everything else ch is carrying */
  for(ob = ch->carrying; ob; ob = ob->next_content)
    current_id = assign_num(ch, ob, current_id);
} 

/* Now, assign parent values to object and all of its containees */
void assign_parents(obdata *obj)
{
  obdata *ob = NULL;

  if (!obj) 
    return;

  /* let object know what object_id it is contained in (find parent) */
  if(obj->in_obj)
    obj->objsave_parent = (obj->in_obj)->objsave_self;

  for(ob = obj->contains; ob; ob = ob->next_content) 
    assign_parents(ob);
}

// now, assign parents to every obj in char eq and inventory
void assign_inventory(chdata *ch)
{
  int i;
  obdata *ob = NULL;

  /* give each object on char a unique position id number */
  assign_position_ids(ch);

  /* Now that the numbers are assigned, assign parents in EQ */
  for(i = 0; i < MAX_WEAR; i++)
    assign_parents(EQ(ch, i));

  /* and assign parents to the objects in inventory */
  for(ob = ch->carrying; ob;ob = ob->next_content)
    assign_parents(ob);
}

// OK, function to restore one object recursively... 
// restore an object and everything inside it... 
// added bounds check, tightened for loop  5/28/98 -jtrhone
void restore_obj_and_contents(chdata *ch, obdata *ob, obdata **ob_array)
{
  int id_count;
  obdata *tmp_obj = NULL;

  for (id_count = 0; (tmp_obj = *(ob_array + id_count)) && (id_count < OBJ_ARRAY_SIZE); id_count++) 
    if (tmp_obj->objsave_parent > -1 && tmp_obj->objsave_parent == ob->objsave_self) 
    {
      restore_obj_and_contents(ch, tmp_obj, ob_array);
      perform_silent_put(ch, tmp_obj, ob);
    }
}

// counts obj + contents + objs in list after it
int inv_count(obdata *obj)
{
   int cnt = 0;

   if (obj) 
   {
      cnt++;
      cnt+=inv_count(obj->contains);
      cnt+=inv_count(obj->next_content);
   }
   return cnt;
}

// adds up obj count of inventory and eq list on char
int check_num(chdata *ch)
{
   int cc = 0;
   int j;

   if (IS_NPC(ch)) 
   return 0;

   cc += inv_count(ch->carrying);

   for (j=0;j<MAX_WEAR; j++)
     cc += inv_count(EQ(ch, j));
   return cc;
}

// if more than MAX_RENT_S objs on char (eq & inv), force them to drop some stuff
// updated for new do_unequip  4/14/98 -jtrhone
void reduce_num(chdata *ch)
{
   obdata *obj = NULL;
   int i;

   if (IS_NPC(ch)) return;

   if ((i = check_num(ch)) <= MAX_RENT_S)  /* player is ok */
     return;

   sprintf(buf, "Player inventory being reduced: %s (%d items)", GET_NAME(ch), i);
   mudlog(buf, BRF, LEV_GOD, FALSE);

   /* place all equipment in player inventory */
   sprintf(buf, "all");
   do_unequip(ch, buf, 0, 0);

   send_to_char("%B%1Your inventory is being reduced.%0\n\r",ch);
   for (i = 0; (check_num(ch) > MAX_RENT_S) && (i < MAX_RENT_S); i++)
   {
     assert(ch->carrying);
     if (ch->in_room < 4)  /* not in the game, idle or what not */
       extract_cheapest(ch->carrying);
     else
     {
       obj = ch->carrying;
       act("%B%1WARNING%0: You have been forced to drop $p.", FALSE, ch, obj, 0, TO_CHAR);

       obj_from_char(obj);
       float_sink_object(obj, ch->in_room);  /* replaces obj_to_room  5/28/98 -jtrhone*/
     }
   }
}

ACMD(do_icount)
{
   int ic = 0;

   if (IS_NPC(ch)) 
   return;

   ic = check_num(ch);

   sprintf(buf,"Total of %%6%d%%0 items (weight: %%6%d%%0, max: %%6%d%%0).\n\r",
           ic, IS_CARRYING_W(ch), CAN_CARRY_W(ch));
   S2C();
 
   if (ic > MAX_RENT_S)
   {
     sprintf(buf,"%%1WARNING!%%0.  You have exceeded the MAX rent limit by %%6%d%%0 items.\n\r",
             (ic - MAX_RENT_S));
     S2C();
   }
}

/* the function to restore the inventory */
/* returns 0 if something's screwy, in which case nothing is done */
// tightened up function in anticipation of eq slot revamp  5/28/98 -jtrhone
int autowear(chdata *ch)
{
  obdata *objects[OBJ_ARRAY_SIZE];
  obdata *ob = NULL;
  int i;

  if (IS_NPC(ch)) 
    return 0;

  /* if obj didnt get assigned a unique # for some reason */
  for(ob = ch->carrying;ob;ob = ob->next_content) 
    if (ob->objsave_self < 0) 
      return FALSE;

  // wipe array memory
  memset(objects, 0, sizeof(obdata*) * OBJ_ARRAY_SIZE);
 
  // make sure they are within limits...
  reduce_num(ch);

  for(i = 0, ob = ch->carrying; ob; ob = ob->next_content) 
  {
    objects[i] = ob;
    i++;
  }

  /* start at first object slot */
  /* if they dont have a parent restore everything inside them */
  /* if they have parent, they will be restored by restore_obj_and_contents */
  for (i = 0; (ob = objects[i]) && i < OBJ_ARRAY_SIZE; i++)
    if (ob->objsave_parent < 0) 
      restore_obj_and_contents(ch, ob, objects);

  // Equip the char with any objects in the list 
  // that have an objsave_where position 
  for (i = 0; (ob = objects[i]) && i < OBJ_ARRAY_SIZE; i++)
    if (ob->objsave_where > -1 && ob->objsave_where < MAX_WEAR) 
    {
      obj_from_char(ob);
      if (!equip_char(ch, ob, (ob->objsave_where), FALSE))
      {
	sprintf(buf, "SYSINFO: %s autowear failure (%s).", GET_NAME(ch), ob->shdesc);
	mudlog(buf, BRF, LEV_IMM, FALSE);
        S2C();
  	return FALSE;
      }
    }

  return TRUE;
}