AwakeMUD-0.8.18B/
AwakeMUD-0.8.18B/doc/
AwakeMUD-0.8.18B/lib/
AwakeMUD-0.8.18B/lib/etc/
AwakeMUD-0.8.18B/lib/etc/pfiles/
AwakeMUD-0.8.18B/lib/misc/
AwakeMUD-0.8.18B/lib/text/
AwakeMUD-0.8.18B/lib/text/help/
AwakeMUD-0.8.18B/lib/text/wizhelp/
AwakeMUD-0.8.18B/lib/veh/
AwakeMUD-0.8.18B/lib/world/
AwakeMUD-0.8.18B/lib/world/mob/
AwakeMUD-0.8.18B/lib/world/mtx/
AwakeMUD-0.8.18B/lib/world/qst/
AwakeMUD-0.8.18B/lib/world/shp/
AwakeMUD-0.8.18B/lib/world/veh/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <errno.h>


#if defined(WIN32) && !defined(__CYGWIN__)
#else
#include <unistd.h>
#endif

#include "structs.h"
#include "awake.h"
#include "comm.h"
#include "handler.h"
#include "db.h"
#include "newdb.h"
#include "interpreter.h"
#include "utils.h"
#include "house.h"
#include "memory.h"
#include "constants.h"
#include "file.h"
#include "vtable.h"

extern char *cleanup(char *dest, const char *src);
extern void ASSIGNMOB(long mob, SPECIAL(fname));
struct landlord *landlords = NULL;
ACMD(do_say);

struct life_data
{
  char *name;
  sh_int cost;
};

struct life_data lifestyle[] =
  {
    { "Low", 1
    },
    { "Middle", 3 },
    { "High", 10 },
    { "Luxury", 25 }
  };

/* First, the basics: finding the filename; loading/saving objects */

/* Return a filename given a house vnum */
bool House_get_filename(vnum_t vnum, char *filename)
{
  if (vnum == NOWHERE)
    return FALSE;

  sprintf(filename, "house/%ld.house", vnum);
  return TRUE;
}

/* Load all objects for a house */
bool House_load(struct house_control_rec *house)
{
  vnum_t room;
  File fl;
  char fname[MAX_STRING_LENGTH];
  rnum_t rnum;
  struct obj_data *obj = NULL, *last_obj = NULL;
  struct phone_data *k, *j;
  long vnum;
  int inside = 0, last_in = 0;

  room = house->vnum;
  if ((rnum = real_room(room)) == NOWHERE)
    return FALSE;
  if (!House_get_filename(room, fname))
    return FALSE;
  if (!(fl.Open(fname, "r+b"))) /* no file found */
    return FALSE;
  VTable data;
  data.Parse(&fl);
  fl.Close();
  for (int i = 0; i < MAX_GUESTS; i++)
  {
    sprintf(buf, "GUESTS/Guest%d", i);
    long p = data.GetLong(buf, 0);
    if (!playerDB.DoesExist(p))
      p = 0;
    house->guests[i] = p;
  }
  int num_objs = data.NumSubsections("HOUSE");
  int x;
  for (int i = 0; i < num_objs; i++)
  {
    const char *sect_name = data.GetIndexSection("HOUSE", i);
    sprintf(buf, "%s/Vnum", sect_name);
    vnum = data.GetLong(buf, 0);
    if ((obj = read_object(vnum, VIRTUAL))) {
      sprintf(buf, "%s/Name", sect_name);
      obj->restring = str_dup(data.GetString(buf, NULL));
      sprintf(buf, "%s/Photo", sect_name);
      obj->photo = str_dup(data.GetString(buf, NULL));
      for (x = 0; x < 10; x++) {
        sprintf(buf, "%s/Value %d", sect_name, x);
        GET_OBJ_VAL(obj, x) = data.GetInt(buf, GET_OBJ_VAL(obj, x));
      }
      if (GET_OBJ_TYPE(obj) == ITEM_PHONE && GET_OBJ_VAL(obj, 2)) {
        bool found = FALSE;
        sprintf(buf, "%d%d", GET_OBJ_VAL(obj, 0), GET_OBJ_VAL(obj, 1));
        for (j = phone_list; j; j = j->next)
          if (j->number == atoi(buf)) {
            found = TRUE;
            break;
          }
        if (!found) {
          k = new phone_data;
          k->number = atoi(buf);
          k->phone = obj;
          if (phone_list)
            k->next = phone_list;
          phone_list = k;
        }
      }
      sprintf(buf, "%s/Condition", sect_name);
      GET_OBJ_CONDITION(obj) = data.GetInt(buf, GET_OBJ_CONDITION(obj));
      sprintf(buf, "%s/Timer", sect_name);
      GET_OBJ_TIMER(obj) = data.GetInt(buf, GET_OBJ_TIMER(obj));
      sprintf(buf, "%s/Attempt", sect_name);
      GET_OBJ_ATTEMPT(obj) = data.GetInt(buf, 0);
      sprintf(buf, "%s/Cost", sect_name);
      GET_OBJ_COST(obj) = data.GetInt(buf, GET_OBJ_COST(obj));
      sprintf(buf, "%s/Inside", sect_name);
      inside = data.GetInt(buf, 0);
      if (inside > 0) {
        if (inside == last_in)
          last_obj = last_obj->in_obj;
        else if (inside < last_in)
          while (inside <= last_in) {
            last_obj = last_obj->in_obj;
            last_in--;
          }
        if (last_obj)
          obj_to_obj(obj, last_obj);
      } else
        obj_to_room(obj, rnum);

      last_in = inside;
      last_obj = obj;
    }
  }
  return TRUE;
}

