1 | /* SCCS Id: @(#)invent.c 3.3 2000/04/12 */
2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | #include "hack.h"
6 | #include "artifact.h"
7 |
8 | #define NOINVSYM '#'
9 | #define CONTAINED_SYM '>' /* designator for inside a container */
10 |
11 | #ifdef OVL1
12 | STATIC_DCL void NDECL(reorder_invent);
13 | STATIC_DCL boolean FDECL(mergable,(struct obj *,struct obj *));
14 | STATIC_DCL void FDECL(invdisp_nothing, (const char *,const char *));
15 | STATIC_DCL boolean FDECL(worn_wield_only, (struct obj *));
16 | STATIC_DCL boolean FDECL(only_here, (struct obj *));
17 | #endif /* OVL1 */
18 | STATIC_DCL void FDECL(compactify,(char *));
19 | STATIC_PTR int FDECL(ckunpaid,(struct obj *));
20 | #ifdef OVLB
21 | STATIC_DCL boolean FDECL(this_type_only, (struct obj *));
22 | STATIC_DCL void NDECL(dounpaid);
23 | STATIC_DCL struct obj *FDECL(find_unpaid,(struct obj *,struct obj **));
24 | STATIC_DCL void FDECL(menu_identify, (int));
25 | static boolean FDECL(tool_in_use, (struct obj *));
26 | #endif /* OVLB */
27 | STATIC_DCL char FDECL(obj_to_let,(struct obj *));
28 |
29 | #ifdef OVLB
30 |
31 | static int lastinvnr = 51; /* 0 ... 51 (never saved&restored) */
32 |
33 | #ifdef WIZARD
34 | /* wizards can wish for venom, which will become an invisible inventory
35 | * item without this. putting it in inv_order would mean venom would
36 | * suddenly become a choice for all the inventory-class commands, which
37 | * would probably cause mass confusion. the test for inventory venom
38 | * is only WIZARD and not wizard because the wizard can leave venom lying
39 | * around on a bones level for normal players to find.
40 | */
41 | static char venom_inv[] = { VENOM_CLASS, 0 }; /* (constant) */
42 | #endif
43 |
44 | void
45 | assigninvlet(otmp)
46 | register struct obj *otmp;
47 | {
48 | boolean inuse[52];
49 | register int i;
50 | register struct obj *obj;
51 |
52 | for(i = 0; i < 52; i++) inuse[i] = FALSE;
53 | for(obj = invent; obj; obj = obj->nobj) if(obj != otmp) {
54 | i = obj->invlet;
55 | if('a' <= i && i <= 'z') inuse[i - 'a'] = TRUE; else
56 | if('A' <= i && i <= 'Z') inuse[i - 'A' + 26] = TRUE;
57 | if(i == otmp->invlet) otmp->invlet = 0;
58 | }
59 | if((i = otmp->invlet) &&
60 | (('a' <= i && i <= 'z') || ('A' <= i && i <= 'Z')))
61 | return;
62 | for(i = lastinvnr+1; i != lastinvnr; i++) {
63 | if(i == 52) { i = -1; continue; }
64 | if(!inuse[i]) break;
65 | }
66 | otmp->invlet = (inuse[i] ? NOINVSYM :
67 | (i < 26) ? ('a'+i) : ('A'+i-26));
68 | lastinvnr = i;
69 | }
70 |
71 | #endif /* OVLB */
72 | #ifdef OVL1
73 |
74 | /* note: assumes ASCII; toggling a bit puts lowercase in front of uppercase */
75 | #define inv_rank(o) ((o)->invlet ^ 040)
76 |
77 | /* sort the inventory; used by addinv() and doorganize() */
78 | STATIC_OVL void
79 | reorder_invent()
80 | {
81 | struct obj *otmp, *prev, *next;
82 | boolean need_more_sorting;
83 |
84 | do {
85 | /*
86 | * We expect at most one item to be out of order, so this
87 | * isn't nearly as inefficient as it may first appear.
88 | */
89 | need_more_sorting = FALSE;
90 | for (otmp = invent, prev = 0; otmp; ) {
91 | next = otmp->nobj;
92 | if (next && inv_rank(next) < inv_rank(otmp)) {
93 | need_more_sorting = TRUE;
94 | if (prev) prev->nobj = next;
95 | else invent = next;
96 | otmp->nobj = next->nobj;
97 | next->nobj = otmp;
98 | prev = next;
99 | } else {
100 | prev = otmp;
101 | otmp = next;
102 | }
103 | }
104 | } while (need_more_sorting);
105 | }
106 |
107 | #undef inv_rank
108 |
109 | /* scan a list of objects to see whether another object will merge with
110 | one of them; used in pickup.c when all 52 inventory slots are in use,
111 | to figure out whether another object could still be picked up */
112 | struct obj *
113 | merge_choice(objlist, obj)
114 | struct obj *objlist, *obj;
115 | {
116 | struct monst *shkp;
117 | int save_nocharge;
118 |
119 | if (obj->otyp == SCR_SCARE_MONSTER) /* punt on these */
120 | return (struct obj *)0;
121 | /* if this is an item on the shop floor, the attributes it will
122 | have when carried are different from what they are now; prevent
123 | that from eliciting an incorrect result from mergable() */
124 | save_nocharge = obj->no_charge;
125 | if (objlist == invent && obj->where == OBJ_FLOOR &&
126 | (shkp = shop_keeper(inside_shop(obj->ox, obj->oy))) != 0) {
127 | if (obj->no_charge) obj->no_charge = 0;
128 | /* A billable object won't have its `unpaid' bit set, so would
129 | erroneously seem to be a candidate to merge with a similar
130 | ordinary object. That's no good, because once it's really
131 | picked up, it won't merge after all. It might merge with
132 | another unpaid object, but we can't check that here (depends
133 | too much upon shk's bill) and if it doesn't merge it would
134 | end up in the '#' overflow inventory slot, so reject it now. */
135 | else if (inhishop(shkp)) return (struct obj *)0;
136 | }
137 | while (objlist) {
138 | if (mergable(objlist, obj)) break;
139 | objlist = objlist->nobj;
140 | }
141 | obj->no_charge = save_nocharge;
142 | return objlist;
143 | }
144 |
145 | /* merge obj with otmp and delete obj if types agree */
146 | int
147 | merged(potmp, pobj)
148 | struct obj **potmp, **pobj;
149 | {
150 | register struct obj *otmp = *potmp, *obj = *pobj;
151 |
152 | if(mergable(otmp, obj)) {
153 | /* Approximate age: we do it this way because if we were to
154 | * do it "accurately" (merge only when ages are identical)
155 | * we'd wind up never merging any corpses.
156 | * otmp->age = otmp->age*(1-proportion) + obj->age*proportion;
157 | *
158 | * Don't do the age manipulation if lit. We would need
159 | * to stop the burn on both items, then merge the age,
160 | * then restart the burn.
161 | */
162 | if (!obj->lamplit)
163 | otmp->age = ((otmp->age*otmp->quan) + (obj->age*obj->quan))
164 | / (otmp->quan + obj->quan);
165 |
166 | otmp->quan += obj->quan;
167 | otmp->owt += obj->owt;
168 | if(!otmp->onamelth && obj->onamelth)
169 | otmp = *potmp = oname(otmp, ONAME(obj));
170 | obj_extract_self(obj);
171 |
172 | /* really should merge the timeouts */
173 | if (obj->lamplit) obj_merge_light_sources(obj, otmp);
174 | if (obj->timed) obj_stop_timers(obj); /* follows lights */
175 |
176 | /* fixup for `#adjust' merging wielded darts, daggers, &c */
177 | if (obj->owornmask) {
178 | otmp->owornmask |= obj->owornmask;
179 | /* (it isn't necessary to "unwear" `obj' first) */
180 | if (carried(otmp))
181 | setworn(otmp, otmp->owornmask);
182 | #if 0
183 | /* (this should never be necessary, since items
184 | already in a monster's inventory don't ever get
185 | merged into other objects [only vice versa]) */
186 | else if (mcarried(otmp)) {
187 | if (obj == MON_WEP(otmp->ocarry))
188 | MON_WEP(otmp->ocarry) = otmp;
189 | }
190 | #endif
191 | }
192 | obfree(obj,otmp); /* free(obj), bill->otmp */
193 | return(1);
194 | }
195 | return 0;
196 | }
197 |
198 | /*
199 | Adjust hero intrinsics as if this object was being added to the hero's
200 | inventory. Called _before_ the object has been added to the hero's
201 | inventory.
202 |
203 | This is called when adding objects to the hero's inventory normally (via
204 | addinv) or when an object in the hero's inventory has been polymorphed
205 | in-place.
206 |
207 | It may be valid to merge this code with with addinv_core2().
208 | */
209 | void
210 | addinv_core1(obj)
211 | struct obj *obj;
212 | {
213 | if (obj->oclass == GOLD_CLASS) {
214 | u.ugold += obj->quan;
215 | flags.botl = 1;
216 | } else if (obj->otyp == AMULET_OF_YENDOR) {
217 | if (u.uhave.amulet) impossible("already have amulet?");
218 | u.uhave.amulet = 1;
219 | } else if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
220 | if (u.uhave.menorah) impossible("already have candelabrum?");
221 | u.uhave.menorah = 1;
222 | } else if (obj->otyp == BELL_OF_OPENING) {
223 | if (u.uhave.bell) impossible("already have silver bell?");
224 | u.uhave.bell = 1;
225 | } else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
226 | if (u.uhave.book) impossible("already have the book?");
227 | u.uhave.book = 1;
228 | } else if (obj->oartifact) {
229 | if (is_quest_artifact(obj)) {
230 | if (u.uhave.questart)
231 | impossible("already have quest artifact?");
232 | u.uhave.questart = 1;
233 | artitouch();
234 | }
235 | set_artifact_intrinsic(obj, 1, W_ART);
236 | }
237 | }
238 |
239 | /*
240 | Adjust hero intrinsics as if this object was being added to the hero's
241 | inventory. Called _after_ the object has been added to the hero's
242 | inventory.
243 |
244 | This is called when adding objects to the hero's inventory normally (via
245 | addinv) or when an object in the hero's inventory has been polymorphed
246 | in-place.
247 | */
248 | void
249 | addinv_core2(obj)
250 | struct obj *obj;
251 | {
252 | if (obj->otyp == LUCKSTONE ||
253 | (obj->oartifact && spec_ability(obj, SPFX_LUCK))) {
254 | /* new luckstone must be in inventory by this point
255 | * for correct calculation */
256 | set_moreluck();
257 | }
258 | }
259 |
260 | /*
261 | Add obj to the hero's inventory. Make sure the object is "free".
262 | Adjust hero attributes as necessary.
263 | */
264 | struct obj *
265 | addinv(obj)
266 | struct obj *obj;
267 | {
268 | struct obj *otmp, *prev;
269 |
270 | if (obj->where != OBJ_FREE)
271 | panic("addinv: obj not free");
272 |
273 | addinv_core1(obj);
274 | /* if handed gold, we're done */
275 | if (obj->oclass == GOLD_CLASS)
276 | return obj;
277 |
278 | /* merge if possible; find end of chain in the process */
279 | for (prev = 0, otmp = invent; otmp; prev = otmp, otmp = otmp->nobj)
280 | if (merged(&otmp, &obj)) {
281 | obj = otmp;
282 | goto added;
283 | }
284 | /* didn't merge, so insert into chain */
285 | if (flags.invlet_constant || !prev) {
286 | if (flags.invlet_constant) assigninvlet(obj);
287 | obj->nobj = invent; /* insert at beginning */
288 | invent = obj;
289 | if (flags.invlet_constant) reorder_invent();
290 | } else {
291 | prev->nobj = obj; /* insert at end */
292 | obj->nobj = 0;
293 | }
294 | obj->where = OBJ_INVENT;
295 |
296 | added:
297 | addinv_core2(obj);
298 | carry_obj_effects(obj); /* carrying affects the obj */
299 | update_inventory();
300 | return(obj);
301 | }
302 |
303 | /*
304 | * Some objects are affected by being carried.
305 | * Make those adjustments here. Called _after_ the object
306 | * has been added to the hero's or monster's inventory,
307 | * and after hero's intrinsics have been updated.
308 | */
309 | void
310 | carry_obj_effects(obj)
311 | struct obj *obj;
312 | {
313 | /* Cursed figurines can spontaneously transform
314 | when carried. */
315 | if (obj->otyp == FIGURINE) {
316 | if (obj->cursed
317 | && obj->corpsenm != NON_PM
318 | && !dead_species(obj->corpsenm,TRUE)) {
319 | attach_fig_transform_timeout(obj);
320 | }
321 | }
322 | }
323 |
324 | #endif /* OVL1 */
325 | #ifdef OVLB
326 |
327 | /* Add an item to the inventory unless we're fumbling, and give a message.
328 | * If there aren't any free inventory slots, we'll drop it instead.
329 | * If both success and failure messages are NULL, then we're just doing the
330 | * fumbling/slot-limit checking for a silent grab.
331 | */
332 | struct obj *
333 | hold_another_object(obj, drop_fmt, drop_arg, hold_msg)
334 | struct obj *obj;
335 | const char *drop_fmt, *drop_arg, *hold_msg;
336 | {
337 | char buf[BUFSZ];
338 |
339 | if (!Blind) obj->dknown = 1; /* maximize mergibility */
340 | if (Fumbling) {
341 | if (drop_fmt) pline(drop_fmt, drop_arg);
342 | dropy(obj);
343 | } else {
344 | long oquan = obj->quan;
345 | int prev_encumbr = near_capacity(); /* before addinv() */
346 | /* encumbrance only matters if it would now become worse
347 | than max( current_value, stressed ) */
348 | if (prev_encumbr < MOD_ENCUMBER) prev_encumbr = MOD_ENCUMBER;
349 | if (drop_arg) {
350 | /* addinv() may redraw the entire inventory, overwriting
351 | * drop_arg when it comes from something like doname()
352 | */
353 | Strcpy(buf, drop_arg);
354 | drop_arg = buf;
355 | }
356 | obj = addinv(obj);
357 | if (inv_cnt() > 52
358 | || ((obj->otyp != LOADSTONE || !obj->cursed)
359 | && near_capacity() > prev_encumbr)) {
360 | if (drop_fmt) pline(drop_fmt, drop_arg);
361 | /* undo any merge which took place */
362 | if (obj->quan > oquan) {
363 | struct obj *otmp = splitobj(obj, oquan);
364 | /* might have merged with weapon */
365 | if (obj->owornmask)
366 | setworn(otmp, obj->owornmask);
367 | }
368 | dropx(obj);
369 | } else {
370 | if (flags.autoquiver && !uquiver &&
371 | (is_missile(obj) ||
372 | (uwep && ammo_and_launcher(obj, uwep))))
373 | setuqwep(obj);
374 | if (hold_msg || drop_fmt) prinv(hold_msg, obj, oquan);
375 | }
376 | }
377 | return obj;
378 | }
379 |
380 | /* useup() all of an item regardless of its quantity */
381 | void
382 | useupall(obj)
383 | struct obj *obj;
384 | {
385 | setnotworn(obj);
386 | freeinv(obj);
387 | obfree(obj, (struct obj *)0); /* deletes contents also */
388 | }
389 |
390 | void
391 | useup(obj)
392 | register struct obj *obj;
393 | {
394 | /* Note: This works correctly for containers because they */
395 | /* (containers) don't merge. */
396 | if (obj->quan > 1L) {
397 | obj->in_use = FALSE; /* no longer in use */
398 | obj->quan--;
399 | obj->owt = weight(obj);
400 | update_inventory();
401 | } else {
402 | useupall(obj);
403 | }
404 | }
405 |
406 | #endif /* OVLB */
407 | #ifdef OVL3
408 |
409 | /*
410 | Adjust hero's attributes as if this object was being removed from the
411 | hero's inventory. This should only be called from freeinv() and
412 | where we are polymorphing an object already in the hero's inventory.
413 |
414 | Should think of a better name...
415 | */
416 | void
417 | freeinv_core(obj)
418 | struct obj *obj;
419 | {
420 | if (obj->oclass == GOLD_CLASS) {
421 | u.ugold -= obj->quan;
422 | flags.botl = 1;
423 | return;
424 | } else if (obj->otyp == AMULET_OF_YENDOR) {
425 | if (!u.uhave.amulet) impossible("don't have amulet?");
426 | u.uhave.amulet = 0;
427 | } else if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
428 | if (!u.uhave.menorah) impossible("don't have candelabrum?");
429 | u.uhave.menorah = 0;
430 | } else if (obj->otyp == BELL_OF_OPENING) {
431 | if (!u.uhave.bell) impossible("don't have silver bell?");
432 | u.uhave.bell = 0;
433 | } else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
434 | if (!u.uhave.book) impossible("don't have the book?");
435 | u.uhave.book = 0;
436 | } else if (obj->oartifact) {
437 | if (is_quest_artifact(obj)) {
438 | if (!u.uhave.questart)
439 | impossible("don't have quest artifact?");
440 | u.uhave.questart = 0;
441 | }
442 | set_artifact_intrinsic(obj, 0, W_ART);
443 | }
444 |
445 | if (obj->otyp == LOADSTONE) {
446 | curse(obj);
447 | } else if (obj->otyp == LUCKSTONE ||
448 | (obj->oartifact && spec_ability(obj, SPFX_LUCK))) {
449 | set_moreluck();
450 | flags.botl = 1;
451 | } else if (obj->otyp == FIGURINE && obj->timed) {
452 | (void) stop_timer(FIG_TRANSFORM, (genericptr_t) obj);
453 | }
454 | }
455 |
456 | /* remove an object from the hero's inventory */
457 | void
458 | freeinv(obj)
459 | register struct obj *obj;
460 | {
461 | extract_nobj(obj, &invent);
462 | freeinv_core(obj);
463 | update_inventory();
464 | }
465 |
466 | void
467 | delallobj(x, y)
468 | int x, y;
469 | {
470 | struct obj *otmp, *otmp2;
471 |
472 | for (otmp = level.objects[x][y]; otmp; otmp = otmp2) {
473 | if (otmp == uball)
474 | unpunish();
475 | /* after unpunish(), or might get deallocated chain */
476 | otmp2 = otmp->nexthere;
477 | if (otmp == uchain)
478 | continue;
479 | delobj(otmp);
480 | }
481 | }
482 |
483 | #endif /* OVL3 */
484 | #ifdef OVL2
485 |
486 | /* destroy object in fobj chain (if unpaid, it remains on the bill) */
487 | void
488 | delobj(obj)
489 | register struct obj *obj;
490 | {
491 | boolean update_map;
492 |
493 | if (obj->otyp == AMULET_OF_YENDOR ||
494 | obj->otyp == CANDELABRUM_OF_INVOCATION ||
495 | obj->otyp == BELL_OF_OPENING ||
496 | obj->otyp == SPE_BOOK_OF_THE_DEAD) {
497 | /* player might be doing something stupid, but we
498 | * can't guarantee that. assume special artifacts
499 | * are indestructible via drawbridges, and exploding
500 | * chests, and golem creation, and ...
501 | */
502 | return;
503 | }
504 | update_map = (obj->where == OBJ_FLOOR);
505 | obj_extract_self(obj);
506 | if (update_map) newsym(obj->ox, obj->oy);
507 | obfree(obj, (struct obj *) 0); /* frees contents also */
508 | }
509 |
510 | #endif /* OVL2 */
511 | #ifdef OVL0
512 |
513 | struct obj *
514 | sobj_at(n,x,y)
515 | register int n, x, y;
516 | {
517 | register struct obj *otmp;
518 |
519 | for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
520 | if(otmp->otyp == n)
521 | return(otmp);
522 | return((struct obj *)0);
523 | }
524 |
525 | #endif /* OVL0 */
526 | #ifdef OVLB
527 |
528 | struct obj *
529 | carrying(type)
530 | register int type;
531 | {
532 | register struct obj *otmp;
533 |
534 | for(otmp = invent; otmp; otmp = otmp->nobj)
535 | if(otmp->otyp == type)
536 | return(otmp);
537 | return((struct obj *) 0);
538 | }
539 |
540 | boolean
541 | have_lizard()
542 | {
543 | register struct obj *otmp;
544 |
545 | for(otmp = invent; otmp; otmp = otmp->nobj)
546 | if(otmp->otyp == CORPSE && otmp->corpsenm == PM_LIZARD)
547 | return(TRUE);
548 | return(FALSE);
549 | }
550 |
551 | struct obj *
552 | o_on(id, objchn)
553 | unsigned int id;
554 | register struct obj *objchn;
555 | {
556 | struct obj *temp;
557 |
558 | while(objchn) {
559 | if(objchn->o_id == id) return(objchn);
560 | if (Has_contents(objchn) && (temp = o_on(id,objchn->cobj)))
561 | return temp;
562 | objchn = objchn->nobj;
563 | }
564 | return((struct obj *) 0);
565 | }
566 |
567 | boolean
568 | obj_here(obj, x, y)
569 | register struct obj *obj;
570 | int x, y;
571 | {
572 | register struct obj *otmp;
573 |
574 | for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
575 | if(obj == otmp) return(TRUE);
576 | return(FALSE);
577 | }
578 |
579 | #endif /* OVLB */
580 | #ifdef OVL2
581 |
582 | struct obj *
583 | g_at(x,y)
584 | register int x, y;
585 | {
586 | register struct obj *obj = level.objects[x][y];
587 | while(obj) {
588 | if (obj->oclass == GOLD_CLASS) return obj;
589 | obj = obj->nexthere;
590 | }
591 | return((struct obj *)0);
592 | }
593 |
594 | #endif /* OVL2 */
595 | #ifdef OVLB
596 |
597 | /* Make a gold object from the hero's gold. */
598 | struct obj *
599 | mkgoldobj(q)
600 | register long q;
601 | {
602 | register struct obj *otmp;
603 |
604 | otmp = mksobj(GOLD_PIECE, FALSE, FALSE);
605 | u.ugold -= q;
606 | otmp->quan = q;
607 | otmp->owt = weight(otmp);
608 | flags.botl = 1;
609 | return(otmp);
610 | }
611 |
612 | #endif /* OVLB */
613 | #ifdef OVL1
614 |
615 | STATIC_OVL void
616 | compactify(buf)
617 | register char *buf;
618 | /* compact a string of inventory letters by dashing runs of letters */
619 | {
620 | register int i1 = 1, i2 = 1;
621 | register char ilet, ilet1, ilet2;
622 |
623 | ilet2 = buf[0];
624 | ilet1 = buf[1];
625 | buf[++i2] = buf[++i1];
626 | ilet = buf[i1];
627 | while(ilet) {
628 | if(ilet == ilet1+1) {
629 | if(ilet1 == ilet2+1)
630 | buf[i2 - 1] = ilet1 = '-';
631 | else if(ilet2 == '-') {
632 | buf[i2 - 1] = ++ilet1;
633 | buf[i2] = buf[++i1];
634 | ilet = buf[i1];
635 | continue;
636 | }
637 | }
638 | ilet2 = ilet1;
639 | ilet1 = ilet;
640 | buf[++i2] = buf[++i1];
641 | ilet = buf[i1];
642 | }
643 | }
644 |
645 | /*
646 | * getobj returns:
647 | * struct obj *xxx: object to do something with.
648 | * (struct obj *) 0 error return: no object.
649 | * &zeroobj explicitly no object (as in w-).
650 | */
651 | struct obj *
652 | getobj(let,word)
653 | register const char *let,*word;
654 | {
655 | register struct obj *otmp;
656 | register char ilet;
657 | char buf[BUFSZ], qbuf[QBUFSZ];
658 | char lets[BUFSZ], altlets[BUFSZ], *ap;
659 | register int foo = 0;
660 | register char *bp = buf;
661 | xchar allowcnt = 0; /* 0, 1 or 2 */
662 | boolean allowgold = FALSE, usegold = FALSE;
663 | /* Two possibilities: they can't use gold because it's illegal,
664 | * or they can't use gold because they don't have any.
665 | */
666 | boolean allowall = FALSE;
667 | boolean allownone = FALSE;
668 | xchar foox = 0;
669 | long cnt;
670 | boolean prezero = FALSE;
671 | long dummymask;
672 |
673 | if(*let == ALLOW_COUNT) let++, allowcnt = 1;
674 | if(*let == GOLD_CLASS) let++,
675 | usegold = TRUE, allowgold = (u.ugold ? TRUE : FALSE);
676 |
677 | /* Equivalent of an "ugly check" for gold */
678 | if (usegold && !strcmp(word, "eat") && !metallivorous(youmonst.data))
679 | usegold = allowgold = FALSE;
680 |
681 | if(*let == ALL_CLASSES) let++, allowall = TRUE;
682 | if(*let == ALLOW_NONE) let++, allownone = TRUE;
683 | /* "ugly check" for reading fortune cookies, part 1 */
684 | /* The normal 'ugly check' keeps the object on the inventory list.
685 | * We don't want to do that for shirts/cookies, so the check for
686 | * them is handled a bit differently (and also requires that we set
687 | * allowall in the caller)
688 | */
689 | if(allowall && !strcmp(word, "read")) allowall = FALSE;
690 |
691 | if(allownone) *bp++ = '-';
692 | if(allowgold) *bp++ = def_oc_syms[GOLD_CLASS];
693 | if(bp > buf && bp[-1] == '-') *bp++ = ' ';
694 | ap = altlets;
695 |
696 | ilet = 'a';
697 | for (otmp = invent; otmp; otmp = otmp->nobj) {
698 | if (!flags.invlet_constant) otmp->invlet = ilet; /* reassign() */
699 | if (!*let || index(let, otmp->oclass)) {
700 | register int otyp = otmp->otyp;
701 | bp[foo++] = otmp->invlet;
702 |
703 | /* ugly check: remove inappropriate things */
704 | if((!strcmp(word, "take off") &&
705 | (!(otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL))
706 | || (otmp==uarm && uarmc)
707 | #ifdef TOURIST
708 | || (otmp==uarmu && (uarm || uarmc))
709 | #endif
710 | ))
711 | || (!strcmp(word, "wear") &&
712 | (otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)))
713 | /* already worn */
714 | || (!strcmp(word, "wield") &&
715 | (otmp->owornmask & W_WEP))
716 | || (!strcmp(word, "ready") &&
717 | (otmp->owornmask & (W_WEP | W_SWAPWEP | W_QUIVER)))
718 | ) {
719 | foo--;
720 | foox++;
721 | }
722 |
723 | /* Second ugly check; unlike the first it won't trigger an
724 | * "else" in "you don't have anything else to ___".
725 | */
726 | else if ((!strcmp(word, "wear") &&
727 | ((otmp->oclass == FOOD_CLASS && otmp->otyp != MEAT_RING) ||
728 | (otmp->oclass == TOOL_CLASS &&
729 | otyp != BLINDFOLD && otyp != TOWEL && otyp != LENSES)))
730 | || (!strcmp(word, "wield") &&
731 | (otmp->oclass == TOOL_CLASS && !is_weptool(otmp)))
732 | || (!strcmp(word, "eat") && !is_edible(otmp))
733 | || (!strcmp(word, "sacrifice") &&
734 | (otyp != CORPSE &&
735 | otyp != AMULET_OF_YENDOR && otyp != FAKE_AMULET_OF_YENDOR))
736 | || (!strcmp(word, "write with") &&
737 | (otmp->oclass == TOOL_CLASS &&
738 | otyp != MAGIC_MARKER && otyp != TOWEL))
739 | || (!strcmp(word, "tin") &&
740 | (otyp != CORPSE || !tinnable(otmp)))
741 | || (!strcmp(word, "rub") &&
742 | (otmp->oclass == TOOL_CLASS &&
743 | otyp != OIL_LAMP && otyp != MAGIC_LAMP &&
744 | otyp != BRASS_LANTERN))
745 | || ((!strcmp(word, "use or apply") ||
746 | !strcmp(word, "untrap with")) &&
747 | /* Picks, axes, pole-weapons, bullwhips */
748 | ((otmp->oclass == WEAPON_CLASS && !is_pick(otmp) &&
749 | !is_pole(otmp) && otyp != BULLWHIP)
750 | || (otmp->oclass == POTION_CLASS &&
751 | /* only applicable potion is oil, and it will only
752 | be offered as a choice when already discovered */
753 | (otyp != POT_OIL || !otmp->dknown ||
754 | !objects[POT_OIL].oc_name_known))))
755 | || (!strcmp(word, "invoke") &&
756 | (!otmp->oartifact && !objects[otyp].oc_unique &&
757 | (otyp != FAKE_AMULET_OF_YENDOR || otmp->known) &&
758 | otyp != CRYSTAL_BALL && /* #invoke synonym for apply */
759 | /* note: presenting the possibility of invoking non-artifact
760 | mirrors and/or lamps is a simply a cruel deception... */
761 | otyp != MIRROR && otyp != MAGIC_LAMP &&
762 | (otyp != OIL_LAMP || /* don't list known oil lamp */
763 | (otmp->dknown && objects[OIL_LAMP].oc_name_known))))
764 | || (!strcmp(word, "untrap with") &&
765 | (otmp->oclass == TOOL_CLASS && otyp != CAN_OF_GREASE))
766 | || (!strcmp(word, "charge") && !is_chargeable(otmp))
767 | )
768 | foo--;
769 | /* ugly check for unworn armor that can't be worn */
770 | else if (!strcmp(word, "wear") && *let == ARMOR_CLASS &&
771 | !canwearobj(otmp, &dummymask, FALSE)) {
772 | foo--;
773 | allowall = TRUE;
774 | *ap++ = otmp->invlet;
775 | }
776 | } else {
777 |
778 | /* "ugly check" for reading fortune cookies, part 2 */
779 | if ((!strcmp(word, "read") &&
780 | (otmp->otyp == FORTUNE_COOKIE
781 | #ifdef TOURIST
782 | || otmp->otyp == T_SHIRT
783 | #endif
784 | )))
785 | allowall = TRUE;
786 | }
787 |
788 | if(ilet == 'z') ilet = 'A'; else ilet++;
789 | }
790 | bp[foo] = 0;
791 | if(foo == 0 && bp > buf && bp[-1] == ' ') *--bp = 0;
792 | Strcpy(lets, bp); /* necessary since we destroy buf */
793 | if(foo > 5) /* compactify string */
794 | compactify(bp);
795 | *ap = '\0';
796 |
797 | if(!foo && !allowall && !allowgold && !allownone) {
798 | You("don't have anything %sto %s.",
799 | foox ? "else " : "", word);
800 | return((struct obj *)0);
801 | }
802 | for(;;) {
803 | cnt = 0;
804 | if (allowcnt == 2) allowcnt = 1; /* abort previous count */
805 | if(!buf[0]) {
806 | Sprintf(qbuf, "What do you want to %s? [*]", word);
807 | } else {
808 | Sprintf(qbuf, "What do you want to %s? [%s or ?*]",
809 | word, buf);
810 | }
811 | #ifdef REDO
812 | if (in_doagain)
813 | ilet = readchar();
814 | else
815 | #endif
816 | ilet = yn_function(qbuf, (char *)0, '\0');
817 | if(ilet == '0') prezero = TRUE;
818 | while(digit(ilet) && allowcnt) {
819 | #ifdef REDO
820 | if (ilet != '?' && ilet != '*') savech(ilet);
821 | #endif
822 | cnt = 10*cnt + (ilet - '0');
823 | allowcnt = 2; /* signal presence of cnt */
824 | ilet = readchar();
825 | }
826 | if(digit(ilet)) {
827 | pline("No count allowed with this command.");
828 | continue;
829 | }
830 | if(index(quitchars,ilet)) {
831 | if(flags.verbose)
832 | pline(Never_mind);
833 | return((struct obj *)0);
834 | }
835 | if(ilet == '-') {
836 | return(allownone ? &zeroobj : (struct obj *) 0);
837 | }
838 | if(ilet == def_oc_syms[GOLD_CLASS]) {
839 | if(!usegold){
840 | You("cannot %s gold.", word);
841 | return(struct obj *)0;
842 | } else if (!allowgold) {
843 | You("are not carrying any gold.");
844 | return(struct obj *)0;
845 | }
846 | if(cnt == 0 && prezero) return((struct obj *)0);
847 | /* Historic note: early Nethack had a bug which was
848 | * first reported for Larn, where trying to drop 2^32-n
849 | * gold pieces was allowed, and did interesting things
850 | * to your money supply. The LRS is the tax bureau
851 | * from Larn.
852 | */
853 | if(cnt < 0) {
854 | pline_The("LRS would be very interested to know you have that much.");
855 | return(struct obj *)0;
856 | }
857 |
858 | if(!(allowcnt == 2 && cnt < u.ugold))
859 | cnt = u.ugold;
860 | return(mkgoldobj(cnt));
861 | }
862 | if(allowcnt == 2 && !strcmp(word,"throw")) {
863 | /* permit counts for throwing gold, but don't accept
864 | * counts for other things since the throw code will
865 | * split off a single item anyway */
866 | allowcnt = 1;
867 | if(cnt == 0 && prezero) return((struct obj *)0);
868 | if(cnt > 1) {
869 | You("can only throw one item at a time.");
870 | continue;
871 | }
872 | }
873 | if(ilet == '?' || ilet == '*') {
874 | char *allowed_choices = (ilet == '?') ? lets : (char *)0;
875 |
876 | if (ilet == '?' && !*lets && *altlets)
877 | allowed_choices = altlets;
878 | ilet = display_inventory(allowed_choices, TRUE);
879 | if(!ilet) continue;
880 | if(ilet == '\033') {
881 | if(flags.verbose)
882 | pline(Never_mind);
883 | return((struct obj *)0);
884 | }
885 | /* they typed a letter (not a space) at the prompt */
886 | }
887 | #ifdef REDO
888 | savech(ilet);
889 | #endif
890 | for (otmp = invent; otmp; otmp = otmp->nobj)
891 | if (otmp->invlet == ilet) break;
892 | if(!otmp) {
893 | You("don't have that object.");
894 | #ifdef REDO
895 | if (in_doagain) return((struct obj *) 0);
896 | #endif
897 | continue;
898 | } else if (cnt < 0 || otmp->quan < cnt) {
899 | You("don't have that many! You have only %ld.",
900 | otmp->quan);
901 | #ifdef REDO
902 | if (in_doagain) return((struct obj *) 0);
903 | #endif
904 | continue;
905 | }
906 | break;
907 | }
908 | if(!allowall && let && !index(let,otmp->oclass)) {
909 | pline(silly_thing_to, word);
910 | return((struct obj *)0);
911 | }
912 | if(allowcnt == 2) { /* cnt given */
913 | if(cnt == 0) return (struct obj *)0;
914 | if(cnt != otmp->quan) {
915 | register struct obj *obj = splitobj(otmp, cnt);
916 | /* Very ugly kludge necessary to prevent someone from trying
917 | * to drop one of several loadstones and having the loadstone
918 | * now be separate.
919 | */
920 | if (!strcmp(word, "drop") &&
921 | obj->otyp == LOADSTONE && obj->cursed)
922 | otmp->corpsenm = obj->invlet;
923 | if(otmp == uwep) setuwep(obj);
924 | else if (otmp == uquiver) setuqwep(obj);
925 | if (otmp == uswapwep) setuswapwep(obj);
926 | }
927 | }
928 | return(otmp);
929 | }
930 |
931 | #endif /* OVL1 */
932 | #ifdef OVLB
933 |
934 | STATIC_PTR int
935 | ckunpaid(otmp)
936 | register struct obj *otmp;
937 | {
938 | return((int)(otmp->unpaid));
939 | }
940 |
941 | boolean
942 | wearing_armor()
943 | {
944 | return((boolean)(uarm || uarmc || uarmf || uarmg || uarmh || uarms
945 | #ifdef TOURIST
946 | || uarmu
947 | #endif
948 | ));
949 | }
950 |
951 | boolean
952 | is_worn(otmp)
953 | register struct obj *otmp;
954 | {
955 | return((boolean)(!!(otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL |
956 | #ifdef STEED
957 | W_SADDLE |
958 | #endif
959 | W_WEP | W_SWAPWEP | W_QUIVER))));
960 | }
961 |
962 | static NEARDATA const char removeables[] =
963 | { ARMOR_CLASS, WEAPON_CLASS, RING_CLASS, AMULET_CLASS, TOOL_CLASS, 0 };
964 |
965 | /* interactive version of getobj - used for Drop, Identify and */
966 | /* Takeoff (A). Return the number of times fn was called successfully */
967 | /* If combo is TRUE, we just use this to get a category list */
968 | int
969 | ggetobj(word, fn, mx, combo)
970 | const char *word;
971 | int FDECL((*fn),(OBJ_P)), mx;
972 | boolean combo; /* combination menu flag */
973 | {
974 | int FDECL((*ckfn),(OBJ_P)) = (int FDECL((*),(OBJ_P))) 0;
975 | boolean FDECL((*filter),(OBJ_P)) = (boolean FDECL((*),(OBJ_P))) 0;
976 | boolean takeoff, ident, allflag, m_seen;
977 | int oletct, iletct, allowgold, unpaid, oc_of_sym;
978 | char sym, *ip, olets[MAXOCLASSES+5], ilets[MAXOCLASSES+5];
979 | char buf[BUFSZ], qbuf[QBUFSZ];
980 |
981 | allowgold = (u.ugold && !strcmp(word, "drop")) ? 1 : 0;
982 | takeoff = ident = allflag = m_seen = FALSE;
983 | if(!invent && !allowgold){
984 | You("have nothing to %s.", word);
985 | return(0);
986 | }
987 | if (combo) add_valid_menu_class(0); /* reset */
988 | if (!strcmp(word, "take off")) {
989 | takeoff = TRUE;
990 | filter = is_worn;
991 | } else if (!strcmp(word, "identify")) {
992 | ident = TRUE;
993 | filter = not_fully_identified;
994 | }
995 |
996 | iletct = collect_obj_classes(ilets, invent,
997 | FALSE, (allowgold != 0), filter);
998 | unpaid = count_unpaid(invent);
999 |
1000 | if (ident && !iletct) {
1001 | return -1; /* no further identifications */
1002 | } else if (!takeoff && (unpaid || invent)) {
1003 | ilets[iletct++] = ' ';
1004 | if (unpaid) ilets[iletct++] = 'u';
1005 | if (invent) ilets[iletct++] = 'a';
1006 | } else if (takeoff && invent) {
1007 | ilets[iletct++] = ' ';
1008 | }
1009 | ilets[iletct++] = 'i';
1010 | if (!combo)
1011 | ilets[iletct++] = 'm'; /* allow menu presentation on request */
1012 | ilets[iletct] = '\0';
1013 |
1014 | for (;;) {
1015 | Sprintf(qbuf,"What kinds of thing do you want to %s? [%s]",
1016 | word, ilets);
1017 | getlin(qbuf, buf);
1018 | if (buf[0] == '\033') return(0);
1019 | if (index(buf, 'i')) {
1020 | if (display_inventory((char *)0, TRUE) == '\033') return 0;
1021 | } else
1022 | break;
1023 | }
1024 |
1025 | ip = buf;
1026 | olets[oletct = 0] = '\0';
1027 | while ((sym = *ip++) != '\0') {
1028 | if (sym == ' ') continue;
1029 | oc_of_sym = def_char_to_objclass(sym);
1030 | if (takeoff && !(uwep && oc_of_sym == uwep->oclass) &&
1031 | (oc_of_sym != MAXOCLASSES)) {
1032 | if (!index(removeables, oc_of_sym)) {
1033 | pline("Not applicable.");
1034 | return 0;
1035 | } else if (oc_of_sym == ARMOR_CLASS && !wearing_armor()) {
1036 | You("are not wearing any armor.");
1037 | return 0;
1038 | } else if (oc_of_sym == WEAPON_CLASS && !uwep && !uswapwep && !uquiver) {
1039 | You("are not wielding anything.");
1040 | return 0;
1041 | } else if (oc_of_sym == RING_CLASS && !uright && !uleft) {
1042 | You("are not wearing rings.");
1043 | return 0;
1044 | } else if (oc_of_sym == AMULET_CLASS && !uamul) {
1045 | You("are not wearing an amulet.");
1046 | return 0;
1047 | } else if (oc_of_sym == TOOL_CLASS && !ublindf) {
1048 | You("are not wearing a blindfold.");
1049 | return 0;
1050 | }
1051 | }
1052 |
1053 | if (oc_of_sym == GOLD_CLASS && !combo) {
1054 | if (allowgold == 1)
1055 | (*fn)(mkgoldobj(u.ugold));
1056 | else if (!u.ugold)
1057 | You("have no gold.");
1058 | allowgold = 2;
1059 | } else if (sym == 'a') {
1060 | allflag = TRUE;
1061 | } else if (sym == 'A') {
1062 | /* same as the default */ ;
1063 | } else if (sym == 'u' || sym == 'U') {
1064 | add_valid_menu_class('u');
1065 | ckfn = ckunpaid;
1066 | } else if (sym == 'm') {
1067 | m_seen = TRUE;
1068 | } else if (oc_of_sym == MAXOCLASSES) {
1069 | You("don't have any %c's.", sym);
1070 | } else if (oc_of_sym != VENOM_CLASS) { /* suppress venom */
1071 | if (!index(olets, oc_of_sym)) {
1072 | add_valid_menu_class(oc_of_sym);
1073 | olets[oletct++] = oc_of_sym;
1074 | olets[oletct] = 0;
1075 | }
1076 | }
1077 | }
1078 |
1079 | if (m_seen)
1080 | return (allflag || (!oletct && ckfn != ckunpaid)) ? -2 : -3;
1081 | else if (flags.menu_style != MENU_TRADITIONAL && combo && !allflag)
1082 | return 0;
1083 | else if (allowgold == 2 && !oletct)
1084 | return 1; /* you dropped gold (or at least tried to) */
1085 | else
1086 | return askchain(&invent, olets, allflag, fn, ckfn, mx, word);
1087 | }
1088 |
1089 | /*
1090 | * Walk through the chain starting at objchn and ask for all objects
1091 | * with olet in olets (if nonNULL) and satisfying ckfn (if nonnull)
1092 | * whether the action in question (i.e., fn) has to be performed.
1093 | * If allflag then no questions are asked. Max gives the max nr of
1094 | * objects to be treated. Return the number of objects treated.
1095 | */
1096 | int
1097 | askchain(objchn, olets, allflag, fn, ckfn, mx, word)
1098 | struct obj **objchn;
1099 | register int allflag, mx;
1100 | register const char *olets, *word; /* olets is an Obj Class char array */
1101 | register int FDECL((*fn),(OBJ_P)), FDECL((*ckfn),(OBJ_P));
1102 | {
1103 | register struct obj *otmp, *otmp2;
1104 | register char sym, ilet;
1105 | register int cnt = 0, dud = 0, tmp;
1106 | boolean takeoff, nodot, ident, ininv;
1107 | char qbuf[QBUFSZ];
1108 |
1109 | takeoff = !strcmp(word, "take off");
1110 | ident = !strcmp(word, "identify");
1111 | nodot = (!strcmp(word, "nodot") || !strcmp(word, "drop") ||
1112 | ident || takeoff);
1113 | ininv = (*objchn == invent);
1114 | /* Changed so the askchain is interrogated in the order specified.
1115 | * For example, if a person specifies =/ then first all rings will be
1116 | * asked about followed by all wands -dgk
1117 | */
1118 | nextclass:
1119 | ilet = 'a'-1;
1120 | if (*objchn && (*objchn)->oclass == GOLD_CLASS)
1121 | ilet--; /* extra iteration */
1122 | for (otmp = *objchn; otmp; otmp = otmp2) {
1123 | if(ilet == 'z') ilet = 'A'; else ilet++;
1124 | otmp2 = otmp->nobj;
1125 | if (olets && *olets && otmp->oclass != *olets) continue;
1126 | if (takeoff && !is_worn(otmp)) continue;
1127 | if (ident && !not_fully_identified(otmp)) continue;
1128 | if (ckfn && !(*ckfn)(otmp)) continue;
1129 | if (!allflag) {
1130 | Strcpy(qbuf, !ininv ? doname(otmp) :
1131 | xprname(otmp, (char *)0, ilet, !nodot, 0L));
1132 | Strcat(qbuf, "?");
1133 | sym = (takeoff || ident || otmp->quan < 2L) ?
1134 | nyaq(qbuf) : nyNaq(qbuf);
1135 | }
1136 | else sym = 'y';
1137 |
1138 | if (sym == '#') {
1139 | /* Number was entered; split the object unless it corresponds
1140 | to 'none' or 'all'. 2 special cases: cursed loadstones and
1141 | welded weapons (eg, multiple daggers) will remain as merged
1142 | unit; done to avoid splitting an object that won't be
1143 | droppable (even if we're picking up rather than dropping).
1144 | */
1145 | if (!yn_number)
1146 | sym = 'n';
1147 | else {
1148 | sym = 'y';
1149 | if (yn_number < otmp->quan && !welded(otmp) &&
1150 | (!otmp->cursed || otmp->otyp != LOADSTONE)) {
1151 | struct obj *otmpx = splitobj(otmp, yn_number);
1152 | if (!otmpx || otmpx->nobj != otmp2)
1153 | impossible("bad object split in askchain");
1154 | /* assume other worn items aren't mergable */
1155 | if (otmp == uwep) setuwep(otmpx);
1156 | if (otmp == uquiver) setuqwep(otmpx);
1157 | if (otmp == uswapwep) setuswapwep(otmpx);
1158 | }
1159 | }
1160 | }
1161 | switch(sym){
1162 | case 'a':
1163 | allflag = 1;
1164 | case 'y':
1165 | tmp = (*fn)(otmp);
1166 | if(tmp < 0) goto ret;
1167 | cnt += tmp;
1168 | if(--mx == 0) goto ret;
1169 | case 'n':
1170 | if(nodot) dud++;
1171 | default:
1172 | break;
1173 | case 'q':
1174 | /* special case for seffects() */
1175 | if (ident) cnt = -1;
1176 | goto ret;
1177 | }
1178 | }
1179 | if (olets && *olets && *++olets)
1180 | goto nextclass;
1181 | if(!takeoff && (dud || cnt)) pline("That was all.");
1182 | else if(!dud && !cnt) pline("No applicable objects.");
1183 | ret:
1184 | return(cnt);
1185 | }
1186 |
1187 |
1188 | /*
1189 | * Object identification routines:
1190 | */
1191 |
1192 | /* make an object actually be identified; no display updating */
1193 | void
1194 | fully_identify_obj(otmp)
1195 | struct obj *otmp;
1196 | {
1197 | makeknown(otmp->otyp);
1198 | if (otmp->oartifact) discover_artifact((xchar)otmp->oartifact);
1199 | otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
1200 | if (otmp->otyp == EGG && otmp->corpsenm != NON_PM)
1201 | learn_egg_type(otmp->corpsenm);
1202 | }
1203 |
1204 | /* ggetobj callback routine; identify an object and give immediate feedback */
1205 | int
1206 | identify(otmp)
1207 | struct obj *otmp;
1208 | {
1209 | fully_identify_obj(otmp);
1210 | prinv((char *)0, otmp, 0L);
1211 | return 1;
1212 | }
1213 |
1214 | /* menu of unidentified objects; select and identify up to id_limit of them */
1215 | STATIC_OVL void
1216 | menu_identify(id_limit)
1217 | int id_limit;
1218 | {
1219 | menu_item *pick_list;
1220 | int n, i, first = 1;
1221 | char buf[BUFSZ];
1222 | /* assumptions: id_limit > 0 and at least one unID'd item is present */
1223 |
1224 | while (id_limit) {
1225 | Sprintf(buf, "What would you like to identify %s?",
1226 | first ? "first" : "next");
1227 | n = query_objlist(buf, invent, SIGNAL_NOMENU|USE_INVLET|INVORDER_SORT,
1228 | &pick_list, PICK_ANY, not_fully_identified);
1229 |
1230 | if (n > 0) {
1231 | if (n > id_limit) n = id_limit;
1232 | for (i = 0; i < n; i++, id_limit--)
1233 | (void) identify(pick_list[i].item.a_obj);
1234 | free((genericptr_t) pick_list);
1235 | mark_synch(); /* Before we loop to pop open another menu */
1236 | } else {
1237 | if (n < 0) pline("That was all.");
1238 | id_limit = 0; /* Stop now */
1239 | }
1240 | first = 0;
1241 | }
1242 | }
1243 |
1244 | /* dialog with user to identify a given number of items; 0 means all */
1245 | void
1246 | identify_pack(id_limit)
1247 | int id_limit;
1248 | {
1249 | struct obj *obj, *the_obj;
1250 | int n, unid_cnt;
1251 |
1252 | unid_cnt = 0;
1253 | the_obj = 0; /* if unid_cnt ends up 1, this will be it */
1254 | for (obj = invent; obj; obj = obj->nobj)
1255 | if (not_fully_identified(obj)) ++unid_cnt, the_obj = obj;
1256 |
1257 | if (!unid_cnt) {
1258 | You("have already identified all of your possessions.");
1259 | } else if (!id_limit) {
1260 | /* identify everything */
1261 | if (unid_cnt == 1) {
1262 | (void) identify(the_obj);
1263 | } else {
1264 |
1265 | /* TODO: use fully_identify_obj and cornline/menu/whatever here */
1266 | for (obj = invent; obj; obj = obj->nobj)
1267 | if (not_fully_identified(obj)) (void) identify(obj);
1268 |
1269 | }
1270 | } else {
1271 | /* identify up to `id_limit' items */
1272 | n = 0;
1273 | if (flags.menu_style == MENU_TRADITIONAL)
1274 | do {
1275 | n = ggetobj("identify", identify, id_limit, FALSE);
1276 | if (n < 0) break; /* quit or no eligible items */
1277 | } while ((id_limit -= n) > 0);
1278 | if (n == 0 || n < -1)
1279 | menu_identify(id_limit);
1280 | }
1281 | update_inventory();
1282 | }
1283 |
1284 | #endif /* OVLB */
1285 | #ifdef OVL2
1286 |
1287 | STATIC_OVL char
1288 | obj_to_let(obj) /* should of course only be called for things in invent */
1289 | register struct obj *obj;
1290 | {
1291 | if (obj->oclass == GOLD_CLASS)
1292 | return GOLD_SYM;
1293 | if (!flags.invlet_constant) {
1294 | obj->invlet = NOINVSYM;
1295 | reassign();
1296 | }
1297 | return obj->invlet;
1298 | }
1299 |
1300 | /*
1301 | * Print the indicated quantity of the given object. If quan == 0L then use
1302 | * the current quantity.
1303 | */
1304 | void
1305 | prinv(prefix, obj, quan)
1306 | const char *prefix;
1307 | register struct obj *obj;
1308 | long quan;
1309 | {
1310 | long savequan = obj->quan;
1311 | if (quan) obj->quan = quan;
1312 | if (!prefix) prefix = "";
1313 | pline("%s%s%s",
1314 | prefix, *prefix ? " " : "",
1315 | xprname(obj, (char *)0, obj_to_let(obj), TRUE, 0L));
1316 | if (quan) obj->quan = savequan;
1317 | }
1318 |
1319 | #endif /* OVL2 */
1320 | #ifdef OVL1
1321 |
1322 | char *
1323 | xprname(obj, txt, let, dot, cost)
1324 | struct obj *obj;
1325 | const char *txt; /* text to print instead of obj */
1326 | char let; /* inventory letter */
1327 | boolean dot; /* append period; (dot && cost => Iu) */
1328 | long cost; /* cost (for inventory of unpaid or expended items) */
1329 | {
1330 | #ifdef LINT /* handle static char li[BUFSZ]; */
1331 | char li[BUFSZ];
1332 | #else
1333 | static char li[BUFSZ];
1334 | #endif
1335 | boolean use_invlet = flags.invlet_constant && let != CONTAINED_SYM;
1336 | /*
1337 | * If let is:
1338 | * * Then obj == null and we are printing a total amount.
1339 | * > Then the object is contained and doesn't have an inventory letter.
1340 | */
1341 | if (cost != 0 || let == '*') {
1342 | /* if dot is true, we're doing Iu, otherwise Ix */
1343 | Sprintf(li, "%c - %-45s %6ld zorkmid%s",
1344 | (dot && use_invlet ? obj->invlet : let),
1345 | (txt ? txt : doname(obj)), cost, plur(cost));
1346 | } else if (obj->oclass == GOLD_CLASS) {
1347 | Sprintf(li, "%ld gold piece%s%s", obj->quan, plur(obj->quan),
1348 | (dot ? "." : ""));
1349 | } else {
1350 | /* ordinary inventory display or pickup message */
1351 | Sprintf(li, "%c - %s%s",
1352 | (use_invlet ? obj->invlet : let),
1353 | (txt ? txt : doname(obj)), (dot ? "." : ""));
1354 | }
1355 | return li;
1356 | }
1357 |
1358 | #endif /* OVL1 */
1359 | #ifdef OVLB
1360 |
1361 | /* the 'i' command */
1362 | int
1363 | ddoinv()
1364 | {
1365 | (void) display_inventory((char *)0, FALSE);
1366 | return 0;
1367 | }
1368 |
1369 | /*
1370 | * find_unpaid()
1371 | *
1372 | * Scan the given list of objects. If last_found is NULL, return the first
1373 | * unpaid object found. If last_found is not NULL, then skip over unpaid
1374 | * objects until last_found is reached, then set last_found to NULL so the
1375 | * next unpaid object is returned. This routine recursively follows
1376 | * containers.
1377 | */
1378 | STATIC_OVL struct obj *
1379 | find_unpaid(list, last_found)
1380 | struct obj *list, **last_found;
1381 | {
1382 | struct obj *obj;
1383 |
1384 | while (list) {
1385 | if (list->unpaid) {
1386 | if (*last_found) {
1387 | /* still looking for previous unpaid object */
1388 | if (list == *last_found)
1389 | *last_found = (struct obj *) 0;
1390 | } else
1391 | return (*last_found = list);
1392 | }
1393 | if (Has_contents(list)) {
1394 | if ((obj = find_unpaid(list->cobj, last_found)) != 0)
1395 | return obj;
1396 | }
1397 | list = list->nobj;
1398 | }
1399 | return (struct obj *) 0;
1400 | }
1401 |
1402 | /*
1403 | * If lets == NULL or "", list all objects in the inventory. Otherwise,
1404 | * list all objects with object classes that match the order in lets.
1405 | *
1406 | * Returns the letter identifier of a selected item, or 0 if nothing
1407 | * was selected.
1408 | */
1409 | char
1410 | display_inventory(lets, want_reply)
1411 | register const char *lets;
1412 | boolean want_reply;
1413 | {
1414 | struct obj *otmp;
1415 | char ilet, ret;
1416 | char *invlet = flags.inv_order;
1417 | int n, classcount;
1418 | winid win; /* windows being used */
1419 | static winid local_win = WIN_ERR; /* window for partial menus */
1420 | anything any;
1421 | menu_item *selected;
1422 |
1423 | /* overriden by global flag */
1424 | if (flags.perm_invent) {
1425 | win = (lets && *lets) ? local_win : WIN_INVEN;
1426 | /* create the first time used */
1427 | if (win == WIN_ERR)
1428 | win = local_win = create_nhwindow(NHW_MENU);
1429 | } else
1430 | win = WIN_INVEN;
1431 |
1432 | /*
1433 | Exit early if no inventory -- but keep going if we are doing
1434 | a permanent inventory update. We need to keep going so the
1435 | permanent inventory window updates itself to remove the last
1436 | item(s) dropped. One down side: the addition of the exception
1437 | for permanent inventory window updates _can_ pop the window
1438 | up when it's not displayed -- even if it's empty -- because we
1439 | don't know at this level if its up or not. This may not be
1440 | an issue if empty checks are done before hand and the call
1441 | to here is short circuited away.
1442 | */
1443 | if (!invent && !(flags.perm_invent && !lets && !want_reply)) {
1444 | pline("Not carrying anything%s.", u.ugold ? " except gold" : "");
1445 | return 0;
1446 | }
1447 |
1448 | /* oxymoron? temporarily assign permanent inventory letters */
1449 | if (!flags.invlet_constant) reassign();
1450 |
1451 | if (lets && strlen(lets) == 1) {
1452 | /* when only one item of interest, use pline instead of menus;
1453 | we actually use a fake message-line menu in order to allow
1454 | the user to perform selection at the --More-- prompt for tty */
1455 | ret = '\0';
1456 | for (otmp = invent; otmp; otmp = otmp->nobj) {
1457 | if (otmp->invlet == lets[0]) {
1458 | ret = message_menu(lets[0],
1459 | want_reply ? PICK_ONE : PICK_NONE,
1460 | xprname(otmp, (char *)0, lets[0], TRUE, 0L));
1461 | break;
1462 | }
1463 | }
1464 | return ret;
1465 | }
1466 |
1467 | start_menu(win);
1468 | nextclass:
1469 | classcount = 0;
1470 | any.a_void = 0; /* set all bits to zero */
1471 | for(otmp = invent; otmp; otmp = otmp->nobj) {
1472 | ilet = otmp->invlet;
1473 | if(!lets || !*lets || index(lets, ilet)) {
1474 | if (!flags.sortpack || otmp->oclass == *invlet) {
1475 | if (flags.sortpack && !classcount) {
1476 | any.a_void = 0; /* zero */
1477 | add_menu(win, NO_GLYPH, &any, 0, 0, ATR_INVERSE,
1478 | let_to_name(*invlet, FALSE), MENU_UNSELECTED);
1479 | classcount++;
1480 | }
1481 | any.a_char = ilet;
1482 | add_menu(win, obj_to_glyph(otmp),
1483 | &any, ilet, 0, ATR_NONE, doname(otmp),
1484 | MENU_UNSELECTED);
1485 | }
1486 | }
1487 | }
1488 | if (flags.sortpack) {
1489 | if (*++invlet) goto nextclass;
1490 | #ifdef WIZARD
1491 | if (--invlet != venom_inv) {
1492 | invlet = venom_inv;
1493 | goto nextclass;
1494 | }
1495 | #endif
1496 | }
1497 | end_menu(win, (char *) 0);
1498 |
1499 | n = select_menu(win, want_reply ? PICK_ONE : PICK_NONE, &selected);
1500 | if (n > 0) {
1501 | ret = selected[0].item.a_char;
1502 | free((genericptr_t)selected);
1503 | } else
1504 | ret = !n ? '\0' : '\033'; /* cancelled */
1505 |
1506 | return ret;
1507 | }
1508 |
1509 | /*
1510 | * Returns the number of unpaid items within the given list. This includes
1511 | * contained objects.
1512 | */
1513 | int
1514 | count_unpaid(list)
1515 | struct obj *list;
1516 | {
1517 | int count = 0;
1518 |
1519 | while (list) {
1520 | if (list->unpaid) count++;
1521 | if (Has_contents(list))
1522 | count += count_unpaid(list->cobj);
1523 | list = list->nobj;
1524 | }
1525 | return count;
1526 | }
1527 |
1528 | STATIC_OVL void
1529 | dounpaid()
1530 | {
1531 | winid win;
1532 | struct obj *otmp, *marker;
1533 | register char ilet;
1534 | char *invlet = flags.inv_order;
1535 | int classcount, count, num_so_far;
1536 | int save_unpaid = 0; /* lint init */
1537 | long cost, totcost;
1538 |
1539 | count = count_unpaid(invent);
1540 |
1541 | if (count == 1) {
1542 | marker = (struct obj *) 0;
1543 | otmp = find_unpaid(invent, &marker);
1544 |
1545 | /* see if the unpaid item is in the top level inventory */
1546 | for (marker = invent; marker; marker = marker->nobj)
1547 | if (marker == otmp) break;
1548 |
1549 | pline("%s", xprname(otmp, distant_name(otmp, doname),
1550 | marker ? otmp->invlet : CONTAINED_SYM,
1551 | TRUE, unpaid_cost(otmp)));
1552 | return;
1553 | }
1554 |
1555 | win = create_nhwindow(NHW_MENU);
1556 | cost = totcost = 0;
1557 | num_so_far = 0; /* count of # printed so far */
1558 | if (!flags.invlet_constant) reassign();
1559 |
1560 | do {
1561 | classcount = 0;
1562 | for (otmp = invent; otmp; otmp = otmp->nobj) {
1563 | ilet = otmp->invlet;
1564 | if (otmp->unpaid) {
1565 | if (!flags.sortpack || otmp->oclass == *invlet) {
1566 | if (flags.sortpack && !classcount) {
1567 | putstr(win, 0, let_to_name(*invlet, TRUE));
1568 | classcount++;
1569 | }
1570 |
1571 | totcost += cost = unpaid_cost(otmp);
1572 | /* suppress "(unpaid)" suffix */
1573 | save_unpaid = otmp->unpaid;
1574 | otmp->unpaid = 0;
1575 | putstr(win, 0, xprname(otmp, distant_name(otmp, doname),
1576 | ilet, TRUE, cost));
1577 | otmp->unpaid = save_unpaid;
1578 | num_so_far++;
1579 | }
1580 | }
1581 | }
1582 | } while (flags.sortpack && (*++invlet));
1583 |
1584 | if (count > num_so_far) {
1585 | /* something unpaid is contained */
1586 | if (flags.sortpack)
1587 | putstr(win, 0, let_to_name(CONTAINED_SYM, TRUE));
1588 | /*
1589 | * Search through the container objects in the inventory for
1590 | * unpaid items. The top level inventory items have already
1591 | * been listed.
1592 | */
1593 | for (otmp = invent; otmp; otmp = otmp->nobj) {
1594 | if (Has_contents(otmp)) {
1595 | marker = (struct obj *) 0; /* haven't found any */
1596 | while (find_unpaid(otmp->cobj, &marker)) {
1597 | totcost += cost = unpaid_cost(marker);
1598 | save_unpaid = marker->unpaid;
1599 | marker->unpaid = 0; /* suppress "(unpaid)" suffix */
1600 | putstr(win, 0,
1601 | xprname(marker, distant_name(marker, doname),
1602 | CONTAINED_SYM, TRUE, cost));
1603 | marker->unpaid = save_unpaid;
1604 | }
1605 | }
1606 | }
1607 | }
1608 |
1609 | putstr(win, 0, "");
1610 | putstr(win, 0, xprname((struct obj *)0, "Total:", '*', FALSE, totcost));
1611 | display_nhwindow(win, FALSE);
1612 | destroy_nhwindow(win);
1613 | }
1614 |
1615 |
1616 | /* query objlist callback: return TRUE if obj type matches "this_type" */
1617 | static int this_type;
1618 |
1619 | STATIC_OVL boolean
1620 | this_type_only(obj)
1621 | struct obj *obj;
1622 | {
1623 | return (obj->oclass == this_type);
1624 | }
1625 |
1626 | /* the 'I' command */
1627 | int
1628 | dotypeinv()
1629 | {
1630 | char c = '\0';
1631 | int n, i = 0;
1632 | char *extra_types, types[BUFSZ];
1633 | int class_count, oclass, unpaid_count;
1634 | boolean billx = *u.ushops && doinvbill(0);
1635 | menu_item *pick_list;
1636 | boolean traditional = TRUE;
1637 | const char *prompt = "What type of object do you want an inventory of?";
1638 |
1639 | if (!invent && !u.ugold && !billx) {
1640 | You("aren't carrying anything.");
1641 | return 0;
1642 | }
1643 | unpaid_count = count_unpaid(invent);
1644 | if (flags.menu_style != MENU_TRADITIONAL) {
1645 | if (flags.menu_style == MENU_FULL ||
1646 | flags.menu_style == MENU_PARTIAL) {
1647 | traditional = FALSE;
1648 | i = UNPAID_TYPES;
1649 | if (billx) i |= BILLED_TYPES;
1650 | n = query_category(prompt, invent, i, &pick_list, PICK_ONE);
1651 | if (!n) return 0;
1652 | this_type = c = pick_list[0].item.a_int;
1653 | free((genericptr_t) pick_list);
1654 | }
1655 | }
1656 | if (traditional) {
1657 | /* collect a list of classes of objects carried, for use as a prompt */
1658 | types[0] = 0;
1659 | class_count = collect_obj_classes(types, invent,
1660 | FALSE, (u.ugold != 0),
1661 | (boolean FDECL((*),(OBJ_P))) 0);
1662 | if (unpaid_count) {
1663 | Strcat(types, "u");
1664 | class_count++;
1665 | }
1666 | if (billx) {
1667 | Strcat(types, "x");
1668 | class_count++;
1669 | }
1670 | /* add everything not already included; user won't see these */
1671 | extra_types = eos(types);
1672 | *extra_types++ = '\033';
1673 | if (!unpaid_count) *extra_types++ = 'u';
1674 | if (!billx) *extra_types++ = 'x';
1675 | *extra_types = '\0'; /* for index() */
1676 | for (i = 0; i < MAXOCLASSES; i++)
1677 | if (!index(types, def_oc_syms[i])) {
1678 | *extra_types++ = def_oc_syms[i];
1679 | *extra_types = '\0';
1680 | }
1681 |
1682 | if(class_count > 1) {
1683 | c = yn_function(prompt, types, '\0');
1684 | #ifdef REDO
1685 | savech(c);
1686 | #endif
1687 | if(c == '\0') {
1688 | clear_nhwindow(WIN_MESSAGE);
1689 | return 0;
1690 | }
1691 | } else {
1692 | /* only one thing to itemize */
1693 | if (unpaid_count)
1694 | c = 'u';
1695 | else if (billx)
1696 | c = 'x';
1697 | else
1698 | c = types[0];
1699 | }
1700 | }
1701 | if (c == 'x') {
1702 | if (billx)
1703 | (void) doinvbill(1);
1704 | else
1705 | pline("No used-up objects on your shopping bill.");
1706 | return 0;
1707 | }
1708 | if (c == 'u') {
1709 | if (unpaid_count)
1710 | dounpaid();
1711 | else
1712 | You("are not carrying any unpaid objects.");
1713 | return 0;
1714 | }
1715 | if (traditional) {
1716 | oclass = def_char_to_objclass(c); /* change to object class */
1717 | if (oclass == GOLD_CLASS) {
1718 | return doprgold();
1719 | } else if (index(types, c) > index(types, '\033')) {
1720 | You("have no such objects.");
1721 | return 0;
1722 | }
1723 | this_type = oclass;
1724 | }
1725 | if (query_objlist((char *) 0, invent,
1726 | (flags.invlet_constant ? USE_INVLET : 0)|INVORDER_SORT,
1727 | &pick_list, PICK_NONE, this_type_only) > 0)
1728 | free((genericptr_t)pick_list);
1729 | return 0;
1730 | }
1731 |
1732 | /* return a string describing the dungeon feature at <x,y> if there
1733 | is one worth mentioning at that location; otherwise null */
1734 | const char *
1735 | dfeature_at(x, y, buf)
1736 | int x, y;
1737 | char *buf;
1738 | {
1739 | struct rm *lev = &levl[x][y];
1740 | int ltyp = lev->typ, cmap = -1;
1741 | const char *dfeature = 0;
1742 | static char altbuf[BUFSZ];
1743 |
1744 | if (IS_DOOR(ltyp)) {
1745 | switch (lev->doormask) {
1746 | case D_NODOOR: cmap = S_ndoor; break; /* "doorway" */
1747 | case D_ISOPEN: cmap = S_vodoor; break; /* "open door" */
1748 | case D_BROKEN: dfeature = "broken door"; break;
1749 | default: cmap = S_vcdoor; break; /* "closed door" */
1750 | }
1751 | /* override door description for open drawbridge */
1752 | if (is_drawbridge_wall(x, y) >= 0)
1753 | dfeature = "open drawbridge portcullis", cmap = -1;
1754 | } else if (IS_FOUNTAIN(ltyp))
1755 | cmap = S_fountain; /* "fountain" */
1756 | else if (IS_THRONE(ltyp))
1757 | cmap = S_throne; /* "opulent throne" */
1758 | else if (is_lava(x,y))
1759 | cmap = S_lava; /* "molten lava" */
1760 | else if (is_ice(x,y))
1761 | cmap = S_ice; /* "ice" */
1762 | else if (is_pool(x,y))
1763 | dfeature = "pool of water";
1764 | #ifdef SINKS
1765 | else if (IS_SINK(ltyp))
1766 | cmap = S_sink; /* "sink" */
1767 | #endif
1768 | else if (IS_ALTAR(ltyp)) {
1769 | Sprintf(altbuf, "altar to %s (%s)", a_gname(),
1770 | align_str(Amask2align(lev->altarmask & ~AM_SHRINE)));
1771 | dfeature = altbuf;
1772 | } else if ((x == xupstair && y == yupstair) ||
1773 | (x == sstairs.sx && y == sstairs.sy && sstairs.up))
1774 | cmap = S_upstair; /* "staircase up" */
1775 | else if ((x == xdnstair && y == ydnstair) ||
1776 | (x == sstairs.sx && y == sstairs.sy && !sstairs.up))
1777 | cmap = S_dnstair; /* "staircase down" */
1778 | else if (x == xupladder && y == yupladder)
1779 | cmap = S_upladder; /* "ladder up" */
1780 | else if (x == xdnladder && y == ydnladder)
1781 | cmap = S_dnladder; /* "ladder down" */
1782 | else if (ltyp == DRAWBRIDGE_DOWN)
1783 | cmap = S_vodbridge; /* "lowered drawbridge" */
1784 | else if (ltyp == DBWALL)
1785 | cmap = S_vcdbridge; /* "raised drawbridge" */
1786 | else if (IS_GRAVE(ltyp))
1787 | cmap = S_grave; /* "grave" */
1788 | else if (ltyp == TREE)
1789 | cmap = S_tree; /* "tree" */
1790 | else if (ltyp == IRONBARS)
1791 | dfeature = "set of iron bars";
1792 |
1793 | if (cmap >= 0) dfeature = defsyms[cmap].explanation;
1794 | if (dfeature) Strcpy(buf, dfeature);
1795 | return dfeature;
1796 | }
1797 |
1798 | /* look at what is here; if there are many objects (5 or more),
1799 | don't show them unless obj_cnt is 0 */
1800 | int
1801 | look_here(obj_cnt, picked_some)
1802 | int obj_cnt; /* obj_cnt > 0 implies that autopickup is in progess */
1803 | boolean picked_some;
1804 | {
1805 | struct obj *otmp;
1806 | struct trap *trap;
1807 | const char *verb = Blind ? "feel" : "see";
1808 | const char *dfeature = (char *)0;
1809 | char fbuf[BUFSZ], fbuf2[BUFSZ];
1810 | winid tmpwin;
1811 | boolean skip_objects = (obj_cnt >= 5);
1812 |
1813 | if (u.uswallow) {
1814 | You("%s no objects here.", verb);
1815 | return(!!Blind);
1816 | }
1817 | if (!skip_objects && (trap = t_at(u.ux,u.uy)) && trap->tseen)
1818 | There("is %s here.",
1819 | an(defsyms[trap_to_defsym(trap->ttyp)].explanation));
1820 |
1821 | otmp = level.objects[u.ux][u.uy];
1822 | dfeature = dfeature_at(u.ux, u.uy, fbuf2);
1823 | if (dfeature && !strcmp(dfeature, "pool of water") && Underwater)
1824 | dfeature = 0;
1825 |
1826 | if (Blind) {
1827 | boolean drift = Is_airlevel(&u.uz) || Is_waterlevel(&u.uz);
1828 | You("try to feel what is %s%s.",
1829 | drift ? "floating here" : "lying here on the ",
1830 | drift ? "" : surface(u.ux, u.uy));
1831 | if (dfeature && !drift && !strcmp(dfeature, surface(u.ux,u.uy)))
1832 | dfeature = 0; /* ice already identifed */
1833 | if (!can_reach_floor()) {
1834 | pline("But you can't reach it!");
1835 | return(0);
1836 | }
1837 | }
1838 |
1839 | if (dfeature)
1840 | Sprintf(fbuf, "There is %s here.", an(dfeature));
1841 |
1842 | if (!otmp || is_lava(u.ux,u.uy) || (is_pool(u.ux,u.uy) && !Underwater)) {
1843 | if (dfeature) pline(fbuf);
1844 | read_engr_at(u.ux, u.uy); /* Eric Backus */
1845 | if (!skip_objects && (Blind || !dfeature))
1846 | You("%s no objects here.", verb);
1847 | return(!!Blind);
1848 | }
1849 | /* we know there is something here */
1850 |
1851 | if (skip_objects) {
1852 | if (dfeature) pline(fbuf);
1853 | read_engr_at(u.ux, u.uy); /* Eric Backus */
1854 | There("are %s%s objects here.",
1855 | (obj_cnt <= 10) ? "several" : "many",
1856 | picked_some ? " more" : "");
1857 | } else if (!otmp->nexthere) {
1858 | /* only one object */
1859 | if (dfeature) pline(fbuf);
1860 | read_engr_at(u.ux, u.uy); /* Eric Backus */
1861 | #ifdef INVISIBLE_OBJECTS
1862 | if (otmp->oinvis && !See_invisible) verb = "feel";
1863 | #endif
1864 | You("%s here %s.", verb, doname(otmp));
1865 | if (otmp->otyp == CORPSE) feel_cockatrice(otmp, FALSE);
1866 | } else {
1867 | display_nhwindow(WIN_MESSAGE, FALSE);
1868 | tmpwin = create_nhwindow(NHW_MENU);
1869 | if(dfeature) {
1870 | putstr(tmpwin, 0, fbuf);
1871 | putstr(tmpwin, 0, "");
1872 | }
1873 | putstr(tmpwin, 0, "Things that are here:");
1874 | for ( ; otmp; otmp = otmp->nexthere) {
1875 | putstr(tmpwin, 0, doname(otmp));
1876 | if (otmp->otyp == CORPSE) feel_cockatrice(otmp, FALSE);
1877 | }
1878 | display_nhwindow(tmpwin, TRUE);
1879 | destroy_nhwindow(tmpwin);
1880 | read_engr_at(u.ux, u.uy); /* Eric Backus */
1881 | }
1882 | return(!!Blind);
1883 | }
1884 |
1885 | /* explicilty look at what is here, including all objects */
1886 | int
1887 | dolook()
1888 | {
1889 | return look_here(0, FALSE);
1890 | }
1891 |
1892 | void
1893 | feel_cockatrice(otmp, force_touch)
1894 | struct obj *otmp;
1895 | boolean force_touch;
1896 | {
1897 | char kbuf[BUFSZ];
1898 |
1899 | if ((Blind || force_touch) && !uarmg && !Stone_resistance &&
1900 | (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm]))) {
1901 | if(poly_when_stoned(youmonst.data))
1902 | You("touched the %s corpse with your bare %s.",
1903 | mons[otmp->corpsenm].mname, makeplural(body_part(HAND)));
1904 | else
1905 | pline("Touching the %s corpse is a fatal mistake...",
1906 | mons[otmp->corpsenm].mname);
1907 | Sprintf(kbuf, "%s corpse", an(mons[otmp->corpsenm].mname));
1908 | instapetrify(kbuf);
1909 | }
1910 | }
1911 |
1912 | #endif /* OVLB */
1913 | #ifdef OVL1
1914 |
1915 | void
1916 | stackobj(obj)
1917 | struct obj *obj;
1918 | {
1919 | struct obj *otmp;
1920 |
1921 | for(otmp = level.objects[obj->ox][obj->oy]; otmp; otmp = otmp->nexthere)
1922 | if(otmp != obj && merged(&obj,&otmp))
1923 | break;
1924 | return;
1925 | }
1926 |
1927 | STATIC_OVL boolean
1928 | mergable(otmp, obj) /* returns TRUE if obj & otmp can be merged */
1929 | register struct obj *otmp, *obj;
1930 | {
1931 | if (obj->otyp != otmp->otyp || obj->unpaid != otmp->unpaid ||
1932 | obj->spe != otmp->spe || obj->dknown != otmp->dknown ||
1933 | (obj->bknown != otmp->bknown && !Role_if(PM_PRIEST)) ||
1934 | obj->cursed != otmp->cursed || obj->blessed != otmp->blessed ||
1935 | obj->no_charge != otmp->no_charge ||
1936 | obj->obroken != otmp->obroken ||
1937 | obj->otrapped != otmp->otrapped ||
1938 | obj->lamplit != otmp->lamplit ||
1939 | #ifdef INVISIBLE_OBJECTS
1940 | obj->oinvis != otmp->oinvis ||
1941 | #endif
1942 | obj->greased != otmp->greased ||
1943 | obj->oeroded != otmp->oeroded ||
1944 | obj->oeroded2 != otmp->oeroded2)
1945 | return(FALSE);
1946 |
1947 | if ((obj->oclass==WEAPON_CLASS || obj->oclass==ARMOR_CLASS) &&
1948 | (obj->oerodeproof!=otmp->oerodeproof || obj->rknown!=otmp->rknown))
1949 | return FALSE;
1950 |
1951 | if (obj->oclass == FOOD_CLASS && (obj->oeaten != otmp->oeaten ||
1952 | obj->orotten != otmp->orotten))
1953 | return(FALSE);
1954 |
1955 | if (obj->otyp == CORPSE || obj->otyp == EGG || obj->otyp == TIN) {
1956 | if (obj->corpsenm != otmp->corpsenm)
1957 | return FALSE;
1958 | }
1959 |
1960 | /* hatching eggs don't merge; ditto for revivable corpses */
1961 | if ((obj->otyp == EGG && (obj->timed || otmp->timed)) ||
1962 | (obj->otyp == CORPSE && otmp->corpsenm >= LOW_PM &&
1963 | mons[otmp->corpsenm].mlet == S_TROLL))
1964 | return FALSE;
1965 |
1966 | /* allow candle merging only if their ages are close */
1967 | /* see begin_burn() for a reference for the magic "25" */
1968 | if (Is_candle(obj) && obj->age/25 != otmp->age/25)
1969 | return(FALSE);
1970 |
1971 | /* burning potions of oil never merge */
1972 | if (obj->otyp == POT_OIL && obj->lamplit)
1973 | return FALSE;
1974 |
1975 | /* don't merge surcharged item with base-cost item */
1976 | if (obj->unpaid && !same_price(obj, otmp))
1977 | return FALSE;
1978 |
1979 | /* if they have names, make sure they're the same */
1980 | if ( (obj->onamelth != otmp->onamelth &&
1981 | ((obj->onamelth && otmp->onamelth) || obj->otyp == CORPSE)
1982 | ) ||
1983 | (obj->onamelth && otmp->onamelth &&
1984 | strncmp(ONAME(obj), ONAME(otmp), (int)obj->onamelth)))
1985 | return FALSE;
1986 |
1987 | /* for the moment, any additional information is incompatible */
1988 | if (obj->oxlth || otmp->oxlth) return FALSE;
1989 |
1990 | if(obj->oartifact != otmp->oartifact) return FALSE;
1991 |
1992 | if(obj->known == otmp->known ||
1993 | !objects[otmp->otyp].oc_uses_known) {
1994 | return((boolean)(objects[obj->otyp].oc_merge));
1995 | } else return(FALSE);
1996 | }
1997 |
1998 | int
1999 | doprgold()
2000 | {
2001 | /* the messages used to refer to "carrying gold", but that didn't
2002 | take containers into account */
2003 | if(!u.ugold)
2004 | Your("wallet is empty.");
2005 | else
2006 | Your("wallet contains %ld gold piece%s.", u.ugold, plur(u.ugold));
2007 | shopper_financial_report();
2008 | return 0;
2009 | }
2010 |
2011 | #endif /* OVL1 */
2012 | #ifdef OVLB
2013 |
2014 | int
2015 | doprwep()
2016 | {
2017 | if (!uwep) {
2018 | You("are empty %s.", body_part(HANDED));
2019 | } else {
2020 | prinv((char *)0, uwep, 0L);
2021 | if (u.twoweap) prinv((char *)0, uswapwep, 0L);
2022 | }
2023 | return 0;
2024 | }
2025 |
2026 | int
2027 | doprarm()
2028 | {
2029 | if(!wearing_armor())
2030 | You("are not wearing any armor.");
2031 | else {
2032 | #ifdef TOURIST
2033 | char lets[8];
2034 | #else
2035 | char lets[7];
2036 | #endif
2037 | register int ct = 0;
2038 |
2039 | #ifdef TOURIST
2040 | if(uarmu) lets[ct++] = obj_to_let(uarmu);
2041 | #endif
2042 | if(uarm) lets[ct++] = obj_to_let(uarm);
2043 | if(uarmc) lets[ct++] = obj_to_let(uarmc);
2044 | if(uarmh) lets[ct++] = obj_to_let(uarmh);
2045 | if(uarms) lets[ct++] = obj_to_let(uarms);
2046 | if(uarmg) lets[ct++] = obj_to_let(uarmg);
2047 | if(uarmf) lets[ct++] = obj_to_let(uarmf);
2048 | lets[ct] = 0;
2049 | (void) display_inventory(lets, FALSE);
2050 | }
2051 | return 0;
2052 | }
2053 |
2054 | int
2055 | doprring()
2056 | {
2057 | if(!uleft && !uright)
2058 | You("are not wearing any rings.");
2059 | else {
2060 | char lets[3];
2061 | register int ct = 0;
2062 |
2063 | if(uleft) lets[ct++] = obj_to_let(uleft);
2064 | if(uright) lets[ct++] = obj_to_let(uright);
2065 | lets[ct] = 0;
2066 | (void) display_inventory(lets, FALSE);
2067 | }
2068 | return 0;
2069 | }
2070 |
2071 | int
2072 | dopramulet()
2073 | {
2074 | if (!uamul)
2075 | You("are not wearing an amulet.");
2076 | else
2077 | prinv((char *)0, uamul, 0L);
2078 | return 0;
2079 | }
2080 |
2081 | static boolean
2082 | tool_in_use(obj)
2083 | struct obj *obj;
2084 | {
2085 | if ((obj->owornmask & (W_TOOL
2086 | #ifdef STEED
2087 | | W_SADDLE
2088 | #endif
2089 | )) != 0L) return TRUE;
2090 | if (obj->oclass != TOOL_CLASS) return FALSE;
2091 | return (boolean)(obj == uwep || obj->lamplit ||
2092 | (obj->otyp == LEASH && obj->leashmon));
2093 | }
2094 |
2095 | int
2096 | doprtool()
2097 | {
2098 | struct obj *otmp;
2099 | int ct = 0;
2100 | char lets[52+1];
2101 |
2102 | for (otmp = invent; otmp; otmp = otmp->nobj)
2103 | if (tool_in_use(otmp))
2104 | lets[ct++] = obj_to_let(otmp);
2105 | lets[ct] = '\0';
2106 | if (!ct) You("are not using any tools.");
2107 | else (void) display_inventory(lets, FALSE);
2108 | return 0;
2109 | }
2110 |
2111 | /* '*' command; combines the ')' + '[' + '=' + '"' + '(' commands;
2112 | show inventory of all currently wielded, worn, or used objects */
2113 | int
2114 | doprinuse()
2115 | {
2116 | struct obj *otmp;
2117 | int ct = 0;
2118 | char lets[52+1];
2119 |
2120 | for (otmp = invent; otmp; otmp = otmp->nobj)
2121 | if (is_worn(otmp) || tool_in_use(otmp))
2122 | lets[ct++] = obj_to_let(otmp);
2123 | lets[ct] = '\0';
2124 | if (!ct) You("are not wearing or wielding anything.");
2125 | else (void) display_inventory(lets, FALSE);
2126 | return 0;
2127 | }
2128 |
2129 | /*
2130 | * uses up an object that's on the floor, charging for it as necessary
2131 | */
2132 | void
2133 | useupf(obj, numused)
2134 | register struct obj *obj;
2135 | long numused;
2136 | {
2137 | register struct obj *otmp;
2138 |
2139 | /* burn_floor_paper() keeps an object pointer that it tries to
2140 | * useupf() multiple times, so obj must survive if plural */
2141 | if (obj->quan > numused)
2142 | otmp = splitobj(obj, obj->quan - numused);
2143 | else
2144 | otmp = obj;
2145 | if(costly_spot(otmp->ox, otmp->oy)) {
2146 | if(index(u.urooms, *in_rooms(otmp->ox, otmp->oy, 0)))
2147 | addtobill(otmp, FALSE, FALSE, FALSE);
2148 | else (void)stolen_value(otmp, otmp->ox, otmp->oy, FALSE, FALSE);
2149 | }
2150 | delobj(otmp);
2151 | }
2152 |
2153 | #endif /* OVLB */
2154 |
2155 |
2156 | #ifdef OVL1
2157 |
2158 | /*
2159 | * Conversion from a class to a string for printing.
2160 | * This must match the object class order.
2161 | */
2162 | STATIC_VAR NEARDATA const char *names[] = { 0,
2163 | "Illegal objects", "Weapons", "Armor", "Rings", "Amulets",
2164 | "Tools", "Comestibles", "Potions", "Scrolls", "Spellbooks",
2165 | "Wands", "Coins", "Gems", "Boulders/Statues", "Iron balls",
2166 | "Chains", "Venoms"
2167 | };
2168 |
2169 | static NEARDATA const char oth_symbols[] = {
2170 | CONTAINED_SYM,
2171 | '\0'
2172 | };
2173 |
2174 | static NEARDATA const char *oth_names[] = {
2175 | "Bagged/Boxed items"
2176 | };
2177 |
2178 | static NEARDATA char *invbuf = (char *)0;
2179 | static NEARDATA unsigned invbufsiz = 0;
2180 |
2181 | char *
2182 | let_to_name(let,unpaid)
2183 | char let;
2184 | boolean unpaid;
2185 | {
2186 | const char *class_name;
2187 | const char *pos;
2188 | int oclass = (let >= 1 && let < MAXOCLASSES) ? let : 0;
2189 | unsigned len;
2190 |
2191 | if (oclass)
2192 | class_name = names[oclass];
2193 | else if ((pos = index(oth_symbols, let)) != 0)
2194 | class_name = oth_names[pos - oth_symbols];
2195 | else
2196 | class_name = names[0];
2197 |
2198 | len = strlen(class_name) + (unpaid ? sizeof "unpaid_" : sizeof "");
2199 | if (len > invbufsiz) {
2200 | if (invbuf) free((genericptr_t)invbuf);
2201 | invbufsiz = len + 10; /* add slop to reduce incremental realloc */
2202 | invbuf = (char *) alloc(invbufsiz);
2203 | }
2204 | if (unpaid)
2205 | Strcat(strcpy(invbuf, "Unpaid "), class_name);
2206 | else
2207 | Strcpy(invbuf, class_name);
2208 | return invbuf;
2209 | }
2210 |
2211 | void
2212 | free_invbuf()
2213 | {
2214 | if (invbuf) free((genericptr_t)invbuf), invbuf = (char *)0;
2215 | invbufsiz = 0;
2216 | }
2217 |
2218 | #endif /* OVL1 */
2219 | #ifdef OVLB
2220 |
2221 | void
2222 | reassign()
2223 | {
2224 | register int i;
2225 | register struct obj *obj;
2226 |
2227 | for(obj = invent, i = 0; obj; obj = obj->nobj, i++)
2228 | obj->invlet = (i < 26) ? ('a'+i) : ('A'+i-26);
2229 | lastinvnr = i;
2230 | }
2231 |
2232 | #endif /* OVLB */
2233 | #ifdef OVL1
2234 |
2235 | int
2236 | doorganize() /* inventory organizer by Del Lamb */
2237 | {
2238 | struct obj *obj, *otmp;
2239 | register int ix, cur;
2240 | register char let;
2241 | char alphabet[52+1], buf[52+1];
2242 | char qbuf[QBUFSZ];
2243 | char allowall[2];
2244 | const char *adj_type;
2245 |
2246 | if (!flags.invlet_constant) reassign();
2247 | /* get a pointer to the object the user wants to organize */
2248 | allowall[0] = ALL_CLASSES; allowall[1] = '\0';
2249 | if (!(obj = getobj(allowall,"adjust"))) return(0);
2250 |
2251 | /* initialize the list with all upper and lower case letters */
2252 | for (let = 'a', ix = 0; let <= 'z';) alphabet[ix++] = let++;
2253 | for (let = 'A', ix = 26; let <= 'Z';) alphabet[ix++] = let++;
2254 | alphabet[52] = 0;
2255 |
2256 | /* blank out all the letters currently in use in the inventory */
2257 | /* except those that will be merged with the selected object */
2258 | for (otmp = invent; otmp; otmp = otmp->nobj)
2259 | if (otmp != obj && !mergable(otmp,obj)) {
2260 | if (otmp->invlet <= 'Z')
2261 | alphabet[(otmp->invlet) - 'A' + 26] = ' ';
2262 | else alphabet[(otmp->invlet) - 'a'] = ' ';
2263 | }
2264 |
2265 | /* compact the list by removing all the blanks */
2266 | for (ix = cur = 0; ix <= 52; ix++)
2267 | if (alphabet[ix] != ' ') buf[cur++] = alphabet[ix];
2268 |
2269 | /* and by dashing runs of letters */
2270 | if(cur > 5) compactify(buf);
2271 |
2272 | /* get new letter to use as inventory letter */
2273 | for (;;) {
2274 | Sprintf(qbuf, "Adjust letter to what [%s]?",buf);
2275 | let = yn_function(qbuf, (char *)0, '\0');
2276 | if(index(quitchars,let)) {
2277 | pline(Never_mind);
2278 | return(0);
2279 | }
2280 | if (let == '@' || !letter(let))
2281 | pline("Select an inventory slot letter.");
2282 | else
2283 | break;
2284 | }
2285 |
2286 | /* change the inventory and print the resulting item */
2287 | adj_type = "Moving:";
2288 |
2289 | /*
2290 | * don't use freeinv/addinv to avoid double-touching artifacts,
2291 | * dousing lamps, losing luck, cursing loadstone, etc.
2292 | */
2293 | extract_nobj(obj, &invent);
2294 |
2295 | for (otmp = invent; otmp;)
2296 | if (merged(&otmp,&obj)) {
2297 | adj_type = "Merging:";
2298 | obj = otmp;
2299 | otmp = otmp->nobj;
2300 | extract_nobj(obj, &invent);
2301 | } else {
2302 | if (otmp->invlet == let) {
2303 | adj_type = "Swapping:";
2304 | otmp->invlet = obj->invlet;
2305 | }
2306 | otmp = otmp->nobj;
2307 | }
2308 |
2309 | /* inline addinv (assuming flags.invlet_constant and !merged) */
2310 | obj->invlet = let;
2311 | obj->nobj = invent; /* insert at beginning */
2312 | obj->where = OBJ_INVENT;
2313 | invent = obj;
2314 | reorder_invent();
2315 |
2316 | prinv(adj_type, obj, 0L);
2317 | update_inventory();
2318 | return(0);
2319 | }
2320 |
2321 | /* common to display_minventory and display_cinventory */
2322 | STATIC_OVL void
2323 | invdisp_nothing(hdr, txt)
2324 | const char *hdr, *txt;
2325 | {
2326 | winid win;
2327 | anything any;
2328 | menu_item *selected;
2329 |
2330 | any.a_void = 0;
2331 | win = create_nhwindow(NHW_MENU);
2332 | start_menu(win);
2333 | add_menu(win, NO_GLYPH, &any, 0, 0, ATR_INVERSE, hdr, MENU_UNSELECTED);
2334 | add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
2335 | add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, txt, MENU_UNSELECTED);
2336 | end_menu(win, (char *)0);
2337 | if (select_menu(win, PICK_NONE, &selected) > 0)
2338 | free((genericptr_t)selected);
2339 | destroy_nhwindow(win);
2340 | return;
2341 | }
2342 |
2343 | /* query_objlist callback: return things that could possibly be worn/wielded */
2344 | STATIC_OVL boolean
2345 | worn_wield_only(obj)
2346 | struct obj *obj;
2347 | {
2348 | return (obj->oclass == WEAPON_CLASS
2349 | || obj->oclass == ARMOR_CLASS
2350 | || obj->oclass == AMULET_CLASS
2351 | || obj->oclass == RING_CLASS
2352 | || obj->oclass == TOOL_CLASS);
2353 | }
2354 |
2355 | /*
2356 | * Display a monster's inventory.
2357 | * Returns a pointer to the object from the monster's inventory selected
2358 | * or NULL if nothing was selected.
2359 | *
2360 | * By default, only worn and wielded items are displayed. The caller
2361 | * can pick one. Modifier flags are:
2362 | *
2363 | * MINV_NOLET - nothing selectable
2364 | * MINV_ALL - display all inventory
2365 | */
2366 | struct obj *
2367 | display_minventory(mon, dflags)
2368 | register struct monst *mon;
2369 | int dflags;
2370 | {
2371 | struct obj *ret, m_gold;
2372 | char tmp[QBUFSZ];
2373 | int n;
2374 | menu_item *selected = 0;
2375 | int do_all = (dflags & MINV_ALL) != 0,
2376 | do_gold = (do_all && mon->mgold);
2377 |
2378 | Sprintf(tmp,"%s %s:", s_suffix(noit_Monnam(mon)),
2379 | do_all ? "possessions" : "armament");
2380 |
2381 | if (do_all ? (mon->minvent || mon->mgold)
2382 | : (mon->misc_worn_check || MON_WEP(mon))) {
2383 | /* Fool the 'weapon in hand' routine into
2384 | * displaying 'weapon in claw', etc. properly.
2385 | */
2386 | youmonst.data = mon->data;
2387 |
2388 | if (do_gold) {
2389 | /*
2390 | * Make temporary gold object and insert at the head of
2391 | * the mon's inventory. We can get away with using a
2392 | * stack variable object because monsters don't carry
2393 | * gold in their inventory, so it won't merge.
2394 | */
2395 | m_gold = zeroobj;
2396 | m_gold.otyp = GOLD_PIECE; m_gold.oclass = GOLD_CLASS;
2397 | m_gold.quan = mon->mgold; m_gold.dknown = 1;
2398 | m_gold.where = OBJ_FREE;
2399 | /* we had better not merge and free this object... */
2400 | if (add_to_minv(mon, &m_gold))
2401 | panic("display_minventory: static object freed.");
2402 | }
2403 |
2404 | n = query_objlist(tmp, mon->minvent, INVORDER_SORT, &selected,
2405 | (dflags & MINV_NOLET) ? PICK_NONE : PICK_ONE,
2406 | do_all ? allow_all : worn_wield_only);
2407 |
2408 | if (do_gold) obj_extract_self(&m_gold);
2409 |
2410 | set_uasmon();
2411 | } else {
2412 | invdisp_nothing(tmp, "(none)");
2413 | n = 0;
2414 | }
2415 |
2416 | if (n > 0) {
2417 | ret = selected[0].item.a_obj;
2418 | free((genericptr_t)selected);
2419 | /*
2420 | * Unfortunately, we can't return a pointer to our temporary
2421 | * gold object. We'll have to work out a scheme where this
2422 | * can happen. Maybe even put gold in the inventory list...
2423 | */
2424 | if (ret == &m_gold) ret = (struct obj *) 0;
2425 | } else
2426 | ret = (struct obj *) 0;
2427 | return ret;
2428 | }
2429 |
2430 | /*
2431 | * Display the contents of a container in inventory style.
2432 | * Currently, this is only used for statues, via wand of probing.
2433 | */
2434 | struct obj *
2435 | display_cinventory(obj)
2436 | register struct obj *obj;
2437 | {
2438 | struct obj *ret;
2439 | char tmp[QBUFSZ];
2440 | int n;
2441 | menu_item *selected = 0;
2442 |
2443 | Sprintf(tmp,"Contents of %s:", doname(obj));
2444 |
2445 | if (obj->cobj) {
2446 | n = query_objlist(tmp, obj->cobj, INVORDER_SORT, &selected,
2447 | PICK_NONE, allow_all);
2448 | } else {
2449 | invdisp_nothing(tmp, "(empty)");
2450 | n = 0;
2451 | }
2452 | if (n > 0) {
2453 | ret = selected[0].item.a_obj;
2454 | free((genericptr_t)selected);
2455 | } else
2456 | ret = (struct obj *) 0;
2457 | return ret;
2458 | }
2459 |
2460 | /* query objlist callback: return TRUE if obj is at given location */
2461 | static coord only;
2462 |
2463 | STATIC_OVL boolean
2464 | only_here(obj)
2465 | struct obj *obj;
2466 | {
2467 | return (obj->ox == only.x && obj->oy == only.y);
2468 | }
2469 |
2470 | /*
2471 | * Display a list of buried items in inventory style. Return a non-zero
2472 | * value if there were items at that spot.
2473 | *
2474 | * Currently, this is only used with a wand of probing zapped downwards.
2475 | */
2476 | int
2477 | display_binventory(x, y, as_if_seen)
2478 | int x, y;
2479 | boolean as_if_seen;
2480 | {
2481 | struct obj *obj;
2482 | menu_item *selected = 0;
2483 | int n;
2484 |
2485 | /* count # of objects here */
2486 | for (n = 0, obj = level.buriedobjlist; obj; obj = obj->nobj)
2487 | if (obj->ox == x && obj->oy == y) {
2488 | if (as_if_seen) obj->dknown = 1;
2489 | n++;
2490 | }
2491 |
2492 | if (n) {
2493 | only.x = x;
2494 | only.y = y;
2495 | if (query_objlist("Things that are buried here:",
2496 | level.buriedobjlist, INVORDER_SORT,
2497 | &selected, PICK_NONE, only_here) > 0)
2498 | free((genericptr_t)selected);
2499 | only.x = only.y = 0;
2500 | }
2501 | return n;
2502 | }
2503 |
2504 | #endif /* OVL1 */
2505 |
2506 | /*invent.c*/