/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/***********************************************************
Realms of Aurealis Online Creation System version 2.1
RoAOLCv2.1   -   James Rhone aka Vall of RoA

roaolc.c  (online creation system - mob/room/object/house edit files)
Adopted from Realms of Imagination's original version of OLC (7/95)

 ******** Heavily modified and expanded ********
 *** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
 ******** Heavily modified and expanded ********
 All rights reserved henceforth.  Author: James Rhone
				  Original Running Version: 0.5beta

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  Thanks to Jeremy Elson
who has provided a stable base for Realms of Aurealis.  RoA was originally
created from a CircleMUD 2.2 base, back in the dark ages, when even that
release was considered new wave.
  Since RoA is a perpetual BETA MUD, all code from it is also in BETA
stages.  I personally feel this is the best OLC ever made available for
CircleMUD and its derivatives.  My experienced builders agree.  How you
feel depends on how well you can fit into your mud.  Good luck.  There are
no patch files, no step by step instructions, and no hand holders.  All
code written for Realms of Aurealis, including that contained in this file
was written by me, James Rhone, so nobody is very familiar with it except
me.  Perhaps that will change. :)

*** 
If you do not know C, I suggest getting to know it before you attempt
to plug this into your mud.  This is not a trivial addition to any mud and
without someone there to guide you, you will inevitably get frustrated.
**

    note: zone OLC can be found in z_olc.c
    note: room OLC can be found in r_olc.c
    note: mobile OLC can be found in m_olc.c
    note: object OLC can be found in o_olc.c
**********************************************************/
#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "comm.h"
#include "interpreter.h"
#include "acmd.h"
#include "db.h"
#include "utils.h"
#include "mudlimits.h"
#include "handler.h"
#include "roaolc.h"
#include "magic.h"
#include "house.h"
#include "lists.h"
#include "global.h"

/* for chars */
extern char	*terrain_types[];
extern char	*alter_types[];
extern char	*room_bits[];
extern char	*room_bits2[];
extern char	*exit_bits[];
extern char	*dirs[];
extern char	*comm_dirs[];
extern const int rev_dir[];

void check_invalid_room(rmdata *r);
extern exdescdata *correct_extra_descrips(exdescdata *ths);
extern exdescdata *free_extra_descrips(exdescdata *ths);
extern exdescdata *dupe_exdesc_over(exdescdata *ths);
extern rtrig *free_rtriggers(rtrig *ths);

ROA_MENU(redit_top_menu);
ROA_MENU(redit_confirm_quit);
ROA_MENU(redit_confirm_save);
ROA_MENU(hedit_top_menu);
ROA_MENU(hedit_confirm_quit);
ROA_MENU(hedit_confirm_save);
ROA_MENU(confirm_room_wipe);

// given a ptr, vnum, and rnum, dont allocate just default it
void  default_room_out(rmdata *room, int vnum)
{
  room->number      = vnum;
  room->zone        = vnum / 100;
  room->name        = STR_DUP("(def)");
  room->description = STR_DUP("(def)\n\r");
  room->exdesc      = NULL;
  room->max_contains= -1;
  room->owner       = -1;
  room->alter_type  = 0;
  room->numdice     = 0;
  room->sizedice    = 0;
  room->sound_file  = NULL;
  room->music_timer = -1;
  FLOATTO(room)     = -1;
  DRIFTTO(room)	    = -1;
  DROPTO(room)      = -1;
  RPROC_BEG(room)   = NULL;
  RPROC_CUR(room)   = NULL;
  RTARGET_CHAR(room) = NULL;
  RTARGET_OBJ(room) = NULL;
  RTRIGS(room)      = NULL;
}

// wax and null all dirs on a room re: wax room
void free_room_dirs(rmdata *trg)
{
  int i;
  
  for (i=0; i < NUM_OF_DIRS; i++)
    if (trg->dir_option[i])
    {
      FREENULL(trg->dir_option[i]->keyword);
      FREENULL(trg->dir_option[i]->exit_descr);

      // Nuke all exit messages, as well. 03/24/98 -callahan
      FREENULL(trg->dir_option[i]->enter);
      FREENULL(trg->dir_option[i]->oenter);
      FREENULL(trg->dir_option[i]->drop);
      FREENULL(trg->dir_option[i]->odrop);

      FREENULL(trg->dir_option[i]);
    }
}

// make another copy of src dirs on trg room
void dupe_room_dirs(rmdata *src, rmdata *trg)
{
  int i;

  for (i=0; i < NUM_OF_DIRS; i++)
    if (src->dir_option[i])
    {
      CREATE(trg->dir_option[i], rmdirdata, 1);
      *trg->dir_option[i] = *src->dir_option[i];

      if (src->dir_option[i]->keyword)
        trg->dir_option[i]->keyword = STR_DUP(src->dir_option[i]->keyword);
      else
        trg->dir_option[i]->keyword = NULL;

      if (src->dir_option[i]->exit_descr)
        trg->dir_option[i]->exit_descr = STR_DUP(src->dir_option[i]->exit_descr);
      else
        trg->dir_option[i]->exit_descr = NULL;

      // Do stuff for exit messages... 03/24/98 -callahan
      if (src->dir_option[i]->enter)
        trg->dir_option[i]->enter = STR_DUP(src->dir_option[i]->enter);
      else
        trg->dir_option[i]->enter = NULL;

      if (src->dir_option[i]->oenter)
        trg->dir_option[i]->oenter = STR_DUP(src->dir_option[i]->oenter);
      else
        trg->dir_option[i]->oenter = NULL;

      if (src->dir_option[i]->drop)
        trg->dir_option[i]->drop = STR_DUP(src->dir_option[i]->drop);
      else
        trg->dir_option[i]->drop = NULL;

      if (src->dir_option[i]->odrop)
        trg->dir_option[i]->odrop = STR_DUP(src->dir_option[i]->odrop);
      else
        trg->dir_option[i]->odrop = NULL;
    }
    else
      trg->dir_option[i] = NULL;
}

void free_room_randoms(rmdata *trg)
{
  int i;
  for (i = 0; i < 5; i++)
    FREENULL(trg->randoms[i]);
}

void dupe_room_randoms(rmdata *src, rmdata *trg)
{
  int i;

  for(i = 0; i < 5; i++)
    if (src->randoms[i])
      trg->randoms[i] = STR_DUP(src->randoms[i]);
    else
      trg->randoms[i] = NULL;
}

// this OBLITERATES a room, it does not (repeat) does not
// remove itself from the world array if it is a part of it
// this is intended to wax ROOM_EDITTED ptrs on char after 
// an edit 
// now also wax sound_file...2/21/98 -jtrhone
void wax_room(rmdata *trg)
{
  FREENULL(trg->name);
  FREENULL(trg->description);
  FREENULL(trg->sound_file);
  trg->exdesc = free_extra_descrips(trg->exdesc);
  free_room_dirs(trg);
  free_room_randoms(trg);

  // have to do rprocs/trigs  6/5/98 -jtrhone
  RTRIGS(trg) = free_rtriggers(RTRIGS(trg));
  FREENULL(RPROC_BEG(trg));
  RPROC_CUR(trg) = NULL;
  FREENULL(RTARGET_CHAR(trg));
  FREENULL(RTARGET_OBJ(trg));
}

// wipe_room is a builder tool to bamph an existing room, it frees everything
// and defaults the room out...
void wipe_room(chdata *ch, int room)
{
  if (INVALID_ROOM(room))
  {
    send_to_char("Invalid room...\n\r",ch);
    return;
  }

  wax_room(&world[room]);
  default_room_out(&world[room], world[room].number);
}

// dupe over an rtrig list to a whole new one
// return ptr to new one  6/5/98 -jtrhone
rtrig *dupe_rtrigs_over(rtrig *src)
{
  rtrig *e_new, *tmp, *trg = NULL;

  for (tmp = src; tmp; tmp = tmp->next)
  {
    CREATE(e_new, rtrig, 1);
    e_new->trigger = str_dup(tmp->trigger);
    e_new->reaction = str_dup(tmp->reaction);
    e_new->next = trg;
    trg = e_new;
  }

  return trg;
}

// dupe EVERYTHING over, first the strings from trg get
// waxed, and extra descrip list
// then it gets a copy of all src's things
void dupe_room_over(rmdata *src, rmdata *trg)
{
  chdata *people;
  obdata *contents;
  byte light;
  sh_int num_present;
  int trans_present;
  int location;
  byte to_port;
  byte wait_time;

  // MUST save data IN USE on rooms before we transfer
  // we will set this data back after we're done -roa
  people = trg->people;
  contents = trg->contents;
  light = trg->light;
  num_present = trg->num_present;
  trans_present = trg->trans_present;
  location = trg->location;
  to_port = trg->to_port;
  wait_time = trg->wait_time;
  
  wax_room(trg);

  *trg = *src;
  trg->name        = STR_DUP(src->name);
  trg->description = STR_DUP(src->description);
  trg->sound_file  = STR_DUP(src->sound_file);
  trg->exdesc      = dupe_exdesc_over(src->exdesc);
  dupe_room_dirs(src, trg);
  dupe_room_randoms(src, trg);

  // add rtrigs...rprocs  6/5/98 -jtrhone
  RTRIGS(trg)       = dupe_rtrigs_over(RTRIGS(src));
  RPROC_BEG(trg)    = STR_DUP(RPROC_BEG(src));
  RPROC_CUR(trg)    = RPROC_BEG(trg);
  RTARGET_CHAR(trg) = STR_DUP(RTARGET_CHAR(src));
  RTARGET_OBJ(trg)  = STR_DUP(RTARGET_OBJ(src));

  // now retrieve the saved data we didnt wanna alter
  trg->people = people;
  trg->contents = contents;
  trg->light = light;
  trg->num_present = num_present;
  trg->trans_present = trans_present;
  trg->location = location;
  trg->to_port = to_port;
  trg->wait_time = wait_time;
}

