/
Archipelago/
Archipelago/doc/
Archipelago/lib/misc/
Archipelago/lib/plrobjs/
Archipelago/lib/plrobjs/P-T/
Archipelago/lib/world/mob/
Archipelago/lib/world/obj/
Archipelago/lib/world/shp/
Archipelago/lib/world/wld/
Archipelago/lib/world/zon/
Archipelago/slave/
/* ************************************************************************
*   File: objsave.c                                     Part of CircleMUD *
*  Usage: loading/saving player objects for rent and crash-save           *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Copyright (C) 1993 by the Trustees of the Johns Hopkins University     *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */
/* Archipelago changes by Alastair J. Neil Copyright (C) 1993, 94, 95, 96 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>

#include "structs.h"
#include "comm.h"
#include "handler.h"
#include "db.h"
#include "interpreter.h"
#include "utils.h"
#include "spells.h"
#include "screen.h"
/* these factors should be unique integers */
#define RENT_FACTOR 	2
#define CHEAP_FACTOR 	1
#define CRYO_FACTOR 	10

extern struct str_app_type str_app[];
extern struct room_data *world;
extern struct index_data *mob_index;
extern struct index_data *obj_index;
extern struct obj_data *obj_proto;
extern struct descriptor_data *descriptor_list;
extern struct player_index_element *player_table;
extern int	top_of_p_table;
extern int	min_rent_cost;

/* Extern functions */
ACMD(do_tell);
SPECIAL(receptionist);
SPECIAL(c_receptionist);
SPECIAL(cryogenicist);
void save_all_object_limit_data(void);
void process_equipment(struct char_data *ch);
void	silent_perform_wear(struct char_data *ch, struct obj_data *obj, int where);
int     find_eq_pos(struct char_data *ch, struct obj_data *obj, char *arg);
int     report_money_weight(int amount);
char    *report_cost(int gold);
int     save_zone(int zone);
int	save_mobiles(int zone);
int	save_objects(int zone);
int	save_rooms(int zone);
int     is_goditem(struct obj_data *j);
bool    has_corpse(struct obj_data *obj);
void    add_event(int plse, int event, int inf1, int inf2, int inf3
	       , int inf4, char *arg, void *subj, void *vict);
void   clear_timer(struct obj_data *object);
FILE *fd;

int	Crash_get_filename(char *orig_name, char *filename)
{
   char	*ptr, name[30];

   if (!*orig_name)
      return 0;

   strcpy(name, orig_name);
   for (ptr = name; *ptr; ptr++)
      *ptr = tolower(*ptr);
   
   switch (tolower(*name)) {
   case 'a': case 'b': case 'c': case 'd': case 'e':
      sprintf(filename, "plrobjs/A-E/%s.objs", name); break;
   case 'f': case 'g': case 'h': case 'i': case 'j':
      sprintf(filename, "plrobjs/F-J/%s.objs", name); break;
   case 'k': case 'l': case 'm': case 'n': case 'o':
      sprintf(filename, "plrobjs/K-O/%s.objs", name); break;
   case 'p': case 'q': case 'r': case 's': case 't':
      sprintf(filename, "plrobjs/P-T/%s.objs", name); break;
   case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
      sprintf(filename, "plrobjs/U-Z/%s.objs", name); break;
   default:
      sprintf(filename, "plrobjs/ZZZ/%s.objs", name); break;
   }
   
   return 1;
}

bool    has_corpse(struct obj_data *obj){
  bool found = FALSE;
  
  if (!obj)
    return(FALSE);
  if (GET_ITEM_TYPE(obj) == ITEM_CONTAINER && obj->obj_flags.value[3] == -1)
    found = TRUE;
  if (!found)
    found = has_corpse(obj->contains);
  if (!found)
    found = has_corpse(obj->next_content);
  return(found);
}

int	Crash_delete_file(char *name)
{
   char	filename[50];
   FILE * fl;

   if (!Crash_get_filename(name, filename))
      return 0;
   if (!(fl = fopen(filename, "rb"))) {
      if (errno != ENOENT) { /* if it fails but NOT because of no file */
         sprintf(buf1, "SYSERR: deleting crash file %s (1)", filename);
         perror(buf1);
      }
      return 0;
   }
   fclose(fl);

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

   return(1);
}


int	Crash_delete_crashfile(struct char_data *ch)
{
   char	fname[MAX_INPUT_LENGTH];
   struct rent_info rent;
   FILE * fl;

   if (!Crash_get_filename(GET_NAME(ch), fname))
      return 0;
   if (!(fl = fopen(fname, "rb"))) {
      if (errno != ENOENT) { /* if it fails, NOT because of no file */
         sprintf(buf1, "SYSERR: checking for crash file %s (3)", fname);
         perror(buf1);
      }
      return 0;
   }

   if (!feof(fl))
      fread(&rent, sizeof(struct rent_info), 1, fl);
   fclose(fl);

   if (rent.rentcode == RENT_CRASH)
      Crash_delete_file(GET_NAME(ch));

   return 1;
}


int	Crash_clean_file(char *name)
{
   char	fname[MAX_STRING_LENGTH], filetype[20];
   struct rent_info rent;
   extern int rent_file_timeout, crash_file_timeout;
   FILE * fl;

   if (!Crash_get_filename(name, fname))
      return 0;
   /* open for write so that permission problems will be flagged now,
      at boot time. */
   if (!(fl = fopen(fname, "r+b"))) {
      if (errno != ENOENT) { /* if it fails, NOT because of no file */
         sprintf(buf1, "SYSERR: OPENING OBJECT FILE %s (4)", fname);
         perror(buf1);
      }
      return 0;
   }

   if (!feof(fl))
      fread(&rent, sizeof(struct rent_info), 1, fl);
   fclose(fl);

   if ( (rent.rentcode == RENT_CRASH) || 
       (rent.rentcode == RENT_FORCED) || (rent.rentcode == RENT_TIMEDOUT) )  {
      if (rent.time < time(0) - (crash_file_timeout * SECS_PER_REAL_DAY)) {
	 Crash_delete_file(name);
	 switch (rent.rentcode) {
	 case RENT_CRASH   : strcpy(filetype, "crash"); break;
	 case RENT_FORCED  : strcpy(filetype, "forced rent"); break;
	 case RENT_TIMEDOUT: strcpy(filetype, "idlesave"); break;
	 default: strcpy(filetype, "UNKNOWN!"); break;
	 }
	 sprintf(buf, "    Deleting %s's %s file.", name, filetype);
	 logg(buf);
	 return 1;
      }

      /* Must retrieve rented items w/in 30 days */
   } else if (rent.rentcode == RENT_RENTED)
      if (rent.time < time(0) - (rent_file_timeout * SECS_PER_REAL_DAY)) {
	 Crash_delete_file(name);
	 sprintf(buf, "    Deleting %s's rent file.", name);
	 logg(buf);
	 return 1;
      }
   return(0);
}



void	update_obj_file(void) 
{
   int	i;

   for (i = 0; i <= top_of_p_table; i++)
      Crash_clean_file((player_table + i)->name);
   return;
}


