/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefiting.  We hope that you share your changes too.  What goes       *
 *  around, comes around.                                                  *
 ***************************************************************************/

#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"

/* 
===========================================================================
This snippet was written by Erwin S. Andreasen, erwin@pip.dknet.dk. You may 
use this code freely, as long as you retain my name in all of the files. You
also have to mail me telling that you are using it. I am giving this,
hopefully useful, piece of source code to you for free, and all I require
from you is some feedback.

Please mail me if you find any bugs or have any new ideas or just comments.

All my snippets are publically available at:

http://pip.dknet.dk/~pip1773/

If you do not have WWW access, try ftp'ing to pip.dknet.dk and examine
the /pub/pip1773 directory.
===========================================================================
Note that function prototypes are not included in this code - remember to add
them to merc.h yourself.

Last update: October 27, 1995

Should work on : MERC2.2
   
   The object lister writes every object *reset* on the mud to a file. The
   list is created in comma-delimited format, perfect for importing into
   a spreadsheet or database and further analyzing, allowing to quickly answer
   questions like: Which weapon does the most damage? What spells are available
   in wands? Which levels have the least items? What resets where and on who?
   Which equipment is missing or has too many wear-bits? Etc., etc.

   The function has been tested with a MERC2.2 MUD. It should work with
   ROM 2.3 (though it was not tested). It cannot be used with The Isles right
   away - The Isles uses a reset system that is a bit different. I do not
   know about Envy.
   
   Note: There is also a ROM 2.4+ version available.
   
LIMITATIONS
===========

    P supports only one level of recursion, i.e.
    obj inside something on a mob
	NOT obj inside obj inside obj on a mob
	if obj is inside obj inside obj, room vnum cannot be found

INSTRUCTIONS
============

to customize this lister to your Merc MUD:

1) Add relevant APPLY_XXX to number_app and bool_app (see below)

2) Add relevant other item_types to item_type
   Also add them in the big switch () in printobj (around line 600), which
   prints object-specific data.
   Perhaps change your item_type_name to instead call THIS item_type
   so new item types have only to be added in this place only.

3) Add other wear_locations to wear_table

4) Add other object specific information that does not fit anywhere in the
   get_other_information() function
   
5) Add other flags to the extra_flags_name table

6) Add objdump entry to your help file. objdump shows that entry if wrong
   parameters are issued.

7) Make attack_table global in fight.c (right now it is local to damage).

FIELD KEY
=========

1	Short Desc		Contains short_desc of the object

2	Keywords		Keywords, separated by a space

3	Level			The level the object is reset at (can vary +/-2)

4	Vnum			The vnum of the object

5	Room			The vnum of the room the object resets in

6	Location		Text desc of the location (on mob X, in room: Y, inside Z)

7	Flags			GENVMDRghdlvb#*
					anti-Good, anti-Evil, anti-Neutral, inVisible, Magic, noDrop,
					noRemove, Glowing, Humming, Dark, Lock, eVil, Bless, Shop.
					If set, the letter is there, if not, a '-' appears.
					Important flags are in uppercase.
					The last flag is set, if the object resets on a shopkeeper
					and the shopkeeper sells that particural object.
					Note that those objects' levels are differently derived.
					* denotes a reboot-only object. An object is reboot if
					either it or the mob it resets on has a reset-count of
					-1 (note that this is not a standard MERC feature).
					
8	Weight			Weight of object

9	Value			Value/cost of object

10	Worn			Where the object can be worn. Be on lookout for objects
					without Take flag, if they are armor, weapons etc. Also
					watch out for Weapons with other than (Take Wield)

11	Item Type		Armor, Wand, Staff, Weapon etc.

12	AC/SLevel		For Armor: contains AC
					For wand,staff,pill,potion,scroll: contains spell level
					For containers: Contains key number.
					For drink containers and food: non-zero means it is 
					 poisoned
					

13	Capacity		For wand/staves contains number of charges
					For containers (drink, too) contains capacity
					For lights: contains number of hours it can burn (more
					 explanation is given in the Spells field).
					For food: number of hours the food is 'worth'

14	Spells			For spell-using objects, contains spell(s) cast by obj.
					For light: contains explanation on Capacity (dead,
					 unlimited, limited).
					For containers: contains flags (lockable, closeable etc.)
					For drink containers: contains liquid type.

15  Affects			Contains all affects of the obj, in one long field. Useful
					if you have to produce a text-only version of the list.
					
16+	Affects			Contains modifier to that affect

??	Bool affects	For every affect in bool_app contains the name of that
					affect if that affect exists on the object

??	Other			Contains other information, gathered from the
					get_other_information function.

Use objdump command, then load the file in your favorite database or spread-
sheet. I use Excel. Sorting by: Item Type, Worn, Level gives me the best
results. The start analyzing. Find objects with silly names, misspelled names,
missing Take worn_flags, too many worn_flags, too high affects (just sort after
damage roll or hit roll field!). Or perhaps add some formula to evaluate the
reality of an object (i know Farside had one in its builder docs?). Try also
searching for a '!' - it indicates objects resetting on non-existing mobs and
other errors (but your bug file should hopefullly already have told you that).

If you need to distribute a pure-text form version of the database, there is a
field which includes all the affects. You will still need some spreadsheet or
database to export it though. I do not plan on making the function do pure
text (i.e. not comma delimetered) output - it is easier for you to use a spread-
sheet to organize the database to fit exactly your needs.

Note that the command is best run just after reboot, with no players on, so 
the contains for P resets can be found properly. The P resets look for the
first and best object of the appropriate type, so if a player has it, the
file will say that the object resets on a player :()

FUNCTIONS AND STRUCTURES CONTAINED WITHIN THIS FILE
===================================================

const sh_int number_app [];

 contains a list of APPLY_XXX constants that are to be printed, with the number
 they apply with (e.g. damage roll). Not all have to be here, some like
 APPLY_AGE, APPLY_GOLD or the like are irrelevant.

const sh_int bool_app [];

 Contains a list of APPLY_XXX constants that are just to be printed, where the
 number is not relevant (e.g APPLY_INVIS or APPLY_DETECT_INVIS, where level
 does not matter).
 
 Note: This may not apply to your MUD, but it is how I have implemented
 permanent spell effects.

char * get_other_information (OBJ_INDEX_DATA*)

 Returns whatever else is relevant for this object. Special functions, powers,
 etc. By default this function returns nothing, it is up to you to add something
 you feel is relevant.
 
struct obj_reset;

 A list containing pairs of a obj vnum and a level. Used by printobj
 to avoid printing the same object at the same level several times to the file.
 If the same obj resets at several _different_ levels, they are printed.
 
dispose_list()
new_list()
add_to_list()
is_in_list()

 Functions to manipulate such a list. Not much to say about those...
  
const char * wear_table [];

 Contains names of all possible places to wear eq. You should update this
 with your own locations.

const char * wear_name (sh_int);

 When given a wear_flag, returns a string containining the places the obj
 with such a wear flag can be worn. Standard MERC did not have such a function,
 so I had to make one myself.

const char * item_type (sh_int);

 This version of item_type_name does the same, but takes just a sh_int as a
 parameter - the handler.c:item_type_name takes an OBJ_DATA*, and what I 
 needed was to find the type name of a OBJ_INDEX_DATA*
 
int get_affect_total (AFFECT_DATA*, sh_int);

 Returns the total modifier of a certain affect type in this linked list
 of AFFECT_DATAs.
 
bool is_affect (AFFECT_DATA*, sh_int);

 Returns TRUE if a given affect is in the list.

char *extra_flag_string (int)

 Returns a string like this: "GEVN-----g---' for a given object's extra_flag
 string. looks up in the extra_flag_names table.
 
 The extra_flag_names table consists of constants for the flags and a letter,
 for each of the flags you want displayed.

char * affect_str (AFFECT_DATA*, char buffer*);

 Returns a string in the form "+40hp,+2dex,-1wis" containing all the number_app
 and bool_app affects in the given affect-list. Useful for quick overview.
 As the list can get pretty long, the caller must provide a buffer of reasonable
 length. (MAX_STRING_LENGTH should do?)

printheader (FILE*)

 Prints the header rows

printobj (FILE*, OBJ_RESET*, where, sh_int, sh_int, sh_int, sh_int, sh_int);

 Prints out the specified object into FILE*. Arguments depend on whether the
 obj is reset on ground, inside a container, or on a mob. Check function
 source code for more info.

printarea (FILE*, AREA_DATA*);

 Prints the objs reset in that area to FILE*, which has to have been
 opened in mode "w" before then.

do_objdump (CHAR_DATA*, char *);

 Core function, calls printarea for either all or just one area, depending
 on users' wishes. Syntax is:
 
 objdump <ALL|AREA> <filename>
 
 AREA just prints the area the character is in. Undefined result if 
 ch->in_room is NULL (but when is it?)
 
 ALL runs through the list of areas and dumps every one to the file.

	
*/