// build another room in current zone_locked if one available
// from direction given and room currently in
// example: rbuild north, finds next slot in zone, builds room connects
// it via a north exit, gives opposite exits from new room to current room
ACMD(do_rbuild)
{
  int vroom, rroom, zone;
  int top;
  int dir;
  rmdata *newroom;
  rmdata *thisroom;
  
  if (IS_NPC(ch)) return;

  if (!ch->pc_specials->saved.zone_locked)
  {
    send_to_char("You must lock a zone first.\n\r",ch);
    return;
  }

  zone = world[ch->in_room].zone;
  if (zone != ch->pc_specials->saved.zone_locked)
  {
    send_to_char("You must %6rbuild%0 from the zone you have locked.\n\r",ch);
    return;
  }

  if (ZONE_FREED(zone))
  {
    send_to_char("The rooms from this zone are not stored in memory.\n\r",ch);
    send_to_char("Use %Bzlock%0 to load them into memory.\n\r",ch);
    return;
  }

  top = zone_table[zone].top;
  for (vroom = zone * 100; vroom <= top; vroom++)
    if ((rroom = real_room(vroom)) <= 0)
      break;

  if (vroom > top)
  {
    send_to_char("No available rooms in this zone.\n\r",ch);
    return;
  }    

  if (!WLD_SLOTS_LEFT)
  {
    send_to_char("Max room creation for this runtime reached.\n\r",ch);
    send_to_char("You must wait for reboot to create more protos.\n\r",ch);
    mudlog("!SYSWAR!: World padding exhausted.  Suggest immediate reboot.", BRF, 
           LEV_IMM, TRUE);
    return;
  }

  // parse arg dir and check it against existing exits -roa
  one_argument(argument, arg);

  if (!*arg)
  {
    send_to_char("Usage: rbuild <direction>.\n\r",ch);
    return;
  }

  if ((dir = search_block(arg, comm_dirs, FALSE)) == -1 || dir >= NUM_OF_DIRS)
  {
    send_to_char("Valid dirs: N, S, E, W, NE, NW, SE, SW, U, D.\n\r",ch);
    return;
  }

  if (EXIT(ch, dir))
  {
    send_to_char("That direction exists already.\n\r",ch);
    return;
  }

  newroom = &world[top_of_world];
  default_room_out(newroom, vroom);
  top_of_world++;

  // make sure all the strings are in there -roa
  check_invalid_room(newroom);

  thisroom = &world[ch->in_room];

  // create dir to new room
  CREATE(thisroom->dir_option[dir], rmdirdata, 1);
  thisroom->dir_option[dir]->keyword = STR_DUP("(def)");
  thisroom->dir_option[dir]->exit_descr = STR_DUP("(def)\n\r");

  // Exit messages. 03/24/98 -callahan
  thisroom->dir_option[dir]->enter = STR_DUP("(def)\n\r");
  thisroom->dir_option[dir]->oenter = STR_DUP("(def)\n\r");
  thisroom->dir_option[dir]->drop = STR_DUP("(def)\n\r");
  thisroom->dir_option[dir]->odrop = STR_DUP("(def)\n\r");

  thisroom->dir_option[dir]->to_room_virtual = vroom;
  thisroom->dir_option[dir]->to_room = real_room(vroom);

  // calc opposite dir
  dir = rev_dir[dir];

  // create dir from new room to thisroom
  CREATE(newroom->dir_option[dir], rmdirdata, 1);
  newroom->dir_option[dir]->keyword = STR_DUP("(def)");
  newroom->dir_option[dir]->exit_descr = STR_DUP("(def)\n\r");

  // Exit messages. 03/24/98 -callahan
  newroom->dir_option[dir]->enter = STR_DUP("(def)\n\r");
  newroom->dir_option[dir]->oenter = STR_DUP("(def)\n\r");
  newroom->dir_option[dir]->drop = STR_DUP("(def)\n\r");
  newroom->dir_option[dir]->odrop = STR_DUP("(def)\n\r");

  newroom->dir_option[dir]->to_room_virtual = thisroom->number;
  newroom->dir_option[dir]->to_room = real_room(thisroom->number);

  sprintf(buf, "PLRUPD: %s has rbuilt room #%d", GET_NAME(ch), newroom->number);
  mudlog(buf, NRM, GET_LEVEL(ch), TRUE);
}

// connect room yer in to another existing room with rev_dir connection
ACMD(do_rconnect)
{
  int vroom, rroom, zone;
  int dir;
  int rdir;
  rmdata *otherroom;
  rmdata *thisroom;
  BOOL ok = FALSE;

  if (IS_NPC(ch)) return;

  if (ROOM_FLAGGED2(ch->in_room, HOUSE))
    ok = TRUE;

  if (!ch->pc_specials->saved.zone_locked && !ok)
  {
    send_to_char("You must lock a zone first.\n\r",ch);
    return;
  }

  zone = world[ch->in_room].zone;
  if (zone != ch->pc_specials->saved.zone_locked && !ok)
  {
    send_to_char("You must rconnect from the zone you have locked.\n\r",ch);
    return;
  }

  if (ZONE_FREED(zone))
  {
    send_to_char("The rooms from this zone are not stored in memory.\n\r",ch);
    send_to_char("Use %Bzlock%0 to load them into memory.\n\r",ch);
    return;
  }

  // parse arg dir and check it against existing exits -roa
  half_chop(argument, arg, buf2);

  if (!*arg || !*buf2 || !is_number(buf2))
  {
    send_to_char("Usage: rconnect <direction> <room vnum>.\n\r",ch);
    return;
  }

  if ((dir = search_block(arg, comm_dirs, FALSE)) == -1 || dir >= NUM_OF_DIRS)
  {
    send_to_char("Valid dirs: N, S, E, W, NE, NW, SE, SW, U, D.\n\r",ch);
    return;
  }

  if (EXIT(ch, dir))
  {
    send_to_char("That direction exists already.\n\r",ch);
    return;
  }

  vroom = atoi(buf2);

  // now make sure other room is real and doesnt have that rev_dir
  if ((rroom = real_room(vroom)) < 0)
  {
    send_to_char("That room doesn't exist.\n\r",ch);
    return;
  }

  if (world[rroom].zone != zone && !ok)
  {
    send_to_char("Both rooms must be in the locked zone.\n\r",ch);
    return;
  }

  // calc opposite dir
  rdir = rev_dir[dir];

  if (DIR(rroom, rdir))
  {
    send_to_char("Reverse dir exists in target room.\n\r",ch);
    return;
  }

  otherroom = &world[rroom];
  thisroom = &world[ch->in_room];

  // create dir to new room
  CREATE(thisroom->dir_option[dir], rmdirdata, 1);
  thisroom->dir_option[dir]->keyword = STR_DUP("(def)");
  thisroom->dir_option[dir]->exit_descr = STR_DUP("(def)\n\r");

  // Exit messages. 03/24/98 -callahan
  thisroom->dir_option[dir]->enter = STR_DUP("(def)\n\r");
  thisroom->dir_option[dir]->oenter = STR_DUP("(def)\n\r");
  thisroom->dir_option[dir]->drop = STR_DUP("(def)\n\r");
  thisroom->dir_option[dir]->odrop = STR_DUP("(def)\n\r");

  thisroom->dir_option[dir]->to_room_virtual = vroom;
  thisroom->dir_option[dir]->to_room = rroom;

  // create dir from new room to thisroom
  CREATE(otherroom->dir_option[rdir], rmdirdata, 1);
  otherroom->dir_option[rdir]->keyword = STR_DUP("(def)");
  otherroom->dir_option[rdir]->exit_descr = STR_DUP("(def)\n\r");

  // Exit messages. 03/24/98 -callahan
  thisroom->dir_option[dir]->enter = STR_DUP("(def)\n\r");
  thisroom->dir_option[dir]->oenter = STR_DUP("(def)\n\r");
  thisroom->dir_option[dir]->drop = STR_DUP("(def)\n\r");
  thisroom->dir_option[dir]->odrop = STR_DUP("(def)\n\r");

  otherroom->dir_option[rdir]->to_room_virtual = thisroom->number;
  otherroom->dir_option[rdir]->to_room = real_room(thisroom->number);

  sprintf(buf, "PLRUPD: %s has rconnected room #%d to room #%d", GET_NAME(ch),
	  thisroom->number, otherroom->number);
  mudlog(buf, NRM, GET_LEVEL(ch), TRUE);

  if (ok)
    send_to_char("Remember to %B%6wldsave%0 both zones.\n\r",ch);
}

