/
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

olc.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: RoAOLC base source file can be found in roaolc.c
    note: zone OLC can be found in z_olc.c
    note: room OLC can be found in r_olc.c
    note: mobile OLC can be found in m_olc.c
    note: object OLC can be found in o_olc.c
**********************************************************/
#include "conf.h"
#include "sysdep.h"

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

/* for chars */
extern char	*material_types[];
extern int	material_hits[];
extern char	*affected_bits[];
extern char	*affected2_bits[];
extern char	*apply_types[];
extern char	*item_types[];
extern char	*wear_bits[];
extern char	*wv_bits[];
extern char	*extra_bits[];
extern char	*extra_bits2[];
extern char	*container_bits[];
extern char	*drinks[];

char *weapon_strs[] =
{
    "hits, hit",
    "pounds, pound",
    "pierces, pierce",
    "slashes, slash",
    "blasts, blast",
    "whips, whip",
    "pierce (non BS)",
    "claws, claw",
    "bites, bite",
    "stings, sting",
    "crushes, crush",
    "BUILDER DEFINED",
    "\n",
};

char *value_strs[][4] =
{
    { "*", "*", "*", "*" },				// UNDEFINED
    { "*", "*", "Duration", "*" },			// LIGHT
    { "Spell level", "Spell 1", "Spell 2", "Spell 3" },	// SCROLL
    { "Spell level", "Max charges", "Charges left", "Spell" },	// WAND
    { "Spell level", "Max charges", "Charges left", "Spell" },	// STAFF
    { "*", "# of damage dice", "Size of damage dice", "Weapon type" },	        // WEAPON
    { "*", "*", "*", "*" },				// RANGED
    { "*", "*", "*", "*" },				// OINTMENT
    { "*", "*", "*", "*" },				// TREASURE
    { "Armor class", "*", "*", "*" },			// type ARMOR
    { "Spell level", "Spell 1", "Spell 2", "Spell 3" },	// type POTION
    { "*", "*", "*", "*" },				// type +BOARD	
    { "*", "*", "*", "*" },				// OTHER
    { "*", "*", "*", "*" },				// TRASH
    { "*", "*", "*", "*" },				// TRAP
    { "Max weight of contents", "Flags", "Key type", "*" }, // CONTAINER	
    { "*", "*", "*", "*" },				// NOTE
    { "Max # of drinks", "# of drinks left", "Liquid type", "Poisoned? / Pure Poison level" },      // LIQUID CONT
    { "Key type", "*", "*", "*" },			// KEY
    { "Food hours", "*", "*", "Poisoned?" },		// FOOD
    { "# of coins", "*", "*", "*" },			// MONEY
    { "*", "*", "*", "*" },				// PEN
    { "*", "*", "*", "*" },				// BOAT
    { "Max # of drinks", "# of drinks left", "Liquid type", "Poisoned?" },      // FOUNTAIN
    { "*", "Max charges", "Charges left", "Teleport to(vnum)" },  // PORTAL
    { "Gate is an", "INTERNAL", "OBJECT TYPE", "Do Not Use" },	  // GATEWAY
    { "Max charges", "# of charges left", "Num Mana Dice", "Size Mana Dice" },  // HARMONIC GEM
    { "Skill 1", "Skill 2", "Skill 3", "Skill 4" },     // TOME OF LEARNING
};

#define VTYPE_INT	1
#define VTYPE_SPELL	2
#define VTYPE_WEAPON	3
#define VTYPE_CONFLAGS	4
#define VTYPE_LIQUID	5

int value_types[][4] =
{
    { 0, 0, 0, 0 },	
    { 0, 0, VTYPE_INT, 0 },
    { VTYPE_INT, VTYPE_SPELL, VTYPE_SPELL, VTYPE_SPELL },	
    { VTYPE_INT, VTYPE_INT, VTYPE_INT, VTYPE_SPELL },
    { VTYPE_INT, VTYPE_INT, VTYPE_INT, VTYPE_SPELL },
    { 0, VTYPE_INT, VTYPE_INT, VTYPE_WEAPON },	
    { 0, 0, 0, 0 },	
    { 0, 0, 0, 0 },	
    { 0, 0, 0, 0 },	
    { VTYPE_INT, 0, 0, 0 },	
    { VTYPE_INT, VTYPE_SPELL, VTYPE_SPELL, VTYPE_SPELL },	
    { 0, 0, 0, 0 },	
    { 0, 0, 0, 0 },	
    { 0, 0, 0, 0 },	
    { 0, 0, 0, 0 },	
    { VTYPE_INT, VTYPE_CONFLAGS, VTYPE_INT, 0 },
    { 0, 0, 0, 0 },	
    { VTYPE_INT, VTYPE_INT, VTYPE_LIQUID, VTYPE_INT },
    { VTYPE_INT, 0, 0, 0 },	
    { VTYPE_INT, 0, 0, VTYPE_INT },	
    { VTYPE_INT, 0, 0, 0 },	
    { 0, 0, 0, 0 },	
    { 0, 0, 0, 0 },	
    { VTYPE_INT, VTYPE_INT, VTYPE_LIQUID, VTYPE_INT },
    { 0, VTYPE_INT, VTYPE_INT, VTYPE_INT},
    { 0, 0, 0, 0 },	
    { VTYPE_INT, VTYPE_INT, VTYPE_INT, VTYPE_INT},  // HARMONIC GEM
    { VTYPE_SPELL, VTYPE_SPELL, VTYPE_SPELL, VTYPE_SPELL},  // TOME OF LEARNING
};