struct obj_data	*Crash_obj2char(struct char_data *ch, void *objct, byte rent_ver) 
{
   struct obj_data *obj;
   int	j, ronum;
   if (rent_ver == 0){
       struct obj_file_elem_0 *object;
       object = (struct obj_file_elem_0 *) objct;
       
       if ((ronum = real_object(object->item_number)) > 0) {
	 obj = read_object(object->item_number, VIRTUAL, 1);
	 CREATE(obj->name, char, strlen(object->name) +1);
	 strcpy(obj->name,object->name);
	 CREATE(obj->description, char, strlen(object->description) +1);
	 strcpy(obj->description,object->description);
	 CREATE(obj->short_description, char, strlen(object->short_description) +1);
	 strcpy(obj->short_description,object->short_description);
	 CREATE(obj->action_description, char, strlen(object->action_description) +1);
	 strcpy(obj->action_description,object->action_description);
	 obj->obj_flags.value[0] = object->value[0];
	 obj->obj_flags.value[1] = object->value[1];
	 obj->obj_flags.value[2] = object->value[2];
	 obj->obj_flags.value[3] = object->value[3];
	 obj->obj_flags.value[4] = object->value[4];
	 obj->obj_flags.value[5] = object->value[5];      
	 obj->obj_flags.value[6] = object->value[6];
	 obj->obj_flags.value[7] = object->value[7];
	 if (IS_SET(obj->obj_flags.extra_flags, ITEM_TEST_ONLY))
	   SET_BIT(object->extra_flags, ITEM_TEST_ONLY);
	 obj->obj_flags.extra_flags = object->extra_flags;
	 obj->obj_flags.weight = object->weight;
	 obj->obj_flags.timer = object->timer;
	 obj->obj_flags2.bitvector_aff = 0;
	 for (j = 0; j < MAX_OBJ_AFFECT; j++)
	   obj->affected[j] = object->affected[j];
	 if (((obj->obj_flags.type_flag == ITEM_PHILTRE) ||
	      (obj->obj_flags.type_flag == ITEM_ROD) ||
	      (obj->obj_flags.type_flag == ITEM_CANTRIP)) &&
	     (obj->obj_flags.value[0] !=obj_proto[ronum].obj_flags.value[0]))
	   {
	     extract_obj(obj,0);
	     return(0);
	   }
	 if (!IS_GODITEM(obj) && is_goditem(obj))
	   SET_BIT(obj->obj_flags.extra_flags, ITEM_GODONLY); 
	 if (IS_GODITEM(obj) && (GET_LEVEL(ch) < LEVEL_BUILDER)){
	   extract_obj(obj,0);
	   return(0);}
	 obj_to_char(obj, ch, 1);
	 if (object->bag[0] >= 0)
	   silent_perform_wear(ch,obj,object->bag[0]);
	 return(obj);
       }
       else
	 return(0);
   }
   else if (rent_ver == 1){
     struct obj_file_elem_1 *object;
     object = (struct obj_file_elem_1 *) objct;
     if ((ronum = real_object(object->item_number)) > 0) {
       obj = read_object(object->item_number, VIRTUAL, 1);
       CREATE(obj->name, char, strlen(object->name) +1);
       strcpy(obj->name,object->name);
       CREATE(obj->description, char, strlen(object->description) +1);
       strcpy(obj->description,object->description);
       CREATE(obj->short_description, char, strlen(object->short_description) +1);
       strcpy(obj->short_description,object->short_description);
       CREATE(obj->action_description, char, strlen(object->action_description) +1);
       strcpy(obj->action_description,object->action_description);      
       obj->obj_flags.value[0] = object->value[0];
       obj->obj_flags.value[1] = object->value[1];
       obj->obj_flags.value[2] = object->value[2];
       obj->obj_flags.value[3] = object->value[3];
       obj->obj_flags.value[4] = object->value[4];
       obj->obj_flags.value[5] = object->value[5];      
       obj->obj_flags.value[6] = object->value[6];
       obj->obj_flags.value[7] = object->value[7];
       if (IS_SET(obj->obj_flags.extra_flags, ITEM_TEST_ONLY))
	 SET_BIT(object->extra_flags, ITEM_TEST_ONLY);	   
       obj->obj_flags.extra_flags = object->extra_flags;
       obj->obj_flags.weight = object->weight;
       obj->obj_flags.timer = object->timer;
       obj->obj_flags.bitvector = object->bitvector;
       
       for (j = 0; j < MAX_OBJ_AFFECT; j++)
	 obj->affected[j] = object->affected[j];
       obj->obj_flags2.light = object->light;
       obj->obj_flags2.no_use_timer = object->no_use_timer;
       obj->obj_flags2.aff_timer = object->aff_timer;
       if (object->aff_timer > 0)  
	 obj->obj_flags2.bitvector_aff =  obj->obj_flags.bitvector;
       add_event(1,EVENT_OBJTIMER,0,0,0,0,0,obj,0);
       if (((obj->obj_flags.type_flag == ITEM_PHILTRE) ||
	    (obj->obj_flags.type_flag == ITEM_ROD) ||
	    (obj->obj_flags.type_flag == ITEM_CANTRIP)) &&
	   (obj->obj_flags.value[0] !=obj_proto[ronum].obj_flags.value[0]))
	 {
	   extract_obj(obj,0);
	   return(0);
	 }
       if (!IS_GODITEM(obj) && is_goditem(obj))
	 SET_BIT(obj->obj_flags.extra_flags, ITEM_GODONLY); 
       if (IS_GODITEM(obj) && (GET_LEVEL(ch) < LEVEL_BUILDER)){
	 extract_obj(obj,0);
	 return(0);}
       obj_to_char(obj, ch, 1);
       if (object->bag[0] >= 0)
	 silent_perform_wear(ch,obj,object->bag[0]);
       return(obj);
     }
     else
       return(0);
   }
   else if (rent_ver == 2){
     struct obj_file_elem *object;
     object = (struct obj_file_elem *) objct;
     if ((ronum = real_object(object->item_number)) > 0) {
       obj = read_object(object->item_number, VIRTUAL, 1);
       CREATE(obj->name, char, strlen(object->name) +1);
       strcpy(obj->name,object->name);
       CREATE(obj->description, char, strlen(object->description) +1);
       strcpy(obj->description,object->description);
       CREATE(obj->short_description, char, strlen(object->short_description) +1);
       strcpy(obj->short_description,object->short_description);
       CREATE(obj->action_description, char, strlen(object->action_description) +1);
       strcpy(obj->action_description,object->action_description);      
       obj->obj_flags.value[0] = object->value[0];
       obj->obj_flags.value[1] = object->value[1];
       obj->obj_flags.value[2] = object->value[2];
       obj->obj_flags.value[3] = object->value[3];
       obj->obj_flags.value[4] = object->value[4];
       obj->obj_flags.value[5] = object->value[5];      
       obj->obj_flags.value[6] = object->value[6];
       obj->obj_flags.value[7] = object->value[7];
       if (IS_SET(obj->obj_flags.extra_flags, ITEM_TEST_ONLY))
	 SET_BIT(object->extra_flags, ITEM_TEST_ONLY);	   
       obj->obj_flags.extra_flags = object->extra_flags;
       obj->obj_flags.weight = object->weight;
       obj->obj_flags.timer = object->timer;
       obj->obj_flags.bitvector = object->bitvector;
       obj->obj_flags.bitvector = object->bitvector;
       obj->obj_flags.cost = object->cost;
       obj->obj_flags.cost_per_day = object->cost_per_day;	   
       
       for (j = 0; j < MAX_OBJ_AFFECT; j++)
	 obj->affected[j] = object->affected[j];
       obj->obj_flags2.light = object->light;
       obj->obj_flags2.no_use_timer = object->no_use_timer;
       obj->obj_flags2.aff_timer = object->aff_timer;
       if (object->aff_timer > 0)  
	 obj->obj_flags2.bitvector_aff =  obj->obj_flags.bitvector;
       add_event(1,EVENT_OBJTIMER,0,0,0,0,0,obj,0);
       if (((obj->obj_flags.type_flag == ITEM_PHILTRE) ||
	    (obj->obj_flags.type_flag == ITEM_ROD) ||
	    (obj->obj_flags.type_flag == ITEM_CANTRIP)) &&
	   (obj->obj_flags.value[0] !=obj_proto[ronum].obj_flags.value[0]))
	 {
	   extract_obj(obj,0);
	   return(0);
	 }
       if (!IS_GODITEM(obj) && is_goditem(obj))
	 SET_BIT(obj->obj_flags.extra_flags, ITEM_GODONLY); 
       if (IS_GODITEM(obj) && (GET_LEVEL(ch) < LEVEL_BUILDER)){
	 extract_obj(obj,0);
	 return(0);}
       obj_to_char(obj, ch, 1);
       if (object->bag[0] >= 0)
	 silent_perform_wear(ch,obj,object->bag[0]);
       return(obj);
     }
     else
       return(0);
   }
   
