// Matt, I have just put in the drink facility. It's not complete yet as
// it can only distinguish if the object is drinkable.
// Mods made 20/12/99. CRTH.

#include <stdio.h>
#include "db.h"
#include "config.h"
#include "structs.h"
#include "utils.h"
#include "command.h"
#include "functions.h"

/* VARIABLES */
extern struct object_data *object_list;
extern struct cook_data *cook_list;
extern int items_cooking;
extern struct regen_index_element *regen_index;
extern struct regen_data *timed_regen;
extern struct char_data *who, *who_next;
extern int top_of_regen, active_regen;
extern struct zone_data *zone_list[];

extern char buf[SMALL_BUFSIZE];
extern char buf1[SMALL_BUFSIZE];
extern char buf2[SMALL_BUFSIZE];
extern char buf3[SMALL_BUFSIZE];

struct room_data *room;

void  do_equipment(struct char_data *ch)
{
   int count=0,c;

   for(c=0;c<MAX_WEAR;c++) {
      if(ch->wearing[c] != 0) {
         sprintf(buf,"%s %s\n\r",ch->wearing[c]->name,worn[c].where);
         send_2_output(buf,ch->desc);
         count++;
      }
   }
   if(!count)
      send_2_output("None.\n\r",ch->desc);
   return;
}
void  do_remove(struct char_data *ch, char *arg)
{
   struct object_data *obj, *obj_next;
   int c,count=0;

   if(!get_target(buf,arg,1)) {
      send_2_output("Remove your brain perhaps?\n\r",ch->desc);
   } else {
      for(c=0;c<MAX_WEAR;c++) {
         if((obj_cmp(buf,ch->wearing[c]) || !str_cmp(buf,"all")) && ch->wearing[c]) {
            sprintf(buf1,"You stop using %s.\n\r",ch->wearing[c]->name);
            send_2_output(buf1,ch->desc);
            obj_affects(ch,ch->wearing[c],DO_REMOVE);
            ch->wearing[c]->next_carried=ch->carrying;
            ch->carrying=ch->wearing[c];
            ch->wearing[c]=0;
            count++;
				//CRTH - put this in, cuz we kept removing anything with the same
				// name!!!
				if(str_cmp(buf,"all") != 0)
					break;
         }
      }
      if(!count) {
         sprintf(buf1,"You don't appear to be using anything.\n\r",buf);
         sprintf(buf1,"You don't appear to be using anything.\n\r",buf);
         send_2_output(buf1,ch->desc);
      }
   }
}
void  do_wield(struct char_data *ch, char *arg)
{
   struct   object_data *obj,*obj_next;

   if(!get_target(buf,arg,1)) {
      send_2_output("wield what?\n\r",ch->desc);
   } else {
      for(obj=ch->carrying;obj;obj=obj_next) {
         obj_next=obj->next_carried;
         if(obj_cmp(buf,obj)) {
            if(ch->wearing[0] != 0) {
               send_2_output("You are are already wielding something.\n\r",ch->desc);
               return;
            } else if(check_can_wear(ch,obj)) {
               return;
            } else {
               ch->wearing[0] = obj;
               sprintf(buf1,"You wield %s.\n\r",obj->name);
               send_2_output(buf1,ch->desc);
               if(ch->wearing[0] == ch->carrying) {
                  ch->carrying=ch->wearing[0]->next_carried;
               } else {
                  for(obj=ch->carrying;(obj->next_carried != ch->wearing[0]) && obj;obj=obj ->next_carried)
                     ;
                  obj->next_carried=ch->wearing[0]->next_carried;
               }
               ch->wearing[0]->next_carried=0;
               obj_affects(ch,obj,DO_ADD);
               return;
            }
         }
      }
      sprintf(buf1,"You don't seem to have the %s.\n\r",buf);
      send_2_output(buf1,ch->desc);
   }
   return;
}

void  do_inventory(struct char_data *ch)
{
   struct object_data *obj, *obj_next;
   int    count=0;

   for(obj=ch->carrying;obj;obj=obj_next) {
      obj_next=obj->next_carried;
      sprintf(buf,"You are carrying %s\n",obj->name);
      send_2_output(buf,ch->desc);
      count++;
   }

   if(count == 0)
      send_2_output("You have nothing in you inventory.\n\r",ch->desc);

   return;
}

