Logo Search packages:      
Sourcecode: falconseye version File versions  Download package

pickup.c

/*    SCCS Id: @(#)pickup.c   3.3   2000/03/01  */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

/*
 *    Contains code for picking objects up, and container use.
 */

#include "hack.h"

STATIC_DCL void FDECL(simple_look, (struct obj *,BOOLEAN_P));
STATIC_DCL boolean FDECL(query_classes, (char *,boolean *,boolean *,
            const char *,struct obj *,BOOLEAN_P,BOOLEAN_P,int *));
STATIC_DCL void FDECL(check_here, (BOOLEAN_P));
STATIC_DCL boolean FDECL(n_or_more, (struct obj *));
STATIC_DCL boolean FDECL(all_but_uchain, (struct obj *));
#if 0 /* not used */
STATIC_DCL boolean FDECL(allow_cat_no_uchain, (struct obj *));
#endif
STATIC_DCL int FDECL(autopick, (struct obj*, int, menu_item **));
STATIC_DCL int FDECL(count_categories, (struct obj *,int));
STATIC_DCL long FDECL(carry_count,
                  (struct obj *,struct obj *,long,BOOLEAN_P,int *,int *));
STATIC_DCL int FDECL(lift_object, (struct obj *,struct obj *,long *,BOOLEAN_P));
STATIC_DCL boolean FDECL(mbag_explodes, (struct obj *,int));
STATIC_PTR int FDECL(in_container,(struct obj *));
STATIC_PTR int FDECL(ck_bag,(struct obj *));
STATIC_PTR int FDECL(out_container,(struct obj *));
STATIC_DCL int FDECL(menu_loot, (int, struct obj *, BOOLEAN_P));
STATIC_DCL int FDECL(in_or_out_menu, (const char *,struct obj *));
STATIC_DCL int FDECL(container_at, (int, int, BOOLEAN_P));
STATIC_DCL boolean FDECL(able_to_loot, (int, int));
STATIC_DCL boolean FDECL(mon_beside, (int, int));

/* define for query_objlist() and autopickup() */
#define FOLLOW(curr, flags) \
    (((flags) & BY_NEXTHERE) ? (curr)->nexthere : (curr)->nobj)

/*
 *  How much the weight of the given container will change when the given
 *  object is removed from it.  This calculation must match the one used
 *  by weight() in mkobj.c.
 */
#define DELTA_CWT(cont,obj)         \
    ((cont)->cursed ? (obj)->owt * 2 :    \
                  1 + ((obj)->owt / ((cont)->blessed ? 4 : 2)))
#define GOLD_WT(n)            (((n) + 50L) / 100L)
/* if you can figure this out, give yourself a hearty pat on the back... */
#define GOLD_CAPACITY(w,n)    (((w) * -100L) - ((n) + 50L) - 1L)

static const char moderateloadmsg[] = "You have a little trouble lifting";
static const char nearloadmsg[] = "You have much trouble lifting";
static const char overloadmsg[] = "You have extreme difficulty lifting";

/* BUG: this lets you look at cockatrice corpses while blind without
   touching them */
/* much simpler version of the look-here code; used by query_classes() */
STATIC_OVL void
simple_look(otmp, here)
struct obj *otmp; /* list of objects */
boolean here;           /* flag for type of obj list linkage */
{
      /* Neither of the first two cases is expected to happen, since
       * we're only called after multiple classes of objects have been
       * detected, hence multiple objects must be present.
       */
      if (!otmp) {
          impossible("simple_look(null)");
      } else if (!(here ? otmp->nexthere : otmp->nobj)) {
          pline("%s", doname(otmp));
      } else {
          winid tmpwin = create_nhwindow(NHW_MENU);
          putstr(tmpwin, 0, "");
          do {
            putstr(tmpwin, 0, doname(otmp));
            otmp = here ? otmp->nexthere : otmp->nobj;
          } while (otmp);
          display_nhwindow(tmpwin, TRUE);
          destroy_nhwindow(tmpwin);
      }
}

int
collect_obj_classes(ilets, otmp, here, incl_gold, filter)
char ilets[];
register struct obj *otmp;
boolean here, incl_gold;
boolean FDECL((*filter),(OBJ_P));
{
      register int iletct = 0;
      register char c;

      if (incl_gold)
          ilets[iletct++] = def_oc_syms[GOLD_CLASS];
      ilets[iletct] = '\0'; /* terminate ilets so that index() will work */
      while (otmp) {
          c = def_oc_syms[(int)otmp->oclass];
          if (!index(ilets, c) && (!filter || (*filter)(otmp)))
            ilets[iletct++] = c,  ilets[iletct] = '\0';
          otmp = here ? otmp->nexthere : otmp->nobj;
      }

      return iletct;
}

/*
 * Suppose some '?' and '!' objects are present, but '/' objects aren't:
 *    "a" picks all items without further prompting;
 *    "A" steps through all items, asking one by one;
 *    "?" steps through '?' items, asking, and ignores '!' ones;
 *    "/" becomes 'A', since no '/' present;
 *    "?a" or "a?" picks all '?' without further prompting;
 *    "/a" or "a/" becomes 'A' since there aren't any '/'
 *        (bug fix:  3.1.0 thru 3.1.3 treated it as "a");
 *    "?/a" or "a?/" or "/a?",&c picks all '?' even though no '/'
 *        (ie, treated as if it had just been "?a").
 */
STATIC_OVL boolean
query_classes(oclasses, one_at_a_time, everything, action, objs,
            here, incl_gold, menu_on_demand)
char oclasses[];
boolean *one_at_a_time, *everything;
const char *action;
struct obj *objs;
boolean here, incl_gold;
int *menu_on_demand;
{
      char ilets[20], inbuf[BUFSZ];
      int iletct, oclassct;
      boolean not_everything;
      char qbuf[QBUFSZ];
      boolean m_seen;

      oclasses[oclassct = 0] = '\0';
      *one_at_a_time = *everything = m_seen = FALSE;
      iletct = collect_obj_classes(ilets, objs, here, incl_gold,
                             (boolean FDECL((*),(OBJ_P))) 0);
      if (iletct == 0) {
            return FALSE;
      } else if (iletct == 1) {
            oclasses[0] = def_char_to_objclass(ilets[0]);
            oclasses[1] = '\0';
      } else  {   /* more than one choice available */
            const char *where = 0;
            register char sym, oc_of_sym, *p;
            /* additional choices */
            ilets[iletct++] = ' ';
            ilets[iletct++] = 'a';
            ilets[iletct++] = 'A';
            ilets[iletct++] = (objs == invent ? 'i' : ':');
            if (menu_on_demand) {
                  ilets[iletct++] = 'm';
                  *menu_on_demand = 0;
            }
            ilets[iletct] = '\0';
ask_again:
            oclasses[oclassct = 0] = '\0';
            *one_at_a_time = *everything = FALSE;
            not_everything = FALSE;
            Sprintf(qbuf,"What kinds of thing do you want to %s? [%s]",
                  action, ilets);
            getlin(qbuf,inbuf);
            if (*inbuf == '\033') return FALSE;

            for (p = inbuf; (sym = *p++); ) {
                /* new A function (selective all) added by GAN 01/09/87 */
                if (sym == ' ') continue;
                else if (sym == 'A') *one_at_a_time = TRUE;
                else if (sym == 'a') *everything = TRUE;
                else if (sym == ':') {
                  simple_look(objs, here);  /* dumb if objs==invent */
                  goto ask_again;
                } else if (sym == 'i') {
                  (void) display_inventory((char *)0, TRUE);
                  goto ask_again;
                } else if (sym == 'm') {
                  m_seen = TRUE;
                } else {
                  oc_of_sym = def_char_to_objclass(sym);
                  if (index(ilets,sym)) {
                      add_valid_menu_class(oc_of_sym);
                      oclasses[oclassct++] = oc_of_sym;
                      oclasses[oclassct] = '\0';
                  } else {
                      if (!where)
                        where = !strcmp(action,"pick up")  ? "here" :
                              !strcmp(action,"take out") ?
                                              "inside" : "";
                      if (*where)
                        There("are no %c's %s.", sym, where);
                      else
                        You("have no %c's.", sym);
                      not_everything = TRUE;
                  }
                }
            }
            if (m_seen && menu_on_demand) {
                  *menu_on_demand = (*everything || !oclassct) ? -2 : -3;
                  return FALSE;
            }
            if (!oclassct && (!*everything || not_everything)) {
                /* didn't pick anything,
                   or tried to pick something that's not present */
                *one_at_a_time = TRUE;    /* force 'A' */
                *everything = FALSE;      /* inhibit 'a' */
            }
      }
      return TRUE;
}

