/
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 "quest.h"
#include "lists.h"
#include "global.h"

extern int	material_hits[];
extern char	*affected_bits[];
extern char	*affected2_bits[];
extern char	*npc_class_names[];
extern char	*action_bits[];
extern char	*player_bits[];
extern char	*preference_bits[];
extern char	*spc_bits[];
extern char	*mob2_bits[];
extern char	*position_types[];
extern struct pc_special_data dummy_mob;

extern void init_mob(chdata *mob, int i);
extern struct mtrig_type *free_mtriggers(struct mtrig_type *head);

char *char_sex_strs[5] = { "NEUTRAL", "MALE", "FEMALE", "M/F (random)", "\n" }; 
char *mob_size_strings[] =
{
    "ENORMOUS", "huge", "large", "medium (man-sized)", "small", "very small",
    "tiny", "\n"
};

ROA_MENU(medit_top_menu);
ROA_MENU(medit_confirm_quit);
ROA_MENU(medit_confirm_save);

// 7/1/98 -jtrhone
void send_dialect_listing(chdata *ch)
{
  int i, count;

  for (i=count=0, *buf = '\0'; i < MAX_GSKILLS; i++)
  {
    if (GSKILL_FLAGGED(i, G_DIALECT))
    {
      count++;
      sprintf(buf+strlen(buf), "(%2d.) %%6%-12.12s%%0%s", i, gskill_names[i], ((count % 3) ? "" : "\n\r"));
    }
  }
  page_string(ch->desc, buf, TRUE);
}

// dust a mob... 
// added sound_file dusting 2/21/98 -jtrhone
void wax_mobile(chdata *trg)
{
  int i;
  FREENULL(trg->player.name);
  FREENULL(trg->player.short_descr);
  FREENULL(trg->player.long_descr);
  FREENULL(trg->player.description);
  FREENULL(trg->player.title);
  for (i=0; i < 5; i++)
    FREENULL(trg->npc_specials.strs[i]);
  FREENULL(trg->npc_specials.walkIn);
  FREENULL(trg->npc_specials.walkOut);
  FREENULL(trg->npc_specials.target_char);
  FREENULL(trg->npc_specials.target_obj);

  FREENULL(trg->npc_specials.sound_file);  
  FREENULL(trg->npc_specials.shop_data);  
  TRIGS(trg) = free_mtriggers(TRIGS(trg));
}

// dupe EVERYTHING over, first the strings from trg get
// waxed, then it gets a copy of all src's things
void dupe_mobile_over(chdata *src, chdata *trg)
{
  int i;

  wax_mobile(trg);

  // take care of all the basics...
  *trg = *src;

  // now make copies of the deeper stuff...
  trg->player.name        = STR_DUP(src->player.name);
  trg->player.short_descr = STR_DUP(src->player.short_descr);
  trg->player.long_descr  = STR_DUP(src->player.long_descr);
  trg->player.description = STR_DUP(src->player.description);
  for (i = 0; i < 5; i++)
    trg->npc_specials.strs[i] = STR_DUP(src->npc_specials.strs[i]);
  trg->npc_specials.strs[5] = NULL;
  trg->npc_specials.walkIn = STR_DUP(src->npc_specials.walkIn);
  trg->npc_specials.walkOut = STR_DUP(src->npc_specials.walkOut);
  trg->npc_specials.target_char = NULL;
  trg->npc_specials.target_obj = NULL;
  TRIGS(trg) = NULL;
  TRIG_WAITING(trg) = FALSE;
  TRIG_IGNORE(trg) = FALSE;

  if (src->npc_specials.shop_data)
  {
    CREATE(trg->npc_specials.shop_data, struct shop_data_type, 1);
    *trg->npc_specials.shop_data = *src->npc_specials.shop_data;
  }

  // sound_file support  2/21/98 -jtrhone
  trg->npc_specials.sound_file = STR_DUP(src->npc_specials.sound_file);
}

// stick a bunch of default values in da mob
void default_mob_out(chdata *m, int vnum)
{
  m->nr = vnum;
  m->player.name        = STR_DUP("(def)");
  m->player.short_descr = STR_DUP("(def)");
  m->player.long_descr  = STR_DUP("(def)");
  m->player.description = STR_DUP("(def)");
  m->npc_specials.walkIn = NULL;
  m->npc_specials.walkOut = NULL;
  m->real_abils.str   = 13;
  m->real_abils.str_add = 0;
  m->real_abils.intel = 13;
  m->real_abils.wis   = 13;
  m->real_abils.dex   = 13;
  m->real_abils.con   = 13;
  m->player.cls = 0;
  m->player.weight = 200;
  m->player.height = 198;
  m->points.hit     = 1;
  m->points.mana    = 1;
  m->points.move    = 1;

  /* zero values for roll arent any good -roa */
  m->npc_specials.mob_num_hit = 1;
  m->npc_specials.mob_size_hit = 1;
  m->npc_specials.mob_add_hit = 0;
  m->npc_specials.mob_num_mana = 1;
  m->npc_specials.mob_size_mana = 1;
  m->npc_specials.mob_add_mana = 0;
  m->npc_specials.mob_num_move = 1;
  m->npc_specials.mob_size_move = 1;
  m->npc_specials.mob_add_move = 0;

  m->npc_specials.default_pos = POS_STANDING;
  m->specials.position        = POS_STANDING;

  MSTR1(m) = STR_DUP("(def)");
  MSTR2(m) = STR_DUP("(def)");
  MSTR3(m) = STR_DUP("(def)");
  MSTR4(m) = STR_DUP("(def)");
  MSTR5(m) = STR_DUP("(def)");
  MPROC_CUR(m) = NULL;
  m->npc_specials.shop_data = NULL;
  TRIGS(m) = NULL;
  TRIG_WAITING(m) = FALSE;
  TRIG_IGNORE(m) = FALSE;

  // sound support...2/21/98 -jtrhone
  m->npc_specials.sound_file  = NULL;
  m->npc_specials.music_timer = -1;
}