/********************************************************************
	Created 26/12/99 by Matt.
	A useful command.  Maybe add give to mob (anywhere) feature,
	rather than having to be in the same room.
********************************************************************/
void  do_give(struct char_data *ch, char *arg)
{
   struct object_data *obj, *obj_next;
	struct char_data *who;
   int count=0;
   int items=0;

	if(!get_arg(buf,arg,1)) {
      send_2_output("Give up, perhaps?\n\r",ch->desc);
	} else if(!get_target(buf1,arg,2)) {
      send_2_output("Whom do you wish to endow?\n\r",ch->desc);
   } else if((who = get_xuser(ch,buf1)) == ch) {
      send_2_output("You wish to receive what you already have?\n\r",ch->desc);
	} else if((!who) || !see_char_chk(ch, who)) {
		send_2_output("That is impossible!\n\r",ch->desc);
	} else if(IN_ROOM(who) != IN_ROOM(ch) && ch->LEVEL != IMP) {
		send_2_output("They are too far away!\n\r",ch->desc);
	} else {
   	for(obj=ch->carrying;obj;obj=obj_next) {
      	obj_next=obj->next_carried;
      	items++;
      	if(obj_cmp(buf,obj) || !str_cmp("all",buf)) {
         	obj_from_char(obj);
				obj_2_char(obj,who);
         	sprintf(buf1,"%s has given you %s.\n\r",ch->name,obj->name);
         	send_2_output(buf1,who->desc);
         	sprintf(buf1,"You give %s to %s.\n\r",obj->name,who->name);
         	send_2_output(buf1,ch->desc);
         	sprintf(buf1,"gives %s %s",who->name,obj->name);
         	send_2_xcept(ch,who,IGNR_INVIS,buf1);
         	count++;
				//CRTH.- Put this in cuz we keep giving all of the same name.
				if(str_cmp(buf,"all") != 0)
					break;
      	}
		}

   	if(!count && !items)
      	send_2_output("You aren't carrying anything.\n\r",ch->desc);
   	else if(!count)
      	send_2_output("You aren't carrying that.\n\r",ch->desc);
	}
	return;
}
void  do_deaddrop(struct char_data *ch, char *arg)
{
   struct object_data *obj, *obj_next;
   int count=0;
   int items=0;

   if(!get_target(buf,arg,1)) {
      send_2_output("drop what?\n\r",ch->desc);
      return;
   }

   for(obj=ch->carrying;obj;obj=obj_next) {
      obj_next=obj->next_carried;
      items++;
      if(obj_cmp(buf,obj) || !str_cmp("all",buf)) {
         obj_from_char(obj);
			obj_2_room(obj,CHAR_ROOM);
         sprintf(buf1,"You drop %s.\n\r",obj->name);
         send_2_output(buf1,ch->desc);
         sprintf(buf1,"\b's %s falls to the floor",obj->name);
         send_2_room(ch,IGNR_INVIS,buf1);
			count++;
			// CRTH. if loop is not broken, then all items of this type
			// are droped.
			if(str_cmp(buf,"all") != 0)
				break;
      }
   }

   return;
}
void  do_drop(struct char_data *ch, char *arg)
{
   struct object_data *obj, *obj_next;
   int count=0;
   int items=0;

   if(!get_target(buf,arg,1)) {
      send_2_output("drop what?\n\r",ch->desc);
      return;
   }

   for(obj=ch->carrying;obj;obj=obj_next) {
      obj_next=obj->next_carried;
      items++;
      if(obj_cmp(buf,obj) || !str_cmp("all",buf)) {
         obj_from_char(obj);
			obj_2_room(obj,CHAR_ROOM);
         sprintf(buf1,"You drop %s.\n\r",obj->name);
         send_2_output(buf1,ch->desc);
         sprintf(buf1,"drops %s",obj->name);
         send_2_room(ch,IGNR_INVIS,buf1);
			count++;
			// CRTH. if loop is not broken, then all items of this type
			// are droped.
			if(str_cmp(buf,"all") != 0)
				break;
      }
   }

   if(!count && !items)
      send_2_output("You aren't carrying anything.\n\r",ch->desc);
   else if(!count)
      send_2_output("You aren't carrying that.\n\r",ch->desc);
   return;
}

int  do_get(struct char_data *ch, char *arg)
{
   struct object_data *obj, *obj_next;
   int   count=0;

   if(!get_target(buf,arg,1)) {
      send_2_output("get what?\n\r",ch->desc);
      return;
   }

   for(obj=CHAR_ROOM->objects;obj;obj=obj_next) {
      obj_next=obj->next_in_room;
      if(obj_cmp(buf,obj) || !str_cmp(buf,"all")) {
         if(!can_take(ch,obj,OBJ_GET)) {
            sprintf(buf1,"%s is too heavy for you to pick up!\n\r",obj->name);
            send_2_output(buf1,ch->desc);
         } else {
				obj_from_room(obj);
				obj_2_char(obj,ch);
            sprintf(buf1,"You get %s.\n\r",obj->name);
            send_2_output(buf1,ch->desc);
            sprintf(buf1,"gets %s",obj->name);
            send_2_room(ch,IGNR_INVIS,buf1);
         }
         count++;
			//CRTH. - if not, then all items of same name are taken.
			if(str_cmp(buf,"all") != 0)
				break;
      }
   }

   if(!count)
      send_2_output("You don't see that here.\n\r",ch->desc);
   return(count);
}