/* add extra menu for rproc string 6/5/98 -jtrhone*/
ROA_MENU(redit_rproc_menu)
{
    char buf[20000];
    char *p;
    int field;
    rmdata *r = ROOM_EDITTED(ch);

    if (!input_str)
    {
        menu_title_send("RoomEdit RoomProc Menu", ch);
        if (IS_SET(r->room_flags, RPROC))
        {
          send_to_char("%B--NOTE--:%0  Parse commands with CR|LF.\n\r",ch);
          sprintf(buf, "1.) %%6Room Proc Command list%%0:\n\r%s\n\r", RPROC_BEG(r));
          page_string(ch->desc, buf, TRUE);
        }
        else
          send_to_char("    %B%5ROOMPROC rooms only.%0\n\r",ch);
        send_to_char("\n\r", ch);
        MENU_PROMPT(ch) = "Enter field number to change or 0 to end: ";
        return;
    }

    strcpy(buf, input_str);
    p = strtok(buf, "   \n\r");
    if (p)
        field = atoi(p);
    else
        field = 0;

    switch (field)
    {
      case 0:
        menu_back(ch);
        return;
        break;

      case 1:
        if (IS_SET(r->room_flags, RPROC))
          do_var_string_arg(ch, "Enter command list (each command on newline) (@) :\n\r",
                            &RPROC_BEG(r), MAX_STRING_LENGTH - 100);
        break;

      default:
        send_to_char("That field cannot be changed.\n\r", ch);
        break;
    }
}

ROA_MENU(confirm_room_wipe)
{
  char buf[MAX_STRING_LENGTH];
  char *p;

  if (!input_str)
  {
    MENU_PROMPT(ch) = "Confirm room wipe (YES/no)? ";
    return;
  }

  strcpy(buf, input_str);
  p = strtok(buf, "     \n\r");
  if (!p || strncasecmp("no", p, strlen(p)) != 0)
  {
    send_to_char("Wipe confirmed...\n\r",ch);
    // room rnum was saved as index2
    wipe_room(ch, ch->pc_specials->index2);
    ch->pc_specials->index2 = -1;
  }
  else
    send_to_char("Wipe not confirmed...\n\r",ch);

  MENU_PROMPT(ch) = NULL;
  MENU_HANDLER(ch) = NULL;
  MENU_DEPTH(ch) = 0;
}

ACMD(do_wipe_room)
{
  int room = ch->in_room;

  if (INVALID_ROOM(room))
  {
    send_to_char("Invalid room.\n\r",ch);
    return;
  }

  if (!ZONE_LOCKED(world[room].zone))
  {
    send_to_char("Zone must be locked...\n\r",ch);
    return;
  }

  MENU_HANDLER_ARG(ch) = (void *) 0;
  ch->pc_specials->index2 = room;
  menu_jump(ch, confirm_room_wipe);
}

ACMD(do_redit)
{
  char buf[512];
  int vnum, rnum, zone;
    
  if (IS_NPC(ch)) return;

  one_argument(argument, buf);

  if (!*buf)
    vnum = world[ch->in_room].number;
  else
    vnum = atoi(buf);

  if (vnum <= 0)
  {
    send_to_char("usage: REDIT <room VNUM>\n\r", ch);
    return;
  }  

  zone = real_zone(vnum / 100);

  if (!olc_perms(ch, LEV_CIMP, zone))
    return;

  if (GET_LEVEL(ch) < LEV_AIMP)
    if (!ZONE_FLAGGED(zone, Z_LOCKED) && zone != HOUSE_ZONE) 
    {
       send_to_char("You must ZLOCK that zone before you can edit it.\n\r",ch);
       return;
    }

  if (ZONE_FREED(zone))
  {
    send_to_char("That zone is freed from memory.  Zlock it first.\n\r",ch);
    return;
  }

  // editting existing room
  if ((rnum = real_room(vnum)) >= 0)
  {
    CREATE(ROOM_EDITTED(ch), rmdata, 1);
    dupe_room_over(&world[rnum], ROOM_EDITTED(ch));
  }
  else // creating new room
  {
    if (!WLD_SLOTS_LEFT)
    {
      send_to_char("Max room creation for this runtime reached.\n\r",ch);
      send_to_char("You must wait for reboot to create more protos.\n\r",ch);
      mudlog("!SYSWAR!: World padding exhausted.  Suggest immediate reboot.", 
	     BRF, LEV_IMM, TRUE);
      return;
    }
    CREATE(ROOM_EDITTED(ch), rmdata, 1);
    default_room_out(ROOM_EDITTED(ch), vnum);
  }

  SET_BIT(PLR_FLAGS(ch), PLR_BUILDING);
  MENU_DEPTH(ch) = 0;   
  ch->pc_specials->field_changed = FALSE;
  menu_jump(ch, redit_top_menu);  
}

ROA_MENU(redit_add_extradesc_menu)
{
    rmdata *r = ROOM_EDITTED(ch);
    exdescdata *e;

    switch ((int) MENU_HANDLER_ARG(ch))
    {
      case 0:  // create and insert into list
	CREATE(e, exdescdata, 1);
	e->next = r->exdesc;
	r->exdesc = e;
	    
	MENU_HANDLER_ARG(ch) = (void *) 1;
	do_string_arg(ch, "Enter keywords: ", &e->keyword, "");
	break;
	
      case 1:
	MENU_HANDLER_ARG(ch) = (void *) 2;
	do_long_string_arg(ch, "Enter new description (@):\n\r",
			   &r->exdesc->description);
	break;

      default:
	menu_back(ch);
	break;
    }
}

void delete_room_extra(chdata *ch)
{
    rmdata *r = ROOM_EDITTED(ch);
    exdescdata *e, *temp;
    int i;

    /* advance to the one we want to delete */
    for (i = 1, e = r->exdesc; 
	 (i < (int) MENU_RETURN(ch)) && e; e = e->next, i++)
	;

    if (e)
    {
      REMOVE_FROM_LIST(e, r->exdesc, next);
      FREENULL(e->keyword);
      FREENULL(e->description);
      FREENULL(e);
    }

    MENU_RETURN(ch) = 0;
}

ROA_MENU(redit_extradesc_menu)
{
    char buf[MAX_STRING_LENGTH];
    rmdata *r = ROOM_EDITTED(ch);
    exdescdata *e = NULL;
    char *p;
    int i;

    if (!input_str)
    {
	/* if we have one to delete from earlier */
	if ((int) MENU_RETURN(ch) > 0)
	  delete_room_extra(ch);
	
        menu_title_send("Extra Descrip Main Menu", ch);
	for (i = 1, e = r->exdesc; e; e = e->next, i++)
	{
	    sprintf(buf, "%2d.) %%6%s%%0\n\r", i, e->keyword); 
	    S2C();
	    sprintf(buf, "      %s\n\r", e->description); 
	    S2C();
	}
	i--;
    
	MENU_PROMPT(ch) = "(D)elete, (A)dd, (Q)uit/<return>: ";
	return;
    }
    
    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p)
    {
	if (!strncasecmp("delete", p, strlen(p)))
	{
	    if (!r->exdesc)
	    GET_INTEGER_ARG(ch, "No descriptions to delete (hit return): ", 
			    MENU_RETURN(ch), 0, 0);
	    else
	    {
	    for (i = 1, e = r->exdesc; e; e = e->next, i++)
		;
	    GET_INTEGER_ARG(ch, "Enter desc number to delete (return for none): ", 
			    MENU_RETURN(ch), 1, i-1);
	    }
	}
	else if (!strncasecmp("add", p, strlen(p)))
	{
	    menu_push(ch);
	    MENU_HANDLER_ARG(ch) = (void *) 0;
	    menu_jump(ch, redit_add_extradesc_menu);
	}
	else if (!strncasecmp("quit", p, strlen(p)) || !p || !atoi(p))
	{
	  menu_back(ch);
	  return;
	}
    }
    else if (!p || !atoi(p))
    {
      menu_back(ch);
      return;
    }
}

void validate_exit(rmdirdata *d)
{
  if (!EXIT_ISDOOR(d))
    UNFLAG_EXIT(d, EX_CLOSED | EX_LOCKED | EX_PICKPROOF | EX_AUTOSHUT | EX_AUTOLOCK);

  if (!EXIT_FLAGGED(d, EX_AUTOSHUT))
    UNFLAG_EXIT(d, EX_AUTOLOCK);
}

