30 Apr, 2012, Liko wrote in the 1st comment:
Votes: 0
Hello,

I installed Jamdogs Craftedit patch to TbaMud 3.62. It complies and runs smooth, but when you try to delete the craft from craftedit, it crashes the mud. I'd also like to note, there is no crafts made. I've also tried making two different crafts and tried deleting the other and it still crashed. Here is the GDB output. Also here is the link to the craft patch itself. Craft Patch.

Here is the GDB with void free_craft
#0  0xb7fe0832 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2
#1 0xb7e47bd1 in raise () from /lib/libc.so.6
#2 0xb7e494aa in abort () from /lib/libc.so.6
#3 0xb7e856ed in __libc_message () from /lib/libc.so.6
#4 0xb7e8bb41 in malloc_printerr () from /lib/libc.so.6
#5 0x08099555 in free_craft (this_craft=0x8309c88) at craft.c:112
#6 0x080f0570 in cleanup_olc (d=0x83024f8, cleanup_type=1 '{{uie-code}}1') at oasis.c:210
#7 0x080e3d0c in nanny (d=0x83024f8, arg=0xbffff1f0 "y") at interpreter.c:1299
#8 0x0809872e in game_loop (local_mother_desc=7) at comm.c:855
#9 0x08098e18 in init_game (argc=2, argv=0xbffff784) at comm.c:509
#10 main (argc=2, argv=0xbffff784) at comm.c:35
—————————————————————————-
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);
}


Here is line 112

free(this_craft->craft_name);


Also here is the print out for frame 5
= {vnum = 27695, result = 25193, craft_name = 0x62696c00 <Address 0x62696c00 out of bounds>, class_req = 1600349031, skill_req = 1869819507, min_skill = 12590,
min_level = 17, num_reqs = 137406448, obj_reqs = 0xb7fa53d8, next = 0x10}


I hope you all can help me finally crack this mystery.

Sincerely,
Liko
02 May, 2012, Sharmair wrote in the 2nd comment:
Votes: 0
Well, from this info it seems this_craft->craft_name is an invalid pointer. You do not show
enough code to see where that pointer is initialized (or set). My guess would be it is either
not initialized when the structure is created (leaving what ever just happened to be in those
memory locations, which did not happen to be a valid address) or it is being set with something
other then a memory address (the bytes happen to look alot like ascii chars). I would follow
the program flow from the time the structure is created and see how craft_name is used (set,
changed etc).

After taking a closer look at the data in that structure, I am pretty sure it is the first thing.
you are just allocating the structure on the heap and not ever initializing it. One way to fix
this would be to replace malloc with calloc where memory is allocated (calloc initializes the
memory to all zeros). Or right after the struct is allocated, set all the fields to valid values.
If this is C++ code that uses new (new does not normaly set any of the memory itself), the
constructor should initialize all the members of the struct (class) to valid values.
02 May, 2012, Liko wrote in the 3rd comment:
Votes: 0
Here is Craft.C

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


And here is Craft.H

/* **************************************************************************
* File: craft.h Part of tbaMUD *
* Version: 1.0 (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. *
************************************************************************** */

/** Craft Requirement Flags */
#define REQ_NON_EXPEND 0 /**< The object is not used up. Stays after creation */
#define REQ_IN_ROOM 1 /**< Object doesn't need to be in inventory. Can be in room */

/** The maximum number of crafts allowed in the MUD */
#define MAX_CRAFTS 100
/** The maximum number of requirements that a craft can have */
#define MAX_REQUIREMENTS 50

/** Craft utility macros */
#define CRAFT_VNUM(i) ((i) ? ((i)->vnum) : NOTHING)
#define CRAFT_NAME(i) ((i) ? ((i)->craft_name) : NULL)
#define CRAFT_RESULT(i) ((i) ? ((i)->result) : NOTHING)
#define CRAFT_CLASS_FLAGS(i) ((i) ? ((i)->class_req) : 0)
#define CRAFT_SKILL_REQ(i) ((i) ? ((i)->skill_req) : 0)
#define CRAFT_MIN_SKILL(i) ((i) ? ((i)->min_skill) : 0)
#define CRAFT_MIN_LEVEL(i) ((i) ? ((i)->min_level) : 0)
#define CRAFT_NEXT(i) ((i) ? ((i)->next) : NULL)

#define CRAFT_CLASS_FLAGGED(i,j) ((i) ? IS_SET((i)->class_req, (1 << (j))) : 0)