void  do_wear(struct char_data *ch, char *arg)
{
   struct object_data *obj, *obj_next;
   int   count=0;

   if(!get_target(buf,arg,1)) {
      send_2_output("wear what?\n\r",ch->desc);
      return;
   }

   for(obj=ch->carrying;obj;obj=obj_next) {
      obj_next=obj->next_carried;
      if((obj_cmp(buf,obj) || !str_cmp(buf,"all")) && obj->WORN) {
         obj_wear(ch,obj);
			count++;
			// CRTH. - stops us wearing all the same object.
			if(str_cmp(buf,"all") != 0)
				break;
      }
   }

   if(!count)
      if(!str_cmp(buf,"all"))
         send_2_output("Oh, dear. You don't have anything to wear...\n\r",ch->desc);
      else
         send_2_output("You can't wear that.\n\r",ch->desc);
   return;
}

int   obj_wear(struct char_data *ch, struct object_data *object)
{
   struct object_data *obj;
   int c,found;

   if(check_can_wear(ch,object))
      return(0);

   for(c=0;c<MAX_WEAR;c++) {
      if(BTST(object->WORN,worn[c].setbit)) {
         found=c;
         if(ch->wearing[c] == 0) {
            ch->wearing[c] = object;
            if(ch->carrying == object)
               ch->carrying = object->next_carried;
            else {
               for(obj=ch->carrying;(obj->next_carried != object) && obj;obj=obj->next_carried)
                  ;
               obj->next_carried=object->next_carried;
            }
            object->next_carried=0;
				object->contained_in=0;
				object->next_contained=0;
				object->carried_by = ch;
            obj_affects(ch,object,DO_ADD);
            sprintf(buf3,"You %s %s.\n\r",worn[c].verb,object->name);
            send_2_output(buf3,ch->desc);
            sprintf(buf3,"%ss %s.\n\r",worn[c].verb,object->name);
            send_2_room(ch,IGNR_INVIS,buf3);
            return(c);
         }
      }
   }
   switch(found) {
   case 0:
   case 1:
      send_2_output("Your hands are full!\n\r",ch->desc);
      break;
   case 3:
      send_2_output("You can't wear any more rings...\n\r",ch->desc);
      break;
   case 4:
      send_2_output("You are already wearing something on your hands.\n\r",ch->desc);
      break;
   case 6:
      send_2_output("You are already wearing something on your wrists.\n\r",ch->desc);
      break;
   case 7:
      send_2_output("You already have something on your arms.\n\r",ch->desc);
      break;
   case 9:
      send_2_output("You can't wear anything else around your neck.\n\r",ch->desc);
      break;
   case 10:
      send_2_output("You already have something on your head.\n\r",ch->desc);
      break;
   case 11:
      send_2_output("You are already wearing something on your chest\n\r",ch->desc);
      break;
   case 12:
      send_2_output("You are already wearing something around your waist\n\r",ch->desc);
      break;
   case 13:
      send_2_output("You are already wearing something on your legs\n\r",ch->desc);
      break;
   case 14:
      send_2_output("You are already wearing something on your feet\n\r",ch->desc);
      break;
   case 15:
      send_2_output("You are already wearing something as a shield\n\r",ch->desc);
	  break;
   case 18:
	  send_2_output("You are already wearing something on your back\n\r",ch->desc);
	  break;
   default:
      send_2_output("You are already wearing something around your body\n\r",ch->desc);
   }
   return(0);
}

/*****************************************************************/
/*****************************************************************/
int obj_2_obj(struct object_data *obj, struct object_data *target)
{

	obj->next_contained = target->contains;
	target->contains = obj;

	obj->contained_in = target;
   obj->carried_by = 0;
	obj->next_carried = 0;
	obj->in_room = 0;

	/* Adjust the weight of the object containing this object */
	obj_weight_adjust(target, obj->OBJ_WEIGHT);
}

/**********************/
int obj_from_obj(struct object_data *object)
{
   struct object_data *obj;

	/* Check we have an object and 
      that we are inside an object */
	if(!((object) && object->contained_in))
		return(FALSE);

	obj=object->contained_in;

	if(obj->contains == object) {
//CRTH - You were seting the pointer to next, then setting it to 0??
// So i have switched the two lines bellow.
		obj->contains=0;
		obj->contains=object->next_contained;
	} else {
		obj=obj->contains;
		while(obj) {
			if(object == obj->next_contained) {
				obj->next_contained=object->next_contained;
				break;
			}
			obj=obj->next_contained;
		}
	}
	obj_weight_adjust(object->contained_in, (object->OBJ_WEIGHT * -1));
	object->next_contained=0;
	object->contained_in=0;
	return(TRUE);
}

