/* **************************************************************************
 * File:      craft.c                                       Part of tbaMUD  *
 * Version:   1.2 (March 2009) Written for tbaMUD 3.58 by Jamdog            *
 * Purpose:   To provide player crafting with OLC for creating new crafts   *
 * Copyright: tbaMUD and Jamdog 2009                                        *
 * tbaMUD is a derivative of, and the continuation of, CircleMUD since v3.5 *
 * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.                 *
 ************************************************************************** */

#ifndef __CRAFT_C__
#define __CRAFT_C__

#include "conf.h"
#include "sysdep.h"
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "handler.h"
#include "db.h"
#include "interpreter.h"
#include "genolc.h"
#include "genzon.h"
#include "spells.h"
#include "oasis.h"
#include "class.h"
#include "screen.h"

#include "craft.h"

/* local static functions */
static void craftedit_save_internally(struct descriptor_data *d);
static void craftedit_save_to_disk(void);
static void craftedit_setup_new(struct descriptor_data *d);
static void craftedit_setup_existing(struct descriptor_data *d, struct craft_data *cr);
static void craftedit_disp_menu(struct descriptor_data *d);
static void craftedit_req_menu(struct descriptor_data *d);
static void craftedit_class_menu(struct descriptor_data *d);
static void craftedit_flag_menu(struct descriptor_data *d);
static void craftedit_skill_menu(struct descriptor_data *d);

const char *craft_req_flags[] = {
  "NON-EXPEND",
  "IN-ROOM",
  "\n"
};

/* Global variables used for crafting system */
struct craft_data *craft_list = NULL;

/* Global variable as placeholder for OLC system */
struct craft_requirement temp_req;

struct craft_data *new_craft(void)
{
  struct craft_data *new_data;

  CREATE(new_data, struct craft_data, 1);

  new_data->result     = NOTHING;
  new_data->craft_name = strdup("New Craft");
  new_data->obj_reqs   = NULL;
  new_data->num_reqs   = 0;
  new_data->class_req  = 0;
  new_data->skill_req  = 0;
  new_data->min_skill  = 0;
  new_data->min_level  = 0;
  new_data->next       = NULL;

  return (new_data);
}

struct craft_data *get_craft_by_vnum(craft_vnum c_num)
{
  struct craft_data *this_craft;

  if (!craft_list) return NULL;

  for (this_craft=craft_list; this_craft; this_craft = this_craft->next) {
    if (this_craft->vnum == c_num)
      return this_craft;
  }
  return NULL;
}

void remove_craft_from_list(struct craft_data *rem_craft)
{
  struct craft_data *this_craft = NULL;

  if (!craft_list) return;

  /* Check the main craft list */
  for (this_craft=craft_list; this_craft && this_craft != rem_craft; this_craft = this_craft->next);
  if (this_craft == NULL) return;

  /* We found the craft in the list, remove it */
  if (this_craft == craft_list) {
    craft_list = craft_list->next;   /* 1st item - move the main craft_list pointer */
    this_craft->vnum = NOTHING;
    free_craft(this_craft);
  } else {
    for (this_craft=craft_list; this_craft && this_craft->next != rem_craft; this_craft = this_craft->next);
    this_craft->next = rem_craft->next;
    rem_craft->vnum = NOTHING;
    free_craft(rem_craft);
  }
}

void free_craft(struct craft_data *this_craft)
{
  if (this_craft) {
    free_craft_requirements(this_craft);
    if (this_craft->craft_name)
      free(this_craft->craft_name);
    if (get_craft_by_vnum(this_craft->vnum) != NULL)
      remove_craft_from_list(this_craft);
    free(this_craft);
  }
}

void free_craft_requirements(struct craft_data *this_craft)
{
	if (this_craft->obj_reqs)
	  free(this_craft->obj_reqs);

	this_craft->obj_reqs = NULL;
	this_craft->num_reqs = 0;
}

void free_craft_list(void)
{
  struct craft_data *this_craft;

  for (this_craft=craft_list; this_craft; this_craft=craft_list) {
    craft_list = this_craft->next;
    this_craft->next = NULL;
    if (this_craft->craft_name)
      free(this_craft->craft_name);
    free_craft_requirements(this_craft);

  }
  craft_list = NULL;
}

void copy_craft(struct craft_data *to_craft, struct craft_data *from_craft)
{
  int i;

  if (!to_craft || !from_craft) return;

  if (to_craft->craft_name) free(to_craft->craft_name);
  if (to_craft->obj_reqs) free_craft_requirements(to_craft);

  *to_craft = *from_craft;

  if (from_craft->craft_name)
    to_craft->craft_name = strdup(from_craft->craft_name);
  else
    to_craft->craft_name = NULL;

  to_craft->obj_reqs = NULL;
  to_craft->num_reqs = 0;

  for (i=0; i<from_craft->num_reqs; i++) {
    add_craft_requirement(to_craft, from_craft->obj_reqs[i].onum,
                                    from_craft->obj_reqs[i].num_required,
                                    from_craft->obj_reqs[i].req_flags);
  }
}

void add_craft(struct craft_data *toadd)
{
  struct craft_data *this_craft=NULL, *i=NULL, *j=NULL;

  if (craft_list == NULL) {
    craft_list = new_craft();
    this_craft = craft_list;

  } else {
    this_craft = new_craft();
    /* Sort numerically at add-time */
    for (i=craft_list; i && (CRAFT_VNUM(i) < CRAFT_VNUM(toadd)); i=i->next) j = i;
    if (j) {
      this_craft->next = j->next;
      j->next = this_craft;
	}
  }
  copy_craft(this_craft, toadd);
}

void add_craft_requirement(struct craft_data *this_craft, obj_vnum o_num, int num_objs, int *rq_flags)
{
  int i;
  if (!this_craft)
  {
    log("SYSERR: Invalid craft (NULL) passed to add_craft_requirement");
    return;
  }

  if (real_object(o_num) == NOTHING)
  {
    log("SYSERR: Invalid object vnum (%d) passed to add_craft_requirement", o_num);
    return;
  }

  if (num_objs < 1)
  {
    log("SYSERR: Invalid number of objects (%d) passed to add_craft_requirement", num_objs);
    return;
  }

  this_craft->num_reqs++;
  if (this_craft->obj_reqs && this_craft->num_reqs > 1)
    RECREATE(this_craft->obj_reqs, struct craft_requirement, this_craft->num_reqs);
  else
    CREATE(this_craft->obj_reqs, struct craft_requirement, this_craft->num_reqs);

  this_craft->obj_reqs[(this_craft->num_reqs-1)].onum         = o_num;
  this_craft->obj_reqs[(this_craft->num_reqs-1)].num_required = num_objs;

  for (i=0; i<RQ_ARRAY_MAX; i++)
    this_craft->obj_reqs[(this_craft->num_reqs-1)].req_flags[i] = rq_flags[i];
}