ROA_MENU(redit_exit_menu)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int field;
    int i;
    rmdata *r = ROOM_EDITTED(ch);
    int dir = (int) MENU_HANDLER_ARG(ch);
    rmdirdata *d = r->dir_option[dir];
    
    if (!input_str)
    {
        menu_title_send("Exit Edit Menu", ch);

	if (d->to_room_virtual >= 0)
	{
	    if ((i = real_room(d->to_room_virtual)) < 0)
	    {
		sprintf(buf, "Room %d does not exist.\n\n\r", d->to_room_virtual);
		S2C();
	    }
	    
	    d->to_room = i;
	    d->to_room_virtual = -1;
	}
	
	sprintf(buf, "%%6Direction %%B%s%%0\n\r", dirs[dir]); S2C();
	sprintf(buf, " 1.) %%6Keyword%%0     : %s\n\r", d->keyword); S2C();
	sprintf(buf, " 2.) %%6Description%%0 :\n\r%s", d->exit_descr); S2C();

        // Exit messages... 03/24/98 -callahan
	sprintf(buf, " 3.) %%6Enter Msg%%0   :\n\r%s", 
                d->enter ? d->enter : "None.\n\r"); S2C();
	sprintf(buf, " 4.) %%6OEnter Msg%%0  :\n\r%s", 
                d->oenter ? d->oenter : "None.\n\r"); S2C();
	sprintf(buf, " 5.) %%6Drop Msg%%0    :\n\r%s", 
                d->drop ? d->drop : "None.\n\r"); S2C();
	sprintf(buf, " 6.) %%6ODrop Msg%%0   :\n\r%s", 
                d->odrop ? d->odrop : "None.\n\r"); S2C();

	validate_exit(d);
	sprintbit(d->exinfo, exit_bits, buf2);
	sprintf(buf, " 7.) %%6Exit Flags%%0  : %%5%s%%0\n\r", buf2); S2C();
	sprintf(buf, " 8.) %%6Key VNUM%%0    : %d\n\r", d->key); S2C();
	sprintf(buf, " 9.) %%6To Room VNUM%%0: %d\n\r", world[d->to_room].number); 
	S2C();
	send_to_char("\n\r10.) %1Delete this exit%0\n\n\r", ch);

	MENU_PROMPT(ch) = "Enter field number to change or 0 to end: ";
	return;
    }

    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p)
	field = atoi(p);
    else
	field = 0;

    switch (field)
    {
      case 0:
	menu_back(ch);
	return;
	break;

      case 10:
        FREENULL(d->keyword);   
        FREENULL(d->exit_descr);
      
        // Exit messages. 03/24/98 -callahan
        FREENULL(d->enter); 
        FREENULL(d->oenter);
        FREENULL(d->drop); 
        FREENULL(d->odrop);
      
        FREENULL(d);
        r->dir_option[dir] = NULL;
        menu_back(ch);
        return;
        break;

      case 1:
	do_string_arg(ch, "Enter exit keyword: ", &d->keyword, "");
	break;
	
      case 2:
	do_long_string_arg(ch, "Enter exit description (@):\n\r", &d->exit_descr);
	break;

      // Exit messages. 03/24/98 -callahan
      case 3:
	do_long_string_arg(ch, "Enter message player sees upon entering the exit (@):\n\r", &d->enter);
	break;

      case 4:
	do_long_string_arg(ch, "Enter message others see when player enters the exit (@):\n\r", &d->oenter);
	break;

      case 5:
	do_long_string_arg(ch, "Enter message player sees when dropped into the next room (@):\n\r", &d->drop);
	break;

      case 6:
	do_long_string_arg(ch, "Enter message others in next room see when player arrives (@):\n\r", &d->odrop);
	break;

      case 7:
	toggle_menu(ch, "Enter exit bit to toggle: ", &d->exinfo, exit_bits);
	break;
	
      case 8:
	GET_INTEGER_ARG(ch, "Enter Key VNUM: ", d->key, -1, 99999);
	break;
	
      case 9:
	GET_INTEGER_ARG(ch, "Enter Room VNUM: ", d->to_room_virtual, 0, 99999);
	break;
	
      default:
	send_to_char("That field cannot be changed, yet.\n\r", ch);
	break;
    }
}

ROA_MENU(redit_string_menu)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int field;
    rmdata *r = ROOM_EDITTED(ch);
    int i;
    char *trans_strs[] = {
       "Keywords",
       "Short Description",
       "Long Description",
       "Movement Message",
       "Arrival Message"
    };
    
    if (!input_str)
    {
	if (IS_SET(r->room_flags, RANDOM))
	{
          menu_title_send("Room Random Menu", ch);

	  for (i = 0; i < 5; i++)
	  {
	    sprintf(buf, "%d.) %%6Random%d%%0: %s\n\r", i+1, i+1, r->randoms[i]); 
	    S2C();
	  }
	}
	else
	if (IS_SET(r->room_flags2, TRANSPORT))
	{
          menu_title_send("Transport String Menu", ch);

	  for (i = 0; i < 5; i++)
	  {
	    if (i == 3)
	      sprintf(buf, "%d.) %%6%s%%0:\n\r%s <%%Bdir%%0>.\n\r", i+1, trans_strs[i], 
                      r->randoms[i]); 
	    else
	      sprintf(buf, "%d.) %%6%s%%0:\n\r%s\n\r", i+1, trans_strs[i], r->randoms[i]); 
	    S2C();
	    send_to_char("\n\r",ch);
          }
	}
	else
	  send_to_char("    %B%5Room must be flagged RANDOM or TRANSPORT.%0\n\r",ch);
	send_to_char("\n\r", ch);
	MENU_PROMPT(ch) = "Enter field number to change or 0 to end: ";
	return;
    }

    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p)
	field = atoi(p);
    else
	field = 0;

    switch (field)
    {
      case 0:
	menu_back(ch);
	return;
	break;

      case 1:
	if (IS_SET(r->room_flags2, TRANSPORT))
	do_string_arg(ch, "Enter transport keywords:\n\r", &TRANS_KEYWORDS(r), "");
	else
	do_long_string_arg(ch, "Enter Room Random 1 (@):\n\r", &r->randoms[0]);
	break;

      case 2:
	if (IS_SET(r->room_flags2, TRANSPORT))
	do_string_arg(ch, "Enter transport short descr:\n\r", &TRANS_SHDESC(r), "");
	else
	do_long_string_arg(ch, "Enter Room Random 2 (@):\n\r", &r->randoms[1]);
	break;

      case 3:
	if (IS_SET(r->room_flags2, TRANSPORT))
	do_long_string_arg(ch, "Enter transport long description (@):\n\r", &TRANS_LDESC(r));
	else
	do_long_string_arg(ch, "Enter Room Random 3 (@):\n\r", &r->randoms[2]);
	break;

      case 4:
	if (IS_SET(r->room_flags2, TRANSPORT))
	  do_string_arg(ch, "Enter transport movement message:\n\r", &r->randoms[3], "");
	else
	do_long_string_arg(ch, "Enter Room Random 4 (@):\n\r", &r->randoms[3]);
	break;

      case 5:
	if (IS_SET(r->room_flags2, TRANSPORT))
	do_long_string_arg(ch, "Enter transport arrival message (@):\n\r", &r->randoms[4]);
	else
	do_long_string_arg(ch, "Enter Room Random 5 (@):\n\r", &r->randoms[4]);
	break;

      default:
	send_to_char("That field cannot be changed.\n\r", ch);
	break;
    }
}

ROA_MENU(redit_transport_menu)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int field;
    rmdata *r = ROOM_EDITTED(ch);
    
    if (!input_str)
    {
	if (IS_SET(r->room_flags2, TRANSPORT))
	{
          menu_title_send("Transport Data Menu", ch);
	  sprintf(buf, "1.) %%6Start Location|Port1%%0: %d\n\r", r->portals[0]); S2C();
	  sprintf(buf, "2.) %%6Port2%%0: %d\n\r", r->portals[1]); S2C();
	  sprintf(buf, "3.) %%6Port3%%0: %d\n\r", r->portals[2]); S2C();
	  sprintf(buf, "4.) %%6Port4%%0: %d\n\r", r->portals[3]); S2C();
	  sprintf(buf, "5.) %%6TDockWait%%0  : %d\n\r", r->dock_wait); S2C();
	  sprintf(buf, "6.) %%6TTravelWait%%0: %d\n\r", r->travel_wait); S2C();
	  sprintf(buf, "7.) %%6TTicketNum%%0 : %d\n\r", r->ticket_num); S2C();
	}
	else
	  send_to_char("    %B%5Room must be flagged TRANSPORT.%0\n\r",ch);

	send_to_char("\n\r", ch);
	MENU_PROMPT(ch) = "Enter field number to change or 0 to end: ";
	return;
    }

    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p)
	field = atoi(p);
    else
	field = 0;

    switch (field)
    {
      case 0:
	menu_back(ch);
	return;
	break;

      case 1:
	  GET_INTEGER_ARG(ch, "Enter VNUM of Starting Location (-1 for none): ", r->portals[0], 
                          -1, 99999);
	break;
      case 2:
	  GET_INTEGER_ARG(ch, "Enter VNUM of port2 (-1 for none): ", r->portals[1], -1, 99999);
	break;
      case 3:
	  GET_INTEGER_ARG(ch, "Enter VNUM of port3 (-1 for none): ", r->portals[2], -1, 99999);
	break;
      case 4:
	  GET_INTEGER_ARG(ch, "Enter VNUM of port4 (-1 for none): ", r->portals[3], -1, 99999);
	break;

      case 5:
	GET_INTEGER_ARG(ch, "Enter quarterticks to wait at port: ", r->dock_wait, 0, 127);
        break;

      case 6:
	GET_INTEGER_ARG(ch, "Enter quarterticks to wait during travel: ", r->travel_wait, 0, 127);
        break;

      case 7:
	GET_INTEGER_ARG(ch, "Enter VNUM of boarding pass (-1 for none): ", r->ticket_num, -1, 99999);
        break;

      default:
	send_to_char("That field cannot be changed.\n\r", ch);
	break;
    }
}

ROA_MENU(redit_portal_menu)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int field;
    rmdata *r = ROOM_EDITTED(ch);
    int i;
 
    if (!input_str)
    {
	if (IS_SET(r->room_flags, PORTAL))
	{
          menu_title_send("Portal Data Menu", ch);
	  for (i = 0; i < 4; i++)
	  {
	    sprintf(buf, "%d.) %%6Portal %d%%0: %d\n\r",i+1, i+1, r->portals[i]);
	    S2C();
	  }
	}
	else
	  send_to_char("    %B%5Room must be flagged PORTAL.%0\n\r",ch);

	send_to_char("\n\r", ch);
	MENU_PROMPT(ch) = "Enter field number to change or 0 to end: ";
	return;
    }

    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p)
	field = atoi(p);
    else
	field = 0;

    switch (field)
    {
      case 0:
	menu_back(ch);
	return;
	break;

      case 1:
	  GET_INTEGER_ARG(ch, "Enter Room VNUM to teleport to (-1 for none): ", 
                        r->portals[0], -1, 99999);
	break;
      case 2:
	  GET_INTEGER_ARG(ch, "Enter Room VNUM to teleport to (-1 for none): ", 
                        r->portals[1], -1, 99999);
	break;
      case 3:
	  GET_INTEGER_ARG(ch, "Enter Room VNUM to teleport to (-1 for none): ", 
                        r->portals[2], -1, 99999);
	break;
      case 4:
	  GET_INTEGER_ARG(ch, "Enter Room VNUM to teleport to (-1 for none): ", 
                        r->portals[3], -1, 99999);
	break;

      default:
	send_to_char("That field cannot be changed.\n\r", ch);
	break;
    }
}