/** Craft requirement utility macros */
#define CRAFT_NUM_REQS(i) ((i) ? ((i)->num_reqs) : 0)
#define CRAFT_REQ_OBJ(i,j) ((i) && (i)->obj_reqs ? ((i)->obj_reqs[j]).onum : NOTHING)
#define CRAFT_REQ_NUM(i,j) ((i) && (i)->obj_reqs ? ((i)->obj_reqs[j]).num_required : 0)
#define CRAFT_REQ_FLAGS(i,j) ((i) && (i)->obj_reqs ? ((i)->obj_reqs[j]).req_flags : NULL)

#define CRAFT_REQ_FLAGGED(i,j,k) ((i) && (i)->obj_reqs ? IS_SET_AR(((i)->obj_reqs[(j)]).req_flags, (k)) : 0)

/** Craft requirement flags */
#define C_REQ_NON_EXPEND 0 /**< Required object isn't expended when crafting */
#define C_REQ_IN_ROOM 1 /**< Object may be in the room or players inventory */

#define NUM_C_REQ_FLAGS 2

/** General Craft Command - subcommands for interpreter.c */
#define SCMD_BREW 1 /**< Subcommand for the command 'brew' */
#define SCMD_SCRIBE 2 /**< Subcommand for the command 'scribe' */
#define SCMD_CONSTRUCT 3 /**< Subcommand for the command 'construct' */

/** The requirement structure: objects required to craft the result */
struct craft_requirement
{
obj_vnum onum; /**< The vnum of the required object */
int num_required; /**< How many of the object are required? */
int req_flags[RQ_ARRAY_MAX]; /**< Requirement flags for this object */
};

/** The main craft structure: All information about the craft */
struct craft_data
{
craft_vnum vnum; /**< The unique vnum for this craft */
obj_vnum result; /**< The object result of crafting */
char *craft_name; /**< The name used to craft this item */
int class_req; /**< Class requirements (32-bit flags) */
int skill_req; /**< Skill required for this crafting */
int min_skill; /**< Minimum required craft skill level */
int min_level; /**< Minimum required player level */
int num_reqs; /**< Number of requirements in the list */
struct craft_requirement *obj_reqs; /**< Object requirements */
struct craft_data *next; /**< Linked List: next craft in the list */
};

/** External Function prototypes in craft.c */
struct craft_data *new_craft(void);
struct craft_data *get_craft_by_name(char *cr_name);
struct craft_data *get_craft_by_vnum(craft_vnum cr);
void remove_craft_from_list(struct craft_data *this_craft);
void free_craft(struct craft_data *this_craft);
void free_craft_requirements(struct craft_data *this_craft);
void copy_craft(struct craft_data *to_craft, struct craft_data *from_craft);
void add_craft(struct craft_data *new_craft);
void add_craft_requirement(struct craft_data *this_craft, obj_vnum o_num, int num_objs, int *rq_flags);
int load_crafts(void);
int save_crafts(void);
void show_crafts(struct char_data *ch, char *arg);
struct obj_data *check_obj_in_inventory(struct char_data *ch, obj_rnum item_rnum);
int count_objs_in_inventory(struct char_data *ch, obj_rnum item_rnum);
ACMD(do_crafts);
bool known_craft(struct char_data *ch, craft_rnum c_num);
bool check_craft_kit(struct obj_data *kit, int num);
bool reduce_craft_kit(struct obj_data *kit, int num);
bool has_craft_requirements(struct char_data *ch, craft_rnum c_id);
void remove_all_craft_requirements(struct char_data *ch, craft_rnum c_id);
ACMD(do_gen_craft);

/** NOTE: External Function prototypes for craftedit OLC are in oasis.h */

/** Global variables: extern if not in craft.c */
#ifndef __CRAFT_C__
extern struct craft_data *craft_list;
#endif
03 May, 2012, Kline wrote in the 4th comment:
Votes: 0
Liko said:
Here is Craft.C

<snip large file>

Pastebin -> This way!
03 May, 2012, Kaz wrote in the 5th comment:
Votes: 0
All the accesses of craft_name look reasonably kosher. A little more complicated than they needed to be, but no real problem.

My hunch is in the arrangement of the code in case CRAFTEDIT_DEL_CRAFT. In the 'Y' case, the craft is freed. Looking at free_craft, this deletes the craft name, then frees the craft. I note that this seems to be the only point that craft_name is left with an indeterminate value. Not a problem in and of itself, since craft_name should not be accessed after this point. The code then goes on to free the craft proper. In the calling context, OLC_CRAFT(d) now yields also an invalid value (a freed pointer)