void delete_craft_requirement(struct craft_data *this_craft, int req_id)
{
  int i, how_many;
  struct craft_requirement *new_list;

  /* Validate the variables */
  if (!this_craft) return;
  how_many = this_craft->num_reqs;
  if (how_many == 0 || this_craft->obj_reqs == NULL) return;
  if (req_id < 0 || req_id >= how_many) return;


  /* We know which requirement in the list, make a new list */
  CREATE(new_list, struct craft_requirement, how_many-1);

  /* Copy to the new list, ignoring the one to be removed */
  for (i=0; i<how_many; i++) {
    if (i < req_id) {
      new_list[i] = this_craft->obj_reqs[i];
    }
    else if (i > req_id) {
      new_list[(i-1)] = this_craft->obj_reqs[i];
    }
  }

  /* All the data is copied to the new list - kill the old one */
  free(this_craft->obj_reqs);
  this_craft->obj_reqs = new_list;
  this_craft->num_reqs--;
}

int load_crafts(void)
{
  FILE *fl;
  int gl = 0, num_found=0, i, j, line_no = 0, v[2], b_flags[RQ_ARRAY_MAX];
  char tag[6], line[MAX_INPUT_LENGTH + 1], buf[MAX_STRING_LENGTH];
  char buf2[MAX_STRING_LENGTH], buf3[MAX_STRING_LENGTH], buf4[MAX_STRING_LENGTH];
  struct craft_data *this_craft=NULL;

  log("Loading Crafts file...");
  if (!(fl = fopen(CRAFT_FILE, "r"))) {
    log("   Crafts file (%s) does not exist. Will create a new one", CRAFT_FILE);
    save_crafts();
    return FALSE;
  } else {
    /* Ensure the craft_list is empty before loading */
    free_craft_list();
    while ((gl = get_line(fl, line)) && *line != '$') {
      line_no++;
	  while (*line == '*') {gl = get_line(fl, line);  line_no++;}
	  if (*line == '#') {
        num_found++;
        if (num_found > MAX_CRAFTS) {
          log("SYSERR: Too many crafts found in crafts file (Max: %d)", MAX_CRAFTS);
          return FALSE;
		}
		/* Put the previous craft into the list */
		if (this_craft)
		{
          add_craft(this_craft);
          free_craft(this_craft);
          this_craft = NULL;
		}

		/* Create a new craft ctructure to hold the data being loaded */
        this_craft = new_craft();
        this_craft->vnum = atoi(line+1);
        gl = 0;
	  }
	  if (gl) {
        tag_argument(line, tag);

        switch (*tag) {
          case 'C':
                 if (!strcmp(tag, "Clas"))  this_craft->class_req = asciiflag_conv(line);
            else log("SYSERR: Unknown tag %s in craft file %s, line %d", tag, CRAFT_FILE, line_no);
          break;

          case 'M':
                 if (!strcmp(tag, "MSkl"))  this_craft->min_skill = MIN(MAX(atoi(line), 0), 100);
            else if (!strcmp(tag, "MLvl"))  this_craft->min_level = MIN(MAX(atoi(line), 0), LVL_IMMORT);
            else log("SYSERR: Unknown tag %s in craft file %s, line %d", tag, CRAFT_FILE, line_no);
          break;

          case 'N':
                 if (!strcmp(tag, "Name"))  {
                   if (this_craft->craft_name)
                     free (this_craft->craft_name);
                   this_craft->craft_name = strdup(line);
                 }
            else log("SYSERR: Unknown tag %s in craft file %s, line %d", tag, CRAFT_FILE, line_no);
          break;

          case 'O':
                 if (!strcmp(tag, "ORes"))  this_craft->result = real_object(atoi(line)) != NOTHING ? atoi(line) : NOTHING;
            else log("SYSERR: Unknown tag %s in craft file %s, line %d", tag, CRAFT_FILE, line_no);
          break;

          case 'R':
            if (!strcmp(tag, "Reqs"))  {
              j=0;
              get_line(fl, line);
              line_no++;
              while (*line && *line != '~') {
                if (++j >= MAX_REQUIREMENTS) {
                  log("SYSERR: Too many requirements in craft file (craft VNUM: %d, line %d, requirement %d)", this_craft->vnum, line_no, j+1);
                } else {
                  if ((i = sscanf(line, "%d %d %s %s %s %s", v, v+1, buf, buf2, buf3, buf4)) != 6)
                  {
                    log("SYSERR: Invalid requirement in craft file (craft VNUM: %d, line %d, requirement %d)", this_craft->vnum, line_no, j+1);
                  }
                  else
                  {
                    b_flags[0] = asciiflag_conv(buf);
                    b_flags[1] = asciiflag_conv(buf2);
                    b_flags[2] = asciiflag_conv(buf3);
                    b_flags[3] = asciiflag_conv(buf4);

                    add_craft_requirement(this_craft, v[0], v[1], b_flags);
				  }
                }
                get_line(fl, line);
                line_no++;
              }
            }
            else log("SYSERR: Unknown tag %s in craft file %s, line %d", tag, CRAFT_FILE, line_no);
          break;

          case 'S':
                 if (!strcmp(tag, "Skil"))  this_craft->skill_req = atoi(line);
            else log("SYSERR: Unknown tag %s in craft file %s, line %d", tag, CRAFT_FILE, line_no);
          break;


          default:
            log("SYSERR: Unknown tag %s in craft file %s, line %d", tag, CRAFT_FILE, line_no);
          break;
        } /* end switch tag */
      } /* end if (gl) */
    } /* end while get_line */

    /* Put the final craft into the list (if one was found) */
    if (this_craft)
    {
      add_craft(this_craft);
      free_craft(this_craft);
      this_craft = NULL;
    }
    log("- %d crafts loaded successfully", num_found);
    fclose(fl);
  } /* end else */
  return TRUE;
}

int save_crafts(void)
{
  FILE *fl;
  int j;
  char bits[127], bits2[127], bits3[127], bits4[127];
  struct craft_data *this_craft;

  if (!(fl = fopen(CRAFT_FILE, "w"))) {
    mudlog(CMP, LVL_IMPL, TRUE, "SYSERR: Unable to open craft file for writing!");
    return FALSE;
  }
  fprintf(fl, "* ASCII Craft File\n");
  fprintf(fl, "* Part of tbaMUD\n");
  for(this_craft=craft_list; this_craft; this_craft = this_craft->next) {
    fprintf(fl, "#%d\n", this_craft->vnum);

    if (this_craft->craft_name)
      fprintf(fl, "Name: %s\n", this_craft->craft_name);

    if (this_craft->result)
      fprintf(fl, "ORes: %d\n", this_craft->result);

    if (this_craft->class_req)
      fprintf(fl, "Clas: %d\n", this_craft->class_req);

    if (this_craft->skill_req)
      fprintf(fl, "Skil: %d\n", this_craft->skill_req);

    if (this_craft->min_skill)
      fprintf(fl, "MSkl: %d\n", this_craft->min_skill);

    if (this_craft->min_level)
      fprintf(fl, "MLvl: %d\n", this_craft->min_level);

    if (this_craft->num_reqs > 0)
    {
      fprintf(fl, "Reqs:\n");
      for (j=0; j<this_craft->num_reqs; j++) {
        sprintascii(bits,  CRAFT_REQ_FLAGS(this_craft,j)[0]);
        sprintascii(bits2, CRAFT_REQ_FLAGS(this_craft,j)[1]);
        sprintascii(bits3, CRAFT_REQ_FLAGS(this_craft,j)[2]);
        sprintascii(bits4, CRAFT_REQ_FLAGS(this_craft,j)[3]);

        fprintf(fl, "%d %d %s %s %s %s\n", CRAFT_REQ_OBJ(this_craft,j), CRAFT_REQ_NUM(this_craft,j),
                                           bits, bits2, bits3, bits4);
      }
      fprintf(fl, "~\n");
    }
  }
  fprintf(fl, "$\n");
  fclose(fl);

  return TRUE;
}