/* Now, without further interruptions... the code itself */

extern AREA_DATA* area_first; /* snarf the local fro db.c */
extern char * const attack_table[]; /* snarf the attack table from fight.c */
typedef enum {on_ground, inside, on_mob} where;
typedef struct obj_reset OBJ_RESET;

#define MAX_NUMBER_APP   (sizeof(number_app) / sizeof (number_app[0]))
#define MAX_BOOL_APP     (sizeof(bool_app)   / sizeof (bool_app[0]))
#define MAX_EXTRA_FLAG (sizeof(flag_names) / sizeof (flag_names[0]))

struct obj_reset
{
	sh_int vnum;
	sh_int level;
	OBJ_RESET* next;
};

struct extra_flag_names
{
	char name;
	int value;
};

/* order must be the same as the ITEM_TAKE, ITEM_WEAR_XXXX #defines */
const char * wear_table [] =
{
	"take", "finger", "neck", "body", "head", "legs",   "feet", "hands",
	"arms", "shield", "about","waist","wrist","wield",  "hold"
};

/* applys where value matters. They are displayed in exactly this order: */
const sh_int number_app [] =
{
	APPLY_HITROLL,
	APPLY_DAMROLL,
	APPLY_HIT,
	APPLY_MANA,
	APPLY_MOVE,
	APPLY_AC,
	APPLY_STR,
	APPLY_DEX,
	APPLY_INT,
	APPLY_WIS,
	APPLY_CON,
};