void House_save(struct house_control_rec *house, FILE *fl, long rnum)
{
  int level = 0, o = 0;
  struct obj_data *obj = NULL;
  if (house)
  {
    obj = world[real_room(house->vnum)].contents;
    fprintf(fl, "[GUESTS]\n");
    for (;o < MAX_GUESTS; o++)
      fprintf(fl, "\tGuest%d:\t%ld\n", o, house->guests[o]);
  } else
  {
    obj = world[rnum].contents;
  }
  fprintf(fl, "[HOUSE]\n");
  o = 0;
  for (;obj;)
  {
    if (!IS_OBJ_STAT(obj, ITEM_NORENT) && GET_OBJ_TYPE(obj) != ITEM_KEY) {
      fprintf(fl, "\t[Object %d]\n", o);
      o++;
      fprintf(fl, "\t\tVnum:\t%ld\n", GET_OBJ_VNUM(obj));
      fprintf(fl, "\t\tInside:\t%d\n", level);
      if (GET_OBJ_TYPE(obj) == ITEM_PHONE)
        for (int x = 0; x < 4; x++)
          fprintf(fl, "\t\tValue %d:\t%d\n", x, GET_OBJ_VAL(obj, x));
      else if (GET_OBJ_TYPE(obj) != ITEM_WORN)
        for (int x = 0; x < 10; x++)
          fprintf(fl, "\t\tValue %d:\t%d\n", x, GET_OBJ_VAL(obj, x));
      fprintf(fl, "\t\tCondition:\t%d\n", GET_OBJ_CONDITION(obj));
      fprintf(fl, "\t\tTimer:\t%d\n", GET_OBJ_TIMER(obj));
      fprintf(fl, "\t\tAttempt:\t%d\n", GET_OBJ_ATTEMPT(obj));
      fprintf(fl, "\t\tCost:\t%d\n", GET_OBJ_COST(obj));
      fprintf(fl, "\t\tExtraFlags:\t%s\n", GET_OBJ_EXTRA(obj).ToString());
      if (obj->restring)
        fprintf(fl, "\t\tName:\t%s\n", obj->restring);
      if (obj->photo)
        fprintf(fl, "\t\tPhoto:$\n%s~\n", cleanup(buf2, obj->photo));
    }

    if (obj->contains && !IS_OBJ_STAT(obj, ITEM_NORENT) && GET_OBJ_TYPE(obj) != ITEM_PART) {
      obj = obj->contains;
      level++;
      continue;
    } else if (!obj->next_content && obj->in_obj)
      while (obj && !obj->next_content && level >= 0) {
        obj = obj->in_obj;
        level--;
      }

    if (obj)
      obj = obj->next_content;
  }
}

