circle-2.01/circle/
circle-2.01/circle/lib/boards/
circle-2.01/circle/lib/misc/
circle-2.01/circle/lib/plrobjs/
circle-2.01/circle/lib/text/
circle-2.01/circle/lib/world/shp/
/* ************************************************************************
*  file: objsave.c; module for crashsave, reception, etc.		  *
*  Usage: Procedures handling saving/loading of player objects            *
*  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
*                                                                         *
*  This code is part of CircleMUD.  Copyright (C) 1992, 1993 The Trustees *
*  of The Johns Hopkins University.                                       *
************************************************************************* */


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

#include "structs.h"
#include "comm.h"
#include "handler.h"
#include "db.h"
#include "interpreter.h"
#include "utils.h"
#include "spells.h"

/* these factors should be unique integers */
#define RENT_FACTOR 	1
#define CRYO_FACTOR 	4

#define MIN_RENT_COST 100

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 descriptor_data *descriptor_list;
extern struct player_index_element *player_table;
extern int	top_of_p_table;

/* Extern functions */
void	store_to_char(struct char_file_u *st, struct char_data *ch);
ACMD(do_tell);
SPECIAL(receptionist);
SPECIAL(cryogenicist);
int	str_cmp(char *arg1, char *arg2);
void	clear_char(struct char_data *ch);

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;
}



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

   if (!Crash_get_filename(name, filename))
      return 0;
   if (!(fl = fopen(filename, "rb")))
      return 0;
   fclose(fl);

   unlink(filename);

   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")))
      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;
   FILE * fl;

   if (!Crash_get_filename(name, fname))
      return 0;
   if (!(fl = fopen(fname, "rb")))
      return 0;

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

   /* Must retrieve crash-saved items w/in 10 days */
   if ( (rent.rentcode == RENT_CRASH) || 
       (rent.rentcode == RENT_FORCED) || (rent.rentcode == RENT_TIMEDOUT) )  {
      if (rent.time < time(0) - 864000) {
	 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);
	 log(buf);
	 return 1;
      }

      /* Must retrieve rented items w/in 30 days */
   } else if (rent.rentcode == RENT_RENTED)
      if (rent.time < time(0) - 2592000) {
	 Crash_delete_file(name);
	 sprintf(buf, "    Deleting %s's rent file.", name);
	 log(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;
}


void	Crash_obj2char(struct char_data *ch, struct obj_file_elem *object) 
{
   struct obj_data *obj;
   int	j;

   if (real_object(object->item_number) > -1) {
      obj = read_object(object->item_number, VIRTUAL);
      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.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_to_char(obj, ch);
   }
}


void	Crash_listrent(struct char_data *ch, char *name) 
{
   FILE * fl;
   char	fname[MAX_INPUT_LENGTH], buf[MAX_STRING_LENGTH];
   struct obj_file_elem object;
   struct obj_data *obj;
   struct rent_info rent;

   if (!Crash_get_filename(name, fname))
      return;
   if (!(fl = fopen(fname, "rb"))) {
      sprintf(buf, "%s has no rent file.\n\r", name);
      send_to_char(buf, ch);
      return;
   }
   sprintf(buf, "%s\n\r", fname);
   if (!feof(fl))
      fread(&rent, sizeof(struct rent_info), 1, fl);
   switch (rent.rentcode) {
   case RENT_RENTED: 
      strcat(buf, "Rent\n\r");     
      break;
   case RENT_CRASH:  
      strcat(buf, "Crash\n\r");    
      break;
   case RENT_CRYO:   
      strcat(buf, "Cryo\n\r");     
      break;
   case RENT_TIMEDOUT:
   case RENT_FORCED: 
      strcat(buf, "TimedOut\n\r"); 
      break;
   default:          
      strcat(buf, "Undef\n\r");    
      break;
   }
   while (!feof(fl)) {
      fread(&object, sizeof(struct obj_file_elem), 1, fl);
      if (ferror(fl)) {
	 fclose(fl);
	 return;
      }
      if (!feof(fl))
	 if (real_object(object.item_number) > -1) {
	    obj = read_object(object.item_number, VIRTUAL);
	    sprintf(buf, "%s [%5d] (%5dau) %-20s\n\r", buf,
	        object.item_number, obj->obj_flags.cost_per_day,
	        obj->short_description);
	 }
   }
   send_to_char(buf, ch);
   fclose(fl);
}




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);

   FILE * fl;
   char	fname[MAX_STRING_LENGTH];
   struct obj_file_elem object;
   struct rent_info rent;
   int	cost, orig_rent_code;
   float	num_of_days;

   if (!Crash_get_filename(GET_NAME(ch), fname))
      return 1;
   if (!(fl = fopen(fname, "r+b"))) {
      sprintf(buf, "%s entering game with no equipment.",
          GET_NAME(ch));
      syslog(buf, NRM, MAX(LEVEL_IMMORT, 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) / 86400;
      cost = (int)(rent.net_cost_per_diem * num_of_days);
      if (cost > GET_GOLD(ch) + GET_BANK_GOLD(ch)) {
	 fclose(fl);
	 sprintf(buf, "%s entering game, rented equipment lost (no $).",
	     GET_NAME(ch));
         syslog(buf, NRM, MAX(LEVEL_IMMORT, GET_INVIS_LEV(ch)), TRUE);
	 Crash_crashsave(ch);
	 return 2;
      } else {
	 GET_BANK_GOLD(ch) -= MAX(cost - GET_GOLD(ch), 0);
	 GET_GOLD(ch) = MAX(GET_GOLD(ch) - cost, 0);
	 save_char(ch, NOWHERE);
      }
   }

   switch (orig_rent_code = rent.rentcode) {
   case RENT_RENTED:
      sprintf(buf, "%s un-renting and entering game.", GET_NAME(ch));
      syslog(buf, NRM, MAX(LEVEL_IMMORT, GET_INVIS_LEV(ch)), TRUE);
      break;
   case RENT_CRASH:
      sprintf(buf, "%s retrieving crash-saved items and entering game.", GET_NAME(ch));
      syslog(buf, NRM, MAX(LEVEL_IMMORT, GET_INVIS_LEV(ch)), TRUE);
      break;
   case RENT_CRYO:
      sprintf(buf, "%s un-cryo'ing and entering game.", GET_NAME(ch));
      syslog(buf, NRM, MAX(LEVEL_IMMORT, 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));
      syslog(buf, NRM, MAX(LEVEL_IMMORT, GET_INVIS_LEV(ch)), TRUE);
      break;
   default:
      sprintf(buf, "WARNING: %s entering game with undefined rent code.", GET_NAME(ch));
      syslog(buf, NRM, MAX(LEVEL_IMMORT, GET_INVIS_LEV(ch)), TRUE);
      break;
   }

   while (!feof(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))
	 Crash_obj2char(ch, &object);
   }

   /* turn this into a crash file by re-writing the control block */
   rent.rentcode = RENT_CRASH;
   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, struct char_data *ch, FILE *fl)
{
   int	j;
   struct obj_file_elem object;

   object.item_number = obj_index[obj->item_number].virtual;
   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.extra_flags = obj->obj_flags.extra_flags;
   object.weight = obj->obj_flags.weight;
   object.timer = obj->obj_flags.timer;
   object.bitvector = obj->obj_flags.bitvector;
   for (j = 0; j < MAX_OBJ_AFFECT; j++)
      object.affected[j] = obj->affected[j];

   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, struct char_data *ch, FILE *fp)
{
   struct obj_data *tmp;
   int	result;

   if (obj) {
      Crash_save(obj->contains, ch, fp);
      Crash_save(obj->next_content, ch, fp);
      result = Crash_obj2store(obj, ch, fp);
      if (!result) {
	 return 0;
      }

      for (tmp = obj->in_obj; tmp; tmp = tmp->in_obj)
	 GET_OBJ_WEIGHT(tmp) -= GET_OBJ_WEIGHT(obj);
   }
   return TRUE;
}