ROA_MENU(redit_alter_menu)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int field;
    rmdata *r = ROOM_EDITTED(ch);
    
    if (!input_str)
    {
	if (IS_SET(r->room_flags, ALTERHIT | ALTERMOVE | ALTERMANA) ||
	    IS_SET(r->room_flags, DEATH | FLY_DEATH))
	{
          menu_title_send("Alteration Data Menu", ch);
	  send_to_char("%B%6Note:%0 If room flagged DEATH or FLY_DEATH:\n\r",ch);
	  send_to_char("      A value of 0 in either %B%6size%0 or %B%6num%0 "
		       "will be instant death.\n\r",ch);
	  send_to_char("      Any other dice roll will deduct that dice roll "
		       "from the character.\n\r",ch);

	  sprintf(buf, "\n\r1.) %%6Num dice%%0 : %d\n\r", r->numdice); S2C();
	  sprintf(buf, "2.) %%6Size dice%%0: %d\n\r", r->sizedice); S2C();

	  if (IS_SET(r->room_flags, DEATH) || IS_SET(r->room_flags,FLY_DEATH))
	    send_to_char("3.) %6Alter Type%0: Decrease, DEATH || FLY_DEATH set.\n\r",ch);
	  else
	  {
	    sprinttype(r->alter_type, alter_types, buf2);
	    sprintf(buf, "3.) %%6Alter Type%%0: %s\n\r",buf2);
	    S2C();
	  }
	}
	else
	  send_to_char("    %B%5Room must be flagged ALTER--- .%0\n\r",ch);

	send_to_char("\n\r", ch);

	MENU_PROMPT(ch) = "Enter field number to change or 0 to end: ";
	return;
    }

    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p)
	field = atoi(p);
    else
	field = 0;

    switch (field)
    {
      case 0:
	menu_back(ch);
	return;
	break;

      case 1:
	  if (IS_SET(r->room_flags, DEATH) || IS_SET(r->room_flags,FLY_DEATH))
	    GET_INTEGER_ARG(ch, "Enter number of dice: ", r->numdice, 0, 100);
	  else
	    GET_INTEGER_ARG(ch, "Enter number of dice: ", r->numdice, 0, 10);
	break;

      case 2:
	  if (IS_SET(r->room_flags, DEATH) || IS_SET(r->room_flags,FLY_DEATH))
	    GET_INTEGER_ARG(ch, "Enter size of dice: ", r->sizedice, 0, 100);
	  else
	    GET_INTEGER_ARG(ch, "Enter size of dice: ", r->sizedice, 0, 10);
	break; 

      case 3:
	if (IS_SET(r->room_flags, DEATH) || IS_SET(r->room_flags,FLY_DEATH))
	  send_to_char("Room flagged DEATH or FLY_DEATH, auto decrease.\n\r",ch);
	else
	  get_integer_list(ch, "Enter number of the desired alteration: ",
			   &r->alter_type, sizeof(r->alter_type), alter_types);
	break;

      default:
	send_to_char("That field cannot be changed.\n\r", ch);
	break;
    }
}

ROA_MENU(redit_main_exit_menu)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int field;
    int i;
    rmdata *r = ROOM_EDITTED(ch);
    
    if (!input_str)
    {
        menu_title_send("Main Exit Menu", ch);
	for (i = 0; i < NUM_OF_DIRS; i++)
	{
	    sprintf(buf, "%2d.) %%6Exit%%0 %%5%5s%%0, ", i + 1, dirs[i]); S2C();
	    if (r->dir_option[i])
	    {
		sprintbit(r->dir_option[i]->exinfo, exit_bits, buf2);
		sprintf(buf, "To: %%6%d%%0, Key#: %%6%d%%0, \n\r\tExFlags: %%5%s%%0\n\r",
			world[r->dir_option[i]->to_room].number, 
			r->dir_option[i]->key, buf2);
		S2C();
	    }
	    else
		send_to_char("\n\r", ch);
	}

	send_to_char("\n\r", ch);

	MENU_PROMPT(ch) = "Enter field number to change or 0 to end: ";
	return;
    }

    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p)
	field = atoi(p);
    else
	field = 0;

    switch (field)
    {
      case 0:
	menu_back(ch);
	return;
	break;

      case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9:
      case 10:  /* directions */
	i = field - 1;
	if (!r->dir_option[i])
	{
	    CREATE(r->dir_option[i], rmdirdata, 1);
	    r->dir_option[i]->keyword = STR_DUP("(def)");
	    r->dir_option[i]->exit_descr = STR_DUP("(def)\n\r");

            // Exit messages. 03/24/98 -callahan
	    r->dir_option[i]->enter = STR_DUP("(def)\n\r");
	    r->dir_option[i]->oenter = STR_DUP("(def)\n\r");
	    r->dir_option[i]->drop = STR_DUP("(def)\n\r");
	    r->dir_option[i]->odrop = STR_DUP("(def)\n\r");

	    r->dir_option[i]->exinfo = 0;	// no bitvector
	}
	
	r->dir_option[i]->to_room_virtual = -1;
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) i;
	menu_jump(ch, redit_exit_menu);
	break;

      default:
	send_to_char("That field cannot be changed.\n\r", ch);
	break;
    }
}

ROA_MENU(redit_environment_menu)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int field;
    rmdata *r = ROOM_EDITTED(ch);
    
    if (!input_str)
    {
        menu_title_send("Environment Control Menu", ch);

	if (r->terrain_type == TERRAIN_UWATER)
	{
	  if (FLOATTO(r) <= 0)
	    strcpy(buf2,"(none)");
	  else 
	    strcpy(buf2, "");
	  sprintf(buf, "1.) %%6Float to room%%0: %d (%s)\n\r", FLOATTO(r), buf2);
	  S2C();
	}
	else
	  FLOATTO(r) = -1;

	if ((r->terrain_type == TERRAIN_UWATER)      ||
	   (r->terrain_type == TERRAIN_AIR)          ||
	   (r->terrain_type == TERRAIN_WATER_NOSWIM) ||
	   (r->terrain_type == TERRAIN_WATER_SWIM))
	{
	  if (DROPTO(r) <= 0)
	    strcpy(buf2,"(none)");
	  else 
	    strcpy(buf2, "");
	  sprintf(buf, "2.) %%6Drop to room%%0: %d (%s)\n\r",DROPTO(r), buf2);
	  S2C();
	}
	else
	  DROPTO(r) = -1;

	if (IS_SET(r->room_flags, WINDY)              ||
	    (r->terrain_type == TERRAIN_UWATER)       ||
	    (r->terrain_type == TERRAIN_WATER_NOSWIM) ||
	    (r->terrain_type == TERRAIN_WATER_SWIM))
	{
	  if (DRIFTTO(r) == -1)
	    strcpy(buf2,"(none)");
	  else
	  if (DRIFTTO(r) == 0)
	    strcpy(buf2,"(random)");
	  else 
	    strcpy(buf2, "");
	  sprintf(buf, "3.) %%6Drift to room%%0: %d %s\n\r", DRIFTTO(r), buf2);
	  S2C();
	}
	else
	  DRIFTTO(r) = -1;

	send_to_char("\n\r", ch);

	MENU_PROMPT(ch) = "Enter field number to change or 0 to end: ";
	return;
    }

    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p)
	field = atoi(p);
    else
	field = 0;

    switch (field)
    {
      case 0:
	menu_back(ch);
	return;
	break;

      case 1:
	if (r->terrain_type != TERRAIN_UWATER)
	  break;
	GET_INTEGER_ARG(ch, "Enter Room VNUM to float to (-1 for none): ", 
                        FLOATTO(r), -1, 99999);
        break;

      case 2:
	if ((r->terrain_type != TERRAIN_UWATER)       &&
	    (r->terrain_type != TERRAIN_AIR)          &&
	    (r->terrain_type != TERRAIN_WATER_NOSWIM) &&
	    (r->terrain_type != TERRAIN_WATER_SWIM))
	  break;
	GET_INTEGER_ARG(ch, "Enter Room VNUM to drop to (-1 for none): ", 
                        DROPTO(r), -1, 99999);
        break;

      case 3:
	if (!IS_SET(r->room_flags, WINDY)             &&
	    (r->terrain_type != TERRAIN_UWATER)       &&
	    (r->terrain_type != TERRAIN_WATER_NOSWIM) &&
	    (r->terrain_type != TERRAIN_WATER_SWIM))
	  break;
	GET_INTEGER_ARG(ch, "Room VNUM to drift to (-1 = none, 0 = random): ", 
                        DRIFTTO(r), -1, 99999);
        break;

      default:
	send_to_char("That field cannot be changed.\n\r", ch);
	break;
    }
}