void show_crafts(struct char_data *ch, char *arg)
{
  int j;
  obj_rnum o_num;
  char buf[MAX_STRING_LENGTH];
  struct craft_data *i;

  get_char_colors(ch);

  if (!craft_list) {
    send_to_char(ch, "There are no crafts currently available.\r\n");
    return;
  }

  if (arg && *arg) {    /* List a single craft's details */
    if ((i = get_craft_by_vnum(atoi(arg))) == NULL) {
      send_to_char(ch, "Invalid craft number.\r\n");
      return;
    }
    sprintbit(CRAFT_CLASS_FLAGS(i), pc_class_types, buf, sizeof(buf));

    send_to_char(ch, "Craft VNUM     : %s[%s%d%s]%s\r\n"
                     "Craft Name     : %s%s%s\r\n"
                     "Result Object  : %s%d %s(%s)%s\r\n"
                     "Allowed Classes: %s%s%s\r\n"
                     "Required Skill : %s%d %s(%s)%s\r\n"
                     "Minimum Skill  : %s%d%s\r\n"
                     "Minimum Level  : %s%d%s\r\n",
                     cyn, yel, CRAFT_VNUM(i), cyn, nrm,
                     CRAFT_NAME(i) != NULL ? yel : cyn,
                     CRAFT_NAME(i) != NULL ? CRAFT_NAME(i) : "<Not Set!>", nrm,
                     yel, CRAFT_RESULT(i) != NOTHING ? CRAFT_RESULT(i) : 0,
                     cyn, real_object(CRAFT_RESULT(i)) != NOTHING ? obj_proto[real_object(CRAFT_RESULT(i))].short_description : "<Not Set!>", nrm,
                     cyn, buf, nrm,
                     yel, CRAFT_SKILL_REQ(i),
                     cyn, CRAFT_SKILL_REQ(i) != 0 ? spell_info[CRAFT_SKILL_REQ(i)].name : "<None>", nrm,
                     yel, CRAFT_MIN_SKILL(i), nrm,
                     yel, CRAFT_MIN_LEVEL(i), nrm);

    if (CRAFT_NUM_REQS(i) > 0) {
      send_to_char(ch, "Requirements   :\r\n");
      for (j=0; j<CRAFT_NUM_REQS(i); j++) {
        sprintbitarray(CRAFT_REQ_FLAGS(i,j), craft_req_flags, RQ_ARRAY_MAX, buf);
        o_num = real_object(CRAFT_REQ_OBJ(i,j));
        send_to_char(ch, "(%d) %s[%s%5d%s]%s %-30s %s%s%s\r\n",
                         CRAFT_REQ_NUM(i,j),
                         cyn, yel, CRAFT_REQ_OBJ(i,j) != NOTHING ? CRAFT_REQ_OBJ(i,j) : 0, cyn, yel,
                         o_num != NOTHING ? obj_proto[o_num].short_description: "<Invalid Obj VNUM>",
                         cyn, buf, nrm
                         );
      }
    } else {
      send_to_char(ch, "Requirements   : %s<None Set!>%s\r\n", cyn, nrm);
    }
  }
  else                  /* List all crafts */
  {
    send_to_char(ch, "VNUM     Object   Name                            Lvl  Skill\r\n");
    for (i=craft_list; i; i=i->next) {
      send_to_char(ch, "%s[%s%5d%s]  [%s%5d%s]%s  %-30s  %s%-3d  %s%s\r\n",
               cyn, yel, CRAFT_VNUM(i), cyn,
               yel, CRAFT_RESULT(i) != NOTHING ? CRAFT_RESULT(i) : 0, cyn,
               CRAFT_NAME(i) != NULL ? yel : cyn,
               CRAFT_NAME(i) != NULL ? CRAFT_NAME(i) : "<Not Set!>",
               yel, CRAFT_MIN_LEVEL(i),
               CRAFT_SKILL_REQ(i) != 0 ? spell_info[CRAFT_SKILL_REQ(i)].name : "<None>",
               nrm);
    }
  }
}

struct obj_data *check_obj_in_inventory(struct char_data *ch, obj_rnum item_rnum)
{
  struct obj_data *obj;
  for (obj = ch->carrying;obj;obj=obj->next_content)
    if(GET_OBJ_RNUM(obj)==item_rnum)
      return obj;
  return NULL;
}

int count_objs_in_inventory(struct char_data *ch, obj_rnum item_rnum)
{
  struct obj_data *obj;
  int count=0;

  for (obj = ch->carrying;obj;obj=obj->next_content)
    if(GET_OBJ_RNUM(obj)==item_rnum)
      count++;

  return count;
}

struct obj_data *check_obj_in_inv_or_room(struct char_data *ch, obj_rnum item_rnum)
{
  struct obj_data *obj;

  for (obj = ch->carrying;obj;obj=obj->next_content)
    if(GET_OBJ_RNUM(obj)==item_rnum)
      return obj;

  for (obj = world[IN_ROOM(ch)].contents;obj;obj=obj->next_content)
    if(GET_OBJ_RNUM(obj)==item_rnum)
      return obj;

  return NULL;
}

int count_objs_in_inv_or_room(struct char_data *ch, obj_rnum item_rnum)
{
  struct obj_data *obj;
  int count=0;

  for (obj = ch->carrying;obj;obj=obj->next_content)
    if(GET_OBJ_RNUM(obj)==item_rnum)
      count++;

  for (obj = world[IN_ROOM(ch)].contents;obj;obj=obj->next_content)
    if(GET_OBJ_RNUM(obj)==item_rnum)
      count++;

  return count;
}