/* applys which are either 'on' or 'off' */
const sh_int bool_app [] =
{
	APPLY_NONE,
};

/* extra flags and corresponding characters in the flag list */
const struct extra_flag_names flag_names [] =
{
	/* important flags, in uppercase */
	{ 'G', ITEM_ANTI_GOOD},
	{ 'E', ITEM_ANTI_EVIL},
	{ 'N', ITEM_ANTI_NEUTRAL},
	{ 'V', ITEM_INVIS},
	{ 'M', ITEM_MAGIC},
	{ 'D', ITEM_NODROP},
	{ 'R', ITEM_NOREMOVE},
	/* less important flags, in lowercase */
	{ 'g', ITEM_GLOW },
	{ 'h', ITEM_HUM  },
	{ 'd', ITEM_DARK }, /* Diku? */
	{ 'l', ITEM_LOCK },
	{ 'v', ITEM_EVIL },
	{ 'b', ITEM_BLESS}	
};

/* return other information relevant to your MUD */
char * get_other_information (OBJ_INDEX_DATA *obj)
{
	return "";
}
	

/* manipulation of a small list to hold vnums & levels they were reset at
   to avoid redundancy */   
void dispose_list (OBJ_RESET *list)
{
	OBJ_RESET* next;
	
	for ( ; list != NULL; list = next)
	{
		next = list->next;
		free (list);
	}
}

OBJ_RESET * new_list (void) /* create a new list */
{
	OBJ_RESET * list;
	/* create an empty list head */	
	list = (OBJ_RESET*) malloc (sizeof(OBJ_RESET));	
	list->next = NULL;	
	return list;	
}

void add_to_list (OBJ_RESET *list, sh_int vnum, sh_int level)
{
	OBJ_RESET * new_item;
	new_item = (OBJ_RESET*) malloc (sizeof(OBJ_RESET));
	new_item->next = list->next;
	list->next = new_item;
	new_item->vnum = vnum;
	new_item->level = level;
}

bool is_in_list (OBJ_RESET *list, sh_int vnum, sh_int level)
{
	for (list = list->next ; list != NULL; list = list->next)
		if ((list->vnum == vnum) && (list->level == level))
			return TRUE;
	return FALSE;
}


/* returns a pointer to a static buffer containing the places where obj
   can be worn  */ 