   return(obj);
}
void	Crash_listrent(struct char_data *ch, char *name) 
{
   FILE * fl;
   char	fname[MAX_INPUT_LENGTH], buf[MAX_STRING_LENGTH];
   char pref[40], pref2[40], *buffz, col[10];
   struct obj_data *obj;
   struct rent_info rent;
   int nobj=0;
   byte container=0, last_cont = 0;

   if (!Crash_get_filename(name, fname))
      return;
   if (!(fl = fopen(fname, "rb"))) {
      sprintf(buf, "%s has no rent file.\r\n", name);
      send_to_char(buf, ch);
      return;
   }
   sprintf(buf, "%s\r\n", fname);
   if (!feof(fl))
      fread(&rent, sizeof(struct rent_info), 1, fl);
   switch (rent.rentcode) {
   case RENT_RENTED  :  strcat(buf, "Rent\r\n"); break;
   case RENT_CRASH   :  strcat(buf, "Crash\r\n"); break;
   case RENT_CRYO    :  strcat(buf, "Cryo\r\n");  break;
   case RENT_TIMEDOUT:
   case RENT_FORCED  : strcat(buf, "TimedOut\r\n"); break;
   default	     : strcat(buf, "Undef\r\n"); break;
   }
   if (rent.version == 0){
     struct obj_file_elem_0 object;
       while (!feof(fl)) {
	   fread(&object, sizeof(struct obj_file_elem_0), 1, fl);
	   if (ferror(fl)) {
	       fclose(fl);
	       return;
	   }
	   if (!feof(fl))
	     if (real_object(object.item_number) > 0) {
	       obj = read_object(object.item_number, VIRTUAL,0);
	       if (obj->obj_flags.value[6] > 0)
		 sprintf(col,"%s", CCRED(ch, C_NRM));
	       else
		 sprintf(col,"%s", CCWHT(ch, C_NRM));	       
	       sprintf(buf,"%s[%d] [%s%5d%s] (%s%5dau%s) %s%-20s%s\r\n",
		       buf,nobj,CCGRN(ch,C_NRM),
		       object.item_number,
		       CCNRM(ch,C_NRM),CCYEL(ch,C_NRM),
		       obj->obj_flags.cost_per_day,CCNRM(ch,C_NRM),
		       col,obj->short_description, CCNRM(ch,C_NRM));
	       extract_obj(obj,0);
	     }
	   nobj++;
       }
   }
   else if (rent.version == 1){
     struct obj_file_elem_1 object;
     while (!feof(fl)) {
       fread(&container, sizeof(byte),1,fl);
       fread(&object, sizeof(struct obj_file_elem_1), 1, fl);
       if (ferror(fl)) {
	 fclose(fl);
	 return;
       }
       bzero(pref,40);	   
       for(last_cont = 1; last_cont <= container; last_cont++)
	 sprintf(pref,"%s ", pref);
       bzero(pref2,40);
       if (container > 0)
	 sprintf(pref2,"%s%s>%s",CCBBLU(ch,C_NRM),pref,CCNRM(ch,C_NRM));
       else if (object.bag[0] >=0)
	 sprintf(pref2,"%s*%s",CCBBLU(ch,C_NRM),CCNRM(ch,C_NRM));
       else
	 sprintf(pref2,"%s"," ");
       if (!feof(fl))
	 if (real_object(object.item_number) > 0) {
	   obj = read_object(object.item_number, VIRTUAL,0);
	   if (obj->obj_flags.value[6] > 0)
	     sprintf(col,"%s", CCRED(ch, C_NRM));
	   else
	     sprintf(col,"%s", CCWHT(ch, C_NRM));	       
	   sprintf(buf,"%s[%3d]%s [%s%5d%s] (%s%5dau%s) %s%-20s%s\r\n",
		   buf,nobj,pref2,
		   CCGRN(ch,C_NRM),object.item_number
		   ,CCNRM(ch,C_NRM),CCBYEL(ch,C_NRM),
		   obj->obj_flags.cost_per_day,CCNRM(ch,C_NRM), col,
		   obj->short_description, CCNRM(ch,C_NRM));
	   extract_obj(obj,0);
	 }
       nobj++;
     }
   }
   else if (rent.version == 2){
     struct obj_file_elem object;
     while (!feof(fl)) {
       fread(&container, sizeof(byte),1,fl);
       fread(&object, sizeof(struct obj_file_elem), 1, fl);
       if (ferror(fl)) {
	 fclose(fl);
	 return;
       }
       bzero(pref,40);	   
       for(last_cont = 1; last_cont <= container; last_cont++)
	 sprintf(pref,"%s ", pref);
       bzero(pref2,40);
       if (container > 0)
	 sprintf(pref2,"%s%s>%s",CCBBLU(ch,C_NRM),pref,CCNRM(ch,C_NRM));
       else if (object.bag[0] >=0)
	 sprintf(pref2,"%s*%s",CCBBLU(ch,C_NRM),CCNRM(ch,C_NRM));
       else
	 sprintf(pref2,"%s"," ");
       if (!feof(fl))
	 if (real_object(object.item_number) > 0) {
	   obj = read_object(object.item_number, VIRTUAL,0);
	   if (obj->obj_flags.value[6] > 0)
	     sprintf(col,"%s", CCRED(ch, C_NRM));
	   else
	     sprintf(col,"%s", CCWHT(ch, C_NRM));	       
	   sprintf(buf,"%s[%3d]%s [%s%5d%s] (%s%5dau%s) %s%-20s%s\r\n",
		   buf,nobj,pref2,
		   CCGRN(ch,C_NRM),object.item_number
		   ,CCNRM(ch,C_NRM),CCBYEL(ch,C_NRM),
		   obj->obj_flags.cost_per_day,CCNRM(ch,C_NRM), col,
		   obj->short_description, CCNRM(ch,C_NRM));
	   extract_obj(obj,0);
	 }
       nobj++;
     }
   }
   fclose(fl);
   CREATE(buffz, char, strlen(buf) +1);
   strcpy(buffz, buf);
   page_string(ch->desc, buffz,1);
   free(buffz);
}



int	Crash_write_rentcode(struct char_data *ch, FILE *fl, struct rent_info *rent)
{
   if (fwrite(rent, sizeof(struct rent_info), 1, fl) < 1) {
      perror("Writing rent code.");
      return 0;
   }
   return 1;
}