/* Player command: lists known crafts */
ACMD(do_crafts)
{
  int j, count=0;
  struct char_data *vict = ch;
  struct craft_data *i;
  char arg[MAX_STRING_LENGTH];
  obj_rnum o_num=NOTHING;
  bool fail=FALSE;

  argument = one_argument(argument, arg);
  if (GET_LEVEL(ch) >= LVL_GRGOD)
  {
    if (*arg)
      if (!(vict = get_player_vis(ch, arg, NULL, FIND_CHAR_WORLD)))
        vict = ch;

    if (vict != ch) {
      send_to_char(ch, "%sCraft details for %s%s\r\n", CCCYN(ch, C_NRM), GET_NAME(vict), CCNRM(ch, C_NRM));
      argument = one_argument(argument, arg);  /* Check for another arg */
    }
  }

  if (*arg) {
    if ((i = get_craft_by_name(arg)) != NULL) {
      /* Show details for an individual craft */
      if (!known_craft(vict, CRAFT_VNUM(i))) {
        send_to_char(ch, "You do not know that craft!\r\n");
        return;
      }
      send_to_char(ch, "%sCraft Details for '%s'%s\r\n", CCYEL(ch, C_NRM), CRAFT_NAME(i), CCNRM(ch, C_NRM));
      send_to_char(ch, "Skill Required: %s%s%s\r\n", CCCYN(ch, C_NRM), spell_info[CRAFT_SKILL_REQ(i)].name, CCNRM(ch, C_NRM));
      send_to_char(ch, "Requirements  :\r\n");
      for (j=0; j<CRAFT_NUM_REQS(i); j++) {
        o_num = real_object(CRAFT_REQ_OBJ(i,j));
        send_to_char(ch, "%s%d%s x %s%-30s%s\r\n",
                         CCYEL(ch, C_NRM), CRAFT_REQ_NUM(i,j), CCNRM(ch, C_NRM),
                         CCCYN(ch, C_NRM),
                         o_num != NOTHING ? obj_proto[o_num].short_description : "<Invalid Obj VNUM>",
                         CCNRM(ch, C_NRM)
                         );
      }
      return;
    }
  }

  for( i=craft_list; i; i=i->next) {
    if (CRAFT_SKILL_REQ(i) == 0)                     /* No known skill (craft err) */
      continue;

    if (GET_SKILL(vict, CRAFT_SKILL_REQ(i)) < CRAFT_MIN_SKILL(i)) /* Player doesn't know skill  */
      continue;

    if (GET_LEVEL(vict) < CRAFT_MIN_LEVEL(i))        /* Player too low level  */
      continue;

    if (real_object(CRAFT_RESULT(i)) == NOTHING)     /* Invalid result object */
      continue;

    /* Check Class restriction flags */
    if (!CRAFT_CLASS_FLAGGED(i, GET_CLASS(vict)))
      continue;

    fail=FALSE;

    /* Check all requirements are real objects */
    for(j=0; j<CRAFT_NUM_REQS(i); j++)
      if ((o_num = real_object(CRAFT_REQ_OBJ(i,j))) == NOTHING)  /* Valid result object */
        fail=TRUE;

    if (fail == TRUE)
      continue;

    /* All checks passed - show this line of the list */
    if (count == 0)
      send_to_char(ch, "Craft Name           Skill           Creates\r\n");

    count++;
    o_num = real_object(CRAFT_RESULT(i));
    send_to_char(ch, "%s%-20s%s %-15s%s %s\r\n", CCYEL(ch, C_NRM), CRAFT_NAME(i),
                                         CCCYN(ch, C_NRM), spell_info[CRAFT_SKILL_REQ(i)].name,
                                         CCNRM(ch, C_NRM), obj_proto[o_num].short_description);
  }
  if (count == 0) {
    if (vict == ch) {
      send_to_char(ch, "You do not know any crafts!\r\n");
    } else {
      send_to_char(ch, "%s does not know any crafts!\r\n", GET_NAME(vict));
    }
  } else {
    if (vict == ch) {
      send_to_char(ch, "You know %s%d%s craft%s!\r\n", CCYEL(ch, C_NRM), count,
                                                       CCNRM(ch, C_NRM), count == 1 ? "" : "s");
    } else {
      send_to_char(ch, "%s knows %s%d%s craft%s!\r\n", GET_NAME(vict), CCYEL(ch, C_NRM), count,
                                                       CCNRM(ch, C_NRM), count == 1 ? "" : "s");
    }
  }
}

/* Returns TRUE if the craft is valid and if ch should know it */
bool known_craft(struct char_data *ch, craft_rnum c_num)
{
  int i;
  struct craft_data *this_craft;

  /* Perform checks - if any fail, return FALSE */
  if ((this_craft = get_craft_by_vnum(c_num)) == NULL)
    return FALSE;

  if (CRAFT_SKILL_REQ(this_craft) == 0)                     /* No known skill (craft err) */
    return FALSE;

  if (GET_SKILL(ch, CRAFT_SKILL_REQ(this_craft) < CRAFT_MIN_SKILL(this_craft))) /* Player doesn't know skill  */
    return FALSE;

  if (GET_LEVEL(ch) < CRAFT_MIN_LEVEL(this_craft))        /* Player too low level  */
    return FALSE;

  if (real_object(CRAFT_RESULT(this_craft)) == NOTHING)     /* Invalid result object */
    return FALSE;

  /* Check Class restriction flags */
  if (!CRAFT_CLASS_FLAGGED(this_craft, GET_CLASS(ch)))
    return FALSE;

  /* Check all requirements are real objects */
  for(i=0; i<CRAFT_NUM_REQS(this_craft); i++)
    if ((real_object(CRAFT_REQ_OBJ(this_craft,i))) == NOTHING)  /* Valid result object */
      return FALSE;

  /* All checks passed - return TRUE */
  return TRUE;
}

bool check_craft_kit(struct obj_data *kit, int num)
{
  /* Kit is infinite */
  if (GET_OBJ_VAL(kit, 0) == -1)
    return TRUE;  /* Yes, removal would be successful */

  if (GET_OBJ_VAL(kit, 1) < num)
    return FALSE; /* No, there are not enough kit parts */

  return TRUE;    /* Yes, removal would be successful */
}

bool reduce_craft_kit(struct obj_data *kit, int num)
{
  /* Kit is infinite */
  if (GET_OBJ_VAL(kit, 0) == -1)
    return TRUE;

  if (GET_OBJ_VAL(kit, 1) < num)
    return FALSE; /* No, there are not enough kit parts */

  GET_OBJ_VAL(kit, 1) = MIN(MAX((GET_OBJ_VAL(kit, 1) - num), 0), GET_OBJ_VAL(kit, 0));
    return TRUE;
}

/* Checks all requirements, telling player everything they are missing */
bool has_craft_requirements(struct char_data *ch, craft_vnum c_id)
{
  int i;
  obj_rnum o_rnum;
  struct obj_data *obj;
  struct craft_data *cr;
  bool success = TRUE;

  if ((cr = get_craft_by_vnum(c_id)) == NULL) {
    log("SYSERR: Invalid craft VNUM (%d) passed to has_craft_requirements", c_id);
    return FALSE;
  }
  for (i=0; i<CRAFT_NUM_REQS(cr); i++) {
    if ((o_rnum = real_object(CRAFT_REQ_OBJ(cr,i))) != NOTHING) {  /* Only check if valid */
      if (CRAFT_REQ_FLAGGED(cr,i,C_REQ_IN_ROOM)) {
        if ((obj = check_obj_in_inv_or_room(ch, o_rnum)) != NULL) {
          /* Is this a kit? */
          if (GET_OBJ_TYPE(obj) == ITEM_CRAFTKIT) {
            if (!check_craft_kit(obj, CRAFT_REQ_NUM(cr,i))) {
              success = FALSE;
              send_to_char(ch, "%s is too empty.\r\n", GET_OBJ_SHORT(obj));
            }
          } else {  /* Not a craftkit */
            if (count_objs_in_inv_or_room(ch, o_rnum) < CRAFT_REQ_NUM(cr,i)) {
              success = FALSE;
              send_to_char(ch, "You don't have enough of %s.\r\n", GET_OBJ_SHORT(obj));
            }
          }
        } else {
          success = FALSE;
          send_to_char(ch, "You don't have %s.\r\n", obj_proto[o_rnum].short_description);
        }
      } else {
        if ((obj = check_obj_in_inventory(ch, o_rnum)) != NULL) {
          /* Is this a kit? */
          if (GET_OBJ_TYPE(obj) == ITEM_CRAFTKIT) {
            if (!check_craft_kit(obj, CRAFT_REQ_NUM(cr,i))) {
              success = FALSE;
              send_to_char(ch, "%s is too empty.\r\n", GET_OBJ_SHORT(obj));
            }
          } else {  /* Not a craftkit */
            if (count_objs_in_inventory(ch, o_rnum) < CRAFT_REQ_NUM(cr,i)) {
              success = FALSE;
              send_to_char(ch, "You don't have enough of %s.\r\n", GET_OBJ_SHORT(obj));
            }
          }
        } else {
          success = FALSE;
          send_to_char(ch, "You don't have %s.\r\n", obj_proto[o_rnum].short_description);
        }
      }
    }
  }
  return(success);
}

