/*
 * Routines to deal with the pack
 *
 * @(#)pack.c	9.0	(rdk)	 7/17/84
 *
 * Super-Rogue
 * Copyright (C) 1984 Robert D. Kindelberger
 * All rights reserved.
 *
 * Based on "Rogue: Exploring the Dungeons of Doom"
 * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
 * All rights reserved.
 *
 * See the file LICENSE.TXT for full copyright and licensing information.
 */

#include <ctype.h>
#include "rogue.h"
#include "rogue.ext"

/*
 * add_pack:
 * Pick up an object and add it to the pack.  If the argument
 * is non-null use it as the linked_list pointer instead of
 * getting it off the ground.
 */
bool
add_pack(struct linked_list *item, bool silent)
{
	reg struct linked_list *ip, *lp;
	reg struct object *obj, *op = NULL;
	bool from_floor;
	char delchar;

	if (player.t_room == NULL)
		delchar = PASSAGE;
	else
		delchar = FLOOR;
	if (item == NULL) {
		from_floor = TRUE;
		if ((item = find_obj(hero.y, hero.x)) == NULL) {
			mpos = 0;
			msg("That object must have been an illusion.");
			mvaddch(hero.y, hero.x, delchar);
			return FALSE;
		}
		/*
		 * Check for scare monster scrolls
		 */
		obj = OBJPTR(item);
		if (obj->o_type == SCROLL && obj->o_which == S_SCARE) {
			if (o_on(obj,ISFOUND)) {
				msg("The scroll turns to dust as you pick it up.");
				detach(lvl_obj, item);
				discard(item);
				mvaddch(hero.y, hero.x, delchar);	
				return FALSE;
			}
		}
	}
	else
		from_floor = FALSE;
	obj = OBJPTR(item);
	/*
	 * See if this guy can carry any more weight
	 */
	if (itemweight(obj) + him->s_pack > him->s_carry) {
		msg("You can't carry that %s.", obj->o_typname);
		return FALSE;
	}
	/*
	 * Check if there is room
	 */
	if (packvol + obj->o_vol > V_PACK) {
		msg("That %s won't fit in your pack.", obj->o_typname);
		return FALSE;
	}
	if (from_floor) {
		detach(lvl_obj, item);
		mvaddch(hero.y, hero.x, delchar);
	}
	item->l_prev = NULL;
	item->l_next = NULL;
	setoflg(obj, ISFOUND);
	/*
	 * start looking thru pack to find the start of items
	 * with the same type.
	 */
	lp = pack;
	for (ip = pack; ip != NULL; ip = next(ip)) {
		op = OBJPTR(ip);
		/*
		 * If we find a matching type then quit.
		 */
		if (op->o_type == obj->o_type)
			break;
		if (next(ip) != NULL)
			lp = next(lp);		/* update "previous" entry */
	}
	/*
	 * If the pack was empty, just stick the item in it.
	 */
	if (pack == NULL) {
		pack = item;
		item->l_prev = NULL;
	}
	/*
	 * If we looked thru the pack, but could not find an
	 * item of the same type, then stick it at the end,
	 * unless it was food, then put it in front.
	 */
	else if (ip == NULL) {
		if (obj->o_type == FOOD) {	/* insert food at front */
			item->l_next = pack;
			pack->l_prev = item;
			pack = item;
			item->l_prev = NULL;
		}
		else {						/* insert other stuff at back */
			lp->l_next = item;
			item->l_prev = lp;
		}
	}
	/*
	 * Here, we found at least one item of the same type.
	 * Look thru these items to see if there is one of the
	 * same group. If so, increment the count and throw the
	 * new item away. If not, stick it at the end of the
	 * items with the same type. Also keep all similar
	 * objects near each other, like all identify scrolls, etc.
	 */
	else {
		struct linked_list **save;

		while (ip != NULL && op->o_type == obj->o_type) {
			if (op->o_group == obj->o_group) {
				if (op->o_flags == obj->o_flags) {
					op->o_count++;
					discard(item);
					item = ip;
					goto picked_up;
				}
				else {
					goto around;
				}
			}
			if (op->o_which == obj->o_which) {
				if (obj->o_type == FOOD)
					ip = next(ip);
				break;
			}
around:
			ip = next(ip);
			if (ip != NULL) {
				op = OBJPTR(ip);
				lp = next(lp);
			}
		}
		/*
		 * If inserting into last of group at end of pack,
		 * just tack on the end.
		 */
		if (ip == NULL) {
			lp->l_next = item;
			item->l_prev = lp;
		}
		/*
		 * Insert into the last of a group of objects
		 * not at the end of the pack.
		 */
		else {
			save = &((ip->l_prev)->l_next);
			item->l_next = ip;
			item->l_prev = ip->l_prev;
			ip->l_prev = item;
			*save = item;
		}
	}
picked_up:
	obj = OBJPTR(item);
	if (!silent)
		msg("%s (%c)",inv_name(obj,FALSE),pack_char(obj));
	if (obj->o_type == AMULET)
		amulet = TRUE;
	updpack();				/* new pack weight & volume */
	return TRUE;
}