ROA_MENU(redit_top_menu)
{
    char buf[MAX_STRING_LENGTH];
    char buf2[MAX_STRING_LENGTH];
    char *p;
    int field;
    rmdata *r = ROOM_EDITTED(ch);
    
    if (!input_str)
    {
        menu_title_send("RoomEdit Main Menu", ch);

	sprintf(buf, " 1.) %%1Nr%%0        : %d\n\r", r->number); S2C();
	sprintf(buf, " 2.) %%6Name%%0      : %s\n\r", r->name); S2C();
	sprintf(buf, " 3.) %%6Desc%%0      :\n\r%s", r->description); S2C();

	sprinttype(r->terrain_type, terrain_types, buf2);
	sprintf(buf, " 4.) %%6TerrainType%%0: %%B%s%%0\n\r", buf2); S2C();

        sprintf(buf, " 5.) %%6Max Contains%%0: %d\n\r",r->max_contains);     
	S2C();

	sprintbit(r->room_flags, room_bits, buf2);
	sprintf(buf, " 6.) %%6RmFlags%%0 : %%5%s%%0\n\r", buf2); S2C();

	sprintbit(r->room_flags2, room_bits2, buf2);
	sprintf(buf, " 7.) %%6RmFlags2%%0: %%5%s%%0\n\r", buf2); S2C();
	
  	send_to_char(" 8.) %6Direction Edit Menu...%0\n\r",ch);

	if ((r->terrain_type == TERRAIN_UWATER)      ||
	    (r->terrain_type == TERRAIN_AIR)          ||
	    (r->terrain_type == TERRAIN_WATER_NOSWIM) ||
	    (r->terrain_type == TERRAIN_WATER_SWIM)   ||
	    IS_SET(r->room_flags, WINDY))
  	  send_to_char(" 9.) %6Environment Control Menu...%0\n\r",ch);

	if (IS_SET(r->room_flags2, TRANSPORT))
  	  send_to_char("10.) %6Transport String Menu...%0\n\r",ch);
	else 
        if (IS_SET(r->room_flags, RANDOM))
  	  send_to_char("10.) %6Random String Menu...%0\n\r",ch);

	if (IS_SET(r->room_flags2, TRANSPORT))
  	  send_to_char("11.) %6Transport Data Menu...%0\n\r",ch);

	if (IS_SET(r->room_flags, PORTAL))
  	  send_to_char("12.) %6Portal Data Menu...%0\n\r",ch);

	if (IS_SET(r->room_flags, ALTERHIT | ALTERMOVE | ALTERMANA) ||
	    IS_SET(r->room_flags, DEATH | FLY_DEATH))
  	  send_to_char("13.) %6Alteration Menu...%0\n\r",ch);

	send_to_char("14.) %6Xtra Descrip Menu...%0\n\r", ch);

	if (r->owner >= 0 && GET_LEVEL(ch) >= LEV_AIMP)
	{
          sprintf(buf, "15.) %%6Room Owner%%0: %d\n\r",r->owner);
	  S2C();
	}

	sprintf(buf, "16.) %%6Sound file%%0: %s\n\r", r->sound_file); S2C();
        sprintf(buf, "17.) %%6Sound replay time%%0: %d\n\r",r->music_timer); S2C();

        if (IS_SET(r->room_flags, RPROC))
          send_to_char("18.) %6RoomProc CommandList Menu...%0\n\r",ch);

	send_to_char("\n\r", ch);
	MENU_PROMPT(ch) = "Enter field number to change or 0 to end: ";
	return;
    }

    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p)
	field = atoi(p);
    else
	field = 0;

    switch (field)
    {
      case 0:
	menu_jump(ch, redit_confirm_quit);
	break;

      case 1:  /* builder wants to DUPE the room */
	if (ch->pc_specials->field_changed)
	{
	    send_to_char("That field cannot be changed after another "
			 "field is changed.\n\r", ch);
	    break;
	}
	
	if (GET_LEVEL(ch) >= LEV_CIMP)
	{
	    get_integer_arg(ch, "Enter room virtual number: ", 
			    &r->number, sizeof(r->number), 
			    0, 89999);
	}
	else
	{
  	  send_to_char("That field cannot be changed.\n\r", ch);
	}
	break;

      case 2:
	do_string_arg(ch, "Enter new room name:\n\r", &r->name, "");
	break;
	
      case 3:
	do_long_string_arg(ch, "Enter new description (@):\n\r", &r->description);
	break;
	
      case 4:
	get_integer_list(ch, "Enter number of the desired terrain: ",
			 &r->terrain_type,
			 sizeof(r->terrain_type),
			 terrain_types);
	break;

      case 5:
	GET_INTEGER_ARG(ch, "Enter Max Contains (-1 for no limit) ", r->max_contains, -1, 50);
        break;
       
      case 6:
	toggle_menu(ch, "Enter room bit to toggle: ", &r->room_flags, room_bits);
	break;
	
      case 7:
	toggle_menu(ch, "Enter room bit to toggle: ", &r->room_flags2, room_bits2);
	break;

      case 8:
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, redit_main_exit_menu);
	break;

      case 9:
	if ((r->terrain_type != TERRAIN_UWATER)       &&
	    (r->terrain_type != TERRAIN_AIR)          &&
	    (r->terrain_type != TERRAIN_WATER_NOSWIM) &&
	    (r->terrain_type != TERRAIN_WATER_SWIM)   &&
	    !IS_SET(r->room_flags, WINDY))
	  break;
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, redit_environment_menu);
	break;

      case 10:
	if (!IS_SET(r->room_flags2, TRANSPORT) &&
	    !IS_SET(r->room_flags, RANDOM))
	  break;
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, redit_string_menu);
	break;

      case 11:
	if (!IS_SET(r->room_flags2, TRANSPORT))
	  break;
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, redit_transport_menu);
	break;

      case 12:
	if (!IS_SET(r->room_flags, PORTAL))
	  break;
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, redit_portal_menu);
	break;

      case 13:
	if (!IS_SET(r->room_flags, ALTERHIT | ALTERMOVE | ALTERMANA) &&
	    !IS_SET(r->room_flags, DEATH | FLY_DEATH))
	  break;
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, redit_alter_menu);
	break;

      case 14:
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, redit_extradesc_menu);
	break;

      case 15:
	if (GET_LEVEL(ch) < LEV_AIMP)
	  break;
	GET_INTEGER_ARG(ch, "Enter IDNUM of room owner (-1 recommended) ", r->owner, -1, 5000);
        break;

      case 16:
	do_string_arg(ch, "Enter sound filename:\n\r", &r->sound_file, "");
	break;

      case 17:
	GET_INTEGER_ARG(ch, "Enter repeat time (qticks, -1 no repeat) ", 
                        r->music_timer, -1, 32000);
	break;

      case 18:
        if (!IS_SET(r->room_flags, RPROC))
          break;
        menu_push(ch);
        MENU_RETURN(ch) = 0;
        MENU_HANDLER_ARG(ch) = (void *) 0;
        menu_jump(ch, redit_rproc_menu);
        break;

      default:
	send_to_char("That field cannot be changed.\n\r", ch);
	break;
    }

    ch->pc_specials->field_changed = TRUE;
}

ROA_MENU(redit_confirm_quit)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    
    if (!input_str)
    {
      MENU_PROMPT(ch) = "Quit editting room (yes/NO)? ";
      return;
    }
    
    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p && strncasecmp("yes", p, strlen(p)) == 0)
      menu_jump(ch, redit_confirm_save);
    else
      menu_jump(ch, redit_top_menu);
}

// simply makes sure no strings on room are invalid
// also checks the exdesc list -roa
void check_invalid_room(rmdata *r)
{
  BOOL dolog = FALSE;

  if (!r->name || !*r->name)
  {
    r->name = STR_DUP("%1WARNING:%0 Room has no name, report immediately!\n\r");
    dolog = TRUE;
  }

  if (!r->description || !*r->description)
  {
    r->description = 
      STR_DUP("%1WARNING:%0 Room has no description, report immediately!\n\r");
    dolog = TRUE;
  }

  r->exdesc = correct_extra_descrips(r->exdesc);

  if (dolog)
  {
    sprintf(buf, "SYSERR: Invalid room string detected and fixed: #%d.",
	    r->number);
    mudlog(buf, BRF, LEV_IMM, TRUE);
  }
}

ROA_MENU(redit_confirm_save)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int rnum;
    rmdata *r = ROOM_EDITTED(ch);
    
    if (!input_str)
    {
	MENU_PROMPT(ch) = "Save editted room (YES/no)? ";
	return;
    }
    
    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (!p || strncasecmp("no", p, strlen(p)) != 0)
    {
      r->zone = real_zone(r->number / 100);
      check_invalid_room(r);
      if ((rnum = real_room(r->number)) >= 0)
      {
	strcpy(buf2, "editted");
	dupe_room_over(r, &world[rnum]);
      }
      else
      {
	strcpy(buf2, "created");
	dupe_room_over(r, &world[top_of_world]);
	top_of_world++;
      }
      sprintf(buf, "PLRUPD: %s has %s room #%d", GET_NAME(ch), buf2, r->number);
      mudlog(buf, NRM, GET_LEVEL(ch), TRUE);
    }

    wax_room(ROOM_EDITTED(ch));
    FREENULL(ROOM_EDITTED(ch));
    MENU_PROMPT(ch)  = NULL;
    MENU_HANDLER(ch) = NULL;
    MENU_DEPTH(ch)   = 0;
    REMOVE_BIT(PLR_FLAGS(ch), PLR_BUILDING);
}