void remove_all_craft_requirements(struct char_data *ch, craft_vnum c_id)
{
  int i, j;
  obj_rnum o_rnum;
  struct obj_data *obj;
  struct craft_data *cr;

  if ((cr = get_craft_by_vnum(c_id)) == NULL) {
    log("SYSERR: Invalid craft VNUM (%d) passed to remove_all_craft_requirements", c_id);
    return;
  }

  for (i=0; i<CRAFT_NUM_REQS(cr); i++) {
	if (CRAFT_REQ_FLAGGED(cr, i, C_REQ_NON_EXPEND))  /* Flagged 'do not expend' */
	  continue;

    if ((o_rnum = real_object(CRAFT_REQ_OBJ(cr,i))) != NOTHING) {
      if (CRAFT_REQ_FLAGGED(cr,i,C_REQ_IN_ROOM)) {
        /* IN-ROOM is flagged, so check both room and inventory */
        if ((obj = check_obj_in_inv_or_room(ch, o_rnum)) != NULL) {
          if (GET_OBJ_TYPE(obj) == ITEM_CRAFTKIT) {
            reduce_craft_kit(obj, CRAFT_REQ_NUM(cr,i));
          } else {
            /* Repeat as many times as necessary */
            for(j=0; j<CRAFT_REQ_NUM(cr,i); j++) {
              if (obj) {
                obj_from_char(obj);
                extract_obj(obj);
              }
              obj = check_obj_in_inv_or_room(ch, o_rnum);
            }
          }
        }
      } else if ((obj = check_obj_in_inventory(ch, o_rnum)) != NULL) {
        /* IN-ROOM isn't flagged, so only check inventory */
        if (GET_OBJ_TYPE(obj) == ITEM_CRAFTKIT) {
          reduce_craft_kit(obj, CRAFT_REQ_NUM(cr,i));
        } else {
          /* Repeat as many times as necessary */
          for(j=0; j<CRAFT_REQ_NUM(cr,i); j++) {
            if (obj) {
              obj_from_char(obj);
              extract_obj(obj);
            }
            obj = check_obj_in_inventory(ch, o_rnum);
          }
        }
      }
    }
  }
}

struct craft_data *get_craft_by_name(char *cr_name)
{
  struct craft_data *i;

  if (!cr_name || !*cr_name) return NULL;

  /* Check for perfect match first */
  for (i=craft_list; i; i=i->next) {
    if (i->craft_name && str_cmp(cr_name, i->craft_name) == 0)
      return i;
  }

  /* Not found - check for abbreviated match */
  for (i=craft_list; i; i=i->next) {
    if ( is_abbrev(cr_name, CRAFT_NAME(i)) )
      return i;
  }

  /* Still not found - return NOTHING */
  return NULL;
}

/* General crafting function - used for all crafts, regardless of command */
ACMD(do_gen_craft)
{
  int skill_id=0, i;
  struct obj_data *obj;
  struct craft_data *cr;
  obj_rnum o_num;
  char to_ch[MAX_STRING_LENGTH], to_rm[MAX_STRING_LENGTH];

  switch(subcmd)
  {
    case SCMD_BREW:      skill_id = SKILL_BREW;
                         sprintf(to_ch, "You carefully brew the ingredients and create $p.");
                         sprintf(to_rm, "$n brews up $p.");
                         break;
    case SCMD_SCRIBE:    skill_id = SKILL_SCRIBE;
                         sprintf(to_ch, "You carefully scribe to create $p.");
                         sprintf(to_rm, "$n scribes $p.");
                         break;
    case SCMD_CONSTRUCT: skill_id = SKILL_CONSTRUCT;
                         sprintf(to_ch, "You carefully put the pieces together to contruct $p.");
                         sprintf(to_rm, "$n constructs $p.");
                         break;
    default:             /* Check all crafts, to see if it's been missed in the list */
                         for (i=0; i<MAX_SKILLS; i++)
						   if (spell_info[i].routines == SK_CRAFT)
                             if (is_abbrev(complete_cmd_info[cmd].command, spell_info[i].name))
                               skill_id = i;

                         if (skill_id == 0) {
                           log("SYSERR: Invalid scmd in do_gen_craft");
                           return;
                         }
                         sprintf(to_ch, "You carefully craft $p.");
                         sprintf(to_rm, "$N crafts $p.");
                         break;
  }

  skip_spaces(&argument);

  if (!*argument) {
    send_to_char(ch, "%s what?  Type crafts to see your available crafts.", spell_info[(skill_id)].name);
    return;
  }

  /* Is the 'name' a valid craft? */
  if ((cr = get_craft_by_name(argument)) == NULL) {
    send_to_char(ch, "You have no idea how to %s that!", spell_info[(skill_id)].name);
    return;
  }

  /* Does the player know this craft? */
  if (!known_craft(ch, CRAFT_VNUM(cr))) {
    send_to_char(ch, "You don't know how to %s that!", spell_info[(skill_id)].name);
    return;
  }

  /* Is player using the correct required skill */
  if (skill_id != CRAFT_SKILL_REQ(cr)) {
    send_to_char(ch, "You can't %s that! Try %s instead!", spell_info[(skill_id)].name, spell_info[(CRAFT_SKILL_REQ(cr))].name);
    return;
  }

  if (!has_craft_requirements(ch, CRAFT_VNUM(cr))) {
    /* has_craft_requirements shows messages, so just return */
    return;
  }

  /* Check probability of success */
  if (rand_number(0,101) > GET_SKILL(ch, skill_id)) {
    send_to_char(ch, "You fail, miserably!");
    return;
  }

  if ((o_num = real_object(CRAFT_RESULT(cr))) == NOTHING) {
    send_to_char(ch, "Your attempts result in a gelatinous mess that oozes into the earth.");
    return;
  }

  /* All checks passed, let's craft! */
  remove_all_craft_requirements(ch, CRAFT_VNUM(cr));
  obj = read_object(o_num, REAL);
  obj_to_char(obj, ch);

  act(to_ch, TRUE, ch, obj, NULL, TO_CHAR);
  act(to_rm, TRUE, ch, obj, NULL, TO_ROOM);
}

