AwakeMUD-0.51Beta/area/
AwakeMUD-0.51Beta/doc/
AwakeMUD-0.51Beta/lib/
AwakeMUD-0.51Beta/lib/etc/
AwakeMUD-0.51Beta/lib/fixer_data/
AwakeMUD-0.51Beta/lib/misc/
AwakeMUD-0.51Beta/lib/plrobjs/
AwakeMUD-0.51Beta/lib/plrobjs/A-E/
AwakeMUD-0.51Beta/lib/plrobjs/K-O/
AwakeMUD-0.51Beta/lib/plrobjs/U-Z/
AwakeMUD-0.51Beta/lib/plrspells/A-E/
AwakeMUD-0.51Beta/lib/plrtext/A-E/
AwakeMUD-0.51Beta/lib/world/
AwakeMUD-0.51Beta/lib/world/mob/
AwakeMUD-0.51Beta/lib/world/obj/
AwakeMUD-0.51Beta/lib/world/qst/
AwakeMUD-0.51Beta/lib/world/shp/
AwakeMUD-0.51Beta/lib/world/wld/
AwakeMUD-0.51Beta/lib/world/zon/
/* *************************************************
* file: redit.cc                                   *
* authors: Andrew Hynek, Nick Robertson, Jon Lin,  *
* Phrodo, Demise, Terra, Washu                     *
* purpose: Room Editor for AwakeOLC, a             *
* component of AwakeMUD                            *
* (c) 1997-1999 Andrew Hynek, (c) 2001             *
* The AwakeMUD Consortium                          *
************************************************* */


#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "structs.h"
#include "awake.h"
#include "interpreter.h"
#include "comm.h"
#include "spells.h"
#include "utils.h"
#include "db.h"
#include "boards.h"
#include "screen.h"
#include "olc.h"
#include "memory.h"

extern class memoryClass *Mem;

/* change this depending on the number of flags you have defined */
#define NUM_ROOM_SECTORS 15
#define NUM_ROOM_FLAGS   26
#define NUM_ROOM_FUNCS   6

#define ROOM d->edit_room

extern int      top_of_world;
extern int      top_of_world_array;
extern struct room_data *world;
extern struct obj_data *obj_proto;
extern struct char_data *mob_proto;
extern const char    *room_bits[];
extern const char    *sector_types[];
extern const char    *exit_bits[];
extern const char    *dirs[];
extern struct zone_data *zone_table;
extern struct char_data *character_list;
extern sh_int r_mortal_start_room;
extern sh_int r_immort_start_room;
extern sh_int r_frozen_start_room;
extern sh_int r_newbie_start_room;
extern int olc_state;
extern char *material_names[];
extern char *barrier_names[];
extern const char *room_functions[];
extern int material_ratings[];
extern int barrier_ratings[];
extern int rev_dir[];
extern void char_to_room(struct char_data * ch, int room);


// extern funcs
extern char *cleanup(char *dest, const char *src);
extern char *remove_CRs(char *string);
extern bool resize_world_array();
extern int real_zone(int);

/* function protos */
void redit_disp_extradesc_menu(struct descriptor_data * d);
void redit_disp_exit_menu(struct descriptor_data * d);
void redit_disp_exit_flag_menu(struct descriptor_data * d);
void redit_disp_flag_menu(struct descriptor_data * d);
void redit_disp_sector_menu(struct descriptor_data * d);
void redit_disp_menu(struct descriptor_data * d);
void redit_parse(struct descriptor_data * d, char *arg);
void write_world_to_disk(int vnum);

/**************************************************************************
 Menu functions
 **************************************************************************/

void redit_disp_barrier_menu(struct descriptor_data *d)
{
  CLS(CH);

  for (register int counter = 0; counter < NUM_BARRIERS; ++counter)
    send_to_char(CH, "%2d) %s\r\n", counter + 1, barrier_names[counter]);
  send_to_char("Enter construction category, 0 to return: ", CH);
}

void redit_disp_material_menu(struct descriptor_data *d)
{
  CLS(CH);

  for (register int counter = 0; counter < NUM_MATERIALS; ++counter)
    send_to_char(CH, "%2d) %s\r\n", counter + 1, material_names[counter]);
  send_to_char("Enter material type, 0 to return: ", CH);
}


/* For extra descriptions */
void redit_disp_extradesc_menu(struct descriptor_data * d)
{
  struct extra_descr_data *extra_desc =
  (struct extra_descr_data *) * d->misc_data;

  send_to_char(CH, "Extra descript menu\r\n"
                   "0) Quit\r\n"
                   "1) Keyword: %s%s%s\r\n"
                   "2) Description:\r\n%s\r\n", CCCYN(CH, C_CMP),
      extra_desc->keyword, CCNRM(CH, C_CMP),
     (extra_desc->description ? extra_desc->description : "(none)"));
  send_to_char(CH, "3) %s\r\n"
                   "Enter Choice:\r\n",
     (extra_desc->next ? "Another description set. (not viewed)" : "Another description"));
  d->edit_mode = REDIT_EXTRADESC_MENU;
}