struct house_control_rec *find_house(vnum_t vnum)
{
  struct landlord *llord;
  struct house_control_rec *room;

  for (llord = landlords; llord; llord = llord->next)
    for (room = llord->rooms; room; room = room->next)
      if (room->vnum == vnum)
        return room;

  return NULL;
}

/* Save all objects in a house */
void House_crashsave(vnum_t vnum)
{
  int rnum;
  char buf[MAX_STRING_LENGTH];
  FILE *fp;

  if ((rnum = real_room(vnum)) == NOWHERE)
    return;
  if (!House_get_filename(vnum, buf))
    return;
  if (!(fp = fopen(buf, "wb"))) {
    perror("SYSERR: Error saving house file");
    return;
  }
  House_save(find_house(vnum), fp, 0);
  fclose(fp);
}

/* Delete a house save file */
void House_delete_file(vnum_t vnum)
{
  char buf[MAX_INPUT_LENGTH], fname[MAX_INPUT_LENGTH];
  FILE *fl;

  if (!House_get_filename(vnum, fname))
    return;
  if (!(fl = fopen(fname, "rb"))) {
    if (errno != ENOENT) {
      sprintf(buf, "SYSERR: Error deleting house file #%ld. (1)", vnum);
      perror(buf);
    }
    return;
  }
  fclose(fl);
  if (unlink(fname) < 0) {
    sprintf(buf, "SYSERR: Error deleting house file #%ld. (2)", vnum);
    perror(buf);
  }
}

/******************************************************************
 *  Functions for house administration (creation, deletion, etc.  *
 *****************************************************************/


/* Save the house control information */
void House_save_control(void)
{
  struct house_control_rec *room;
  struct landlord *llord = landlords;
  int i = 0;
  FILE *fl;
  if (!(fl = fopen(HCONTROL_FILE, "w+b"))) {
    perror("SYSERR: Unable to open house control file");
    return;
  }
  for (;llord; llord = llord->next)
    i++;
  fprintf(fl, "%d\n", i);
  for (llord = landlords; llord; llord = llord->next) {
    fprintf(fl, "%ld %s %d %d\n", llord->vnum, llord->race.ToString(), llord->basecost, llord->num_room);
    for (room = llord->rooms; room; room = room->next)
      fprintf(fl, "%ld %ld %d %d %s %ld 0 %ld\n", room->vnum, room->key, room->exit_num, room->mode,
              room->name, room->owner, room->owner ? room->date : 0);
  }

  fclose(fl);
}

struct house_control_rec *find_room(char *arg, struct house_control_rec *room, struct char_data *recep)
{
  for (; room; room = room->next)
    if (isname(arg, room->name))
      return room;
  do_say(recep, "Which room is that?", 0, 0);
  return NULL;
}