/* look at the objects at our location, unless there are too many of them */
STATIC_OVL void
check_here(picked_some)
boolean picked_some;
{
      register struct obj *obj;
      register int ct = 0;

      /* count the objects here */
      for (obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere) {
          if (obj != uchain)
            ct++;
      }

      /* If there are objects here, take a look. */
      if (ct) {
          if (flags.run) nomul(0);
          flush_screen(1);
          (void) look_here(ct, picked_some);
      } else {
          read_engr_at(u.ux,u.uy);
      }
}

/* Value set by query_objlist() for n_or_more(). */
static long val_for_n_or_more;

/* query_objlist callback: return TRUE if obj's count is >= reference value */
STATIC_OVL boolean
n_or_more(obj)
struct obj *obj;
{
    if (obj == uchain) return FALSE;
    return (obj->quan >= val_for_n_or_more);
}

/* List of valid menu classes for query_objlist() and allow_category callback */
static char valid_menu_classes[MAXOCLASSES + 2];

void
add_valid_menu_class(c)
int c;
{
      static int vmc_count = 0;

      if (c == 0)  /* reset */
        vmc_count = 0;
      else
        valid_menu_classes[vmc_count++] = (char)c;
      valid_menu_classes[vmc_count] = '\0';
}

/* query_objlist callback: return TRUE if not uchain */
STATIC_OVL boolean
all_but_uchain(obj)
struct obj *obj;
{
    return (obj != uchain);
}

/* query_objlist callback: return TRUE */
/*ARGSUSED*/
boolean
allow_all(obj)
struct obj *obj;
{
    return TRUE;
}

boolean
allow_category(obj)
struct obj *obj;
{
    if (((index(valid_menu_classes,'u') != (char *)0) && obj->unpaid) ||
      (index(valid_menu_classes, obj->oclass) != (char *)0))
      return TRUE;
    else
      return FALSE;
}

#if 0 /* not used */
/* query_objlist callback: return TRUE if valid category (class), no uchain */
STATIC_OVL boolean
allow_cat_no_uchain(obj)
struct obj *obj;
{
    if ((obj != uchain) &&
      (((index(valid_menu_classes,'u') != (char *)0) && obj->unpaid) ||
      (index(valid_menu_classes, obj->oclass) != (char *)0)))
      return TRUE;
    else
      return FALSE;
}
#endif

/* query_objlist callback: return TRUE if valid class and worn */
boolean
is_worn_by_type(otmp)
register struct obj *otmp;
{
      return((boolean)(!!(otmp->owornmask &
                  (W_ARMOR | W_RING | W_AMUL | W_TOOL | W_WEP | W_SWAPWEP | W_QUIVER)))
              && (index(valid_menu_classes, otmp->oclass) != (char *)0));
}

/*
 * Have the hero pick things from the ground.
 *
 * Arg what:
 *    >0  autopickup
 *    =0  interactive
 *    <0  pickup count of something
 *
 * Returns 1 if tried to pick something up, whether
 * or not it succeeded.
 */
int
pickup(what)
int what;         /* should be a long */
{
      int i, n, res, count, n_tried = 0, n_picked = 0;
      menu_item *pick_list = (menu_item *) 0;
      boolean autopickup = what > 0;

      if (what < 0)           /* pick N of something */
          count = -what;
      else              /* pick anything */
          count = 0;

      /* no auto-pick if no-pick move, nothing there, or in a pool */
      if (autopickup && (flags.nopick || !OBJ_AT(u.ux, u.uy) ||
                  (is_pool(u.ux, u.uy) && !Underwater) || is_lava(u.ux, u.uy))) {
          read_engr_at(u.ux, u.uy);
          return (0);
      }

      /* no pickup if levitating & not on air or water level */
      if (!can_reach_floor()) {
          if ((multi && !flags.run) || (autopickup && !flags.pickup))
            read_engr_at(u.ux, u.uy);
          return (0);
      }

      /* multi && !flags.run means they are in the middle of some other
       * action, or possibly paralyzed, sleeping, etc.... and they just
       * teleported onto the object.  They shouldn't pick it up.
       */
      if ((multi && !flags.run) || (autopickup && !flags.pickup)) {
          check_here(FALSE);
          return (0);
      }

      if (notake(youmonst.data)) {
          if (!autopickup)
            You("are physically incapable of picking anything up.");
          else
            check_here(FALSE);
          return (0);
      }

      /* if there's anything here, stop running */
      if (OBJ_AT(u.ux,u.uy) && flags.run && !flags.nopick) nomul(0);

      add_valid_menu_class(0);      /* reset */
      /*
       * Start the actual pickup process.  This is split into two main
       * sections, the newer menu and the older "traditional" methods.
       * Automatic pickup has been split into its own menu-style routine
       * to make things less confusing.
       */
      if (autopickup) {
          n = autopick(level.objects[u.ux][u.uy], BY_NEXTHERE, &pick_list);
          goto menu_pickup;
      }

      if (flags.menu_style != MENU_TRADITIONAL) {
          /* use menus exclusively */

          if (count) {  /* looking for N of something */
            char buf[QBUFSZ];
            Sprintf(buf, "Pick %d of what?", count);
            val_for_n_or_more = count;    /* set up callback selector */
            n = query_objlist(buf, level.objects[u.ux][u.uy],
                      BY_NEXTHERE|AUTOSELECT_SINGLE|INVORDER_SORT,
                      &pick_list, PICK_ONE, n_or_more);
            /* correct counts, if any given */
            for (i = 0; i < n; i++)
                pick_list[i].count = count;
          } else {
            n = query_objlist("Pick up what?", level.objects[u.ux][u.uy],
                      BY_NEXTHERE|AUTOSELECT_SINGLE|INVORDER_SORT,
                      &pick_list, PICK_ANY, all_but_uchain);
          }
menu_pickup:
          n_tried = n;
          for (n_picked = i = 0 ; i < n; i++) {
            res = pickup_object(pick_list[i].item.a_obj,pick_list[i].count,
                              FALSE);
            if (res < 0) break;     /* can't continue */
            n_picked += res;
          }
          if (pick_list) free((genericptr_t)pick_list);

      } else {
          /* old style interface */
          int ct = 0;
          long lcount;
          boolean all_of_a_type, selective;
          char oclasses[MAXOCLASSES];
          struct obj *obj, *obj2;

          oclasses[0] = '\0';       /* types to consider (empty for all) */
          all_of_a_type = TRUE;     /* take all of considered types */
          selective = FALSE;        /* ask for each item */

          /* check for more than one object */
          for (obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere)
            ct++;

          if (ct == 1 && count) {
            /* if only one thing, then pick it */
            obj = level.objects[u.ux][u.uy];
            lcount = min(obj->quan, (long)count);
            n_tried++;
            if (pickup_object(obj, lcount, FALSE) > 0)
                n_picked++;   /* picked something */
            goto end_query;

          } else if (ct >= 2) {
            int via_menu = 0;

            There("are %s objects here.",
                  (ct <= 10) ? "several" : "many");
            if (!query_classes(oclasses, &selective, &all_of_a_type,
                           "pick up", level.objects[u.ux][u.uy],
                           TRUE, FALSE, &via_menu)) {
                if (!via_menu) return (0);
                n = query_objlist("Pick up what?",
                          level.objects[u.ux][u.uy],
                          BY_NEXTHERE|(selective ? 0 : INVORDER_SORT),
                          &pick_list, PICK_ANY,
                          via_menu == -2 ? allow_all : allow_category);
                goto menu_pickup;
            }
          }

          for (obj = level.objects[u.ux][u.uy]; obj; obj = obj2) {
            obj2 = obj->nexthere;   /* perhaps obj will be picked up */
            lcount = -1L;

            if (!selective && oclasses[0] && !index(oclasses,obj->oclass))
                continue;

            if (!all_of_a_type) {
                char qbuf[QBUFSZ];
                Sprintf(qbuf, "Pick up %s?", doname(obj));
                switch ((obj->quan < 2L) ? ynaq(qbuf) : ynNaq(qbuf)) {
                case 'q': goto end_query; /* out 2 levels */
                case 'n': continue;
                case 'a':
                  all_of_a_type = TRUE;
                  if (selective) {
                      selective = FALSE;
                      oclasses[0] = obj->oclass;
                      oclasses[1] = '\0';
                  }
                  break;
                case '#':     /* count was entered */
                  if (!yn_number) continue; /* 0 count => No */
                  lcount = (long) yn_number;
                  if (lcount > obj->quan) lcount = obj->quan;
                  /* fall thru */
                default:      /* 'y' */
                  break;
                }
            }
            if (lcount == -1L) lcount = obj->quan;

            n_tried++;
            if ((res = pickup_object(obj, lcount, FALSE)) < 0) break;
            n_picked += res;
          }
end_query:
          ; /* semicolon needed by brain-damaged compilers */
      }

      /* position may need updating (invisible hero) */
      if (n_picked) newsym(u.ux,u.uy);

      /* see whether there's anything else here, after auto-pickup is done */
      if (autopickup) check_here(n_picked > 0);
      return (n_tried > 0);
}