/*****************************************************************/
int obj_2_char(struct object_data *obj, struct char_data *ch)
{
	/***********************************************************
		 For automated routines where 'clone_object' might fail
		because of the maximum limits, we need a check.  Though
		it would probably have been good to check anyway 
	************************************************************/
	if(!obj)
		return;

	obj->next_carried=ch->carrying;
	ch->carrying=obj;

	obj->in_room = 0;
	obj->contained_in = 0;
	obj->next_contained = 0;
	obj->carried_by = ch;

	/* adjust the weights */
	char_weight_adjust(ch, obj->OBJ_WEIGHT);
}
/*************************/
int obj_from_char(struct object_data *object)
{
	struct char_data *ch;
   struct object_data *obj;
   int c;

	/* Do we have an object and a carrier? */
	if(!((object) && object->carried_by)) {
		return(FALSE);
	}

	ch=object->carried_by;

   for(c=0;c<MAX_WEAR;c++) {
     	if(object == ch->wearing[c]) {
        	obj_affects(ch,ch->wearing[c],DO_REMOVE);
        	ch->wearing[c]=0;
			char_weight_adjust(ch, (object->OBJ_WEIGHT * -1));
			object->carried_by=0;
			return(TRUE);
      }
   }

	if(object == ch->carrying) {
// CRTH : switched these two lines cuz if you drop the last item picked
// up, you seemed to loose all your stuff !!! ! ! 
		ch->carrying=0;
		ch->carrying=object->next_carried;
	} else {
		/* This is a failsafe way of searching...
			If the object does not exist in this list the
			routine will drop out without crashing. */
		obj=ch->carrying;
		while(obj) {
	   	if(object == obj->next_carried) {
				obj->next_carried = object->next_carried;
				break;
			}
			obj=obj->next_carried;
	 	 }
	}
	char_weight_adjust(ch, (object->OBJ_WEIGHT * -1));
	object->next_carried=0;
	object->carried_by=0;
	return(TRUE);
}
/*****************************************************************/
int obj_2_room(struct object_data *obj, struct room_data *room)
{
	obj->next_in_room = room->objects;
	room->objects = obj;

	obj->contained_in = 0;
	obj->next_contained = 0;
	obj->carried_by = 0;
	obj->next_carried = 0;
	obj->in_room = room;
}
/*************************/
int obj_from_room(struct object_data *object)
{
	struct object_data *obj;

	if(object->in_room->objects == object) {
		object->in_room->objects=object->next_in_room;
	} else {
		obj=object->in_room->objects;
		while(obj) {
			if(object == obj->next_in_room) {
				obj->next_in_room=object->next_in_room;
				break;
			}
			obj=obj->next_in_room;
		}
	}
	object->next_in_room = 0;
	object->in_room = 0;
	return(TRUE);
}
/*****************************************************************/
/*****************************************************************/
struct object_data *check_carried(struct char_data *ch, char *name)
{
   struct object_data *obj;
	int 	 c;

   for(c=0;c<MAX_WEAR;c++) {
     	if(obj_cmp(name,ch->wearing[c]))
			return(ch->wearing[c]);
	}

	for(obj=ch->carrying;obj;obj=obj->next_carried) {
		if(obj_cmp(name,obj))
			return(obj);
	}
	
	return(0);
}

/*************************************************************/
/* Function to regenerate an item that was killed or removed */
/* from the world.                                           */
/*************************************************************/
void regenerate(struct regen_data *item)
{
	struct regen_data *top, *next;
	struct room_data *room=0;
	struct char_data *mob=0;	

	/**************************************************
		Find out where we exist in the list -
		We should be at the top, but one never knows
	***************************************************/
	if(item == timed_regen) 
		timed_regen = item->next;
	else 
	{
		for(top=timed_regen;(top->next != item) && top;top=top->next);
		top->next = item->next;
	}

	/************************************************/
	/* Reset the counter (in case it isn't already) */
	item->counter = 0;
	// CRTH This line did re activate the item on the list so i removed it.
	//activate_regen(item->reg_num);

	// CRTH - Done a bit of work here to get the buggers to REGEN..
	switch(item->type) {
	case IS_OBJECT:	
		printf("Regen an object\n");
		printf("Done\n");
		break;
	case IS_ROOM:		
		printf("Regen a room\n");
		printf("Done\n");
		break;
	case IS_CHAR: 
		room = get_room(item->X,item->Y,item->Z,item->ROOM);
		printf("Regenerating %s in zone %d room %s ID = %d\n",item->proto.ch->name,item->Z,room->name,item->proto.ch->IDNUM);
		mob = clone_mob(item->proto.ch->IDNUM);
		init_mob(item->proto.ch);
		char_2_room(mob,room,"enters.");
		break;
	default:
		printf("No regen found\n");
		break;
	}
}

/************************************************************/
/* Function to activate an item on the regeneration list.   */
/*                                                          */
/* NOTE:                                                    */
/* Only add items to this list with this function!          */
/* There is no remove function as the only way items get    */
/* removed off this list is by regeneration...              */
/* Items can only be declared regenerative at boot-up!      */
/************************************************************/
void activate_regen(int num)
{
	struct regen_data *chk;
	/***********************************************************/
	/* Do we have an invalid number or an already active item? */
	if(num >= top_of_regen)
		return;
	if(regen_index[num].item->counter)
		return;

	/* Set the counter for this item */
	regen_index[num].item->counter = regen_index[num].item->time;

	/**********************************************************/
	/* Search through the regeneration index to find where we */
	/* fit in with the other regenerative objects.            */
	if(!timed_regen) {
		timed_regen = regen_index[num].item;
		timed_regen->next = 0;
	} else if(regen_index[num].item->counter > timed_regen->counter) {
		timed_regen->counter -= regen_index[num].item->counter;
		regen_index[num].item->next = timed_regen;
		timed_regen = regen_index[num].item;
	} else {
		for(chk=timed_regen;regen_index[num].item->counter > chk->counter;chk=chk->next) {
			regen_index[num].item->counter -= chk->counter;
			if(!chk->next)
				break;
		}

		/* Insert the new item */
		regen_index[num].item->next = chk->next;
		chk->next = regen_index[num].item;
		if(regen_index[num].item->next)
			regen_index[num].item->next->counter -= regen_index[num].item->counter;
	}
}