SPECIAL(landlord_spec)
{
  struct char_data *recep = (struct char_data *) me;
  struct landlord *lord;
  struct house_control_rec *room;
  char buf3[MAX_STRING_LENGTH];

  int i = 0;
  if (!(CMD_IS("list") || CMD_IS("retrieve") || CMD_IS("lease")
        || CMD_IS("leave") || CMD_IS("pay") || CMD_IS("status")))
    return FALSE;

  if (!CAN_SEE(recep, ch)) {
    do_say(recep, "I don't deal with people I can't see!", 0, 0);
    return TRUE;
  }
  for (lord = landlords; lord; lord = lord->next)
    if (GET_MOB_VNUM(recep) == lord->vnum)
      break;
  if (!lord)
    return FALSE;
  one_argument(argument, arg);

  if (CMD_IS("list")) {
    send_to_char(ch, "The following rooms are free: \r\n");
    send_to_char(ch, "Name     Class     Price      Name     Class     Price\r\n");
    send_to_char(ch, "-----    ------    ------     -----    ------    -----\r\n");
    for (room = lord->rooms; room; room = room->next)
      if (!room->owner) {
        if (!i) {
          sprintf(buf, "%-5s    %-6s    %-8d", room->name, lifestyle[room->mode].name,
                  lord->basecost * lifestyle[room->mode].cost);
          i++;
        } else {
          sprintf(buf2, "   %-5s    %-6s    %-8d\r\n", room->name, lifestyle[room->mode].name,
                  lord->basecost * lifestyle[room->mode].cost);
          strcat(buf, buf2);
          send_to_char(ch, buf);
          i = 0;
        }
      }
    if (i)
      strcat(buf, "\r\n\n");
    else
      sprintf(buf, "\r\n");
    send_to_char(ch, buf);
    return TRUE;
  } else if (CMD_IS("retrieve")) {
    if (!*arg) {
      do_say(recep, "Which room would you like the key to?", 0, 0);
      return TRUE;
    }
    room = find_room(arg, lord->rooms, recep);
    if (!room)
      do_say(recep, "Which room is that?", 0, 0);
    else if (room->owner != GET_IDNUM(ch))
      do_say(recep, "I would get fired if I did that.", 0, 0);
    else {
      do_say(recep, "Here you go...", 0, 0);
      struct obj_data *key = read_object(room->key, VIRTUAL);
      obj_to_char(key, ch);
    }
    return TRUE;
  } else if (CMD_IS("lease")) {
    if (!*arg) {
      do_say(recep, "Which room would you like to lease?", 0, 0);
      return TRUE;
    }
    room = find_room(arg, lord->rooms, recep);
    if (!room) {
      do_say(recep, "Which room is that?", 0, 0);
      return TRUE;
    } else if (room->owner) {
      do_say(recep, "Sorry, I'm afraid that room is already taken.", 0, 0);
      return TRUE;
    }
    int cost = lord->basecost * lifestyle[room->mode].cost;
    sprintf(buf3, "That will be %d nuyen.", cost);
    do_say(recep, buf3, 0, 0);
    if (GET_NUYEN(ch) < cost) {
      if (GET_BANK(ch) >= cost) {
        do_say(recep, "You don't have the money on you so I'll transfer it from your bank account.", 0, 0);
        GET_BANK(ch) -= cost;
      } else {
        do_say(recep, "Sorry, you don't have the required funds.", 0, 0);
        return TRUE;
      }
    } else {
      GET_NUYEN(ch) -= cost;
      send_to_char(ch, "You hand over the cash.\r\n");
    }
    do_say(recep, "Thank you, here is your key.", 0, 0);
    struct obj_data *key = read_object(room->key, VIRTUAL);
    obj_to_char(key, ch);
    room->owner = GET_IDNUM(ch);
    room->date = time(0) + (SECS_PER_REAL_DAY*30);
    ROOM_FLAGS(real_room(room->vnum)).SetBit(ROOM_HOUSE);
    ROOM_FLAGS(room->atrium).SetBit(ROOM_ATRIUM);
    House_crashsave(room->vnum);
    House_save_control();
    return TRUE;
  } else if (CMD_IS("leave")) {
    if (!*arg) {
      do_say(recep, "Which room would you like to leave?", 0, 0);
      return TRUE;
    }
    room = find_room(arg, lord->rooms, recep);
    if (!room)
      do_say(recep, "Which room is that?", 0, 0);
    else if (room->owner != GET_IDNUM(ch))
      do_say(recep, "I would get fired if I did that.", 0, 0);
    else {
      room->owner = 0;
      ROOM_FLAGS(real_room(room->vnum)).RemoveBit(ROOM_HOUSE);
      ROOM_FLAGS(room->atrium).RemoveBit(ROOM_ATRIUM);
      House_save_control();
      House_delete_file(room->vnum);
      do_say(recep, "I hope you enjoyed your time here.", 0, 0);
    }
    return TRUE;
  } else if (CMD_IS("pay")) {
    if (!*arg) {
      do_say(recep, "Which room would you like to pay the rent to?", 0, 0);
      return TRUE;
    }
    room = find_room(arg, lord->rooms, recep);
    if (!room)
      do_say(recep, "Which room is that?", 0, 0);
    else if (room->owner != GET_IDNUM(ch))
      do_say(recep, "But that isn't your room.", 0, 0);
    else {
      int cost = lord->basecost * lifestyle[room->mode].cost;
      sprintf(buf3, "That will be %d nuyen.", cost);
      do_say(recep, buf3, 0, 0);
      if (GET_NUYEN(ch) < cost) {
        if (GET_BANK(ch) >= cost) {
          do_say(recep, "You don't have the money on you so I'll transfer it from your bank account.", 0, 0);
          GET_BANK(ch) -= cost;
        } else {
          do_say(recep, "Sorry, you don't have the required funds.", 0, 0);
          return TRUE;
        }
      } else {
        GET_NUYEN(ch) -= cost;
        send_to_char(ch, "You hand over the cash.\r\n");
      }
      do_say(recep, "You are payed up for the next period.", 0, 0);
      room->date += SECS_PER_REAL_DAY*30;
      House_save_control();
    }
    return TRUE;
  } else if (CMD_IS("status")) {
    if (!*arg) {
      do_say(recep, "Which room would you like to check the rent on?", 0, 0);
      return TRUE;
    }
    room = find_room(arg, lord->rooms, recep);
    if (!room)
      do_say(recep, "Which room is that?", 0, 0);
    else if (room->owner != GET_IDNUM(ch))
      do_say(recep, "I'm afraid I can't release that information.", 0, 0);
    else {
      sprintf(buf2, "You are payed for another %d days.", (int)((room->date - time(0)) / 86400));
      do_say(recep, buf2, 0, 0);
    }
    return TRUE;
  }
  return FALSE;
}
/* call from boot_db - will load control recs, load objs, set atrium bits */
/* should do sanity checks on vnums & remove invalid records */
void House_boot(void)
{
  vnum_t real_house, owner, land, payed;
  FILE *fl;
  int num_land;
  char line[256], name[20];
  int t[4];
  struct house_control_rec *temp;
  struct landlord *templ = NULL, *lastl = NULL;
  if (!(fl = fopen(HCONTROL_FILE, "r+b"))) {
    log("House control file does not exist.");
    return;
  }
  if (!get_line(fl, line) || sscanf(line, "%d", &num_land) != 1) {
    log("Error at beginning of house control file.");
    return;
  }

  for (int i = 0; i < num_land; i++) {
    get_line(fl, line);
    if (sscanf(line, "%ld %s %d %d", &land, name, t, t + 1) != 4) {
      fprintf(stderr, "Format error in landlord #%d.\r\n", i);
      return;
    }
    ASSIGNMOB(land, landlord_spec);
    templ = new struct landlord;
    templ->vnum = land;
    templ->race.FromString(name);
    templ->basecost = t[0];
    templ->num_room = t[1];
    struct house_control_rec *last = NULL, *first = NULL;
    for (int x = 0; x < templ->num_room; x++) {
      get_line(fl, line);
      if (sscanf(line, "%ld %ld %d %d %s %ld %d %ld", &real_house, &land, t, t + 1, name,
                 &owner, t + 2, &payed) != 8) {
        fprintf(stderr, "Format error in landlord #%d room #%d.\r\n", i, x);
        return;
      }
      temp = new struct house_control_rec;
      temp->vnum = real_house;
      temp->key = land;
      temp->atrium = TOROOM(real_room(real_house), t[0]);
      temp->exit_num = t[0];
      temp->owner = owner;
      temp->date = payed;
      temp->mode = t[1];
      temp->name = str_dup(name);
      for (int q = 0; q < MAX_GUESTS; q++)
        temp->guests[q] = 0;
      if (temp->owner) {
        if (playerDB.DoesExist(temp->owner) && temp->date > time(0))
          House_load(temp);
        else {
          temp->owner = 0;
          House_delete_file(temp->vnum);
        }
      }
      if (last)
        last->next = temp;
      if (!first)
        first = temp;
      if (temp->owner) {
        ROOM_FLAGS(real_room(temp->vnum)).SetBit(ROOM_HOUSE);
        ROOM_FLAGS(temp->atrium).SetBit(ROOM_ATRIUM);
      }
      last = temp;
    }
    templ->rooms = first;
    templ->next = lastl;
    lastl = templ;
  }
  landlords = templ;

  fclose(fl);
  House_save_control();
}