/*
 * Pick from the given list using flags.pickup_types.  Return the number
 * of items picked (not counts).  Create an array that returns pointers
 * and counts of the items to be picked up.  If the number of items
 * picked is zero, the pickup list is left alone.  The caller of this
 * function must free the pickup list.
 */
STATIC_OVL int
autopick(olist, follow, pick_list)
struct obj *olist;      /* the object list */
int follow;       /* how to follow the object list */
menu_item **pick_list;  /* list of objects and counts to pick up */
{
      menu_item *pi;    /* pick item */
      struct obj *curr;
      int n;
      const char *otypes = flags.pickup_types;

      /* first count the number of eligible items */
      for (n = 0, curr = olist; curr; curr = FOLLOW(curr, follow))
          if (!*otypes || index(otypes, curr->oclass))
            n++;

      if (n) {
          *pick_list = pi = (menu_item *) alloc(sizeof(menu_item) * n);
          for (n = 0, curr = olist; curr; curr = FOLLOW(curr, follow))
            if (!*otypes || index(otypes, curr->oclass)) {
                pi[n].item.a_obj = curr;
                pi[n].count = curr->quan;
                n++;
            }
      }
      return n;
}


/*
 * Put up a menu using the given object list.  Only those objects on the
 * list that meet the approval of the allow function are displayed.  Return
 * a count of the number of items selected, as well as an allocated array of
 * menu_items, containing pointers to the objects selected and counts.  The
 * returned counts are guaranteed to be in bounds and non-zero.
 *
 * Query flags:
 *    BY_NEXTHERE   - Follow object list via nexthere instead of nobj.
 *    AUTOSELECT_SINGLE - Don't ask if only 1 object qualifies - just
 *                    use it.
 *    USE_INVLET    - Use object's invlet.
 *    INVORDER_SORT       - Use hero's pack order.
 *    SIGNAL_NOMENU       - Return -1 rather than 0 if nothing passes "allow".
 */
int
query_objlist(qstr, olist, qflags, pick_list, how, allow)
const char *qstr;       /* query string */
struct obj *olist;            /* the list to pick from */
int qflags;             /* options to control the query */
menu_item **pick_list;        /* return list of items picked */
int how;                /* type of query */
boolean FDECL((*allow), (OBJ_P));/* allow function */
{
      int n;
      winid win;
      struct obj *curr, *last;
      char *pack;
      anything any;
      boolean printed_type_name;

      *pick_list = (menu_item *) 0;
      if (!olist) return 0;

      /* count the number of items allowed */
      for (n = 0, last = 0, curr = olist; curr; curr = FOLLOW(curr, qflags))
          if ((*allow)(curr)) {
            last = curr;
            n++;
          }

      if (n == 0) /* nothing to pick here */
          return (qflags & SIGNAL_NOMENU) ? -1 : 0;

      if (n == 1 && (qflags & AUTOSELECT_SINGLE)) {
          *pick_list = (menu_item *) alloc(sizeof(menu_item));
          (*pick_list)->item.a_obj = last;
          (*pick_list)->count = last->quan;
          return 1;
      }

      win = create_nhwindow(NHW_MENU);
      start_menu(win);
      any.a_obj = (struct obj *) 0;

      /*
       * Run through the list and add the objects to the menu.  If
       * INVORDER_SORT is set, we'll run through the list once for
       * each type so we can group them.  The allow function will only
       * be called once per object in the list.
       */
      pack = flags.inv_order;
      do {
          printed_type_name = FALSE;
          for (curr = olist; curr; curr = FOLLOW(curr, qflags))
            if ((!(qflags & INVORDER_SORT) || curr->oclass == *pack)
                                          && (*allow)(curr)) {

                /* if sorting, print type name (once only) */
                if (qflags & INVORDER_SORT && !printed_type_name) {
                  any.a_obj = (struct obj *) 0;
                  add_menu(win, NO_GLYPH, &any, 0, 0, ATR_INVERSE,
                              let_to_name(*pack, FALSE), MENU_UNSELECTED);
                  printed_type_name = TRUE;
                }

                any.a_obj = curr;
                add_menu(win, obj_to_glyph(curr), &any,
                      qflags & USE_INVLET ? curr->invlet : 0,
                      def_oc_syms[(int)objects[curr->otyp].oc_class],
                      ATR_NONE, doname(curr), MENU_UNSELECTED);
            }
          pack++;
      } while (qflags & INVORDER_SORT && *pack);

      end_menu(win, qstr);
      n = select_menu(win, how, pick_list);
      destroy_nhwindow(win);

      if (n > 0) {
          menu_item *mi;
          int i;

          /* fix up counts:  -1 means no count used => pick all */
          for (i = 0, mi = *pick_list; i < n; i++, mi++)
            if (mi->count == -1L || mi->count > mi->item.a_obj->quan)
                mi->count = mi->item.a_obj->quan;
      } else if (n < 0) {
          n = 0;  /* caller's don't expect -1 */
      }
      return n;
}

/*
 * allow menu-based category (class) selection (for Drop,take off etc.)
 *
 */
int
query_category(qstr, olist, qflags, pick_list, how)
const char *qstr;       /* query string */
struct obj *olist;            /* the list to pick from */
int qflags;             /* behaviour modification flags */
menu_item **pick_list;        /* return list of items picked */
int how;                /* type of query */
{
      int n;
      winid win;
      struct obj *curr;
      char *pack;
      anything any;
      boolean collected_type_name;
      char invlet;
      int ccount;
      boolean do_unpaid = FALSE;

      *pick_list = (menu_item *) 0;
      if (!olist) return 0;
      if ((qflags & UNPAID_TYPES) && count_unpaid(olist)) do_unpaid = TRUE;

      ccount = count_categories(olist, qflags);
      /* no point in actually showing a menu for a single category */
      if (ccount == 1 && !do_unpaid && !(qflags & BILLED_TYPES)) {
          for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
            if ((qflags & WORN_TYPES) &&
                !(curr->owornmask & (W_ARMOR|W_RING|W_AMUL|W_TOOL|W_WEP|W_SWAPWEP|W_QUIVER)))
                continue;
            break;
          }
          if (curr) {
            *pick_list = (menu_item *) alloc(sizeof(menu_item));
            (*pick_list)->item.a_int = curr->oclass;
            return 1;
          } else {
#ifdef DEBUG
            impossible("query_category: no single object match");
#endif
          }
          return 0;
      }

      win = create_nhwindow(NHW_MENU);
      start_menu(win);
      pack = flags.inv_order;
      if ((qflags & ALL_TYPES) && (ccount > 1)) {
            invlet = 'a';
            any.a_void = 0;
            any.a_int = ALL_TYPES_SELECTED;
            add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
                   (qflags & WORN_TYPES) ? "All worn types" : "All types",
                  MENU_UNSELECTED);
            invlet = 'b';
      } else
            invlet = 'a';
      do {
          collected_type_name = FALSE;
          for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
            if (curr->oclass == *pack) {
               if ((qflags & WORN_TYPES) &&
                        !(curr->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL |
                  W_WEP | W_SWAPWEP | W_QUIVER)))
                   continue;
               if (!collected_type_name) {
                  any.a_void = 0;
                  any.a_int = curr->oclass;
                  add_menu(win, NO_GLYPH, &any, invlet++,
                        def_oc_syms[(int)objects[curr->otyp].oc_class],
                        ATR_NONE, let_to_name(*pack, FALSE),
                        MENU_UNSELECTED);
                  collected_type_name = TRUE;
               }
            }
          }
          pack++;
          if (invlet >= 'u') {
            impossible("query_category: too many categories");
            return 0;
          }
      } while (*pack);
      /* unpaid items if there are any */
      if (do_unpaid) {
            invlet = 'u';
            any.a_void = 0;
            any.a_int = 'u';
            add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
                  "Unpaid items", MENU_UNSELECTED);
      }
      /* billed items: checked by caller, so always include if BILLED_TYPES */
      if (qflags & BILLED_TYPES) {
            invlet = 'x';
            any.a_void = 0;
            any.a_int = 'x';
            add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
                   "Unpaid items already used up", MENU_UNSELECTED);
      }
      if (qflags & CHOOSE_ALL) {
            invlet = 'A';
            any.a_void = 0;
            any.a_int = 'A';
            add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
                  (qflags & WORN_TYPES) ?
                  "Auto-select every item being worn" :
                  "Auto-select every item", MENU_UNSELECTED);
      }
      end_menu(win, qstr);
      n = select_menu(win, how, pick_list);
      destroy_nhwindow(win);
      if (n < 0)
          n = 0;  /* caller's don't expect -1 */
      return n;
}

