/
UltraEnvy/
UltraEnvy/log/
UltraEnvy/newdocs/
UltraEnvy/newdocs/MobProg2.1/
UltraEnvy/newdocs/OLC11/
UltraEnvy/newdocs/OLC11/area/
UltraEnvy/newdocs/OLC11/doc/
UltraEnvy/newdocs/OLC11/options/
UltraEnvy/player/
UltraEnvy/src/Contributions/
/***************************************************************************
 *  File: olc_act.c                                                        *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 *                                                                         *
 *  This code was freely distributed with the The Isles 1.1 source code,   *
 *  and has been used here for OLC - OLC would not be what it is without   *
 *  all the previous coders who released their source code.                *
 ***************************************************************************/

#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>		/*
				 * OLC 1.1b 
				 */
#include "merc.h"
#include "olc.h"

#define MPEDIT( fun )		bool fun( CHAR_DATA *ch, char *argument )

struct olc_help_type
{
    char *command;
    const void *structure;
    char *desc;
};

/*
 * Added by Zen. Substitutes the C macros they were making a do_switch bug!
 * Return pointers to what is being edited.
 */
bool is_builder( CHAR_DATA *ch, AREA_DATA *area )
{
    CHAR_DATA * rch;

    /* Will always have a pc ch after this */
    rch = ( ch->desc->original ? ch->desc->original : ch->desc->character );
    if ( ( rch->pcdata->security >= area->security
	|| strstr( area->builders, rch->name )
	|| strstr( area->builders, "All" ) ) )
	return TRUE;

    return FALSE;
}

MOB_INDEX_DATA *edit_mob( CHAR_DATA *ch )
{
    return (MOB_INDEX_DATA *) ch->desc->pEdit;
}

OBJ_INDEX_DATA *edit_obj( CHAR_DATA *ch )
{
    return (OBJ_INDEX_DATA *) ch->desc->pEdit;
}

ROOM_INDEX_DATA *edit_room( CHAR_DATA *ch )
{
    return ch->in_room;
}

AREA_DATA *edit_area( CHAR_DATA *ch )
{
    return (AREA_DATA *) ch->desc->pEdit;
}

bool show_version(CHAR_DATA * ch, char *argument)
{
    send_to_char(VERSION, ch);
    send_to_char("\n\r", ch);
    send_to_char(AUTHOR, ch);
    send_to_char("\n\r", ch);
    send_to_char(DATE, ch);
    send_to_char("\n\r", ch);
    send_to_char(CREDITS, ch);
    send_to_char("\n\r", ch);

    return FALSE;
}


/*
 * This table contains help commands and a brief description of each.
 * ------------------------------------------------------------------
 */
const struct olc_help_type help_table[] =
{
    {	"area", 	area_flags, 	"Area attributes."		},
    {	"room", 	room_flags, 	"Room attributes."		},
    {	"sector", 	sector_flags, 	"Sector types, terrain."	},
    {	"exit", 	exit_flags, 	"Exit types."			},
    {	"type", 	type_flags, 	"Types of objects."		},
    {	"extra", 	extra_flags, 	"Object attributes."		},
    {	"wear", 	wear_flags, 	"Where to wear object."		},
    {	"spec", 	spec_table, 	"Available special programs."	},
    {	"sex", 		sex_flags, 	"Sexes."			},
    {	"act", 		act_flags, 	"Mobile attributes."		},
    {	"affect", 	affect_flags, 	"Mobile affects."		},
    {	"wear-loc", 	wear_loc_flags, "Where mobile wears object."	},
    {	"spells", 	skill_table, 	"Names of current spells."	},
    {	"weapon", 	weapon_flags, 	"Type of weapon."		},
    {	"container", 	container_flags, "Container status."		},
    {	"liquid", 	liquid_flags, 	"Types of liquids."		},
    {	"race", 	race_flags, 	"Mobile races."			},
    {	"mobprogs", 	mprog_type_flags, "Types of Mob Programs."	},
    {	"", 		0, 		""				}
};



/*
 * Name:	show_flag_cmds
 * Purpose:	Displays settable flags and stats.
 * Called by:	show_help(olc_act.c).
 */
void show_flag_cmds(CHAR_DATA * ch, const struct flag_type *flag_table)
{
    char buf[MAX_STRING_LENGTH];
    char buf1[MAX_STRING_LENGTH];
    int flag;
    int col;

    buf1[0] = '\0';
    col = 0;
    for (flag = 0; *flag_table[flag].name; flag++)
    {
	if (flag_table[flag].settable)
	{
	    sprintf(buf, "%-19.18s", flag_table[flag].name);
	    strcat(buf1, buf);
	    if (++col % 4 == 0)
		strcat(buf1, "\n\r");
	}
    }

    if (col % 4 != 0)
	strcat(buf1, "\n\r");

    send_to_char(buf1, ch);
    return;
}


/*
 * Name:	show_skill_cmds
 * Purpose:	Displays all skill functions.
 *		Does remove those damn immortal commands from the list.
 *		Could be improved by:
 *		(1) Adding a check for a particular class.
 *		(2) Adding a check for a level range.
 * Called by:	show_help(olc_act.c).
 */
void show_skill_cmds(CHAR_DATA * ch, int tar)
{
    char buf[MAX_STRING_LENGTH];
    char buf1[MAX_STRING_LENGTH * 2];
    int sn;
    int col;

    buf1[0] = '\0';
    col = 0;
    for (sn = 0; sn < MAX_SKILL; sn++)
    {
	if (!skill_table[sn].name)
	    break;

	if (!str_cmp(skill_table[sn].name, "reserved")
	    || skill_table[sn].spell_fun == spell_null)
	    continue;

	if (tar == -1 || skill_table[sn].target == tar)
	{
	    sprintf(buf, "%-19.18s", skill_table[sn].name);
	    strcat(buf1, buf);
	    if (++col % 4 == 0)
		strcat(buf1, "\n\r");
	}
    }

    if (col % 4 != 0)
	strcat(buf1, "\n\r");

    send_to_char(buf1, ch);
    return;
}


/*
 * Name:	show_spec_cmds
 * Purpose:	Displays settable special functions.
 * Called by:	show_help(olc_act.c).
 */
void show_spec_cmds(CHAR_DATA * ch)
{
    char buf[MAX_STRING_LENGTH];
    char buf1[MAX_STRING_LENGTH];
    int spec;
    int col;

    buf1[0] = '\0';
    col = 0;
    send_to_char("Preceed special functions with 'spec_'\n\r\n\r", ch);
    for (spec = 0; *spec_table[spec].spec_fun; spec++)
    {
	sprintf(buf, "%-19.18s", &spec_table[spec].spec_name[5]);
	strcat(buf1, buf);
	if (++col % 4 == 0)
	    strcat(buf1, "\n\r");
    }

    if (col % 4 != 0)
	strcat(buf1, "\n\r");

    send_to_char(buf1, ch);
    return;
}


/*
 * Name:	show_help
 * Purpose:	Displays help for many tables used in OLC.
 * Called by:	olc interpreters.
 */
bool show_help(CHAR_DATA * ch, char *argument)
{
    char buf[MAX_STRING_LENGTH];
    char arg[MAX_INPUT_LENGTH];
    char spell[MAX_INPUT_LENGTH];
    int cnt;

    argument = one_argument(argument, arg);
    one_argument(argument, spell);

    /*
     * Display syntax.
     */
    if (arg[0] == '\0')
    {
	send_to_char("Syntax:  ? [command]\n\r\n\r", ch);
	send_to_char("[command]  [description]\n\r", ch);
	for (cnt = 0; help_table[cnt].command[0] != '\0'; cnt++)
	{
	    sprintf(buf, "%-10.10s -%s\n\r",
		    capitalize(help_table[cnt].command),
		    help_table[cnt].desc);
	    send_to_char(buf, ch);
	}
	return FALSE;
    }

    /*
     * Find the command, show changeable data.
     * ---------------------------------------
     */
    for (cnt = 0; *help_table[cnt].command; cnt++)
    {
	if (arg[0] == help_table[cnt].command[0]
	    && !str_prefix(arg, help_table[cnt].command))
	{
	    if (help_table[cnt].structure == spec_table)
	    {
		show_spec_cmds(ch);
		return FALSE;
	    }
	    else if (help_table[cnt].structure == skill_table)
	    {

		if (spell[0] == '\0')
		{
		    send_to_char("Syntax:  ? spells "
		       "[ignore/attack/defend/self/object/all]\n\r", ch);
		    return FALSE;
		}

		if (!str_prefix(spell, "all"))
		    show_skill_cmds(ch, -1);
		else if (!str_prefix(spell, "ignore"))
		    show_skill_cmds(ch, TAR_IGNORE);
		else if (!str_prefix(spell, "attack"))
		    show_skill_cmds(ch, TAR_CHAR_OFFENSIVE);
		else if (!str_prefix(spell, "defend"))
		    show_skill_cmds(ch, TAR_CHAR_DEFENSIVE);
		else if (!str_prefix(spell, "self"))
		    show_skill_cmds(ch, TAR_CHAR_SELF);
		else if (!str_prefix(spell, "object"))
		    show_skill_cmds(ch, TAR_OBJ_INV);
		else
		    send_to_char("Syntax:  ? spell "
		       "[ignore/attack/defend/self/object/all]\n\r", ch);

		return FALSE;
	    }
	    else
	    {
		show_flag_cmds(ch, help_table[cnt].structure);
		return FALSE;
	    }
	}
    }

    show_help(ch, "");
    return FALSE;
}


bool redit_mlist(CHAR_DATA * ch, char *argument)
{
    MOB_INDEX_DATA *pMobIndex;
    AREA_DATA *pArea;
    char buf[MAX_STRING_LENGTH];
    char buf1[MAX_STRING_LENGTH * 2];
    char arg[MAX_INPUT_LENGTH];
    bool fAll, found;
    int vnum;
    int col = 0;

    one_argument(argument, arg);
    if (arg[0] == '\0')
    {
	send_to_char("Syntax:  mlist <all/name>\n\r", ch);
	return FALSE;
    }

    pArea = ch->in_room->area;
    buf1[0] = '\0';
    fAll = !str_cmp(arg, "all");
    found = FALSE;

    for (vnum = pArea->lvnum; vnum <= pArea->uvnum; vnum++)
    {
	if ((pMobIndex = get_mob_index(vnum)))
	{
	    if (fAll || is_name(arg, pMobIndex->player_name))
	    {
		found = TRUE;
		sprintf(buf, "[%5d] %-17.16s",
		    pMobIndex->vnum, capitalize(pMobIndex->short_descr));
		strcat(buf1, buf);
		if (++col % 3 == 0)
		    strcat(buf1, "\n\r");
	    }
	}
    }

    if (!found)
    {
	send_to_char("Mobile(s) not found in this area.\n\r", ch);
	return FALSE;
    }

    if (col % 3 != 0)
	strcat(buf1, "\n\r");

    send_to_char(buf1, ch);
    return FALSE;
}


bool redit_olist(CHAR_DATA * ch, char *argument)
{
    OBJ_INDEX_DATA *pObjIndex;
    AREA_DATA *pArea;
    char buf[MAX_STRING_LENGTH];
    char buf1[MAX_STRING_LENGTH * 2];
    char arg[MAX_INPUT_LENGTH];
    bool fAll, found;
    int vnum;
    int col = 0;

    one_argument(argument, arg);
    if (arg[0] == '\0')
    {
	send_to_char("Syntax:  olist <all/name/item_type>\n\r", ch);
	return FALSE;
    }

    pArea = ch->in_room->area;
    buf1[0] = '\0';
    fAll = !str_cmp(arg, "all");
    found = FALSE;

    for (vnum = pArea->lvnum; vnum <= pArea->uvnum; vnum++)
    {
	if ((pObjIndex = get_obj_index(vnum)))
	{
	    if (fAll || is_name(arg, pObjIndex->name)
		|| flag_value(type_flags, arg) == pObjIndex->item_type)
	    {
		found = TRUE;
		sprintf(buf, "[%5d] %-17.16s",
		    pObjIndex->vnum, capitalize(pObjIndex->short_descr));
		strcat(buf1, buf);
		if (++col % 3 == 0)
		    strcat(buf1, "\n\r");
	    }
	}
    }

    if (!found)
    {
	send_to_char("Object(s) not found in this area.\n\r", ch);
	return FALSE;
    }

    if (col % 3 != 0)
	strcat(buf1, "\n\r");

    send_to_char(buf1, ch);
    return FALSE;
}


bool redit_mshow(CHAR_DATA * ch, char *argument)
{
    MOB_INDEX_DATA *pMob;
    int value;

    if (argument[0] == '\0')
    {
	send_to_char("Syntax:  mshow <vnum>\n\r", ch);
	return FALSE;
    }

    if (is_number(argument))
    {
	value = atoi(argument);
	if (!(pMob = get_mob_index(value)))
	{
	    send_to_char("REdit:  That mobile does not exist.\n\r", ch);
	    return FALSE;
	}

	ch->desc->pEdit = (void *) pMob;
    }

    medit_show(ch, argument);
    ch->desc->pEdit = (void *) ch->in_room;
    return FALSE;
}