/* "House Control" functions */

char *HCONTROL_FORMAT =
  "Usage:  hcontrol destroy <house vnum>\r\n"
  "       hcontrol show\r\n";

static inline const char *NAME(DBIndex::vnum_t id)
{
  const char *name = playerDB.GetNameV(id);

  return (name? name : "<UNDEF>");
}

void hcontrol_list_houses(struct char_data *ch)
{
  char *own_name;

  strcpy(buf, "Address  Atrium  Guests  Owner\r\n");
  strcat(buf, "-------  ------  ------  ------------\r\n");
  send_to_char(buf, ch);

  for (struct landlord *llord = landlords; llord; llord = llord->next)
    for (struct house_control_rec *house = llord->rooms; house; house = house->next)
      if (house->owner)
      {
        own_name = str_dup(playerDB.GetNameV(house->owner));
        if (!own_name)
          own_name = str_dup("<UNDEF>");
        sprintf(buf, "%7ld %7ld    0     %-12s\r\n",
                house->vnum, world[house->atrium].number, CAP(own_name));
        send_to_char(buf, ch);
      }
}

void hcontrol_destroy_house(struct char_data * ch, char *arg)
{
  struct house_control_rec *i = NULL;
  vnum_t real_atrium, real_house;

  if (!*arg)
  {
    send_to_char(HCONTROL_FORMAT, ch);
    return;
  }
  if ((i = find_house(atoi(arg))) == NULL)
  {
    send_to_char("Unknown house.\r\n", ch);
    return;
  }
  if ((real_atrium = real_room(i->atrium)) < 0)
    log("SYSERR: House had invalid atrium!");
  else
    ROOM_FLAGS(real_atrium).RemoveBit(ROOM_ATRIUM);

  if ((real_house = real_room(i->vnum)) < 0)
    log("SYSERR: House had invalid vnum!");
  else
  {
    ROOM_FLAGS(real_house).RemoveBit(ROOM_HOUSE);

    House_delete_file(i->vnum);
    i->owner = 0;
    send_to_char("House deleted.\r\n", ch);
    House_save_control();

    for (struct landlord *llord = landlords; llord; llord = llord->next)
      for (i = llord->rooms; i; i = i->next)
        if ((real_atrium = real_room(i->atrium)) >= 0) {
          ROOM_FLAGS(real_atrium).SetBit(ROOM_ATRIUM);
        }
  }
}