// internal functions to this file
void oedit_top_menu(chdata *ch, char *input_str);
void oedit_confirm_quit(chdata *ch, char *input_str);
void oedit_confirm_save(chdata *ch, char *input_str);

// external functions
extern exdescdata *correct_extra_descrips(exdescdata *ths);
extern exdescdata *free_extra_descrips(exdescdata *head);
extern exdescdata *dupe_exdesc_over(exdescdata *ths);

// OBLITERATE an object, NOTE this will not remove the object from
// the proto list should it be a part of it...
// its primary use is to wax those OBJ_EDITTED ptrs on char
void wax_object(obdata *trg)
{
  FREENULL(trg->name);
  FREENULL(trg->description);
  FREENULL(trg->shdesc);
  FREENULL(trg->actdesc);
  FREENULL(trg->wear_mesg);
  FREENULL(trg->rem_mesg);
  FREENULL(trg->weap_sing);
  FREENULL(trg->weap_plur);
  trg->exdesc = free_extra_descrips(trg->exdesc);
}

// dupe EVERYTHING over, first the strings from trg get
// waxed, and extra descrip list
// then it gets a copy of all src's things
void dupe_object_over(obdata *src, obdata *trg)
{
  chdata *carried_by;
  obdata *in_obj, *contains, *next_content, *next;
  int timer;
  BOOL viewed, lit, touched, original;

  // save data in use before we transfer
  carried_by = trg->carried_by;
  in_obj = trg->in_obj;
  contains = trg->contains;
  next_content = trg->next_content;
  next = trg->next;
  timer = trg->timer;
  viewed = trg->viewed;
  lit = trg->lit;
  touched = trg->touched;
  original = trg->original;

  wax_object(trg);

  *trg = *src;
  trg->name = STR_DUP(src->name);
  trg->shdesc = STR_DUP(src->shdesc);
  trg->description = STR_DUP(src->description);
  trg->actdesc = STR_DUP(src->actdesc);
  trg->wear_mesg = STR_DUP(src->wear_mesg);
  trg->rem_mesg = STR_DUP(src->rem_mesg);
  trg->weap_sing = STR_DUP(src->weap_sing);
  trg->weap_plur = STR_DUP(src->weap_plur);
  trg->exdesc = dupe_exdesc_over(src->exdesc); 

  // transfer saved data back over
  trg->carried_by = carried_by;
  trg->in_obj = in_obj;
  trg->contains = contains;
  trg->next_content = next_content;
  trg->next = next;
  trg->timer = timer;
  trg->viewed = viewed;
  trg->lit = lit;
  trg->touched = touched;
  trg->original = original;
}

void default_object_out(obdata *obj, int vnum)
{
  obj->item_number = vnum;
  obj->name        = STR_DUP("BLANK");
  obj->shdesc      = STR_DUP("BLANK");
  obj->description = STR_DUP("BLANK");
  obj->actdesc     = STR_DUP("BLANK");
  obj->wear_mesg   = STR_DUP("BLANK");
  obj->rem_mesg    = STR_DUP("BLANK");
  obj->weap_sing   = STR_DUP("BLANK");
  obj->weap_plur   = STR_DUP("BLANK");
  obj->exdesc      = NULL;
  obj->objsave_self   = -1;
  obj->objsave_parent = -1;
  obj->objsave_where  = -1;
  obj->tmp1 = -1;
  obj->tmp2 = -1;
  OBJ_HITS(obj) = 10000;
  MAX_OBJ_HITS(obj) = 10000;
  obj->in_room = NOWHERE;
  obj->total_game_limit = -1;
  obj->percent_load = 10000;
  obj->alternate_load = 0;
  obj->min_level = 1;
  obj->made_of = OBJ_UNDEFINED;
  obj->touched = FALSE;
  obj->lit = FALSE;
  obj->original = FALSE;
  obj->eqspell = -1;
  obj->eqaffbit = 0;
}