bool redit_oshow(CHAR_DATA * ch, char *argument)
{
    OBJ_INDEX_DATA *pObj;
    int value;

    if (argument[0] == '\0')
    {
	send_to_char("Syntax:  oshow <vnum>\n\r", ch);
	return FALSE;
    }

    if (is_number(argument))
    {
	value = atoi(argument);
	if (!(pObj = get_obj_index(value)))
	{
	    send_to_char("REdit:  That object does not exist.\n\r", ch);
	    return FALSE;
	}

	ch->desc->pEdit = (void *) pObj;
    }

    oedit_show(ch, argument);
    ch->desc->pEdit = (void *) ch->in_room;
    return FALSE;
}


/*
 * Name:	check_range( lower vnum, upper vnum )
 * Purpose:	Ensures the range spans only one area.
 * Called by:	aedit_vnum(olc_act.c).
 */
bool check_range(int lower, int upper)
{
    AREA_DATA *pArea;
    int cnt = 0;

    for (pArea = area_first; pArea; pArea = pArea->next)
    {
	/*
	 * lower < area < upper
	 */
	if ((lower <= pArea->lvnum && upper >= pArea->lvnum)
	    || (upper >= pArea->uvnum && lower <= pArea->uvnum))
	    cnt++;

	if (cnt > 1)
	    return FALSE;
    }
    return TRUE;
}


AREA_DATA *get_vnum_area(int vnum)
{
    AREA_DATA *pArea;

    for (pArea = area_first; pArea; pArea = pArea->next)
    {
	if (vnum >= pArea->lvnum
	    && vnum <= pArea->uvnum)
	    return pArea;
    }

    return 0;
}


/*
 * Area Editor Functions.
 */
bool aedit_show(CHAR_DATA * ch, char *argument)
{
    AREA_DATA *pArea = edit_area( ch );
    char buf[MAX_STRING_LENGTH];

    sprintf(buf, "Name:     [%5d] %s\n\r", pArea->vnum, pArea->name);
    send_to_char(buf, ch);

    sprintf(buf, "Recall:   [%5d] %s\n\r", pArea->recall,
	    get_room_index(pArea->recall)
	    ? get_room_index(pArea->recall)->name : "none");
    send_to_char(buf, ch);

    sprintf(buf, "File:     %s\n\r", pArea->filename);
    send_to_char(buf, ch);

    sprintf(buf, "Vnums:    [%d-%d]\n\r", pArea->lvnum, pArea->uvnum);
    send_to_char(buf, ch);

    sprintf(buf, "Age:      [%d]\n\r", pArea->age);
    send_to_char(buf, ch);

    sprintf(buf, "Players:  [%d]\n\r", pArea->nplayer);
    send_to_char(buf, ch);

    sprintf(buf, "Security: [%d]\n\r", pArea->security);
    send_to_char(buf, ch);

    sprintf(buf, "Builders: [%s]\n\r", pArea->builders);
    send_to_char(buf, ch);

    sprintf(buf, "Flags:    [%s]\n\r", flag_string(area_flags, pArea->area_flags));
    send_to_char(buf, ch);

    return FALSE;
}


bool aedit_reset(CHAR_DATA * ch, char *argument)
{
    AREA_DATA *pArea = edit_area( ch );

    reset_area(pArea);
    send_to_char("Area reset.\n\r", ch);

    return FALSE;
}


bool aedit_create(CHAR_DATA * ch, char *argument)
{
    AREA_DATA *pArea;

    if (top_area >= INT_MAX)	/*
				 * OLC 1.1b 
				 */
    {
	send_to_char("We're out of vnums for new areas.\n\r", ch);
	return FALSE;
    }

    pArea = new_area();
    area_last->next = pArea;
    area_last = pArea;		/*
				 * Thanks, Walker. 
				 */
    ch->desc->pEdit = (void *) pArea;

    SET_BIT(pArea->area_flags, AREA_ADDED);
    send_to_char("Area Created.\n\r", ch);
    return TRUE;		/*
				 * OLC 1.1b 
				 */
}


bool aedit_name(CHAR_DATA * ch, char *argument)
{
    AREA_DATA *pArea = edit_area( ch );

    if (argument[0] == '\0')
    {
	send_to_char("Syntax:   name [$name]\n\r", ch);
	return FALSE;
    }

    free_string(pArea->name);
    pArea->name = str_dup(argument);

    send_to_char("Name set.\n\r", ch);
    return TRUE;
}


bool aedit_file(CHAR_DATA * ch, char *argument)
{
    AREA_DATA *pArea = edit_area( ch );
    char file[MAX_STRING_LENGTH];
    int i, length;

    one_argument(argument, file);	/*
					 * Forces Lowercase 
					 */

    if (argument[0] == '\0')
    {
	send_to_char("Syntax:  filename [$file]\n\r", ch);
	return FALSE;
    }

    /*
     * Simple Syntax Check.
     */
    length = strlen(argument);
    if (length > 8)
    {
	send_to_char("No more than eight characters allowed.\n\r", ch);
	return FALSE;
    }

    /*
     * Allow only letters and numbers.
     */
    for (i = 0; i < length; i++)
    {
	if (!isalnum(file[i]))
	{
	    send_to_char("Only letters and numbers are valid.\n\r", ch);
	    return FALSE;
	}
    }

    free_string(pArea->filename);
    strcat(file, ".are");
    pArea->filename = str_dup(file);

    send_to_char("Filename set.\n\r", ch);
    return TRUE;
}


bool aedit_age(CHAR_DATA * ch, char *argument)
{
    AREA_DATA *pArea = edit_area( ch );
    char age[MAX_STRING_LENGTH];

    one_argument(argument, age);

    if (!is_number(age) || age[0] == '\0')
    {
	send_to_char("Syntax:  age [#age]\n\r", ch);
	return FALSE;
    }

    pArea->age = atoi(age);

    send_to_char("Age set.\n\r", ch);
    return TRUE;
}


bool aedit_recall(CHAR_DATA * ch, char *argument)
{
    AREA_DATA *pArea = edit_area( ch );
    char room[MAX_STRING_LENGTH];
    int value;

    one_argument(argument, room);

    if (!is_number(argument) || argument[0] == '\0')
    {
	send_to_char("Syntax:  recall [#rvnum]\n\r", ch);
	return FALSE;
    }

    value = atoi(room);

    if (!get_room_index(value))
    {
	send_to_char("AEdit:  Room vnum does not exist.\n\r", ch);
	return FALSE;
    }

    pArea->recall = value;

    send_to_char("Recall set.\n\r", ch);
    return TRUE;
}


bool aedit_security(CHAR_DATA * ch, char *argument)
{
    AREA_DATA *pArea = edit_area( ch );
    char sec[MAX_STRING_LENGTH];
    char buf[MAX_STRING_LENGTH];
    int value;

    one_argument(argument, sec);

    if (!is_number(sec) || sec[0] == '\0')
    {
	send_to_char("Syntax:  security [#level]\n\r", ch);
	return FALSE;
    }

    value = atoi(sec);

    if (value > ch->pcdata->security || value < 0)
    {
	if (ch->pcdata->security != 0)
	{
	    sprintf(buf, "Security is 0-%d.\n\r", ch->pcdata->security);
	    send_to_char(buf, ch);
	}
	else
	    send_to_char("Security is 0 only.\n\r", ch);
	return FALSE;
    }

    pArea->security = value;

    send_to_char("Security set.\n\r", ch);
    return TRUE;
}


bool aedit_builder(CHAR_DATA * ch, char *argument)
{
    AREA_DATA *pArea = edit_area( ch );
    char name[MAX_STRING_LENGTH];
    char buf[MAX_STRING_LENGTH];

    one_argument(argument, name);

    if (name[0] == '\0')
    {
	send_to_char("Syntax:  builder [$name]  -toggles builder\n\r", ch);
	send_to_char("Syntax:  builder All      -allows everyone\n\r", ch);
	return FALSE;
    }

    name[0] = UPPER(name[0]);

    if (strstr(pArea->builders, name) != '\0')
    {
	pArea->builders = string_replace(pArea->builders, name, "\0");
	pArea->builders = string_unpad(pArea->builders);

	if (pArea->builders[0] == '\0')
	{
	    free_string(pArea->builders);
	    pArea->builders = str_dup("None");
	}
	send_to_char("Builder removed.\n\r", ch);
	return TRUE;
    }
    else
    {
	buf[0] = '\0';
	if (strstr(pArea->builders, "None") != '\0')
	{
	    pArea->builders = string_replace(pArea->builders, "None", "\0");
	    pArea->builders = string_unpad(pArea->builders);
	}

	if (pArea->builders[0] != '\0')
	{
	    strcat(buf, pArea->builders);
	    strcat(buf, " ");
	}
	strcat(buf, name);
	free_string(pArea->builders);
	pArea->builders = string_proper(str_dup(buf));

	send_to_char("Builder added.\n\r", ch);
	return TRUE;
    }

    return FALSE;
}


bool aedit_vnum(CHAR_DATA * ch, char *argument)
{
    AREA_DATA *pArea = edit_area( ch );
    char lower[MAX_STRING_LENGTH];
    char upper[MAX_STRING_LENGTH];
    int ilower;
    int iupper;

    argument = one_argument(argument, lower);
    one_argument(argument, upper);

    if (!is_number(lower) || lower[0] == '\0'
	|| !is_number(upper) || upper[0] == '\0')
    {
	send_to_char("Syntax:  vnum [#lower] [#upper]\n\r", ch);
	return FALSE;
    }

    if ((ilower = atoi(lower)) > (iupper = atoi(upper)))
    {
	send_to_char("AEdit:  Upper must be larger then lower.\n\r", ch);
	return FALSE;
    }

    /*
     * OLC 1.1b 
     */
    if (ilower <= 0 || ilower >= INT_MAX || iupper <= 0 || iupper >= INT_MAX)
    {
	char output[MAX_STRING_LENGTH];

	sprintf(output, "AEdit: vnum must be between 0 and %d.\n\r", INT_MAX);
	send_to_char(output, ch);
	return FALSE;
    }

    if (!check_range(ilower, iupper))
    {
	send_to_char("AEdit:  Range must include only this area.\n\r", ch);
	return FALSE;
    }

    if (get_vnum_area(ilower)
	&& get_vnum_area(ilower) != pArea)
    {
	send_to_char("AEdit:  Lower vnum already assigned.\n\r", ch);
	return FALSE;
    }

    pArea->lvnum = ilower;
    send_to_char("Lower vnum set.\n\r", ch);

    if (get_vnum_area(iupper)
	&& get_vnum_area(iupper) != pArea)
    {
	send_to_char("AEdit:  Upper vnum already assigned.\n\r", ch);
	return TRUE;		/*
				 * The lower value has been set. 
				 */
    }

    pArea->uvnum = iupper;
    send_to_char("Upper vnum set.\n\r", ch);

    return TRUE;
}


bool aedit_lvnum(CHAR_DATA * ch, char *argument)
{
    AREA_DATA *pArea = edit_area( ch );
    char lower[MAX_STRING_LENGTH];
    int ilower;
    int iupper;

    one_argument(argument, lower);

    if (!is_number(lower) || lower[0] == '\0')
    {
	send_to_char("Syntax:  lvnum [#lower]\n\r", ch);
	return FALSE;
    }

    if ((ilower = atoi(lower)) > (iupper = pArea->uvnum))
    {
	send_to_char("AEdit:  Value must be less than the uvnum.\n\r", ch);
	return FALSE;
    }

    /*
     * OLC 1.1b 
     */
    if (ilower <= 0 || ilower >= INT_MAX || iupper <= 0 || iupper >= INT_MAX)
    {
	char output[MAX_STRING_LENGTH];

	sprintf(output, "AEdit: vnum must be between 0 and %d.\n\r", INT_MAX);
	send_to_char(output, ch);
	return FALSE;
    }

    if (!check_range(ilower, iupper))
    {
	send_to_char("AEdit:  Range must include only this area.\n\r", ch);
	return FALSE;
    }

    if (get_vnum_area(ilower)
	&& get_vnum_area(ilower) != pArea)
    {
	send_to_char("AEdit:  Lower vnum already assigned.\n\r", ch);
	return FALSE;
    }

    pArea->lvnum = ilower;
    send_to_char("Lower vnum set.\n\r", ch);
    return TRUE;
}


bool aedit_uvnum(CHAR_DATA * ch, char *argument)
{
    AREA_DATA *pArea = edit_area( ch );
    char upper[MAX_STRING_LENGTH];
    int ilower;
    int iupper;

    one_argument(argument, upper);

    if (!is_number(upper) || upper[0] == '\0')
    {
	send_to_char("Syntax:  uvnum [#upper]\n\r", ch);
	return FALSE;
    }

    if ((ilower = pArea->lvnum) > (iupper = atoi(upper)))
    {
	send_to_char("AEdit:  Upper must be larger then lower.\n\r", ch);
	return FALSE;
    }

    /*
     * OLC 1.1b 
     */
    if (ilower <= 0 || ilower >= INT_MAX || iupper <= 0 || iupper >= INT_MAX)
    {
	char output[MAX_STRING_LENGTH];

	sprintf(output, "AEdit: vnum must be between 0 and %d.\n\r", INT_MAX);
	send_to_char(output, ch);
	return FALSE;
    }

    if (!check_range(ilower, iupper))
    {
	send_to_char("AEdit:  Range must include only this area.\n\r", ch);
	return FALSE;
    }

    if (get_vnum_area(iupper)
	&& get_vnum_area(iupper) != pArea)
    {
	send_to_char("AEdit:  Upper vnum already assigned.\n\r", ch);
	return FALSE;
    }

    pArea->uvnum = iupper;
    send_to_char("Upper vnum set.\n\r", ch);

    return TRUE;
}