STATIC_OVL int
count_categories(olist, qflags)
struct obj *olist;
int qflags;
{
      char *pack;
      boolean counted_category;
      int ccount = 0;
      struct obj *curr;

      pack = flags.inv_order;
      do {
          counted_category = FALSE;
          for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
            if (curr->oclass == *pack) {
               if ((qflags & WORN_TYPES) &&
                  !(curr->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL |
                  W_WEP | W_SWAPWEP | W_QUIVER)))
                   continue;
               if (!counted_category) {
                  ccount++;
                  counted_category = TRUE;
               }
            }
          }
          pack++;
      } while (*pack);
      return ccount;
}

/* could we carry `obj'? if not, could we carry some of it/them? */
STATIC_OVL
long carry_count(obj, container, count, telekinesis, wt_before, wt_after)
struct obj *obj, *container;  /* object to pick up, bag it's coming out of */
long count;
boolean telekinesis;
int *wt_before, *wt_after;
{
    boolean adjust_wt = container && carried(container),
          is_gold = obj->oclass == GOLD_CLASS;
    int wt, iw, ow, oow;
    long qq, savequan;
    unsigned saveowt;
    const char *verb, *prefx1, *prefx2, *suffx;
    char obj_nambuf[BUFSZ], where[BUFSZ];

    savequan = obj->quan;
    saveowt = obj->owt;
    iw = max_capacity();
    if (count != savequan) {
      obj->quan = count;
      obj->owt = (unsigned)weight(obj);
    }
    wt = iw + (int)obj->owt;
    if (adjust_wt)
      wt -= (container->otyp == BAG_OF_HOLDING) ?
            (int)DELTA_CWT(container, obj) : (int)obj->owt;
    if (is_gold)  /* merged gold might affect cumulative weight */
      wt -= (GOLD_WT(u.ugold) + GOLD_WT(count) - GOLD_WT(u.ugold + count));
    if (count != savequan) {
      obj->quan = savequan;
      obj->owt = saveowt;
    }
    *wt_before = iw;
    *wt_after  = wt;
    if (wt < 0)
      return count;

    /* see how many we can lift */
    if (is_gold) {
      iw -= (int)GOLD_WT(u.ugold);
      if (!adjust_wt) {
          qq = GOLD_CAPACITY((long)iw, u.ugold);
      } else {
          oow = 0;
          qq = 50L - (u.ugold % 100L) - 1L;
          if (qq < 0L) qq += 100L;
          for ( ; qq <= count; qq += 100L) {
            obj->quan = qq;
            obj->owt = (unsigned)GOLD_WT(qq);
            ow = (int)GOLD_WT(u.ugold + qq);
            ow -= (container->otyp == BAG_OF_HOLDING) ?
                  (int)DELTA_CWT(container, obj) : (int)obj->owt;
            if (iw + ow >= 0) break;
            oow = ow;
          }
          iw -= oow;
          qq -= 100L;
      }
      if (qq < 0L) qq = 0L;
      else if (qq > count) qq = count;
      wt = iw + (int)GOLD_WT(u.ugold + qq);
    } else if (count > 1 || count < obj->quan) {
      /*
       * Ugh. Calc num to lift by changing the quan of of the
       * object and calling weight.
       *
       * This works for containers only because containers
       * don't merge.         -dean
       */
      for (qq = 1L; qq <= count; qq++) {
          obj->quan = qq;
          obj->owt = (unsigned)(ow = weight(obj));
          if (adjust_wt)
            ow -= (container->otyp == BAG_OF_HOLDING) ?
                  (int)DELTA_CWT(container, obj) : (int)obj->owt;
          if (iw + ow >= 0)
            break;
          wt = iw + ow;
      }
      --qq;
    } else {
      /* there's only one, and we can't lift it */
      qq = 0L;
    }
    obj->quan = savequan;
    obj->owt = saveowt;

    if (qq < count) {
      /* some message will be given */
      Strcpy(obj_nambuf, doname(obj));
      if (container) {
          Sprintf(where, "in %s", the(xname(container)));
          verb = "carry";
      } else {
          Strcpy(where, "lying here");
          verb = telekinesis ? "acquire" : "lift";
      }
    } else {
      /* lint supppression */
      *obj_nambuf = *where = '\0';
      verb = "";
    }
    /* we can carry qq of them */
    if (qq > 0) {
      if (qq < count)
          You("can only %s %s of the %s %s.",
            verb, (qq == 1L) ? "one" : "some", obj_nambuf, where);
      *wt_after = wt;
      return qq;
    }

    if (!container) Strcpy(where, "here");  /* slightly shorter form */
    if (invent || u.ugold) {
      prefx1 = "you cannot ";
      prefx2 = "";
      suffx  = " any more";
    } else {
      prefx1 = (obj->quan == 1L) ? "it " : "even one ";
      prefx2 = "is too heavy for you to ";
      suffx  = "";
    }
    There("%s %s %s, but %s%s%s%s.",
        (obj->quan == 1L) ? "is" : "are", obj_nambuf, where,
        prefx1, prefx2, verb, suffx);

 /* *wt_after = iw; */
    return 0L;
}

/* determine whether character is able and player is willing to carry `obj' */
STATIC_OVL
int lift_object(obj, container, cnt_p, telekinesis)
struct obj *obj, *container;  /* object to pick up, bag it's coming out of */
long *cnt_p;
boolean telekinesis;
{
    int result, old_wt, new_wt, prev_encumbr, next_encumbr;


    if (obj->otyp == BOULDER && In_sokoban(&u.uz)) {
      You("cannot get your %s around this %s.",
                  body_part(HAND), xname(obj));
      return -1;
    }
    if (obj->otyp == LOADSTONE ||
          (obj->otyp == BOULDER && throws_rocks(youmonst.data)))
      return 1;         /* lift regardless of current situation */

    *cnt_p = carry_count(obj, container, *cnt_p, telekinesis, &old_wt, &new_wt);
    if (*cnt_p < 1L) {
      result = -1;      /* nothing lifted */
    } else if (obj->oclass != GOLD_CLASS && inv_cnt() >= 52 &&
            !merge_choice(invent, obj)) {
      Your("knapsack cannot accommodate any more items.");
      result = -1;      /* nothing lifted */
    } else {
      result = 1;
      prev_encumbr = near_capacity();
      if (prev_encumbr < flags.pickup_burden)
            prev_encumbr = flags.pickup_burden;
      next_encumbr = calc_capacity(new_wt - old_wt);
      if (next_encumbr > prev_encumbr) {
          if (telekinesis) {
            result = 0; /* don't lift */
          } else {
            char qbuf[QBUFSZ];
            long savequan = obj->quan;

            obj->quan = *cnt_p;
            Sprintf(qbuf, "%s %s.  Continue?",
                  (next_encumbr > HVY_ENCUMBER) ? overloadmsg :
                  (next_encumbr > MOD_ENCUMBER) ? nearloadmsg :
                  moderateloadmsg, doname(obj));
            obj->quan = savequan;
            switch (ynq(qbuf)) {
            case 'q':  result = -1; break;
            case 'n':  result =  0; break;
            default:   break; /* 'y' => result == 1 */
            }
          }
      }
    }

    if (obj->otyp == SCR_SCARE_MONSTER && result <= 0 && !container)
      obj->spe = 0;
    return result;
}

/*
 * Pick up <count> of obj from the ground and add it to the hero's inventory.
 * Returns -1 if caller should break out of its loop, 0 if nothing picked
 * up, 1 if otherwise.
 */