/*
 * inventory:
 *	Show what items are in a specific list
 */
bool
inventory(struct linked_list *list, int type)
{
	reg struct linked_list *pc;
	reg struct object *obj;
	reg char ch;
	reg int cnt;

	if (list == NULL) {			/* empty list */
		msg(type == 0 ? "Empty handed." : "Nothing appropriate.");
		return FALSE;
	}
	else if (next(list) == NULL) {	/* only 1 item in list */
		obj = OBJPTR(list);
		msg("a) %s", inv_name(obj, FALSE));
		return TRUE;
	}
	cnt = 0;
	wclear(hw);
	for (ch = 'a', pc = list; pc != NULL; pc = next(pc), ch = npch(ch)) {
		obj = OBJPTR(pc);
		wprintw(hw,"%c) %s\n\r",ch,inv_name(obj, FALSE));
		if (++cnt > LINES - 2 && next(pc) != NULL) {
			dbotline(hw, morestr);
			cnt = 0;
			wclear(hw);
		} 
	}
	dbotline(hw,spacemsg);
	restscr(cw);
	return TRUE;
}

/*
 * pick_up:
 *	Add something to characters pack.
 */
void
pick_up(char ch)
{
	nochange = FALSE;
	switch(ch) {
		case GOLD:
			money();
		when ARMOR:
		case POTION:
		case FOOD:
		case WEAPON:
		case SCROLL:	
		case AMULET:
		case RING:
		case STICK:
			add_pack(NULL, FALSE);
		otherwise:
			msg("That item is ethereal !!!");
	}
}

/*
 * picky_inven:
 *	Allow player to inventory a single item
 */
void
picky_inven(void)
{
	reg struct linked_list *item;
	reg char ch, mch;

	if (pack == NULL)
		msg("You aren't carrying anything.");
	else if (next(pack) == NULL)
		msg("a) %s", inv_name(OBJPTR(pack), FALSE));
	else {
		msg("Item: ");
		mpos = 0;
		if ((mch = readchar()) == ESCAPE) {
			msg("");
			return;
		}
		for (ch='a',item=pack; item != NULL; item=next(item),ch=npch(ch))
			if (ch == mch) {
				msg("%c) %s",ch,inv_name(OBJPTR(item), FALSE));
				return;
			}
		if (ch == 'A')
			ch = 'z';
		else
			ch -= 1;
		msg("Range is 'a' to '%c'", ch);
	}
}

/*
 * get_item:
 *	pick something out of a pack for a purpose
 */