int	Crash_load(struct char_data *ch)
/* return values:
	0 - successful load, keep char in rent room.
	1 - load failure or load of crash items -- put char in temple.
	2 - rented equipment lost (no $)
*/
{
   void	Crash_crashsave(struct char_data *ch);

   struct container_type {
       struct obj_data *bag;
       struct container_type *next;
   };
   
   FILE * fl;
   struct obj_data *tmp;
   char	fname[MAX_STRING_LENGTH];
   struct rent_info rent;
   int	cost,j, orig_rent_code,i, amount;
   float	num_of_days;
   struct container_type *cont=0, *tmp_cont, *tmpp_cont;
   byte container;
   byte last_contain;
   /* add money weight */

   add_weight_to_char(ch,report_money_weight(GET_GOLD(ch)));
   if (!Crash_get_filename(GET_NAME(ch), fname))
      return 1;
   if (!(fl = fopen(fname, "r+b"))) {
      if (errno != ENOENT) { /* if it fails, NOT because of no file */
         sprintf(buf1, "SYSERR: READING OBJECT FILE %s (5)", fname);
         perror(buf1);
	 send_to_char("\r\n********************* NOTICE *********************\r\n"
		      "There was a problem loading your objects from disk.\r\n"
                      "Contact a God for assistance.\r\n", ch);
      }
      sprintf(buf, "%s entering game with no equipment.", GET_NAME(ch));
      mudlog(buf, NRM, MAX(LEVEL_BUILDER, GET_INVIS_LEV(ch)), TRUE);
      return 1;
   }

   if (!feof(fl))
      fread(&rent, sizeof(struct rent_info), 1, fl);
   if (rent.rentcode == RENT_RENTED) {
      num_of_days = (float)(time(0) - rent.time) / SECS_PER_REAL_DAY;
      cost = (int)(rent.net_cost_per_diem * num_of_days);
      if (cost > GET_GOLD(ch) + GET_BANK_GOLD(ch)) {
	 sprintf(buf, "%s entering game, rented equipment lost (no $).",
	     GET_NAME(ch));
         mudlog(buf, NRM, MAX(LEVEL_BUILDER, GET_INVIS_LEV(ch)), TRUE);
	 if (rent.version == 0){
	   struct obj_file_elem_0 object;   
	     while (!feof(fl)) {
		 fread(&object, sizeof(struct obj_file_elem_0),1,fl);
		 if (ferror(fl)) {
		     perror("Reading crash file: Crash_load.");
		     fclose(fl);
		     return 1;
		 }
		 if (!feof(fl))
		     tmp = Crash_obj2char(ch, &object, rent.version);
	     }
	 }
	 else if (rent.version == 1){
	       struct obj_file_elem_1 object;
		 while (!feof(fl)) {
		     fread(&container, 1,1,fl);
		     fread(&object, sizeof(struct obj_file_elem_1), 1, fl);
		     if (ferror(fl)) {
		       perror("Reading crash file: Crash_load.");
		       fclose(fl);
		       return 1;
		     }
		     if (!feof(fl))
		       tmp = Crash_obj2char(ch, &object, rent.version);
		 }
	 }
	 else if (rent.version == 2){
	       struct obj_file_elem object;
		 while (!feof(fl)) {
		     fread(&container, 1,1,fl);
		     fread(&object, sizeof(struct obj_file_elem), 1, fl);
		     if (ferror(fl)) {
		       perror("Reading crash file: Crash_load.");
		       fclose(fl);
		       return 1;
		     }
		     if (!feof(fl))
		       tmp = Crash_obj2char(ch, &object, rent.version);
		 }
	 }
	 for (j=0;j< MAX_WEAR;j++)
	     if (ch->equipment[j])
		 obj_to_char(unequip_char(ch,j), ch,1);
	 
	 /* ok now remove all the stuff and extract it */
	 for (tmp = ch->inventory; tmp;tmp = tmp->next_content){
	     obj_from_char(tmp,0);
	     extract_obj(tmp,0);}
	 
	 Crash_crashsave(ch);
	 return 2;
      }
      else {
	if (cost > GET_GOLD(ch)){
	  cost -= GET_GOLD(ch);
	  amount = GET_GOLD(ch);
	  change_gold(ch, -amount);
	  GET_BANK_GOLD(ch) -= cost;
	}
	else
	  change_gold(ch, -cost);	  
	 save_char(ch, NOWHERE);
      }
   }
   
   switch (orig_rent_code = rent.rentcode) {
   case RENT_RENTED:
      sprintf(buf, "%s un-renting and entering game.", GET_NAME(ch));
      mudlog(buf, NRM, MAX(LEVEL_BUILDER, GET_INVIS_LEV(ch)), TRUE);
      break;
   case RENT_CRASH:
      sprintf(buf, "%s retrieving crash-saved items and entering game. Rent Version %d", GET_NAME(ch), rent.version);
      mudlog(buf, NRM, MAX(LEVEL_BUILDER, GET_INVIS_LEV(ch)), TRUE);
      break;
   case RENT_CRYO:
      sprintf(buf, "%s un-cryo'ing and entering game.", GET_NAME(ch));
      mudlog(buf, NRM, MAX(LEVEL_BUILDER, GET_INVIS_LEV(ch)), TRUE);
      break;
   case RENT_FORCED:
   case RENT_TIMEDOUT:
      sprintf(buf, "%s retrieving force-saved items and entering game.", GET_NAME(ch));
      mudlog(buf, NRM, MAX(LEVEL_BUILDER, GET_INVIS_LEV(ch)), TRUE);
      break;
   default:
      sprintf(buf, "WARNING: %s entering game with undefined rent code.", GET_NAME(ch));
      mudlog(buf, NRM, MAX(LEVEL_BUILDER, GET_INVIS_LEV(ch)), TRUE);
      break;
   }
   if (rent.version == 0){
     struct obj_file_elem_0 object;
       while (!feof(fl)) {
	   fread(&object, sizeof(struct obj_file_elem_0),1,fl);
	   if (ferror(fl)) {
	       perror("Reading crash file: Crash_load.");
	       fclose(fl);
	       return 1;
	   }
	   if (!feof(fl))
	       tmp = Crash_obj2char(ch, &object, rent.version);
       }
   }
   else if (rent.version == 1){
     struct obj_file_elem_1 object;
     while (!feof(fl)) {
       fread(&container, 1,1,fl);
       fread(&object, sizeof(struct obj_file_elem_1), 1, fl);
       if (ferror(fl)) {
	 perror("Reading crash file: Crash_load.");
	 fclose(fl);
	 return 1;
       }
       if ((last_contain == container +1) && cont) {
	 for (tmp_cont = cont;
	      (tmp_cont->next && tmp_cont->next->next);
	      tmp_cont = tmp_cont->next)
	   ;
	   {
	     tmpp_cont = tmp_cont->next;
	     free(tmpp_cont);
	     tmp_cont->next = 0;
	   }
       }
       last_contain = container;
       if ((container == 0) && cont){ 
	 for (tmp_cont = cont; tmp_cont;)
	   {
	     tmpp_cont = tmp_cont->next;
	     free(tmp_cont);
	     tmp_cont = tmpp_cont;
	   }
	 cont =0;
       }
       tmp = 0;
       if (!feof(fl))
	 tmp = Crash_obj2char(ch, &object, rent.version);
       if (tmp){
	 if ((GET_ITEM_TYPE(tmp) == ITEM_CONTAINER) ||
	     (GET_ITEM_TYPE(tmp) == ITEM_SCABBARD))
	   {
	     CREATE(tmp_cont, struct container_type,1);
	     tmp_cont->bag = tmp;
	     if (cont){
	       for (tmpp_cont = cont;
		    tmpp_cont->next;
		    tmpp_cont = tmpp_cont->next)
		 ;
	       tmpp_cont->next = tmp_cont;
	     }
	     else
	       cont = tmp_cont;
	   }
	 if (container > 0 && cont){
	   i = 1;
	   tmp_cont = cont;
	   while(i < container && tmp_cont)
	     {
	       tmp_cont = tmp_cont->next;
	       i++;
	     }
	   if (tmp_cont && tmp_cont->bag && tmp != tmp_cont->bag)
	     {
	       obj_from_char(tmp, 1);
	       clear_timer(tmp);
	       obj_to_obj(tmp, tmp_cont->bag); 
	     }
	 }
	 
       }
     }
   }
   else if (rent.version == 2){
     struct obj_file_elem object;
     while (!feof(fl)) {
       fread(&container, 1,1,fl);
       fread(&object, sizeof(struct obj_file_elem), 1, fl);
       if (ferror(fl)) {
	 perror("Reading crash file: Crash_load.");
	 fclose(fl);
	 return 1;
       }
       if (container == 0 && cont){ 
	 for (tmp_cont = cont; tmp_cont;)
	   {
	     tmpp_cont = tmp_cont->next;
	     free(tmp_cont);
	     tmp_cont = tmpp_cont;
	   }
	 cont =0;
       }
       tmp = 0;
       if (!feof(fl))
	 tmp = Crash_obj2char(ch, &object, rent.version);
       if (tmp){
	 if ((GET_ITEM_TYPE(tmp) == ITEM_CONTAINER) ||
	     (GET_ITEM_TYPE(tmp) == ITEM_SCABBARD))
	   {
	     CREATE(tmp_cont, struct container_type,1);
	     tmp_cont->bag = tmp;
	     if (cont){
	       for (tmpp_cont = cont;
		    tmpp_cont->next;
		    tmpp_cont = tmpp_cont->next)
		 ;
	       tmpp_cont->next = tmp_cont;
	     }
	     else
	       cont = tmp_cont;
	   }
	 if (container > 0 && cont){
	   i = 1;
	   tmp_cont = cont;
	   while(i < container && tmp_cont)
	     {
	       tmp_cont = tmp_cont->next;
	       i++;
	     }
	   if (tmp_cont && tmp_cont->bag && tmp != tmp_cont->bag)
	     {
	       obj_from_char(tmp, 1);
	       clear_timer(tmp);
	       obj_to_obj(tmp, tmp_cont->bag); 
	     }
	 }
	 
       }
     }
   }
   
   if (cont) {/* free the container list */
     for (tmp_cont = cont; tmp_cont;)
       {
	 tmpp_cont = tmp_cont->next;
	 free(tmp_cont);
	 tmp_cont = tmpp_cont;
       }
     cont =0;
   }
   
/*   process_equipment(ch); */

   /* turn this into a crash file by re-writing the control block */
   rent.rentcode = RENT_CRASH;
   rent.version = 2;
   rent.time = time(0);
   rewind(fl);
   Crash_write_rentcode(ch, fl, &rent);

   fclose(fl);

   if ((orig_rent_code == RENT_RENTED) || (orig_rent_code == RENT_CRYO))
      return 0;
   else
      return 1;
}