/****************************************************************************/
/** All craftedit functions are below this point                           **/
/****************************************************************************/

static void craftedit_save_internally(struct descriptor_data *d)
{
  struct craft_data *r_craft;
  if ((r_craft = get_craft_by_vnum(OLC_CRAFT(d)->vnum)) != NULL) {
    copy_craft(r_craft, OLC_CRAFT(d));
  } else {
    add_craft(OLC_CRAFT(d));
  }
}

static void craftedit_save_to_disk(void)
{
  save_crafts();
}

static void craftedit_setup_new(struct descriptor_data *d)
{
  OLC_CRAFT(d) = new_craft();
  OLC_CRAFT(d)->vnum = OLC_NUM(d);
  /* Show the main craft edit menu                              */
  craftedit_disp_menu(d);
}

static void craftedit_setup_existing(struct descriptor_data *d, struct craft_data *cr)
{
  OLC_CRAFT(d) = new_craft();
  copy_craft(OLC_CRAFT(d), cr);
  OLC_CRAFT(d)->vnum = OLC_NUM(d);
  /* Show the main craft edit menu                              */
  craftedit_disp_menu(d);
}

ACMD(do_oasis_craftedit)
{
  int save = 0;
  craft_vnum number = NOTHING;
  struct descriptor_data *d;
  struct craft_data *cr;
  char *buf3;
  char buf1[MAX_INPUT_LENGTH];
  char buf2[MAX_INPUT_LENGTH];

  /****************************************************************************/
  /** Parse any arguments.                                                   **/
  /****************************************************************************/
  buf3 = two_arguments(argument, buf1, buf2);

  if (!*buf1) {
    send_to_char(ch, "Specify a craft VNUM to edit.\r\n");
    return;
  } else if (!isdigit(*buf1)) {
    if (str_cmp("save", buf1) != 0) {
      send_to_char(ch, "Yikes!  Stop that, someone will get hurt!\r\n");
      return;
    }

    save = TRUE;

    if (is_number(buf2)) {
      send_to_char(ch, "Crafts are all saved in one file.  You can't specify a zone!\r\n");
      return;
    }
    send_to_char(ch, "Saving all crafts.\r\n");
    mudlog(CMP, MAX(LVL_BUILDER, GET_INVIS_LEV(ch)), TRUE,
      "OLC: %s saves craft info.", GET_NAME(ch));

    /**************************************************************************/
    /** Save the quest to the quest file.                                    **/
    /**************************************************************************/
    craftedit_save_to_disk();

    return;
  }

  /****************************************************************************/
  /** If a numeric argument was given, get it.                               **/
  /****************************************************************************/
  if (number == NOWHERE)
    number = atoi(buf1);

  /****************************************************************************/
  /** Check that the craft isn't already being edited.                       **/
  /****************************************************************************/
  for (d = descriptor_list; d; d = d->next) {
    if (STATE(d) == CON_CRAFTEDIT) {
      if (d->olc && OLC_NUM(d) == number) {
        send_to_char(ch, "That craft is currently being edited by %s.\r\n",
          PERS(d->character, ch));
        return;
      }
    }
  }

  /****************************************************************************/
  /** Point d to the builder's descriptor.                                   **/
  /****************************************************************************/
  d = ch->desc;

  /****************************************************************************/
  /** Give the descriptor an OLC structure.                                  **/
  /****************************************************************************/
  if (d->olc) {
    mudlog(BRF, LVL_IMMORT, TRUE,
      "SYSERR: do_oasis_craftedit: Player already had olc structure.");
    free(d->olc);
  }

  CREATE(d->olc, struct oasis_olc_data, 1);

  /****************************************************************************/
  /** Find the zone.                                                         **/
  /****************************************************************************/
  if ((OLC_ZNUM(d) = real_zone_by_thing(number)) == NOWHERE) {
    send_to_char(ch, "Sorry, there is no zone for that number!\r\n");
    free(d->olc);
    d->olc = NULL;
    return;
  }

  /****************************************************************************/
  /** Everyone but IMPLs can only edit zones they have been assigned.        **/
  /****************************************************************************/
  if (!can_edit_zone(ch, OLC_ZNUM(d))) {
    send_to_char(ch, "You do not have permission to edit this zone.\r\n");

    /**************************************************************************/
    /** Free the OLC structure.                                              **/
    /**************************************************************************/
    free(d->olc);
    d->olc = NULL;
    return;
  }

  OLC_NUM(d) = number;

  if ((cr = get_craft_by_vnum(number)) != NULL)
    craftedit_setup_existing(d, cr);
  else
    craftedit_setup_new(d);

  STATE(d) = CON_CRAFTEDIT;

  act("$n starts using OLC.", TRUE, d->character, 0, 0, TO_ROOM);
  SET_BIT_AR(PLR_FLAGS(ch), PLR_WRITING);

  mudlog(BRF, LVL_IMMORT, TRUE,
         "OLC: %s starts editing craft %d allowed zone %d",
         GET_NAME(ch), OLC_NUM(d), GET_OLC_ZONE(ch));
}

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

/*-------------------------------------------------------------------*/
/*. Display main menu . */

static void craftedit_disp_menu(struct descriptor_data *d)
{
  char buf[MAX_STRING_LENGTH];

  get_char_colors(d->character);
  clear_screen(d);

  sprintbit(OLC_CRAFT(d)->class_req, pc_class_types, buf, sizeof(buf));

  write_to_output(d, "-- Craft VNUM     : %s[%s%d%s]%s\r\n"
                     "%s1%s) Craft Name     : %s%s%s\r\n"
                     "%s2%s) Result Object  : %s%d %s%s%s\r\n"
                     "%s3%s) Allowed Classes: %s%s%s\r\n"
                     "%s4%s) Required Skill : %s%d %s(%s)%s\r\n"
                     "%s5%s) Minimum Skill  : %s%d%s\r\n"
                     "%s6%s) Minimum Level  : %s%d%s\r\n"
                     "%s7%s) Craft Requirements...\r\n"
                     "%sX%s) Delete this Craft\r\n"
                     "%sQ%s) Quit\r\n"
                     "Enter choice : ",
                     cyn, yel, OLC_NUM(d), cyn, nrm,
                     cyn, nrm, OLC_CRAFT(d)->craft_name != NULL ? yel : cyn,
                     OLC_CRAFT(d)->craft_name != NULL ? OLC_CRAFT(d)->craft_name : "<Not Set!>", nrm,
                     cyn, nrm, yel, OLC_CRAFT(d)->result != NOTHING ? OLC_CRAFT(d)->result : 0,
                     cyn, OLC_CRAFT(d)->result != NOTHING ? obj_proto[(real_object(OLC_CRAFT(d)->result))].short_description : "<Not Set!>", nrm,
                     cyn, nrm, cyn, buf, nrm,
                     cyn, nrm, yel, OLC_CRAFT(d)->skill_req,
                     cyn, OLC_CRAFT(d)->skill_req != 0 ? spell_info[OLC_CRAFT(d)->skill_req].name : "<None>", nrm,
                     cyn, nrm, yel, OLC_CRAFT(d)->min_skill, nrm,
                     cyn, nrm, yel, OLC_CRAFT(d)->min_level, nrm,
                     cyn, nrm,
                     cyn, nrm,
                     cyn, nrm);

  OLC_MODE(d) = CRAFTEDIT_MAIN_MENU;
}