int
pickup_object(obj, count, telekinesis)
struct obj *obj;
long count;
boolean telekinesis;    /* not picking it up directly by hand */
{
      int res, nearload;
      const char *where = (obj->ox == u.ux && obj->oy == u.uy) ?
                      "here" : "there";

      if (obj->quan < count) {
          impossible("pickup_object: count %ld > quan %ld?",
            count, obj->quan);
          return 0;
      }

      /* In case of auto-pickup, where we haven't had a chance
         to look at it yet; affects docall(SCR_SCARE_MONSTER). */
      if (!Blind)
#ifdef INVISIBLE_OBJECTS
            if (!obj->oinvis || See_invisible)
#endif
            obj->dknown = 1;

      if (obj == uchain) {    /* do not pick up attached chain */
          return 0;
      } else if (obj->oartifact && !touch_artifact(obj,&youmonst)) {
          return 0;
      } else if (obj->oclass == GOLD_CLASS) {
          /* Special consideration for gold pieces... */
          long iw = (long)max_capacity() - GOLD_WT(u.ugold);
          long gold_capacity = GOLD_CAPACITY(iw, u.ugold);

          if (gold_capacity <= 0L) {
            pline(
             "There %s %ld gold piece%s %s, but you cannot carry any more.",
                  (obj->quan == 1L) ? "is" : "are",
                  obj->quan, plur(obj->quan), where);
            return 0;
          } else if (gold_capacity < count) {
            You("can only %s %s of the %ld gold pieces lying %s.",
                telekinesis ? "acquire" : "carry",
                gold_capacity == 1L ? "one" : "some", obj->quan, where);
            pline("%s %ld gold piece%s.",
                nearloadmsg, gold_capacity, plur(gold_capacity));
            u.ugold += gold_capacity;
            obj->quan -= gold_capacity;
            costly_gold(obj->ox, obj->oy, gold_capacity);
          } else {
            u.ugold += count;
            if ((nearload = near_capacity()) != 0)
                pline("%s %ld gold piece%s.",
                    nearload < MOD_ENCUMBER ?
                    moderateloadmsg : nearloadmsg,
                    count, plur(count));
            else
                prinv((char *) 0, obj, count);
            costly_gold(obj->ox, obj->oy, count);
            if (count == obj->quan)
                delobj(obj);
            else
                obj->quan -= count;
          }
          flags.botl = 1;
          if (flags.run) nomul(0);
          return 1;
      } else if (obj->otyp == CORPSE) {
            if ( (touch_petrifies(&mons[obj->corpsenm])) && !uarmg
                        && !Stone_resistance && !telekinesis) {
            if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))
                display_nhwindow(WIN_MESSAGE, FALSE);
            else {
                  char kbuf[BUFSZ];

                  pline("Touching %s corpse is a fatal mistake.",
                              an(mons[obj->corpsenm].mname));
                  Sprintf(kbuf, "%s corpse", an(mons[obj->corpsenm].mname));
                  instapetrify(kbuf);
                return -1;
            }
          } else if (is_rider(&mons[obj->corpsenm])) {
            pline("At your %s, the corpse suddenly moves...",
                  telekinesis ? "attempted acquisition" : "touch");
            (void) revive_corpse(obj);
            exercise(A_WIS, FALSE);
            return -1;
          }
      } else  if (obj->otyp == SCR_SCARE_MONSTER) {
          if (obj->blessed) obj->blessed = 0;
          else if (!obj->spe && !obj->cursed) obj->spe = 1;
          else {
            pline_The("scroll%s turn%s to dust as you %s %s up.",
                  plur(obj->quan), (obj->quan == 1L) ? "s" : "",
                  telekinesis ? "raise" : "pick",
                  (obj->quan == 1L) ? "it" : "them");
            if (!(objects[SCR_SCARE_MONSTER].oc_name_known) &&
                            !(objects[SCR_SCARE_MONSTER].oc_uname))
                docall(obj);
            useupf(obj, obj->quan);
            return 1;   /* tried to pick something up and failed, but
                           don't want to terminate pickup loop yet   */
          }
      }

      if ((res = lift_object(obj, (struct obj *)0, &count, telekinesis)) <= 0)
          return res;

      if (obj->quan != count && obj->otyp != LOADSTONE)
          (void) splitobj(obj, count);

      obj = pick_obj(obj);

      if (uwep && uwep == obj) mrg_to_wielded = TRUE;
      nearload = near_capacity();
      prinv(nearload == SLT_ENCUMBER ? moderateloadmsg : (char *) 0, obj, count);
      mrg_to_wielded = FALSE;
      return 1;
}

/*
 * Do the actual work of picking otmp from the floor and putting
 * it in the hero's inventory.  Take care of billing.  Return a
 * pointer to the object where otmp ends up.  This may be different
 * from otmp because of merging.
 *
 * Gold never reaches this routine.
 */
struct obj *
pick_obj(otmp)
register struct obj *otmp;
{
      obj_extract_self(otmp);
      if (*u.ushops && costly_spot(u.ux, u.uy) &&
          otmp != uball)     /* don't charge for this - kd, 1/17/90 */
         /* sets obj->unpaid if necessary */
          addtobill(otmp, TRUE, FALSE, FALSE);
      if(Invisible) newsym(u.ux,u.uy);
      return(addinv(otmp));    /* might merge it with other objects */
}

/*
 * prints a message if encumbrance changed since the last check and
 * returns the new encumbrance value (from near_capacity()).
 */
int
encumber_msg()
{
    static int oldcap = UNENCUMBERED;
    int newcap = near_capacity();

    if(oldcap < newcap) {
      switch(newcap) {
      case 1: Your("movements are slowed slightly because of your load.");
            break;
      case 2: You("rebalance your load.  Movement is difficult.");
            break;
      case 3: You("stagger under your heavy load.  Movement is very hard.");
            break;
      default: You("%s move a handspan with this load!",
                 newcap == 4 ? "can barely" : "can't even");
            break;
      }
      flags.botl = 1;
    } else if(oldcap > newcap) {
      switch(newcap) {
      case 0: Your("movements are now unencumbered.");
            break;
      case 1: Your("movements are only slowed slightly by your load.");
            break;
      case 2: You("rebalance your load.  Movement is still difficult.");
            break;
      case 3: You("stagger under your load.  Movement is still very hard.");
            break;
      }
      flags.botl = 1;
    }

    oldcap = newcap;
    return (newcap);
}

/* Is there a container at x,y. Optional: return count of containers at x,y */
STATIC_OVL int
container_at(x, y, countem)
int x,y;
boolean countem;
{
      struct obj *cobj, *nobj;
      int container_count = 0;
      
      for(cobj = level.objects[x][y]; cobj; cobj = nobj) {
            nobj = cobj->nexthere;
            if(Is_container(cobj)) {
                  container_count++;
                  if (!countem) break;
            }
      }
      return container_count;
}

STATIC_OVL boolean
able_to_loot(x, y)
int x, y;
{
      if (!can_reach_floor()) {
#ifdef STEED
            if (u.usteed && P_SKILL(P_RIDING) < P_BASIC)
                  You("aren't skilled enough to reach from %s.",
                        mon_nam(u.usteed));
            else
#endif
                  You("cannot reach the %s.", surface(x, y));
            return FALSE;
      } else if (is_pool(x, y) || is_lava(x, y)) {
            /* at present, can't loot in water even when Underwater */
            You("cannot loot things that are deep in the %s.",
                is_lava(x, y) ? "lava" : "water");
            return FALSE;
      } else if (nolimbs(youmonst.data)) {
            pline("Without limbs, you cannot loot anything.");
            return FALSE;
      }
      return TRUE;
}

STATIC_OVL
boolean mon_beside(x,y)
int x, y;
{
      int i,j,nx,ny;
      for(i = -1; i <= 1; i++)
          for(j = -1; j <= 1; j++) {
            nx = x + i;
            ny = y + j;
            if(isok(nx, ny) && MON_AT(nx, ny))
                  return TRUE;
          }
      return FALSE;
}