/*
 * Room Editor Functions.
 */
bool redit_show(CHAR_DATA * ch, char *argument)
{
    ROOM_INDEX_DATA *pRoom;
    char buf[MAX_STRING_LENGTH];
    char buf1[2 * MAX_STRING_LENGTH];
    OBJ_DATA *obj;
    CHAR_DATA *rch;
    int door;
    bool fcnt;

    pRoom = edit_room(ch);

    buf1[0] = '\0';

    sprintf(buf, "Description:\n\r%s", pRoom->description);
    strcat(buf1, buf);

    sprintf(buf, "Name:       [%s]\n\rArea:       [%5d] %s\n\r",
	    pRoom->name, pRoom->area->vnum, pRoom->area->name);
    strcat(buf1, buf);

    sprintf(buf, "Vnum:       [%5d]\n\rSector:     [%s]\n\r",
	    pRoom->vnum, flag_string(sector_flags, pRoom->sector_type));
    strcat(buf1, buf);

    sprintf(buf, "Room flags: [%s]\n\r",
	    flag_string(room_flags, pRoom->room_flags));
    strcat(buf1, buf);

    if (pRoom->extra_descr)
    {
	EXTRA_DESCR_DATA *ed;

	strcat(buf1, "Desc Kwds:  [");
	for (ed = pRoom->extra_descr; ed; ed = ed->next)
	{
	    strcat(buf1, ed->keyword);
	    if (ed->next)
		strcat(buf1, " ");
	}
	strcat(buf1, "]\n\r");
    }

    strcat(buf1, "Characters: [");
    fcnt = FALSE;
    for (rch = pRoom->people; rch; rch = rch->next_in_room)
    {
	one_argument(rch->name, buf);
	strcat(buf1, buf);
	strcat(buf1, " ");
	fcnt = TRUE;
    }

    if (fcnt)
    {
	int end;

	end = strlen(buf1) - 1;
	buf1[end] = ']';
	strcat(buf1, "\n\r");
    }
    else
	strcat(buf1, "none]\n\r");

    strcat(buf1, "Objects:    [");
    fcnt = FALSE;
    for (obj = pRoom->contents; obj; obj = obj->next_content)
    {
	one_argument(obj->name, buf);
	strcat(buf1, buf);
	strcat(buf1, " ");
	fcnt = TRUE;
    }

    if (fcnt)
    {
	int end;

	end = strlen(buf1) - 1;
	buf1[end] = ']';
	strcat(buf1, "\n\r");
    }
    else
	strcat(buf1, "none]\n\r");

    for (door = 0; door < MAX_DIR; door++)
    {
	EXIT_DATA *pexit;

	if ((pexit = pRoom->exit[door]))
	{
	    char word[MAX_INPUT_LENGTH];
	    char reset_state[MAX_STRING_LENGTH];
	    char *state;
	    int i, length;

	    sprintf(buf, "-%-5s to [%5d] Key: [%5d]",
		    capitalize(dir_name[door]),
		    pexit->to_room ? pexit->to_room->vnum : 0,
		    pexit->key);
	    strcat(buf1, buf);

	    /*
	     * Format up the exit info.
	     * Capitalize all flags that are not part of the reset info.
	     */
	    strcpy(reset_state, flag_string(exit_flags, pexit->rs_flags));
	    state = flag_string(exit_flags, pexit->exit_info);
	    strcat(buf1, " Exit flags: [");
	    for (;;)
	    {
		state = one_argument(state, word);

		if (word[0] == '\0')
		{
		    int end;

		    end = strlen(buf1) - 1;
		    buf1[end] = ']';
		    strcat(buf1, "\n\r");
		    break;
		}

		if (str_infix(word, reset_state))
		{
		    length = strlen(word);
		    for (i = 0; i < length; i++)
			word[i] = toupper(word[i]);
		}
		strcat(buf1, word);
		strcat(buf1, " ");
	    }

	    if (pexit->keyword && pexit->keyword[0] != '\0')
	    {
		sprintf(buf, "Kwds: [%s]\n\r", pexit->keyword);
		strcat(buf1, buf);
	    }
	    if (pexit->description && pexit->description[0] != '\0')
	    {
		sprintf(buf, "%s", pexit->description);
		strcat(buf1, buf);
	    }
	}
    }

    send_to_char(buf1, ch);
    return FALSE;
}


/*
 * OLC 1.1b 
 *
 * Name:	change_exit
 * Purpose:	Command interpreter for changing exits.
 * Called by:	redit_<dir>.  This is a local function.
 */
bool change_exit(CHAR_DATA * ch, char *argument, int door)
{
    ROOM_INDEX_DATA *pRoom;
    char command[MAX_INPUT_LENGTH];
    char arg[MAX_INPUT_LENGTH];
    char total_arg[MAX_STRING_LENGTH];
    int rev;
    int value = 0;

    pRoom = edit_room(ch);

    /*
     * Often used data. 
     */
    rev = rev_dir[door];

    if (argument[0] == '\0')
    {
	do_help(ch, "EXIT");
	return FALSE;
    }

    /*
     * Now parse the arguments.
     */
    strcpy(total_arg, argument);
    argument = one_argument(argument, command);
    one_argument(argument, arg);

    if (!str_cmp(command, "delete"))
    {
	if (!pRoom->exit[door])
	{
	    send_to_char("REdit:  Exit does not exist.\n\r", ch);
	    return FALSE;
	}

	/*
	 * Remove To Room Exit.
	 */
	if (pRoom->exit[door]->to_room->exit[rev])
	{
	    free_exit(pRoom->exit[door]->to_room->exit[rev]);
	    pRoom->exit[door]->to_room->exit[rev] = NULL;
	}

	/*
	 * Remove this exit.
	 */
	free_exit(pRoom->exit[door]);
	pRoom->exit[door] = NULL;

	send_to_char("Exit unlinked.\n\r", ch);
	return TRUE;
    }

    /*
     * Create a two-way exit.
     */
    if (!str_cmp(command, "link"))
    {
	EXIT_DATA *pExit;
	ROOM_INDEX_DATA *pLinkRoom;

	if (arg[0] == '\0' || !is_number(arg))
	{
	    send_to_char("Syntax:  [direction] link [vnum]\n\r", ch);
	    return FALSE;
	}

	if (!(pLinkRoom = get_room_index(atoi(arg))))
	{
	    send_to_char("REdit:  Non-existant room.\n\r", ch);
	    return FALSE;
	}

	if (!is_builder(ch, pLinkRoom->area))
	{
	    send_to_char("REdit:  Cannot link to that area.\n\r", ch);
	    return FALSE;
	}

	if (pLinkRoom->exit[rev])
	{
	    send_to_char("REdit:  Remote side's exit exists.\n\r", ch);
	    return FALSE;
	}

	if (!pRoom->exit[door])	/*
				 * No exit.             
				 */
	    pRoom->exit[door] = new_exit();

	pRoom->exit[door]->to_room = pLinkRoom;		/*
							 * Assign data.         
							 */
	pRoom->exit[door]->vnum = value;

	pExit = new_exit();	/*
				 * No remote exit.      
				 */

	pExit->to_room = ch->in_room;	/*
					 * Assign data.         
					 */
	pExit->vnum = ch->in_room->vnum;

	pLinkRoom->exit[rev] = pExit;	/*
					 * Link exit to room.   
					 */

	send_to_char("Two-way link established.\n\r", ch);
	return TRUE;
    }

    /*
     * Create room and make two-way exit.
     */
    if (!str_cmp(command, "dig"))
    {
	char buf[MAX_INPUT_LENGTH];

	if (arg[0] == '\0' || !is_number(arg))
	{
	    send_to_char("Syntax: [direction] dig <vnum>\n\r", ch);
	    return FALSE;
	}

	redit_create(ch, arg);	/*
				 * Create the room.     
				 */
	sprintf(buf, "link %s", arg);
	change_exit(ch, buf, door);	/*
					 * Create the exits.    
					 */
	return TRUE;
    }

    /*
     * Create one-way exit.
     */
    if (!str_cmp(command, "room"))
    {
	ROOM_INDEX_DATA *pLinkRoom;

	if (arg[0] == '\0' || !is_number(arg))
	{
	    send_to_char("Syntax:  [direction] room [vnum]\n\r", ch);
	    return FALSE;
	}

	if (!(pLinkRoom = get_room_index(atoi(arg))))
	{
	    send_to_char("REdit:  Non-existant room.\n\r", ch);
	    return FALSE;
	}

	if (!pRoom->exit[door])
	    pRoom->exit[door] = new_exit();

	pRoom->exit[door]->to_room = pLinkRoom;
	pRoom->exit[door]->vnum = value;

	send_to_char("One-way link established.\n\r", ch);
	return TRUE;
    }

    if (!str_cmp(command, "remove"))
    {
	if (arg[0] == '\0')
	{
	    send_to_char("Syntax:  [direction] remove [key/name/desc]\n\r", ch);
	    return FALSE;
	}

	if (!pRoom->exit[door])
	{
	    send_to_char("REdit:  Exit does not exist.\n\r", ch);
	    return FALSE;
	}

	if (!str_cmp(argument, "key"))
	{
	    pRoom->exit[door]->key = 0;
	    send_to_char("Exit key removed.\n\r", ch);
	    return TRUE;
	}

	if (!str_cmp(argument, "name"))
	{
	    free_string(pRoom->exit[door]->keyword);
	    pRoom->exit[door]->keyword = &str_empty[0];
	    send_to_char("Exit name removed.\n\r", ch);
	    return TRUE;
	}

	if (argument[0] == 'd' && !str_prefix(argument, "description"))
	{
	    free_string(pRoom->exit[door]->description);
	    pRoom->exit[door]->description = &str_empty[0];
	    send_to_char("Exit description removed.\n\r", ch);
	    return TRUE;
	}

	send_to_char("Syntax:  [direction] remove [key/name/desc]\n\r", ch);
	return FALSE;
    }

    if (!str_cmp(command, "key"))
    {
	OBJ_INDEX_DATA *pObjIndex;

	if (arg[0] == '\0' || !is_number(arg))
	{
	    send_to_char("Syntax:  [direction] key [vnum]\n\r", ch);
	    return FALSE;
	}

	if (!(pObjIndex = get_obj_index(atoi(arg))))
	{
	    send_to_char("REdit:  Item does not exist.\n\r", ch);
	    return FALSE;
	}

	if (pObjIndex->item_type != ITEM_KEY)
	{
	    send_to_char("REdit:  Item is not a key.\n\r", ch);
	    return FALSE;
	}

	if (!pRoom->exit[door])
	    pRoom->exit[door] = new_exit();

	pRoom->exit[door]->key = pObjIndex->vnum;

	send_to_char("Exit key set.\n\r", ch);
	return TRUE;
    }

    if (!str_cmp(command, "name"))
    {
	if (arg[0] == '\0')
	{
	    send_to_char("Syntax:  [direction] name [string]\n\r", ch);
	    return FALSE;
	}

	if (!pRoom->exit[door])
	    pRoom->exit[door] = new_exit();

	free_string(pRoom->exit[door]->keyword);
	pRoom->exit[door]->keyword = str_dup(argument);

	send_to_char("Exit name set.\n\r", ch);
	return TRUE;
    }

    if (command[0] == 'd' && !str_prefix(command, "description"))
    {
	if (arg[0] == '\0')
	{
	    if (!pRoom->exit[door])
		pRoom->exit[door] = new_exit();

	    string_append(ch, &pRoom->exit[door]->description);
	    return TRUE;
	}

	send_to_char("Syntax:  [direction] desc\n\r", ch);
	return FALSE;
    }

    /*
     * Set the exit flags, needs full argument.
     * ----------------------------------------
     */
    if ((value = flag_value(exit_flags, total_arg)) != NO_FLAG)
    {
	ROOM_INDEX_DATA *pToRoom;

	/*
	 * Create an exit if none exists.
	 */
	if (!pRoom->exit[door])
	    pRoom->exit[door] = new_exit();

	/*
	 * Set door bits for this room.
	 */
	TOGGLE_BIT(pRoom->exit[door]->rs_flags, value);
	pRoom->exit[door]->exit_info = pRoom->exit[door]->rs_flags;

	/*
	 * Set door bits of connected room.
	 * Skip one-way exits and non-existant rooms.
	 */
	if ((pToRoom = pRoom->exit[door]->to_room) && pToRoom->exit[rev])
	{
	    TOGGLE_BIT(pToRoom->exit[rev]->rs_flags, value);
	    pToRoom->exit[rev]->exit_info = pToRoom->exit[rev]->rs_flags;
	}

	send_to_char("Exit flag toggled.\n\r", ch);
	return TRUE;
    }

    return FALSE;
}


bool redit_north(CHAR_DATA * ch, char *argument)
{
    if (change_exit(ch, argument, DIR_NORTH))
	return TRUE;

    return FALSE;
}


bool redit_south(CHAR_DATA * ch, char *argument)
{
    if (change_exit(ch, argument, DIR_SOUTH))
	return TRUE;

    return FALSE;
}


bool redit_east(CHAR_DATA * ch, char *argument)
{
    if (change_exit(ch, argument, DIR_EAST))
	return TRUE;

    return FALSE;
}


bool redit_west(CHAR_DATA * ch, char *argument)
{
    if (change_exit(ch, argument, DIR_WEST))
	return TRUE;

    return FALSE;
}