char* wear_name (sh_int wear_flags)
{
	static char buffer [256];
	unsigned long int i,pos;

	strcpy (buffer, "");	
	pos = 0;
	
	for (i = ITEM_TAKE; i <= ITEM_HOLD; i = i << 1, pos++)			
		if (wear_flags & i) /* can be worn in that place */
		if (pos < ( sizeof(wear_table) / sizeof(wear_table[0])) )
		{
			strcat (buffer, wear_table[pos]);
			strcat (buffer, " ");
		}
		else
		{
			strcat (buffer, "buggy-location");
			return buffer;
		}
		
	if (strlen(buffer)) /* if there is actually SOMETHING in the buffer */
		if ( buffer[ strlen(buffer)-1] == ' ' )
			buffer[strlen(buffer)-1] = '\0'; /* no terminating ' ' */
	
	return buffer;	
}

/* standard item_type_names accepts only OBJ_DATA* */
char *item_index_type_name (sh_int item_type)
{
	switch (item_type)
	{
    case ITEM_LIGHT:		return "light";
    case ITEM_SCROLL:		return "scroll";
    case ITEM_WAND:			return "wand";
    case ITEM_STAFF:		return "staff";
    case ITEM_WEAPON:		return "weapon";
    case ITEM_TREASURE:		return "treasure";
    case ITEM_ARMOR:		return "armor";
    case ITEM_POTION:		return "potion";
    case ITEM_FURNITURE:	return "furniture";
    case ITEM_TRASH:		return "trash";
    case ITEM_CONTAINER:	return "container";
    case ITEM_DRINK_CON:	return "drink container";
    case ITEM_KEY:			return "key";
    case ITEM_FOOD:			return "food";
    case ITEM_MONEY:		return "money";
    case ITEM_BOAT:			return "boat";
    case ITEM_CORPSE_NPC:	return "npc corpse";
    case ITEM_CORPSE_PC:	return "pc corpse";
    case ITEM_FOUNTAIN:		return "fountain";
    case ITEM_PILL:			return "pill";
/*    case ITEM_PORTAL:   	return "magical portal"; */
    }

    bug( "Item_type_name: unknown type %d.", item_type );
    return "(unknown)";
}

/* how much affect_num is there in pAffectr */
int get_affect_total (AFFECT_DATA* pAffect, int affect_num)
{
	int total = 0;
	
	for ( ; pAffect != NULL; pAffect = pAffect->next)
		if (pAffect->location == affect_num)
			total = total + pAffect->modifier;
			
	return total;
}

/* is affect_num contained in pAffect somewhere ? */
bool is_affect (AFFECT_DATA* pAffect, int affect_num)
{
	for ( ; pAffect != NULL; pAffect = pAffect->next)
		if (pAffect->location == affect_num)
			return TRUE;
	
	return FALSE;
}


/* returns a string of extra flags */
/* strings looks like this: -----V--b-E, i.e.. set flags have their letter,
   flags not set, have just a '-'
 */
  
char * extra_flag_string (int extra)
{
	int i;
	static char buffer [MAX_EXTRA_FLAG+1]; /* 1 for each flag + term NUL */

	for (i = 0; i < MAX_EXTRA_FLAG; i++)
		if (IS_SET (extra, flag_names[i].value))
			buffer[i] = flag_names[i].name; /* it's my buffer and I'll do whatever I want - nyah! */
		else
			buffer[i] = '-'; /* flag not there */

	/* insert the terminating NUL ! */
	
	buffer[MAX_EXTRA_FLAG] = '\0';
	return buffer;			
}

/* returns a string with all relevant (i.e. contained in number_app/bool_app
   affects in pAffect. Only three first letters of location-name are used.
   Example: +4dam,+1dex,-1str
*/
void affect_str (AFFECT_DATA* pAffect, char* buffer)
{
	int i,total;
	char buf1 [20]; /* just a small buffer, should not hold more than +9999999999nam, */
	
	strcpy (buffer, ""); /* clear the buffer */

	for (i = 0; i < MAX_NUMBER_APP; i++)
	{	
		total = get_affect_total(pAffect, number_app[i]);
		if (total) /* total != 0 */
		{
			/* i hope this works on non-linux? */
			/* 'arm' abbrev for armor class is not good BTW */
			sprintf (buf1, "%+d%-2.3s,", total,affect_loc_name(number_app[i])); /* there is no itoa on unix ? */
			strcat (buffer, buf1);
		}
	}
	/* then, one big lump of boolean affects */

	for (i = 0; i < MAX_BOOL_APP; i++)
	{
		if ( is_affect (pAffect, bool_app[i]) )
		{
			strcat (buffer, affect_loc_name(bool_app[i]));
			strcat (buffer, ",");
		} /* if */		
	} /* for */
	
	if (strlen(buffer) > 0) /* if we actually have printed something */
		buffer[strlen(buffer)-1] = '\0';		
} /* affect_str */