ACMD(do_rlist)
{
  rmdata *w;
  int zone;
  int i, top;
  BOOL found = FALSE;
  char buf[20000];
  
  if (IS_NPC(ch))
	return;
    
  one_argument(argument, buf);
  if (!*buf || *buf == '.')
	zone = world[ch->in_room].zone;
  else
	zone = real_zone(atoi(buf));

  if (zone < 0)
  {
    send_to_char("That zone does not exist.\n\r",ch);
    return;
  }
    
  if (!olc_perms(ch, LEV_GOD, zone))
    return;

  if (ZONE_FREED(zone))
  {
    send_to_char("The rooms from that zone are not stored in memory.\n\r",ch);
    send_to_char("Use %Bzlock%0 to load them into memory.\n\r",ch);
    return;
  }

  top = zone_table[zone].top;
  for (i = zone * 100; i <= top; i++)
  {
	if (real_room(i) >= 0)
	    break;
  }

  if (i > top)
  {
	send_to_char("No rooms in that zone.\n\r",ch);
	return;
  }    
    
  buf[0] = '\0';
  while (i <= top)
  {
	if (real_room(i) < 0)
	{
	    i++;
	    continue;
	}
	found = TRUE;	
	w = &world[real_room(i)];

	sprintf(buf1, "[%5d]\t%-22.22s%%0\t", i, w->name);
	sprintbit(w->room_flags, room_bits, buf2);
	strcat(buf1, buf2);
	strcat(buf1, "\n\r");
	strcat(buf, buf1);
	i++;
  }
  if (found)
    page_string(ch->desc, buf, 1);
  else
    send_to_char("No rooms in that zone.\n\r",ch);
}

/* HEDIT  BASICALLY same as REDIT features, except its for PLAYERS
(ROA)	  with many of the room functions/options disabled */
ACMD(do_hedit)
{
  int find_house(int vnum);
  int vnum;
  int rnum;
  int i;
  BOOL ok;
  extern struct house_control_rec house_control[MAX_HOUSES];

  if (IS_NPC(ch))
	return;
 
  vnum = world[ch->in_room].number;

  if (world[ch->in_room].owner == GET_IDNUM(ch))
    ok = TRUE;
  else
    ok = FALSE;

  if (!ok && !ROOM_FLAGGED2(ch->in_room, HOUSE))
  {
    send_to_char("You must be in your house to edit it.\r\n", ch);
    return;
  }

  if (!ok && ((i = find_house(vnum)) < 0))
  {
    send_to_char("Your house was not found...seek immortal help.\r\n", ch);
    return;
  }

  if (!ok && GET_IDNUM(ch) != house_control[i].owner)
  {
    send_to_char("Only the owner of this house may edit it.\r\n", ch);
    return;
  }
 
  if ((rnum = real_room(vnum)) >= 0)
  {
    CREATE(ROOM_EDITTED(ch), rmdata, 1);
    dupe_room_over(&world[rnum], ROOM_EDITTED(ch));
  }
  else
  {
    send_to_char("House room does not exist. Seek immortal help.\n\r",ch);
    return;
  }

  SET_BIT(PLR_FLAGS(ch), PLR_BUILDING);
  MENU_DEPTH(ch) = 0;
  ch->pc_specials->field_changed = FALSE;
  menu_jump(ch, hedit_top_menu);
}

// let players add extra descriptions to their rooms
ROA_MENU(hedit_add_extradesc_menu)
{
    rmdata *r = ROOM_EDITTED(ch);
    exdescdata *e;

    switch ((int) MENU_HANDLER_ARG(ch))
    {
      case 0:
	CREATE(e, exdescdata, 1);
	CREATE(e->keyword, char, 1);
	CREATE(e->description, char, 1);
	e->next = r->exdesc;
	r->exdesc = e;
	    
	MENU_HANDLER_ARG(ch) = (void *) 1;
	do_string_arg(ch, "Enter keywords: ", &e->keyword, "");
	break;
	
      case 1:
	MENU_HANDLER_ARG(ch) = (void *) 2;
	do_long_string_arg(ch, "Enter new description (@):\n\r",
			   &r->exdesc->description);
	break;

      default:
	menu_back(ch);
	break;
    }
}

ROA_MENU(hedit_extradesc_menu)
{
    char buf[MAX_STRING_LENGTH];
    rmdata *r = ROOM_EDITTED(ch);
    exdescdata *e = NULL;
    char *p;
    int i;

    if (!input_str)
    {
	/* if we have one to delete from earlier */
	if ((int) MENU_RETURN(ch) > 0)
	  delete_room_extra(ch);
	
        menu_title_send("Extra Descrip Main Menu", ch);
	for (i = 1, e = r->exdesc; e; e = e->next, i++)
	{
	    sprintf(buf, "%2d.) %%6%s%%0\n\r", i, e->keyword); 
	    S2C();
	    sprintf(buf, "      %s\n\r", e->description); 
	    S2C();
	}
	i--;
    
	MENU_PROMPT(ch) = "(D)elete, (A)dd, (Q)uit/<return>: ";
	return;
    }
    
    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p)
    {
	if (!strncasecmp("delete", p, strlen(p)))
	{
	    if (!r->exdesc)
	    GET_INTEGER_ARG(ch, "No descriptions to delete (hit return): ", 
			    MENU_RETURN(ch), 0, 0);
	    else
	    {
	    for (i = 1, e = r->exdesc; e; e = e->next, i++)
		;
	    GET_INTEGER_ARG(ch, "Enter desc number to delete (return for none): ", 
			    MENU_RETURN(ch), 1, i-1);
	    }
	}
	else if (!strncasecmp("add", p, strlen(p)))
	{
	    menu_push(ch);
	    MENU_HANDLER_ARG(ch) = (void *) 0;
	    menu_jump(ch, hedit_add_extradesc_menu);
	}
	else if (!strncasecmp("quit", p, strlen(p)) || !p || !atoi(p))
	{
	  menu_back(ch);
	  return;
	}
    }
    else if (!p || !atoi(p))
    {
      menu_back(ch);
      return;
    }
}

ROA_MENU(hedit_exit_menu)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int field;
    int i;
    rmdata *r = ROOM_EDITTED(ch);
    int dir = (int) MENU_HANDLER_ARG(ch);
    rmdirdata *d = r->dir_option[dir];
    
    if (!input_str)
    {
	if (d->to_room_virtual >= 0)
	{
	    i = real_room(d->to_room_virtual);
	    if (i < 0)
	    {
		sprintf(buf, "Room %d does not exist.\n\n\r", 
			d->to_room_virtual);
		S2C();
	    }
	    
	    d->to_room = i;
	    d->to_room_virtual = -1;
	}
	
	sprintf(buf, "Direction: %%6%s%%0\n\r", dirs[dir]); S2C();
	sprintf(buf, " 1.) Keyword   : %s\n\r", d->keyword); S2C();
	sprintf(buf, " 2.) Desc      :\n\r%s\n\r", d->exit_descr); S2C();

	MENU_PROMPT(ch) = "Enter field number to change or 0 to end: ";
	return;
    }

    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p)
	field = atoi(p);
    else
	field = 0;

    switch (field)
    {
      case 0:
	if (world[d->to_room].number <= 0)
  	  r->dir_option[dir] = NULL;
        menu_back(ch);
	return;
	break;

      case 1:
	do_string_arg(ch, "Enter new exit keyword:\n\r", &d->keyword, "");
	break;
	
      case 2:
	do_long_string_arg(ch, "Enter new description (@):\n\r",
			   &d->exit_descr);
	break;
      default:
	send_to_char("That field cannot be changed.\n\r", ch);
	break;
    }
}

// the top house editting menu
ROA_MENU(hedit_top_menu)
{
    char buf[MAX_STRING_LENGTH];
    char buf2[MAX_STRING_LENGTH];
    char *p;
    int field;
    int i;
    rmdata *r = ROOM_EDITTED(ch);
    
    if (!input_str)
    {
	sprintf(buf, " 1.) %%1Nr%%0        : %d\n\r", r->number); S2C();
	sprintf(buf, " 2.) %%6Name%%0      : %s\n\r", r->name); S2C();
	sprintf(buf, " 3.) %%6Desc%%0      :\n\r%s", r->description); S2C();

	sprinttype(r->terrain_type, terrain_types, buf2);
	sprintf(buf, " 4.) %%6TerrainType%%0: %%B%s%%0\n\r", buf2); S2C();

	sprintf(buf, " 6.) %%6Extra Descriptions%%0: %s\n\r",
		r->exdesc?"Exist":"None");
	S2C();
	for (i = 0; i < NUM_OF_DIRS; i++)
	{
	    sprintf(buf, "%2d.) %%6Exit%%0 %%5%5s%%0, ", i + 7, dirs[i]); S2C();
	    if (r->dir_option[i])
	    {
		sprintbit(r->dir_option[i]->exinfo, exit_bits, buf2);
		sprintf(buf, "To: %%6%d%%0, Key#: %%6%d%%0, \n\r\tExFlags: %%5%s%%0\n\r",
			world[r->dir_option[i]->to_room].number, 
			r->dir_option[i]->key, buf2);
		S2C();
	    }
	    else
		send_to_char("\n\r", ch);
	}

	send_to_char("\n\r", ch);
	MENU_PROMPT(ch) = "Enter field number to change or 0 to end: ";
	return;
    }

    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p)
	field = atoi(p);
    else
	field = 0;

    switch (field)
    {
      case 0:
	menu_jump(ch, hedit_confirm_quit);
	break;

      case 2:
	do_string_arg(ch, "Enter new room name:\n\r", &r->name, "");
	break;
	
      case 3:
	do_long_string_arg(ch, "Enter new description (@):\n\r",
			   &r->description);
	break;
	
      case 4:
	get_integer_list(ch, "Enter number of the desired terrain: ",
			 &r->terrain_type,
			 sizeof(r->terrain_type),
			 terrain_types);
	break;

      case 6:
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, hedit_extradesc_menu);
	break;

      case 7: case 8: case 9: case 10: case 11: case 12:
      case 13: case 14: case 15:
      case 16:
	i = field - 7;
	if (!r->dir_option[i])
	{
	    CREATE(r->dir_option[i], rmdirdata, 1);
	    r->dir_option[i]->keyword = STR_DUP("(def)");
	    r->dir_option[i]->exit_descr = STR_DUP("(def)");

            // Exit messages. 03/24/98 -callahan
	    r->dir_option[i]->enter = STR_DUP("(def)");
	    r->dir_option[i]->oenter = STR_DUP("(def)");
	    r->dir_option[i]->drop = STR_DUP("(def)");
	    r->dir_option[i]->odrop = STR_DUP("(def)");

	}
	
	r->dir_option[i]->to_room_virtual = -1;
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) i;
	menu_jump(ch, hedit_exit_menu);
	break;

      default:
	send_to_char("That field cannot be changed.\n\r", ch);
	break;
    }

    ch->pc_specials->field_changed = TRUE;
}