int	Crash_obj2store(struct obj_data *obj, int where, int container, struct char_data *ch, FILE *fl)
{
   int	j;
   struct obj_file_elem object;
   bzero(object.action_description,1920);
   strcpy(object.name,obj->name);
   strcpy(object.description,obj->description);
   strcpy(object.short_description,obj->short_description);
   if (obj->action_description && *(obj->action_description))
       strcpy(object.action_description,obj->action_description);
   object.item_number = obj_index[obj->item_number].virtual;
   object.light    = obj->obj_flags2.light;
   object.value[0] = obj->obj_flags.value[0];
   object.value[1] = obj->obj_flags.value[1];
   object.value[2] = obj->obj_flags.value[2];
   object.value[3] = obj->obj_flags.value[3];
   object.value[4] = obj->obj_flags.value[4];
   object.value[5] = obj->obj_flags.value[5];
   object.value[6] = obj->obj_flags.value[6];
   object.value[7] = obj->obj_flags.value[7];
   object.extra_flags = obj->obj_flags.extra_flags;
   object.weight = obj->obj_flags.weight;
   object.timer = obj->obj_flags.timer;
   object.aff_timer = obj->obj_flags2.aff_timer;
   object.no_use_timer = obj->obj_flags2.no_use_timer;   
   object.bitvector = obj->obj_flags.bitvector;
   object.cost = obj->obj_flags.cost;
   object.cost_per_day = obj->obj_flags.cost_per_day;   
   object.bag[0] = where;
   for (j = 0; j < MAX_OBJ_AFFECT; j++)
      object.affected[j] = obj->affected[j];
   
   if (fwrite(&container, 1, 1, fl) < 1) {
      perror("Writing crash data Crash_obj2store");
      return 0;
   }
      if (fwrite(&object, sizeof(struct obj_file_elem), 1, fl) < 1) {
      perror("Writing crash data Crash_obj2store");
      return 0;
   }

   return 1;
}


int	Crash_save(struct obj_data *obj,
		   int where,
		   struct char_data *ch,
		   FILE *fp)
{
  struct obj_data *tmp;
  int	result;
  byte container=0;
  
  save_all_object_limit_data();
  if (obj) {
    container = 0;
    if (!(IS_SET(obj->obj_flags.extra_flags, ITEM_NORENT) ||
	  IS_SET(obj->obj_flags.extra_flags, ITEM_TEST_ONLY))){
      for (tmp = obj->contains; tmp; tmp = tmp->next_content)
	 GET_OBJ_WEIGHT(obj) -= GET_OBJ_WEIGHT(tmp);
      result = Crash_obj2store(obj, where, container, ch, fp);
      for (tmp = obj->contains; tmp; tmp = tmp->next_content)
	GET_OBJ_WEIGHT(obj) += GET_OBJ_WEIGHT(tmp);
      if (!result) 
	return 0;
    }
    if (obj->contains){
      container++;
      Crash_save_container(obj->contains, ch, container, fp);
    }
    Crash_save(obj->next_content, -1, ch, fp);
  }
  return TRUE;
}

int	Crash_save_container(struct obj_data *obj, struct char_data *ch, byte container, FILE *fp)
{
    int	result;
    struct obj_data *tmp;
    bool saved=FALSE;
    
    if (obj) {
      if (!(IS_SET(obj->obj_flags.extra_flags, ITEM_NORENT) ||
	    IS_SET(obj->obj_flags.extra_flags, ITEM_TEST_ONLY))){
	for (tmp = obj->contains; tmp; tmp = tmp->next_content)
	  GET_OBJ_WEIGHT(obj) -= GET_OBJ_WEIGHT(tmp);
	result = Crash_obj2store(obj, -1, container, ch, fp);
	saved=TRUE;
	for (tmp = obj->contains; tmp; tmp = tmp->next_content)
	  GET_OBJ_WEIGHT(obj) += GET_OBJ_WEIGHT(tmp);
	if (!result) 
	  return 0;
      }
      if (obj->contains){
	if (saved){
	  container++;
	  Crash_save_container(obj->contains, ch, container,fp);
	  container--;
	}
	else
	  Crash_save_container(obj->contains, ch, container,fp);	  
      }
      if (obj->next_content)
	Crash_save_container(obj->next_content, ch, container,fp);
    }
    return TRUE;
}

void	Crash_extract_objs(struct obj_data *obj)
{
  if (obj) {
    Crash_extract_objs(obj->contains);
    Crash_extract_objs(obj->next_content);
    extract_obj(obj,1);
  }
}


int	Crash_is_unrentable(struct obj_data *obj)
{
   if (!obj)
      return 0;
   if (IS_SET(obj->obj_flags.extra_flags, ITEM_NORENT) ||
       IS_SET(obj->obj_flags.extra_flags, ITEM_TEST_ONLY) ||
       obj->obj_flags.cost_per_day < 0 || 
       obj->item_number <= -1 ||
       GET_ITEM_TYPE(obj) == ITEM_TRASH ||
       GET_ITEM_TYPE(obj) == ITEM_KEY )
      return 1;
   return 0;
}


void	Crash_extract_norents(struct char_data *ch, struct obj_data **obj)
{
  struct obj_data *tmp=0;
  
  if (*obj) {
    Crash_extract_norents(ch,&(*obj)->contains);
    Crash_extract_norents(ch,&(*obj)->next_content);
    if (Crash_is_unrentable(*obj)){
      if(ch)
	act("$p disintegrates.",TRUE,ch,*obj,0,TO_ROOM);
      extract_obj(*obj,0);
    }
  }
}