/************************************************************/
/* This function MUST be used to add items to the slow_cook */
/* timed departure list.                                    */
/* This sorts all items into ascending time order so that   */
/* the program has only to decrement one item each second.  */
/************************************************************/
void slow_cook_item(union any item, char type)
{
	struct cook_data *new_item, *chk;

	/* Create a new element */
	CREATE(new_item,struct cook_data,1);

	/* Save its type */
	new_item->type = type;

	/* Show we have a new item cooking */
	items_cooking++;

	/* Save it to the appropriate position */
	switch(type) {
	case IS_OBJECT:
		new_item->data = item;
		new_item->counter = new_item->data.ob->LIFE;
		new_item->data.ob->LIFE = 0;
		break;
	case IS_ROOM:
		new_item->data = item;
		new_item->counter = new_item->data.ro->LIFE;
		new_item->data.ro->LIFE = 0;
		break;
	case IS_CHAR:	
		new_item->data = item;
		new_item->counter = new_item->data.ch->LIFE;
		new_item->data.ch->LIFE = 0;
		break;
	case IS_ZONE:
		break;
	default:
	}

	/*****************************************/
	/* Find the right slot for this new item */
	if(!cook_list) {
		/* This is the first item */
		cook_list = new_item;
		new_item->next = 0;
	} else if(new_item->counter < cook_list->counter) {
		/* This is less than the first item */
		cook_list->counter -= new_item->counter;
		new_item->next = cook_list;
		cook_list = new_item;
	} else {
		/* This is somewhere in the crowd */
		for(chk=cook_list;new_item->counter > chk->counter;chk=chk->next) {
			new_item->counter -= chk->counter;
			if(!chk->next)
				break;
		}

		/* Insert the new item */
		new_item->next = chk->next;
		chk->next = new_item;
		if(new_item->next)
			new_item->next->counter -= new_item->counter;
	}
}

/************************************************************/
/* Function to remove an 'un-cooked' item.                  */
/* This could be because a character has died or left the   */
/* game.  In the latter case the item will be suspended     */
/* until that user logs on again, when it will be put back  */
/* on the list.                                             */
/*                                                          */
/* NOTE:                                                    */
/* This function only removes the item from the 'slow_cook' */
/* list.  Removing the item from other lists is the         */
/* responsibility of the calling function.                  */
/************************************************************/
void rem_uncooked_item(union any item, char type)
{
	struct cook_data *old_item, *chk;
	struct object_data;
	int    time=0;

	if(cook_list->data.ob == item.ob) {
		old_item = cook_list;
		cook_list = old_item->next;
		time = old_item->counter;
	} else {
		for(chk=cook_list;(chk->next) && chk->next->data.ob != item.ob;chk=chk->next)
			time += chk->counter;
		time += chk->counter;

		/* Trying to remove invalid object? */
		if(chk->next->data.ob != item.ob)
			return;

		/* Remove the item from the list and update the times */
		old_item = chk->next;
		chk->next = old_item->next;
		chk->next->counter += old_item->counter;
		--items_cooking;

		printf("Uncooked: %d -- %d [%d]\n",old_item,item,type);

		switch(type) {
		case IS_OBJECT:
			old_item->data.ob->LIFE = time;
			break;
		case IS_ROOM:
			old_item->data.ro->LIFE = time;
			break;
		case IS_CHAR:
			old_item->data.ch->LIFE = time;
			break;
		case IS_ZONE:
			break;
		default:
		}
	}
	
	free(old_item);
}