/* For exits */
void redit_disp_exit_menu(struct descriptor_data * d)
{
  CLS(CH);
  #define DOOR d->edit_room->dir_option[d->edit_number2]
  /* if exit doesn't exist, alloc/create it and clear it*/
  if(!DOOR)
  {
    DOOR = new room_direction_data;
    memset((char *) DOOR, 0, sizeof (struct room_direction_data));
    DOOR->barrier = 4;
    DOOR->condition = DOOR->barrier;
    DOOR->material = 5;
  }

  send_to_char(CH,      "1) Exit to: %s%d%s\r\n"
                        "2) Description: %s\r\n",
    CCCYN(CH, C_CMP), DOOR->to_room_vnum, CCNRM(CH, C_CMP),
    (DOOR->general_description ? DOOR->general_description : "(None)"));
  send_to_char(CH,      "3) Door names: %s%s%s\r\n"
                        "4) Key vnum: %s%d%s\r\n"
                        "5) Door flag: %s%s%s\r\n",
   CCCYN(CH, C_CMP), (DOOR->keyword ? DOOR->keyword : "(none)"),
   CCNRM(CH, C_CMP), CCCYN(CH, C_CMP), DOOR->key, CCNRM(CH, C_CMP),
   CCCYN(CH, C_CMP), (IS_SET(DOOR->exit_info, EX_ISDOOR) ?
   (IS_SET(DOOR->exit_info, EX_PICKPROOF) ? "Pickproof" : "Regular door") :
    "No door"), CCNRM(CH, C_CMP));

  send_to_char(CH,        "6) Lock level: %s%d%s\r\n"
                       "7) Material Type: %s%s%s\r\n"
                        "8) Barrier Rating: %s%d%s\r\n",
   CCCYN(CH, C_CMP), DOOR->key_level,
   CCNRM(CH, C_CMP), CCCYN(CH, C_CMP), material_names[DOOR->material],
   CCNRM(CH, C_CMP), CCCYN(CH, C_CMP), DOOR->barrier, CCNRM(CH, C_CMP));


 send_to_char(CH,       "9) Hidden Rating: %s%d%s\r\n"
                        "a) Purge exit.\r\n"
                        "Enter choice, 0 to quit:",
   CCCYN(CH, C_CMP), DOOR->hidden, CCNRM(CH, C_CMP));
  d->edit_mode = REDIT_EXIT_MENU;
}

/* For exit flags */
void redit_disp_exit_flag_menu(struct descriptor_data * d)
{
  send_to_char( "0) No door\r\n"
                "1) Closeable door\r\n"
                "2) Pickproof\r\n"
                "Enter choice:", CH);
}

/* For room flags */
void redit_disp_flag_menu(struct descriptor_data * d)
{
  int             counter;

  CLS(CH);
  for (counter = 0; counter < NUM_ROOM_FLAGS; counter += 2) {
    send_to_char(CH, "%2d) %-10s        %2d) %-10s\r\n",
            counter + 1, room_bits[counter],
            counter + 2, counter + 1 < NUM_ROOM_FLAGS ?
            room_bits[counter + 1] : "");
  }
  sprintbit(d->edit_room->room_flags, room_bits, buf1);
  send_to_char(CH, "Room flags: %s%s%s\r\n"
                   "Enter room flags, 0 to quit:", CCCYN(CH, C_CMP),
                   buf1, CCNRM(CH, C_CMP));
  d->edit_mode = REDIT_FLAGS;
}

/* for sector type */
void redit_disp_sector_menu(struct descriptor_data * d)
{
  int             counter;

  CLS(CH);
  for (counter = 0; counter < NUM_ROOM_SECTORS; counter += 2) {
    sprintf(buf, "%2d) %-10s     %2d) %-10s\r\n",
            counter, sector_types[counter],
            counter + 1, counter + 1 < NUM_ROOM_SECTORS ?
            sector_types[counter + 1] : "");
    send_to_char(buf, d->character);
  }
  send_to_char("Enter sector type:", d->character);
  d->edit_mode = REDIT_SECTOR;
}

void redit_disp_func_menu(struct descriptor_data *d)
{
  int i;

  CLS(CH);
  d->edit_mode = REDIT_ROOM_FUNCTION;
  for (i = 0; i <= NUM_ROOM_FUNCS; i++)
    send_to_char(CH, "  %d) %s\r\n", i, room_functions[i]);
  send_to_char("Enter room function: ", CH);
}