On reflection, I think a good case of the bug is that whatever OLC_CRAFT(d) points at should probably be assigned NULL at this point:

This then goes on to call olc_cleanup(), which your stack trace shows also calls free_craft(). It doesn't seem to be a problem at this point, though olc_cleanup gets called again during the nanny() function. It may be because of the CLEANUP_ALL, or something else; I don't have the olc_cleanup() function on hand. But the above is still my guess.
03 May, 2012, Liko wrote in the 6th comment:
Votes: 0
Cleanup_Olc does call for Craft to be free. I posted the cleanup_olc code below. That was my first thought when the bug first happen.

void cleanup_olc(struct descriptor_data *d, byte cleanup_type)
{
if (d->olc == NULL)
return;

if (OLC_ROOM(d)) {
switch (cleanup_type) {
case CLEANUP_ALL:
/* free(OLC_SCRIPT(d)) equivalent */
free_proto_script(OLC_ROOM(d), WLD_TRIGGER);
free_room(OLC_ROOM(d));
break;
case CLEANUP_STRUCTS:
free(OLC_ROOM(d));
break;
case CLEANUP_CONFIG:
free_config(OLC_CONFIG(d));
break;
default: /* The caller has screwed up. */
log("SYSERR: cleanup_olc: Unknown type!");
break;
}
}

if (OLC_OBJ(d)) {
free_object_strings(OLC_OBJ(d));
free(OLC_OBJ(d));
}

if (OLC_MOB(d))
free_mobile(OLC_MOB(d));

if (OLC_ZONE(d)) {
if (OLC_ZONE(d)->builders)
free(OLC_ZONE(d)->builders);
if (OLC_ZONE(d)->name)
free(OLC_ZONE(d)->name);
if (OLC_ZONE(d)->cmd)
free(OLC_ZONE(d)->cmd);
free(OLC_ZONE(d));
}
if (OLC_SHOP(d))
free_shop(OLC_SHOP(d));
if (OLC_QUEST(d)) {
switch (cleanup_type) {
case CLEANUP_ALL:
free_quest(OLC_QUEST(d));
break;
case CLEANUP_STRUCTS:
free(OLC_QUEST(d));
break;
default:
break;
}
}

if (OLC_ACTION(d)) {
switch(cleanup_type) {
case CLEANUP_ALL:
free_action(OLC_ACTION(d));
break;
case CLEANUP_STRUCTS:
free(OLC_ACTION(d));
break;
default:
break;
}
}
if (OLC_HELP(d)) {
switch(cleanup_type) {
case CLEANUP_ALL:
free_help(OLC_HELP(d));
break;
case CLEANUP_STRUCTS:
free(OLC_HELP(d));
break;
default:
break;
}
}

if (OLC_IBT(d)) {
free_olc_ibt(OLC_IBT(d));
OLC_IBT(d) = NULL;
}


if (OLC_STORAGE(d)) {
free(OLC_STORAGE(d));
OLC_STORAGE(d) = NULL;
}
if (OLC_TRIG(d)) {
free_trigger(OLC_TRIG(d));
OLC_TRIG(d) = NULL;
}

if(OLC_PREFS(d)) {
free(OLC_PREFS(d));
OLC_PREFS(d) = NULL;
}

if(OLC_PREFS(d)) {
free(OLC_PREFS(d));
OLC_PREFS(d) = NULL;
}

if (OLC_CRAFT(d)) {
free_craft(OLC_CRAFT(d));
OLC_CRAFT(d) = NULL;
}
if (d->character) {
REMOVE_BIT_AR(PLR_FLAGS(d->character), PLR_WRITING);
act("$n stops using OLC.", TRUE, d->character, NULL, NULL, TO_ROOM);

if (cleanup_type == CLEANUP_CONFIG)
mudlog(BRF, LVL_IMMORT, TRUE, "OLC: %s stops editing the game configuration", GET_NAME(d->character));
else if (STATE(d) == CON_TEDIT)
mudlog(BRF, LVL_IMMORT, TRUE, "OLC: %s stops editing text files.", GET_NAME(d->character));
else if (STATE(d) == CON_TEDIT)
mudlog(BRF, LVL_IMMORT, TRUE, "OLC: %s stops editing text files.", GET_NAME(d->character));
else if (STATE(d) == CON_HEDIT)
mudlog(CMP, LVL_IMMORT, TRUE, "OLC: %s stops editing help files.", GET_NAME(d->character));
else
mudlog(CMP, LVL_IMMORT, TRUE, "OLC: %s stops editing zone %d allowed zone %d", GET_NAME(d->character), zone_table[OLC_ZNUM(d)].number, GET_OLC_ZONE(d->character)$

STATE(d) = CON_PLAYING;
}

free(d->olc);
d->olc = NULL;
}
03 May, 2012, Kaz wrote in the 7th comment:
Votes: 0
Well there you go. olc_cleanup() will call free_craft() on the already-freed-but-non-NULL pointer. This will eventually go horribly wrong.

=case 'Y':
= /* Free craft automatically checks the craft list and removes matching vnums */
= free_craft(OLC_CRAFT(d));
+ OLC_CRAFT(d) = NULL;
03 May, 2012, Liko wrote in the 8th comment:
Votes: 0
fixed the crashes and is successfully deleting crafts. Now when someone disconnects while in craftedit the mud crashes

GDB seems to be pointing to cleanup_olc line 222
#0  0x08097fa5 in act (str=0x8164728 "$n stops using OLC.", hide_invisible=1, ch=0x8317310, obj=0x0, vict_obj=0x0, type=1) at comm.c:2642
#1 0x080f29d2 in cleanup_olc (d=0x830ff60, cleanup_type=1 '\001') at oasis.c:222
#2 0x080987e7 in close_socket (d=0x830ff60) at comm.c:2142
#3 0x08099867 in game_loop (local_mother_desc=7) at comm.c:816
#4 0x0809a8d8 in init_game (argc=2, argv=0xbffff784) at comm.c:510
#5 main (argc=2, argv=0xbffff784) at comm.c:352


and Line 222 from cleanup_olc
act("$n stops using OLC.", TRUE, d->character, NULL, NULL, TO_ROOM);
09 May, 2012, Liko wrote in the 9th comment:
Votes: 0
I fixed the crashing.

It wasn't reckoning CON_CRAFTEDIT as a state

in structs.h

#define LAST_OLC_STATE  CON_PREFEDIT


changed to:
#define LAST_OLC_STATE  CON_CRAFTEDIT


I do plan to release the bug free craft edit to MUDBYTES :) Thanks for all your help :)
29 May, 2012, halmud wrote in the 10th comment:
Votes: 0
Liko,