/* function to take character into the medit (mobedit) top menu if
   everything (perms, valid args, etc) pan out ok -roa*/
ACMD(do_medit)
{
  int vnum, rnum, zone;
  char *argu = argument;
    
  if (IS_NPC(ch))
    return;
    
  skip_spaces(&argu);
  if (!*argu || !is_number(argu))
  {
    send_to_char("Usage: medit <mobile vnum>.\n\r",ch);
    return;
  }

  vnum = atoi(argu);
  zone = vnum / 100;

  if (GET_LEVEL(ch) < LEV_CIMP && (!zone || 
      zone < ch->pc_specials->saved.olc_min_zone ||
      zone > ch->pc_specials->saved.olc_max_zone))
  {
     send_to_char("You don't have privileges to edit that mob.\n\r", ch);
     return;
  }

  /* if mob exists in proto array, just copy over and edit */
  if ((rnum = real_mobile(vnum)) >= 0)
  {
    CREATE(MOB_EDITTED(ch), chdata, 1);
    dupe_mobile_over(&mob_proto[rnum], MOB_EDITTED(ch));
    MOB_EDITTED(ch)->nr = vnum;
  }
  else  /* make a NEW mob, preparing to add it to end of proto array */
  {
    /* before we do this however, lets check to see if we have padding
       space left at end of proto array */
    if (!MOB_SLOTS_LEFT)
    {
     send_to_char("Max mobile creation for this runtime reached.\n\r",ch);
     send_to_char("You must wait for reboot to create more protos.\n\r",ch);
     return;
    }

    CREATE(MOB_EDITTED(ch), chdata, 1);
    default_mob_out(MOB_EDITTED(ch), vnum);
  }

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

ROA_MENU(medit_hitdice_menu)
{
    chdata *m = MOB_EDITTED(ch);
    int max;

    switch ((int) MENU_HANDLER_ARG(ch))
    {
      case 0:
	MENU_HANDLER_ARG(ch) = (void *) 1;
	max = 32000 / m->npc_specials.mob_size_hit;
	m->npc_specials.mob_num_hit = 1;
	GET_INTEGER_ARG(ch, "How many hit dice? ", m->npc_specials.mob_num_hit, 1, max);
	break;
      case 1:
	MENU_HANDLER_ARG(ch) = (void *) 2;
	max = 32000 / m->npc_specials.mob_num_hit;
	m->npc_specials.mob_size_hit = 1;
	GET_INTEGER_ARG(ch, "What size hit dice? ", m->npc_specials.mob_size_hit, 
			1, max);
	break;
      case 2:
	MENU_HANDLER_ARG(ch) = (void *) 3;
	max = MAX(1, (32000 - (m->npc_specials.mob_size_hit * m->npc_specials.mob_num_hit)));
	m->npc_specials.mob_add_hit = 1;
	GET_INTEGER_ARG(ch, "How many additional hit points? ", 
			m->npc_specials.mob_add_hit, 1, max);
	break;
      default:
	menu_back(ch);
    }
}

ROA_MENU(medit_manadice_menu)
{
    chdata *m = MOB_EDITTED(ch);
    int max;

    switch ((int) MENU_HANDLER_ARG(ch))
    {
      case 0:
	MENU_HANDLER_ARG(ch) = (void *) 1;
	max = 32000 / m->npc_specials.mob_size_mana;
	m->npc_specials.mob_num_mana = 1;
	GET_INTEGER_ARG(ch, "How many mana dice? ",
			m->npc_specials.mob_num_mana, 1, max);
	break;

      case 1:
	MENU_HANDLER_ARG(ch) = (void *) 2;
	max = 32000 / m->npc_specials.mob_num_mana;
	m->npc_specials.mob_size_mana = 1;
	GET_INTEGER_ARG(ch, "What size mana dice? ",
			m->npc_specials.mob_size_mana, 1, max);
	break;
      case 2:
	MENU_HANDLER_ARG(ch) = (void *) 3;
	max = MAX(1, (32000 - (m->npc_specials.mob_size_mana *
		  m->npc_specials.mob_num_mana)));
	m->npc_specials.mob_add_mana = 1;
	GET_INTEGER_ARG(ch, "How many additional mana points? ", 
			m->npc_specials.mob_add_mana, 1, max);
	break;
      default:
	menu_back(ch);
    }
}

ROA_MENU(medit_movedice_menu)
{
    chdata *m = MOB_EDITTED(ch);
    int max;

    switch ((int) MENU_HANDLER_ARG(ch))
    {
      case 0:
	MENU_HANDLER_ARG(ch) = (void *) 1;
	max = 32000 / m->npc_specials.mob_size_move;
	m->npc_specials.mob_num_move = 1;
	GET_INTEGER_ARG(ch, "How many move dice? ", m->npc_specials.mob_num_move, 1, max);
	break;
      case 1:
	MENU_HANDLER_ARG(ch) = (void *) 2;
	max = 32000 / m->npc_specials.mob_num_move;
	m->npc_specials.mob_size_move = 1;
	GET_INTEGER_ARG(ch, "What size move dice? ", m->npc_specials.mob_size_move, 
			1, max);
	break;
      case 2:
	MENU_HANDLER_ARG(ch) = (void *) 3;
	max = MAX(1, (32000 - (m->npc_specials.mob_size_move * m->npc_specials.mob_num_move)));
	m->npc_specials.mob_add_move = 1;
	GET_INTEGER_ARG(ch, "How many additional move points? ", 
			m->npc_specials.mob_add_move, 1, max);
	break;
      default:
	menu_back(ch);
    }
}

ROA_MENU(medit_damage_menu)
{
    chdata *m = MOB_EDITTED(ch);

    switch ((int) MENU_HANDLER_ARG(ch))
    {
      case 0:
	MENU_HANDLER_ARG(ch) = (void *) 1;
	GET_INTEGER_ARG(ch, "How many damage dice? ", m->npc_specials.damnodice, 
			0, 100);
	break;
      case 1:
	MENU_HANDLER_ARG(ch) = (void *) 2;
	GET_INTEGER_ARG(ch, "What size damage dice? ", 
			m->npc_specials.damsizedice, 0, 100);
	break;
      case 2:
	MENU_HANDLER_ARG(ch) = (void *) 3;
	GET_INTEGER_ARG(ch, "How many additional damage points? ", 
			m->points.damroll, 0, 10000);
	break;
      default:
	menu_back(ch);
    }
}

/* add extra menu for strings RoA jtrhone*/
ROA_MENU(medit_string_menu)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int field;
    chdata *m = MOB_EDITTED(ch);
    int i;
    
    if (!input_str)
    {
        menu_title_send("MobEdit String Menu", ch);

	if (SPC_FLAGGED(m, SPC_SHOPKEEP))
	{
	  sprintf(buf, "1.) %%6Welcome%%0   : %s\n\r", MSTR1(m));
	  S2C();
	  sprintf(buf, "2.) %%6Don't want%%0: %s\n\r", MSTR2(m));
	  S2C();
	  sprintf(buf, "3.) %%6Don't have%%0: %s\n\r", MSTR3(m));
	  S2C();
	  sprintf(buf, "4.) %%6Thank You%%0 : %s\n\r", MSTR4(m));
	  S2C();
	  sprintf(buf, "5.) %%6Too high%%0  : %s\n\r", MSTR5(m));
	  S2C();
	}
	else
	if (SPC_FLAGGED(m, SPC_REACTOR))
	{
	  send_to_char("%B--NOTE--:%0  Parse reacts & triggers with an asterisk (*).\n\r",ch);
	  sprintf(buf, "1.) %%6Trigg1%%0: %s\n\r", TRIG1(m)); S2C();
	  sprintf(buf, "2.) %%6React1%%0: %s\n\r", REAC1(m)); S2C();
	  sprintf(buf, "3.) %%6Trigg2%%0: %s\n\r", TRIG2(m)); S2C();
	  sprintf(buf, "4.) %%6React2%%0: %s\n\r", REAC2(m)); S2C();
	}
	else
	if (SPC_FLAGGED(m, SPC_RANDOM))
	  for (i = 0; i < 5; i++)
	  {
	    sprintf(buf, "%d.) %%6Random%d%%0: %s\n\r",i+1,i+1,
		    m->npc_specials.strs[i]);
	    S2C();
	  }
	else
	  send_to_char("    %B%5SHOPKEEP, RANDOM, REACTOR mobs 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 (SPC_FLAGGED(m, SPC_SHOPKEEP))
	do_long_string_arg(ch, "Enter WELCOME message (@):\n\r", 
		      &MSTR1(m));
	else
	if (SPC_FLAGGED(m, SPC_RANDOM))
	do_long_string_arg(ch, "Enter RANDOM message 1 (@):\n\r", 
		      &MSTR1(m));
	else
	if (SPC_FLAGGED(m, SPC_REACTOR))
	do_long_string_arg(ch, "Enter TRIGGER 1 (@) :\n\r", 
		      &MSTR1(m));
        break;
      case 2:
	if (SPC_FLAGGED(m, SPC_SHOPKEEP))
	do_long_string_arg(ch, "Enter DON'T WANT message (@):\n\r", 
		      &MSTR2(m));
	else
	if (SPC_FLAGGED(m, SPC_RANDOM))
	do_long_string_arg(ch, "Enter RANDOM message 2 (@):\n\r", 
		      &MSTR2(m));
	else
	if (SPC_FLAGGED(m, SPC_REACTOR))
	do_long_string_arg(ch, "Enter REACTION 1 (@):\n\r", 
		      &MSTR2(m));
        break;
      case 3:
	if (SPC_FLAGGED(m, SPC_SHOPKEEP))
	do_long_string_arg(ch, "Enter DON'T HAVE message (@):\n\r", 
		      &MSTR3(m));
	else
	if (SPC_FLAGGED(m, SPC_RANDOM))
	do_long_string_arg(ch, "Enter RANDOM message 3 (@):\n\r", 
		      &MSTR3(m));
	else
	if (SPC_FLAGGED(m, SPC_REACTOR))
	do_long_string_arg(ch, "Enter TRIGGER 2 (@):\n\r", 
		      &MSTR3(m));
        break;
      case 4:
	if (SPC_FLAGGED(m, SPC_SHOPKEEP))
	do_long_string_arg(ch, "Enter THANK YOU message (@):\n\r", 
		      &MSTR4(m));
	else
	if (SPC_FLAGGED(m, SPC_RANDOM))
	do_long_string_arg(ch, "Enter RANDOM message 4 (@):\n\r", 
		      &MSTR4(m));
	else
	if (SPC_FLAGGED(m, SPC_REACTOR))
	do_long_string_arg(ch, "Enter REACTION 2 (@):\n\r", 
		      &MSTR4(m));
        break;
      case 5:
	if (SPC_FLAGGED(m, SPC_SHOPKEEP))
	do_long_string_arg(ch, "Enter TOO HIGH message (@):\n\r", 
		      &MSTR5(m));
        else
	if (SPC_FLAGGED(m, SPC_RANDOM))
	do_long_string_arg(ch, "Enter RANDOM message 5 (@):\n\r", 
		      &MSTR5(m));
        break;

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

/* add extra menu for walkin/out strings RoA jtrhone*/
ROA_MENU(medit_walk_menu)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int field;
    chdata *m = MOB_EDITTED(ch);
    
    if (!input_str)
    {
        menu_title_send("MobEdit Walkin/out Menu", ch);
	sprintf(buf, "1.) %%6Walkin%%0 :\n\r%s %s from <dir>.\n\r", 
		GET_NAME(m), MWALKIN(m)); S2C();
	sprintf(buf, "2.) %%6Walkout%%0:\n\r%s %s <dir>.\n\r", 
		GET_NAME(m), MWALKOUT(m)); 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:
	menu_back(ch);
	return;
	break;

      case 1:
	do_string_arg(ch, "Enter mob walkin:\n\r", &MWALKIN(m), "");
	break;

      case 2:
	do_string_arg(ch, "Enter mob walkout:\n\r", &MWALKOUT(m), "");
	break;

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

/* add extra menu for mob saving throws RoA jtrhone */
ROA_MENU(medit_saves_menu)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int field;
    chdata *m = MOB_EDITTED(ch);
    
    if (!input_str)
    {
        menu_title_send("MobEdit Saving Throw Menu", ch);
	sprintf(buf, "1.) %%6Save versus Heat%%0  : %%5%d%%0.\n\r", 
		m->npc_specials.saving_throws[0]); S2C();
	sprintf(buf, "2.) %%6Save versus Cold%%0  : %%5%d%%0.\n\r", 
		m->npc_specials.saving_throws[1]); S2C();
	sprintf(buf, "3.) %%6Save versus Magic%%0 : %%5%d%%0.\n\r", 
		m->npc_specials.saving_throws[2]); S2C();
	sprintf(buf, "4.) %%6Save versus Poison%%0: %%5%d%%0.\n\r", 
		m->npc_specials.saving_throws[3]); S2C();
	sprintf(buf, "5.) %%6Save versus Breath%%0: %%5%d%%0.\n\r", 
		m->npc_specials.saving_throws[4]); 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:
	menu_back(ch);
	return;
	break;

      case 1:
	GET_INTEGER_ARG(ch, "Enter Saving Throw versus Heat (0 - 100): ",
		m->npc_specials.saving_throws[0], 0, 100);
	break;

      case 2:
	GET_INTEGER_ARG(ch, "Enter Saving Throw versus Cold (0 - 100): ",
		m->npc_specials.saving_throws[1], 0, 100);
	break;

      case 3:
	GET_INTEGER_ARG(ch, "Enter Saving Throw versus Magic (0 - 100): ",
		m->npc_specials.saving_throws[2], 0, 100);
	break;

      case 4:
	GET_INTEGER_ARG(ch, "Enter Saving Throw versus Poison (0 - 100): ",
		m->npc_specials.saving_throws[3], 0, 100);
	break;

      case 5:
	GET_INTEGER_ARG(ch, "Enter Saving Throw versus Breath (0 - 100): ",
		m->npc_specials.saving_throws[4], 0, 100);
	break;

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

/* add extra menu for shop keep data -hours home shop vnums RoA jtrhone*/
ROA_MENU(medit_shop_menu)
{
    char buf[MAX_STRING_LENGTH];
    char *p;
    int field;
    chdata *m = MOB_EDITTED(ch);
    
    /* make SURE that this shopkeep mob has a shop_data_struct allocated*/
    if (!m->npc_specials.shop_data)
    {
      CREATE(m->npc_specials.shop_data, struct shop_data_type, 1);
      m->npc_specials.shop_data->vhome = -1;
      m->npc_specials.shop_data->vshop = -1;
      m->npc_specials.shop_data->open = -1;
      m->npc_specials.shop_data->close = -1;
    }

    if (!input_str)
    {
        menu_title_send("MobEdit Shopkeep Data Menu", ch);

	sprintf(buf, "1.) %%6Vnum of shopkeep's home%%0: %d\n\r", 
		m->npc_specials.shop_data->vhome); S2C();
	sprintf(buf, "2.) %%6Vnum of shopkeep's shop%%0: %d\n\r", 
		m->npc_specials.shop_data->vshop); S2C();
	sprintf(buf, "3.) %%6Opening hour of shop%%0: %d\n\r", 
		m->npc_specials.shop_data->open); S2C();
	sprintf(buf, "4.) %%6Closing hour of shop%%0: %d\n\r", 
		m->npc_specials.shop_data->close); 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:
	menu_back(ch);
	return;
	break;

     case 1:
	GET_INTEGER_ARG(ch, "Enter Vnum of shopkeep's home (-1 for none): ",
		m->npc_specials.shop_data->vhome, -1, 99999);
	break;

     case 2:
	GET_INTEGER_ARG(ch, "Enter Vnum of shopkeep's shop (-1 for none): ",
		m->npc_specials.shop_data->vshop, -1, 99999);
	break;

     case 3:
	GET_INTEGER_ARG(ch, "Enter opening hour for shopkeep (0-23) (-1 for never open): ",
		m->npc_specials.shop_data->open, -1, 23);
	break;

     case 4:
	GET_INTEGER_ARG(ch, "Enter closing hour for shopkeep (-1 for never close): ",
		m->npc_specials.shop_data->close, -1, 23);
	break;

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

/* add extra menu for mobproc string RoA jtrhone*/
ROA_MENU(medit_mobproc_menu)
{
    char buf[20000];
    char *p;
    int field;
    chdata *m = MOB_EDITTED(ch);
    
    if (!input_str)
    {
        menu_title_send("MobEdit MobProc Menu", ch);
	if (SPC_FLAGGED(m, SPC_MOBPROC))
	{
	  send_to_char("%B--NOTE--:%0  Parse commands with CR|LF.\n\r",ch);
	  sprintf(buf, "1.) %%6Mob Proc Command list%%0:\n\r%s\n\r", MSTR5(m)); 
	  page_string(ch->desc, buf, TRUE);
	}
	else
	  send_to_char("    %B%5MOBPROC mobs 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 (SPC_FLAGGED(m, SPC_MOBPROC))
	do_var_string_arg(ch, "Enter command list (each command on newline) (@) :\n\r", 
		      &MSTR5(m), MAX_STRING_LENGTH - 100);
        break;

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

// the main menu while in medit -roa
ROA_MENU(medit_top_menu)
{
    char buf[MAX_STRING_LENGTH];
    char buf2[MAX_STRING_LENGTH];
    char *p;
    int field;
    chdata *m = MOB_EDITTED(ch);
    
    if (!input_str)
    {
        menu_title_send("MobEdit Main Menu", ch);
	sprintf(buf, " 1.) %%6Nr%%0       : [%5d],    ", m->nr); S2C();
	sprintf(buf, " 2.) %%6NameList%%0 : %s\n\r", m->player.name); S2C();
	sprintf(buf, " 3.) %%6ShortDesc%%0: %s\n\r", m->player.short_descr); S2C();
	sprintf(buf, " 4.) %%6LongDesc%%0 : %s", m->player.long_descr); S2C();
	sprintf(buf, " 5.) %%6Desc%%0     :\n\r%s", m->player.description); S2C();

	sprintbit(m->specials.affected_by, affected_bits, buf2);
	sprintf(buf, " 6.) %%6Affected%%0  : %%5%s%%0\n\r", buf2); S2C();
	sprintbit(m->specials.affected2_by, affected2_bits, buf2);
	sprintf(buf, " 7.) %%6Affected2%%0 : %%5%s%%0\n\r", buf2); S2C();

	sprintf(buf, " 8.) %%6Alignment%%0: %6d,  ",m->npc_specials.alignment); S2C();
	sprintf(buf, " 9.) %%6Level%%0: %6d,  ", GET_LEVEL(m)); S2C();
	sprintf(buf, "10.) %%6HitRoll%%0: %6d\n\r", m->points.hitroll); S2C();

	sprintf(buf, "11.) %%6ArmorClas%%0: %6d,  ",m->points.armor);S2C();
	sprintf(buf, "12.) %%6%s%%0 : %6d,  ", currency_name_plural, GET_GOLD(m)); S2C();
	sprintf(buf, "13.) %%6ExpPoints%%0: %6d\n\r", GET_EXP(m)); S2C();

	sprintf(buf, "14.) %%6%%BHit%%0: %dd%d+%d, ",
		m->npc_specials.mob_num_hit, 
		m->npc_specials.mob_size_hit, 
		m->npc_specials.mob_add_hit); 	S2C();

	sprintf(buf, "15.) %%6%%BMana%%0: %dd%d+%d, ",
		m->npc_specials.mob_num_mana, 
		m->npc_specials.mob_size_mana, 
		m->npc_specials.mob_add_mana); 	S2C();

	sprintf(buf, "16.) %%6%%BMove%%0: %dd%d+%d\n\r",
		m->npc_specials.mob_num_move, 
		m->npc_specials.mob_size_move, 
		m->npc_specials.mob_add_move); 	S2C();

	sprinttype(GET_POS(m), position_types, buf2);
	sprintf(buf, "17.) %%6Position%%0 : %s,  ", buf2);
	sprintf(buf2, "%-27s", buf); send_to_char(buf2, ch);

	sprinttype((m->npc_specials.default_pos), position_types, buf2);
	sprintf(buf, "18.) %%6DefaulPos%%0: %s,  ", buf2);
	sprintf(buf2, "%-27s", buf); send_to_char(buf2, ch);

	sprintf(buf, "19.) %%6Sex%%0: %s\n\r", char_sex_strs[(int)m->player.sex]); 
	S2C();

	if (m->real_abils.str < 18)
	    m->real_abils.str_add = 0;

	sprintf(buf, "20.) %%6STR%%0: %2d,  21.) %%6STRADD%%0: %2d,  22.) %%6INT%%0: %2d,  23.) %%6WIS%%0: %2d\n\r",
		m->real_abils.str, m->real_abils.str_add,
		m->real_abils.intel, m->real_abils.wis); S2C();

	sprintf(buf, "24.) %%6DEX%%0: %2d,  25.) %%6CON%%0: %2d,     ",
		m->real_abils.dex, m->real_abils.con); S2C();

	sprintf(buf, "26.) %%6Dam%%0: %dd%d+%d\n\r", 
		m->npc_specials.damnodice,
		m->npc_specials.damsizedice, m->points.damroll); S2C();

	sprintbit(MOB_FLAGS(m), action_bits, buf2);
	sprintf(buf, "27.) %%6Mflags1%%0: %%5%s%%0\n\r", buf2); S2C();

	sprintbit(SPC_FLAGS(m), spc_bits, buf2);
	sprintf(buf, "28.) %%6Mflags2%%0: %%5%s%%0\n\r",buf2);S2C();

	sprintbit(MOB2_FLAGS(m), mob2_bits, buf2);
	sprintf(buf, "29.) %%6Mflags3%%0: %%5%s%%0\n\r",buf2);S2C();

	sprintbit(MOB_CLASS_FLAGS(m), npc_class_names, buf2);
	sprintf(buf, "30.) %%6MClasses%%0: %%5%s%%0\n\r", buf2); S2C();

	sprinttype(m->npc_specials.mob_size, mob_size_strings, buf2);
	sprintf(buf, "31.) %%6Size%%0: %s,  ", buf2); S2C();

	sprintf(buf, "32.) %%6Lang%%0: %%B%s%%0\n\r", gskill_names[SPEAKING(m)]);
	S2C();

	if (SPC_FLAGGED(m, SPC_SHOPKEEP) || SPC_FLAGGED(m, SPC_REACTOR) || SPC_FLAGGED(m, SPC_RANDOM))
  	send_to_char("33.) %6Mob String Edit Menu...%0\n\r",ch);

	if (SPC_FLAGGED(m, SPC_MOBPROC))
	  send_to_char("34.) %6MobProc CommandList Menu...%0\n\r",ch);

	if (SPC_FLAGGED(m, SPC_GMASTER))
	{
	  sprintf(buf, "35.) %%6MinTrainLevel%%0: %2d\n\r36.) %%6MaxTrainLevel%%0: %2d\n\r",
 		  m->npc_specials.min_train_level, m->npc_specials.max_train_level);
	  S2C();
	}

  	send_to_char("37.) %6Mob Walkin/out Edit Menu...%0\n\r",ch);

	if (SPC_FLAGGED(m, SPC_SHOPKEEP))
	  send_to_char("38.) %6Shopkeep Data Menu...%0\n\r",ch);

  	send_to_char("39.) %6Mob Saving Throw Menu...%0\n\r",ch);

	if (MOB_FLAGGED(m, MOB_QUESTOR))
	{
	  int qnum = m->npc_specials.qnum;
	  if (VALID_QUEST(qnum))
	    sprintf(buf, "40.) %%6Mob Quest#%%0: %d (%s)\n\r",m->npc_specials.qnum,
		    qarray[qnum].qname ? qarray[qnum].qname : "Invalid Qname");
	  else
	    sprintf(buf, "40.) %%6Mob Quest#%%0: Invalid Qnum.\n\r");
	  S2C();
	}

	if (SPC_FLAGGED(m, SPC_QHUNTER))
	{
	  int qnum = m->npc_specials.hunt_quest;
	  if (VALID_QUEST(qnum))
	    sprintf(buf,"41.) %%6Mob Hunts Quest#%%0: %d (%s)\n\r",qnum,
		    qarray[qnum].qname ? qarray[qnum].qname : "Invalid Qname");
	  else
	    sprintf(buf, "41.) %%6Mob Hunts Quest#%%0: Invalid Qnum.\n\r");
	  S2C();
	}

	sprintf(buf, "42.) %%6Sound file%%0: %s, ",        m->npc_specials.sound_file); S2C();
	sprintf(buf, "43.) %%6Sound repeat time%%0: %d\n\r", m->npc_specials.music_timer);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:
	menu_jump(ch, medit_confirm_quit);
	break;

      case 1:
	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 mob virtual number: ", m->nr, 0, 89999);
	}
	else
	{
	    GET_INTEGER_ARG(ch, "Enter mob virtual number: ", m->nr,
			    ch->pc_specials->saved.olc_min_zone * 100, 
			    ch->pc_specials->saved.olc_max_zone * 100 + 99);
	}
	break;

      case 2:
	do_string_arg(ch, "Enter new name list:\n\r", &m->player.name, "");
	break;
	
      case 3:
	do_string_arg(ch, "Enter new short description:\n\r", 
		      &m->player.short_descr, "");
	break;
	
      case 4:
	do_string_arg(ch, "Enter new long description:\n\r", 
		      &m->player.long_descr, "\n\r");
	break;

      case 5:
	do_long_string_arg(ch, "Enter new description (@):\n\r",
			   &m->player.description);
	break;
	
      case 6:
	toggle_menu_long(ch, "Enter affected bit to toggle: ", 
                          &m->specials.affected_by, affected_bits);
	break;
	
      case 7:
	toggle_menu_long(ch, "Enter affected2 bit to toggle: ", 
                          &m->specials.affected2_by, affected2_bits);
	break;
	
      case 8:
	GET_INTEGER_ARG(ch, "Enter mob alignment: ", m->npc_specials.alignment, 
			-1000, 1000);
	break;
	
      case 9:
	GET_INTEGER_ARG(ch, "Enter mob level: ", m->player.level, 0, 75);
	break;
	
      case 10:
	GET_INTEGER_ARG(ch, "Enter mob hitroll: ", m->points.hitroll, -20, 40);
	break;
	
      case 11:
	GET_INTEGER_ARG(ch, "Enter mob armor class: ", m->points.armor, -490, 100);
	break;
	
      case 12:
	GET_INTEGER_ARG(ch, "Mob should carry how much currency? ", m->points.gold, 
			0, 1000000);
	break;
	
      case 13:
	GET_INTEGER_ARG(ch, "Mob is worth how many exp points? ", 
			m->points.exp, 0, 1000000);
	break;
	
      case 14:
	menu_push(ch);
	MENU_HANDLER_ARG(ch) = (void *) 0; 
	menu_jump(ch, medit_hitdice_menu);
	break;

      case 15:
	menu_push(ch);
	MENU_HANDLER_ARG(ch) = (void *) 0; 
	menu_jump(ch, medit_manadice_menu);
	break;

      case 16:
	menu_push(ch);
	MENU_HANDLER_ARG(ch) = (void *) 0; 
	menu_jump(ch, medit_movedice_menu);
	break;
	
      case 17:
	get_integer_list(ch, "Enter number of desired position: ",
			 &m->specials.position,
			 sizeof(m->specials.position),
			 position_types);
	break;
	
      case 18:
	get_integer_list(ch, "Enter number of desired default position: ",
			 &m->npc_specials.default_pos,
			 sizeof(m->npc_specials.default_pos),
			 position_types);
	break;
	
      case 19:
	get_integer_list(ch, "Enter desired sex number: ",
			 &m->player.sex,
			 sizeof(m->player.sex),
			 char_sex_strs);
	break;
	
      case 20:
	GET_INTEGER_ARG(ch, "Mob strength (3-18): ", m->real_abils.str, 
			3, 18);
	break;
	
      case 21:
	GET_INTEGER_ARG(ch, "Mob strength additional (0-100): ", 
			m->real_abils.str_add, 
			0, 100);
	break;
	
      case 22:
	GET_INTEGER_ARG(ch, "Mob intelligence (3-18): ", m->real_abils.intel, 
			3, 18);
	break;
	
      case 23:
	GET_INTEGER_ARG(ch, "Mob wisdom (3-18): ", m->real_abils.wis, 
			3, 18);
	break;
	
      case 24:
	GET_INTEGER_ARG(ch, "Mob dexterity (3-18): ", m->real_abils.dex, 
			3, 18);
	break;
	
      case 25:
	GET_INTEGER_ARG(ch, "Mob constitution (3-18): ", m->real_abils.con, 3, 18);
	break;
	
      case 26:
	menu_push(ch);
	MENU_HANDLER_ARG(ch) = (void *) 0; 
	menu_jump(ch, medit_damage_menu);
	break;

      case 27:
	toggle_menu_long(ch, "Enter Mflag bit to toggle: ", &m->npc_specials.mob_bits, action_bits);
	break;

      case 28:
	toggle_menu_long(ch, "Enter Mflag bit to toggle: ", &m->npc_specials.spc_bits, spc_bits);
	break;
	
      case 29:
	toggle_menu(ch, "Enter Mflag bit to toggle: ", &m->npc_specials.mob2_bits, mob2_bits);
	break;
	
      case 30:
	toggle_menu_long(ch, "Enter Mob_class bit to toggle: ", 
			  &m->npc_specials.mob_class, npc_class_names);
	break;
	
      case 31:
	get_integer_list(ch, "Enter number of desired mob size: ",
			 &m->npc_specials.mob_size, sizeof(m->npc_specials.mob_size), mob_size_strings);
	break;
	
      case 32:
        send_dialect_listing(ch);
	GET_INTEGER_ARG(ch, "Enter # of desired language: ", SPEAKING(m), 0, LANG_DRAGON);
	break;

      case 33:
	if (!SPC_FLAGGED(m, SPC_SHOPKEEP) && !SPC_FLAGGED(m, SPC_REACTOR) && !SPC_FLAGGED(m, SPC_RANDOM))
	  break;
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, medit_string_menu);
	break;

      case 34:
	if (!SPC_FLAGGED(m, SPC_MOBPROC))
	  break;
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, medit_mobproc_menu);
	break;

      case 35:
	GET_INTEGER_ARG(ch, "Enter minimum training level: ", 
			m->npc_specials.min_train_level, 1, 
			m->npc_specials.max_train_level);
	break;

      case 36:
	GET_INTEGER_ARG(ch, "Enter maximum training level: ", 
			m->npc_specials.max_train_level, 
			m->npc_specials.min_train_level, 70);
	break;

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

      case 38:
	if (!SPC_FLAGGED(m, SPC_SHOPKEEP))
	  break;
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, medit_shop_menu);
	break;

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

      case 40:
	if (MOB_FLAGGED(m, MOB_QUESTOR))
	  GET_INTEGER_ARG(ch, "Enter quest# mob gives out: ", m->npc_specials.qnum, 0, NUM_QUESTS); 
	break;

      case 41:
	if (SPC_FLAGGED(m, SPC_QHUNTER))
	  GET_INTEGER_ARG(ch, "Enter quest# mob hunts: ", m->npc_specials.hunt_quest, 0, NUM_QUESTS); 
	break;

      case 42:
	do_string_arg(ch, "Enter sound filename: ", &m->npc_specials.sound_file, "");
	break;
	
      case 43:
	  GET_INTEGER_ARG(ch, "Enter sound repeat time (qticks, -1 no repeat): ",
			  m->npc_specials.music_timer, -1, 32000); 
	break;
	
      default:
	send_to_char("That field cannot be changed, yet.\n\r", ch);
	break;
    }

    ch->pc_specials->field_changed = TRUE;
}

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

// now go thru character list and alter all the mobs pointing 
// to the old proto to point to this new protos stuff
void update_mobs_in_game(int rnum)
{
  chdata *m;

  for (m = character_list; m; m = m->next)
  if (m->nr == rnum && IS_NPC(m))
  {
    m->player.name = mob_proto[rnum].player.name;
    m->player.title = mob_proto[rnum].player.title;
    m->player.short_descr = mob_proto[rnum].player.short_descr;
    m->player.long_descr = mob_proto[rnum].player.long_descr;
    m->player.description = mob_proto[rnum].player.description;
    m->npc_specials.walkIn = mob_proto[rnum].npc_specials.walkIn;
    m->npc_specials.walkOut = mob_proto[rnum].npc_specials.walkOut;
    MOB_FLAGS(m) = MOB_FLAGS(mob_proto + rnum);
    GET_LEVEL(m) = GET_LEVEL(mob_proto + rnum);
    GET_GOLD(m) = GET_GOLD(mob_proto + rnum);
    GET_EXP(m) = GET_EXP(mob_proto + rnum);
    MSTR1(m) = mob_proto[rnum].npc_specials.strs[0];
    MSTR2(m) = mob_proto[rnum].npc_specials.strs[1];
    MSTR3(m) = mob_proto[rnum].npc_specials.strs[2];
    MSTR4(m) = mob_proto[rnum].npc_specials.strs[3];
    MSTR5(m) = mob_proto[rnum].npc_specials.strs[4];
    if (SPC_FLAGGED(m, SPC_MOBPROC)) 
      MPROC_CUR(m) = MPROC_BEG(m);
    else
      MPROC_CUR(m) = NULL;
    m->npc_specials.spc_bits = mob_proto[rnum].npc_specials.spc_bits;
    m->npc_specials.mob_size = mob_proto[rnum].npc_specials.mob_size;
    m->npc_specials.mob_class = mob_proto[rnum].npc_specials.mob_class;
    m->npc_specials.alignment = mob_proto[rnum].npc_specials.alignment;
    m->points.hitroll = mob_proto[rnum].points.hitroll;
    m->npc_specials.mob_num_hit = mob_proto[rnum].npc_specials.mob_num_hit;
    m->npc_specials.mob_size_hit = mob_proto[rnum].npc_specials.mob_size_hit;
    m->npc_specials.mob_add_hit = mob_proto[rnum].npc_specials.mob_add_hit;
    m->npc_specials.damnodice = mob_proto[rnum].npc_specials.damnodice;
    m->npc_specials.damsizedice = mob_proto[rnum].npc_specials.damsizedice;
    m->npc_specials.default_pos = mob_proto[rnum].npc_specials.default_pos;
    m->player.sex = mob_proto[rnum].player.sex;
    m->real_abils = mob_proto[rnum].real_abils;

    m->npc_specials.shop_data = mob_proto[rnum].npc_specials.shop_data;

    // added sound support...
    m->npc_specials.sound_file  = mob_proto[rnum].npc_specials.sound_file;
    m->npc_specials.music_timer = mob_proto[rnum].npc_specials.music_timer;
  }
}

// check a mobile for null strings -roa
void check_invalid_mobile(chdata *m)
{
  BOOL dolog = FALSE;

  if (!m->player.name || !*m->player.name)
  {
    m->player.name = STR_DUP("invalid mobile");
    dolog = TRUE;
  }

  if (!m->player.short_descr || !*m->player.short_descr)
  {
    m->player.short_descr = STR_DUP("an invalid mobile");
    dolog = TRUE;
  }

  if (!m->player.long_descr || !*m->player.long_descr)
  {
    m->player.long_descr = STR_DUP("an invalid mobile stands here");
    dolog = TRUE;
  }

  if (!m->player.description || !*m->player.description)
  {
    m->player.description = STR_DUP("this is an invalid mobile, report immediately!");
    dolog = TRUE;
  }

  if (dolog)
  {
    sprintf(buf, "SYSERR: Invalid mobile string detected and fixed: #%d.", GET_MOB_VNUM(m));
    mudlog(buf, BRF, LEV_IMM, TRUE);
  }
}

ROA_MENU(medit_confirm_save)
{
  extern struct pc_special_data dummy_mob;
  char buf[MAX_STRING_LENGTH];
  char *p;
  int rnum, vnum;
  chdata *m = MOB_EDITTED(ch);

  if (!input_str)
  {
    MENU_PROMPT(ch) = "Save editted mob (YES/no)? ";
    return;
  }
    
  strcpy(buf, input_str);
  p = strtok(buf, " 	\n\r");
  if (!p || strncasecmp("no", p, strlen(p)) != 0)
  {
    // a few things that MUST be true upon exit of medit -roa
    vnum = m->nr;
    m->aff_abils = m->real_abils;
    SET_BIT(MOB_FLAGS(m), MOB_ISNPC);
    m->pc_specials = &dummy_mob;
    if (!SPC_FLAGGED(m, SPC_SHOPKEEP))
      FREENULL(m->npc_specials.shop_data);

    if ((rnum = real_mobile(vnum)) >= 0)
    {
      m->nr = rnum;
      check_invalid_mobile(m);
      dupe_mobile_over(m, &mob_proto[rnum]);
      update_mobs_in_game(rnum);  /* update whatever is out there */
      strcpy(buf2, "editted");
    }
    else
    {
      mob_index[top_of_mobt].vnum      = vnum;  /* m->nr was storage for vnum */
      mob_index[top_of_mobt].number    = 0;      /* none out there yet */
      mob_index[top_of_mobt].func      = NULL;   /* in roa, hardwiring is rare */

      m->nr = top_of_mobt;
      m->in_room = NOWHERE;

      check_invalid_mobile(m);
      dupe_mobile_over(m, &mob_proto[top_of_mobt]);
      top_of_mobt++;  

      strcpy(buf2, "editted");
    }

    sprintf(buf, "PLRUPD: %s %s mobile #%d.", GET_NAME(ch), buf2, vnum);
    mudlog(buf, BRF, GET_LEVEL(ch), TRUE);
  } /* end if save was confirmed */

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

ACMD(do_mlist)
{
    chdata *m;
    int zone;
    int i, top;
    BOOL found = FALSE;
    BOOL substr = FALSE;
    char buf[20000];

    if (IS_NPC(ch))
	return;
    
    half_chop(argument, buf, buf2);
    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 (GET_LEVEL (ch) < LEV_GOD &&
	(zone == 0 || zone < ch->pc_specials->saved.olc_min_zone ||
	 zone > ch->pc_specials->saved.olc_max_zone))
    {
	send_to_char("You do not have privilieges to those mobiles/zone.\n\r", ch);
	return;
    }

    if (*buf2 && is_abbrev(buf2, "substr"))
      substr = TRUE;
 
    top = zone_table[zone].top;
    for (i = zone * 100; i <= top; i++)
    {
	if (real_mobile(i) >= 0)
	    break;
    }

    if (i > top)
    {
	send_to_char("No mobs in that zone.\n\r",ch);
	return;
    }    

    *buf = '\0';
    while (i <= top)
    {
	if (real_mobile(i) < 0)
	{
	    i++;
	    continue;
	}

	m = &mob_proto[real_mobile(i)];

	if (substr)
	{
 	  if (!SPC_FLAGGED(m, SPC_REACTOR) && !SPC_FLAGGED(m, SPC_MOBPROC))
	  {
	    i++;
	    continue;
	  }
	  if (SPC_FLAGGED(m, SPC_REACTOR) && 
	      (!TRIG1(m) || !strchr(TRIG1(m), '#')) && 
	      (!TRIG2(m) || !strchr(TRIG2(m), '#')) )
	  {
	    i++;
	    continue;
	  }
	  if (SPC_FLAGGED(m, SPC_MOBPROC) && MPROC_BEG(m) && !strchr(MPROC_BEG(m), '#'))
	  {
	    i++;
	    continue;
	  }
	}

	found = TRUE;	
	sprintf(buf1, "[%5d] %-20.20s LVL:%-3d%%0", i, m->player.name, GET_LEVEL(m));
	sprintf(buf2, " HP:%dd%d+%d", m->npc_specials.mob_num_hit, 
		m->npc_specials.mob_size_hit, m->npc_specials.mob_add_hit);
	strcat(buf1, buf2);

	sprintf(buf2, " HR:%-2d AC:%+4d DAM:%dd%d+%d\n\r", 
		m->points.hitroll, m->points.armor, 
		m->npc_specials.damnodice, m->npc_specials.damsizedice, 
		m->points.damroll);
	strcat(buf1, buf2);
	strcat(buf, buf1);
	i++;
    }

    if (found)
      page_string(ch->desc, buf, TRUE);
    else
      send_to_char("No mobs in that zone.\n\r",ch);
}