/* The hcontrol command itself, used by imms to create/destroy houses */
ACMD(do_hcontrol)
{
  char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];

  half_chop(argument, arg1, arg2);

  if (is_abbrev(arg1, "destroy"))
    hcontrol_destroy_house(ch, arg2);
  else if (is_abbrev(arg1, "show"))
    hcontrol_list_houses(ch);
  else
    send_to_char(HCONTROL_FORMAT, ch);
}


ACMD(do_decorate)
{
  extern void write_world_to_disk(int vnum);
  struct house_control_rec *i;
  if (!ROOM_FLAGGED(ch->in_room, ROOM_HOUSE))
    send_to_char("You must be in your house to set the description.\r\n", ch);
  else if ((i = find_house(GET_ROOM_VNUM(IN_ROOM(ch)))) == NULL)
    send_to_char("Um.. this house seems to be screwed up.\r\n", ch);
  else if (GET_IDNUM(ch) != i->owner)
    send_to_char("Only the primary owner can set the room description.\r\n", ch);
  else {
    PLR_FLAGS(ch).SetBit(PLR_WRITING);
    send_to_char("Enter your new room description.  Terminate with a @ on a new line.\r\n", ch);
    act("$n starts to move things around the room.", TRUE, ch, 0, 0, TO_ROOM);
    STATE(ch->desc) = CON_DECORATE;
    ch->desc->str = new (char *);
    *ch->desc->str = NULL;
    ch->desc->max_str = MAX_MESSAGE_LENGTH;
    ch->desc->mail_to = 0;
  }
}

