/*
 * Routines to deal with the pack
 *
 * @(#)pack.c	4.15 (Berkeley) 4/6/82
 *
 * Rogue: Exploring the Dungeons of Doom
 * Copyright (C) 1980, 1981, 1982 Michael Toy, Ken Arnold and Glenn Wichman
 * All rights reserved.
 *
 * See the file LICENSE.TXT for full copyright and licensing information.
 */

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

void money(int value);

/*
 * update_mdest:
 *	Called after picking up an object, before discarding it.
 *	If this was the object of something's desire, that monster will
 *	get mad and run at the hero
 */
void
update_mdest(THING *obj)
{
    register THING *mp;

    for (mp = mlist; mp != NULL; mp = next(mp))
	if (mp->t_dest == &obj->o_pos)
     mp->t_dest = &hero;
}

/*
 * 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 gettting
 *	it off the ground.
 */
void
add_pack(THING *obj, bool silent)
{
    register THING *op, *lp = NULL;
    register bool exact, from_floor;
    register char floor;
    int discarded = 0;

    if (obj == NULL)
    {
	from_floor = TRUE;
	if ((obj = find_obj(hero.y, hero.x)) == NULL)
	    return;
    }
    else
	from_floor = FALSE;
    /*
     * Link it into the pack.  Search the pack for a object of similar type
     * if there isn't one, stuff it at the beginning, if there is, look for one
     * that is exactly the same and just increment the count if there is.
     * it  that.  Food is always put at the beginning for ease of access, but
     * is not ordered so that you can't tell good food from bad.  First check
     * to see if there is something in thr same group and if there is then
     * increment the count.
     */
/*    floor = (proom->r_flags & ISGONE) ? PASSAGE : FLOOR; */
    if (proom) floor = (proom->r_flags & ISGONE) ? PASSAGE : FLOOR;
    else floor = FLOOR;
    if (obj->o_group)
    {
	for (op = pack; op != NULL; op = next(op))
	{
	    if (op->o_group == obj->o_group)
	    {
		/*
		 * Put it in the pack and notify the user
		 */
		op->o_count++;
		if (from_floor)
		{
		    detach(lvl_obj, obj);
		    mvaddch(hero.y, hero.x, floor);
		    chat(hero.y, hero.x) = floor;
		}
		update_mdest(obj);
		discard(obj);
		obj = op;
		discarded = 1;
		goto picked_up;
	    }
	}
    }
    /*
     * Check if there is room
     */
    if (inpack == MAXPACK-1)
    {
	msg("you can't carry anything else");
	return;
    }
    /*
     * Check for and deal with scare monster scrolls
     */
    if (obj->o_type == SCROLL && obj->o_which == S_SCARE)
	if (obj->o_flags & ISFOUND)
	{
	    detach(lvl_obj, obj);
	    mvaddch(hero.y, hero.x, floor);
	    chat(hero.y, hero.x) = floor;
	    msg("the scroll turns to dust as you pick it up");
	    return;
	}
	else
	    obj->o_flags |= ISFOUND;

    inpack++;
    if (from_floor)
    {
	detach(lvl_obj, obj);
	mvaddch(hero.y, hero.x, floor);
	chat(hero.y, hero.x) = floor;
    }
    /*
     * Search for an object of the same type
     */
    exact = FALSE;
    for (op = pack; op != NULL; op = next(op))
	if (obj->o_type == op->o_type)
	    break;
    if (op == NULL)
    {
	/*
	 * Put it at the end of the pack since it is a new type
	 */
	for (op = pack; op != NULL; op = next(op))
	{
	    if (op->o_type != FOOD)
		break;
	    lp = op;
	}
    }
    else
    {
	/*
	 * Search for an object which is exactly the same
	 */
	while (op->o_type == obj->o_type)
	{
	    if (op->o_which == obj->o_which)
	    {
		exact = TRUE;
		break;
	    }
	    lp = op;
	    if ((op = next(op)) == NULL)
		break;
	}
    }
    if (op == NULL)
    {
	/*
	 * Didn't find an exact match, just stick it here
	 */
	if (pack == NULL)
	    pack = obj;
	else
	{
	    lp->l_next = obj;
	    obj->l_prev = lp;
	    obj->l_next = NULL;
	}
    }
    else
    {
	/*
	 * If we found an exact match.  If it is a potion, food, or a 
	 * scroll, increase the count, otherwise put it with its clones.
	 */
	if (exact && ISMULT(obj->o_type))
	{
	    op->o_count++;
	    update_mdest(obj);
	    discard(obj);
	    obj = op;
	    discarded = 1;
	    goto picked_up;
	}
	if ((obj->l_prev = prev(op)) != NULL)
	    obj->l_prev->l_next = obj;
	else
	    pack = obj;
	obj->l_next = op;
	op->l_prev = obj;
    }
picked_up:
    /*
     * If this was the object of something's desire, that monster will
     * get mad and run at the hero
     */
    if (!discarded)
        update_mdest(obj);

    if (obj->o_type == AMULET)
	amulet = TRUE;
    /*
     * Notify the user
     */
    if (!silent)
    {
	if (!terse)
	    addmsg("you now have ");
	msg("%s (%c)", inv_name(obj, !terse), pack_char(obj));
    }
}