/* the main menu */
void redit_disp_menu(struct descriptor_data * d)
{

  CLS(CH);
  d->edit_mode = REDIT_MAIN_MENU;
  send_to_char(CH, "Room number: %s%d%s\r\n"
                   "1) Room name: %s%s%s\r\n",
                   CCCYN(CH, C_CMP),
                   d->edit_number, CCNRM(CH, C_CMP),
                   CCCYN(CH, C_CMP), d->edit_room->name, CCNRM(CH, C_CMP));
  send_to_char(CH, "2) Room Desc:\r\n%s\r\n", d->edit_room->description);
  send_to_char(CH, "Room zone: %s%d%s\r\n",
    CCCYN(CH, C_CMP), zone_table[d->edit_room->zone].number, CCNRM(CH, C_CMP));
  sprintbit((long) d->edit_room->room_flags, room_bits, buf2);
  send_to_char(CH, "3) Room flags: %s%s%s\r\n", CCCYN(CH, C_CMP), buf2, CCNRM(CH, C_CMP));
  sprinttype(d->edit_room->sector_type, sector_types, buf2);
  send_to_char(CH, "4) Sector type: %s%s%s\r\n",CCCYN(CH, C_CMP), buf2, CCNRM(CH, C_CMP));

  if (d->edit_room->dir_option[NORTH])
    send_to_char(CH, "5) Exit north to:     %s%d%s\r\n", CCCYN(CH, C_CMP),
                 d->edit_room->dir_option[NORTH]->to_room_vnum, CCNRM(CH, C_CMP));
  else
    send_to_char(    "5) Exit north to:     (none)\r\n", CH);
  if (d->edit_room->dir_option[NORTHEAST])
    send_to_char(CH, "6) Exit northeast to: %s%d%s\r\n", CCCYN(CH, C_CMP),
                 d->edit_room->dir_option[NORTHEAST]->to_room_vnum, CCNRM(CH, C_CMP));
  else
    send_to_char(    "6) Exit northeast to: (none)\r\n", CH);
  if (d->edit_room->dir_option[EAST])
    send_to_char(CH, "7) Exit east to:      %s%d%s\r\n", CCCYN(CH, C_CMP),
                 d->edit_room->dir_option[EAST]->to_room_vnum, CCNRM(CH, C_CMP));
  else
    send_to_char(    "7) Exit east to:      (none)\r\n", CH);
  if (d->edit_room->dir_option[SOUTHEAST])
    send_to_char(CH, "8) Exit southeast to: %s%d%s\r\n", CCCYN(CH, C_CMP),
                 d->edit_room->dir_option[SOUTHEAST]->to_room_vnum, CCNRM(CH, C_CMP));
  else
    send_to_char(    "8) Exit southeast to: (none)\r\n", CH);
  if (d->edit_room->dir_option[SOUTH])
    send_to_char(CH, "9) Exit south to:     %s%d%s\r\n", CCCYN(CH, C_CMP),
                 d->edit_room->dir_option[SOUTH]->to_room_vnum, CCNRM(CH, C_CMP));
  else
    send_to_char(    "9) Exit south to:     (none)\r\n", CH);
  if (d->edit_room->dir_option[SOUTHWEST])
    send_to_char(CH, "a) Exit southwest to: %s%d%s\r\n", CCCYN(CH, C_CMP),
                 d->edit_room->dir_option[SOUTHWEST]->to_room_vnum, CCNRM(CH, C_CMP));
  else
    send_to_char(    "a) Exit southwest to: (none)\r\n", CH);
  if (d->edit_room->dir_option[WEST])
    send_to_char(CH, "b) Exit west to:      %s%d%s\r\n", CCCYN(CH, C_CMP),
                 d->edit_room->dir_option[WEST]->to_room_vnum, CCNRM(CH, C_CMP));
  else
    send_to_char(    "b) Exit west to:      (none)\r\n", CH);
  if (d->edit_room->dir_option[NORTHWEST])
    send_to_char(CH, "c) Exit northwest to: %s%d%s\r\n", CCCYN(CH, C_CMP),
                 d->edit_room->dir_option[NORTHWEST]->to_room_vnum, CCNRM(CH, C_CMP));
  else
    send_to_char(    "c) Exit northwest to: (none)\r\n", CH);
  if (d->edit_room->dir_option[UP])
    send_to_char(CH, "d) Exit up to:        %s%d%s\r\n", CCCYN(CH, C_CMP),
                 d->edit_room->dir_option[UP]->to_room_vnum, CCNRM(CH, C_CMP));
  else
    send_to_char(    "d) Exit up to:        (none)\r\n", CH);
  if (d->edit_room->dir_option[DOWN])
    send_to_char(CH, "e) Exit down to:      %s%d%s\r\n", CCCYN(CH, C_CMP),
                 d->edit_room->dir_option[DOWN]->to_room_vnum, CCNRM(CH, C_CMP));
  else
    send_to_char(    "e) Exit down to:      (none)\r\n", CH);
  if (d->edit_room->dir_option[MATRIX])
    send_to_char(CH, "f) Exit matrix to:    %s%d%s\r\n", CCCYN(CH, C_CMP),
                 d->edit_room->dir_option[MATRIX]->to_room_vnum, CCNRM(CH, C_CMP));
  else
    send_to_char(    "f) Exit matrix to:    (none)\r\n", CH);

  send_to_char("g) Extra descriptions\r\n", d->character);
  // send this option if they make the room a library
  if (ROOM->sector_type == SECT_WATER_SWIM)
    send_to_char(CH, "h) Current rating: %s%d%s\r\n", CCCYN(CH, C_CMP),
                 ROOM->rating, CCNRM(CH, C_CMP));
  else if (IS_SET(ROOM->room_flags, ROOM_HERMETIC_LIBRARY) ||
      IS_SET(ROOM->room_flags, ROOM_MEDICINE_LODGE) ||
      IS_SET(ROOM->room_flags, ROOM_FALL))
    send_to_char(CH, "h) Library or Climb Rating: %s%d%s\r\n", CCCYN(CH, C_CMP),
       ROOM->rating, CCNRM(CH, C_CMP));
  else if (IS_SET(ROOM->room_flags, ROOM_MATRIX)) {
    send_to_char(CH, "h) Node rating: %s%d%s\r\n", CCCYN(CH, C_CMP), ROOM->rating, CCNRM(CH, C_CMP));
    if (ROOM->sector_type >= SECT_BLUE)
      send_to_char(CH, "i) Node function: %s%s%s\r\n", CCCYN(CH, C_CMP),
                       room_functions[ROOM->spec], CCNRM(CH, C_CMP));
  }
  if (access_level(CH, LVL_PRESIDENT))
    send_to_char(CH, "s) MSP trigger: %s%s%s\r\n", CCCYN(CH, C_CMP), ROOM->msp_trigger ?
                 ROOM->msp_trigger : "(none)", CCNRM(CH, C_CMP));
  send_to_char("q) Quit and save\r\n", d->character);
  send_to_char("x) Exit and abort\r\n", d->character);
  send_to_char("Enter your choice:\r\n", d->character);
}

/**************************************************************************
  The main loop
 **************************************************************************/