ROA_MENU(hedit_confirm_quit)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    
    if (!input_str)
    {
	MENU_PROMPT(ch) = "Quit editting room (yes/NO)? ";
	return;
    }
    
    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (p && strncasecmp("yes", p, strlen(p)) == 0)
      menu_jump(ch, hedit_confirm_save);
    else
      menu_jump(ch, hedit_top_menu);
}

ROA_MENU(hedit_confirm_save)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int rnum;
    rmdata *r = ROOM_EDITTED(ch);
    
    if (!input_str)
    {
	MENU_PROMPT(ch) = "Save editted room (YES/no)? ";
	return;
    }
    
    strcpy(buf, input_str);
    p = strtok(buf, " 	\n\r");
    if (!p || strncasecmp("no", p, strlen(p)) != 0)
    {
	r->zone = real_zone(r->number / 100);
	if ((rnum = real_room(r->number)) >= 0)
	{
	  sprintf(buf, "PLRUPD: %s has h_editted room #%d.", GET_NAME(ch), 
		    r->number);
	  mudlog(buf, NRM, GET_LEVEL(ch), TRUE);
	  check_invalid_room(r);
	  dupe_room_over(r, &world[rnum]);
	
	  sprintf(buf, "%d",(r->number / 100));
	  do_wldsave(ch, buf, 0, 0);
	}
	else
	{
	  sprintf(buf, "SYSERR: %s has H_CREATED room #%d", GET_NAME(ch), r->number);
	  mudlog(buf, BRF, GET_LEVEL(ch), TRUE);
	}
    }

    wax_room(ROOM_EDITTED(ch));
    FREENULL(ROOM_EDITTED(ch));
    MENU_PROMPT(ch)  = NULL;
    MENU_HANDLER(ch) = NULL;
    MENU_DEPTH(ch)   = 0;
    REMOVE_BIT(PLR_FLAGS(ch), PLR_BUILDING);
}

// let a player add a room onto their house for a certain amount of gold
// RoA Innovations   (yanking slots from zones 3 and 4 for now)
//		     (HBUILD_ZONE1 and HBUILD_ZONE2)
ACMD(do_hbuild)
{
  int vroom, rroom, zone;
  int top;
  int dir;
  rmdata *newroom;
  rmdata *thisroom;
  BOOL found = FALSE;
  int unidle_reset_zone(int zon);
  
  if (IS_NPC(ch)) return;
  if (IN_NOWHERE(ch)) return;

  if (!REAL_ZONE(HBUILD_ZONE1) || !REAL_ZONE(HBUILD_ZONE2))
  {
    send_to_char("Unable to build room.  Zones do not exist.\n\r",ch);
    mudlog("SYSERR: HBUILD zones DNE.", BRF, LEV_IMM, TRUE);
    return;
  }

  if (!WLD_SLOTS_LEFT)
  {
    send_to_char("Max room creation for this runtime reached.\n\r",ch);
    send_to_char("You must wait for reboot to build more rooms.\n\r",ch);
    mudlog("!SYSWAR!: World padding exhausted.  Suggest immediate reboot.", BRF, LEV_IMM, TRUE);
    return;
  }

  if (world[ch->in_room].owner != GET_IDNUM(ch))
  {
    send_to_char("You can only build from rooms which you own.\n\r",ch);
    return;
  }

  if (GET_GOLD(ch) < HBUILD_COST)
  {
    sprintf(buf, "Addon rooms cost %%6%d%%0 %s, which you don't have.\n\r", HBUILD_COST, 
	    currency_name_plural);
    S2C();
    return;
  }

  // first try HBUILD_ZONE1, look for available room slot
  zone = HBUILD_ZONE1;
  top = zone_table[zone].top;
  for (vroom = zone * 100; vroom <= top; vroom++)
    if ((rroom = real_room(vroom)) <= 0)
      break;

  if (vroom <= top)
    found = TRUE;

  // ok havent found one in zone1, try next zone
  if (!found)
  {
    zone = HBUILD_ZONE2;
    top = zone_table[zone].top;
    for (vroom = zone * 100; vroom <= top; vroom++)
      if ((rroom = real_room(vroom)) <= 0)
        break;

    if (vroom <= top)
      found = TRUE;
  }

  if (!found)
  {
    send_to_char("There are no rooms available.  Seek immortal help.\n\r",ch);
    mudlog("SYSUPD: Hbuild unable to locate avail rooms.", BRF, LEV_IMM, FALSE);
    return;
  }

  // lesse if the zone is useable
  if (ZONE_CLOSED(zone) || ZONE_LOCKED(zone))
  {
    send_to_char("Unable to build room.  Zone closed, locked or freed.\n\r",ch);
    return;
  }

  // parse arg dir and check it against existing exits -roa
  one_argument(argument, arg);

  if (!*arg)
  {
    send_to_char("Usage: hbuild <direction>.\n\r",ch);
    return;
  }

  if ((dir = search_block(arg, comm_dirs, FALSE)) == -1 || dir >= NUM_OF_DIRS)
  {
    send_to_char("Valid dirs: N, S, E, W, NE, NW, SE, SW, U, D.\n\r",ch);
    return;
  }

  if (EXIT(ch, dir))
  {
    send_to_char("That direction already exists.\n\r",ch);
    return;
  }

  // 2/5/98 -jtrhone  if zone freed, unidle, reset it...
  if (ZONE_FREED(zone))
    unidle_reset_zone(zone);

  // take gold babe
  sprintf(buf, "Your house addition costs %%6%d%%0 %s.\n\r", HBUILD_COST, currency_name_plural);
  S2C();
  GET_GOLD(ch) -= HBUILD_COST;

  newroom = &world[top_of_world];
  top_of_world++;
  default_room_out(newroom, vroom);

  // make sure all the strings are in there -roa
  check_invalid_room(newroom);

  thisroom = &world[ch->in_room];

  // create dir to new room
  CREATE(thisroom->dir_option[dir], rmdirdata, 1);
  thisroom->dir_option[dir]->keyword = STR_DUP("(def)");
  thisroom->dir_option[dir]->exit_descr = STR_DUP("(def)\n\r");

  // Exit messages... 03/24/98 -callahan
  thisroom->dir_option[dir]->enter = STR_DUP("(def)\n\r"); 
  thisroom->dir_option[dir]->oenter = STR_DUP("(def)\n\r"); 
  thisroom->dir_option[dir]->drop = STR_DUP("(def)\n\r"); 
  thisroom->dir_option[dir]->odrop = STR_DUP("(def)\n\r");

  thisroom->dir_option[dir]->to_room_virtual = vroom;
  thisroom->dir_option[dir]->to_room = real_room(vroom);

  // calc opposite dir
  dir = rev_dir[dir];

  // create dir from new room to thisroom
  CREATE(newroom->dir_option[dir], rmdirdata, 1);
  newroom->dir_option[dir]->keyword = STR_DUP("(def)");
  newroom->dir_option[dir]->exit_descr = STR_DUP("(def)\n\r");

  // Exit messages... 03/24/98 -callahan
  newroom->dir_option[dir]->enter = STR_DUP("(def)\n\r"); 
  newroom->dir_option[dir]->oenter = STR_DUP("(def)\n\r"); 
  newroom->dir_option[dir]->drop = STR_DUP("(def)\n\r"); 
  newroom->dir_option[dir]->odrop = STR_DUP("(def)\n\r");

  newroom->dir_option[dir]->to_room_virtual = thisroom->number;
  newroom->dir_option[dir]->to_room = real_room(thisroom->number);

  // assign ownership to new room
  newroom->owner = GET_IDNUM(ch);

  sprintf(buf, "PLRUPD: %s has hbuilt room #%d", GET_NAME(ch), newroom->number);
  mudlog(buf, NRM, GET_LEVEL(ch), TRUE);

  // now save zone we just connected to...
  sprintf(buf, "%d", zone);
  do_wldsave(ch, buf, 0, 0);

  // 2/5/98  -jtrhone  ALSO save house zone...
  sprintf(buf, "%d", HOUSE_ZONE);
  do_wldsave(ch, buf, 0, 0);
}