/*
 * inventory:
 *	List what is in the pack
 */
bool
inventory(THING *list, int type)
{
    register char ch;
    register int n_objs;
    char inv_temp[MAXSTR];

    n_objs = 0;
    for (ch = 'a'; list != NULL; ch++, list = next(list))
    {
	if (type && type != list->o_type && !(type == CALLABLE &&
	    (list->o_type == SCROLL || list->o_type == POTION ||
	     list->o_type == RING || list->o_type == STICK)))
		continue;
	n_objs++;
	sprintf(inv_temp, "%c) %%s", ch);
	add_line(inv_temp, inv_name(list, FALSE));
    }
    if (n_objs == 0)
    {
	if (terse)
	    msg(type == 0 ? "empty handed" :
			    "nothing appropriate");
	else
	    msg(type == 0 ? "you are empty handed" :
			    "you don't have anything appropriate");
	return FALSE;
    }
    end_line();
    return TRUE;
}

/*
 * pick_up:
 *	Add something to characters pack.
 */
void
pick_up(char ch)
{
    register THING *obj, *mp;

    switch (ch)
    {
	case GOLD:
	    if ((obj = find_obj(hero.y, hero.x)) == NULL)
		return;
	    money(obj->o_goldval);
	    detach(lvl_obj, obj);
	    update_mdest(obj);
	    discard(obj);
	    proom->r_goldval = 0;
	    break;
	default:
#ifdef WIZARD
	    debug("Where did you pick a '%s' up???", unctrol(ch));
#endif
	case ARMOR:
	case POTION:
	case FOOD:
	case WEAPON:
	case SCROLL:	
	case AMULET:
	case RING:
	case STICK:
	    add_pack(NULL, FALSE);
	    break;
    }
}

/*
 * picky_inven:
 *	Allow player to inventory a single item
 */
void
picky_inven(void)
{
    register THING *obj;
    register char ch, mch;

    if (pack == NULL)
	msg("you aren't carrying anything");
    else if (next(pack) == NULL)
	msg("a) %s", inv_name(pack, FALSE));
    else
    {
	msg(terse ? "item: " : "which item do you wish to inventory: ");
	mpos = 0;
	if ((mch = readchar()) == ESCAPE)
	{
	    msg("");
	    return;
	}
	for (ch = 'a', obj = pack; obj != NULL; obj = next(obj), ch++)
	    if (ch == mch)
	    {
		msg("%c) %s",ch,inv_name(obj, FALSE));
		return;
	    }
	if (!terse)
	    msg("'%s' not in pack", unctrol(mch));
	msg("range is 'a' to '%c'", --ch);
    }
}

/*
 * get_item:
 *	Pick something out of a pack for a purpose
 */
THING *
get_item(char *purpose, int type)
{
    register THING *obj;
    register char ch, och;

    if (pack == NULL)
	msg("you aren't carrying anything");
    else
    {
	for (;;)
	{
	    if (!terse)
		addmsg("which object do you want to ");
	    addmsg(purpose);
	    if (terse)
		addmsg(" what");
	    msg("? (* for list): ");
	    ch = readchar();
	    mpos = 0;
	    /*
	     * Give the poor player a chance to abort the command
	     */
	    if (ch == ESCAPE || ch == CTRL('G'))
	    {
		after = FALSE;
		msg("");
		return NULL;
	    }
	    if (ch == '*')
	    {
		mpos = 0;
		if (inventory(pack, type) == 0)
		{
		    after = FALSE;
		    return NULL;
		}
		continue;
	    }
	    for (obj = pack, och = 'a'; obj != NULL; obj = next(obj), och++)
		if (ch == och)
		    break;
	    if (obj == NULL)
	    {
		msg("please specify a letter between 'a' and '%c'", och-1);
		continue;
	    }
	    else 
		return obj;
	}
    }
    return NULL;
}

/*
 * pack_char:
 *	Return which character would address a pack object
 */
char
pack_char(THING *obj)
{
    register THING *item;
    register char c;

    c = 'a';
    for (item = pack; item != NULL; item = next(item))
	if (item == obj)
	    return c;
	else
	    c++;
    return '?';
}

/*
 * money:
 *	Add or subtract gold from the pack
 */
void
money(int value)
{
    register char floor;

    floor = (proom->r_flags & ISGONE) ? PASSAGE : FLOOR;
    purse += value;
    mvaddch(hero.y, hero.x, floor);
    chat(hero.y, hero.x) = floor;
    if (value > 0)
    {
	if (!terse)
	    addmsg("you found ");
	msg("%d gold pieces", value);
    }
}