int
doloot()    /* loot a container on the floor. */
{
    register struct obj *cobj, *nobj;
    register int c = -1;
    int timepassed = 0;
    int x,y;
    boolean underfoot = TRUE;
    const char *dont_find_anything = "don't find anything";
    struct monst *mtmp;
    char qbuf[QBUFSZ];
#ifdef STEED
    struct obj *otmp;
    boolean saddled_there = FALSE;
    boolean got_saddle = FALSE;
#endif

    if (check_capacity((char *)0)) {
      /* "Can't do that while carrying so much stuff." */
      return 0;
    }
    x = u.ux; y = u.uy;

lootcont:

    if (container_at(x, y, FALSE)) {
      if (!able_to_loot(x, y)) return 0;
      for (cobj = level.objects[x][y]; cobj; cobj = nobj) {
          nobj = cobj->nexthere;

          if (Is_container(cobj)) {
            Sprintf(qbuf, "There is %s here, loot it?", doname(cobj));
            c = ynq(qbuf);
            if (c == 'q') return (timepassed);
            if (c == 'n') continue;

            if (cobj->olocked) {
                pline("Hmmm, it seems to be locked.");
                continue;
            }
            if (cobj->otyp == BAG_OF_TRICKS) {
                You("carefully open the bag...");
                pline("It develops a huge set of teeth and bites you!");
                c = rnd(10);
                if (Half_physical_damage) c = (c+1) / 2;
                losehp(c, "carnivorous bag", KILLED_BY_AN);
                makeknown(BAG_OF_TRICKS);
                timepassed = 1;
                continue;
            }

            You("carefully open %s...", the(xname(cobj)));
            timepassed |= use_container(cobj, 0);
            if (multi < 0) return 1;            /* chest trap */
          }
      }
    } else if (Confusion) {
      if (u.ugold){
          long contribution = rnd((int)min(LARGEST_INT,u.ugold));
          struct obj *goldob = mkgoldobj(contribution);
          if (IS_THRONE(levl[u.ux][u.uy].typ)){
            struct obj *coffers;
            int pass;
            /* find the original coffers chest, or any chest */
            for (pass = 2; pass > -1; pass -= 2)
                for (coffers = fobj; coffers; coffers = coffers->nobj)
                  if (coffers->otyp == CHEST && coffers->spe == pass)
                      goto gotit;   /* two level break */
gotit:
            if (coffers){
                struct obj *tmp;
          verbalize("Thank you for your contribution to reduce the debt.");
                for (tmp = coffers->cobj; tmp; tmp = tmp->nobj)
                  if (tmp->otyp == goldob->otyp) break;

                if (tmp) {
                  tmp->quan += goldob->quan;
                  delobj(goldob);
                } else {
                  add_to_container(coffers, goldob);
                }
            } else {
                struct monst *mon = makemon(courtmon(),
                                  u.ux, u.uy, NO_MM_FLAGS);
                if (mon) {
                  mon->mgold += goldob->quan;
                  delobj(goldob);
                  pline("The exchequer accepts your contribution.");
                } else {
                  dropx(goldob);
                }
            }
          } else {
            dropx(goldob);
            pline("Ok, now there is loot here.");
          }
      }
    } else if (IS_GRAVE(levl[x][y].typ)) {
      You("need to dig up the grave to effectively loot it...");
    }
    /*
     * 3.3.1 introduced directional looting for some things.
     */
    if (c != 'y' && mon_beside(u.ux, u.uy)) {
      if (!getdir("Loot in what direction?")) {
          pline(Never_mind);
          return(0);
      }
      x = u.ux + u.dx;
      y = u.uy + u.dy;
      if (x == u.ux && y == u.uy) {
          underfoot = TRUE;
          if (container_at(x, y, FALSE))
            goto lootcont;
      } else
          underfoot = FALSE;
      if (u.dz < 0) {
          You("%s to loot on the %s.", dont_find_anything,
            ceiling(x, y));
          timepassed = 1;
          return timepassed;
      }
      mtmp = m_at(x, y);
#ifdef STEED
      /* 3.3.1 introduced the ability to remove saddle from a steed */
      if (mtmp && mtmp != u.usteed && (otmp = which_armor(mtmp, W_SADDLE))) {
          long unwornmask;
          saddled_there = TRUE;
          Sprintf(qbuf, "Do you want to remove the saddle from %s?",
            x_monnam(mtmp, ARTICLE_THE, (char *)0, SUPPRESS_SADDLE, FALSE));
          if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y') {
            if (nolimbs(youmonst.data)) {
                You_cant("do that without limbs."); /* not body_part(HAND) */
                return (0);
            }
            if (otmp->cursed) {
                You("can't. The saddle seems to be stuck to %s.",
                  x_monnam(mtmp, ARTICLE_THE, (char *)0,
                        SUPPRESS_SADDLE, FALSE));
                      
                /* the attempt costs you time */
                  return (1);
            }
            obj_extract_self(otmp);
            if ((unwornmask = otmp->owornmask) != 0L) {
                mtmp->misc_worn_check &= ~unwornmask;
                otmp->owornmask = 0L;
                update_mon_intrinsics(mtmp, otmp, FALSE);
            }
            otmp = hold_another_object(otmp, "You drop %s!", doname(otmp),
                              (const char *)0);
            timepassed = rnd(3);
            got_saddle = TRUE;
          } else if (c == 'q') {
            return (0);
          }
      }
# if 0
      /* Loot your steed, even if you can't reach the floor */
      if (u.usteed) {
          Sprintf(qbuf, "Do you want to loot %s inventory?",
                  s_suffix(x_monnam(u.usteed, ARTICLE_YOUR,
                        (char *)0, SUPPRESS_SADDLE, FALSE)));
          switch (c = ynq(qbuf)) {
            case 'y':
                if (!u.usteed->minvent) {
                  impossible("no saddle?");
                  break;
                }
                /* TO DO: get and put things into the inventory */
                You("peek at %s inventory...",
                  s_suffix(x_monnam(u.usteed, ARTICLE_YOUR,
                        (char *)0, SUPPRESS_SADDLE, FALSE)));
                (void) display_minventory(u.usteed, MINV_ALL);
                timepassed = 1;
                break;
            case 'n':
                break;
            case 'q':
                return (0);
          }
      }
# endif
#endif      /* STEED */

      /* Preserve pre-3.3.1 behaviour for containers.
       * Adjust this if-block to allow container looting
       * from one square away to change that in the future.
       */
      if (!underfoot) {
          if (container_at(x, y, FALSE)) {
            if (mtmp) {
                You("can't loot anything %sthere with %s in the way.",
#ifdef STEED
                      saddled_there ? "else " :
#endif
                      "", mon_nam(mtmp));
                return timepassed;
            } else {
                You("have to be at a container to loot it.");
            }
          } else {
            You("%s %sthere to loot.", dont_find_anything,
#ifdef STEED
                  (saddled_there || got_saddle) ? "else " :
#endif
                  "");
            return timepassed;
          }
      }
    } else if (c != 'y' && c != 'n') {
      You("%s %s to loot.", dont_find_anything,
                underfoot ? "here" : "there");
    }
    return (timepassed);
}

/*
 * Decide whether an object being placed into a magic bag will cause
 * it to explode.  If the object is a bag itself, check recursively.
 */
STATIC_OVL boolean
mbag_explodes(obj, depthin)
    struct obj *obj;
    int depthin;
{
    /* these won't cause an explosion when they're empty */
    if ((obj->otyp == WAN_CANCELLATION || obj->otyp == BAG_OF_TRICKS) &&
          obj->spe <= 0)
      return FALSE;

    /* odds: 1/1, 2/2, 3/4, 4/8, 5/16, 6/32, 7/64, 8/128, 9/128, 10/128,... */
    if ((Is_mbag(obj) || obj->otyp == WAN_CANCELLATION) &&
      (rn2(1 << (depthin > 7 ? 7 : depthin)) <= depthin))
      return TRUE;
    else if (Has_contents(obj)) {
      struct obj *otmp;

      for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
          if (mbag_explodes(otmp, depthin+1)) return TRUE;
    }
    return FALSE;
}

/* A variable set in use_container(), to be used by the callback routines   */
/* in_container(), and out_container() from askchain() and use_container(). */
static NEARDATA struct obj *current_container;
#define Icebox (current_container->otyp == ICE_BOX)