/* print a comma-delimetered line into a file */
void printobj (FILE *fp, OBJ_RESET *list, where location, sh_int vnum_what, 
               sh_int vnum_where, sh_int vnum_who, sh_int vnum_inside,
               bool reboot)
{
	char buf[MAX_STRING_LENGTH];
	char buf1[MAX_STRING_LENGTH];
	MOB_INDEX_DATA *mob;
	int level,i;
	bool shopkeeper = FALSE;

	OBJ_INDEX_DATA *obj = get_obj_index (vnum_what);

if (!obj) /* could not find that vnum! */
{
	printf ("printobj: could not find obj %d\n",vnum_what);
	return;
} /* if */

switch (location) /* write something about WHERE the objects is reset */
{

default:
	bug ("printobj: invalid location:%d",location);
	return;
	
case on_ground: /* where is used */
{		
	ROOM_INDEX_DATA* room = get_room_index (vnum_where);
	
	if (room) /* room != NULL */
		sprintf (buf1, "\"in room: %s\"", room->name);
	else
		sprintf (buf1, "\"nowhere(!)\"");
	break;
}

case inside:    /* inside is used. where is derived */
{
	OBJ_INDEX_DATA* obj_inside_index;
	OBJ_DATA* obj_inside;
	
	
	obj_inside_index = get_obj_index (vnum_inside);
	
	if (!obj_inside_index) /* could not even find the obj index?! */
	{
		bug ("printobj: container-obj %d non-existant",vnum_inside);
		return;
	}
	
	/* find an instance of the object. Note we can risk finding one on a player.. :( */
	
	obj_inside = get_obj_type (obj_inside_index);
	
	if (!obj_inside) /* couldnt find where one of those is */
		vnum_where = 0; /* could not locate */
	else
		if (obj_inside->in_room) /* check for valid room */
			vnum_where = obj_inside->in_room->vnum;
		else /* not in a room.. check inventory of a mob ? */
			if (obj_inside->carried_by && obj_inside->carried_by->in_room)
				vnum_where = obj_inside->carried_by->in_room->vnum;
			
	if (vnum_where)
	{
		ROOM_INDEX_DATA* room = get_room_index (vnum_where);
		if (room)
			if (obj_inside->carried_by) /* the container is carried by a mob */
				sprintf (buf1, "\"inside %s on %s\"", obj_inside->short_descr, 
								IS_NPC (obj_inside->carried_by)
								? obj_inside->carried_by->short_descr
								: "(a player)" );
			else
				sprintf (buf1, "\"inside %s in %s\"", obj_inside->short_descr, room->name);
		else
			sprintf (buf1, "\"inside %s somewhere(!)\"", obj_inside->short_descr);
	} /* if vnum_where */
	else
		sprintf (buf1, "\"inside something(!), somewhere(!)\"");				
				
	break;
} /* case inside */

case on_mob:    /* where, who is used */
{
	mob = get_mob_index (vnum_who);	
	if (mob)
		sprintf (buf1, "\"on %s\"", mob->short_descr);
	else
		sprintf (buf1, "\"on someone(!)\"");

	break;
} /* on mob */

} /* switch where */

mob = get_mob_index (vnum_who); /* vnum_who */
	
if (mob != NULL)
{
	level = URANGE(0, mob->level-2, LEVEL_HERO);  
	/* level of obj ranges from (mob - 4) to (mob - 0), clipped to 0-HERO */
	/* however, this number is number_fuzzied, resulting in -1 (!) to HERO+1 */
}
else
	level = 0; /* when there is no last mob, level is set to 0 */

/* check if item is the part of a shop's inventory, and if so adjust level */
/* snarfed from reset_area, changed to only average level */
/* note that we can have a valid mob pointer even if the item is not to be 
   given to a shopkeeper, therefore check for location
   Problem: Are items equipped on shopkeeper set to shop-level or normal level?
   Seems to be the former..
 */
    
if ( (location == on_mob) &&  
	 (mob != NULL) && 
	 (mob->pShop != NULL)) /* loaded on mob, valid mob, valid shopkeeper */
{

	shopkeeper = TRUE;
	
    switch ( obj->item_type )
    {
    default:                level =  0; break;
    case ITEM_PILL:         level =  5; break; /* 0 - 10 */
    case ITEM_POTION:       level =  5; break; /* number_range(  0, 10 ); */
    case ITEM_SCROLL:       level = 10; break; /* number_range(  5, 15 ); */
    case ITEM_WAND:         level = 15; break; /* number_range( 10, 20 ); */
    case ITEM_STAFF:        level = 20; break; /* number_range( 15, 25 ); */
    case ITEM_ARMOR:        level = 10; break; /* number_range(  5, 15 ); */
    case ITEM_WEAPON:       level = 10; break; /* number_range(  5, 15 ); */
    } /* switch */
}
/* check if we already have written an object like this of the same level */
if (is_in_list (list, vnum_what, level))
	return; /* only write obj of same level once */
	
add_to_list (list, vnum_what, level);

/* short descr, keywords, level, vnum_what, vnum_where */

sprintf (buf, "\"%s\",\"%s\",%d,%d,%d,", 
	    obj->short_descr, obj->name, level, vnum_what, vnum_where);
strcat (buf, buf1);
strcat (buf, ","); /* append textual description of place where it is reset */

/* append extra-flags. if item on a shopkeeper, append the extra # flag,
   if item reboot, append * (asterix) */
sprintf (buf1, "\"%s%c%c\",", extra_flag_string (obj->extra_flags),
		 shopkeeper ? '#' : '-',
		 reboot     ? '*' : '-');
strcat (buf, buf1);

/* weight, cost, wear flags */
sprintf (buf1, "%d,%d,\"(%s)\",", obj->weight, obj->cost,wear_name(obj->wear_flags)); /* weight&value of object */
strcat (buf, buf1);

/* data specific for objects */

/* name, AC, charges, level, spell1, spell2, spell3 */
switch (obj->item_type)
{
case ITEM_WEAPON: /* weapon (damage_type) */	
	sprintf (buf1, "\"weapon(%s)\",", attack_table[obj->value[3]]); /* damtype */
	strcat (buf1, "0,0,\"\","); /* charges, spell level, empty spells */
	break; /* weapon */

case ITEM_ARMOR: /* armor, AC */
	sprintf (buf1, "\"armor\",%d,0,\"\",", 
		 	 obj->value[0] 
		 	); /* places to wear, AC*/
	break; /* armor */
	
case ITEM_WAND: /* wand|staff, spell-level, charges, spellname */
case ITEM_STAFF:
	sprintf (buf1, "\"%s\",%d,%d,\"%s\",", 
			 item_index_type_name(obj->item_type),
			 obj->value[0],
			 obj->value[1],
			 ((obj->value[3] > 0) && (obj->value[3] < MAX_SKILL)) ?
			 skill_table[obj->value[3]].name
			 : "unknown spell"
	        ); 
	break; /* wand, staff */

case ITEM_PILL:
case ITEM_SCROLL:
case ITEM_POTION: /* pill|scroll|potion, spell-level, 0, spell-name(s) */
	sprintf (buf1, "\"%s\",%d,0,\"", 
			 item_index_type_name(obj->item_type),
			 obj->value[0]
			);
			
	/* pill/scroll/potion, spell level, no charges, */
	for (i = 1; i < 4; i++)
	{
		/* valid skills range from 1 .. MAX_SKILL-1 */
		if ( (obj->value[i] > 0) && (obj->value[i] < MAX_SKILL) ) /* spell name, if any */
		{
			strcat (buf1, skill_table[obj->value[i]].name);
			strcat (buf1, ","); /* add a comma after, should be OK inside a string ?? */
		}
	}
		if (buf1[strlen(buf1)-1] == ',')
			buf1[strlen(buf1)-1] = '\0'; /* remove trailing comma */
		strcat (buf1, "\","); /* terminating quote, only one! */
		
	break; /* pill, scroll, potion */

case ITEM_LIGHT: /* number of hours the light lasts */
	if (obj->value[2] == 0)
		sprintf (buf1, "\"light\",0,0,\"(dead)\",");
	else
		if (obj->value[2] == -1)
			sprintf (buf1, "\"light\",0,-1,\"(unlimited)\",");
		else
			sprintf (buf1, "\"light\",0,%d,\"(limited)\",", obj->value[2]);
			
	break; /* forgot */
	
case ITEM_CONTAINER:
{
	sprintf (buf1, "\"container\",%d,%d,\"", 
			 obj->value[2], obj->value[0]);
	if (IS_SET(obj->value[1], CONT_CLOSEABLE))
		strcat (buf1, "close ");
	if (IS_SET(obj->value[1], CONT_PICKPROOF))
		strcat (buf1, "nopick ");
	if (IS_SET(obj->value[1], CONT_CLOSED))
		strcat (buf1, "closed ");
	if (IS_SET(obj->value[1], CONT_LOCKED))
		strcat (buf1, "locked");
		
	strcat (buf1, "\",");
	break;		
}

case ITEM_DRINK_CON: /* drink container */
{
	/* print out poison factor, number of drinks and liquid type */
	/* only print liq type if it is a valid type */
	/* Valid liquids range from 0 .. LIQ_MAX-1 */
	sprintf (buf1, "\"drink container\",%d,%d,\"%s\",",
		            obj->value[3], obj->value[0],
		            ((obj->value[2] >= 0) && (obj->value[2] < LIQ_MAX)) 
		            ? liq_table[obj->value[2]].liq_name
		            : "uknown liquid"
		    );
	break;		
}

case ITEM_TREASURE:  /* treasure, nothing extra */
case ITEM_FURNITURE: /* do */
case ITEM_TRASH:     /* do */
	sprintf (buf1, "\"%s\",0,0,\"\",", 
		 	 item_index_type_name(obj->item_type)
		 	);
	break;
	
case ITEM_FOOD:
	sprintf (buf1, "\"food\",%d,%d,\"\",", 
			 obj->value[3], obj->value[0]
		 	);
	break;


default:
	return; /* do not save those (food,key,money,boat,corpse,fountain */
}

strcat (buf, buf1); /* add obj-specific data */
	
/* now: affects */
/* first: a textual list of all affects */

strcat (buf, "\"");
affect_str (obj->affected, buf1);
strcat (buf,buf1);
strcat (buf, "\",");

/* then one field for each numeral affect */

for (i = 0; i < MAX_NUMBER_APP; i++)
{	
	sprintf (buf1, "%d,", get_affect_total (obj->affected, number_app[i]) );
	strcat (buf, buf1);
}

/* then, one big lump of boolean affects */

strcpy (buf1, "\"");

for (i = 0; i < MAX_BOOL_APP; i++)
{
	if ( is_affect (obj->affected, bool_app[i]) )
	{
		strcat (buf1, affect_loc_name(bool_app[i])); /* no comma in here, it would break the fields */
		strcat (buf1, " ");
	} /* if */		
} /* for */

	strcat (buf1, "\"");
	strcat (buf, buf1);

/* add other information. This could be... anything ? */	
	sprintf (buf1, ",\"%s\"", get_other_information(obj));
	strcat (buf,buf1);
	
	fprintf (fp, "%s\n",buf); /* lets gooo! */	
} /* printobj */