struct linked_list *
get_item(char *purpose, int type)
{
	reg struct linked_list *obj, *pit, *savepit = NULL;
	struct object *pob;
	int ch, och, anr, cnt;

	if (pack == NULL) {
		msg("You aren't carrying anything.");
		return NULL;
	}
	if (type != WEAPON && (type != 0 || next(pack) == NULL)) {
		/*
		 * see if we have any of the type requested
		 */
		pit = pack;
		anr = 0;
		for (ch = 'a'; pit != NULL; pit = next(pit), ch = npch(ch)) {
			pob = OBJPTR(pit);
			if (type == pob->o_type || type == 0) {
				++anr;
				savepit = pit;	/* save in case of only 1 */
			}
		}
		if (anr == 0) {
			msg("Nothing to %s",purpose);
			after = FALSE;
			return NULL;
		}
		else if (anr == 1) {	/* only found one of 'em */
			do {
				struct object *opb;

				opb = OBJPTR(savepit);
				msg("%s what (* for the item)?",purpose);
				och = readchar();
				if (och == '*') {
					mpos = 0;
					msg("%c) %s",pack_char(opb),inv_name(opb,FALSE));
					continue;
				}
				if (och == ESCAPE) {
					msg("");
					after = FALSE;
					return NULL;
				}
				if (isalpha(och) && och != pack_char(opb)) {
					mpos = 0;
					msg("You can't %s that !!", purpose);
					after = FALSE;
					return NULL;
				}
			} while(!isalpha(och));
			mpos = 0;
			return savepit;		/* return this item */
		}
	}
	for (;;) {
		msg("%s what? (* for list): ",purpose);
		ch = readchar();
		mpos = 0;
		if (ch == ESCAPE) {		/* abort if escape hit */
			after = FALSE;
			msg("");			/* clear display */
			return NULL;
		}
		if (ch == '*') {
			wclear(hw);
			pit = pack;		/* point to pack */
			cnt = 0;
			for (ch='a'; pit != NULL; pit=next(pit), ch=npch(ch)) {
				pob = OBJPTR(pit);
				if (type == 0 || type == pob->o_type) {
					wprintw(hw,"%c) %s\n\r",ch,inv_name(pob,FALSE));
					if (++cnt > LINES - 2 && next(pit) != NULL) {
						cnt = 0;
						dbotline(hw, morestr);
						wclear(hw);
					}
				}
			}
			wmove(hw, LINES - 1,0);
			wprintw(hw,"%s what? ",purpose);
			draw(hw);		/* write screen */
			anr = FALSE;
			do {
				ch = readchar();
				if (isalpha(ch) || ch == ESCAPE)
					anr = TRUE; 
			} while(!anr);		/* do till we got it right */
			restscr(cw);		/* redraw orig screen */
			if (ch == ESCAPE) {
				after = FALSE;
				msg("");		/* clear top line */
				return NULL;	/* all done if abort */
			}
			/* ch has item to get from pack */
		}
		for (obj=pack,och='a';obj!=NULL;obj=next(obj),och=npch(och))
			if (ch == och)
				break;
		if (obj == NULL) {
			if (och == 'A')
				och = 'z';
			else
				och -= 1;
			msg("Please specify a letter between 'a' and '%c'",och);
			continue;
		}
		else 
			return obj;
	}
}

/*
 * pack_char:
 *	Get the character of a particular item in the pack
 */
char
pack_char(struct object *obj)
{
	reg struct linked_list *item;
	reg char c;

	c = 'a';
	for (item = pack; item != NULL; item = next(item))
		if (OBJPTR(item) == obj)
			return c;
		else
			c = npch(c);
	return '%';
}

/*
 * idenpack:
 *	Identify all the items in the pack
 */
void
idenpack(void)
{
	reg struct linked_list *pc;

	for (pc = pack ; pc != NULL ; pc = next(pc))
		whatis(pc);
}


/* 
 * del_pack:
 *	Take something out of the hero's pack
 */
void
del_pack(struct linked_list *what)
{
	reg struct object *op;

	op = OBJPTR(what);
	cur_null(op);		/* check for current stuff */
	if (op->o_count > 1) {
		op->o_count--;
	}
	else {
		detach(pack,what);
		discard(what);
	}
	updpack();
}

/*
 * cur_null:
 *	This updates cur_weapon etc for dropping things
 */
void
cur_null(struct object *op)
{
	if (op == cur_weapon)
		cur_weapon = NULL;
	else if (op == cur_armor)
		cur_armor = NULL;
	else if (op == cur_ring[LEFT])
		cur_ring[LEFT] = NULL;
	else if (op == cur_ring[RIGHT])
		cur_ring[RIGHT] = NULL;
}