struct obj_data 	*find_expensive_cont(struct obj_data *obj, int mcost)
{
  struct obj_data *max=0, *tmpo, *tmax=0;
  
  for (tmpo = obj;tmpo; tmpo = tmpo->next_content){	
    if (tmpo->contains){
      if((tmax  = find_expensive_cont(tmpo->contains, mcost))){
	max = tmax;
	mcost = max->obj_flags.cost_per_day;}
    }
    else if (tmpo->obj_flags.cost_per_day > mcost){
      max = tmpo;
      mcost = max->obj_flags.cost_per_day;
    }
  }
  return(max);
}

struct obj_data 	*find_expensive(struct char_data *ch)
{
  struct obj_data *max=0, *tmpo, *tmax=0;
  int j, mcost = 0;
  
    for (tmpo=ch->inventory;tmpo; tmpo = tmpo->next_content){
      if (tmpo->contains){
	if ((tmax = find_expensive_cont(tmpo->contains, mcost))){
	  max = tmax;
	  mcost = max->obj_flags.cost_per_day;}
      }
      else if (tmpo->obj_flags.cost_per_day > mcost){
	max = tmpo;
	mcost = max->obj_flags.cost_per_day;
      }
    }
    for (j= 0;j < MAX_WEAR; j++){
      if (ch->equipment[j]){
	if (ch->equipment[j]->contains)
	  if ((tmax=find_expensive_cont(ch->equipment[j]->contains, mcost))){
	    max = tmax;
	    mcost = max->obj_flags.cost_per_day;}
	if (ch->equipment[j]->obj_flags.cost_per_day > mcost){
	  max = ch->equipment[j];
	  mcost = max->obj_flags.cost_per_day;
	}
      }
    }
    return(max);
}
void	extract_expensive_ch(struct char_data *ch)
{
  struct obj_data *max;
  int j;
  
  if ((max = find_expensive(ch))){
    if (max && max->worn_by)
      for (j=0;j<MAX_WEAR;j++)
	if (max == ch->equipment[j]){
	  max = unequip_char(ch,j);
	  extract_obj(max,0);
	  return;
	}
    if (max && max->in_obj){
      obj_from_obj(max);
      extract_obj(max,0);
      return;
    }
    obj_from_char(max,0);
    extract_obj(max,0);
  }
}



void	Crash_calculate_idlerent(struct obj_data *obj, int *cost)
{
  
  if (obj) {
    if (IS_LIMITED(obj))
      *cost += MAX(0, obj->obj_flags.cost_per_day/4);
    else
      *cost += MAX(0, obj->obj_flags.cost_per_day/20);
    Crash_calculate_idlerent(obj->contains, cost);
    Crash_calculate_idlerent(obj->next_content, cost);
  }
}

void	Crash_idlesave(struct char_data *ch)
{
  char	buf[MAX_INPUT_LENGTH];
  struct rent_info rent;
  struct obj_data *obj;
  int	j;
  bool has_eq= FALSE;
  int	cost;
  FILE * fp;
  
  if (IS_NPC(ch)) 
    return;
  
  if (!Crash_get_filename(GET_NAME(ch), buf))
    return;
  if (!(fp = fopen(buf, "wb")))
    return;
  cost = 0;
  for (j = 0; j < MAX_WEAR; j++)
    if (ch->equipment[j]){
      obj = unequip_char(ch, j);
      Crash_extract_norents(ch, &obj);
      if (obj){
	Crash_calculate_idlerent(obj, &cost);
	obj_to_char(obj, ch, 1);	      
	silent_perform_wear(ch, obj, j);
      }
       }
  Crash_extract_norents(ch, &ch->inventory);   
  Crash_calculate_idlerent(ch->inventory, &cost);
  if (cost < 10)
    cost = 0;
  while (cost && cost > (GET_GOLD(ch) + GET_BANK_GOLD(ch))){
    cost = 0;
    extract_expensive_ch(ch);
       for (j = 0; j < MAX_WEAR; j++)
	 if (ch->equipment[j]){
	   obj = unequip_char(ch, j);
	   Crash_calculate_idlerent(obj, &cost);
	   obj_to_char(obj, ch, 1);	      
	   silent_perform_wear(ch, obj, j);
	 }
       Crash_calculate_idlerent(ch->inventory, &cost);
       if (cost < 10)
	 cost = 0;
  }
   
  if (!ch->inventory) {
    for (j= 0;j<MAX_WEAR;j++)
      if (ch->equipment[j]){
	has_eq = TRUE;
	break;
      }
    if (!has_eq){
      fclose(fp);
      Crash_delete_file(GET_NAME(ch));
      return;
    }
   }
  
  rent.net_cost_per_diem = cost;
  rent.version = 2;
  rent.rentcode = RENT_TIMEDOUT;
  rent.time = time(0);
  rent.gold = GET_GOLD(ch);
  rent.account = GET_BANK_GOLD(ch);
  if (!Crash_write_rentcode(ch, fp, &rent)) {
    fclose(fp);
      return;
  }
  
  if (!Crash_save(ch->inventory,-1, ch, fp)) {
    fclose(fp);
    return;
  }
  
  for (j = 0; j < MAX_WEAR; j++)
    if (ch->equipment[j]) {
      if (!Crash_save(ch->equipment[j],j, ch, fp)) {
	fclose(fp);
	return;
      }
      obj = unequip_char(ch,j);
      obj_to_char(obj, ch, 1);
      
    }
  
  
   Crash_extract_objs(ch->inventory);
   fclose(fp);
}

void	Crash_crashsave(struct char_data *ch)
{
  char	buf[MAX_INPUT_LENGTH];
  struct rent_info rent;
  int	j;
  FILE * fp;
  
  if (IS_NPC(ch)) 
    return;
  if (!Crash_get_filename(GET_NAME(ch), buf)){
    sprintf(buf,"Crash_get_filename failed for char: %s",GET_NAME(ch));
    mudlog(buf, CMP, MAX(LEVEL_IMPL, GET_LEVEL(ch)+1), TRUE);
    return;
  }
  if (!(fp = fopen(buf, "wb")))
    return;
  
  rent.rentcode = RENT_CRASH;
  rent.version = 2;
  rent.time = time(0);
  if (!Crash_write_rentcode(ch, fp, &rent)) {
    fclose(fp);
    return;
  }
  if (!Crash_save(ch->inventory, -1, ch, fp)) {
    fclose(fp);
    return;
  }
  for (j = 0; j < MAX_WEAR; j++)
    if (ch->equipment[j]) {
      if (!Crash_save(ch->equipment[j],j, ch, fp)) {
	fclose(fp);
	return;
      }
    }
  
  fclose(fp);
  REMOVE_BIT(PLR_FLAGS(ch), PLR_CRASH);
}




void	Crash_rentsave(struct char_data *ch, int cost)
{
  char	buf[MAX_INPUT_LENGTH];
  struct rent_info rent;
  struct obj_data *obj;
  int	j;
  FILE * fp;
  
  if (IS_NPC(ch)) 
    return;
  
  if (!Crash_get_filename(GET_NAME(ch), buf))
    return;
  if (!(fp = fopen(buf, "wb")))
    return;
  
  for (j = 0; j < MAX_WEAR; j++)
    if (ch->equipment[j]){
      obj = unequip_char(ch, j);
      Crash_extract_norents(ch, &obj);
      if (obj){
	obj_to_char(obj, ch, 1);		      
	silent_perform_wear(ch, obj, j);
      }
    }
  Crash_extract_norents(ch, &ch->inventory);
  rent.version = 2;
  rent.net_cost_per_diem = cost;
  rent.rentcode = RENT_RENTED;
  rent.time = time(0);
  rent.gold = GET_GOLD(ch);
  rent.account = GET_BANK_GOLD(ch);
  if (!Crash_write_rentcode(ch, fp, &rent)) {
    fclose(fp);
    return;
  }
  if (!Crash_save(ch->inventory, -1, ch, fp)) {
    fclose(fp);
    return;
  }
  for (j = 0; j < MAX_WEAR; j++)
    if (ch->equipment[j]) {
      if (!Crash_save(ch->equipment[j], j, ch, fp)) {
	fclose(fp);
	return;
      }
      obj = unequip_char(ch, j);
      obj_to_char(obj, ch, 1);
    }
  fclose(fp);
  Crash_extract_objs(ch->inventory);
}