void redit_parse(struct descriptor_data * d, char *arg)
{
  extern struct room_data *world;
  SPECIAL(cpu);
  SPECIAL(datastore);
  SPECIAL(input_output);
  SPECIAL(spu);
  SPECIAL(system_access);
  SPECIAL(slave);

  int             number;
  int             room_num;
  int min, max;
  switch (d->edit_mode) {
  case REDIT_CONFIRM_EDIT:
    switch (*arg) {
    case 'y':
    case 'Y':
      // put the allocation over here!
      redit_disp_menu(d);
      break;
    case 'n':
    case 'N':
      /* player doesn't want to edit, free entire temp room */
      STATE(d) = CON_PLAYING;
      if (d->edit_room)
        Mem->DeleteRoom(d->edit_room);
      d->edit_room = NULL;
      REMOVE_BIT(PLR_FLAGS(d->character), PLR_EDITING);
      char_to_room(CH, GET_WAS_IN(CH));
      GET_WAS_IN(CH) = NOWHERE;
      break;
    default:
      send_to_char("That's not a valid choice!\r\n", d->character);
      send_to_char("Do you wish to edit it?\r\n", d->character);
      break;
    }
    break; /* end of REDIT_CONFIRM_EDIT */
  case REDIT_CONFIRM_SAVESTRING:
    switch (*arg) {
    case 'y':
    case 'Y':
      {
        int             counter, counter2;
        struct extra_descr_data *This, *next_one;
        room_num = real_room(d->edit_number);
        if (room_num > 0) {
          /* copy people/object pointers over to the temp room
             as a temporary measure */
          d->edit_room->contents = world[room_num].contents;
          d->edit_room->people = world[room_num].people;
          // we use free_room here because we aren't ready to turn it over
          // to the stack just yet as we're gonna use it immediately
          free_room(world + room_num);
          /* now copy everything over! */
          world[room_num] = *d->edit_room;
        } else {
          /* hm, we can't just copy.. gotta insert a new room */
          int             counter;
          int             counter2;
          int             found = 0;
          // check first if you need to resize it
          if ((top_of_world + 1) >= top_of_world_array)
            // if it can't resize, free the edit_room and return
            if (!resize_world_array()) {
              send_to_char("Unable to save, OLC temporarily unavailable.\r\n"
                           ,CH);
              Mem->DeleteRoom(d->edit_room);
              olc_state = 0;
              d->edit_room = NULL;
              REMOVE_BIT(PLR_FLAGS(d->character), PLR_EDITING);
              STATE(d) = CON_PLAYING;
              char_to_room(CH, GET_WAS_IN(CH));
              GET_WAS_IN(CH) = NOWHERE;
              return;
            }
        
        
          /* count thru world tables */
          for (counter = 0; counter < top_of_world + 1; counter++) {
          if (!found) {
            /* check if current virtual is bigger than our virtual */
            if (world[counter].number > d->edit_number) {
              // now, zoom backwards through the list copying over
              for (counter2 = top_of_world + 1; counter2 > counter; counter2--) {
                world[counter2] = world[counter2 - 1];
              }
                
              world[counter] = *(d->edit_room);
              world[counter].number = d->edit_number;
              world[counter].func = NULL;
              found = TRUE;
              }
          } else {
              // okay, it's been found and inserted
              // anything in a room after the one inserted needs their
              // real numbers increased
              struct char_data *temp_ch;
              struct obj_data *temp_obj;
              for (temp_ch = world[counter].people; temp_ch;
                   temp_ch = temp_ch->next_in_room)  {
                if (temp_ch->in_room != NOWHERE)
                  temp_ch->in_room++;
              }

              /* move objects */
              for (temp_obj = world[counter].contents; temp_obj;
                   temp_obj = temp_obj->next_content)
                if (temp_obj->in_room != -1)
                  temp_obj->in_room++;
            } // end else
          } // end 'insert' for-loop
                
          /* if place not found, insert at end */
          if (!found)
          {
            world[top_of_world + 1] = *d->edit_room;
            world[top_of_world + 1].number = d->edit_number;
            world[top_of_world + 1].func = NULL;
          }
          top_of_world++;
          /* now this is the *real* room_num */
          room_num = real_room(d->edit_number);

          /* now zoom through the character list and update anyone in limbo */
          struct char_data * temp_ch;
          for (temp_ch = character_list; temp_ch; temp_ch = temp_ch->next) {
             if (GET_WAS_IN(temp_ch) >= room_num)
               GET_WAS_IN(temp_ch)++;
          }
          /* update zone tables */
          {
            extern int top_of_zone_table;
            int zone, cmd_no;

            for (zone = 0; zone <= top_of_zone_table; zone++)
              for (cmd_no = 0; cmd_no < zone_table[zone].num_cmds; cmd_no++)
                {
                  switch (ZCMD.command)
                    {
                      case 'M':
                        ZCMD.arg3 = (ZCMD.arg3 >= room_num ? ZCMD.arg3 + 1 :
                                     ZCMD.arg3);
                        break;
                      case 'O':
                        if (ZCMD.arg3 != NOWHERE)
                          ZCMD.arg3 =
                            (ZCMD.arg3 >= room_num ?
                             ZCMD.arg3 + 1 : ZCMD.arg3);
                        break;
                      case 'D':
                        ZCMD.arg1 =
                          (ZCMD.arg1 >= room_num ?
                           ZCMD.arg1 + 1 : ZCMD.arg1);
                        break;
                      case 'R': /* rem obj from room */
                        ZCMD.arg1 =
                          (ZCMD.arg1 >= room_num ?
                           ZCMD.arg1 + 1 : ZCMD.arg1);
                        break;
                    }
                }
          }

          /* update load rooms, to fix creeping load room problem */
          if (room_num <= r_mortal_start_room)
            r_mortal_start_room++;
          if (room_num <= r_immort_start_room)
            r_immort_start_room++;
          if (room_num <= r_frozen_start_room)
            r_frozen_start_room++;
          if (room_num <= r_newbie_start_room)
            r_newbie_start_room++;
          /* go through the world. if any of the old rooms indicated an exit
           * to our new room, we have to change it */
          for (counter = 0; counter < top_of_world + 1; counter++) {
            for (counter2 = 0; counter2 < NUM_OF_DIRS; counter2++) {
              /* if exit exists */
              if (world[counter].dir_option[counter2]) {
                /* increment r_nums for rooms bigger than or equal to new one
                 * because we inserted room */
                if (world[counter].dir_option[counter2]->to_room >= room_num)
                  world[counter].dir_option[counter2]->to_room += 1;
                /* if an exit to the new room is indicated, change to_room */
                if (world[counter].dir_option[counter2]->to_room_vnum == d->edit_number)
                  world[counter].dir_option[counter2]->to_room = room_num;
              }
            }
          }
        } // end 'insert' else
        /* resolve all vnum doors to rnum doors in the newly edited room */
        int opposite;
        for (counter2 = 0; counter2 < NUM_OF_DIRS; counter2++) {        
          if (world[room_num].dir_option[counter2]) {
            world[room_num].dir_option[counter2]->to_room =
                       real_room(world[room_num].dir_option[counter2]->to_room_vnum);
            if (counter2 < MATRIX) {
              opposite = world[room_num].dir_option[counter2]->to_room;
              if (opposite != NOWHERE && world[opposite].dir_option[rev_dir[counter2]] &&
                  world[opposite].dir_option[rev_dir[counter2]]->to_room == room_num) {
                world[opposite].dir_option[rev_dir[counter2]]->material =
                     world[room_num].dir_option[counter2]->material;
                world[opposite].dir_option[rev_dir[counter2]]->barrier =
                     world[room_num].dir_option[counter2]->barrier;
                world[opposite].dir_option[rev_dir[counter2]]->condition =
                     world[room_num].dir_option[counter2]->condition;
              }
            }
          }
        }
        send_to_char("Writing room to disk.\r\n", d->character);
        write_world_to_disk(d->character->player_specials->saved.zonenum);
        send_to_char("Saved.\r\n", CH);
        /* do NOT free strings! just the room structure */
        Mem->ClearRoom(d->edit_room);
        d->edit_room = NULL;
        REMOVE_BIT(PLR_FLAGS(d->character), PLR_EDITING);
        STATE(d) = CON_PLAYING;
        char_to_room(CH, GET_WAS_IN(CH));
        GET_WAS_IN(CH) = NOWHERE;
        send_to_char("Done.\r\n", d->character);
        break;
      }
    case 'n':
    case 'N':
      send_to_char("Room not saved, aborting.\r\n", d->character);
      STATE(d) = CON_PLAYING;
      REMOVE_BIT(PLR_FLAGS(d->character), PLR_EDITING);
      /* free everything up, including strings etc */
      if (d->edit_room)
        Mem->DeleteRoom(d->edit_room);
      d->edit_room = NULL;
      d->edit_number = 0;
      char_to_room(CH, GET_WAS_IN(CH));
      GET_WAS_IN(CH) = NOWHERE;
      break;
    default:
      send_to_char("Invalid choice!\r\n", d->character);
      send_to_char("Do you wish to save this room internally?", d->character);
      break;
    }
    break; /* end of REDIT_CONFIRM_SAVESTRING */
  case REDIT_MAIN_MENU:
    switch (LOWER(*arg)) {
    case 'q':
      d->edit_mode = REDIT_CONFIRM_SAVESTRING;
#if CONFIRM_SAVE
      send_to_char("Do you wish to save this room internally?\r\n", d->character);
#else
      redit_parse(d, "y");
#endif
      break;
    case 'x':
      d->edit_mode = REDIT_CONFIRM_SAVESTRING;
#if CONFIRM_SAVE
      send_to_char("Do you wish to save this room internally?\r\n", d->character);
#else
      redit_parse(d, "n");
#endif
      break;
    case '1':
      send_to_char("Enter room name:", d->character);
      d->edit_mode = REDIT_NAME;
      break;
    case '2':
      send_to_char("Enter room description:\r\n", d->character);
      d->edit_mode = REDIT_DESC;
      //d->str = (char **) xmalloc(sizeof(char *));
      d->str = new (char *);
      if (!d->str) {
        mudlog("Malloc failed!", NULL, LOG_SYSLOG, TRUE);
        exit(1);
      }

      *(d->str) = NULL;
      d->max_str = MAX_MESSAGE_LENGTH;
      d->mail_to = 0;
      break;
    case '3':
      redit_disp_flag_menu(d);
      break;
    case '4':
      redit_disp_sector_menu(d);
      break;
    case '5':
      d->edit_number2 = NORTH;
      redit_disp_exit_menu(d);
      break;
    case '6':
      d->edit_number2 = NORTHEAST;
      redit_disp_exit_menu(d);
      break;
    case '7':
      d->edit_number2 = EAST;
      redit_disp_exit_menu(d);
      break;
    case '8':
      d->edit_number2 = SOUTHEAST;
      redit_disp_exit_menu(d);
      break;
    case '9':
      d->edit_number2 = SOUTH;
      redit_disp_exit_menu(d);
      break;
    case 'a':
      d->edit_number2 = SOUTHWEST;
      redit_disp_exit_menu(d);
      break;
    case 'b':
      d->edit_number2 = WEST;
      redit_disp_exit_menu(d);
      break;
    case 'c':
      d->edit_number2 = NORTHWEST;
      redit_disp_exit_menu(d);
      break;
    case 'd':
      d->edit_number2 = UP;
      redit_disp_exit_menu(d);
      break;
    case 'e':
      d->edit_number2 = DOWN;
      redit_disp_exit_menu(d);
      break;
    case 'f':
      d->edit_number2 = MATRIX;
      redit_disp_exit_menu(d);
      break;
    case 'g':
      /* if extra desc doesn't exist . */
      if (!d->edit_room->ex_description) {
        d->edit_room->ex_description = new extra_descr_data;
        memset((char *) d->edit_room->ex_description, 0,
              sizeof(struct extra_descr_data));
      }
      d->misc_data = (void **) &(d->edit_room->ex_description);
      redit_disp_extradesc_menu(d);
      break;
    case 'h':
      if (ROOM->sector_type == SECT_WATER_SWIM) {
        send_to_char("Enter current rating (1 to 20): ", CH);
        d->edit_mode = REDIT_LIBRARY_RATING;
      } else if (IS_SET(ROOM->room_flags, ROOM_HERMETIC_LIBRARY) ||
          IS_SET(ROOM->room_flags, ROOM_MEDICINE_LODGE) ||
          IS_SET(ROOM->room_flags, ROOM_FALL)) {
        send_to_char("Enter library (or climb) rating (1 to 20): ", CH);
        d->edit_mode = REDIT_LIBRARY_RATING;
      } else if (IS_SET(ROOM->room_flags, ROOM_MATRIX)) {
        send_to_char("Enter node rating: ", CH);
        d->edit_mode = REDIT_LIBRARY_RATING;
      } else {
        redit_disp_menu(d);
        return;
      }
      break;
    case 'i':
      if (IS_SET(ROOM->room_flags, ROOM_MATRIX) && ROOM->sector_type >= SECT_BLUE)
        redit_disp_func_menu(d);
      else redit_disp_menu(d);
      break;
    case 's':
      if (access_level(CH, LVL_PRESIDENT)) {
        send_to_char("Enter room MSP trigger (or \"clear\"): ", CH);
        d->edit_mode = REDIT_MSP_TRIGGER;
      } else redit_disp_menu(d);
      break;
    default:
      send_to_char("Invalid choice!", d->character);
      redit_disp_menu(d);
      break;
    }
    break;
  case REDIT_NAME:
    if (d->edit_room->name)
      delete [] d->edit_room->name;
    d->edit_room->name = str_dup(arg);
    redit_disp_menu(d);
    break;
  case REDIT_DESC:
    /* we will NEVER get here */
    break;
  case REDIT_FLAGS:
    number = atoi(arg);
    if ((number < 0) || (number > NUM_ROOM_FLAGS)) {
      send_to_char("That's not a valid choice!\r\n", d->character);
      redit_disp_flag_menu(d);
    } else {
      if (number == 0)
        /* back out */
        redit_disp_menu(d);
      else {
        /* toggle bits */
        if (IS_SET(d->edit_room->room_flags, 1 << (number - 1))) {
          REMOVE_BIT(d->edit_room->room_flags, 1 << (number - 1));
        if ((1 << (number - 1)) == ROOM_MATRIX && ROOM->spec) {
          ROOM->func = NULL;
          ROOM->spec = 0;
        }
        } else SET_BIT(d->edit_room->room_flags, 1 << (number - 1));
        redit_disp_flag_menu(d);
      }
    }
    break;
  case REDIT_SECTOR:
    number = atoi(arg);
    if (number < 0 || number >= NUM_ROOM_SECTORS) {
      send_to_char("Invalid choice!", d->character);
      redit_disp_sector_menu(d);
    } else {
      d->edit_room->sector_type = number;
      if (number < SECT_BLUE && ROOM->spec) {
        ROOM->func = NULL;
        ROOM->spec = 0;
      }
      redit_disp_menu(d);
    }
    break;
  case REDIT_EXIT_MENU:
    switch (*arg) {
    case '0':
      redit_disp_menu(d);
      break;
    case '1':
      d->edit_mode = REDIT_EXIT_NUMBER;
      send_to_char("Exit to room number:", d->character);
      break;
    case '2':
      d->edit_mode = REDIT_EXIT_DESCRIPTION;
      //d->str = (char **) xmalloc(sizeof(char *));
      d->str = new (char *);
      if (!d->str) {
        mudlog("Malloc failed!", NULL, LOG_SYSLOG, TRUE);
        exit(1);
      }

      *(d->str) = NULL;
      d->max_str = MAX_MESSAGE_LENGTH;
      d->mail_to = 0;
      send_to_char("Enter exit description:\r\n", d->character);
      break;
    case '3':
      d->edit_mode = REDIT_EXIT_KEYWORD;
      send_to_char("Enter keywords:", d->character);
      break;
    case '4':
      d->edit_mode = REDIT_EXIT_KEY;
      send_to_char("Enter key number:", d->character);
      break;
    case '5':
      redit_disp_exit_flag_menu(d);
      d->edit_mode = REDIT_EXIT_DOORFLAGS;
      break;
    case '6':
      d->edit_mode = REDIT_EXIT_KEY_LEV;
      send_to_char("Enter lock level:", CH);
      break;
    case '7':
      d->edit_mode = REDIT_EXIT_MATERIAL;
      redit_disp_material_menu(d);
      break;
    case '8':
      d->edit_mode = REDIT_EXIT_BARRIER;
      redit_disp_barrier_menu(d);
      break;
    case '9':
      d->edit_mode = REDIT_EXIT_HIDDEN;
      send_to_char("Enter hidden rating of the exit: ", CH);
      break;
    case 'a':
      /* delete exit */
      if (d->edit_room->dir_option[d->edit_number2]->keyword)
        delete [] d->edit_room->dir_option[d->edit_number2]->keyword;
      if (d->edit_room->dir_option[d->edit_number2]->general_description)
        delete [] d->edit_room->dir_option[d->edit_number2]->general_description;
      delete d->edit_room->dir_option[d->edit_number2];
      d->edit_room->dir_option[d->edit_number2] = NULL;
      redit_disp_menu(d);
      break;
    default:
      send_to_char("Invalid choice!\r\n", d->character);
      break;
    }
    break;
  case REDIT_EXIT_NUMBER:
    number = atoi(arg);
    if (number < 0)
      send_to_char("Invalid choice!\r\nExit to room number:", d->character);
    else {
      d->edit_room->dir_option[d->edit_number2]->to_room_vnum = number;
      redit_disp_exit_menu(d);
    }
    break;
  case REDIT_EXIT_DESCRIPTION:
    /* we should NEVER get here */
    break;
  case REDIT_EXIT_KEYWORD:
    if (d->edit_room->dir_option[d->edit_number2]->keyword)
      delete [] d->edit_room->dir_option[d->edit_number2]->keyword;
    d->edit_room->dir_option[d->edit_number2]->keyword = str_dup(arg);
    redit_disp_exit_menu(d);
    break;
  case REDIT_EXIT_KEY:
    number = atoi(arg);
    d->edit_room->dir_option[d->edit_number2]->key = number;
    redit_disp_exit_menu(d);
    break;
  case REDIT_EXIT_KEY_LEV:
    number = atoi(arg);
    DOOR->key_level = MAX(0, MIN(50, number));
    redit_disp_exit_menu(d);
    break;
  case REDIT_EXIT_HIDDEN:
    number = atoi(arg);
    DOOR->hidden = MAX(0, MIN(50, number));
    redit_disp_exit_menu(d);
    break;
  case REDIT_EXIT_MATERIAL:
    number = atoi(arg);
    if (number == 0)
      redit_disp_exit_menu(d);
    else if (number > 0 && number <= NUM_MATERIALS) {
      DOOR->material = number - 1;
      redit_disp_exit_menu(d);
    } else redit_disp_material_menu(d);
    break;
  case REDIT_EXIT_BARRIER:
    number = atoi(arg);
    if (number == 0)
      redit_disp_exit_menu(d);
    else if (number > 0 && number <= NUM_BARRIERS) {
      DOOR->condition = DOOR->barrier = barrier_ratings[number - 1];
      redit_disp_exit_menu(d);
    } else redit_disp_barrier_menu(d);
    break;

  case REDIT_LIBRARY_RATING:
    number = atoi(arg);
    if (IS_SET(ROOM->room_flags, ROOM_HERMETIC_LIBRARY) ||
        IS_SET(ROOM->room_flags, ROOM_MEDICINE_LODGE) ||
        IS_SET(ROOM->room_flags, ROOM_FALL) || ROOM->sector_type == SECT_WATER_SWIM) {
      if ((number < 0) || (number > 20)) {
        send_to_char("Value must be between 1 and 20.\r\n", CH);
        if (ROOM->sector_type == SECT_WATER_SWIM)
          send_to_char("Enter current rating: ", CH);
        else send_to_char("Enter library (or climb) rating: ", CH);
      } else {
        ROOM->rating = number;
        redit_disp_menu(d);
      }
    } else if (IS_SET(ROOM->room_flags, ROOM_MATRIX)) {
      switch (ROOM->sector_type) {
        case SECT_BLUE:
          if (number < 1 || number > 6) {
            send_to_char("Rating must be between 1 and 6.\r\n", CH);
            return;
          }
          break;
        case SECT_GREEN:
          if (number < 2 || number > 7) {
            send_to_char("Rating must be between 2 and 7.\r\n", CH);
            return;
          }
          break;
        case SECT_ORANGE:
          if (number < 3 || number > 8) {
            send_to_char("Rating must be between 3 and 8.\r\n", CH);
            return;
          }
          break;
        case SECT_RED:
          if (number < 2 || number > 12) {
            send_to_char("Rating must be between 2 and 12.\r\n", CH);
            return;
          }
          break;
        case SECT_BLACK:
          if (number < 3 || number > 13) {
            send_to_char("Rating must be between 3 and 13.\r\n", CH);
            return;
          }
          break;
        default:
          if (number < 1 || number > 12) {
            send_to_char("Rating must be between 1 and 6.\r\n", CH);
            return;
          }
          break;
      }
      ROOM->rating = number;
      redit_disp_menu(d);
    }
    break;
  case REDIT_EXIT_DOORFLAGS:
    number = atoi(arg);
    if ((number < 0) || (number > 2)) {
      send_to_char("That's not a valid choice!\r\n", d->character);
      redit_disp_exit_flag_menu(d);
    } else {
      /* doors are a bit idiotic, don't you think? :) */
      if (number == 0)
        d->edit_room->dir_option[d->edit_number2]->exit_info = 0;
      else if (number == 1)
        d->edit_room->dir_option[d->edit_number2]->exit_info = EX_ISDOOR;
      else if (number == 2)
        d->edit_room->dir_option[d->edit_number2]->exit_info =
          EX_ISDOOR | EX_PICKPROOF;
      /* jump out to menu */
      redit_disp_exit_menu(d);
    }
    break;
  case REDIT_ROOM_FUNCTION:
    number = atoi(arg);
    if (number < 0 || number > NUM_ROOM_FUNCS)
      redit_disp_func_menu(d);
    else {
      ROOM->spec = number;
      switch (number) {
        case 1 : ROOM->func = cpu;           break;
        case 2 : ROOM->func = datastore;     break;
        case 3 : ROOM->func = input_output;  break;
        case 4 : ROOM->func = spu;           break;
        case 5 : ROOM->func = system_access; break;
        case 6 : ROOM->func = slave;         break;
        default:
          if (ROOM->func == cpu || ROOM->func == datastore || ROOM->func == spu ||
              ROOM->func == input_output || ROOM->func == system_access ||
              ROOM->func == slave)
            ROOM->func = NULL;
          break;
      }
      redit_disp_menu(d);
    }
    break;
  case REDIT_EXTRADESC_KEY:
    if (((struct extra_descr_data *) *d->misc_data)->keyword)
      delete [] (((struct extra_descr_data *) *d->misc_data)->keyword);
    ((struct extra_descr_data *) * d->misc_data)->keyword = str_dup(arg);
    redit_disp_extradesc_menu(d);
    break;
  case REDIT_EXTRADESC_MENU:
    number = atoi(arg);
    switch (number) {
    case 0:
      {
        /* if something got left out, delete the extra desc
         when backing out to menu */
        if (!((struct extra_descr_data *) * d->misc_data)->keyword ||
            !((struct extra_descr_data *) * d->misc_data)->description) {
          if (((struct extra_descr_data *) * d->misc_data)->keyword)
            delete [] ((struct extra_descr_data *) * d->misc_data)->keyword;
          if (((struct extra_descr_data *) * d->misc_data)->description)
            delete [] ((struct extra_descr_data *) * d->misc_data)->description;

          delete *d->misc_data;
          *d->misc_data = NULL;
        }
        /* else, we don't need to do anything.. jump to menu */
        redit_disp_menu(d);
      }
      break;
    case 1:
      d->edit_mode = REDIT_EXTRADESC_KEY;
      send_to_char("Enter keywords, separated by spaces:", d->character);
      break;
    case 2:
      d->edit_mode = REDIT_EXTRADESC_DESCRIPTION;
      send_to_char("Enter extra description:\r\n", d->character);
      /* send out to modify.c */
      //d->str = (char **) xmalloc(sizeof(char *));
      d->str = new (char *);
      if (!d->str) {
        mudlog("Malloc failed!", NULL, LOG_SYSLOG, TRUE);
        exit(1);
      }

      *(d->str) = NULL;
      d->max_str = MAX_MESSAGE_LENGTH;
      d->mail_to = 0;
      break;
    case 3:
      if (!((struct extra_descr_data *) *d->misc_data)->keyword ||
          !((struct extra_descr_data *) *d->misc_data)->description) {
        send_to_char("You can't edit the next extra desc without completing this one.\r\n", d->character);
        redit_disp_extradesc_menu(d);
      } else {
        struct extra_descr_data *new_extra;

        if (((struct extra_descr_data *) * d->misc_data)->next)
          d->misc_data = (void **) &((struct extra_descr_data *) * d->misc_data)->next;
        else {
          /* make new extra, attach at end */
          new_extra = new extra_descr_data;
          memset((char *) new_extra, 0, sizeof(extra_descr_data));
          ((struct extra_descr_data *) * d->misc_data)->next = new_extra;
          d->misc_data =
            (void **) &((struct extra_descr_data *) * d->misc_data)->next;
        }
        redit_disp_extradesc_menu(d);
      }
    }
    break;
  case REDIT_MSP_TRIGGER:
    if (!str_cmp(arg, "clear")) {
      if (ROOM->msp_trigger)
        delete [] ROOM->msp_trigger;
      ROOM->msp_trigger = NULL;
      redit_disp_menu(d);
      return;
    } else if (strstr(arg, "MUSIC(") != arg && strstr(arg, "SOUND(") != arg) {
      send_to_char("Invalid MSP trigger!\r\nEnter room MSP trigger (or \"clear\"): ", CH);
      return;
    }
    if (ROOM->msp_trigger)
      delete [] ROOM->msp_trigger;
    ROOM->msp_trigger = str_dup(arg);
    redit_disp_menu(d);
    break;
  default:
    /* we should never get here */
    break;
  }
}