bool redit_up(CHAR_DATA * ch, char *argument)
{
    if (change_exit(ch, argument, DIR_UP))
	return TRUE;

    return FALSE;
}


bool redit_down(CHAR_DATA * ch, char *argument)
{
    if (change_exit(ch, argument, DIR_DOWN))
	return TRUE;

    return FALSE;
}


/*
 * OLC 1.1b 
 */
bool redit_move(CHAR_DATA * ch, char *argument)
{
    interpret(ch, argument);
    return FALSE;
}


bool redit_ed(CHAR_DATA * ch, char *argument)
{
    ROOM_INDEX_DATA *pRoom;
    EXTRA_DESCR_DATA *ed;
    char command[MAX_INPUT_LENGTH];
    char keyword[MAX_INPUT_LENGTH];

    pRoom = edit_room(ch);

    argument = one_argument(argument, command);
    one_argument(argument, keyword);

    if (command[0] == '\0' || keyword[0] == '\0')
    {
	send_to_char("Syntax:  ed add [keyword]\n\r", ch);
	send_to_char("         ed edit [keyword]\n\r", ch);
	send_to_char("         ed delete [keyword]\n\r", ch);
	send_to_char("         ed format [keyword]\n\r", ch);
	return FALSE;
    }

    if (!str_cmp(command, "add"))
    {
	if (keyword[0] == '\0')
	{
	    send_to_char("Syntax:  ed add [keyword]\n\r", ch);
	    return FALSE;
	}

	ed = new_extra_descr();
	ed->keyword = str_dup(keyword);
	ed->description = str_dup("");
	ed->next = pRoom->extra_descr;
	pRoom->extra_descr = ed;

	string_append(ch, &ed->description);

	return TRUE;
    }


    if (!str_cmp(command, "edit"))
    {
	if (keyword[0] == '\0')
	{
	    send_to_char("Syntax:  ed edit [keyword]\n\r", ch);
	    return FALSE;
	}

	for (ed = pRoom->extra_descr; ed; ed = ed->next)
	{
	    if (is_name(keyword, ed->keyword))
		break;
	}

	if (!ed)
	{
	    send_to_char("REdit:  Extra description keyword not found.\n\r", ch);
	    return FALSE;
	}

	string_append(ch, &ed->description);

	return TRUE;
    }


    if (!str_cmp(command, "delete"))
    {
	EXTRA_DESCR_DATA *ped = NULL;

	if (keyword[0] == '\0')
	{
	    send_to_char("Syntax:  ed delete [keyword]\n\r", ch);
	    return FALSE;
	}

	for (ed = pRoom->extra_descr; ed; ed = ed->next)
	{
	    if (is_name(keyword, ed->keyword))
		break;
	    ped = ed;
	}

	if (!ed)
	{
	    send_to_char("REdit:  Extra description keyword not found.\n\r", ch);
	    return FALSE;
	}

	if (!ped)
	    pRoom->extra_descr = ed->next;
	else
	    ped->next = ed->next;

	free_extra_descr(ed);

	send_to_char("Extra description deleted.\n\r", ch);
	return TRUE;
    }


    if (!str_cmp(command, "format"))
    {
	if (keyword[0] == '\0')
	{
	    send_to_char("Syntax:  ed format [keyword]\n\r", ch);
	    return FALSE;
	}

	for (ed = pRoom->extra_descr; ed; ed = ed->next)
	{
	    if (is_name(keyword, ed->keyword))
		break;
	}

	if (!ed)
	{
	    send_to_char("REdit:  Extra description keyword not found.\n\r", ch);
	    return FALSE;
	}

	/*
	 * OLC 1.1b 
	 */
	if (strlen(ed->description) >= (MAX_STRING_LENGTH - 4))
	{
	    send_to_char("String too long to be formatted.\n\r", ch);
	    return FALSE;
	}

	ed->description = format_string(ed->description);

	send_to_char("Extra description formatted.\n\r", ch);
	return TRUE;
    }

    redit_ed(ch, "");
    return FALSE;
}


bool redit_create(CHAR_DATA * ch, char *argument)
{
    AREA_DATA *pArea;
    ROOM_INDEX_DATA *pRoom;
    int value;
    int iHash;

    pRoom = edit_room(ch);

    value = atoi(argument);

    /*
     * OLC 1.1b 
     */
    if (argument[0] == '\0' || value <= 0 || value >= INT_MAX)
    {
	char output[MAX_STRING_LENGTH];

	sprintf(output, "Syntax:  create [0 < vnum < %d]\n\r", INT_MAX);
	send_to_char(output, ch);
	return FALSE;
    }

    if (argument[0] == '\0' || value <= 0)
    {
	send_to_char("Syntax:  create [vnum > 0]\n\r", ch);
	return FALSE;
    }

    pArea = get_vnum_area(value);
    if (!pArea)
    {
	send_to_char("REdit:  That vnum is not assigned an area.\n\r", ch);
	return FALSE;
    }

    if (!is_builder(ch, pArea))
    {
	send_to_char("REdit:  Vnum in an area you cannot build in.\n\r", ch);
	return FALSE;
    }

    if (get_room_index(value))
    {
	send_to_char("REdit:  Room vnum already exists.\n\r", ch);
	return FALSE;
    }

    pRoom = new_room_index();
    pRoom->area = pArea;
    pRoom->vnum = value;

    if (value > top_vnum_room)
	top_vnum_room = value;

    iHash = value % MAX_KEY_HASH;
    pRoom->next = room_index_hash[iHash];
    room_index_hash[iHash] = pRoom;
    ch->desc->pEdit = (void *) pRoom;

    send_to_char("Room created.\n\r", ch);
    return TRUE;
}


bool redit_name(CHAR_DATA * ch, char *argument)
{
    ROOM_INDEX_DATA *pRoom;

    pRoom = edit_room(ch);

    if (argument[0] == '\0')
    {
	send_to_char("Syntax:  name [name]\n\r", ch);
	return FALSE;
    }

    free_string(pRoom->name);
    pRoom->name = str_dup(argument);

    send_to_char("Name set.\n\r", ch);
    return TRUE;
}


bool redit_desc(CHAR_DATA * ch, char *argument)
{
    ROOM_INDEX_DATA *pRoom;

    pRoom = edit_room(ch);

    if (argument[0] == '\0')
    {
	string_append(ch, &pRoom->description);
	return TRUE;
    }

    send_to_char("Syntax:  desc\n\r", ch);
    return FALSE;
}


bool redit_format(CHAR_DATA * ch, char *argument)
{
    ROOM_INDEX_DATA *pRoom;

    pRoom = edit_room(ch);

    /*
     * OLC 1.1b 
     */
    if (strlen(pRoom->description) >= (MAX_STRING_LENGTH - 4))
    {
	send_to_char("String too long to be formatted.\n\r", ch);
	return FALSE;
    }

    pRoom->description = format_string(pRoom->description);

    send_to_char("String formatted.\n\r", ch);
    return TRUE;
}


bool redit_mreset(CHAR_DATA * ch, char *argument)
{
    ROOM_INDEX_DATA *pRoom;
    MOB_INDEX_DATA *pMobIndex;
    CHAR_DATA *newmob;
    char arg[MAX_INPUT_LENGTH];

    RESET_DATA *pReset;
    char output[MAX_STRING_LENGTH];

    pRoom = edit_room(ch);

    argument = one_argument(argument, arg);

    if (arg[0] == '\0' || !is_number(arg))
    {
	send_to_char("Syntax:  mreset <vnum> <max #>\n\r", ch);
	return FALSE;
    }

    if (!(pMobIndex = get_mob_index(atoi(arg))))
    {
	send_to_char("REdit: No mobile has that vnum.\n\r", ch);
	return FALSE;
    }

    if (pMobIndex->area != pRoom->area)
    {
	send_to_char("REdit: No such mobile in this area.\n\r", ch);
	return FALSE;
    }

    /*
     * Create the mobile reset.
     */
    pReset = new_reset_data();
    pReset->command = 'M';
    pReset->arg1 = pMobIndex->vnum;
    pReset->arg2 = is_number(argument) ? atoi(argument) : MAX_MOB;
    pReset->arg3 = pRoom->vnum;
    add_reset(pRoom, pReset, 0	/*
				 * Last slot
				 */ );

    /*
     * Create the mobile.
     */
    newmob = create_mobile(pMobIndex);
    char_to_room(newmob, pRoom);

    sprintf(output, "%s (%d) has been loaded and added to resets.\n\r"
	    "There will be a maximum of %d loaded to this room.\n\r",
	    capitalize(pMobIndex->short_descr),
	    pMobIndex->vnum,
	    pReset->arg2);
    send_to_char(output, ch);
    act("$n has created $N!", ch, NULL, newmob, TO_ROOM);
    return TRUE;
}


struct wear_type
{
    int wear_loc;
    int wear_bit;
};


const struct wear_type wear_table_olc[] =
{
    {	WEAR_NONE, 	ITEM_TAKE		},
    {	WEAR_LIGHT, 	ITEM_LIGHT		},
    {	WEAR_FINGER_L, 	ITEM_WEAR_FINGER	},
    {	WEAR_FINGER_R, 	ITEM_WEAR_FINGER	},
    {	WEAR_NECK_1, 	ITEM_WEAR_NECK		},
    {	WEAR_NECK_2, 	ITEM_WEAR_NECK		},
    {	WEAR_BODY, 	ITEM_WEAR_BODY		},
    {	WEAR_HEAD, 	ITEM_WEAR_HEAD		},
    {	WEAR_LEGS, 	ITEM_WEAR_LEGS		},
    {	WEAR_FEET, 	ITEM_WEAR_FEET		},
    {	WEAR_HANDS, 	ITEM_WEAR_HANDS		},
    {	WEAR_ARMS, 	ITEM_WEAR_ARMS		},
    {	WEAR_SHIELD, 	ITEM_WEAR_SHIELD	},
    {	WEAR_ABOUT, 	ITEM_WEAR_ABOUT		},
    {	WEAR_WAIST, 	ITEM_WEAR_WAIST		},
    {	WEAR_WRIST_L, 	ITEM_WEAR_WRIST		},
    {	WEAR_WRIST_R, 	ITEM_WEAR_WRIST		},
    {	WEAR_WIELD, 	ITEM_WIELD		},
    {	WEAR_HOLD, 	ITEM_HOLD		},
    {	NO_FLAG, 	NO_FLAG			}
};


/*
 * Name:	wear_loc
 * Purpose:	Returns the location of the bit that matches the count.
 *		1 = first match, 2 = second match etc.
 * Called by:	oedit_reset(olc_act.c).
 */
int wear_loc(int bits, int count)
{
    int flag;

    for (flag = 0; wear_table_olc[flag].wear_bit != NO_FLAG; flag++)
    {
	if (IS_SET(bits, wear_table_olc[flag].wear_bit) && --count < 1)
	    return wear_table_olc[flag].wear_loc;
    }

    return NO_FLAG;
}


/*
 * Name:	wear_bit
 * Purpose:	Converts a wear_loc into a bit.
 * Called by:	redit_oreset(olc_act.c).
 */
int wear_bit(int loc)
{
    int flag;

    for (flag = 0; wear_table_olc[flag].wear_loc != NO_FLAG; flag++)
    {
	if (loc == wear_table_olc[flag].wear_loc)
	    return wear_table_olc[flag].wear_bit;
    }

    return 0;
}