void	Crash_cryosave(struct char_data *ch, int cost)
{
  char	buf[MAX_INPUT_LENGTH];
  struct rent_info rent;
  struct obj_data *obj;
  int	j, amount;
  FILE * fp;
  
  if (IS_NPC(ch)) 
    return;
  
  if (!Crash_get_filename(GET_NAME(ch), buf))
    return;
  if (!(fp = fopen(buf, "wb")))
    return;
  
  for (j = 0; j < MAX_WEAR; j++)
    if (ch->equipment[j]){
      obj = unequip_char(ch, j);
      Crash_extract_norents(ch, &obj);
      if (obj){
	obj_to_char(obj, ch, 1);		      
	silent_perform_wear(ch, obj, j);
      }
    }
  Crash_extract_norents(ch, &ch->inventory);
   if (cost > GET_GOLD(ch)){
     cost -= GET_GOLD(ch);
     amount = GET_GOLD(ch);
     change_gold(ch, -amount);
     GET_BANK_GOLD(ch) -= cost;
   }
   else
     change_gold(ch, -1*cost);
   
   
   rent.version = 2;
   rent.rentcode = RENT_CRYO;
   rent.time = time(0);
   rent.gold = GET_GOLD(ch);
   rent.account = GET_BANK_GOLD(ch);
   rent.net_cost_per_diem = 0;
   if (!Crash_write_rentcode(ch, fp, &rent)) {
      fclose(fp);
      return;
   }
   if (!Crash_save(ch->inventory, -1,ch, fp)) {
     fclose(fp);
     return;
   }
   for (j = 0; j < MAX_WEAR; j++)
     if (ch->equipment[j]) {
       if (!Crash_save(ch->equipment[j],j, ch, fp)) {
	    fclose(fp);
	    return;
       }
       obj = unequip_char(ch, j);
       obj_to_char(obj, ch, 1);
     }
   fclose(fp);
   
   Crash_extract_objs(ch->inventory);
   SET_BIT(PLR_FLAGS(ch), PLR_CRYO);
}


/* ************************************************************************
* Routines used for the "Offer"                                           *
************************************************************************* */

int	Crash_report_unrentables(struct char_data *ch, struct char_data *recep,
struct obj_data *obj)
{
    int has_norents;
    
    if (obj) {
	if (Crash_is_unrentable(obj)) {
	    has_norents += 1;
	    sprintf(buf,"$n tells you, 'You cannot store %s.'", OBJS(obj, ch));
	    act(buf, FALSE, recep, 0, ch, TO_VICT); 
	}
	has_norents += Crash_report_unrentables(ch, recep, obj->contains);
	has_norents += Crash_report_unrentables(ch, recep, obj->next_content);
    }
    return(has_norents);
}



void	Crash_report_rent(struct char_data *ch, struct char_data *recep, 
struct obj_data *obj, long *cost, long *nitems, int display, int factor)
{
   static char	buf[256];

   if (obj) {
     if (!Crash_is_unrentable(obj)){
       (*nitems)++;
       if (!IS_LIMITED(obj)){
	 *cost += MAX(0, (obj->obj_flags.cost_per_day*factor)/40);
	 if (display) {
	   sprintf(buf, "$n tells you, '%s  for %s..'", 
		   report_cost((obj->obj_flags.cost_per_day*factor)/40), OBJS(obj, ch));
	   act(buf, FALSE, recep, 0, ch, TO_VICT);
	 }
       }
       else{
	 *cost += MAX(0, (obj->obj_flags.cost_per_day*factor)/8);
	 if (display) {
	   sprintf(buf, "$n tells you, '%s  for %s..'", 
		   report_cost((obj->obj_flags.cost_per_day*factor)/8), OBJS(obj, ch));
	   act(buf, FALSE, recep, 0, ch, TO_VICT);
	 }
       }
       
     }
     Crash_report_rent(ch, recep, obj->contains, cost, nitems, display, factor);
     Crash_report_rent(ch, recep, obj->next_content, cost, nitems, display, factor);
   }
}



int	Crash_offer_rent(struct char_data *ch, struct char_data *receptionist,
int display, int factor)
{
   extern int	max_obj_save;	/* change in config.c */
   char	buf[MAX_INPUT_LENGTH];
   int	i;
   long	totalcost = 0, numitems = 0, norent = 0, rent_deadline;

   norent = Crash_report_unrentables(ch, receptionist, ch->inventory);
   for (i = 0; i < MAX_WEAR; i++)
      norent += Crash_report_unrentables(ch, receptionist, ch->equipment[i]);

   totalcost = min_rent_cost * factor;

   Crash_report_rent(ch, receptionist, ch->inventory, &totalcost, &numitems, display, factor);

   for (i = 0; i < MAX_WEAR; i++)
      Crash_report_rent(ch, receptionist, ch->equipment[i], &totalcost, &numitems, display, factor);

   if (numitems > max_obj_save) {
      sprintf(buf, "$n tells you, 'Sorry, but I cannot store more than %d items.'",
          max_obj_save);
      act(buf, FALSE, receptionist, 0, ch, TO_VICT);
      return(0);
   }

   if (display) {
       if (min_rent_cost >0){
      sprintf(buf, "$n tells you, 'Plus, my %s fee..'",
          report_cost(min_rent_cost * factor));
      if (totalcost < 10)
	  totalcost = 0;
      act(buf, FALSE, receptionist, 0, ch, TO_VICT);}
      sprintf(buf, "$n tells you, 'For a total of, hmm, call it %s %s'",
          report_cost(totalcost), ((factor == RENT_FACTOR || factor == CHEAP_FACTOR) ? " per day" : ""));
      act(buf, FALSE, receptionist, 0, ch, TO_VICT);
       if (totalcost < 10)
	   totalcost = 0;
      if (totalcost > GET_GOLD(ch) + GET_BANK_GOLD(ch)) {
	 act("$n tells you, '...which I see you can't afford.'",
	     FALSE, receptionist, 0, ch, TO_VICT);
	 return(0);
      } else if (factor == RENT_FACTOR || factor == CHEAP_FACTOR) {
	 if (totalcost) {
	    rent_deadline = ((GET_GOLD(ch) + GET_BANK_GOLD(ch)) / (totalcost));
	    sprintf(buf, 
	        "$n tells you, 'You can rent for %ld day%s with the money"
	        " you have\r\non hand and in the bank.'\r\n",
	        rent_deadline, (rent_deadline > 1) ? "s" : "");
	 }
	 act(buf, FALSE, receptionist, 0, ch, TO_VICT);
      }
   }

   return(totalcost);
}