/*-------------------------------------------------------------------*/
/*. Display craft requirements menu . */

static void craftedit_req_menu(struct descriptor_data *d)
{
  int i;
  obj_rnum r_obj = NOTHING;
  struct craft_requirement *this_req=NULL;
  char buf[MAX_STRING_LENGTH];

  get_char_colors(d->character);
  clear_screen(d);

  write_to_output(d, "-- Requirements for Craft VNUM     : %s[%s%d%s]%s\r\n",cyn, yel, OLC_NUM(d), cyn, nrm);
  for (i=0; i<OLC_CRAFT(d)->num_reqs; i++) {
    this_req = OLC_CRAFT(d)->obj_reqs+i;
    r_obj = real_object(this_req->onum);
    sprintbitarray(this_req->req_flags, craft_req_flags, RQ_ARRAY_MAX, buf);

    write_to_output(d, "(%s%d%s) %d x %s[%s%d%s]%s %s %s(%s)%s\r\n",
                       yel, i+1, nrm, this_req->num_required,
                       cyn, yel, this_req->onum, cyn, nrm,
                       r_obj != NOTHING ? obj_proto[r_obj].short_description : "<Invalid Obj VNUM>",
                       cyn, buf, nrm
                       );
  }
  write_to_output(d, "\r\n"
                     "%sA%s) Add a new requirement\r\n"
                     "%sD%s) Delete a requirement\r\n"
                     "%sX%s) Delete ALL requirements\r\n"
                     "%sQ%s) Return to previous menu\r\n",
                     cyn, nrm,
                     cyn, nrm,
                     cyn, nrm,
                     cyn, nrm);

  OLC_MODE(d) = CRAFTEDIT_REQ_MENU;
}

/*-------------------------------------------------------------------*/
/*. Display class flags menu . */

static void craftedit_class_menu(struct descriptor_data *d)
{
  int counter, columns = 0;
  char buf[MAX_STRING_LENGTH];

  get_char_colors(d->character);
  clear_screen(d);

  sprintbit(OLC_CRAFT(d)->class_req, pc_class_types, buf, sizeof(buf));

  write_to_output(d, "-- Allowed Classes for Craft VNUM     : %s[%s%d%s]%s\r\n",cyn, yel, OLC_NUM(d), cyn, nrm);
  for (counter = 0; counter < NUM_CLASSES; counter++) {
    write_to_output(d, "%s%2d%s) %-20.20s %s", cyn, counter+1, nrm,
		pc_class_types[counter], !(++columns % 2) ? "\r\n" : "");
  }
  write_to_output(d, "Class Flags: %s%s%s\r\n", cyn, buf, nrm);

  write_to_output(d, "\r\nEnter class (0 to finish) : ");
  OLC_MODE(d) = CRAFTEDIT_CLASS_MENU;
}

/*-------------------------------------------------------------------*/
/*. Display craft requirement flags menu . */

static void craftedit_flag_menu(struct descriptor_data *d)
{
  int counter, columns = 0;
  char buf[MAX_STRING_LENGTH];

  get_char_colors(d->character);
  clear_screen(d);

  sprintbitarray(temp_req.req_flags, craft_req_flags, RQ_ARRAY_MAX, buf);

  write_to_output(d, "-- Requirement Flags for Craft VNUM     : %s[%s%d%s]%s\r\n",cyn, yel, OLC_NUM(d), cyn, nrm);

  for (counter = 0; counter < NUM_C_REQ_FLAGS; counter++) {
    write_to_output(d, "%s%2d%s) %-20.20s %s", cyn, counter+1, nrm,
		craft_req_flags[counter], !(++columns % 2) ? "\r\n" : "");
  }
  write_to_output(d, "Requirement Flags: %s%s%s\r\n", cyn, buf, nrm);

  write_to_output(d, "\r\nEnter requirement flag (0 to finish) : ");
  OLC_MODE(d) = CRAFTEDIT_FLAG_MENU;
}

/*-------------------------------------------------------------------*/
/*. Display craft skills menu . */

static void craftedit_skill_menu(struct descriptor_data *d)
{
  int i, counter=0, columns = 0;
  for (i=0; i<MAX_SKILLS; i++) {
    if (spell_info[i].routines == SK_CRAFT) {
      write_to_output(d, "%d) %-20.20s %s", ++counter, spell_info[i].name, !(++columns % 2) ? "\r\n" : "");
    }
  }
  write_to_output(d, "\r\nEnter craft skill : ");
  OLC_MODE(d) = CRAFTEDIT_SKILL_MENU;
}