// player interface to oedit
ACMD(do_oedit)
{
    int vnum, rnum, zone;
    char *argu = argument;

    if (IS_NPC(ch))
	return;
    
    skip_spaces(&argu);
    if (!*argu || !is_number(argu))
    {
      send_to_char("Usage: oedit <object vnum>.\n\r",ch);
      return;
    }

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

    /* check permissions on zone and objects -roa */
    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 object.\n\r", ch);
	return;
    }

    /* if the object exists, copy from the existing proto */
    /* NOTE: we store the vnum of this object in obj->item_number */
    /* for later use -roa */
    if ((rnum = real_object(vnum)) >= 0)
    {
      CREATE(OBJ_EDITTED(ch), obdata, 1);
      dupe_object_over(&obj_proto[rnum], OBJ_EDITTED(ch)); 
      OBJ_EDITTED(ch)->item_number = vnum; 
    }
    else  /* make a whole new one, initializing the strings to non NULL */
    {
      if (!OBJ_SLOTS_LEFT)
      {
	send_to_char("Max object 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(OBJ_EDITTED(ch), obdata, 1);
      default_object_out(OBJ_EDITTED(ch), vnum);
    }

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

/* sends a list of spells that have function pointers -roa */
/* note: i have to make this screen pause if builder has refresh on */
void send_spell_listing(chdata *ch)
{
  int i, count;

  for (i=1, count=0, *buf = '\0'; i < MAX_SPELLS; i++)
    if (*skill_names[i] && spell_info[i].spell_pointer)
    {
      count++;
      sprintf(buf+strlen(buf), "(%2d.) %%6%-17.17s%%0%s", i, skill_names[i], ((count % 3) ? "" : "\n\r"));
    }

  page_string(ch->desc, buf, TRUE);
}

void oedit_get_value(chdata *ch, int i)
{
    char *p;
    obdata *o = OBJ_EDITTED(ch);

    p = MENU_PROMPT_BUF(ch);
    sprintf(p, "Enter %s: ", value_strs[(int)ITEM_TYPE(o)][i]);
    switch(value_types[(int)ITEM_TYPE(o)][i])
    {
      case VTYPE_INT:
	GET_INTEGER_ARG(ch, p, o->value[i], -30000, 30000);
	break;
      case VTYPE_SPELL:
        send_spell_listing(ch);
	GET_INTEGER_ARG(ch, "Enter Spell/Skill number: ", o->value[i], -1, MAX_SKILLS-1);
	break;
      case VTYPE_WEAPON:
	get_integer_list(ch, p, &o->value[i], sizeof(o->value[i]), weapon_strs);
	break;
      case VTYPE_CONFLAGS:
	toggle_menu(ch, "Enter container bit to toggle: ", &o->value[i], container_bits);
	break;
      case VTYPE_LIQUID:
	get_integer_list(ch, p, &o->value[i], sizeof(o->value[i]), drinks);
	break;
      default:
	break;
    }
}

ROA_MENU(oedit_add_extradesc_menu)
{
  obdata *o = OBJ_EDITTED(ch);
  exdescdata *e = NULL;

  switch ((int) MENU_HANDLER_ARG(ch))
  {
      case 0:
	CREATE(e, exdescdata, 1);
	e->next = o->exdesc;
	o->exdesc = e;
	e = NULL;

	MENU_HANDLER_ARG(ch) = (void *) 1;
	do_string_arg(ch, "Enter keywords: ", &o->exdesc->keyword,"");
	break;
	
      case 1:
	MENU_HANDLER_ARG(ch) = (void *) 2;
	do_long_string_arg(ch, "Enter new description (@):\n\r",
			   &o->exdesc->description);
	break;

      default:
	menu_pop(ch);
	(*MENU_HANDLER(ch))(ch, NULL);
	break;
  }
}

void delete_obj_extra(chdata *ch)
{
    obdata *o = OBJ_EDITTED(ch);
    exdescdata *e, *temp;
    int i;

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

    if (e)
    {
        REMOVE_FROM_LIST(e, o->exdesc, next);
	if(e->keyword)
	  free_log(e->keyword, "delete_obj_extra");
	if(e->description)
	  free_log(e->description, "delete_obj_extra");
	free_log(e, "delete_obj_extra");
	e = NULL;
    }

    MENU_RETURN(ch) = 0;
}

ROA_MENU(oedit_extradesc_menu)
{
    char buf[MAX_STRING_LENGTH];
    obdata *o = OBJ_EDITTED(ch);
    exdescdata *e = NULL;
    char *p;
    int i;

    if (!input_str)
    {
	/* if we have one to delete from earlier */
	if ((int) MENU_RETURN(ch) > 0)
	  delete_obj_extra(ch);
	
        menu_title_send("Extra Descrip Main Menu", ch);

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

ROA_MENU(oedit_affects_menu)
{
    obdata *o = OBJ_EDITTED(ch);
    int aff_n;
    int aff_field;
    int *i = (int *) &MENU_HANDLER_ARG(ch);

    aff_n     = *i / 2;
    aff_field = *i & 1;
    (*i)++;

    if (!aff_field)
    {
	if (aff_n == MAX_OBJ_AFFECT)
	{
	  menu_back(ch);
	  return;
	}
	
	get_integer_list(ch, "Enter affect application: ", 
			 &o->affected[aff_n].location,
			 sizeof(o->affected[aff_n].location),
			 apply_types);
    }
    else
    {
	if (!o->affected[aff_n].location)
	{
	  for(; aff_n < MAX_OBJ_AFFECT; aff_n++)
	      o->affected[aff_n].location = APPLY_NONE;
	  menu_back(ch);
	  return;
	}
	else
	{
	  sprinttype(o->affected[aff_n].location, apply_types, buf2);
	  sprintf(MENU_PROMPT_BUF(ch), "Enter modification value for %s: ", buf2);
	  GET_INTEGER_ARG(ch, MENU_PROMPT_BUF(ch), 
			  o->affected[aff_n].modifier, -1000, 1000);
	}
    }
}

// add a menu for throw objects dice
ROA_MENU(oedit_throwdice_menu)
{
    char buf[MAX_STRING_LENGTH];
    obdata *o = OBJ_EDITTED(ch);
    char *p;
    int field;

    if (!input_str)
    {
      menu_title_send("ObjectEdit ThrowDice Menu", ch);
      sprintf(buf, "1.) %%6To Hit modifier%%0   : %d\n\r",o->throw_plushit);
      S2C();
      sprintf(buf, "2.) %%6To Damage NumDice%%0 : %d\n\r",o->throw_numdam);
      S2C();
      sprintf(buf, "3.) %%6To Damage SizeDice%%0: %d\n\r",o->throw_sizedam);
      S2C();
      sprintf(buf, "4.) %%6To Damage Bonus%%0   : %d\n\r",o->throw_plusdam);
      S2C();

      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);
	break;

      case 1:
	GET_INTEGER_ARG(ch, "Enter Throw Hit modifier: ", o->throw_plushit, -20, 20);
	break;

      case 2:
	GET_INTEGER_ARG(ch, "Enter Num Damage Dice: ", o->throw_numdam, 1, 10);
	break;

      case 3:
	GET_INTEGER_ARG(ch, "Enter Size Damage Dice: ", o->throw_sizedam, 1, 10);
	break;

      case 4:
	GET_INTEGER_ARG(ch, "Enter Damage Bonus: ", o->throw_plusdam, -10, 20);
	break;

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

/* add extra menu for strings RoA jtrhone*/
ROA_MENU(oedit_string_menu)
{
  char buf[MAX_STRING_LENGTH];
  char *p;
  int field;
  obdata *o = OBJ_EDITTED(ch);
  extern char *delete_doubledollar(char *str);

  if (!input_str)
  {
      menu_title_send("ObjectEdit String Menu", ch);

      sprintf(buf, "1.) %%6Long description%%0:\n\r%s\n\r",o->actdesc); S2C();

      if (o->wear_mesg)
	o->wear_mesg = delete_doubledollar(o->wear_mesg);
      sprintf(buf, "2.) %%6Wear Message%%0    :\n\r%s\n\r",o->wear_mesg); S2C();

      if (o->rem_mesg)
	o->rem_mesg = delete_doubledollar(o->rem_mesg);
      sprintf(buf, "3.) %%6Removal Message%%0 :\n\r%s\n\r",o->rem_mesg); S2C();

      if (IS_WEAPON(o) && o->value[3] == TYPE_BDEFINED - TYPE_HIT)
      {
        sprintf(buf, "4.) %%6Singular weapon noun%%0: %s\n\r",o->weap_sing); 
	S2C();
        sprintf(buf, "5.) %%6Plural weapon verb%%0  : %s\n\r",o->weap_plur); 
	S2C();
      }

      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:
	do_long_string_arg(ch, "Enter long description (@):\n\r", 
			   &o->actdesc);
	break; 

      case 2:
	do_long_string_arg(ch, "Enter object wear message (@):\n\r", 
			   &o->wear_mesg);
	break; 

      case 3:
	do_long_string_arg(ch, "Enter object removal message (@):\n\r", 
			   &o->rem_mesg);
	break; 

      case 4:
	if (!IS_WEAPON(o) || o->value[3] != TYPE_BDEFINED - TYPE_HIT)
	  break;
	do_string_arg(ch, "Enter SINGULAR weapon noun: ", &o->weap_sing, "");
	break;

      case 5:
	if (!IS_WEAPON(o) || o->value[3] != TYPE_BDEFINED - TYPE_HIT)
	  break;
	do_string_arg(ch, "Enter PLURAL weapon verb: ", &o->weap_plur, "");
	break;

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

/* ROA OLC object edit main menu */
ROA_MENU(oedit_top_menu)
{
  char buf[MAX_STRING_LENGTH];
  char buf2[MAX_STRING_LENGTH];
  char *p;
  int field;
  int i;
  obdata *o = OBJ_EDITTED(ch);
  int maxhits;
    
    if (!input_str)
    {
        menu_title_send("ObjectEdit Main Menu", ch);
	sprintf(buf, " 1.) %%6Nr%%0       : %%1%%B%d%%0    ", o->item_number); S2C();
	sprintf(buf, " 2.) %%6NameList%%0 : %s\n\r", o->name); S2C();
	sprintf(buf, " 3.) %%6ShortDesc%%0: %s\n\r", o->shdesc); S2C();
	sprintf(buf, " 4.) %%6Descrip %%0 : %s\n\r", o->description); S2C();
	sprintf(buf, " 5.) %%6String Menu%%0...\n\r"); S2C();

	sprinttype(ITEM_TYPE(o), item_types, buf2);
	sprintf(buf, " 6.) %%6TypeFlag%%0 : %%5%s%%0\n\r", buf2); S2C();

	sprintbit(OBJ_WEARS(o), wear_bits, buf2);
	sprintf(buf, " 7.) %%6WearFlag%%0 : %%5%s%%0\n\r", buf2); S2C();

	sprintbit(OBJ_EXTRAS(o), extra_bits, buf2);
	sprintf(buf, " 8.) %%6XtraFlag%%0 : %%5%s%%0\n\r", buf2); S2C();

	sprintbit(OBJ_EXTRAS2(o), extra_bits2, buf2);
	sprintf(buf, " 9.) %%6XtraFlags2%%0: %%5%s%%0\n\r", buf2); S2C();

	for (i = 0; i < 4; i++)
	  if (*value_strs[(int)ITEM_TYPE(o)][i] == '*')
	    o->value[i] = 0;
	  else
	  if (*value_strs[(int)ITEM_TYPE(o)][i] != '*')
	  {
	    switch(value_types[(int)ITEM_TYPE(o)][i])
	    {
	      case VTYPE_INT:
		sprintf(buf2, "%d", o->value[i]);
		break;
	      case VTYPE_SPELL:
		if (o->value[i] > 0 && o->value[i] < MAX_SKILLS)
		    sprintf(buf2, "%s", skill_names[o->value[i]]);
		else
		    *buf2 = '\0';
		break;
	      case VTYPE_WEAPON:
		sprintf(buf2, "%s", weapon_strs[o->value[i]]);
		break;
	      case VTYPE_CONFLAGS:
		sprintbit(o->value[i], container_bits, buf2);
		break;
	      case VTYPE_LIQUID:
		sprinttype(o->value[i], drinks, buf2);
		break;
	      default:
		*buf2 = '\0'; 
		break;
	    }
	    sprintf(buf, "%2d.) %%6%s%%0: %s\n\r", i + 10, 
		    value_strs[(int)ITEM_TYPE(o)][i], buf2); S2C();
	  } /* end for/if used */

	sprintf(buf, "14.) %%6Weight%%0: %d,   15.) %%6Value%%0:  %d\n", 
		o->weight, o->cost); 
	S2C();
	
	sprintf(buf, "16.) %%6Extra Descriptions%%0: %s\n\r",
		o->exdesc?"Exist":"None");
	S2C();

	send_to_char("17.) %6Affections%0: ", ch);
	for (i = 0; i < MAX_OBJ_AFFECT; i++)
	  if (o->affected[i].location)
	  {
	     sprinttype(o->affected[i].location, apply_types, buf2);
	     sprintf(buf, "%%5%+d to %s%%0, ", o->affected[i].modifier, buf2);
	     S2C();
	  }

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

	sprintf(buf, "18.) %%6%%BLoad Prob%%0 (chance in 10000): %d    ", 
		o->percent_load); 
	S2C();
	sprintf(buf, "19.) %%6Alt Obj Load%%0: %d\n\r", o->alternate_load);
	S2C();

	sprintf(buf, "21.) %%6%%BGame limit%%0: %d   ", o->total_game_limit);
	S2C();

	sprintf(buf, "22.) %%6Min level%%0: %d\n\r", o->min_level);
	S2C();

	/*  start with spell eq stuff here JTRHONE */
        if (OBJ_FLAGGED(o, ITEM_SPELLEQ))
        {
          sprintf(buf, "23.) %%6SpellEQ SPELL%%0: %s\n\r", (o->eqspell > 0) ? skill_names[o->eqspell] : "");
          S2C();

  	  sprintbit(o->eqaffbit, affected_bits, buf2);
	  sprintf(buf, "24.) %%6SpellEQ Aff%%0: %%5%s%%0\n\r", buf2); S2C();    
  	  sprintbit(o->eqaff2bit, affected2_bits, buf2);
	  sprintf(buf, "25.) %%6SpellEQ Aff2%%0: %%5%s%%0\n\r", buf2); S2C();
        }
	else
	{
	  o->eqaffbit = 0;
	  o->eqaff2bit = 0;
	  o->eqspell = 0;
	}

        if (!OBJ_FLAGGED(o, ITEM_MAGIC))
	{
	  MAX_OBJ_HITS(o) = material_hits[MADE_OF(o)];
	  OBJ_HITS(o) = MIN(MAX_OBJ_HITS(o), OBJ_HITS(o));
	}

	sprintf(buf, "26.) %%6Obj Hits%%0: %%5%d%%0    ",OBJ_HITS(o)); 
        S2C();

	sprintf(buf, "27.) %%6MAX Obj Hits%%0: %%5%d%%0\n\r",MAX_OBJ_HITS(o)); 
        S2C();

	sprinttype(MADE_OF(o), material_types, buf2);
	sprintf(buf, "28.) %%6Material%%0: %%5%s%%0\n\r", buf2); S2C();

	sprintf(buf, "29.) %%6Success Rate%%0: %%5%d%%0\n\r", SUCCESS_RATE(o));
	S2C();

	if (THROWABLE(o))
	{
	  sprintf(buf, "30.) %%6Throw stats menu...%%0\n\r"); S2C();
	}

	sprintbit(WV_FLAGS(o), wv_bits, buf2);
	sprintf(buf, "31.) %%6Wear Vector%%0 : %%5%s%%0\n\r", buf2); 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, oedit_confirm_quit);
	break;

      case 1:	// DUPE the object into another proto
	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 object virtual number: ",
			    &o->item_number, sizeof(o->item_number), 0, 89999);
	}
	else
	{
	    get_integer_arg(ch, "Enter object virtual number: ", 
			    &o->item_number, sizeof(o->item_number), 
			    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", &o->name, "");
	break;
	
      case 3:
	do_string_arg(ch, "Enter new short description:\n\r", &o->shdesc, "");
	break;
	
      case 4:
	do_string_arg(ch, "Enter new description:\n\r", &o->description, "");
	break;
	
      case 5:
        menu_push(ch);
        MENU_RETURN(ch) = 0;
        MENU_HANDLER_ARG(ch) = (void *) 0;
        menu_jump(ch, oedit_string_menu);
        break;

      case 6:
	get_integer_list(ch, "Enter number of the desired type: ",
			 &o->type_flag, sizeof(o->type_flag), item_types);
	break;

      case 7:
	toggle_menu(ch, "Enter wear bit to toggle: ", &OBJ_WEARS(o), wear_bits);
	break;
	
      case 8:
	toggle_menu(ch, "Enter extra bit to toggle: ", &OBJ_EXTRAS(o), extra_bits);
	break;

      case 9:
	toggle_menu(ch, "Enter extra bit to toggle: ", &OBJ_EXTRAS2(o), extra_bits2);
	break;

      case 10:
      case 11:
      case 12:
      case 13:
	oedit_get_value(ch, field - 10);
	break;

      case 14:
	GET_INTEGER_ARG(ch, "Enter object weight: ", o->weight, 
			0, 10000000);
	break;

      case 15:
	GET_INTEGER_ARG(ch, "Enter object value: ", o->cost, 
			0, 10000000);
	break;

      case 16:
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, oedit_extradesc_menu);
	break;
	
      case 17:
	menu_push(ch);
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, oedit_affects_menu);
	break;

      case 18:
	GET_INTEGER_ARG(ch, "Chance in 10000 that object will load on reset: ", o->percent_load, 1, 10000);
	break;

      case 19:
	GET_INTEGER_ARG(ch, "Alternate object to load if this one fails: ", o->alternate_load, 0, 99999);
	break;

   // huh?
      case 20:
        break;

      case 21:
	GET_INTEGER_ARG(ch, "Total number allowed in game at one time: ", o->total_game_limit, 0, 9999);
	break;

      case 22:
	GET_INTEGER_ARG(ch, "Minimum level to use/wear/hold/wield/etc: ", o->min_level, 0, 74);
	break;

      case 23:
        if (!OBJ_FLAGGED(o, ITEM_SPELLEQ))
	   break; 
        if (GET_LEVEL(ch) < LEV_GOD)
	{
	  sprintf(buf, "See a GOD+ to change spelleq attributes.\n\r");
          S2C();
          break;
        }

        send_spell_listing(ch);
	GET_INTEGER_ARG(ch, "Enter SpellEQ offensive number: ", o->eqspell, -1, MAX_SPELLS-1);
        
        break;

      case 24:
        if (!OBJ_FLAGGED(o, ITEM_SPELLEQ))
	   break; 
        if (GET_LEVEL(ch) < LEV_GOD)
	{
	  sprintf(buf, "See a GOD+ to change spelleq attributes.\n\r");
          S2C();
          break;
        }

	toggle_menu_long(ch, "Enter affect bits to toggle: ", &o->eqaffbit, affected_bits);

        break;

      case 25:
        if (!OBJ_FLAGGED(o, ITEM_SPELLEQ))
	   break; 
        if (GET_LEVEL(ch) < LEV_GOD)
	{
	  sprintf(buf, "See a GOD+ to change spelleq attributes.\n\r");
          S2C();
          break;
        }
	toggle_menu_long(ch, "Enter AFF2 bits to toggle: ", &o->eqaff2bit, affected2_bits);
        break;

      case 26:
	if (!OBJ_FLAGGED(o, ITEM_MAGIC) && MAX_OBJ_HITS(o) >= 0)
	  maxhits = MAX_OBJ_HITS(o);
	else
	  maxhits = 30000;

	GET_INTEGER_ARG(ch, "Enter Object Hits (0 for broken): ", OBJ_HITS(o), 0, maxhits);
        break;

      case 27:
	if (!OBJ_FLAGGED(o, ITEM_MAGIC))
	{
  	  send_to_char("Item must be flagged MAGIC to manually edit MAX_HITS.\n\r", ch);
	  break;
	}

	GET_INTEGER_ARG(ch, "Enter MAX Object Hits (-1 for indestruct): ", MAX_OBJ_HITS(o), -1, 30000);
        break;

      case 28:
	get_integer_list(ch, "Enter the number of desired material: ",
			 &MADE_OF(o), sizeof(MADE_OF(o)), material_types);
	break;

      case 29:
	GET_INTEGER_ARG(ch, "Enter object success percentage (0-100): ", SUCCESS_RATE(o), 0, 100);
        break;

      case 30:
	if (!THROWABLE(o))
	  break;
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, oedit_throwdice_menu); 
	break;

      case 31:
	toggle_menu(ch, "Enter wear vector bit to toggle: ", &WV_FLAGS(o), wv_bits);
	break;
	
      default:
	send_to_char("That field cannot be changed, yet.\n\r", ch);
	break;
    }

    ch->pc_specials->field_changed = TRUE;
}

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

/* now go thru object list and alter all the objs pointing 
   to the old proto to point to this new protos stuff */
void update_objs_in_game(int rnum)
{
  obdata *o;
  
  sprintf(buf, "SYSUPD: Attempting olc object list update rnum: %d, vnum: %d.", rnum, obj_index[rnum].vnum);
  mudlog(buf, BUG, LEV_IMM, TRUE);

  for (o = object_list; o; o = o->next)
  if (o->item_number == rnum)
  {
    o->name = obj_proto[rnum].name;
    o->shdesc = obj_proto[rnum].shdesc;
    o->description = obj_proto[rnum].description;
    o->actdesc = obj_proto[rnum].actdesc;
    o->wear_mesg = obj_proto[rnum].wear_mesg;
    o->rem_mesg = obj_proto[rnum].rem_mesg;
    o->weap_sing = obj_proto[rnum].weap_sing;
    o->weap_plur = obj_proto[rnum].weap_plur;
    o->exdesc = obj_proto[rnum].exdesc;

    // COULD add further updates of existing objects in the
    // future, but for now, only strings are that vital -jtrhone
  }
}

// check an object for null strings -roa
void check_invalid_object(obdata *o)
{
  BOOL dolog = FALSE;

  if (!o->name || !*o->name)
  {
    o->name = STR_DUP("invalid object");
    dolog = TRUE;
  }

  if (!o->shdesc || !*o->shdesc)
  {
    o->shdesc = STR_DUP("an invalid object");
    dolog = TRUE;
  }

  if (!o->description || !*o->description)
  {
    o->description = STR_DUP("an invalid object lies here");
    dolog = TRUE;
  }

  if (!o->actdesc || !*o->actdesc)
  {
    o->actdesc = 
    STR_DUP("I'm a lonely invalid object.  Tell an immortal about me:)\n\r");
    dolog = TRUE;
  }

  if (!o->wear_mesg || !*o->wear_mesg)
  {
    o->wear_mesg = STR_DUP("BLANK");
    dolog = TRUE;
  }
  if (!o->rem_mesg || !*o->rem_mesg)
  {
    o->rem_mesg = STR_DUP("BLANK");
    dolog = TRUE;
  }
  if (!o->weap_sing || !*o->weap_sing)
  {
    o->weap_sing = STR_DUP("BLANK");
    dolog = TRUE;
  }
  if (!o->weap_plur || !*o->weap_plur)
  {
    o->weap_plur = STR_DUP("BLANK");
    dolog = TRUE;
  }

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

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

ROA_MENU(oedit_confirm_save)
{
  char buf[MAX_STRING_LENGTH];
  char *p;
  int rnum, vnum;
  obdata *o = OBJ_EDITTED(ch);
    
  if (!input_str)
  {
    MENU_PROMPT(ch) = "Save editted object (YES/no)? ";
    return;
  }
    
  strcpy(buf, input_str);
  p = strtok(buf, " 	\n\r");
  if (!p || strncasecmp("no", p, strlen(p)) != 0)
  {
    vnum = o->item_number;
    if ((rnum = real_object(vnum)) >= 0)
    {
      o->item_number  = rnum;
      check_invalid_object(o);
      dupe_object_over(o, &obj_proto[rnum]);
      update_objs_in_game(rnum);

      strcpy(buf2, "editted");
    }
    else
    {
      obj_index[top_of_objt].vnum      = vnum;
      obj_index[top_of_objt].number    = 0;
      obj_index[top_of_objt].func      = 0;

      o->item_number  = top_of_objt;
      o->in_room      = NOWHERE;

      check_invalid_object(o);
      dupe_object_over(o, &obj_proto[top_of_objt]);
      top_of_objt++;

      strcpy(buf2, "created");
    }

    sprintf(buf, "PLRUPD: %s has %s object #%d", GET_NAME(ch), buf2, vnum);
    mudlog(buf, BRF, GET_LEVEL(ch), TRUE);	
  }  /* end of if SAVE confirmed */

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

ACMD(do_olist)
{
  obdata *o;
  char aname[20];
  int zone, i, j, top, set;
  BOOL found = FALSE;
  BOOL flag = FALSE;
  BOOL spelleq = FALSE;
  BOOL weapon = FALSE;
  char buf[20000];
  char arg1[MAX_INPUT_LENGTH];
  long flg;

  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 (!olc_perms(ch, LEV_GOD, zone))
    return;

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

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

  half_chop(buf2, buf, arg1);

  // added flag subcmd to this, search for flags on objects... 1/19/98 -jtrhone
  if (*buf && is_abbrev(buf, "flag"))
  {
    if ((flg = search_block(arg1, extra_bits, TRUE)) < 0)
    {
      if ((flg = search_block(arg1, extra_bits2, TRUE)) < 0)
      {
        send_to_char("Unable to locate flag.\n\r",ch);
        return;
      }
      else
        set = 2;
    }
    else
      set = 1;

    flag = TRUE;
  }
  else
  if (*buf && is_abbrev(buf, "spelleq"))
    spelleq = TRUE;
  else
  if (*buf && is_abbrev(buf, "weapon"))
    weapon = TRUE;

  buf[0] = '\0';
  while (i <= top)
  {
    if (real_object(i) < 0)
    {
      i++;
      continue;
    }

    o = &obj_proto[real_object(i)];

    if (flag)
    {
      switch (set) {
        case 1:
          if (!OBJ_FLAGGED(o, (1 << flg)))
          {
            i++;
            continue;
          }
          break;

        case 2:
          if (!OBJ_FLAGGED2(o, (1 << flg)))
          {
            i++;
            continue;
          }
          break;
      }
    }

    if (spelleq && !OBJ_FLAGGED(o, ITEM_SPELLEQ))
    {
      i++; 
      continue;
    }
    else
    if (weapon && !IS_WEAPON(o))
    {
      i++; 
      continue;
    }

    found = TRUE;	
    sprintf(buf1, "[%5d] %-20.20s%%0 ", i, o->name);

    sprinttype(ITEM_TYPE(o), item_types, buf2);
    switch (ITEM_TYPE(o))
    {
      case ITEM_ARMOR:
        sprintf(buf2, "%s AC%d ", buf2, o->value[0]);
        break;
      case ITEM_WEAPON:
        sprintf(buf2, "%s %dd%d ", buf2, o->value[1], o->value[2]);
        break;
    }
    strcat(buf1, buf2);
    *buf2 = '\0';
    for (j = 0; j < MAX_OBJ_AFFECT; j++)
      if (o->affected[j].location)
      {
	sprinttype(o->affected[j].location, apply_types, aname);
	sprintf(buf2, " %+d %s   ", o->affected[j].modifier, aname);
	sprintf(buf1 + strlen(buf1), "%s", buf2);
	if (!j)
	  strcat(buf1, " ");
      }

    if (GET_LEVEL(ch) >= LEV_CIMP)
    {
      sprintf(buf2, " #%d",obj_index[GET_OBJ_RNUM(o)].number);
      strcat(buf1, buf2);
    }
    strcat(buf, buf1);
    strcat(buf, "\n\r");
    strcpy(buf2, "");
    i++;
  }

  if (found)
   page_string(ch->desc, buf, TRUE);
  else
  if (flag)
   send_to_char("No objects with that flag found in that zone.\n\r",ch);
  else
  if (spelleq)
   send_to_char("No SPELLEQ objects in that zone.\n\r",ch);
  else
  if (weapon)
   send_to_char("No WEAPON objects in that zone.\n\r",ch);
  else
   send_to_char("No objects in that zone.\n\r",ch);
}

ACMD(do_oquery)
{
  send_to_char("Oquery is no longer implemented due to a mem leak.\n\r",ch);
  send_to_char("... heh, and the fact it was useless :P\n\r",ch);
}