/* Returns: -1 to stop, 1 item was inserted, 0 item was not inserted. */
STATIC_PTR int
in_container(obj)
register struct obj *obj;
{
      register struct obj *gold;
      boolean is_gold = (obj->oclass == GOLD_CLASS);
      boolean floor_container = !carried(current_container);
      char buf[BUFSZ];

      if (!current_container) {
            impossible("<in> no current_container?");
            return 0;
      } else if (obj == uball || obj == uchain) {
            You("must be kidding.");
            return 0;
      } else if (obj == current_container) {
            pline("That would be an interesting topological exercise.");
            return 0;
      } else if (obj->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)) {
            Norep("You cannot %s %s you are wearing.",
                  Icebox ? "refrigerate" : "stash", something);
            return 0;
      } else if ((obj->otyp == LOADSTONE) && obj->cursed) {
            obj->bknown = 1;
            pline_The("stone%s won't leave your person.", plur(obj->quan));
            return 0;
      } else if (obj->otyp == AMULET_OF_YENDOR ||
               obj->otyp == CANDELABRUM_OF_INVOCATION ||
               obj->otyp == BELL_OF_OPENING ||
               obj->otyp == SPE_BOOK_OF_THE_DEAD) {
      /* Prohibit Amulets in containers; if you allow it, monsters can't
       * steal them.  It also becomes a pain to check to see if someone
       * has the Amulet.  Ditto for the Candelabrum, the Bell and the Book.
       */
          pline("%s cannot be confined in such trappings.", The(xname(obj)));
          return 0;
      } else if (obj->otyp == LEASH && obj->leashmon != 0) {
            pline("%s is attached to your pet.", The(xname(obj)));
            return 0;
      } else if (obj == uwep) {
            if (welded(obj)) {
                  weldmsg(obj);
                  return 0;
            }
            setuwep((struct obj *) 0);
            if (uwep) return 0;     /* unwielded, died, rewielded */
      } else if (obj == uswapwep) {
            setuswapwep((struct obj *) 0);
            if (uswapwep) return 0;     /* unwielded, died, rewielded */
      } else if (obj == uquiver) {
            setuqwep((struct obj *) 0);
            if (uquiver) return 0;     /* unwielded, died, rewielded */
      }

      /* boxes, boulders, and big statues can't fit into any container */
      if (obj->otyp == ICE_BOX || Is_box(obj) || obj->otyp == BOULDER ||
            (obj->otyp == STATUE && bigmonst(&mons[obj->corpsenm]))) {
            /*
             *  xname() uses a static result array.  Save obj's name
             *  before current_container's name is computed.  Don't
             *  use the result of strcpy() within You() --- the order
             *  of evaluation of the parameters is undefined.
             */
            Strcpy(buf, the(xname(obj)));
            You("cannot fit %s into %s.", buf,
                the(xname(current_container)));
            return 0;
      }

      freeinv(obj);

      if (is_gold) {    /* look for other money to merge within the container */
            for (gold = current_container->cobj; gold; gold = gold->nobj)
                  if (gold->otyp == obj->otyp) break;
      } else
            gold = 0;

      if (gold) {
            gold->quan += obj->quan;
      } else {
            add_to_container(current_container, obj);
      }

      current_container->owt = weight(current_container);

      Strcpy(buf, the(xname(current_container)));
      You("put %s into %s.", doname(obj), buf);

      if (obj_is_burning(obj))      /* this used to be part of freeinv() */
            (void) snuff_lit(obj);

      if (floor_container && costly_spot(u.ux, u.uy)) {
            sellobj_state(TRUE);
            sellobj(obj, u.ux, u.uy);
            sellobj_state(FALSE);
      }
      if (Icebox && obj->otyp != OIL_LAMP && obj->otyp != BRASS_LANTERN
                  && !Is_candle(obj)) {
            obj->age = monstermoves - obj->age; /* actual age */
            /* stop any corpse timeouts when frozen */
            if (obj->otyp == CORPSE && obj->timed) {
                  (void) stop_timer(ROT_CORPSE, (genericptr_t)obj);
                  (void) stop_timer(REVIVE_MON, (genericptr_t)obj);
            }
      }

      else if (Is_mbag(current_container) && mbag_explodes(obj, 0)) {
            You("are blasted by a magical explosion!");

            /* the !floor_container case is taken care of */
            if(*u.ushops && costly_spot(u.ux, u.uy) && floor_container) {
                register struct monst *shkp;

                if ((shkp = shop_keeper(*u.ushops)) != 0)
                  (void)stolen_value(current_container, u.ux, u.uy,
                                 (boolean)shkp->mpeaceful, FALSE);
            }
            delete_contents(current_container);
            if (!floor_container)
                  useup(current_container);
            else if (obj_here(current_container, u.ux, u.uy))
                  useupf(current_container, obj->quan);
            else
                  panic("in_container:  bag not found.");

            losehp(d(6,6),"magical explosion", KILLED_BY_AN);
            current_container = 0;  /* baggone = TRUE; */
      }

      if (is_gold) {
            if (gold) dealloc_obj(obj);
            bot();      /* update character's gold piece count immediately */
      }

      return(current_container ? 1 : -1);
}

STATIC_PTR int
ck_bag(obj)
struct obj *obj;
{
      return current_container && obj != current_container;
}

/* Returns: -1 to stop, 1 item was removed, 0 item was not removed. */
STATIC_PTR int
out_container(obj)
register struct obj *obj;
{
      register struct obj *otmp;
      boolean is_gold = (obj->oclass == GOLD_CLASS);
      int res, loadlev;
      long count;

      if (!current_container) {
            impossible("<out> no current_container?");
            return -1;
      } else if (is_gold) {
            obj->owt = weight(obj);
      }

      if(obj->oartifact && !touch_artifact(obj,&youmonst)) return 0;

      count = obj->quan;
      if ((res = lift_object(obj, current_container, &count, FALSE)) <= 0)
          return res;

      if (obj->quan != count && obj->otyp != LOADSTONE)
          (void) splitobj(obj, count);

      /* Remove the object from the list. */
      obj_extract_self(obj);
      current_container->owt = weight(current_container);

      if (Icebox && obj->otyp != OIL_LAMP && obj->otyp != BRASS_LANTERN
                  && !Is_candle(obj)) {
            obj->age = monstermoves - obj->age; /* actual age */
            if (obj->otyp == CORPSE)
                  start_corpse_timeout(obj);
      }
      /* simulated point of time */

      if (is_pick(obj) && !obj->unpaid && *u.ushops && shop_keeper(*u.ushops))
            verbalize("You sneaky cad! Get out of here with that pick!");
      if(!obj->unpaid && !carried(current_container) &&
           costly_spot(current_container->ox, current_container->oy)) {

            obj->ox = current_container->ox;
            obj->oy = current_container->oy;
            addtobill(obj, FALSE, FALSE, FALSE);
      }

      otmp = addinv(obj);
      loadlev = near_capacity();
      prinv(loadlev ?
            (loadlev < MOD_ENCUMBER ?
             "You have a little trouble removing" :
             "You have much trouble removing") : (char *)0,
            otmp, count);

      if (is_gold) {
            dealloc_obj(obj);
            bot();      /* update character's gold piece count immediately */
      }
      return 1;
}

#undef Icebox

