1 | /* SCCS Id: @(#)pickup.c 3.3 2000/03/01 */
2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | /*
6 | * Contains code for picking objects up, and container use.
7 | */
8 |
9 | #include "hack.h"
10 |
11 | STATIC_DCL void FDECL(simple_look, (struct obj *,BOOLEAN_P));
12 | STATIC_DCL boolean FDECL(query_classes, (char *,boolean *,boolean *,
13 | const char *,struct obj *,BOOLEAN_P,BOOLEAN_P,int *));
14 | STATIC_DCL void FDECL(check_here, (BOOLEAN_P));
15 | STATIC_DCL boolean FDECL(n_or_more, (struct obj *));
16 | STATIC_DCL boolean FDECL(all_but_uchain, (struct obj *));
17 | #if 0 /* not used */
18 | STATIC_DCL boolean FDECL(allow_cat_no_uchain, (struct obj *));
19 | #endif
20 | STATIC_DCL int FDECL(autopick, (struct obj*, int, menu_item **));
21 | STATIC_DCL int FDECL(count_categories, (struct obj *,int));
22 | STATIC_DCL long FDECL(carry_count,
23 | (struct obj *,struct obj *,long,BOOLEAN_P,int *,int *));
24 | STATIC_DCL int FDECL(lift_object, (struct obj *,struct obj *,long *,BOOLEAN_P));
25 | STATIC_DCL boolean FDECL(mbag_explodes, (struct obj *,int));
26 | STATIC_PTR int FDECL(in_container,(struct obj *));
27 | STATIC_PTR int FDECL(ck_bag,(struct obj *));
28 | STATIC_PTR int FDECL(out_container,(struct obj *));
29 | STATIC_DCL int FDECL(menu_loot, (int, struct obj *, BOOLEAN_P));
30 | STATIC_DCL int FDECL(in_or_out_menu, (const char *,struct obj *));
31 | STATIC_DCL int FDECL(container_at, (int, int, BOOLEAN_P));
32 | STATIC_DCL boolean FDECL(able_to_loot, (int, int));
33 | STATIC_DCL boolean FDECL(mon_beside, (int, int));
34 |
35 | /* define for query_objlist() and autopickup() */
36 | #define FOLLOW(curr, flags) \
37 | (((flags) & BY_NEXTHERE) ? (curr)->nexthere : (curr)->nobj)
38 |
39 | /*
40 | * How much the weight of the given container will change when the given
41 | * object is removed from it. This calculation must match the one used
42 | * by weight() in mkobj.c.
43 | */
44 | #define DELTA_CWT(cont,obj) \
45 | ((cont)->cursed ? (obj)->owt * 2 : \
46 | 1 + ((obj)->owt / ((cont)->blessed ? 4 : 2)))
47 | #define GOLD_WT(n) (((n) + 50L) / 100L)
48 | /* if you can figure this out, give yourself a hearty pat on the back... */
49 | #define GOLD_CAPACITY(w,n) (((w) * -100L) - ((n) + 50L) - 1L)
50 |
51 | static const char moderateloadmsg[] = "You have a little trouble lifting";
52 | static const char nearloadmsg[] = "You have much trouble lifting";
53 | static const char overloadmsg[] = "You have extreme difficulty lifting";
54 |
55 | /* BUG: this lets you look at cockatrice corpses while blind without
56 | touching them */
57 | /* much simpler version of the look-here code; used by query_classes() */
58 | STATIC_OVL void
59 | simple_look(otmp, here)
60 | struct obj *otmp; /* list of objects */
61 | boolean here; /* flag for type of obj list linkage */
62 | {
63 | /* Neither of the first two cases is expected to happen, since
64 | * we're only called after multiple classes of objects have been
65 | * detected, hence multiple objects must be present.
66 | */
67 | if (!otmp) {
68 | impossible("simple_look(null)");
69 | } else if (!(here ? otmp->nexthere : otmp->nobj)) {
70 | pline("%s", doname(otmp));
71 | } else {
72 | winid tmpwin = create_nhwindow(NHW_MENU);
73 | putstr(tmpwin, 0, "");
74 | do {
75 | putstr(tmpwin, 0, doname(otmp));
76 | otmp = here ? otmp->nexthere : otmp->nobj;
77 | } while (otmp);
78 | display_nhwindow(tmpwin, TRUE);
79 | destroy_nhwindow(tmpwin);
80 | }
81 | }
82 |
83 | int
84 | collect_obj_classes(ilets, otmp, here, incl_gold, filter)
85 | char ilets[];
86 | register struct obj *otmp;
87 | boolean here, incl_gold;
88 | boolean FDECL((*filter),(OBJ_P));
89 | {
90 | register int iletct = 0;
91 | register char c;
92 |
93 | if (incl_gold)
94 | ilets[iletct++] = def_oc_syms[GOLD_CLASS];
95 | ilets[iletct] = '\0'; /* terminate ilets so that index() will work */
96 | while (otmp) {
97 | c = def_oc_syms[(int)otmp->oclass];
98 | if (!index(ilets, c) && (!filter || (*filter)(otmp)))
99 | ilets[iletct++] = c, ilets[iletct] = '\0';
100 | otmp = here ? otmp->nexthere : otmp->nobj;
101 | }
102 |
103 | return iletct;
104 | }
105 |
106 | /*
107 | * Suppose some '?' and '!' objects are present, but '/' objects aren't:
108 | * "a" picks all items without further prompting;
109 | * "A" steps through all items, asking one by one;
110 | * "?" steps through '?' items, asking, and ignores '!' ones;
111 | * "/" becomes 'A', since no '/' present;
112 | * "?a" or "a?" picks all '?' without further prompting;
113 | * "/a" or "a/" becomes 'A' since there aren't any '/'
114 | * (bug fix: 3.1.0 thru 3.1.3 treated it as "a");
115 | * "?/a" or "a?/" or "/a?",&c picks all '?' even though no '/'
116 | * (ie, treated as if it had just been "?a").
117 | */
118 | STATIC_OVL boolean
119 | query_classes(oclasses, one_at_a_time, everything, action, objs,
120 | here, incl_gold, menu_on_demand)
121 | char oclasses[];
122 | boolean *one_at_a_time, *everything;
123 | const char *action;
124 | struct obj *objs;
125 | boolean here, incl_gold;
126 | int *menu_on_demand;
127 | {
128 | char ilets[20], inbuf[BUFSZ];
129 | int iletct, oclassct;
130 | boolean not_everything;
131 | char qbuf[QBUFSZ];
132 | boolean m_seen;
133 |
134 | oclasses[oclassct = 0] = '\0';
135 | *one_at_a_time = *everything = m_seen = FALSE;
136 | iletct = collect_obj_classes(ilets, objs, here, incl_gold,
137 | (boolean FDECL((*),(OBJ_P))) 0);
138 | if (iletct == 0) {
139 | return FALSE;
140 | } else if (iletct == 1) {
141 | oclasses[0] = def_char_to_objclass(ilets[0]);
142 | oclasses[1] = '\0';
143 | } else { /* more than one choice available */
144 | const char *where = 0;
145 | register char sym, oc_of_sym, *p;
146 | /* additional choices */
147 | ilets[iletct++] = ' ';
148 | ilets[iletct++] = 'a';
149 | ilets[iletct++] = 'A';
150 | ilets[iletct++] = (objs == invent ? 'i' : ':');
151 | if (menu_on_demand) {
152 | ilets[iletct++] = 'm';
153 | *menu_on_demand = 0;
154 | }
155 | ilets[iletct] = '\0';
156 | ask_again:
157 | oclasses[oclassct = 0] = '\0';
158 | *one_at_a_time = *everything = FALSE;
159 | not_everything = FALSE;
160 | Sprintf(qbuf,"What kinds of thing do you want to %s? [%s]",
161 | action, ilets);
162 | getlin(qbuf,inbuf);
163 | if (*inbuf == '\033') return FALSE;
164 |
165 | for (p = inbuf; (sym = *p++); ) {
166 | /* new A function (selective all) added by GAN 01/09/87 */
167 | if (sym == ' ') continue;
168 | else if (sym == 'A') *one_at_a_time = TRUE;
169 | else if (sym == 'a') *everything = TRUE;
170 | else if (sym == ':') {
171 | simple_look(objs, here); /* dumb if objs==invent */
172 | goto ask_again;
173 | } else if (sym == 'i') {
174 | (void) display_inventory((char *)0, TRUE);
175 | goto ask_again;
176 | } else if (sym == 'm') {
177 | m_seen = TRUE;
178 | } else {
179 | oc_of_sym = def_char_to_objclass(sym);
180 | if (index(ilets,sym)) {
181 | add_valid_menu_class(oc_of_sym);
182 | oclasses[oclassct++] = oc_of_sym;
183 | oclasses[oclassct] = '\0';
184 | } else {
185 | if (!where)
186 | where = !strcmp(action,"pick up") ? "here" :
187 | !strcmp(action,"take out") ?
188 | "inside" : "";
189 | if (*where)
190 | There("are no %c's %s.", sym, where);
191 | else
192 | You("have no %c's.", sym);
193 | not_everything = TRUE;
194 | }
195 | }
196 | }
197 | if (m_seen && menu_on_demand) {
198 | *menu_on_demand = (*everything || !oclassct) ? -2 : -3;
199 | return FALSE;
200 | }
201 | if (!oclassct && (!*everything || not_everything)) {
202 | /* didn't pick anything,
203 | or tried to pick something that's not present */
204 | *one_at_a_time = TRUE; /* force 'A' */
205 | *everything = FALSE; /* inhibit 'a' */
206 | }
207 | }
208 | return TRUE;
209 | }
210 |
211 | /* look at the objects at our location, unless there are too many of them */
212 | STATIC_OVL void
213 | check_here(picked_some)
214 | boolean picked_some;
215 | {
216 | register struct obj *obj;
217 | register int ct = 0;
218 |
219 | /* count the objects here */
220 | for (obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere) {
221 | if (obj != uchain)
222 | ct++;
223 | }
224 |
225 | /* If there are objects here, take a look. */
226 | if (ct) {
227 | if (flags.run) nomul(0);
228 | flush_screen(1);
229 | (void) look_here(ct, picked_some);
230 | } else {
231 | read_engr_at(u.ux,u.uy);
232 | }
233 | }
234 |
235 | /* Value set by query_objlist() for n_or_more(). */
236 | static long val_for_n_or_more;
237 |
238 | /* query_objlist callback: return TRUE if obj's count is >= reference value */
239 | STATIC_OVL boolean
240 | n_or_more(obj)
241 | struct obj *obj;
242 | {
243 | if (obj == uchain) return FALSE;
244 | return (obj->quan >= val_for_n_or_more);
245 | }
246 |
247 | /* List of valid menu classes for query_objlist() and allow_category callback */
248 | static char valid_menu_classes[MAXOCLASSES + 2];
249 |
250 | void
251 | add_valid_menu_class(c)
252 | int c;
253 | {
254 | static int vmc_count = 0;
255 |
256 | if (c == 0) /* reset */
257 | vmc_count = 0;
258 | else
259 | valid_menu_classes[vmc_count++] = (char)c;
260 | valid_menu_classes[vmc_count] = '\0';
261 | }
262 |
263 | /* query_objlist callback: return TRUE if not uchain */
264 | STATIC_OVL boolean
265 | all_but_uchain(obj)
266 | struct obj *obj;
267 | {
268 | return (obj != uchain);
269 | }
270 |
271 | /* query_objlist callback: return TRUE */
272 | /*ARGSUSED*/
273 | boolean
274 | allow_all(obj)
275 | struct obj *obj;
276 | {
277 | return TRUE;
278 | }
279 |
280 | boolean
281 | allow_category(obj)
282 | struct obj *obj;
283 | {
284 | if (((index(valid_menu_classes,'u') != (char *)0) && obj->unpaid) ||
285 | (index(valid_menu_classes, obj->oclass) != (char *)0))
286 | return TRUE;
287 | else
288 | return FALSE;
289 | }
290 |
291 | #if 0 /* not used */
292 | /* query_objlist callback: return TRUE if valid category (class), no uchain */
293 | STATIC_OVL boolean
294 | allow_cat_no_uchain(obj)
295 | struct obj *obj;
296 | {
297 | if ((obj != uchain) &&
298 | (((index(valid_menu_classes,'u') != (char *)0) && obj->unpaid) ||
299 | (index(valid_menu_classes, obj->oclass) != (char *)0)))
300 | return TRUE;
301 | else
302 | return FALSE;
303 | }
304 | #endif
305 |
306 | /* query_objlist callback: return TRUE if valid class and worn */
307 | boolean
308 | is_worn_by_type(otmp)
309 | register struct obj *otmp;
310 | {
311 | return((boolean)(!!(otmp->owornmask &
312 | (W_ARMOR | W_RING | W_AMUL | W_TOOL | W_WEP | W_SWAPWEP | W_QUIVER)))
313 | && (index(valid_menu_classes, otmp->oclass) != (char *)0));
314 | }
315 |
316 | /*
317 | * Have the hero pick things from the ground.
318 | *
319 | * Arg what:
320 | * >0 autopickup
321 | * =0 interactive
322 | * <0 pickup count of something
323 | *
324 | * Returns 1 if tried to pick something up, whether
325 | * or not it succeeded.
326 | */
327 | int
328 | pickup(what)
329 | int what; /* should be a long */
330 | {
331 | int i, n, res, count, n_tried = 0, n_picked = 0;
332 | menu_item *pick_list = (menu_item *) 0;
333 | boolean autopickup = what > 0;
334 |
335 | if (what < 0) /* pick N of something */
336 | count = -what;
337 | else /* pick anything */
338 | count = 0;
339 |
340 | /* no auto-pick if no-pick move, nothing there, or in a pool */
341 | if (autopickup && (flags.nopick || !OBJ_AT(u.ux, u.uy) ||
342 | (is_pool(u.ux, u.uy) && !Underwater) || is_lava(u.ux, u.uy))) {
343 | read_engr_at(u.ux, u.uy);
344 | return (0);
345 | }
346 |
347 | /* no pickup if levitating & not on air or water level */
348 | if (!can_reach_floor()) {
349 | if ((multi && !flags.run) || (autopickup && !flags.pickup))
350 | read_engr_at(u.ux, u.uy);
351 | return (0);
352 | }
353 |
354 | /* multi && !flags.run means they are in the middle of some other
355 | * action, or possibly paralyzed, sleeping, etc.... and they just
356 | * teleported onto the object. They shouldn't pick it up.
357 | */
358 | if ((multi && !flags.run) || (autopickup && !flags.pickup)) {
359 | check_here(FALSE);
360 | return (0);
361 | }
362 |
363 | if (notake(youmonst.data)) {
364 | if (!autopickup)
365 | You("are physically incapable of picking anything up.");
366 | else
367 | check_here(FALSE);
368 | return (0);
369 | }
370 |
371 | /* if there's anything here, stop running */
372 | if (OBJ_AT(u.ux,u.uy) && flags.run && !flags.nopick) nomul(0);
373 |
374 | add_valid_menu_class(0); /* reset */
375 | /*
376 | * Start the actual pickup process. This is split into two main
377 | * sections, the newer menu and the older "traditional" methods.
378 | * Automatic pickup has been split into its own menu-style routine
379 | * to make things less confusing.
380 | */
381 | if (autopickup) {
382 | n = autopick(level.objects[u.ux][u.uy], BY_NEXTHERE, &pick_list);
383 | goto menu_pickup;
384 | }
385 |
386 | if (flags.menu_style != MENU_TRADITIONAL) {
387 | /* use menus exclusively */
388 |
389 | if (count) { /* looking for N of something */
390 | char buf[QBUFSZ];
391 | Sprintf(buf, "Pick %d of what?", count);
392 | val_for_n_or_more = count; /* set up callback selector */
393 | n = query_objlist(buf, level.objects[u.ux][u.uy],
394 | BY_NEXTHERE|AUTOSELECT_SINGLE|INVORDER_SORT,
395 | &pick_list, PICK_ONE, n_or_more);
396 | /* correct counts, if any given */
397 | for (i = 0; i < n; i++)
398 | pick_list[i].count = count;
399 | } else {
400 | n = query_objlist("Pick up what?", level.objects[u.ux][u.uy],
401 | BY_NEXTHERE|AUTOSELECT_SINGLE|INVORDER_SORT,
402 | &pick_list, PICK_ANY, all_but_uchain);
403 | }
404 | menu_pickup:
405 | n_tried = n;
406 | for (n_picked = i = 0 ; i < n; i++) {
407 | res = pickup_object(pick_list[i].item.a_obj,pick_list[i].count,
408 | FALSE);
409 | if (res < 0) break; /* can't continue */
410 | n_picked += res;
411 | }
412 | if (pick_list) free((genericptr_t)pick_list);
413 |
414 | } else {
415 | /* old style interface */
416 | int ct = 0;
417 | long lcount;
418 | boolean all_of_a_type, selective;
419 | char oclasses[MAXOCLASSES];
420 | struct obj *obj, *obj2;
421 |
422 | oclasses[0] = '\0'; /* types to consider (empty for all) */
423 | all_of_a_type = TRUE; /* take all of considered types */
424 | selective = FALSE; /* ask for each item */
425 |
426 | /* check for more than one object */
427 | for (obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere)
428 | ct++;
429 |
430 | if (ct == 1 && count) {
431 | /* if only one thing, then pick it */
432 | obj = level.objects[u.ux][u.uy];
433 | lcount = min(obj->quan, (long)count);
434 | n_tried++;
435 | if (pickup_object(obj, lcount, FALSE) > 0)
436 | n_picked++; /* picked something */
437 | goto end_query;
438 |
439 | } else if (ct >= 2) {
440 | int via_menu = 0;
441 |
442 | There("are %s objects here.",
443 | (ct <= 10) ? "several" : "many");
444 | if (!query_classes(oclasses, &selective, &all_of_a_type,
445 | "pick up", level.objects[u.ux][u.uy],
446 | TRUE, FALSE, &via_menu)) {
447 | if (!via_menu) return (0);
448 | n = query_objlist("Pick up what?",
449 | level.objects[u.ux][u.uy],
450 | BY_NEXTHERE|(selective ? 0 : INVORDER_SORT),
451 | &pick_list, PICK_ANY,
452 | via_menu == -2 ? allow_all : allow_category);
453 | goto menu_pickup;
454 | }
455 | }
456 |
457 | for (obj = level.objects[u.ux][u.uy]; obj; obj = obj2) {
458 | obj2 = obj->nexthere; /* perhaps obj will be picked up */
459 | lcount = -1L;
460 |
461 | if (!selective && oclasses[0] && !index(oclasses,obj->oclass))
462 | continue;
463 |
464 | if (!all_of_a_type) {
465 | char qbuf[QBUFSZ];
466 | Sprintf(qbuf, "Pick up %s?", doname(obj));
467 | switch ((obj->quan < 2L) ? ynaq(qbuf) : ynNaq(qbuf)) {
468 | case 'q': goto end_query; /* out 2 levels */
469 | case 'n': continue;
470 | case 'a':
471 | all_of_a_type = TRUE;
472 | if (selective) {
473 | selective = FALSE;
474 | oclasses[0] = obj->oclass;
475 | oclasses[1] = '\0';
476 | }
477 | break;
478 | case '#': /* count was entered */
479 | if (!yn_number) continue; /* 0 count => No */
480 | lcount = (long) yn_number;
481 | if (lcount > obj->quan) lcount = obj->quan;
482 | /* fall thru */
483 | default: /* 'y' */
484 | break;
485 | }
486 | }
487 | if (lcount == -1L) lcount = obj->quan;
488 |
489 | n_tried++;
490 | if ((res = pickup_object(obj, lcount, FALSE)) < 0) break;
491 | n_picked += res;
492 | }
493 | end_query:
494 | ; /* semicolon needed by brain-damaged compilers */
495 | }
496 |
497 | /* position may need updating (invisible hero) */
498 | if (n_picked) newsym(u.ux,u.uy);
499 |
500 | /* see whether there's anything else here, after auto-pickup is done */
501 | if (autopickup) check_here(n_picked > 0);
502 | return (n_tried > 0);
503 | }
504 |
505 | /*
506 | * Pick from the given list using flags.pickup_types. Return the number
507 | * of items picked (not counts). Create an array that returns pointers
508 | * and counts of the items to be picked up. If the number of items
509 | * picked is zero, the pickup list is left alone. The caller of this
510 | * function must free the pickup list.
511 | */
512 | STATIC_OVL int
513 | autopick(olist, follow, pick_list)
514 | struct obj *olist; /* the object list */
515 | int follow; /* how to follow the object list */
516 | menu_item **pick_list; /* list of objects and counts to pick up */
517 | {
518 | menu_item *pi; /* pick item */
519 | struct obj *curr;
520 | int n;
521 | const char *otypes = flags.pickup_types;
522 |
523 | /* first count the number of eligible items */
524 | for (n = 0, curr = olist; curr; curr = FOLLOW(curr, follow))
525 | if (!*otypes || index(otypes, curr->oclass))
526 | n++;
527 |
528 | if (n) {
529 | *pick_list = pi = (menu_item *) alloc(sizeof(menu_item) * n);
530 | for (n = 0, curr = olist; curr; curr = FOLLOW(curr, follow))
531 | if (!*otypes || index(otypes, curr->oclass)) {
532 | pi[n].item.a_obj = curr;
533 | pi[n].count = curr->quan;
534 | n++;
535 | }
536 | }
537 | return n;
538 | }
539 |
540 |
541 | /*
542 | * Put up a menu using the given object list. Only those objects on the
543 | * list that meet the approval of the allow function are displayed. Return
544 | * a count of the number of items selected, as well as an allocated array of
545 | * menu_items, containing pointers to the objects selected and counts. The
546 | * returned counts are guaranteed to be in bounds and non-zero.
547 | *
548 | * Query flags:
549 | * BY_NEXTHERE - Follow object list via nexthere instead of nobj.
550 | * AUTOSELECT_SINGLE - Don't ask if only 1 object qualifies - just
551 | * use it.
552 | * USE_INVLET - Use object's invlet.
553 | * INVORDER_SORT - Use hero's pack order.
554 | * SIGNAL_NOMENU - Return -1 rather than 0 if nothing passes "allow".
555 | */
556 | int
557 | query_objlist(qstr, olist, qflags, pick_list, how, allow)
558 | const char *qstr; /* query string */
559 | struct obj *olist; /* the list to pick from */
560 | int qflags; /* options to control the query */
561 | menu_item **pick_list; /* return list of items picked */
562 | int how; /* type of query */
563 | boolean FDECL((*allow), (OBJ_P));/* allow function */
564 | {
565 | int n;
566 | winid win;
567 | struct obj *curr, *last;
568 | char *pack;
569 | anything any;
570 | boolean printed_type_name;
571 |
572 | *pick_list = (menu_item *) 0;
573 | if (!olist) return 0;
574 |
575 | /* count the number of items allowed */
576 | for (n = 0, last = 0, curr = olist; curr; curr = FOLLOW(curr, qflags))
577 | if ((*allow)(curr)) {
578 | last = curr;
579 | n++;
580 | }
581 |
582 | if (n == 0) /* nothing to pick here */
583 | return (qflags & SIGNAL_NOMENU) ? -1 : 0;
584 |
585 | if (n == 1 && (qflags & AUTOSELECT_SINGLE)) {
586 | *pick_list = (menu_item *) alloc(sizeof(menu_item));
587 | (*pick_list)->item.a_obj = last;
588 | (*pick_list)->count = last->quan;
589 | return 1;
590 | }
591 |
592 | win = create_nhwindow(NHW_MENU);
593 | start_menu(win);
594 | any.a_obj = (struct obj *) 0;
595 |
596 | /*
597 | * Run through the list and add the objects to the menu. If
598 | * INVORDER_SORT is set, we'll run through the list once for
599 | * each type so we can group them. The allow function will only
600 | * be called once per object in the list.
601 | */
602 | pack = flags.inv_order;
603 | do {
604 | printed_type_name = FALSE;
605 | for (curr = olist; curr; curr = FOLLOW(curr, qflags))
606 | if ((!(qflags & INVORDER_SORT) || curr->oclass == *pack)
607 | && (*allow)(curr)) {
608 |
609 | /* if sorting, print type name (once only) */
610 | if (qflags & INVORDER_SORT && !printed_type_name) {
611 | any.a_obj = (struct obj *) 0;
612 | add_menu(win, NO_GLYPH, &any, 0, 0, ATR_INVERSE,
613 | let_to_name(*pack, FALSE), MENU_UNSELECTED);
614 | printed_type_name = TRUE;
615 | }
616 |
617 | any.a_obj = curr;
618 | add_menu(win, obj_to_glyph(curr), &any,
619 | qflags & USE_INVLET ? curr->invlet : 0,
620 | def_oc_syms[(int)objects[curr->otyp].oc_class],
621 | ATR_NONE, doname(curr), MENU_UNSELECTED);
622 | }
623 | pack++;
624 | } while (qflags & INVORDER_SORT && *pack);
625 |
626 | end_menu(win, qstr);
627 | n = select_menu(win, how, pick_list);
628 | destroy_nhwindow(win);
629 |
630 | if (n > 0) {
631 | menu_item *mi;
632 | int i;
633 |
634 | /* fix up counts: -1 means no count used => pick all */
635 | for (i = 0, mi = *pick_list; i < n; i++, mi++)
636 | if (mi->count == -1L || mi->count > mi->item.a_obj->quan)
637 | mi->count = mi->item.a_obj->quan;
638 | } else if (n < 0) {
639 | n = 0; /* caller's don't expect -1 */
640 | }
641 | return n;
642 | }
643 |
644 | /*
645 | * allow menu-based category (class) selection (for Drop,take off etc.)
646 | *
647 | */
648 | int
649 | query_category(qstr, olist, qflags, pick_list, how)
650 | const char *qstr; /* query string */
651 | struct obj *olist; /* the list to pick from */
652 | int qflags; /* behaviour modification flags */
653 | menu_item **pick_list; /* return list of items picked */
654 | int how; /* type of query */
655 | {
656 | int n;
657 | winid win;
658 | struct obj *curr;
659 | char *pack;
660 | anything any;
661 | boolean collected_type_name;
662 | char invlet;
663 | int ccount;
664 | boolean do_unpaid = FALSE;
665 |
666 | *pick_list = (menu_item *) 0;
667 | if (!olist) return 0;
668 | if ((qflags & UNPAID_TYPES) && count_unpaid(olist)) do_unpaid = TRUE;
669 |
670 | ccount = count_categories(olist, qflags);
671 | /* no point in actually showing a menu for a single category */
672 | if (ccount == 1 && !do_unpaid && !(qflags & BILLED_TYPES)) {
673 | for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
674 | if ((qflags & WORN_TYPES) &&
675 | !(curr->owornmask & (W_ARMOR|W_RING|W_AMUL|W_TOOL|W_WEP|W_SWAPWEP|W_QUIVER)))
676 | continue;
677 | break;
678 | }
679 | if (curr) {
680 | *pick_list = (menu_item *) alloc(sizeof(menu_item));
681 | (*pick_list)->item.a_int = curr->oclass;
682 | return 1;
683 | } else {
684 | #ifdef DEBUG
685 | impossible("query_category: no single object match");
686 | #endif
687 | }
688 | return 0;
689 | }
690 |
691 | win = create_nhwindow(NHW_MENU);
692 | start_menu(win);
693 | pack = flags.inv_order;
694 | if ((qflags & ALL_TYPES) && (ccount > 1)) {
695 | invlet = 'a';
696 | any.a_void = 0;
697 | any.a_int = ALL_TYPES_SELECTED;
698 | add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
699 | (qflags & WORN_TYPES) ? "All worn types" : "All types",
700 | MENU_UNSELECTED);
701 | invlet = 'b';
702 | } else
703 | invlet = 'a';
704 | do {
705 | collected_type_name = FALSE;
706 | for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
707 | if (curr->oclass == *pack) {
708 | if ((qflags & WORN_TYPES) &&
709 | !(curr->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL |
710 | W_WEP | W_SWAPWEP | W_QUIVER)))
711 | continue;
712 | if (!collected_type_name) {
713 | any.a_void = 0;
714 | any.a_int = curr->oclass;
715 | add_menu(win, NO_GLYPH, &any, invlet++,
716 | def_oc_syms[(int)objects[curr->otyp].oc_class],
717 | ATR_NONE, let_to_name(*pack, FALSE),
718 | MENU_UNSELECTED);
719 | collected_type_name = TRUE;
720 | }
721 | }
722 | }
723 | pack++;
724 | if (invlet >= 'u') {
725 | impossible("query_category: too many categories");
726 | return 0;
727 | }
728 | } while (*pack);
729 | /* unpaid items if there are any */
730 | if (do_unpaid) {
731 | invlet = 'u';
732 | any.a_void = 0;
733 | any.a_int = 'u';
734 | add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
735 | "Unpaid items", MENU_UNSELECTED);
736 | }
737 | /* billed items: checked by caller, so always include if BILLED_TYPES */
738 | if (qflags & BILLED_TYPES) {
739 | invlet = 'x';
740 | any.a_void = 0;
741 | any.a_int = 'x';
742 | add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
743 | "Unpaid items already used up", MENU_UNSELECTED);
744 | }
745 | if (qflags & CHOOSE_ALL) {
746 | invlet = 'A';
747 | any.a_void = 0;
748 | any.a_int = 'A';
749 | add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
750 | (qflags & WORN_TYPES) ?
751 | "Auto-select every item being worn" :
752 | "Auto-select every item", MENU_UNSELECTED);
753 | }
754 | end_menu(win, qstr);
755 | n = select_menu(win, how, pick_list);
756 | destroy_nhwindow(win);
757 | if (n < 0)
758 | n = 0; /* caller's don't expect -1 */
759 | return n;
760 | }
761 |
762 | STATIC_OVL int
763 | count_categories(olist, qflags)
764 | struct obj *olist;
765 | int qflags;
766 | {
767 | char *pack;
768 | boolean counted_category;
769 | int ccount = 0;
770 | struct obj *curr;
771 |
772 | pack = flags.inv_order;
773 | do {
774 | counted_category = FALSE;
775 | for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
776 | if (curr->oclass == *pack) {
777 | if ((qflags & WORN_TYPES) &&
778 | !(curr->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL |
779 | W_WEP | W_SWAPWEP | W_QUIVER)))
780 | continue;
781 | if (!counted_category) {
782 | ccount++;
783 | counted_category = TRUE;
784 | }
785 | }
786 | }
787 | pack++;
788 | } while (*pack);
789 | return ccount;
790 | }
791 |
792 | /* could we carry `obj'? if not, could we carry some of it/them? */
793 | STATIC_OVL
794 | long carry_count(obj, container, count, telekinesis, wt_before, wt_after)
795 | struct obj *obj, *container; /* object to pick up, bag it's coming out of */
796 | long count;
797 | boolean telekinesis;
798 | int *wt_before, *wt_after;
799 | {
800 | boolean adjust_wt = container && carried(container),
801 | is_gold = obj->oclass == GOLD_CLASS;
802 | int wt, iw, ow, oow;
803 | long qq, savequan;
804 | unsigned saveowt;
805 | const char *verb, *prefx1, *prefx2, *suffx;
806 | char obj_nambuf[BUFSZ], where[BUFSZ];
807 |
808 | savequan = obj->quan;
809 | saveowt = obj->owt;
810 | iw = max_capacity();
811 | if (count != savequan) {
812 | obj->quan = count;
813 | obj->owt = (unsigned)weight(obj);
814 | }
815 | wt = iw + (int)obj->owt;
816 | if (adjust_wt)
817 | wt -= (container->otyp == BAG_OF_HOLDING) ?
818 | (int)DELTA_CWT(container, obj) : (int)obj->owt;
819 | if (is_gold) /* merged gold might affect cumulative weight */
820 | wt -= (GOLD_WT(u.ugold) + GOLD_WT(count) - GOLD_WT(u.ugold + count));
821 | if (count != savequan) {
822 | obj->quan = savequan;
823 | obj->owt = saveowt;
824 | }
825 | *wt_before = iw;
826 | *wt_after = wt;
827 | if (wt < 0)
828 | return count;
829 |
830 | /* see how many we can lift */
831 | if (is_gold) {
832 | iw -= (int)GOLD_WT(u.ugold);
833 | if (!adjust_wt) {
834 | qq = GOLD_CAPACITY((long)iw, u.ugold);
835 | } else {
836 | oow = 0;
837 | qq = 50L - (u.ugold % 100L) - 1L;
838 | if (qq < 0L) qq += 100L;
839 | for ( ; qq <= count; qq += 100L) {
840 | obj->quan = qq;
841 | obj->owt = (unsigned)GOLD_WT(qq);
842 | ow = (int)GOLD_WT(u.ugold + qq);
843 | ow -= (container->otyp == BAG_OF_HOLDING) ?
844 | (int)DELTA_CWT(container, obj) : (int)obj->owt;
845 | if (iw + ow >= 0) break;
846 | oow = ow;
847 | }
848 | iw -= oow;
849 | qq -= 100L;
850 | }
851 | if (qq < 0L) qq = 0L;
852 | else if (qq > count) qq = count;
853 | wt = iw + (int)GOLD_WT(u.ugold + qq);
854 | } else if (count > 1 || count < obj->quan) {
855 | /*
856 | * Ugh. Calc num to lift by changing the quan of of the
857 | * object and calling weight.
858 | *
859 | * This works for containers only because containers
860 | * don't merge. -dean
861 | */
862 | for (qq = 1L; qq <= count; qq++) {
863 | obj->quan = qq;
864 | obj->owt = (unsigned)(ow = weight(obj));
865 | if (adjust_wt)
866 | ow -= (container->otyp == BAG_OF_HOLDING) ?
867 | (int)DELTA_CWT(container, obj) : (int)obj->owt;
868 | if (iw + ow >= 0)
869 | break;
870 | wt = iw + ow;
871 | }
872 | --qq;
873 | } else {
874 | /* there's only one, and we can't lift it */
875 | qq = 0L;
876 | }
877 | obj->quan = savequan;
878 | obj->owt = saveowt;
879 |
880 | if (qq < count) {
881 | /* some message will be given */
882 | Strcpy(obj_nambuf, doname(obj));
883 | if (container) {
884 | Sprintf(where, "in %s", the(xname(container)));
885 | verb = "carry";
886 | } else {
887 | Strcpy(where, "lying here");
888 | verb = telekinesis ? "acquire" : "lift";
889 | }
890 | } else {
891 | /* lint supppression */
892 | *obj_nambuf = *where = '\0';
893 | verb = "";
894 | }
895 | /* we can carry qq of them */
896 | if (qq > 0) {
897 | if (qq < count)
898 | You("can only %s %s of the %s %s.",
899 | verb, (qq == 1L) ? "one" : "some", obj_nambuf, where);
900 | *wt_after = wt;
901 | return qq;
902 | }
903 |
904 | if (!container) Strcpy(where, "here"); /* slightly shorter form */
905 | if (invent || u.ugold) {
906 | prefx1 = "you cannot ";
907 | prefx2 = "";
908 | suffx = " any more";
909 | } else {
910 | prefx1 = (obj->quan == 1L) ? "it " : "even one ";
911 | prefx2 = "is too heavy for you to ";
912 | suffx = "";
913 | }
914 | There("%s %s %s, but %s%s%s%s.",
915 | (obj->quan == 1L) ? "is" : "are", obj_nambuf, where,
916 | prefx1, prefx2, verb, suffx);
917 |
918 | /* *wt_after = iw; */
919 | return 0L;
920 | }
921 |
922 | /* determine whether character is able and player is willing to carry `obj' */
923 | STATIC_OVL
924 | int lift_object(obj, container, cnt_p, telekinesis)
925 | struct obj *obj, *container; /* object to pick up, bag it's coming out of */
926 | long *cnt_p;
927 | boolean telekinesis;
928 | {
929 | int result, old_wt, new_wt, prev_encumbr, next_encumbr;
930 |
931 |
932 | if (obj->otyp == BOULDER && In_sokoban(&u.uz)) {
933 | You("cannot get your %s around this %s.",
934 | body_part(HAND), xname(obj));
935 | return -1;
936 | }
937 | if (obj->otyp == LOADSTONE ||
938 | (obj->otyp == BOULDER && throws_rocks(youmonst.data)))
939 | return 1; /* lift regardless of current situation */
940 |
941 | *cnt_p = carry_count(obj, container, *cnt_p, telekinesis, &old_wt, &new_wt);
942 | if (*cnt_p < 1L) {
943 | result = -1; /* nothing lifted */
944 | } else if (obj->oclass != GOLD_CLASS && inv_cnt() >= 52 &&
945 | !merge_choice(invent, obj)) {
946 | Your("knapsack cannot accommodate any more items.");
947 | result = -1; /* nothing lifted */
948 | } else {
949 | result = 1;
950 | prev_encumbr = near_capacity();
951 | if (prev_encumbr < flags.pickup_burden)
952 | prev_encumbr = flags.pickup_burden;
953 | next_encumbr = calc_capacity(new_wt - old_wt);
954 | if (next_encumbr > prev_encumbr) {
955 | if (telekinesis) {
956 | result = 0; /* don't lift */
957 | } else {
958 | char qbuf[QBUFSZ];
959 | long savequan = obj->quan;
960 |
961 | obj->quan = *cnt_p;
962 | Sprintf(qbuf, "%s %s. Continue?",
963 | (next_encumbr > HVY_ENCUMBER) ? overloadmsg :
964 | (next_encumbr > MOD_ENCUMBER) ? nearloadmsg :
965 | moderateloadmsg, doname(obj));
966 | obj->quan = savequan;
967 | switch (ynq(qbuf)) {
968 | case 'q': result = -1; break;
969 | case 'n': result = 0; break;
970 | default: break; /* 'y' => result == 1 */
971 | }
972 | }
973 | }
974 | }
975 |
976 | if (obj->otyp == SCR_SCARE_MONSTER && result <= 0 && !container)
977 | obj->spe = 0;
978 | return result;
979 | }
980 |
981 | /*
982 | * Pick up <count> of obj from the ground and add it to the hero's inventory.
983 | * Returns -1 if caller should break out of its loop, 0 if nothing picked
984 | * up, 1 if otherwise.
985 | */
986 | int
987 | pickup_object(obj, count, telekinesis)
988 | struct obj *obj;
989 | long count;
990 | boolean telekinesis; /* not picking it up directly by hand */
991 | {
992 | int res, nearload;
993 | const char *where = (obj->ox == u.ux && obj->oy == u.uy) ?
994 | "here" : "there";
995 |
996 | if (obj->quan < count) {
997 | impossible("pickup_object: count %ld > quan %ld?",
998 | count, obj->quan);
999 | return 0;
1000 | }
1001 |
1002 | /* In case of auto-pickup, where we haven't had a chance
1003 | to look at it yet; affects docall(SCR_SCARE_MONSTER). */
1004 | if (!Blind)
1005 | #ifdef INVISIBLE_OBJECTS
1006 | if (!obj->oinvis || See_invisible)
1007 | #endif
1008 | obj->dknown = 1;
1009 |
1010 | if (obj == uchain) { /* do not pick up attached chain */
1011 | return 0;
1012 | } else if (obj->oartifact && !touch_artifact(obj,&youmonst)) {
1013 | return 0;
1014 | } else if (obj->oclass == GOLD_CLASS) {
1015 | /* Special consideration for gold pieces... */
1016 | long iw = (long)max_capacity() - GOLD_WT(u.ugold);
1017 | long gold_capacity = GOLD_CAPACITY(iw, u.ugold);
1018 |
1019 | if (gold_capacity <= 0L) {
1020 | pline(
1021 | "There %s %ld gold piece%s %s, but you cannot carry any more.",
1022 | (obj->quan == 1L) ? "is" : "are",
1023 | obj->quan, plur(obj->quan), where);
1024 | return 0;
1025 | } else if (gold_capacity < count) {
1026 | You("can only %s %s of the %ld gold pieces lying %s.",
1027 | telekinesis ? "acquire" : "carry",
1028 | gold_capacity == 1L ? "one" : "some", obj->quan, where);
1029 | pline("%s %ld gold piece%s.",
1030 | nearloadmsg, gold_capacity, plur(gold_capacity));
1031 | u.ugold += gold_capacity;
1032 | obj->quan -= gold_capacity;
1033 | costly_gold(obj->ox, obj->oy, gold_capacity);
1034 | } else {
1035 | u.ugold += count;
1036 | if ((nearload = near_capacity()) != 0)
1037 | pline("%s %ld gold piece%s.",
1038 | nearload < MOD_ENCUMBER ?
1039 | moderateloadmsg : nearloadmsg,
1040 | count, plur(count));
1041 | else
1042 | prinv((char *) 0, obj, count);
1043 | costly_gold(obj->ox, obj->oy, count);
1044 | if (count == obj->quan)
1045 | delobj(obj);
1046 | else
1047 | obj->quan -= count;
1048 | }
1049 | flags.botl = 1;
1050 | if (flags.run) nomul(0);
1051 | return 1;
1052 | } else if (obj->otyp == CORPSE) {
1053 | if ( (touch_petrifies(&mons[obj->corpsenm])) && !uarmg
1054 | && !Stone_resistance && !telekinesis) {
1055 | if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))
1056 | display_nhwindow(WIN_MESSAGE, FALSE);
1057 | else {
1058 | char kbuf[BUFSZ];
1059 |
1060 | pline("Touching %s corpse is a fatal mistake.",
1061 | an(mons[obj->corpsenm].mname));
1062 | Sprintf(kbuf, "%s corpse", an(mons[obj->corpsenm].mname));
1063 | instapetrify(kbuf);
1064 | return -1;
1065 | }
1066 | } else if (is_rider(&mons[obj->corpsenm])) {
1067 | pline("At your %s, the corpse suddenly moves...",
1068 | telekinesis ? "attempted acquisition" : "touch");
1069 | (void) revive_corpse(obj);
1070 | exercise(A_WIS, FALSE);
1071 | return -1;
1072 | }
1073 | } else if (obj->otyp == SCR_SCARE_MONSTER) {
1074 | if (obj->blessed) obj->blessed = 0;
1075 | else if (!obj->spe && !obj->cursed) obj->spe = 1;
1076 | else {
1077 | pline_The("scroll%s turn%s to dust as you %s %s up.",
1078 | plur(obj->quan), (obj->quan == 1L) ? "s" : "",
1079 | telekinesis ? "raise" : "pick",
1080 | (obj->quan == 1L) ? "it" : "them");
1081 | if (!(objects[SCR_SCARE_MONSTER].oc_name_known) &&
1082 | !(objects[SCR_SCARE_MONSTER].oc_uname))
1083 | docall(obj);
1084 | useupf(obj, obj->quan);
1085 | return 1; /* tried to pick something up and failed, but
1086 | don't want to terminate pickup loop yet */
1087 | }
1088 | }
1089 |
1090 | if ((res = lift_object(obj, (struct obj *)0, &count, telekinesis)) <= 0)
1091 | return res;
1092 |
1093 | if (obj->quan != count && obj->otyp != LOADSTONE)
1094 | (void) splitobj(obj, count);
1095 |
1096 | obj = pick_obj(obj);
1097 |
1098 | if (uwep && uwep == obj) mrg_to_wielded = TRUE;
1099 | nearload = near_capacity();
1100 | prinv(nearload == SLT_ENCUMBER ? moderateloadmsg : (char *) 0, obj, count);
1101 | mrg_to_wielded = FALSE;
1102 | return 1;
1103 | }
1104 |
1105 | /*
1106 | * Do the actual work of picking otmp from the floor and putting
1107 | * it in the hero's inventory. Take care of billing. Return a
1108 | * pointer to the object where otmp ends up. This may be different
1109 | * from otmp because of merging.
1110 | *
1111 | * Gold never reaches this routine.
1112 | */
1113 | struct obj *
1114 | pick_obj(otmp)
1115 | register struct obj *otmp;
1116 | {
1117 | obj_extract_self(otmp);
1118 | if (*u.ushops && costly_spot(u.ux, u.uy) &&
1119 | otmp != uball) /* don't charge for this - kd, 1/17/90 */
1120 | /* sets obj->unpaid if necessary */
1121 | addtobill(otmp, TRUE, FALSE, FALSE);
1122 | if(Invisible) newsym(u.ux,u.uy);
1123 | return(addinv(otmp)); /* might merge it with other objects */
1124 | }
1125 |
1126 | /*
1127 | * prints a message if encumbrance changed since the last check and
1128 | * returns the new encumbrance value (from near_capacity()).
1129 | */
1130 | int
1131 | encumber_msg()
1132 | {
1133 | static int oldcap = UNENCUMBERED;
1134 | int newcap = near_capacity();
1135 |
1136 | if(oldcap < newcap) {
1137 | switch(newcap) {
1138 | case 1: Your("movements are slowed slightly because of your load.");
1139 | break;
1140 | case 2: You("rebalance your load. Movement is difficult.");
1141 | break;
1142 | case 3: You("stagger under your heavy load. Movement is very hard.");
1143 | break;
1144 | default: You("%s move a handspan with this load!",
1145 | newcap == 4 ? "can barely" : "can't even");
1146 | break;
1147 | }
1148 | flags.botl = 1;
1149 | } else if(oldcap > newcap) {
1150 | switch(newcap) {
1151 | case 0: Your("movements are now unencumbered.");
1152 | break;
1153 | case 1: Your("movements are only slowed slightly by your load.");
1154 | break;
1155 | case 2: You("rebalance your load. Movement is still difficult.");
1156 | break;
1157 | case 3: You("stagger under your load. Movement is still very hard.");
1158 | break;
1159 | }
1160 | flags.botl = 1;
1161 | }
1162 |
1163 | oldcap = newcap;
1164 | return (newcap);
1165 | }
1166 |
1167 | /* Is there a container at x,y. Optional: return count of containers at x,y */
1168 | STATIC_OVL int
1169 | container_at(x, y, countem)
1170 | int x,y;
1171 | boolean countem;
1172 | {
1173 | struct obj *cobj, *nobj;
1174 | int container_count = 0;
1175 |
1176 | for(cobj = level.objects[x][y]; cobj; cobj = nobj) {
1177 | nobj = cobj->nexthere;
1178 | if(Is_container(cobj)) {
1179 | container_count++;
1180 | if (!countem) break;
1181 | }
1182 | }
1183 | return container_count;
1184 | }
1185 |
1186 | STATIC_OVL boolean
1187 | able_to_loot(x, y)
1188 | int x, y;
1189 | {
1190 | if (!can_reach_floor()) {
1191 | #ifdef STEED
1192 | if (u.usteed && P_SKILL(P_RIDING) < P_BASIC)
1193 | You("aren't skilled enough to reach from %s.",
1194 | mon_nam(u.usteed));
1195 | else
1196 | #endif
1197 | You("cannot reach the %s.", surface(x, y));
1198 | return FALSE;
1199 | } else if (is_pool(x, y) || is_lava(x, y)) {
1200 | /* at present, can't loot in water even when Underwater */
1201 | You("cannot loot things that are deep in the %s.",
1202 | is_lava(x, y) ? "lava" : "water");
1203 | return FALSE;
1204 | } else if (nolimbs(youmonst.data)) {
1205 | pline("Without limbs, you cannot loot anything.");
1206 | return FALSE;
1207 | }
1208 | return TRUE;
1209 | }
1210 |
1211 | STATIC_OVL
1212 | boolean mon_beside(x,y)
1213 | int x, y;
1214 | {
1215 | int i,j,nx,ny;
1216 | for(i = -1; i <= 1; i++)
1217 | for(j = -1; j <= 1; j++) {
1218 | nx = x + i;
1219 | ny = y + j;
1220 | if(isok(nx, ny) && MON_AT(nx, ny))
1221 | return TRUE;
1222 | }
1223 | return FALSE;
1224 | }
1225 |
1226 | int
1227 | doloot() /* loot a container on the floor. */
1228 | {
1229 | register struct obj *cobj, *nobj;
1230 | register int c = -1;
1231 | int timepassed = 0;
1232 | int x,y;
1233 | boolean underfoot = TRUE;
1234 | const char *dont_find_anything = "don't find anything";
1235 | struct monst *mtmp;
1236 | char qbuf[QBUFSZ];
1237 | #ifdef STEED
1238 | struct obj *otmp;
1239 | boolean saddled_there = FALSE;
1240 | boolean got_saddle = FALSE;
1241 | #endif
1242 |
1243 | if (check_capacity((char *)0)) {
1244 | /* "Can't do that while carrying so much stuff." */
1245 | return 0;
1246 | }
1247 | x = u.ux; y = u.uy;
1248 |
1249 | lootcont:
1250 |
1251 | if (container_at(x, y, FALSE)) {
1252 | if (!able_to_loot(x, y)) return 0;
1253 | for (cobj = level.objects[x][y]; cobj; cobj = nobj) {
1254 | nobj = cobj->nexthere;
1255 |
1256 | if (Is_container(cobj)) {
1257 | Sprintf(qbuf, "There is %s here, loot it?", doname(cobj));
1258 | c = ynq(qbuf);
1259 | if (c == 'q') return (timepassed);
1260 | if (c == 'n') continue;
1261 |
1262 | if (cobj->olocked) {
1263 | pline("Hmmm, it seems to be locked.");
1264 | continue;
1265 | }
1266 | if (cobj->otyp == BAG_OF_TRICKS) {
1267 | You("carefully open the bag...");
1268 | pline("It develops a huge set of teeth and bites you!");
1269 | c = rnd(10);
1270 | if (Half_physical_damage) c = (c+1) / 2;
1271 | losehp(c, "carnivorous bag", KILLED_BY_AN);
1272 | makeknown(BAG_OF_TRICKS);
1273 | timepassed = 1;
1274 | continue;
1275 | }
1276 |
1277 | You("carefully open %s...", the(xname(cobj)));
1278 | timepassed |= use_container(cobj, 0);
1279 | if (multi < 0) return 1; /* chest trap */
1280 | }
1281 | }
1282 | } else if (Confusion) {
1283 | if (u.ugold){
1284 | long contribution = rnd((int)min(LARGEST_INT,u.ugold));
1285 | struct obj *goldob = mkgoldobj(contribution);
1286 | if (IS_THRONE(levl[u.ux][u.uy].typ)){
1287 | struct obj *coffers;
1288 | int pass;
1289 | /* find the original coffers chest, or any chest */
1290 | for (pass = 2; pass > -1; pass -= 2)
1291 | for (coffers = fobj; coffers; coffers = coffers->nobj)
1292 | if (coffers->otyp == CHEST && coffers->spe == pass)
1293 | goto gotit; /* two level break */
1294 | gotit:
1295 | if (coffers){
1296 | struct obj *tmp;
1297 | verbalize("Thank you for your contribution to reduce the debt.");
1298 | for (tmp = coffers->cobj; tmp; tmp = tmp->nobj)
1299 | if (tmp->otyp == goldob->otyp) break;
1300 |
1301 | if (tmp) {
1302 | tmp->quan += goldob->quan;
1303 | delobj(goldob);
1304 | } else {
1305 | add_to_container(coffers, goldob);
1306 | }
1307 | } else {
1308 | struct monst *mon = makemon(courtmon(),
1309 | u.ux, u.uy, NO_MM_FLAGS);
1310 | if (mon) {
1311 | mon->mgold += goldob->quan;
1312 | delobj(goldob);
1313 | pline("The exchequer accepts your contribution.");
1314 | } else {
1315 | dropx(goldob);
1316 | }
1317 | }
1318 | } else {
1319 | dropx(goldob);
1320 | pline("Ok, now there is loot here.");
1321 | }
1322 | }
1323 | } else if (IS_GRAVE(levl[x][y].typ)) {
1324 | You("need to dig up the grave to effectively loot it...");
1325 | }
1326 | /*
1327 | * 3.3.1 introduced directional looting for some things.
1328 | */
1329 | if (c != 'y' && mon_beside(u.ux, u.uy)) {
1330 | if (!getdir("Loot in what direction?")) {
1331 | pline(Never_mind);
1332 | return(0);
1333 | }
1334 | x = u.ux + u.dx;
1335 | y = u.uy + u.dy;
1336 | if (x == u.ux && y == u.uy) {
1337 | underfoot = TRUE;
1338 | if (container_at(x, y, FALSE))
1339 | goto lootcont;
1340 | } else
1341 | underfoot = FALSE;
1342 | if (u.dz < 0) {
1343 | You("%s to loot on the %s.", dont_find_anything,
1344 | ceiling(x, y));
1345 | timepassed = 1;
1346 | return timepassed;
1347 | }
1348 | mtmp = m_at(x, y);
1349 | #ifdef STEED
1350 | /* 3.3.1 introduced the ability to remove saddle from a steed */
1351 | if (mtmp && mtmp != u.usteed && (otmp = which_armor(mtmp, W_SADDLE))) {
1352 | long unwornmask;
1353 | saddled_there = TRUE;
1354 | Sprintf(qbuf, "Do you want to remove the saddle from %s?",
1355 | x_monnam(mtmp, ARTICLE_THE, (char *)0, SUPPRESS_SADDLE, FALSE));
1356 | if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y') {
1357 | if (nolimbs(youmonst.data)) {
1358 | You_cant("do that without limbs."); /* not body_part(HAND) */
1359 | return (0);
1360 | }
1361 | if (otmp->cursed) {
1362 | You("can't. The saddle seems to be stuck to %s.",
1363 | x_monnam(mtmp, ARTICLE_THE, (char *)0,
1364 | SUPPRESS_SADDLE, FALSE));
1365 |
1366 | /* the attempt costs you time */
1367 | return (1);
1368 | }
1369 | obj_extract_self(otmp);
1370 | if ((unwornmask = otmp->owornmask) != 0L) {
1371 | mtmp->misc_worn_check &= ~unwornmask;
1372 | otmp->owornmask = 0L;
1373 | update_mon_intrinsics(mtmp, otmp, FALSE);
1374 | }
1375 | otmp = hold_another_object(otmp, "You drop %s!", doname(otmp),
1376 | (const char *)0);
1377 | timepassed = rnd(3);
1378 | got_saddle = TRUE;
1379 | } else if (c == 'q') {
1380 | return (0);
1381 | }
1382 | }
1383 | # if 0
1384 | /* Loot your steed, even if you can't reach the floor */
1385 | if (u.usteed) {
1386 | Sprintf(qbuf, "Do you want to loot %s inventory?",
1387 | s_suffix(x_monnam(u.usteed, ARTICLE_YOUR,
1388 | (char *)0, SUPPRESS_SADDLE, FALSE)));
1389 | switch (c = ynq(qbuf)) {
1390 | case 'y':
1391 | if (!u.usteed->minvent) {
1392 | impossible("no saddle?");
1393 | break;
1394 | }
1395 | /* TO DO: get and put things into the inventory */
1396 | You("peek at %s inventory...",
1397 | s_suffix(x_monnam(u.usteed, ARTICLE_YOUR,
1398 | (char *)0, SUPPRESS_SADDLE, FALSE)));
1399 | (void) display_minventory(u.usteed, MINV_ALL);
1400 | timepassed = 1;
1401 | break;
1402 | case 'n':
1403 | break;
1404 | case 'q':
1405 | return (0);
1406 | }
1407 | }
1408 | # endif
1409 | #endif /* STEED */
1410 |
1411 | /* Preserve pre-3.3.1 behaviour for containers.
1412 | * Adjust this if-block to allow container looting
1413 | * from one square away to change that in the future.
1414 | */
1415 | if (!underfoot) {
1416 | if (container_at(x, y, FALSE)) {
1417 | if (mtmp) {
1418 | You("can't loot anything %sthere with %s in the way.",
1419 | #ifdef STEED
1420 | saddled_there ? "else " :
1421 | #endif
1422 | "", mon_nam(mtmp));
1423 | return timepassed;
1424 | } else {
1425 | You("have to be at a container to loot it.");
1426 | }
1427 | } else {
1428 | You("%s %sthere to loot.", dont_find_anything,
1429 | #ifdef STEED
1430 | (saddled_there || got_saddle) ? "else " :
1431 | #endif
1432 | "");
1433 | return timepassed;
1434 | }
1435 | }
1436 | } else if (c != 'y' && c != 'n') {
1437 | You("%s %s to loot.", dont_find_anything,
1438 | underfoot ? "here" : "there");
1439 | }
1440 | return (timepassed);
1441 | }
1442 |
1443 | /*
1444 | * Decide whether an object being placed into a magic bag will cause
1445 | * it to explode. If the object is a bag itself, check recursively.
1446 | */
1447 | STATIC_OVL boolean
1448 | mbag_explodes(obj, depthin)
1449 | struct obj *obj;
1450 | int depthin;
1451 | {
1452 | /* these won't cause an explosion when they're empty */
1453 | if ((obj->otyp == WAN_CANCELLATION || obj->otyp == BAG_OF_TRICKS) &&
1454 | obj->spe <= 0)
1455 | return FALSE;
1456 |
1457 | /* odds: 1/1, 2/2, 3/4, 4/8, 5/16, 6/32, 7/64, 8/128, 9/128, 10/128,... */
1458 | if ((Is_mbag(obj) || obj->otyp == WAN_CANCELLATION) &&
1459 | (rn2(1 << (depthin > 7 ? 7 : depthin)) <= depthin))
1460 | return TRUE;
1461 | else if (Has_contents(obj)) {
1462 | struct obj *otmp;
1463 |
1464 | for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
1465 | if (mbag_explodes(otmp, depthin+1)) return TRUE;
1466 | }
1467 | return FALSE;
1468 | }
1469 |
1470 | /* A variable set in use_container(), to be used by the callback routines */
1471 | /* in_container(), and out_container() from askchain() and use_container(). */
1472 | static NEARDATA struct obj *current_container;
1473 | #define Icebox (current_container->otyp == ICE_BOX)
1474 |
1475 | /* Returns: -1 to stop, 1 item was inserted, 0 item was not inserted. */
1476 | STATIC_PTR int
1477 | in_container(obj)
1478 | register struct obj *obj;
1479 | {
1480 | register struct obj *gold;
1481 | boolean is_gold = (obj->oclass == GOLD_CLASS);
1482 | boolean floor_container = !carried(current_container);
1483 | char buf[BUFSZ];
1484 |
1485 | if (!current_container) {
1486 | impossible("<in> no current_container?");
1487 | return 0;
1488 | } else if (obj == uball || obj == uchain) {
1489 | You("must be kidding.");
1490 | return 0;
1491 | } else if (obj == current_container) {
1492 | pline("That would be an interesting topological exercise.");
1493 | return 0;
1494 | } else if (obj->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)) {
1495 | Norep("You cannot %s %s you are wearing.",
1496 | Icebox ? "refrigerate" : "stash", something);
1497 | return 0;
1498 | } else if ((obj->otyp == LOADSTONE) && obj->cursed) {
1499 | obj->bknown = 1;
1500 | pline_The("stone%s won't leave your person.", plur(obj->quan));
1501 | return 0;
1502 | } else if (obj->otyp == AMULET_OF_YENDOR ||
1503 | obj->otyp == CANDELABRUM_OF_INVOCATION ||
1504 | obj->otyp == BELL_OF_OPENING ||
1505 | obj->otyp == SPE_BOOK_OF_THE_DEAD) {
1506 | /* Prohibit Amulets in containers; if you allow it, monsters can't
1507 | * steal them. It also becomes a pain to check to see if someone
1508 | * has the Amulet. Ditto for the Candelabrum, the Bell and the Book.
1509 | */
1510 | pline("%s cannot be confined in such trappings.", The(xname(obj)));
1511 | return 0;
1512 | } else if (obj->otyp == LEASH && obj->leashmon != 0) {
1513 | pline("%s is attached to your pet.", The(xname(obj)));
1514 | return 0;
1515 | } else if (obj == uwep) {
1516 | if (welded(obj)) {
1517 | weldmsg(obj);
1518 | return 0;
1519 | }
1520 | setuwep((struct obj *) 0);
1521 | if (uwep) return 0; /* unwielded, died, rewielded */
1522 | } else if (obj == uswapwep) {
1523 | setuswapwep((struct obj *) 0);
1524 | if (uswapwep) return 0; /* unwielded, died, rewielded */
1525 | } else if (obj == uquiver) {
1526 | setuqwep((struct obj *) 0);
1527 | if (uquiver) return 0; /* unwielded, died, rewielded */
1528 | }
1529 |
1530 | /* boxes, boulders, and big statues can't fit into any container */
1531 | if (obj->otyp == ICE_BOX || Is_box(obj) || obj->otyp == BOULDER ||
1532 | (obj->otyp == STATUE && bigmonst(&mons[obj->corpsenm]))) {
1533 | /*
1534 | * xname() uses a static result array. Save obj's name
1535 | * before current_container's name is computed. Don't
1536 | * use the result of strcpy() within You() --- the order
1537 | * of evaluation of the parameters is undefined.
1538 | */
1539 | Strcpy(buf, the(xname(obj)));
1540 | You("cannot fit %s into %s.", buf,
1541 | the(xname(current_container)));
1542 | return 0;
1543 | }
1544 |
1545 | freeinv(obj);
1546 |
1547 | if (is_gold) { /* look for other money to merge within the container */
1548 | for (gold = current_container->cobj; gold; gold = gold->nobj)
1549 | if (gold->otyp == obj->otyp) break;
1550 | } else
1551 | gold = 0;
1552 |
1553 | if (gold) {
1554 | gold->quan += obj->quan;
1555 | } else {
1556 | add_to_container(current_container, obj);
1557 | }
1558 |
1559 | current_container->owt = weight(current_container);
1560 |
1561 | Strcpy(buf, the(xname(current_container)));
1562 | You("put %s into %s.", doname(obj), buf);
1563 |
1564 | if (obj_is_burning(obj)) /* this used to be part of freeinv() */
1565 | (void) snuff_lit(obj);
1566 |
1567 | if (floor_container && costly_spot(u.ux, u.uy)) {
1568 | sellobj_state(TRUE);
1569 | sellobj(obj, u.ux, u.uy);
1570 | sellobj_state(FALSE);
1571 | }
1572 | if (Icebox && obj->otyp != OIL_LAMP && obj->otyp != BRASS_LANTERN
1573 | && !Is_candle(obj)) {
1574 | obj->age = monstermoves - obj->age; /* actual age */
1575 | /* stop any corpse timeouts when frozen */
1576 | if (obj->otyp == CORPSE && obj->timed) {
1577 | (void) stop_timer(ROT_CORPSE, (genericptr_t)obj);
1578 | (void) stop_timer(REVIVE_MON, (genericptr_t)obj);
1579 | }
1580 | }
1581 |
1582 | else if (Is_mbag(current_container) && mbag_explodes(obj, 0)) {
1583 | You("are blasted by a magical explosion!");
1584 |
1585 | /* the !floor_container case is taken care of */
1586 | if(*u.ushops && costly_spot(u.ux, u.uy) && floor_container) {
1587 | register struct monst *shkp;
1588 |
1589 | if ((shkp = shop_keeper(*u.ushops)) != 0)
1590 | (void)stolen_value(current_container, u.ux, u.uy,
1591 | (boolean)shkp->mpeaceful, FALSE);
1592 | }
1593 | delete_contents(current_container);
1594 | if (!floor_container)
1595 | useup(current_container);
1596 | else if (obj_here(current_container, u.ux, u.uy))
1597 | useupf(current_container, obj->quan);
1598 | else
1599 | panic("in_container: bag not found.");
1600 |
1601 | losehp(d(6,6),"magical explosion", KILLED_BY_AN);
1602 | current_container = 0; /* baggone = TRUE; */
1603 | }
1604 |
1605 | if (is_gold) {
1606 | if (gold) dealloc_obj(obj);
1607 | bot(); /* update character's gold piece count immediately */
1608 | }
1609 |
1610 | return(current_container ? 1 : -1);
1611 | }
1612 |
1613 | STATIC_PTR int
1614 | ck_bag(obj)
1615 | struct obj *obj;
1616 | {
1617 | return current_container && obj != current_container;
1618 | }
1619 |
1620 | /* Returns: -1 to stop, 1 item was removed, 0 item was not removed. */
1621 | STATIC_PTR int
1622 | out_container(obj)
1623 | register struct obj *obj;
1624 | {
1625 | register struct obj *otmp;
1626 | boolean is_gold = (obj->oclass == GOLD_CLASS);
1627 | int res, loadlev;
1628 | long count;
1629 |
1630 | if (!current_container) {
1631 | impossible("<out> no current_container?");
1632 | return -1;
1633 | } else if (is_gold) {
1634 | obj->owt = weight(obj);
1635 | }
1636 |
1637 | if(obj->oartifact && !touch_artifact(obj,&youmonst)) return 0;
1638 |
1639 | count = obj->quan;
1640 | if ((res = lift_object(obj, current_container, &count, FALSE)) <= 0)
1641 | return res;
1642 |
1643 | if (obj->quan != count && obj->otyp != LOADSTONE)
1644 | (void) splitobj(obj, count);
1645 |
1646 | /* Remove the object from the list. */
1647 | obj_extract_self(obj);
1648 | current_container->owt = weight(current_container);
1649 |
1650 | if (Icebox && obj->otyp != OIL_LAMP && obj->otyp != BRASS_LANTERN
1651 | && !Is_candle(obj)) {
1652 | obj->age = monstermoves - obj->age; /* actual age */
1653 | if (obj->otyp == CORPSE)
1654 | start_corpse_timeout(obj);
1655 | }
1656 | /* simulated point of time */
1657 |
1658 | if (is_pick(obj) && !obj->unpaid && *u.ushops && shop_keeper(*u.ushops))
1659 | verbalize("You sneaky cad! Get out of here with that pick!");
1660 | if(!obj->unpaid && !carried(current_container) &&
1661 | costly_spot(current_container->ox, current_container->oy)) {
1662 |
1663 | obj->ox = current_container->ox;
1664 | obj->oy = current_container->oy;
1665 | addtobill(obj, FALSE, FALSE, FALSE);
1666 | }
1667 |
1668 | otmp = addinv(obj);
1669 | loadlev = near_capacity();
1670 | prinv(loadlev ?
1671 | (loadlev < MOD_ENCUMBER ?
1672 | "You have a little trouble removing" :
1673 | "You have much trouble removing") : (char *)0,
1674 | otmp, count);
1675 |
1676 | if (is_gold) {
1677 | dealloc_obj(obj);
1678 | bot(); /* update character's gold piece count immediately */
1679 | }
1680 | return 1;
1681 | }
1682 |
1683 | #undef Icebox
1684 |
1685 | int
1686 | use_container(obj, held)
1687 | register struct obj *obj;
1688 | register int held;
1689 | {
1690 | struct obj *curr, *otmp, *u_gold = (struct obj *)0;
1691 | struct monst *shkp;
1692 | boolean one_by_one, allflag, loot_out = FALSE, loot_in = FALSE;
1693 | char select[MAXOCLASSES+1];
1694 | char qbuf[QBUFSZ];
1695 | long loss = 0L;
1696 | int cnt = 0, used = 0, lcnt = 0,
1697 | menu_on_request;
1698 |
1699 | if (obj->olocked) {
1700 | pline("%s seems to be locked.", The(xname(obj)));
1701 | if (held) You("must put it down to unlock.");
1702 | return 0;
1703 | } else if (obj->otrapped) {
1704 | if (held) You("open %s...", the(xname(obj)));
1705 | (void) chest_trap(obj, HAND, FALSE);
1706 | /* even if the trap fails, you've used up this turn */
1707 | if (multi >= 0) { /* in case we didn't become paralyzed */
1708 | nomul(-1);
1709 | nomovemsg = "";
1710 | }
1711 | return 1;
1712 | }
1713 | current_container = obj; /* for use by in/out_container */
1714 |
1715 | if (obj->spe == 1) {
1716 | static NEARDATA const char sc[] = "Schroedinger's Cat";
1717 | struct obj *ocat;
1718 | struct monst *cat;
1719 |
1720 | obj->spe = 0; /* obj->owt will be updated below */
1721 | /* this isn't really right, since any form of observation
1722 | (telepathic or monster/object/food detection) ought to
1723 | force the determination of alive vs dead state; but basing
1724 | it just on opening the box is much simpler to cope with */
1725 | cat = rn2(2) ? makemon(&mons[PM_HOUSECAT],
1726 | obj->ox, obj->oy, NO_MINVENT) : 0;
1727 | if (cat) {
1728 | cat->mpeaceful = 1;
1729 | set_malign(cat);
1730 | if (Blind)
1731 | You("think %s brushed your %s.", something,
1732 | body_part(FOOT));
1733 | else
1734 | pline("%s inside the box is still alive!", Monnam(cat));
1735 | (void) christen_monst(cat, sc);
1736 | } else {
1737 | ocat = mk_named_object(CORPSE, &mons[PM_HOUSECAT],
1738 | obj->ox, obj->oy, sc);
1739 | if (ocat) {
1740 | obj_extract_self(ocat);
1741 | add_to_container(obj, ocat); /* weight handled below */
1742 | }
1743 | pline_The("%s inside the box is dead!",
1744 | Hallucination ? rndmonnam() : "housecat");
1745 | }
1746 | used = 1;
1747 | }
1748 | /* Count the number of contained objects. Sometimes toss objects if */
1749 | /* a cursed magic bag. */
1750 | for (curr = obj->cobj; curr; curr = otmp) {
1751 | otmp = curr->nobj;
1752 | if (Is_mbag(obj) && obj->cursed && !rn2(13)) {
1753 | if (curr->dknown)
1754 | pline("%s to have vanished!", The(aobjnam(curr,"seem")));
1755 | else
1756 | You("%s %s disappear.", Blind ? "notice" : "see",
1757 | doname(curr));
1758 | obj_extract_self(curr);
1759 | if (*u.ushops && (shkp = shop_keeper(*u.ushops)) != 0) {
1760 | if(held) {
1761 | if(curr->unpaid)
1762 | loss += stolen_value(curr, u.ux, u.uy,
1763 | (boolean)shkp->mpeaceful, TRUE);
1764 | lcnt++;
1765 | } else if(costly_spot(u.ux, u.uy)) {
1766 | loss += stolen_value(curr, u.ux, u.uy,
1767 | (boolean)shkp->mpeaceful, TRUE);
1768 | lcnt++;
1769 | }
1770 | }
1771 | /* obfree() will free all contained objects */
1772 | obfree(curr, (struct obj *) 0);
1773 | used = 1;
1774 | } else {
1775 | cnt++;
1776 | }
1777 | }
1778 |
1779 | if (cnt && loss)
1780 | You("owe %ld zorkmids for lost item%s.",
1781 | loss, lcnt > 1 ? "s" : "");
1782 |
1783 | obj->owt = weight(obj);
1784 |
1785 | if (!cnt) {
1786 | pline("%s is empty.", Yname2(obj));
1787 | } else {
1788 | Sprintf(qbuf, "Do you want to take %s out of %s?",
1789 | something, yname(obj));
1790 | if (flags.menu_style != MENU_TRADITIONAL) {
1791 | if (flags.menu_style == MENU_FULL) {
1792 | int t = in_or_out_menu("Do what?", current_container);
1793 | if (t <= 0) return 0;
1794 | loot_out = (t & 0x01) != 0;
1795 | loot_in = (t & 0x02) != 0;
1796 | } else { /* MENU_COMBINATION or MENU_PARTIAL */
1797 | loot_out = (yn_function(qbuf, "ynq", 'n') == 'y');
1798 | }
1799 | if (loot_out) {
1800 | add_valid_menu_class(0); /* reset */
1801 | used |= menu_loot(0, current_container, FALSE) > 0;
1802 | }
1803 | } else {
1804 | /* traditional code */
1805 | ask_again2:
1806 | menu_on_request = 0;
1807 | add_valid_menu_class(0); /* reset */
1808 | switch (yn_function(qbuf, ":ynq", 'n')) {
1809 | case ':':
1810 | container_contents(current_container, FALSE, FALSE);
1811 | goto ask_again2;
1812 | case 'y':
1813 | if (query_classes(select, &one_by_one, &allflag,
1814 | "take out", current_container->cobj,
1815 | FALSE, FALSE, &menu_on_request)) {
1816 | if (askchain((struct obj **)¤t_container->cobj,
1817 | (one_by_one ? (char *)0 : select),
1818 | allflag, out_container,
1819 | (int FDECL((*),(OBJ_P)))0,
1820 | 0, "nodot"))
1821 | used = 1;
1822 | } else if (menu_on_request < 0) {
1823 | used |= menu_loot(menu_on_request,
1824 | current_container, FALSE) > 0;
1825 | }
1826 | /*FALLTHRU*/
1827 | case 'n':
1828 | break;
1829 | case 'q':
1830 | default:
1831 | return used;
1832 | }
1833 | }
1834 | }
1835 |
1836 | if (!invent && u.ugold == 0) {
1837 | /* nothing to put in, but some feedback is necessary */
1838 | You("don't have anything to put in.");
1839 | return used;
1840 | }
1841 | if (flags.menu_style != MENU_FULL || !cnt) {
1842 | loot_in = (yn_function("Do you wish to put something in?",
1843 | ynqchars, 'n') == 'y');
1844 | }
1845 | /*
1846 | * Gone: being nice about only selecting food if we know we are
1847 | * putting things in an ice chest.
1848 | */
1849 | if (loot_in) {
1850 | if (u.ugold) {
1851 | /*
1852 | * Hack: gold is not in the inventory, so make a gold object
1853 | * and put it at the head of the inventory list.
1854 | */
1855 | u_gold = mkgoldobj(u.ugold); /* removes from u.ugold */
1856 | u.ugold = u_gold->quan; /* put the gold back */
1857 | assigninvlet(u_gold); /* might end up as NOINVSYM */
1858 | u_gold->nobj = invent;
1859 | invent = u_gold;
1860 | }
1861 | add_valid_menu_class(0); /* reset */
1862 | if (flags.menu_style != MENU_TRADITIONAL) {
1863 | used |= menu_loot(0, current_container, TRUE) > 0;
1864 | } else {
1865 | /* traditional code */
1866 | menu_on_request = 0;
1867 | if (query_classes(select, &one_by_one, &allflag, "put in",
1868 | invent, FALSE, (u.ugold != 0L),
1869 | &menu_on_request)) {
1870 | (void) askchain((struct obj **)&invent,
1871 | (one_by_one ? (char *)0 : select), allflag,
1872 | in_container, ck_bag, 0, "nodot");
1873 | used = 1;
1874 | } else if (menu_on_request < 0) {
1875 | used |= menu_loot(menu_on_request,
1876 | current_container, TRUE) > 0;
1877 | }
1878 | }
1879 | }
1880 |
1881 | if (u_gold && invent && invent->oclass == GOLD_CLASS) {
1882 | /* didn't stash [all of] it */
1883 | u_gold = invent;
1884 | invent = u_gold->nobj;
1885 | dealloc_obj(u_gold);
1886 | }
1887 |
1888 | return used;
1889 | }
1890 |
1891 | /* Loot a container (take things out, put things in), using a menu. */
1892 | STATIC_OVL int
1893 | menu_loot(retry, container, put_in)
1894 | int retry;
1895 | struct obj *container;
1896 | boolean put_in;
1897 | {
1898 | int n, i, n_looted = 0;
1899 | boolean all_categories = TRUE, loot_everything = FALSE;
1900 | char buf[BUFSZ];
1901 | const char *takeout = "Take out", *putin = "Put in";
1902 | struct obj *otmp, *otmp2;
1903 | menu_item *pick_list;
1904 | int mflags, res;
1905 | long count;
1906 |
1907 | if (retry) {
1908 | all_categories = (retry == -2);
1909 | } else if (flags.menu_style == MENU_FULL) {
1910 | all_categories = FALSE;
1911 | Sprintf(buf,"%s what type of objects?", put_in ? putin : takeout);
1912 | mflags = put_in ? ALL_TYPES : ALL_TYPES|CHOOSE_ALL;
1913 | n = query_category(buf, put_in ? invent : container->cobj,
1914 | mflags, &pick_list, PICK_ANY);
1915 | if (!n) return 0;
1916 | for (i = 0; i < n; i++) {
1917 | if (pick_list[i].item.a_int == 'A')
1918 | loot_everything = TRUE;
1919 | else if (pick_list[i].item.a_int == ALL_TYPES_SELECTED)
1920 | all_categories = TRUE;
1921 | else
1922 | add_valid_menu_class(pick_list[i].item.a_int);
1923 | }
1924 | free((genericptr_t) pick_list);
1925 | }
1926 |
1927 | if (loot_everything) {
1928 | for (otmp = container->cobj; otmp; otmp = otmp2) {
1929 | otmp2 = otmp->nobj;
1930 | res = out_container(otmp);
1931 | if (res < 0) break;
1932 | }
1933 | } else {
1934 | mflags = INVORDER_SORT;
1935 | if (put_in && flags.invlet_constant) mflags |= USE_INVLET;
1936 | Sprintf(buf,"%s what?", put_in ? putin : takeout);
1937 | n = query_objlist(buf, put_in ? invent : container->cobj,
1938 | mflags, &pick_list, PICK_ANY,
1939 | all_categories ? allow_all : allow_category);
1940 | if (n) {
1941 | n_looted = n;
1942 | for (i = 0; i < n; i++) {
1943 | otmp = pick_list[i].item.a_obj;
1944 | count = pick_list[i].count;
1945 | if (count > 0 && count < otmp->quan) {
1946 | otmp2 = splitobj(otmp, count);
1947 | /* special split case also handled by askchain() */
1948 | if (otmp == uwep) setuwep(otmp2);
1949 | if (otmp == uquiver) setuqwep(otmp2);
1950 | if (otmp == uswapwep) setuswapwep(otmp2);
1951 | }
1952 | res = put_in ? in_container(otmp) : out_container(otmp);
1953 | if (res < 0)
1954 | break;
1955 | }
1956 | free((genericptr_t)pick_list);
1957 | }
1958 | }
1959 | return n_looted;
1960 | }
1961 |
1962 | STATIC_OVL int
1963 | in_or_out_menu(prompt, obj)
1964 | const char *prompt;
1965 | struct obj *obj;
1966 | {
1967 | winid win;
1968 | anything any;
1969 | menu_item *pick_list;
1970 | char buf[BUFSZ];
1971 | int n;
1972 |
1973 | any.a_void = 0;
1974 | win = create_nhwindow(NHW_MENU);
1975 | start_menu(win);
1976 | any.a_int = 1;
1977 | Sprintf(buf,"Take %s out of %s", something, the(xname(obj)));
1978 | add_menu(win, NO_GLYPH, &any, 'a', 0, ATR_NONE, buf, MENU_UNSELECTED);
1979 | any.a_int = 2;
1980 | Sprintf(buf,"Put %s into %s", something, the(xname(obj)));
1981 | add_menu(win, NO_GLYPH, &any, 'b', 0, ATR_NONE, buf, MENU_UNSELECTED);
1982 | any.a_int = 3;
1983 | add_menu(win, NO_GLYPH, &any, 'c', 0, ATR_NONE,
1984 | "Both of the above", MENU_UNSELECTED);
1985 | end_menu(win, prompt);
1986 | n = select_menu(win, PICK_ONE, &pick_list);
1987 | destroy_nhwindow(win);
1988 | if (n > 0) {
1989 | n = pick_list[0].item.a_int;
1990 | free((genericptr_t) pick_list);
1991 | }
1992 | return n;
1993 | }
1994 |
1995 | /*pickup.c*/