bool redit_oreset(CHAR_DATA * ch, char *argument)
{
    ROOM_INDEX_DATA *pRoom;
    OBJ_INDEX_DATA *pObjIndex;
    OBJ_DATA *newobj;
    OBJ_DATA *to_obj;
    CHAR_DATA *to_mob;
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    int olevel = 0;

    RESET_DATA *pReset;
    char output[MAX_STRING_LENGTH];

    pRoom = edit_room(ch);

    argument = one_argument(argument, arg1);
    argument = one_argument(argument, arg2);

    if (arg1[0] == '\0' || !is_number(arg1))
    {
	send_to_char("Syntax:  oreset <vnum> <args>\n\r", ch);
	send_to_char("        -no_args               = into room\n\r", ch);
	send_to_char("        -<obj_name>            = into obj\n\r", ch);
	send_to_char("        -<mob_name> <wear_loc> = into mob\n\r", ch);
	return FALSE;
    }

    if (!(pObjIndex = get_obj_index(atoi(arg1))))
    {
	send_to_char("REdit: No object has that vnum.\n\r", ch);
	return FALSE;
    }

    if (pObjIndex->area != pRoom->area)
    {
	send_to_char("REdit: No such object in this area.\n\r", ch);
	return FALSE;
    }

    /*
     * Load into room.
     */
    if (arg2[0] == '\0')
    {
	pReset = new_reset_data();
	pReset->command = 'O';
	pReset->arg1 = pObjIndex->vnum;
	pReset->arg2 = 0;
	pReset->arg3 = pRoom->vnum;
	add_reset(pRoom, pReset, 0	/*
					 * Last slot
					 */ );

	newobj = create_object(pObjIndex, number_fuzzy(olevel));
	obj_to_room(newobj, pRoom);

	sprintf(output, "%s (%d) has been loaded and added to resets.\n\r",
		capitalize(pObjIndex->short_descr),
		pObjIndex->vnum);
	send_to_char(output, ch);
    }
    else
	/*
	 * Load into object's inventory.
	 */
	if (argument[0] == '\0'
	 && ((to_obj = get_obj_list(ch, arg2, pRoom->contents)) != NULL))
    {
	pReset = new_reset_data();
	pReset->command = 'P';
	pReset->arg1 = pObjIndex->vnum;
	pReset->arg2 = 0;
	pReset->arg3 = to_obj->pIndexData->vnum;
	add_reset(pRoom, pReset, 0	/*
					 * Last slot
					 */ );

	newobj = create_object(pObjIndex, number_fuzzy(olevel));
	newobj->cost = 0;
	obj_to_obj(newobj, to_obj);

	sprintf(output, "%s (%d) has been loaded into "
		"%s (%d) and added to resets.\n\r",
		capitalize(newobj->short_descr),
		newobj->pIndexData->vnum,
		to_obj->short_descr,
		to_obj->pIndexData->vnum);
	send_to_char(output, ch);
    }
    else
	/*
	 * Load into mobile's inventory.
	 */
    if ((to_mob = get_char_room(ch, arg2)) != NULL)
    {
	int wear_loc;

	/*
	 * Make sure the location on mobile is valid.
	 */
	if ((wear_loc = flag_value(wear_loc_flags, argument)) == NO_FLAG)
	{
	    send_to_char("REdit: Invalid wear_loc.  '? wear-loc'\n\r", ch);
	    return FALSE;
	}

	/*
	 * Disallow loading a sword(WEAR_WIELD) into WEAR_HEAD.
	 */
	if (!IS_SET(pObjIndex->wear_flags, wear_bit(wear_loc)))
	{
	    sprintf(output,
		    "%s (%d) has wear flags: [%s]\n\r",
		    capitalize(pObjIndex->short_descr),
		    pObjIndex->vnum,
		    flag_string(wear_flags, pObjIndex->wear_flags));
	    send_to_char(output, ch);
	    return FALSE;
	}

	/*
	 * Can't load into same position.
	 */
	if (get_eq_char(to_mob, wear_loc))
	{
	    send_to_char("REdit:  Object already equipped.\n\r", ch);
	    return FALSE;
	}

	pReset = new_reset_data();
	pReset->arg1 = pObjIndex->vnum;
	pReset->arg2 = wear_loc;
	if (pReset->arg2 == WEAR_NONE)
	    pReset->command = 'G';
	else
	    pReset->command = 'E';
	pReset->arg3 = wear_loc;

	add_reset(pRoom, pReset, 0	/*
					 * Last slot
					 */ );

	olevel = URANGE(0, to_mob->level - 2, LEVEL_HERO);
	newobj = create_object(pObjIndex, number_fuzzy(olevel));

	if (to_mob->pIndexData->pShop)	/*
					 * Shop-keeper? 
					 */
	{
	    switch (pObjIndex->item_type)
	    {
		default:
		    olevel = 0;
		    break;
		case ITEM_PILL:
		    olevel = number_range(0, 10);
		    break;
		case ITEM_POTION:
		    olevel = number_range(0, 10);
		    break;
		case ITEM_SCROLL:
		    olevel = number_range(5, 15);
		    break;
		case ITEM_WAND:
		    olevel = number_range(10, 20);
		    break;
		case ITEM_STAFF:
		    olevel = number_range(15, 25);
		    break;
		case ITEM_ARMOR:
		    olevel = number_range(5, 15);
		    break;
		case ITEM_WEAPON:
		    if (pReset->command == 'G')
			olevel = number_range(5, 15);
		    else
			olevel = number_fuzzy(olevel);
		    break;
	    }

	    newobj = create_object(pObjIndex, olevel);
	    if (pReset->arg2 == WEAR_NONE)
		SET_BIT(newobj->extra_flags, ITEM_INVENTORY);
	}
	else
	    newobj = create_object(pObjIndex, number_fuzzy(olevel));

	obj_to_char(newobj, to_mob);
	if (pReset->command == 'E')
	    equip_char(to_mob, newobj, pReset->arg3);

	sprintf(output, "%s (%d) has been loaded "
		"%s of %s (%d) and added to resets.\n\r",
		capitalize(pObjIndex->short_descr),
		pObjIndex->vnum,
		flag_string(wear_loc_strings, pReset->arg3),
		to_mob->short_descr,
		to_mob->pIndexData->vnum);
	send_to_char(output, ch);
    }
    else
	/*
	 * Display Syntax 
	 */
    {
	send_to_char("REdit:  That mobile isn't here.\n\r", ch);
	return FALSE;
    }

    act("$n has created $p!", ch, newobj, NULL, TO_ROOM);
    return TRUE;
}


/*
 * Object Editor Functions.
 */
void show_obj_values(CHAR_DATA * ch, OBJ_INDEX_DATA * obj)
{
    char buf[MAX_STRING_LENGTH];

    switch (obj->item_type)
    {
	default:		/*
				 * No values. 
				 */
	    break;

	case ITEM_LIGHT:
	    if (obj->value[2] == -1)
		sprintf(buf, "[v2] Light:  Infinite[-1]\n\r");
	    else
		sprintf(buf, "[v2] Light:  [%d]\n\r", obj->value[2]);
	    send_to_char(buf, ch);
	    break;

	case ITEM_WAND:
	case ITEM_STAFF:
	    sprintf(buf,
		    "[v0] Level:          [%d]\n\r"
		    "[v1] Charges Total:  [%d]\n\r"
		    "[v2] Charges Left:   [%d]\n\r"
		    "[v3] Spell:          %s\n\r",
		    obj->value[0],
		    obj->value[1],
		    obj->value[2],
		    obj->value[3] != -1 ? skill_table[obj->value[3]].name
		     : "none");
	    send_to_char(buf, ch);
	    break;

	case ITEM_SCROLL:
	case ITEM_POTION:
	case ITEM_PILL:
	    sprintf(buf,
		    "[v0] Level:  [%d]\n\r"
		    "[v1] Spell:  %s\n\r"
		    "[v2] Spell:  %s\n\r"
		    "[v3] Spell:  %s\n\r"
		    "[v4] Spell:  %s\n\r",
		    obj->value[0],
		    obj->value[1] != -1 ? skill_table[obj->value[1]].name
		    : "none",
		    obj->value[2] != -1 ? skill_table[obj->value[2]].name
		    : "none",
		    obj->value[3] != -1 ? skill_table[obj->value[3]].name
		    : "none",
		    obj->value[4] != -1 ? skill_table[obj->value[4]].name
		    : "none");
	    send_to_char(buf, ch);
	    break;

	case ITEM_WEAPON:
	    sprintf(buf,
		    "[v1] Damage minimum: [%d]\n\r"
		    "[v2] Damage maximum: [%d]\n\r"
		    "[v3] Type:           %s\n\r",
		    obj->value[1],
		    obj->value[2],
		    flag_string(weapon_flags, obj->value[3]));
	    send_to_char(buf, ch);
	    break;

	case ITEM_CONTAINER:
	    sprintf(buf,
		    "[v0] Weight: [%d kg]\n\r"
		    "[v1] Flags:  [%s]\n\r"
		    "[v2] Key:    %s [%d]\n\r",
		    obj->value[0],
		    flag_string(container_flags, obj->value[1]),
		    get_obj_index(obj->value[2])
		    ? get_obj_index(obj->value[2])->short_descr
		    : "none", obj->value[2]);
	    send_to_char(buf, ch);
	    break;

	case ITEM_DRINK_CON:
	    sprintf(buf,
		    "[v0] Liquid Total: [%d]\n\r"
		    "[v1] Liquid Left:  [%d]\n\r"
		    "[v2] Liquid:       %s\n\r"
		    "[v3] Poisoned:     %s\n\r",
		    obj->value[0],
		    obj->value[1],
		    flag_string(liquid_flags, obj->value[2]),
		    obj->value[3] != 0 ? "Yes" : "No");
	    send_to_char(buf, ch);
	    break;

	case ITEM_FOOD:
	    sprintf(buf,
		    "[v0] Food hours: [%d]\n\r"
		    "[v3] Poisoned:   %s\n\r",
		    obj->value[0],
		    obj->value[3] != 0 ? "Yes" : "No");
	    send_to_char(buf, ch);
	    break;

	case ITEM_MONEY:
	    sprintf(buf, "[v0] Gold:   [%d]\n\r", obj->value[0]);
	    send_to_char(buf, ch);
	    break;

	case ITEM_PORTAL:
	    sprintf(buf,
		    "[v0] Charges:        [%d]\n\r"
		    "[v1] Flags:          [%s]\n\r"
		    "[v2] Key:            %s [%d]\n\r"
		    "[v3] Flags:          [%s]\n\r"
		    "[v4] Destiny:        [%d]\n\r",
		    obj->value[0],
		    flag_string(portal_door_flags, obj->value[1]),
		    get_obj_index(obj->value[2])
		    ? get_obj_index(obj->value[2])->short_descr
		    : "none", obj->value[2],
		    flag_string(portal_flags, obj->value[3]),
		    obj->value[4]);
	    send_to_char(buf, ch);
	    break;
    }

    return;
}


bool set_obj_values(CHAR_DATA * ch, OBJ_INDEX_DATA * pObj, int value_num, char *argument)
{
    switch (pObj->item_type)
    {
	default:
	    break;

	case ITEM_LIGHT:
	    switch (value_num)
	    {
		default:
		    do_help(ch, "ITEM_LIGHT");
		    return FALSE;
		case 2:
		    send_to_char("HOURS OF LIGHT SET.\n\r\n\r", ch);
		    pObj->value[2] = atoi(argument);
		    break;
	    }
	    break;

	case ITEM_WAND:
	case ITEM_STAFF:
	    switch (value_num)
	    {
		default:
		    do_help(ch, "ITEM_STAFF_WAND");
		    return FALSE;
		case 0:
		    send_to_char("SPELL LEVEL SET.\n\r\n\r", ch);
		    pObj->value[0] = atoi(argument);
		    break;
		case 1:
		    send_to_char("TOTAL NUMBER OF CHARGES SET.\n\r\n\r", ch);
		    pObj->value[1] = atoi(argument);
		    break;
		case 2:
		    send_to_char("CURRENT NUMBER OF CHARGES SET.\n\r\n\r", ch);
		    pObj->value[2] = atoi(argument);
		    break;
		case 3:
		    send_to_char("SPELL TYPE SET.\n\r", ch);
		    pObj->value[3] = skill_lookup(argument);
		    break;
	    }
	    break;

	case ITEM_SCROLL:
	case ITEM_POTION:
	case ITEM_PILL:
	    switch (value_num)
	    {
		default:
		    do_help(ch, "ITEM_SCROLL_POTION_PILL");
		    return FALSE;
		case 0:
		    send_to_char("SPELL LEVEL SET.\n\r\n\r", ch);
		    pObj->value[0] = atoi(argument);
		    break;
		case 1:
		    send_to_char("SPELL TYPE 1 SET.\n\r\n\r", ch);
		    pObj->value[1] = skill_lookup(argument);
		    break;
		case 2:
		    send_to_char("SPELL TYPE 2 SET.\n\r\n\r", ch);
		    pObj->value[2] = skill_lookup(argument);
		    break;
		case 3:
		    send_to_char("SPELL TYPE 3 SET.\n\r\n\r", ch);
		    pObj->value[3] = skill_lookup(argument);
		    break;
		case 4:
		    send_to_char("SPELL TYPE 4 SET.\n\r\n\r", ch);
		    pObj->value[4] = skill_lookup(argument);
		    break;
	    }
	    break;

	case ITEM_WEAPON:
	    switch (value_num)
	    {
		default:
		    do_help(ch, "ITEM_WEAPON");
		    return FALSE;
		    break;
		case 1:
		    send_to_char("MINIMUM DAMAGE SET.\n\r\n\r", ch);
		    pObj->value[1] = atoi(argument);
		    break;
		case 2:
		    send_to_char("MAXIMUM DAMAGE SET.\n\r\n\r", ch);
		    pObj->value[2] = atoi(argument);
		    break;
		case 3:
		    send_to_char("WEAPON TYPE SET.\n\r\n\r", ch);
		    pObj->value[3] = flag_value(weapon_flags, argument);
		    break;
	    }
	    break;

	case ITEM_CONTAINER:
	    switch (value_num)
	    {
		    int value;

		default:
		    do_help(ch, "ITEM_CONTAINER");
		    return FALSE;
		case 0:
		    send_to_char("WEIGHT CAPACITY SET.\n\r\n\r", ch);
		    pObj->value[0] = atoi(argument);
		    break;
		case 1:
		    if ((value = flag_value(container_flags, argument))
			!= NO_FLAG)
			TOGGLE_BIT(pObj->value[1], value);
		    else
		    {
			do_help(ch, "ITEM_CONTAINER");
			return FALSE;
		    }
		    send_to_char("CONTAINER TYPE SET.\n\r\n\r", ch);
		    break;
		case 2:
		    if (atoi(argument) != 0)
		    {
			if (!get_obj_index(atoi(argument)))
			{
			    send_to_char("THERE IS NO SUCH ITEM.\n\r\n\r", ch);
			    return FALSE;
			}

			if (get_obj_index(atoi(argument))->item_type != ITEM_KEY)
			{
			    send_to_char("THAT ITEM IS NOT A KEY.\n\r\n\r", ch);
			    return FALSE;
			}
		    }
		    send_to_char("CONTAINER KEY SET.\n\r\n\r", ch);
		    pObj->value[2] = atoi(argument);
		    break;
	    }
	    break;

	case ITEM_DRINK_CON:
	    switch (value_num)
	    {
		default:
		    do_help(ch, "ITEM_DRINK");
		    return FALSE;
		case 0:
		    send_to_char("MAXIMUM AMOUT OF LIQUID HOURS SET.\n\r\n\r", ch);
		    pObj->value[0] = atoi(argument);
		    break;
		case 1:
		    send_to_char("CURRENT AMOUNT OF LIQUID HOURS SET.\n\r\n\r", ch);
		    pObj->value[1] = atoi(argument);
		    break;
		case 2:
		    send_to_char("LIQUID TYPE SET.\n\r\n\r", ch);
		    pObj->value[2] = flag_value(liquid_flags, argument);
		    break;
		case 3:
		    send_to_char("POISON VALUE TOGGLED.\n\r\n\r", ch);
		    pObj->value[3] = (pObj->value[3] == 0) ? 1 : 0;
		    break;
	    }
	    break;

	case ITEM_FOOD:
	    switch (value_num)
	    {
		default:
		    do_help(ch, "ITEM_FOOD");
		    return FALSE;
		case 0:
		    send_to_char("HOURS OF FOOD SET.\n\r\n\r", ch);
		    pObj->value[0] = atoi(argument);
		    break;
		case 3:
		    send_to_char("POISON VALUE TOGGLED.\n\r\n\r", ch);
		    pObj->value[3] = (pObj->value[3] == 0) ? 1 : 0;
		    break;
	    }
	    break;

	case ITEM_MONEY:
	    switch (value_num)
	    {
		default:
		    do_help(ch, "ITEM_MONEY");
		    return FALSE;
		case 0:
		    send_to_char("GOLD AMOUNT SET.\n\r\n\r", ch);
		    pObj->value[0] = atoi(argument);
		    break;
	    }
	    break;

	case ITEM_PORTAL:
	    switch (value_num)
	    {
		    int value;

		default:
		    do_help(ch, "ITEM_PORTAL");
		    return FALSE;
		case 0:
		    send_to_char("NUMBER OF CHARGES SET.\n\r\n\r", ch);
		    pObj->value[0] = atoi(argument);
		    break;
		case 1:
		    if ((value = flag_value(portal_door_flags, argument))
			!= NO_FLAG)
			TOGGLE_BIT(pObj->value[1], value);
		    else
		    {
			do_help(ch, "ITEM_PORTAL");
			return FALSE;
		    }
		    send_to_char("PORTAL DOOR TYPE SET.\n\r\n\r", ch);
		    break;
		case 2:
		    if (atoi(argument) != 0)
		    {
			if (!get_obj_index(atoi(argument)))
			{
			    send_to_char("THERE IS NO SUCH ITEM.\n\r\n\r", ch);
			    return FALSE;
			}

			if (get_obj_index(atoi(argument))->item_type !=
			ITEM_KEY)
			{
			    send_to_char("THAT ITEM IS NOT A KEY.\n\r\n\r", ch);
			    return FALSE;
			}
		    }
		    send_to_char("PORTAL KEY SET.\n\r\n\r", ch);
		    pObj->value[2] = atoi(argument);
		    break;
		case 3:
		    if ((value = flag_value(portal_flags, argument))
			!= NO_FLAG)
			TOGGLE_BIT(pObj->value[3], value);
		    else
		    {
			do_help(ch, "ITEM_PORTAL");
			return FALSE;
		    }
		    send_to_char("PORTAL TYPE SET.\n\r\n\r", ch);
		    break;
		case 4:
		    send_to_char("PORTAL DESTINY ROOM VNUM SET.\n\r\n\r", ch);
		    pObj->value[4] = atoi(argument);
		    break;
	    }
	    break;

    }

    show_obj_values(ch, pObj);

    return TRUE;
}