/* The house command, used by mortal house owners to assign guests */
ACMD(do_house)
{
  struct house_control_rec *i = NULL;
  int j, id;

  one_argument(argument, arg);

  if (!ROOM_FLAGGED(IN_ROOM(ch), ROOM_HOUSE))
    send_to_char("You must be in your house to set guests.\r\n", ch);
  else if ((i = find_house(GET_ROOM_VNUM(IN_ROOM(ch)))) == NULL)
    send_to_char("Um.. this house seems to be screwed up.\r\n", ch);
  else if (GET_IDNUM(ch) != i->owner)
    send_to_char("Only the primary owner can set guests.\r\n", ch);
  else if (!*arg)
    House_list_guests(ch, i, FALSE);
  else if ((id = playerDB.GetID(arg)) < 0)
    send_to_char("No such player.\r\n", ch);
  else if (id == GET_IDNUM(ch))
    send_to_char("It's your house!\r\n", ch);
  else {
    for (j = 0; j < MAX_GUESTS; j++)
      if (i->guests[j] == id) {
        i->guests[j] = 0;
        House_crashsave(i->vnum);
        send_to_char("Guest deleted.\r\n", ch);
        return;
      }
    for (j = 0; j < MAX_GUESTS; j++)
      if (i->guests[j] == 0)
        break;
    if (j == MAX_GUESTS) {
      send_to_char("You have too many guests already.\r\n", ch);
      return;
    }
    i->guests[j] = id;
    House_crashsave(i->vnum);
    send_to_char("Guest added.\r\n", ch);
  }
}



/* Misc. administrative functions */


/* crash-save all the houses */
void House_save_all(void)
{
  struct landlord *llord;
  struct house_control_rec *room;
  rnum_t real_house;

  for (llord = landlords; llord; llord = llord->next)
    for (room = llord->rooms; room; room = room->next)
      if ((real_house = real_room(room->vnum)) != NOWHERE)
        if (room->owner)
          House_crashsave(room->vnum);
  FILE *fl;

  for (int x = 0; x <= top_of_world; x++)
    if (ROOM_FLAGGED(x, ROOM_STORAGE)) {
      sprintf(buf, "storage/%ld", world[x].number);
      if (!(fl = fopen(buf, "wb"))) {
        sprintf(buf2, "Cannot open %s. Aborting Storage Saving.", buf);
        mudlog(buf2, NULL, LOG_WIZLOG, TRUE);
        return;
      }
      House_save(NULL, fl, x);
      fclose(fl);
    }
}


/* note: arg passed must be house vnum, so there. */
bool House_can_enter(struct char_data *ch, vnum_t house)
{
  int j;
  struct house_control_rec *room = find_house(house);

  if (IS_NPC(ch) || !room)
    return FALSE;
  if (GET_LEVEL(ch) >= LVL_ADMIN)
    return TRUE;
  if (GET_IDNUM(ch) == room->owner)
    return TRUE;
  for (j = 0; j < MAX_GUESTS; j++)
    if (GET_IDNUM(ch) == room->guests[j])
      return TRUE;

  return FALSE;
}

void House_list_guests(struct char_data *ch, struct house_control_rec *i, int quiet)
{
  int j;
  char buf[MAX_STRING_LENGTH], buf2[MAX_NAME_LENGTH + 2];

  strcpy(buf, "  Guests: ");
  int x = 0;
  for (j = 0; j < MAX_GUESTS; j++)
  {
    const char *temp = playerDB.GetNameV(i->guests[j]);

    if (!temp || i->guests[j] == 0)
      continue;

    sprintf(buf2, "%s, ", temp);
    strcat(buf, CAP(buf2));
    x++;
  }
  if (!x)
    strcat(buf, "None");
  strcat(buf, "\r\n");
  send_to_char(buf, ch);
}