/* print a header */
void printheader (FILE *fp)
{
	int i;
	
	fprintf (fp,
"\"Short desc\",\"Keywords\",\"Level\",\"Vnum\",\"Room\",\"Location\",\
\"Flags\",\"Weight\",\"Value\",\"Worn\",\"Item type\",\"AC/SLevel\",\"Capacity\",\
\"Spells\",\"Affects\","); /* ouch! */

    for (i = 0; i < MAX_NUMBER_APP; i++)
    {	
    	fprintf (fp, "\"%s\",", affect_loc_name(number_app[i]) );
    } /* for i */

    /* then, one big lump of boolean affects */

    fprintf (fp, "\"");
    for (i = 0; i < MAX_BOOL_APP; i++)
    {
    	fprintf (fp, affect_loc_name(bool_app[i]));
    	fprintf (fp, " ");    		
    } /* for i */
    fprintf (fp, "\",\"Other\"\n"); /* finally, "other" field (?) */

} /* printheader */


/* print one area to the file fp */
/* file must be open for w and pArea != NULL */
void printarea (FILE *fp, AREA_DATA *pArea)
{
	MOB_INDEX_DATA *last_mob; /* remember the last mob */
	ROOM_INDEX_DATA *last_room;
	RESET_DATA * pReset;
	OBJ_RESET *list;
	bool mob_reboot; /* TRUE if the last loaded mob reboots at reboot only */

	list = new_list ();	 /* allocate memory for a new list */
	last_mob = NULL; 
	mob_reboot = FALSE;

	/* run through all the resets in that area*/
	for (pReset = pArea->reset_first; pReset != NULL; pReset = pReset->next)
	{
    	/* check what kind of reset it is */

    	switch (pReset->command) /* always in uppercase */
    	{
    	
    	case 'M': /* loads a mob */
    	{
    		if ( ( last_mob = get_mob_index( pReset->arg1 ) ) == NULL )
           	{ /* mob doesnt seem to exist ? */
           		bug( "objdump: 'M': bad vnum %d.\n\r", pReset->arg1 );
               	continue; /* next reset */
           	}
           	
            if (pReset->arg2 == -1) /* -1 = load mob/obj at reboot only */
            	mob_reboot = TRUE;

           	if ( (last_room = get_room_index( pReset->arg3 ) ) == NULL )
           	{ /* room doesn't seem to exist ? */
           		bug( "objdump: 'R': bad vnum %d.\n\r", pReset->arg3 );
               	continue;
           	}
    		
    		break; /* case M */
    	}
    	
    	case 'O': /* load an obj on the ground */
    	{
    		/*       file, location,   what,          where,   who    inside what*/	
    		
    		if (last_mob) /* do we have a last mob ? */
    			printobj (fp,list,on_ground, pReset->arg1, pReset->arg3, 
    			          last_mob->vnum, 0, (pReset->arg2 == -1));
    		else
    			printobj (fp,list,on_ground, pReset->arg1, pReset->arg3, 0, 0, (pReset->arg2 == -1));
    	
    		break; /* case O */
    	}
    	
    	case 'P': /* put it inside some obj */
    	{
    		/* current version is limited, it can only find the vnum of a room
    		   if the object that this one is inside exists */
    					
    		/*      file,         locat,  what,         where, who,            inside what */
    		if (last_mob)
    			printobj (fp,list,inside, pReset->arg1, 0,     last_mob->vnum, pReset->arg3,(pReset->arg2 == -1));
    		else
    			printobj (fp,list,inside, pReset->arg1, 0,     0,              pReset->arg3,(pReset->arg2 == -1));			
    	
    		break; /* case P */
    	}
    	
    	case 'G': /* create then give to mob */
    	case 'E': /* with an optional equip, but we don't really care here */
    	{
    		if ((last_mob != NULL) && (last_room != NULL))
    			printobj (fp,list,on_mob, pReset->arg1, last_room->vnum, last_mob->vnum, 0, 
    			          mob_reboot );
    		else
    			printf ("objdump: 'G' or 'E': no last mob\n\r");
    	
    		break; /* case G,E */
    	}
    	
    	default: /* uninteresting: exits */
    	} /* case */    	
	} /* for pReset */

	dispose_list (list); /* dispose of the temporary list of vnum/level pairs */
} /* printarea */