int
use_container(obj, held)
register struct obj *obj;
register int held;
{
      struct obj *curr, *otmp, *u_gold = (struct obj *)0;
      struct monst *shkp;
      boolean one_by_one, allflag, loot_out = FALSE, loot_in = FALSE;
      char select[MAXOCLASSES+1];
      char qbuf[QBUFSZ];
      long loss = 0L;
      int cnt = 0, used = 0, lcnt = 0,
          menu_on_request;

      if (obj->olocked) {
          pline("%s seems to be locked.", The(xname(obj)));
          if (held) You("must put it down to unlock.");
          return 0;
      } else if (obj->otrapped) {
          if (held) You("open %s...", the(xname(obj)));
          (void) chest_trap(obj, HAND, FALSE);
          /* even if the trap fails, you've used up this turn */
          if (multi >= 0) {   /* in case we didn't become paralyzed */
            nomul(-1);
            nomovemsg = "";
          }
          return 1;
      }
      current_container = obj;      /* for use by in/out_container */

      if (obj->spe == 1) {
          static NEARDATA const char sc[] = "Schroedinger's Cat";
          struct obj *ocat;
          struct monst *cat;

          obj->spe = 0;       /* obj->owt will be updated below */
          /* this isn't really right, since any form of observation
             (telepathic or monster/object/food detection) ought to
             force the determination of alive vs dead state; but basing
             it just on opening the box is much simpler to cope with */
          cat = rn2(2) ? makemon(&mons[PM_HOUSECAT],
                           obj->ox, obj->oy, NO_MINVENT) : 0;
          if (cat) {
            cat->mpeaceful = 1;
            set_malign(cat);
            if (Blind)
                You("think %s brushed your %s.", something,
                  body_part(FOOT));
            else
                pline("%s inside the box is still alive!", Monnam(cat));
            (void) christen_monst(cat, sc);
          } else {
            ocat = mk_named_object(CORPSE, &mons[PM_HOUSECAT],
                               obj->ox, obj->oy, sc);
            if (ocat) {
                obj_extract_self(ocat);
                add_to_container(obj, ocat);  /* weight handled below */
            }
            pline_The("%s inside the box is dead!",
                Hallucination ? rndmonnam() : "housecat");
          }
          used = 1;
      }
      /* Count the number of contained objects. Sometimes toss objects if */
      /* a cursed magic bag.                                    */
      for (curr = obj->cobj; curr; curr = otmp) {
          otmp = curr->nobj;
          if (Is_mbag(obj) && obj->cursed && !rn2(13)) {
            if (curr->dknown)
                pline("%s to have vanished!", The(aobjnam(curr,"seem")));
            else
                You("%s %s disappear.", Blind ? "notice" : "see",
                                          doname(curr));
            obj_extract_self(curr);
            if (*u.ushops && (shkp = shop_keeper(*u.ushops)) != 0) {
                if(held) {
                  if(curr->unpaid)
                      loss += stolen_value(curr, u.ux, u.uy,
                                   (boolean)shkp->mpeaceful, TRUE);
                  lcnt++;
                } else if(costly_spot(u.ux, u.uy)) {
                  loss += stolen_value(curr, u.ux, u.uy,
                                   (boolean)shkp->mpeaceful, TRUE);
                  lcnt++;
                }
            }
            /* obfree() will free all contained objects */
            obfree(curr, (struct obj *) 0);
            used = 1;
          } else {
            cnt++;
          }
      }

      if (cnt && loss)
          You("owe %ld zorkmids for lost item%s.",
            loss, lcnt > 1 ? "s" : "");

      obj->owt = weight(obj);

      if (!cnt) {
          pline("%s is empty.", Yname2(obj));
      } else {
          Sprintf(qbuf, "Do you want to take %s out of %s?",
                something, yname(obj));
          if (flags.menu_style != MENU_TRADITIONAL) {
            if (flags.menu_style == MENU_FULL) {
                int t = in_or_out_menu("Do what?", current_container);
                if (t <= 0) return 0;
                loot_out = (t & 0x01) != 0;
                loot_in  = (t & 0x02) != 0;
            } else {    /* MENU_COMBINATION or MENU_PARTIAL */
                loot_out = (yn_function(qbuf, "ynq", 'n') == 'y');
            }
            if (loot_out) {
                add_valid_menu_class(0);  /* reset */
                used |= menu_loot(0, current_container, FALSE) > 0;
            }
          } else {
            /* traditional code */
ask_again2:
            menu_on_request = 0;
            add_valid_menu_class(0);      /* reset */
            switch (yn_function(qbuf, ":ynq", 'n')) {
            case ':':
                container_contents(current_container, FALSE, FALSE);
                goto ask_again2;
            case 'y':
                if (query_classes(select, &one_by_one, &allflag,
                              "take out", current_container->cobj,
                              FALSE, FALSE, &menu_on_request)) {
                  if (askchain((struct obj **)&current_container->cobj,
                             (one_by_one ? (char *)0 : select),
                             allflag, out_container,
                             (int FDECL((*),(OBJ_P)))0,
                             0, "nodot"))
                      used = 1;
                } else if (menu_on_request < 0) {
                  used |= menu_loot(menu_on_request,
                                current_container, FALSE) > 0;
                }
                /*FALLTHRU*/
            case 'n':
                break;
            case 'q':
            default:
                return used;
            }
          }
      }

      if (!invent && u.ugold == 0) {
          /* nothing to put in, but some feedback is necessary */
          You("don't have anything to put in.");
          return used;
      }
      if (flags.menu_style != MENU_FULL || !cnt) {
          loot_in = (yn_function("Do you wish to put something in?",
                           ynqchars, 'n') == 'y');
      }
      /*
       * Gone: being nice about only selecting food if we know we are
       * putting things in an ice chest.
       */
      if (loot_in) {
          if (u.ugold) {
            /*
             * Hack: gold is not in the inventory, so make a gold object
             * and put it at the head of the inventory list.
             */
            u_gold = mkgoldobj(u.ugold);  /* removes from u.ugold */
            u.ugold = u_gold->quan;       /* put the gold back */
            assigninvlet(u_gold);         /* might end up as NOINVSYM */
            u_gold->nobj = invent;
            invent = u_gold;
          }
          add_valid_menu_class(0);    /* reset */
          if (flags.menu_style != MENU_TRADITIONAL) {
            used |= menu_loot(0, current_container, TRUE) > 0;
          } else {
            /* traditional code */
            menu_on_request = 0;
            if (query_classes(select, &one_by_one, &allflag, "put in",
                           invent, FALSE, (u.ugold != 0L),
                           &menu_on_request)) {
                (void) askchain((struct obj **)&invent,
                            (one_by_one ? (char *)0 : select), allflag,
                            in_container, ck_bag, 0, "nodot");
                used = 1;
            } else if (menu_on_request < 0) {
                used |= menu_loot(menu_on_request,
                              current_container, TRUE) > 0;
            }
          }
      }

      if (u_gold && invent && invent->oclass == GOLD_CLASS) {
          /* didn't stash [all of] it */
          u_gold = invent;
          invent = u_gold->nobj;
          dealloc_obj(u_gold);
      }

      return used;
}

/* Loot a container (take things out, put things in), using a menu. */
STATIC_OVL int
menu_loot(retry, container, put_in)
int retry;
struct obj *container;
boolean put_in;
{
    int n, i, n_looted = 0;
    boolean all_categories = TRUE, loot_everything = FALSE;
    char buf[BUFSZ];
    const char *takeout = "Take out", *putin = "Put in";
    struct obj *otmp, *otmp2;
    menu_item *pick_list;
    int mflags, res;
    long count;

    if (retry) {
      all_categories = (retry == -2);
    } else if (flags.menu_style == MENU_FULL) {
      all_categories = FALSE;
      Sprintf(buf,"%s what type of objects?", put_in ? putin : takeout);
      mflags = put_in ? ALL_TYPES : ALL_TYPES|CHOOSE_ALL;
      n = query_category(buf, put_in ? invent : container->cobj,
                     mflags, &pick_list, PICK_ANY);
      if (!n) return 0;
      for (i = 0; i < n; i++) {
          if (pick_list[i].item.a_int == 'A')
            loot_everything = TRUE;
          else if (pick_list[i].item.a_int == ALL_TYPES_SELECTED)
            all_categories = TRUE;
          else
            add_valid_menu_class(pick_list[i].item.a_int);
      }
      free((genericptr_t) pick_list);
    }

    if (loot_everything) {
      for (otmp = container->cobj; otmp; otmp = otmp2) {
          otmp2 = otmp->nobj;
          res = out_container(otmp);
          if (res < 0) break;
      }
    } else {
      mflags = INVORDER_SORT;
      if (put_in && flags.invlet_constant) mflags |= USE_INVLET;
      Sprintf(buf,"%s what?", put_in ? putin : takeout);
      n = query_objlist(buf, put_in ? invent : container->cobj,
                    mflags, &pick_list, PICK_ANY,
                    all_categories ? allow_all : allow_category);
      if (n) {
            n_looted = n;
            for (i = 0; i < n; i++) {
                otmp = pick_list[i].item.a_obj;
                count = pick_list[i].count;
                if (count > 0 && count < otmp->quan) {
                  otmp2 = splitobj(otmp, count);
                  /* special split case also handled by askchain() */
                  if (otmp == uwep) setuwep(otmp2);
                  if (otmp == uquiver) setuqwep(otmp2);
                  if (otmp == uswapwep) setuswapwep(otmp2);
                }
                res = put_in ? in_container(otmp) : out_container(otmp);
                if (res < 0)
                  break;
            }
            free((genericptr_t)pick_list);
      }
    }
    return n_looted;
}

STATIC_OVL int
in_or_out_menu(prompt, obj)
const char *prompt;
struct obj *obj;
{
    winid win;
    anything any;
    menu_item *pick_list;
    char buf[BUFSZ];
    int n;

    any.a_void = 0;
    win = create_nhwindow(NHW_MENU);
    start_menu(win);
    any.a_int = 1;
    Sprintf(buf,"Take %s out of %s", something, the(xname(obj)));
    add_menu(win, NO_GLYPH, &any, 'a', 0, ATR_NONE, buf, MENU_UNSELECTED);
    any.a_int = 2;
    Sprintf(buf,"Put %s into %s", something, the(xname(obj)));
    add_menu(win, NO_GLYPH, &any, 'b', 0, ATR_NONE, buf, MENU_UNSELECTED);
    any.a_int = 3;
    add_menu(win, NO_GLYPH, &any, 'c', 0, ATR_NONE,
            "Both of the above", MENU_UNSELECTED);
    end_menu(win, prompt);
    n = select_menu(win, PICK_ONE, &pick_list);
    destroy_nhwindow(win);
    if (n > 0) {
      n = pick_list[0].item.a_int;
      free((genericptr_t) pick_list);
    }
    return n;
}

/*pickup.c*/

Generated by  Doxygen 1.6.0   Back to index