void	Crash_restore_weight(struct obj_data *obj)
{
   if (obj) {
      Crash_restore_weight(obj->contains);
      Crash_restore_weight(obj->next_content);
      if (obj->in_obj)
	 GET_OBJ_WEIGHT(obj->in_obj) += GET_OBJ_WEIGHT(obj);
   }
}


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;
}


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


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


void	Crash_extract_norents(struct obj_data *obj)
{
   if (obj) {
      Crash_extract_norents(obj->contains);
      Crash_extract_norents(obj->next_content);
      if (Crash_is_unrentable(obj))
	 extract_obj(obj);
   }
}


void	Crash_extract_expensive(struct obj_data *obj)
{
   struct obj_data *tobj, *max;

   max = obj;
   for (tobj = obj; tobj; tobj = tobj->next_content)
      if (tobj->obj_flags.cost_per_day > max->obj_flags.cost_per_day)
	 max = tobj;
   extract_obj(max);
}




void	Crash_calculate_rent(struct obj_data *obj, int *cost)
{
   if (obj) {
      *cost += MAX(0, obj->obj_flags.cost_per_day >> 1);
      Crash_calculate_rent(obj->contains, cost);
      Crash_calculate_rent(obj->next_content, cost);
   }
}


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))
      return;
   if (!(fp = fopen(buf, "wb")))
      return;

   rent.rentcode = RENT_CRASH;
   rent.time = time(0);
   if (!Crash_write_rentcode(ch, fp, &rent)) {
      fclose(fp);
      return;
   }
   if (!Crash_save(ch->carrying, ch, fp)) {
      fclose(fp);
      return;
   }
   Crash_restore_weight(ch->carrying);

   for (j = 0; j < MAX_WEAR; j++)
      if (ch->equipment[j]) {
	 if (!Crash_save(ch->equipment[j], ch, fp)) {
	    fclose(fp);
	    return;
	 }
	 Crash_restore_weight(ch->equipment[j]);
      }

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