/************************************************************/
/* This function is called automatically when an object has */
/* been put on the 'slow_cook' list and its LIFE is less    */
/* than 1.                                                  */ 
/*                                                          */
/* NOTE:                                                    */
/* Items should be added using the 'slow_cook' function!    */
/* To save CPU time items are sorted into ascending time    */
/* order so that only one object is referenced each second. */
/************************************************************/
const char *rem_message[] = {
	"turns to dust and is swept away with the wind.\n\r",
	"fades out of existance.\n\r",
	"explodes\n\r",
};
void rem_cooked_item(void)
{
	struct char_data   *who, *who_next, *tmp_who;
	struct object_data *obj, *obj_next, *tmp_obj;
	struct cook_data *item;
	char   target;

	/***************************************/
	/* Remove the cooked item from the top */
	item = cook_list;
	cook_list = item->next;
	--items_cooking;
	/***************************************/

	/****************************************************/
	/* Find out the type of object we have to terminate */
	/****************************************************/
	switch(item->type) {
	case IS_OBJECT:
		/* Prepare the message to show the users */
		sprintf(buf1,"%s %s",item->data.ob->name,rem_message[item->data.ob->DIE_MSG]);
		/*********************************************************************/
		/* Find out the objects location and prepare the destination for any */
		/* objects contained within this one.                                */
		/*********************************************************************/
		if(item->data.ob->in_room) {
			target = IS_ROOM;
			/* Inform the people */
			for(who=item->data.ob->in_room->people;who;who=who_next) {
				who_next = who->next_in_room;
				send_2_output(buf1, who->desc);
			}
// CRTH - Line below moved further down cuz it woz BAD !! ! ! !  see notes by
// remove object.... Also see the occurence of removal for OBJ & CHAR
//			obj_from_room(item->data.ob);  /* Remove the object & set variable 'room' */
		} else if(item->data.ob->contained_in) {
			target = IS_OBJECT;
			tmp_obj=item->data.ob->contained_in;
		} else if(item->data.ob->carried_by) {
			target = IS_CHAR;
			tmp_who=item->data.ob->carried_by;
			if(tmp_who->desc)
				send_2_output(buf1, item->data.ob->carried_by->desc);
		} else {
			printf("Invalid Target: rem_cooked_item()\n");
		}
		/* Save anything that was contained within the object */
		for(obj=item->data.ob->contains;obj;obj=obj_next) {
			obj_next=obj->next_contained;
			obj_from_obj(obj);
			switch(target) {
			case IS_ROOM:
				obj_2_room(obj,item->data.ob->in_room);
				break;
			case IS_OBJECT:
				obj_2_obj(obj, tmp_obj);
				break;
			default:
				obj_2_char(obj,tmp_who);
			}
		}
// CRTH - You removed the object from the room so it kept crashing
// in the above switch statment in the obj_2_* functions!!!!
  		/* Remove the object & set variable 'room' */
		switch(target)
		{	
			case IS_ROOM:		obj_from_room(item->data.ob);
									break;
			case IS_OBJECT:	obj_from_obj(item->data.ob);
									break;
			default		:		obj_from_char(item->data.ob); 
		}

		/* Remove the object from the world */
		delete_object(item->data.ob, FROM_WORLD);
		break;
	case IS_ROOM:
		break;
	case IS_CHAR:
		break;
	case IS_ZONE:
		break;
	deafult:
	}

	/* Give back the memory */
	free(item);
}


/***************************************************************/
/* This function will delete all the links associated with the */
/* passed object and free up those resources being used.       */
/*                                                             */
/* NOTE:                                                       */
/* It will delete any/all objects contained within it.         */
/* It will not remove an object or its links from a room or a  */
/* person - This must be done by the calling function.         */
/***************************************************************/
int  delete_object(struct object_data *object, char mode)
{
	struct object_data *obj;

	/* Do I have an object to delete? */
	if(!object)
		return(0);

	/* Does it contain anything? */
	if(object->contains)
		delete_object(object->contains, mode);

	/* Is it in a container? If so, are there other objects in there too? */
	if(object->next_contained)
		delete_object(object->next_contained, mode);

	/* Was it on the global die list? */
	if(object->LIFE)
		rem_uncooked_item((union any)object, IS_OBJECT);

	/* Delete it from the global list of objects */
	if(object_list == object) {
		object_list = object->next_in_list;
	} else {
		obj=object_list;
		while(obj) {
			if(obj->next_in_list == object) {
				obj->next_in_list = object->next_in_list;
				break;
			}
			obj=obj->next_in_list;
		}
	}

	/* Get this objects prototype */
	obj=object->proto_object;

	/* Is it a permanent removal of this object? */
	if(mode == FROM_WORLD)
		--obj->OBJ_COUNT;

	/* Now to delete it from the same-of-type list */
	if(obj->next_of_type == object) {
		obj->next_of_type = object->next_of_type;
	} else {
		while((obj->next_of_type != object) && obj)
			obj=obj->next_of_type;
		obj->next_of_type=object->next_of_type;
	}

	/* Free up the memory it was using */
	free(object);
}

/***************************************************/
/* A function to clone an object.                  */
/*                                                 */
/* NOTE:                                           */
/* A prototype object must exist.                  */
/* OLD_OBJECT should only be used by the character */
/* loading and saving routines!                    */
/* *************************************************/
struct object_data *clone_object(int object_num, char mode)
{
	struct object_data *obj, *source;
	int c;

	if(!(source = get_proto_object(object_num))) {
		printf("**Invalid Object Number** [%d]\n",object_num);
		return(0);
	} else if(mode == NEW_OBJECT) {
		if(source->OBJ_COUNT < source->OBJ_MAX) {
			source->OBJ_COUNT++;
		} else {
			return(0);
		}
	}
	CREATE(obj, struct object_data, 1);
	memset(obj, 0, sizeof(struct object_data));
	if(mode == NEW_OBJECT) {
			for(c=0;c<MAX_DATA;c++)
				obj->data[c]=source->data[c];
	}