bool oedit_show(CHAR_DATA * ch, char *argument)
{
    OBJ_INDEX_DATA *pObj;
    char buf[MAX_STRING_LENGTH];
    AFFECT_DATA *paf;
    int cnt;

    pObj = edit_obj(ch);

    sprintf(buf, "Name:        [%s]\n\rArea:        [%5d] %s\n\r",
	    pObj->name,
	    !pObj->area ? -1 : pObj->area->vnum,
	    !pObj->area ? "No Area" : pObj->area->name);
    send_to_char(buf, ch);


    sprintf(buf, "Vnum:        [%5d]\n\rType:        [%s]\n\r",
	    pObj->vnum,
	    flag_string(type_flags, pObj->item_type));
    send_to_char(buf, ch);

    sprintf(buf, "Wear flags:  [%s]\n\r",
	    flag_string(wear_flags, pObj->wear_flags));
    send_to_char(buf, ch);

    sprintf(buf, "Extra flags: [%s]\n\r",
	    flag_string(extra_flags, pObj->extra_flags));
    send_to_char(buf, ch);

    sprintf(buf, "Weight:      [%d]\n\rCost:        [%d]\n\r",
	    pObj->weight, pObj->cost);
    send_to_char(buf, ch);

    if (pObj->extra_descr)
    {
	EXTRA_DESCR_DATA *ed;

	send_to_char("Ex desc kwd: ", ch);

	for (ed = pObj->extra_descr; ed; ed = ed->next)
	{
	    send_to_char("[", ch);
	    send_to_char(ed->keyword, ch);
	    send_to_char("]", ch);
	}

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

    sprintf(buf, "Short desc:  %s\n\rLong desc:\n\r     %s\n\r",
	    pObj->short_descr, pObj->description);
    send_to_char(buf, ch);

    for (cnt = 0, paf = pObj->affected; paf; paf = paf->next)
    {
	if (cnt == 0)
	{
	    send_to_char("Number Modifier Affects\n\r", ch);
	    send_to_char("------ -------- -------\n\r", ch);
	}
	sprintf(buf, "[%4d] %-8d %s\n\r", cnt,
		paf->modifier,
		flag_string(apply_flags, paf->location));
	send_to_char(buf, ch);
	cnt++;
    }

    show_obj_values(ch, pObj);

    return FALSE;
}


/*
 * Need to issue warning if flag isn't valid.
 */
bool oedit_addaffect(CHAR_DATA * ch, char *argument)
{
    OBJ_INDEX_DATA *pObj;
    AFFECT_DATA *pAf;
    char loc[MAX_STRING_LENGTH];
    char mod[MAX_STRING_LENGTH];

    pObj = edit_obj(ch);

    argument = one_argument(argument, loc);
    one_argument(argument, mod);

    if (loc[0] == '\0' || mod[0] == '\0' || !is_number(mod))
    {
	send_to_char("Syntax:  addaffect [location] [#mod]\n\r", ch);
	return FALSE;
    }

    pAf = new_affect();
    pAf->location = flag_value(apply_flags, loc);
    pAf->modifier = atoi(mod);
    pAf->type = -1;
    pAf->duration = -1;
    pAf->bitvector = 0;
    pAf->next = pObj->affected;
    pObj->affected = pAf;

    send_to_char("Affect added.\n\r", ch);
    return TRUE;
}


/*
 * My thanks to Hans Hvidsten Birkeland and Noam Krendel(Walker)
 * for really teaching me how to manipulate pointers.
 */
bool oedit_delaffect(CHAR_DATA * ch, char *argument)
{
    OBJ_INDEX_DATA *pObj;
    AFFECT_DATA *pAf;
    AFFECT_DATA *pAf_next;
    char affect[MAX_STRING_LENGTH];
    int value;
    int cnt = 0;

    pObj = edit_obj(ch);

    one_argument(argument, affect);

    if (!is_number(affect) || affect[0] == '\0')
    {
	send_to_char("Syntax:  delaffect [#affect]\n\r", ch);
	return FALSE;
    }

    value = atoi(affect);

    if (value < 0)
    {
	send_to_char("Only non-negative affect-numbers allowed.\n\r", ch);
	return FALSE;
    }

    if (!(pAf = pObj->affected))
    {
	send_to_char("OEdit:  Non-existant affect.\n\r", ch);
	return FALSE;
    }

    if (value == 0)		/*
				 * First case: Remove first affect 
				 */
    {
	pAf = pObj->affected;
	pObj->affected = pAf->next;
	free_affect(pAf);
    }
    else
	/*
	 * Affect to remove is not the first 
	 */
    {
	while ((pAf_next = pAf->next) && (++cnt < value))
	    pAf = pAf_next;

	if (pAf_next)		/*
				 * See if it's the next affect 
				 */
	{
	    pAf->next = pAf_next->next;
	    free_affect(pAf_next);
	}
	else
	    /*
	     * Doesn't exist 
	     */
	{
	    send_to_char("No such affect.\n\r", ch);
	    return FALSE;
	}
    }

    send_to_char("Affect removed.\n\r", ch);
    return TRUE;
}


bool oedit_name(CHAR_DATA * ch, char *argument)
{
    OBJ_INDEX_DATA *pObj;

    pObj = edit_obj(ch);

    if (argument[0] == '\0')
    {
	send_to_char("Syntax:  name [string]\n\r", ch);
	return FALSE;
    }

    free_string(pObj->name);
    pObj->name = str_dup(argument);

    send_to_char("Name set.\n\r", ch);
    return TRUE;
}


bool oedit_short(CHAR_DATA * ch, char *argument)
{
    OBJ_INDEX_DATA *pObj;

    pObj = edit_obj(ch);

    if (argument[0] == '\0')
    {
	send_to_char("Syntax:  short [string]\n\r", ch);
	return FALSE;
    }

    free_string(pObj->short_descr);
    pObj->short_descr = str_dup(argument);
    pObj->short_descr[0] = LOWER(pObj->short_descr[0]);

    send_to_char("Short description set.\n\r", ch);
    return TRUE;
}


bool oedit_long(CHAR_DATA * ch, char *argument)
{
    OBJ_INDEX_DATA *pObj;

    pObj = edit_obj(ch);

    if (argument[0] == '\0')
    {
	send_to_char("Syntax:  long [string]\n\r", ch);
	return FALSE;
    }

    free_string(pObj->description);
    pObj->description = str_dup(argument);
    pObj->description[0] = UPPER(pObj->description[0]);

    send_to_char("Long description set.\n\r", ch);
    return TRUE;
}


bool set_value(CHAR_DATA * ch, OBJ_INDEX_DATA * pObj, char *argument, int value)
{
    if (argument[0] == '\0')
    {
	set_obj_values(ch, pObj, -1, '\0');
	return FALSE;
    }

    if (set_obj_values(ch, pObj, value, argument))
	return TRUE;

    return FALSE;
}


/*
 * Name:	oedit_values
 * Purpose:	Finds the object and sets its value.
 * Called by:	The four valueX functions below.
 */
bool oedit_values(CHAR_DATA * ch, char *argument, int value)
{
    OBJ_INDEX_DATA *pObj;

    pObj = edit_obj(ch);

    if (set_value(ch, pObj, argument, value))
	return TRUE;

    return FALSE;
}


bool oedit_value0(CHAR_DATA * ch, char *argument)
{
    if (oedit_values(ch, argument, 0))
	return TRUE;

    return FALSE;
}


bool oedit_value1(CHAR_DATA * ch, char *argument)
{
    if (oedit_values(ch, argument, 1))
	return TRUE;

    return FALSE;
}


bool oedit_value2(CHAR_DATA * ch, char *argument)
{
    if (oedit_values(ch, argument, 2))
	return TRUE;

    return FALSE;
}


bool oedit_value3(CHAR_DATA * ch, char *argument)
{
    if (oedit_values(ch, argument, 3))
	return TRUE;

    return FALSE;
}


bool oedit_value4(CHAR_DATA * ch, char *argument)
{
    if (oedit_values(ch, argument, 4))
	return TRUE;

    return FALSE;
}


bool oedit_weight(CHAR_DATA * ch, char *argument)
{
    OBJ_INDEX_DATA *pObj;

    pObj = edit_obj(ch);

    if (argument[0] == '\0' || !is_number(argument))
    {
	send_to_char("Syntax:  weight [number]\n\r", ch);
	return FALSE;
    }

    pObj->weight = atoi(argument);

    send_to_char("Weight set.\n\r", ch);
    return TRUE;
}


bool oedit_cost(CHAR_DATA * ch, char *argument)
{
    OBJ_INDEX_DATA *pObj;

    pObj = edit_obj(ch);

    if (argument[0] == '\0' || !is_number(argument))
    {
	send_to_char("Syntax:  cost [number]\n\r", ch);
	return FALSE;
    }

    pObj->cost = atoi(argument);

    send_to_char("Cost set.\n\r", ch);
    return TRUE;
}


bool oedit_create(CHAR_DATA * ch, char *argument)
{
    OBJ_INDEX_DATA *pObj;
    AREA_DATA *pArea;
    int value;
    int iHash;

    value = atoi(argument);

    /*
     * OLC 1.1b 
     */
    if (argument[0] == '\0' || value <= 0 || value >= INT_MAX)
    {
	char output[MAX_STRING_LENGTH];

	sprintf(output, "Syntax:  create [0 < vnum < %d]\n\r", INT_MAX);
	send_to_char(output, ch);
	return FALSE;
    }

    pArea = get_vnum_area(value);
    if (!pArea)
    {
	send_to_char("OEdit:  That vnum is not assigned an area.\n\r", ch);
	return FALSE;
    }

    if (!is_builder(ch, pArea))
    {
	send_to_char("OEdit:  Vnum in an area you cannot build in.\n\r", ch);
	return FALSE;
    }

    if (get_obj_index(value))
    {
	send_to_char("OEdit:  Object vnum already exists.\n\r", ch);
	return FALSE;
    }

    pObj = new_obj_index();
    pObj->vnum = value;
    pObj->area = pArea;

    if (value > top_vnum_obj)
	top_vnum_obj = value;

    iHash = value % MAX_KEY_HASH;
    pObj->next = obj_index_hash[iHash];
    obj_index_hash[iHash] = pObj;
    ch->desc->pEdit = (void *) pObj;

    send_to_char("Object Created.\n\r", ch);
    return TRUE;
}


bool oedit_ed(CHAR_DATA * ch, char *argument)
{
    OBJ_INDEX_DATA *pObj;
    EXTRA_DESCR_DATA *ed;
    char command[MAX_INPUT_LENGTH];
    char keyword[MAX_INPUT_LENGTH];

    pObj = edit_obj(ch);

    argument = one_argument(argument, command);
    one_argument(argument, keyword);

    if (command[0] == '\0')
    {
	send_to_char("Syntax:  ed add [keyword]\n\r", ch);
	send_to_char("         ed delete [keyword]\n\r", ch);
	send_to_char("         ed edit [keyword]\n\r", ch);
	send_to_char("         ed format [keyword]\n\r", ch);
	return FALSE;
    }

    if (!str_cmp(command, "add"))
    {
	if (keyword[0] == '\0')
	{
	    send_to_char("Syntax:  ed add [keyword]\n\r", ch);
	    return FALSE;
	}

	ed = new_extra_descr();
	ed->keyword = str_dup(keyword);
	ed->next = pObj->extra_descr;
	pObj->extra_descr = ed;

	string_append(ch, &ed->description);

	return TRUE;
    }

    if (!str_cmp(command, "edit"))
    {
	if (keyword[0] == '\0')
	{
	    send_to_char("Syntax:  ed edit [keyword]\n\r", ch);
	    return FALSE;
	}

	for (ed = pObj->extra_descr; ed; ed = ed->next)
	{
	    if (is_name(keyword, ed->keyword))
		break;
	}

	if (!ed)
	{
	    send_to_char("OEdit:  Extra description keyword not found.\n\r", ch);
	    return FALSE;
	}

	string_append(ch, &ed->description);

	return TRUE;
    }

    if (!str_cmp(command, "delete"))
    {
	EXTRA_DESCR_DATA *ped = NULL;

	if (keyword[0] == '\0')
	{
	    send_to_char("Syntax:  ed delete [keyword]\n\r", ch);
	    return FALSE;
	}

	for (ed = pObj->extra_descr; ed; ed = ed->next)
	{
	    if (is_name(keyword, ed->keyword))
		break;
	    ped = ed;
	}

	if (!ed)
	{
	    send_to_char("OEdit:  Extra description keyword not found.\n\r", ch);
	    return FALSE;
	}

	if (!ped)
	    pObj->extra_descr = ed->next;
	else
	    ped->next = ed->next;

	free_extra_descr(ed);

	send_to_char("Extra description deleted.\n\r", ch);
	return TRUE;
    }


    if (!str_cmp(command, "format"))
    {
	EXTRA_DESCR_DATA *ped = NULL;

	if (keyword[0] == '\0')
	{
	    send_to_char("Syntax:  ed format [keyword]\n\r", ch);
	    return FALSE;
	}

	for (ed = pObj->extra_descr; ed; ed = ed->next)
	{
	    if (is_name(keyword, ed->keyword))
		break;
	    ped = ed;
	}

	if (!ed)
	{
	    send_to_char("OEdit:  Extra description keyword not found.\n\r", ch);
	    return FALSE;
	}

	/*
	 * OLC 1.1b 
	 */
	if (strlen(ed->description) >= (MAX_STRING_LENGTH - 4))
	{
	    send_to_char("String too long to be formatted.\n\r", ch);
	    return FALSE;
	}

	ed->description = format_string(ed->description);

	send_to_char("Extra description formatted.\n\r", ch);
	return TRUE;
    }

    oedit_ed(ch, "");
    return FALSE;
}


/*
 * Mobile Editor Functions.
 */
bool medit_show(CHAR_DATA * ch, char *argument)
{
    MOB_INDEX_DATA *pMob;
    char buf[MAX_STRING_LENGTH];

    pMob = edit_mob(ch);

    sprintf(buf, "Name:        [%s]\n\rArea:        [%5d] %s\n\r",
	    pMob->player_name,
	    !pMob->area ? -1 : pMob->area->vnum,
	    !pMob->area ? "No Area" : pMob->area->name);
    send_to_char(buf, ch);

    sprintf(buf, "Act:         [%s]\n\r",
	    flag_string(act_flags, pMob->act));
    send_to_char(buf, ch);

    sprintf(buf, "Vnum:        [%5d]\n\rSex:         [%s]\n\r",
	    pMob->vnum,
	    pMob->sex == SEX_MALE ? "male" :
	    pMob->sex == SEX_FEMALE ? "female" : "neutral");
    send_to_char(buf, ch);

    sprintf(buf, "Race:        [%s]\n\r", race_table[pMob->race].name);
    send_to_char(buf, ch);

    sprintf(buf,
	    "Level:       [%2d]\n\rAlign:       [%4d]\n\r",
	    pMob->level, pMob->alignment);
    send_to_char(buf, ch);

    sprintf(buf, "Affected by: [%s]\n\r",
	    flag_string(affect_flags, pMob->affected_by));
    send_to_char(buf, ch);

    if (pMob->spec_fun)
    {
	sprintf(buf, "Spec fun:    [%s]\n\r", spec_string(pMob->spec_fun));
	send_to_char(buf, ch);
    }

    sprintf(buf, "Short descr: %s\n\rLong descr:\n\r%s",
	    pMob->short_descr,
	    pMob->long_descr);
    send_to_char(buf, ch);

    sprintf(buf, "Description:\n\r%s", pMob->description);
    send_to_char(buf, ch);

    if (pMob->pShop)
    {
	SHOP_DATA *pShop;
	int iTrade;

	pShop = pMob->pShop;

	sprintf(buf,
		"Shop data for [%5d]:\n\r"
		"  Markup for purchaser: %d%%\n\r"
		"  Markdown for seller:  %d%%\n\r",
		pShop->keeper, pShop->profit_buy, pShop->profit_sell);
	send_to_char(buf, ch);
	sprintf(buf, "  Hours: %d to %d.\n\r",
		pShop->open_hour, pShop->close_hour);
	send_to_char(buf, ch);

	for (iTrade = 0; iTrade < MAX_TRADE; iTrade++)
	{
	    if (pShop->buy_type[iTrade] != 0)
	    {
		if (iTrade == 0)
		{
		    send_to_char("  Number Trades Type\n\r", ch);
		    send_to_char("  ------ -----------\n\r", ch);
		}
		sprintf(buf, "  [%4d] %s\n\r", iTrade,
			flag_string(type_flags, pShop->buy_type[iTrade]));
		send_to_char(buf, ch);
	    }
	}
    }

    return FALSE;
}


bool medit_create(CHAR_DATA * ch, char *argument)
{
    MOB_INDEX_DATA *pMob;
    AREA_DATA *pArea;
    int value;
    int iHash;

    value = atoi(argument);

    /*
     * OLC 1.1b 
     */
    if (argument[0] == '\0' || value <= 0 || value >= INT_MAX)
    {
	char output[MAX_STRING_LENGTH];

	sprintf(output, "Syntax:  create [0 < vnum < %d]\n\r", INT_MAX);
	send_to_char(output, ch);
	return FALSE;
    }

    pArea = get_vnum_area(value);

    if (!pArea)
    {
	send_to_char("MEdit:  That vnum is not assigned an area.\n\r", ch);
	return FALSE;
    }

    if (!is_builder(ch, pArea))
    {
	send_to_char("MEdit:  Vnum in an area you cannot build in.\n\r", ch);
	return FALSE;
    }

    if (get_mob_index(value))
    {
	send_to_char("MEdit:  Mobile vnum already exists.\n\r", ch);
	return FALSE;
    }

    pMob = new_mob_index();
    pMob->vnum = value;
    pMob->area = pArea;

    if (value > top_vnum_mob)
	top_vnum_mob = value;

    pMob->act = ACT_IS_NPC;
    iHash = value % MAX_KEY_HASH;
    pMob->next = mob_index_hash[iHash];
    mob_index_hash[iHash] = pMob;
    ch->desc->pEdit = (void *) pMob;

    send_to_char("Mobile Created.\n\r", ch);
    return TRUE;
}


bool medit_spec(CHAR_DATA * ch, char *argument)
{
    MOB_INDEX_DATA *pMob;

    pMob = edit_mob(ch);

    if (argument[0] == '\0')
    {
	send_to_char("Syntax:  spec [special function]\n\r", ch);
	return FALSE;
    }


    if (!str_cmp(argument, "none"))
    {
	pMob->spec_fun = NULL;

	send_to_char("Spec removed.\n\r", ch);
	return TRUE;
    }

    if (spec_lookup(argument))
    {
	pMob->spec_fun = spec_lookup(argument);
	send_to_char("Spec set.\n\r", ch);
	return TRUE;
    }

    send_to_char("MEdit: No such special function.\n\r", ch);
    return FALSE;
}


bool medit_align(CHAR_DATA * ch, char *argument)
{
    MOB_INDEX_DATA *pMob;

    pMob = edit_mob(ch);

    if (argument[0] == '\0' || !is_number(argument))
    {
	send_to_char("Syntax:  alignment [number]\n\r", ch);
	return FALSE;
    }

    pMob->alignment = atoi(argument);

    send_to_char("Alignment set.\n\r", ch);
    return TRUE;
}


bool medit_level(CHAR_DATA * ch, char *argument)
{
    MOB_INDEX_DATA *pMob;

    pMob = edit_mob(ch);

    if (argument[0] == '\0' || !is_number(argument))
    {
	send_to_char("Syntax:  level [number]\n\r", ch);
	return FALSE;
    }

    pMob->level = atoi(argument);

    send_to_char("Level set.\n\r", ch);
    return TRUE;
}


bool medit_desc(CHAR_DATA * ch, char *argument)
{
    MOB_INDEX_DATA *pMob;

    pMob = edit_mob(ch);

    if (argument[0] == '\0')
    {
	string_append(ch, &pMob->description);
	return TRUE;
    }

    send_to_char("Syntax:  desc    - line edit\n\r", ch);
    return FALSE;
}


bool medit_long(CHAR_DATA * ch, char *argument)
{
    MOB_INDEX_DATA *pMob;

    pMob = edit_mob(ch);

    if (argument[0] == '\0')
    {
	send_to_char("Syntax:  long [string]\n\r", ch);
	return FALSE;
    }

    free_string(pMob->long_descr);
    strcat(argument, "\n\r");
    pMob->long_descr = str_dup(argument);
    pMob->long_descr[0] = UPPER(pMob->long_descr[0]);

    send_to_char("Long description set.\n\r", ch);
    return TRUE;
}


bool medit_short(CHAR_DATA * ch, char *argument)
{
    MOB_INDEX_DATA *pMob;

    pMob = edit_mob(ch);

    if (argument[0] == '\0')
    {
	send_to_char("Syntax:  short [string]\n\r", ch);
	return FALSE;
    }

    free_string(pMob->short_descr);
    pMob->short_descr = str_dup(argument);

    send_to_char("Short description set.\n\r", ch);
    return TRUE;
}


bool medit_name(CHAR_DATA * ch, char *argument)
{
    MOB_INDEX_DATA *pMob;

    pMob = edit_mob(ch);

    if (argument[0] == '\0')
    {
	send_to_char("Syntax:  name [string]\n\r", ch);
	return FALSE;
    }

    free_string(pMob->player_name);
    pMob->player_name = str_dup(argument);

    send_to_char("Name set.\n\r", ch);
    return TRUE;
}


bool medit_shop(CHAR_DATA * ch, char *argument)
{
    MOB_INDEX_DATA *pMob;
    char command[MAX_INPUT_LENGTH];
    char arg1[MAX_INPUT_LENGTH];

    argument = one_argument(argument, command);
    argument = one_argument(argument, arg1);

    pMob = edit_mob(ch);

    if (command[0] == '\0')
    {
	send_to_char("Syntax:  shop hours [#opening] [#closing]\n\r", ch);
	send_to_char("         shop profit [#buying%] [#selling%]\n\r", ch);
	send_to_char("         shop type [#0-4] [item type]\n\r", ch);
	send_to_char("         shop delete [#0-4]\n\r", ch);
	return FALSE;
    }


    if (!str_cmp(command, "hours"))
    {
	if (arg1[0] == '\0' || !is_number(arg1)
	    || argument[0] == '\0' || !is_number(argument))
	{
	    send_to_char("Syntax:  shop hours [#opening] [#closing]\n\r", ch);
	    return FALSE;
	}

	if (!pMob->pShop)
	{
	    pMob->pShop = new_shop();
	    pMob->pShop->keeper = pMob->vnum;
	    shop_last->next = pMob->pShop;
	}

	pMob->pShop->open_hour = atoi(arg1);
	pMob->pShop->close_hour = atoi(argument);

	send_to_char("Shop hours set.\n\r", ch);
	return TRUE;
    }


    if (!str_cmp(command, "profit"))
    {
	if (arg1[0] == '\0' || !is_number(arg1)
	    || argument[0] == '\0' || !is_number(argument))
	{
	    send_to_char("Syntax:  shop profit [#buying%] [#selling%]\n\r", ch);
	    return FALSE;
	}

	if (!pMob->pShop)
	{
	    pMob->pShop = new_shop();
	    pMob->pShop->keeper = pMob->vnum;
	    shop_last->next = pMob->pShop;
	}

	pMob->pShop->profit_buy = atoi(arg1);
	pMob->pShop->profit_sell = atoi(argument);

	send_to_char("Shop profit set.\n\r", ch);
	return TRUE;
    }


    if (!str_cmp(command, "type"))
    {
	char buf[MAX_INPUT_LENGTH];
	int value;

	if (arg1[0] == '\0' || !is_number(arg1)
	    || argument[0] == '\0')
	{
	    send_to_char("Syntax:  shop type [#0-4] [item type]\n\r", ch);
	    return FALSE;
	}

	if (atoi(arg1) >= MAX_TRADE)
	{
	    sprintf(buf, "REdit:  May sell %d items max.\n\r", MAX_TRADE);
	    send_to_char(buf, ch);
	    return FALSE;
	}

	if ((value = flag_value(type_flags, argument)) == NO_FLAG)
	{
	    send_to_char("REdit:  That type of item is not known.\n\r", ch);
	    return FALSE;
	}

	if (!pMob->pShop)
	{
	    pMob->pShop = new_shop();
	    pMob->pShop->keeper = pMob->vnum;
	    shop_last->next = pMob->pShop;
	}

	pMob->pShop->buy_type[atoi(arg1)] = value;

	send_to_char("Shop type set.\n\r", ch);
	return TRUE;
    }


    if (!str_cmp(command, "delete"))
    {
	SHOP_DATA *pShop;
	SHOP_DATA *pShop_next;
	int value;
	int cnt = 0;

	if (arg1[0] == '\0' || !is_number(arg1))
	{
	    send_to_char("Syntax:  shop delete [#0-4]\n\r", ch);
	    return FALSE;
	}

	value = atoi(argument);

	if (!pMob->pShop)
	{
	    send_to_char("REdit:  Non-existant shop.\n\r", ch);
	    return FALSE;
	}

	if (value == 0)
	{
	    pShop = pMob->pShop;
	    pMob->pShop = pMob->pShop->next;
	    free_shop(pShop);
	}
	else
	    for (pShop = pMob->pShop, cnt = 0; pShop; pShop = pShop_next, cnt++)
	    {
		pShop_next = pShop->next;
		if (cnt + 1 == value)
		{
		    pShop->next = pShop_next->next;
		    free_shop(pShop_next);
		    break;
		}
	    }

	send_to_char("Shop deleted.\n\r", ch);
	return TRUE;
    }

    medit_shop(ch, "");
    return FALSE;
}


/*
 * MobProg Editor Functions.
 */
MPEDIT(mpedit_show)
{
    MOB_INDEX_DATA *pMob;
    MPROG_DATA *pMobProg;
    char buf[MAX_STRING_LENGTH];

    pMob = edit_mob(ch);
    pMobProg = edit_mprog(ch, pMob);

    if (argument[0] == '\0')	/*
				 * Show current mobprog 
				 */
	show_mprog(ch, pMobProg);
    else if (is_number(argument))	/*
					 * show a specific mobprog 
					 */
    {
	MPROG_DATA *mprg;
	int prg = atoi(argument);
	int cnt = mprog_count(pMob);

	if (prg < 1 || prg > cnt)
	{
	    sprintf(buf, "MPEdit:  Valid range is 1 to %d.\n\r", cnt);
	    send_to_char(buf, ch);
	    return FALSE;
	}
	for (mprg = pMob->mobprogs; mprg && prg-- > 1; mprg = mprg->next)
	    ;
	show_mprog(ch, mprg);
    }
    else if (!str_cmp(argument, "all"))
    {
	for (pMobProg = pMob->mobprogs; pMobProg;
	     pMobProg = pMobProg->next)
	    show_mprog(ch, pMobProg);
	send_to_char("|\n\r", ch);
    }
    else
	send_to_char("Syntax: show [all]\n\r", ch);

    return FALSE;
}


MPEDIT(mpedit_create)
{
    MOB_INDEX_DATA *pMob;
    AREA_DATA *pArea;
    MPROG_DATA *mprg;
    int value;

    value = atoi(argument);
    if (argument[0] == '\0' || value == 0)
    {
	send_to_char("Syntax:  mpedit create vnum [svnum]\n\r", ch);
	return FALSE;
    }

    pArea = get_vnum_area(value);

    if (!pArea)
    {
	send_to_char("MPEdit:  That vnum is not assigned an area.\n\r", ch);
	return FALSE;
    }

    if (!is_builder(ch, pArea))
    {
	send_to_char("MPEdit:  Vnum in an area you cannot build in.\n\r", ch);
	return FALSE;
    }

    if (!(pMob = get_mob_index(value)))
    {
	send_to_char("MPEdit:  Mobile vnum does not exist!\n\r", ch);
	return FALSE;
    }

    if (pMob->mobprogs)
    {
	send_to_char("MPEdit:  Mobile already has mob programs!\n\r", ch);
	return FALSE;
    }

    pMob->mobprogs = (MPROG_DATA *) alloc_perm(sizeof(MPROG_DATA));
    mprg = pMob->mobprogs;

    /*
     * empty mob-program 
     */
    mprg->type = 0;
    mprg->comlist = str_dup("break\n");
    mprg->arglist = str_dup("0");

    ch->desc->pEdit = (void *) pMob;
    ch->pcdata->mprog_edit = 0;

    send_to_char("MOBProg Created.\n\r", ch);
    return TRUE;
}


MPEDIT(mpedit_add)
{
    MOB_INDEX_DATA *pMob;
    MPROG_DATA *mprg, *mprg2;
    char buf[MAX_INPUT_LENGTH];
    int count;

    pMob = edit_mob(ch);

    if (!pMob->mobprogs)
    {
	send_to_char("Mobile doesn't have mobprogs.  Use create.\n\r", ch);
	return FALSE;
    }

    count = mprog_count(pMob);

    /*
     * find last mobprog and add after it 
     */
    for (mprg = mprg2 = pMob->mobprogs; mprg; mprg2 = mprg, mprg = mprg->next)
	;

    mprg2->next = (MPROG_DATA *) alloc_perm(sizeof(MPROG_DATA));
    mprg = mprg2->next;
    mprg->type = 0;
    mprg->arglist = str_dup("0");
    mprg->comlist = str_dup("break\n");
    count++;

    ch->pcdata->mprog_edit = count - 1;

    sprintf(buf, "MOBProg %d Added.\n\r", count);
    send_to_char(buf, ch);
    return TRUE;
}


MPEDIT(mpedit_delete)
{
    MOB_INDEX_DATA *pMob;
    char arg[MAX_INPUT_LENGTH];
    int count, pnum;

    pMob = edit_mob(ch);
    count = mprog_count(pMob);

    argument = one_argument(argument, arg);

    if (arg[0] == '\0')		/*
				 * No argument - delete current program 
				 */
    {
	delete_mprog(ch, ch->pcdata->mprog_edit);
    }
    else if (is_number(arg))
    {
	if ((pnum = atoi(arg)) > count)
	{
	    send_to_char("Mobile does not have that many programs.\n\r", ch);
	    return FALSE;
	}
	delete_mprog(ch, pnum - 1);
    }
    else if (!str_cmp(arg, "all"))
    {
	for (pnum = count - 1; pnum >= 0; pnum--)
	    delete_mprog(ch, pnum);
    }
    else
    {
	send_to_char("Syntax:  delete [#pnum/all]\n\r", ch);
	return FALSE;
    }

    count = mprog_count(pMob);	/*
				 * Get new count 
				 */
    if (ch->pcdata->mprog_edit >= count)
    {
	ch->pcdata->mprog_edit = count - 1;
	if (count == 0)
	    edit_done(ch, "");
    }
    send_to_char("Ok.\n\r", ch);
    return TRUE;
}


MPEDIT(mpedit_copy)
{
    MOB_INDEX_DATA *pMob, *cMob;
    MPROG_DATA *mprg, *mprg_next, *cprg;
    int value;

    pMob = edit_mob(ch);

    if (argument[0] == '\0' || !is_number(argument))
    {
	send_to_char("Syntax:  copy [vnum]\n\r", ch);
	return FALSE;
    }

    value = atoi(argument);
    if (!(cMob = get_mob_index(value)))
    {
	send_to_char("No such mobile exists.\n\r", ch);
	return FALSE;
    }

    if (cMob == pMob)
    {
	send_to_char("You can't copy from yourself, sorry.\n\r", ch);
	return FALSE;
    }

    if (!cMob->mobprogs)
    {
	send_to_char("That mobile doesn't have mobprogs!\n\r", ch);
	return FALSE;
    }

    /*
     * free existing mobprog list 
     */
    for (mprg = pMob->mobprogs; mprg; mprg = mprg_next)
    {
	mprg_next = mprg->next;
	free_string(mprg->arglist);
	free_string(mprg->comlist);
	free_mem(mprg, sizeof(MPROG_DATA));
    }

    mprg = pMob->mobprogs = (MPROG_DATA *) alloc_perm(sizeof(MPROG_DATA));

    /*
     * Start copying 
     */
    for (cprg = cMob->mobprogs; cprg;
	 cprg = cprg->next, mprg = mprg->next)
    {
	mprg->type = cprg->type;
	SET_BIT(pMob->progtypes, cprg->type);
	mprg->arglist = str_dup(cprg->arglist);
	mprg->comlist = str_dup(cprg->comlist);
	if (cprg->next)
	    mprg->next = (MPROG_DATA *) alloc_perm(sizeof(MPROG_DATA));
	else
	    mprg->next = NULL;
    }

    ch->pcdata->mprog_edit = mprog_count(pMob) - 1;

    send_to_char("MOBProg copied.\n\r", ch);
    return TRUE;
}


MPEDIT(mpedit_trigger)
{
    MOB_INDEX_DATA *pMob;
    MPROG_DATA *pMobProg;

    if (argument[0] == '\0')
    {
	send_to_char("Syntax:  trigger [trigger value(s)]\n\r", ch);
	return FALSE;
    }

    pMob = edit_mob(ch);
    pMobProg = edit_mprog(ch, pMob);

    free_string(pMobProg->arglist);
    pMobProg->arglist = str_dup(argument);
    return TRUE;
}


MPEDIT(mpedit_program)
{
    MOB_INDEX_DATA *pMob;
    MPROG_DATA *pMobProg;

    pMob = edit_mob(ch);
    pMobProg = edit_mprog(ch, pMob);

    if (argument[0] == '\0')
    {
	string_append(ch, &pMobProg->comlist);
	return TRUE;
    }

    send_to_char("Syntax:  program\n\r", ch);
    return FALSE;
}


int mprog_count(MOB_INDEX_DATA * pMob)
{
    MPROG_DATA *mprg;
    int count;

    for (count = 0, mprg = pMob->mobprogs;
	 mprg;
	 mprg = mprg->next, count++)
	;
    return count;
}


MPROG_DATA *edit_mprog(CHAR_DATA * ch, MOB_INDEX_DATA * pMob)
{
    MPROG_DATA *mprg;
    int mprog_num;
    int count = 0;

    if (IS_NPC(ch))
	return NULL;

    mprog_num = ch->pcdata->mprog_edit;
    for (mprg = pMob->mobprogs; mprg && count < mprog_num; mprg = mprg->next)
	count++;

    return mprg;
}


void show_mprog(CHAR_DATA * ch, MPROG_DATA * pMobProg)
{
    char buf[MAX_STRING_LENGTH];

    sprintf(buf, ">%s %s~\n\r",
	    mprog_type_to_name(pMobProg->type),
	    pMobProg->arglist ? pMobProg->arglist : "NULL");
    send_to_char(buf, ch);

    sprintf(buf, "%s~\n\r", pMobProg->comlist
	    ? pMobProg->comlist
	    : "NULL\n\r");
    send_to_char(buf, ch);
}


void delete_mprog(CHAR_DATA * ch, int pnum)
{
    MPROG_DATA *mprg, *mprg_prev;
    MOB_INDEX_DATA *pMob;
    char buf[MAX_INPUT_LENGTH];
    int count;

    pMob = edit_mob(ch);

    if (pnum < 0)		/*
				 * sanity check 
				 */
	return;

    if (pnum == 0)
    {
	free_string(pMob->mobprogs->arglist);
	free_string(pMob->mobprogs->comlist);
	mprg = pMob->mobprogs->next;
	/*
	 * Here is where we would recycle the memory of pMob->mobprogs...
	 * no such mechanism yet so this actually IS a sort of memory leak
	 * since memory allocated with alloc_perm cannot be freed.  Walker 
	 */
	pMob->mobprogs = mprg;
    }
    else
    {
	mprg_prev = pMob->mobprogs;
	mprg = mprg_prev->next;
	for (count = 1; mprg && count < pnum; count++)
	{
	    mprg_prev = mprg;
	    mprg = mprg->next;
	}
	if (mprg)
	{
	    free_string(mprg->arglist);
	    free_string(mprg->comlist);
	    mprg_prev->next = mprg->next;
	    /*
	     * Here is where we would recycle the memory of mprg...
	     * no such mechanism yet so this actually IS a sort of memory leak
	     * since memory allocated with alloc_perm cannot be freed.  Walker
	     */
	}
    }

    sprintf(buf, "MOBProg %d Deleted.\n\r", pnum + 1);
    send_to_char(buf, ch);
    return;
}