void	Crash_idlesave(struct char_data *ch)
{
   char	buf[MAX_INPUT_LENGTH];
   struct rent_info rent;
   int	j;
   int	cost;
   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_to_char(unequip_char(ch, j), ch);

   Crash_extract_norents(ch->carrying);

   cost = 0;
   Crash_calculate_rent(ch->carrying, &cost);
   cost += cost >> 2;
   while ((cost > GET_GOLD(ch) + GET_BANK_GOLD(ch)) && ch->carrying) {
      Crash_extract_expensive(ch->carrying);
      cost = 0;
      Crash_calculate_rent(ch->carrying, &cost);
      cost += cost >> 2;
   }

   if (!ch->carrying) {
      fclose(fp);
      Crash_delete_file(GET_NAME(ch));
      return;
   }

   rent.net_cost_per_diem = cost;

   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->carrying, ch, fp)) {
      fclose(fp);
      return;
   }
   fclose(fp);

   Crash_extract_objs(ch->carrying);
}


void	Crash_rentsave(struct char_data *ch, int cost)
{
   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))
      return;
   if (!(fp = fopen(buf, "wb")))
      return;

   for (j = 0; j < MAX_WEAR; j++)
      if (ch->equipment[j])
	 obj_to_char(unequip_char(ch, j), ch);

   Crash_extract_norents(ch->carrying);

   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->carrying, ch, fp)) {
      fclose(fp);
      return;
   }
   fclose(fp);

   Crash_extract_objs(ch->carrying);
}


void	Crash_cryosave(struct char_data *ch, int cost)
{
   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))
      return;
   if (!(fp = fopen(buf, "wb")))
      return;

   for (j = 0; j < MAX_WEAR; j++)
      if (ch->equipment[j])
	 obj_to_char(unequip_char(ch, j), ch);

   Crash_extract_norents(ch->carrying);

   GET_GOLD(ch) = MAX(0, GET_GOLD(ch) - cost);

   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->carrying, ch, fp)) {
      fclose(fp);
      return;
   }
   fclose(fp);

   Crash_extract_objs(ch->carrying);
   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)
{
   char	buf[128];
   int	has_norents = 0;

   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)++;
	 *cost += MAX(0, (obj->obj_flags.cost_per_day >> 1) * factor);
	 if (display) {
	    sprintf(buf, "$n tells you, '%5d coins for %s..'", 
	        ((obj->obj_flags.cost_per_day >> 1) * factor), 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;
   char	buf[MAX_INPUT_LENGTH];
   int	i;
   long	totalcost = 0, numitems = 0, norent = 0, rent_deadline;

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

   if (norent)
      return 0;

   totalcost = MIN_RENT_COST * factor;

   Crash_report_rent(ch, receptionist, ch->carrying, &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) {
      act("$n tells you, 'But you are not carrying anything!  Just quit!'",
          FALSE, receptionist, 0, ch, TO_VICT);
      return(0);
   }

   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) {
      sprintf(buf, "$n tells you, 'Plus, my %d coin fee..'",
          MIN_RENT_COST * factor);
      act(buf, FALSE, receptionist, 0, ch, TO_VICT);
      sprintf(buf, "$n tells you, 'For a total of %d coins%s.'",
          totalcost, (factor == RENT_FACTOR ? " per day" : ""));
      act(buf, FALSE, receptionist, 0, ch, TO_VICT);
      if (totalcost > GET_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) {
	 if (totalcost) {
	    rent_deadline = ((GET_GOLD(ch) + GET_BANK_GOLD(ch)) / (totalcost));
	    sprintf(buf, 
	        "$n tells you, 'You can rent for %d day%s with the gold"
	        " you have\n\r     on hand and in the bank.'\n\r",
	        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 ((!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 == cryogenicist)))
	 recep = tch;
   if (!recep) {
      log("Ingen 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)) {
      send_to_char("She is unable to talk to you...\n\r", ch);
      return(TRUE);
   }
   if (!CAN_SEE(recep, ch)) {
      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 (!(cost = Crash_offer_rent(ch, recep, FALSE, mode)))
	 return 1;
      if (mode == RENT_FACTOR)
	 sprintf(buf, "$n tells you, 'Rent will cost you %d gold coins per day.'", cost);
      else if (mode == CRYO_FACTOR)
	 sprintf(buf, "$n tells you, 'It will cost you %d gold coins to be frozen.'", cost);
      act(buf, FALSE, recep, 0, ch, TO_VICT);
      if (cost > GET_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 %d day%s with the gold you have\n\r"
	     "on hand and in the bank.'\n\r",
	     rent_deadline, (rent_deadline > 1) ? "s" : "");
	 act(buf, FALSE, recep, 0, ch, TO_VICT);
      }

      if (mode == RENT_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 helps you into your private chamber.\n\r"
	     "A white mist appears in the room, chilling you to the bone...\n\r"
	     "You begin to lose consciousness...",
	     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);
      }

      syslog(buf, NRM, MAX(LEVEL_IMMORT, 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);
      ch->in_room = world[save_room].number;
      save_char(ch, ch->in_room);
   } else {  /* Offer */
      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(cryogenicist)
{
   return(gen_receptionist(ch, cmd, arg, CRYO_FACTOR));
}


void	Crash_save_all(void)
{
   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);
	    REMOVE_BIT(PLR_FLAGS(d->character), PLR_CRASH);
	 }
      }
   }
}