	obj->IDNUM=source->IDNUM;
	obj->name=source->name;
	obj->exam_desc=source->exam_desc;
	obj->still_desc=source->still_desc;
	obj->extra_desc=source->extra_desc;
	obj->aliases=source->aliases;
	obj->TYPE=source->TYPE;
	obj->WORN=source->WORN;
	obj->OBJ_SPECIAL=source->OBJ_SPECIAL;
	obj->affect=source->affect;
	obj->CLASS=source->CLASS;
	obj->SEX=source->SEX;
	obj->ALIGN=source->ALIGN;

	obj->in_room = 0;                       /* Set it as not in a room */
	obj->carried_by = 0;       				 /* Set it as not carried by anyone */

	/* Put the object on the object list */
	obj->next_in_list=object_list;
	object_list=obj;
	/* Get the proto-type to point to this new object */
	obj->next_of_type=source->next_of_type;
	source->next_of_type=obj;
	/* Set the pointer to the mother of of this type */
	obj->proto_object=source;

	return(obj);
}

/***************************************************************/
/* Create a corpse for the recently (to become) dead character */
/*                                                             */
/* If this is a mob, and the mob has REGEN, then the mob will  */
/* be put on to the regeneration list.                         */
/* Characters just die young...                                */
/***************************************************************/
void	create_corpse(struct char_data *ch, struct char_data *killer)
{
	struct object_data *corpse,*obj,*obj_next;
	int c;
	
	/* Clone a corpse */
	corpse = clone_object(0,NEW_OBJECT);

	/* Give it a life span */
	corpse->LIFE = 120;      		        // Seconds
	corpse->DIE_MSG = 0;     		        // Removal message
	slow_cook_item((union any)corpse, IS_OBJECT);    // Put it on 'slow cook'.

	/* Set it's descriptions */
	sprintf(buf1,"%s's corpse",ch->name);
	corpse->name = str_dup(buf1);
	if(killer)
		sprintf(buf2,"%s lies festering here (killed by %s).",buf1,killer->name);
	else
		sprintf(buf2,"%s lies festering here.",buf1);

	corpse->still_desc = str_dup(buf2);

	/* Put it in the room of the newly deceased */
	obj_2_room(corpse,CHAR_ROOM);

	/* Need to empty the character and fill the corpse */
	for(c=0;c<MAX_WEAR;c++) {
		if((obj=ch->wearing[c])) {
			obj_from_char(obj);
			obj_2_obj(obj,corpse);
		}
	}
	for(obj=ch->carrying;obj;obj=obj_next) {
		obj_next = obj->next_carried;
		obj_from_char(obj);
		obj_2_obj(obj,corpse);
	}
	
	/* Kill the character off */
	if(ch->desc) {
		change_status(ch, CON_CLOSE);
		ch->POSITION = POS_DEAD;
		BSET(ch->SPECIALS,DELETED);
		save_char(ch);
	} else {
		/* Does this mob have regeneration? */
		if(ch->REGEN) {
			if(DEBUG_MOB)
				printf("REGEN: %s is killed in area = %s ID = %d\n",ch->name,ch->in_room->name,ch->IDNUM);
			activate_regen(ch->REGEN);
		}
			
		kill_char(ch,"has been killed");
	}
}

// Sorry if the tabs are out, but the editor auto indents.
void  do_drink(struct char_data *ch, char *arg)
{
   struct object_data *obj, *obj_next;
   int   count=0;

   if(!get_target(buf,arg,1))
   {
	  send_2_output("drink what?\n\r",ch->desc);
      return;
   }

   for(obj = ch->carrying;obj;obj = obj_next)
   {
	  obj_next = obj->next_carried;
	  if((obj_cmp(buf,obj) || !str_cmp(buf,"all")) && BTST(obj->TYPE,OBJ_DRINK))
	  {
		 obj_drink(ch,obj);
		 count++;
		 if(str_cmp(buf,"all") != 0)
			break;				 
      }
   }

   if(!count)
      if(!str_cmp(buf,"all"))
		 send_2_output("Oh, dear. You don't have anything to drink...\n\r",ch->desc);
	  else
		 send_2_output("You can't drink that.\n\r",ch->desc);
   return;
}