When you let a mortal try to craft, are you having an issue?

I can set the difficulty to 0, 20, 50, doesn't matter, they fail just as much.

You fail miserably, ALLLL THE TIME :P

The only way I got it to ever let a non-imm to craft with success is to change

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

(changing the 101 to 0)

Of course I know this is not a fix, but just something I did during my hunt for the reasons it didn't work.
29 May, 2012, halmud wrote in the 11th comment:
Votes: 0
I also noticed that players don't "skill up" where as you may set 10 skill needed or higher, so how do they ever get higher in skill. I think the whole thing is kinda mucked.
29 May, 2012, halmud wrote in the 12th comment:
Votes: 0
Been talking with Jamdog, trying to figure it out. I'll post any results.
29 May, 2012, halmud wrote in the 13th comment:
Votes: 0
Ok so we figured it out, and I will do my best to explain how to fix the problem of players not being able to craft.

First - Under skill initialize in Class.c

Example:

case CLASS_MAGIC_USER:
SET_SKILL(ch, SKILL_BREW, 10);
SET_SKILL(ch, SKILL_CONSTRUCT, 10);
SET_SKILL(ch, SKILL_SCRIBE, 10);
break;

Then in init_spell_levels

spell_level(SKILL_BREW, CLASS_MAGIC_USER, 1);
spell_level(SKILL_CONSTRUCT, CLASS_MAGIC_USER, 1);
spell_level(SKILL_SCRIBE, CLASS_MAGIC_USER, 1);

Besides this make sure you have the crafto set in spell_parser.c

Example:

crafto(SKILL_BREW, "brew");

This combined with the other information from the patch, should successfully finish your craft code.
Of course you may choose to make some alterations and tighten things up a bit, but this should get you a clean
compile and atleast give you somewhere to start.

Hopefully this helps anyone that was having problems. Thanks!
30 May, 2012, Liko wrote in the 14th comment:
Votes: 0
Everything is successfully in the patch I posted here. You have to look at the one in the code repository.
30 May, 2012, Liko wrote in the 15th comment:
Votes: 0
I found a small little glitch:

replace in craft.c in do_gen_craft:
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;


with:

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;


The first block of code was causing it to output as NULL constructs EXAMPLE!
31 May, 2012, halmud wrote in the 16th comment:
Votes: 0
:)
31 May, 2012, Liko wrote in the 17th comment:
Votes: 0
Also updated the patch.
0.0/17