/* 
   objdump dumps a (commadelimetered) list of _reset_ objs in either the
   world or in one specific area. Not much feedback to user.
*/ 
void do_objdump (CHAR_DATA *ch, char *argument)
{
	AREA_DATA  *pArea;
	FILE *fp;

	/* for parsing the command line */	
	char filename[MAX_INPUT_LENGTH];
	char dumptype[MAX_INPUT_LENGTH];
	
	argument = one_argument(argument, dumptype); /* parse */
	argument = one_argument(argument, filename);

	/* check for valid arguments */
	if ( (strlen(filename) == 0) || (strlen(dumptype) == 0) )
	{
		do_help (ch, "objdump"); /* show syntax */
		return;
	}

	if ( 
	     (!str_cmp (dumptype, "all")) ||
	     (!str_cmp (dumptype, "area"))
	   ) /* valid argument */
	{

    	fp = fopen (filename, "w"); /* open for writing */
    	
    	if (!fp) /* could not create file */
    	{
			send_to_char ("Could not open the specified file for output.\n\r",ch);
    		return;
    	}

    	printheader (fp); /* print ... the header */
    	
    	/* now print whatever the player wants */    	
    	if (!str_cmp (dumptype, "all"))
			for (pArea = area_first; pArea != NULL; pArea = pArea->next)
				printarea (fp, pArea);
		else
			printarea (fp, ch->in_room->area);

		fclose (fp);
		
		send_to_char ("Probably successful.\n\r",ch);
	} /* if */
	else /* not 'all' or 'area' */
		do_help (ch, "objdump");
			
} /* do_objdump */