int   obj_drink(struct char_data *ch, struct object_data *object)
{
   struct object_data *obj;
   int c,found;
	

   sprintf(buf3,"You drink from the %s.\n\r",object->name);
   send_2_output(buf3,ch->desc);
   sprintf(buf3,"drink's from a %s.\n\r",object->name);
   send_2_room(ch,IGNR_INVIS,buf3);

	// OK, now we need to know if the drink contains booz or poison.
	switch(object->data[6])
	{
		case 0	:	// Normal drink.
						c = object->data[7];
						if(ch->THIRST > 0)
						{
							ch->THIRST -= c;
							if(ch->THIRST < 0)
								ch->THIRST = 0;
						}
						sprintf(buf1,"Your %s\n\r",drink[ch->THIRST/20].shrt);
						send_2_output(buf1,ch->desc);
						break;
		case 1	:	// Booz.
						
						send_2_output("You can taste the \033[34mBOOZZ\033[0m.\n\r",ch->desc);
						// How Strong.
						c = object->data[7] + (26 - (ch->CON+ch->STR)); 
						if(c < 0)
							c = 0;
						ch->DRUNK += c;
						if(ch->DRUNK > 100)
							ch->DRUNK = 100;
						c = ch->DRUNK / (100/4);
						ch->STATE = 3 - c;
						if((3-c) != STAT_OK)
						{
							if(ch->STATE == STAT_POISEND)
								ch->STATE = STAT_DAP;
							if(ch->STATE == STAT_PLAUGE)
								ch->STATE = STAT_DPL;
							if(ch->STATE == STAT_PAP)
								ch->STATE = STAT_DPP;
						}	
						break;	
		case 2	:	// Poison.
						send_2_output("You can taste somthing strage...\n\r",ch->desc);
						// How strong.
						ch->POISENSTR = object->data[7];
						ch->POISEN += (20 - ch->STR) + ch->POISENSTR;
						if(ch->POISEN > 100)
							ch->POISEN = 100;
						if(ch->STATE != STAT_OK)
						{
							if(ch->STATE < STAT_OK)
								ch->STATE = STAT_DAP;
							if(ch->STATE == STAT_PLAUGE)
								ch->STATE = STAT_PAP;
							if(ch->STATE > STAT_PAP)
								ch->STATE = STAT_DPP; 
						}
						else
							ch->STATE = STAT_POISEND;
						break;
		default	:	// Unknown.
						send_2_output("This tastes like nothing you have ever tasted before..\n\r",ch->desc);
						break;
	}
   return(1);
}

void  do_eat(struct char_data *ch, char *arg)
{
   struct object_data *obj, *obj_next;
   int   count=0;

   if(!get_target(buf,arg,1))
   {
	  send_2_output("eat what?\n\r",ch->desc);
      return;
   }

   for(obj = ch->carrying;obj;obj = obj_next)
   {
	  obj_next = obj->next_carried;
	  if((obj_cmp(buf,obj) || !str_cmp(buf,"all")) && BTST(obj->TYPE,OBJ_FOOD))
	  {
		 obj_eat(ch,obj);
		 count++;
		 if(str_cmp(buf,"all") != 0)
			break;				 
      }
   }

   if(!count)
      if(!str_cmp(buf,"all"))
		 send_2_output("Oh, dear. You don't have anything to eat...\n\r",ch->desc);
	  else
		 send_2_output("You can't eat that.\n\r",ch->desc);
   return;
}

int   obj_eat(struct char_data *ch, struct object_data *object)
{
   struct object_data *obj;
   int c,found;

   sprintf(buf3,"You eat some %s.\n\r",object->name);
   send_2_output(buf3,ch->desc);
   sprintf(buf3,"eat's some %s.\n\r",object->name);
   send_2_room(ch,IGNR_INVIS,buf3);

	// OK, now we need to know if the food contains booz or poison.
	switch(object->data[6])
	{
		case 0	:	// Normal food.
						c = object->data[7];
						if(ch->HUNGER > 0)
						{
							ch->HUNGER -= c;
							if(ch->HUNGER < 0)
								ch->HUNGER = 0;
						}
						sprintf(buf1,"Your %s.\n\r",food[ch->HUNGER/20].shrt);
						send_2_output(buf1,ch->desc);
						break;
		case 1	:	// Booz.
						send_2_output("You can taste the \033[34mBOOZZ\033[0m.\n\r",ch->desc);

						// How Strong.
						c = object->data[7] + (26 - (ch->CON+ch->STR)); 
						if(c < 0)
							c = 0;
						ch->DRUNK += c;
						if(ch->DRUNK > 100)
							ch->DRUNK = 100;
						c = ch->DRUNK / (100/4);
						ch->STATE = 3 - c;
						if((3-c) != STAT_OK)
						{
							if(ch->STATE == STAT_POISEND)
								ch->STATE = STAT_DAP;
							if(ch->STATE == STAT_PLAUGE)
								ch->STATE = STAT_DPL;
							if(ch->STATE == STAT_PAP)
								ch->STATE = STAT_DPP;
						}	
						break;	
		case 2	:	// Poison.
						send_2_output("You can taste somthing strage...\n\r",ch->desc);
						// How strong.
						ch->POISENSTR = object->data[7];
						ch->POISEN += (20 - ch->STR) + ch->POISENSTR;
						if(ch->POISEN > 100)
							ch->POISEN = 100;
						if(ch->STATE != STAT_OK)
						{
							if(ch->STATE < STAT_OK)
								ch->STATE = STAT_DAP;
							if(ch->STATE == STAT_PLAUGE)
								ch->STATE = STAT_PAP;
							if(ch->STATE > STAT_PAP)
								ch->STATE = STAT_DPP; 
						}
						else
							ch->STATE = STAT_POISEND;
						break;
		default	:	// Unknown.
						send_2_output("This tastes like nothing you have ever tasted before..\n\r",ch->desc);
						break;
	}
   return(1);
}