/*-------------------------------------------------------------------*/
/* main craftedit parser function... interpreter throws all input to here. */
void craftedit_parse(struct descriptor_data *d, char *arg)
{
  int i, count, number = atoi(arg);

  switch (OLC_MODE(d)) {

  case CRAFTEDIT_CONFIRM_SAVESTRING:
    switch (*arg) {
    case 'y':
    case 'Y':
      craftedit_save_internally(d);
      mudlog(CMP, MAX(LVL_BUILDER, GET_INVIS_LEV(d->character)), TRUE,
              "OLC: %s edits craft %d", GET_NAME(d->character), OLC_NUM(d));
      if (CONFIG_OLC_SAVE) {
        craftedit_save_to_disk();
        write_to_output(d, "Craft saved to disk.\r\n");
      } else
        write_to_output(d, "Craft saved to memory.\r\n");
      cleanup_olc(d, CLEANUP_ALL);
      return;
    case 'n':
    case 'N':
      cleanup_olc(d, CLEANUP_ALL);
      return;
    case 'a': /* abort quit */
    case 'A':
      craftedit_disp_menu(d);
      return;
    default:
      write_to_output(d, "Invalid choice!\r\n");
      write_to_output(d, "Do you wish to save your changes? : \r\n");
      return;
    }
    break;

  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_MAIN_MENU:
    switch (*arg) {
    case 'q':
    case 'Q':
      if (OLC_VAL(d)) {	/* Something has been modified. */
        write_to_output(d, "Do you wish to save your changes? : ");
        OLC_MODE(d) = CRAFTEDIT_CONFIRM_SAVESTRING;
      } else
        cleanup_olc(d, CLEANUP_ALL);
      return;
    case '1':
      write_to_output(d, "Enter craft name : ");
      OLC_MODE(d) = CRAFTEDIT_NAME;
      break;
    case '2':
      write_to_output(d, "Enter resulting object VNUM : ");
      OLC_MODE(d) = CRAFTEDIT_RESULT;
      break;
    case '3':
      craftedit_class_menu(d);
      break;
    case '4':
      craftedit_skill_menu(d);
      break;
    case '5':
      write_to_output(d, "Enter minimum skill %% (0-100) : ");
      OLC_MODE(d) = CRAFTEDIT_MINSKILL;
      break;
    case '6':
      write_to_output(d, "Enter minimum player level : (0-%d)", (LVL_IMMORT-1));
      OLC_MODE(d) = CRAFTEDIT_MINLEVEL;
      break;
    case '7':
      craftedit_req_menu(d);
      break;
    case 'x':
    case 'X':
      write_to_output(d, "Are you SURE you wish to delete this craft? (Y/N) : ");
      OLC_MODE(d) = CRAFTEDIT_DEL_CRAFT;
      break;

    default:
      craftedit_disp_menu(d);
      break;
    }
    return; /* end of CRAFTEDIT_MAIN_MENU */

  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_REQ_MENU:
    switch (*arg) {
    case 'q':
    case 'Q':
      craftedit_disp_menu(d);
      break;

    case 'a':
    case 'A':
      write_to_output(d, "Enter required object VNUM : ");
      OLC_MODE(d) = CRAFTEDIT_REQ_OBJECT;
      break;

    case 'd':
    case 'D':
      write_to_output(d, "Enter number of requirement to delete : ");
      OLC_MODE(d) = CRAFTEDIT_REQ_DELETE;
      break;

    case 'x':
    case 'X':
      write_to_output(d, "Are you sure you wish to delete ALL requirements (Y/N) : ");
      OLC_MODE(d) = CRAFTEDIT_REQ_DELCONF;
      break;

    default:
      craftedit_req_menu(d);
      break;
    }
    return; /* end of CRAFTEDIT_REQ_MENU */

  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_CLASS_MENU:
    if (number == 0) {
      craftedit_disp_menu(d);
      return;
    }
    if (number < 1 || number > NUM_CLASSES) {
      write_to_output(d, "Invalid class!\r\nTry again : ");
      return;
    }
    TOGGLE_BIT(OLC_CRAFT(d)->class_req, (1 << (number-1)));  /* 32-bit flags */
    OLC_VAL(d) = 1;
    craftedit_class_menu(d);
    return;

  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_FLAG_MENU:
    if (number == 0) {
      /* Finished getting requirement data, add it to the craft */
	  add_craft_requirement(OLC_CRAFT(d), temp_req.onum, temp_req.num_required, temp_req.req_flags);
      craftedit_req_menu(d);
      return;
    }
    if (number < 1 || number > NUM_C_REQ_FLAGS) {
      write_to_output(d, "Invalid flag!\r\nTry again : ");
      return;
    }
    TOGGLE_BIT_AR(temp_req.req_flags, (number-1));           /* 128-bit flags */
    OLC_VAL(d) = 1;
    craftedit_flag_menu(d);
    return;

  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_SKILL_MENU:
    if (number == 0) {
      /* Abort */
      craftedit_disp_menu(d);
      return;
    }
    count = 0;
    for (i=0; i<MAX_SKILLS; i++) {
      if (spell_info[i].routines == SK_CRAFT) {
        if ((++count) == number) {
          OLC_CRAFT(d)->skill_req = i;
          OLC_VAL(d) = 1;
          craftedit_disp_menu(d);
          return;
        }
      }
    }
    write_to_output(d, "Invalid skill!\r\nTry again (0 to abort) : ");
    return;

  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_NAME:
    if (OLC_CRAFT(d)->craft_name)
      free(OLC_CRAFT(d)->craft_name);
    if (*arg)
      OLC_CRAFT(d)->craft_name = strdup(arg);
    else
      OLC_CRAFT(d)->craft_name = NULL;
    OLC_VAL(d) = 1;
    craftedit_disp_menu(d);
    return;

  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_RESULT:
    if (number == 0) {
      craftedit_disp_menu(d);
      return;
	}
    if (real_object(number) == NOTHING) {
      write_to_output(d, "Invalid object VNUM!\r\nTry again (0=abort) : ");
      return;
	}
	OLC_CRAFT(d)->result = number;
    OLC_VAL(d) = 1;
    craftedit_disp_menu(d);
    return;

  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_MINSKILL:
	OLC_CRAFT(d)->min_skill = LIMIT(number, 0, 100);
    OLC_VAL(d) = 1;
    craftedit_disp_menu(d);
    return;

  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_MINLEVEL:
	OLC_CRAFT(d)->min_level = LIMIT(number, 0, (LVL_IMMORT-1));
    OLC_VAL(d) = 1;
    craftedit_disp_menu(d);
    return;

  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_REQ_OBJECT:
    if (number == 0) {
      craftedit_disp_menu(d);
      return;
	}
    if (real_object(number) == NOTHING) {
      write_to_output(d, "Invalid object VNUM!\r\nTry again (0=abort) : ");
      return;
	}
	temp_req.onum = number;
    OLC_VAL(d) = 1;
	write_to_output(d, "How many of this object are required? (1-%d or 0 to abort) : ", MAX_REQUIREMENTS);
    OLC_MODE(d) = CRAFTEDIT_REQ_NUMBER;
    return;

  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_REQ_NUMBER:
    if (number == 0) {
      craftedit_disp_menu(d);
      return;
	}
	temp_req.num_required = LIMIT(number, 1, MAX_REQUIREMENTS);
	craftedit_flag_menu(d);
    OLC_VAL(d) = 1;
    return;

  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_REQ_DELETE:
    if (number > 0 && number <= CRAFT_NUM_REQS(OLC_CRAFT(d))) {
      delete_craft_requirement(OLC_CRAFT(d), number-1);
      OLC_VAL(d) = 1;
    }
	craftedit_req_menu(d);
    return;
  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_REQ_DELCONF:
    switch (*arg) {
    case 'y':
    case 'Y':
      free_craft_requirements(OLC_CRAFT(d));
      OLC_VAL(d) = 1;
      break;

    case 'n':
    case 'N':
      break;

    default:
      write_to_output(d, "Invalid selection - delete aborted!\r\n");
      break;
    }
	craftedit_req_menu(d);
    return;
  /*-------------------------------------------------------------------*/
  case CRAFTEDIT_DEL_CRAFT:
    switch (*arg) {
    case 'y':
    case 'Y':
      /* Free craft automatically checks the craft list and removes matching vnums */
      free_craft(OLC_CRAFT(d));
      OLC_CRAFT(d) = NULL;
      write_to_output(d, "Craft deleted.\r\n");
      if (CONFIG_OLC_SAVE) {
        craftedit_save_to_disk();
        write_to_output(d, "Crafts saved to disk.\r\n");
      } else {
        write_to_output(d, "Crafts saved to memory.\r\n");
      }
      cleanup_olc(d, CLEANUP_ALL);
      break;
    case 'n':
    case 'N':
      write_to_output(d, "Craft NOT deleted.\r\n");
      craftedit_disp_menu(d);
      break;
    }
    return;
  /*-------------------------------------------------------------------*/
  default:
    mudlog(BRF, LVL_BUILDER, TRUE, "SYSERR: OLC: Reached default case in craftedit_parse()!");
    write_to_output(d, "Oops...\r\n");
    break;
  }
}
#endif