int	gen_receptionist(struct char_data *ch, int cmd, char *arg, int mode)
{
   int	cost = 0;
   struct char_data *recep = 0;
   struct char_data *tch;
   sh_int save_room;
   sh_int action_tabel[9] = { 23, 24, 36, 105, 106, 109, 111, 142, 147 };
   long	rent_deadline;

   ACMD(do_action);
   int	number(int from, int to);

   if (cmd <0)
       return(0);
   
   if ((!ch->desc) || IS_NPC(ch))
      return(FALSE);

   for (tch = world[ch->in_room].people; (tch) && (!recep); tch = tch->next_in_room)
      if (IS_MOB(tch) && ((mob_index[tch->nr].func == receptionist ||
			   mob_index[tch->nr].func == c_receptionist ||
			   mob_index[tch->nr].func == cryogenicist)))
	 recep = tch;
   if (!recep) {
      logg("SYSERR: Fubar'd receptionist.");
      exit(1);
   }

   if ((cmd != 92) && (cmd != 93)) {
      if (!number(0, 30))
	 do_action(recep, "", action_tabel[number(0,8)], 0);
      return(FALSE);
   }
   if (!AWAKE(recep)) {
      act("$N is unable to talk to you...",FALSE, ch,0,recep,TO_CHAR);
      return(TRUE);
   }
   if (!CAN_SEE(recep, ch) && GET_LEVEL(ch) < LEVEL_BUILDER) {
      act("$n says, 'I don't deal with people I can't see!'", FALSE, recep, 0, 0, TO_ROOM);
      return(TRUE);
   }

   if (cmd == 92) { /* Rent  */
     if (has_corpse(ch->inventory)){
       act("$n tells you, 'No-one keeps dead bodies in my inn.'", FALSE, recep, 0, ch, TO_VICT);
       return(TRUE);
     }
     cost = Crash_offer_rent(ch, recep, FALSE,  mode);
     if (cost == 0)
       cost++;
     if (mode == RENT_FACTOR || mode == CHEAP_FACTOR)
       sprintf(buf, "$n tells you, 'Rent will cost you %s per day.'", report_cost(cost));
     else if (mode == CRYO_FACTOR)
       sprintf(buf, "$n tells you, 'It will cost you %s to enter the monastery.'", report_cost(cost));
     act(buf, FALSE, recep, 0, ch, TO_VICT);
     if (cost < 10)
       cost = 0;
     if (cost > GET_GOLD(ch) + GET_BANK_GOLD(ch)) {
       act("$n tells you, '...which I see you can't afford.'",
	   FALSE, recep, 0, ch, TO_VICT);
       return(1);
     }
     
     if (cost && (mode == RENT_FACTOR)) {
       rent_deadline = ((GET_GOLD(ch) + GET_BANK_GOLD(ch)) / (cost));
       sprintf(buf, 
	       "$n tells you, 'You can rent for %ld day%s with the money you have\r\n"
	       "on hand and in the bank.'\r\n",
	       rent_deadline, (rent_deadline > 1) ? "s" : "");
       act(buf, FALSE, recep, 0, ch, TO_VICT);
     }
     
     if (mode == RENT_FACTOR || mode == CHEAP_FACTOR) {
       act("$n stores your belongings and helps you into your private chamber.",
	   FALSE, recep, 0, ch, TO_VICT);
       Crash_rentsave(ch, cost);
       sprintf(buf, "%s has rented (%d/day, %d tot.)", GET_NAME(ch),
	       cost, GET_GOLD(ch) + GET_BANK_GOLD(ch));
     } else { /* cryo */
       act("$n stores your belongings and shows you to your cell.\r\n"
	   "$n hands you a habit and a small oil lamp...\r\n",
	   FALSE, recep, 0, ch, TO_VICT);
       act("$n tells you, 'Vespers are at seven of the clock.'",
	   FALSE, recep, 0, ch, TO_VICT);
       Crash_cryosave(ch, cost);
       sprintf(buf, "%s has cryo-rented.", GET_NAME(ch));
       SET_BIT(PLR_FLAGS(ch), PLR_CRYO);
     }
     
     mudlog(buf, NRM, MAX(LEVEL_BUILDER, GET_INVIS_LEV(ch)), TRUE);
     act("$n helps $N into $S private chamber.", FALSE, recep, 0, ch, TO_NOTVICT);
     save_room = ch->in_room;
     extract_char(ch, FALSE);
     ch->in_room = world[save_room].number;
     save_char(ch, ch->in_room);
   } else {  /* Offer */
     if (has_corpse(ch->inventory)){
       act("$n tells you, 'No-one keeps dead bodies in my inn.'", FALSE, recep, 0, ch, TO_VICT);
       return(TRUE);
     }
     Crash_offer_rent(ch, recep, TRUE, mode);
     act("$N gives $n an offer.", FALSE, ch, 0, recep, TO_ROOM);
   }
   return(TRUE);
}


SPECIAL(receptionist)
{
   return(gen_receptionist(ch, cmd, arg, RENT_FACTOR));
}

SPECIAL(c_receptionist)
{
   return(gen_receptionist(ch, cmd, arg, CHEAP_FACTOR));
}


SPECIAL(cryogenicist)
{
   return(gen_receptionist(ch, cmd, arg, CRYO_FACTOR));
}


void	Crash_save_all(void)
{
  int n;
  struct descriptor_data *d;
  for (d = descriptor_list; d; d = d->next) {
    if ((d->connected == CON_PLYNG) && !IS_NPC(d->character)) {
      if (PLR_FLAGGED(d->character, PLR_CRASH)) {
	Crash_crashsave(d->character);
	save_char(d->character, NOWHERE);
	act("Auto-saving $n.",FALSE,d->character,0,0,TO_CHAR);
	REMOVE_BIT(PLR_FLAGS(d->character), PLR_CRASH);
      }
      if (IS_SET(d->character->specials2.builder_flag,BUILD_ZONE)
	  && d->character->specials2.edit_zone > 0
	  && !PLR_FLAGGED(d->character, PLR_BUILDING)){
	n = save_zone(d->character->specials2.edit_zone);
	sprintf(buf,"(%s) Autosaving %d zone resets for zone: %ld"
		,GET_NAME(d->character),n,d->character->specials2.edit_zone);
	mudlog(buf,NRM,MAX(LEVEL_BUILDER,GET_INVIS_LEV(d->character)),TRUE);
	REMOVE_BIT(d->character->specials2.builder_flag,BUILD_ZONE);
	 }
      if (IS_SET(d->character->specials2.builder_flag,BUILD_SHOPS)
	  && d->character->specials2.edit_zone > 0
	  && !PLR_FLAGGED(d->character, PLR_BUILDING)){
	n = save_objects(d->character->specials2.edit_zone);
	sprintf(buf,"(%s) Autosaving %d shops for zone: %ld"
		,GET_NAME(d->character),n,d->character->specials2.edit_zone);
	mudlog(buf, NRM,MAX(LEVEL_BUILDER,GET_INVIS_LEV(d->character)), TRUE); 
	REMOVE_BIT(d->character->specials2.builder_flag,BUILD_SHOPS);
      }
      if (IS_SET(d->character->specials2.builder_flag,BUILD_MOBS)
	  && d->character->specials2.edit_zone > 0
	  && !PLR_FLAGGED(d->character, PLR_BUILDING)){
	n = save_mobiles(d->character->specials2.edit_zone);
	sprintf(buf,"(%s) Autosaving %d mobiles for zone: %ld"
		,GET_NAME(d->character),n,d->character->specials2.edit_zone);
	mudlog(buf,NRM, MAX(LEVEL_BUILDER, GET_INVIS_LEV(d->character)), TRUE);
	     REMOVE_BIT(d->character->specials2.builder_flag,BUILD_MOBS);	 }
      if (IS_SET(d->character->specials2.builder_flag,BUILD_ROOMS)
	  && d->character->specials2.edit_zone > 0
	  && !PLR_FLAGGED(d->character, PLR_BUILDING)){
	n = save_rooms(d->character->specials2.edit_zone);
	sprintf(buf,"(%s) Autosaving %d rooms for zone: %ld"
		,GET_NAME(d->character),n,d->character->specials2.edit_zone);
	mudlog(buf, NRM, MAX(LEVEL_BUILDER,GET_INVIS_LEV(d->character)), TRUE);
	REMOVE_BIT(d->character->specials2.builder_flag,BUILD_ROOMS);
      }
    }
  }
}