// world saving routine
#define RM world[realcounter]
void write_world_to_disk(int vnum) {
  int             counter, counter2, realcounter;
  int             znum = real_zone(vnum);
  FILE           *fp;
  struct room_data *room;
  struct extra_descr_data *ex_desc;

  sprintf(buf, "%s/%d.wld", WLD_PREFIX,
          zone_table[znum].number);
  fp = fopen(buf, "w+");
  for (counter = zone_table[znum].number * 100;
       counter <= zone_table[znum].top; counter++) {
    realcounter = real_room(counter);
    if (realcounter >= 0) {
      fprintf(fp, "#%d\n", counter);
      fprintf(fp, "%s~\n", RM.name ? RM.name : "An unnamed room");
      fprintf(fp, "%s~\n", cleanup(buf2, RM.description ? RM.description :
                                  "You see an empty room"));
      fprintf(fp, "%d %d %d %d\n", RM.spec, RM.room_flags,
              RM.sector_type, RM.rating);
      for (counter2 = 0; counter2 < NUM_OF_DIRS; counter2++) {
        if (RM.dir_option[counter2]) {
          int             temp_door_flag;
          fprintf(fp, "D%d\n", counter2);
          fprintf(fp, "%s~\n",
                  RM.dir_option[counter2]->general_description ?
                  cleanup(buf2, RM.dir_option[counter2]->general_description)
                          : "");
          fprintf(fp, "%s~\n", RM.dir_option[counter2]->keyword ?
                  RM.dir_option[counter2]->keyword : "");
          /* door flags need special handling, unfortunately. argh! */
          if (IS_SET(RM.dir_option[counter2]->exit_info, EX_ISDOOR)) {
            if (IS_SET(RM.dir_option[counter2]->exit_info, EX_PICKPROOF))
              temp_door_flag = 2;
            else
              temp_door_flag = 1;
          } else
            temp_door_flag = 0;
          fprintf(fp, "%d %d %d %d %d %d %d\n", temp_door_flag,
                  RM.dir_option[counter2]->key,
                  RM.dir_option[counter2]->to_room_vnum,
                  RM.dir_option[counter2]->key_level,
                  RM.dir_option[counter2]->material,
                  RM.dir_option[counter2]->barrier,
                  RM.dir_option[counter2]->hidden);
        }
      }
      if (RM.ex_description) {
        for (ex_desc = RM.ex_description; ex_desc; ex_desc = ex_desc->next) {
          fprintf(fp, "E\n");
          fprintf(fp, "%s~\n", ex_desc->keyword);
          fprintf(fp, "%s~\n", cleanup(buf2, ex_desc->description));
        }
      }
      if (RM.msp_trigger) {
        fprintf(fp, "M\n");
        fprintf(fp, "%s~\n", RM.msp_trigger);
      }
      fprintf(fp, "S\n");
    }
  }

  /* write final line and close */
  fprintf(fp, "$~\n");
  fclose(fp);
  write_index_file("wld");
  /* do NOT free strings! just the room structure */
}
#undef RM