make rank() static again
[NetHack.git] / src / apply.c
blob6bf45b266d56514ecd165896225d0433caaae38e
1 /* NetHack 3.7 apply.c $NHDT-Date: 1737275719 2025/01/19 00:35:19 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.464 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2012. */
4 /* NetHack may be freely redistributed. See license for details. */
6 #include "hack.h"
8 staticfn int use_camera(struct obj *);
9 staticfn int use_towel(struct obj *);
10 staticfn boolean its_dead(coordxy, coordxy, int *);
11 staticfn int use_stethoscope(struct obj *);
12 staticfn void use_whistle(struct obj *);
13 staticfn void use_magic_whistle(struct obj *);
14 staticfn void magic_whistled(struct obj *);
15 staticfn int use_leash(struct obj *);
16 staticfn void use_leash_core(struct obj *, struct monst *, coord *, int);
17 staticfn boolean mleashed_next2u(struct monst *);
18 staticfn int use_mirror(struct obj *);
19 staticfn void use_bell(struct obj **);
20 staticfn void use_candelabrum(struct obj *);
21 staticfn void use_candle(struct obj **);
22 staticfn void use_lamp(struct obj *);
23 staticfn void light_cocktail(struct obj **);
24 staticfn int rub_ok(struct obj *);
25 staticfn void display_jump_positions(boolean);
26 staticfn void use_tinning_kit(struct obj *);
27 staticfn int use_figurine(struct obj **);
28 staticfn int grease_ok(struct obj *);
29 staticfn int use_grease(struct obj *);
30 staticfn void use_trap(struct obj *);
31 staticfn int touchstone_ok(struct obj *);
32 staticfn int use_stone(struct obj *);
33 staticfn int set_trap(void); /* occupation callback */
34 staticfn void display_polearm_positions(boolean);
35 staticfn void calc_pole_range(int *, int *);
36 staticfn int use_cream_pie(struct obj *);
37 staticfn int jelly_ok(struct obj *);
38 staticfn int use_royal_jelly(struct obj **);
39 staticfn int grapple_range(void);
40 staticfn boolean can_grapple_location(coordxy, coordxy);
41 staticfn void display_grapple_positions(boolean);
42 staticfn int use_grapple(struct obj *);
43 staticfn void discard_broken_wand(void);
44 staticfn void broken_wand_explode(struct obj *, int, int);
45 staticfn int do_break_wand(struct obj *);
46 staticfn int apply_ok(struct obj *);
47 staticfn int flip_through_book(struct obj *);
48 staticfn int flip_coin(struct obj *);
49 staticfn boolean figurine_location_checks(struct obj *, coord *, boolean);
50 staticfn boolean check_jump(genericptr_t, coordxy, coordxy);
51 staticfn boolean is_valid_jump_pos(coordxy, coordxy, int, boolean);
52 staticfn boolean get_valid_jump_position(coordxy, coordxy);
53 staticfn boolean get_valid_polearm_position(coordxy, coordxy);
54 staticfn boolean find_poleable_mon(coord *, int, int);
56 static const char
57 no_elbow_room[] = "don't have enough elbow-room to maneuver.";
59 void
60 do_blinding_ray(struct obj *obj)
62 struct monst *mtmp = bhit(u.dx, u.dy, COLNO, FLASHED_LIGHT,
63 (int (*) (MONST_P, OBJ_P)) 0,
64 (int (*) (OBJ_P, OBJ_P)) 0, &obj);
66 obj->ox = u.ux, obj->oy = u.uy; /* flash_hits_mon() wants this */
67 if (mtmp) {
68 (void) flash_hits_mon(mtmp, obj);
69 if (obj->otyp == EXPENSIVE_CAMERA)
70 see_monster_closeup(mtmp);
72 /* normally bhit() would do this but for FLASHED_LIGHT we want it
73 to be deferred until after flash_hits_mon() */
74 transient_light_cleanup();
77 staticfn int
78 use_camera(struct obj *obj)
80 if (Underwater) {
81 pline("Using your camera underwater would void the warranty.");
82 return ECMD_OK;
84 if (!getdir((char *) 0))
85 return ECMD_CANCEL;
87 if (obj->spe <= 0) {
88 pline1(nothing_happens);
89 return ECMD_TIME;
91 consume_obj_charge(obj, TRUE);
93 if (obj->cursed && !rn2(2)) {
94 (void) zapyourself(obj, TRUE);
95 } else if (u.uswallow) {
96 You("take a picture of %s %s.", s_suffix(mon_nam(u.ustuck)),
97 mbodypart(u.ustuck, STOMACH));
98 } else if (u.dz) {
99 You("take a picture of the %s.",
100 (u.dz > 0) ? surface(u.ux, u.uy) : ceiling(u.ux, u.uy));
101 } else if (!u.dx && !u.dy) {
102 (void) zapyourself(obj, TRUE);
103 } else {
104 do_blinding_ray(obj);
106 return ECMD_TIME;
109 staticfn int
110 use_towel(struct obj *obj)
112 boolean drying_feedback = (obj == uwep);
114 if (!freehand()) {
115 You("have no free %s!", body_part(HAND));
116 return ECMD_OK;
117 } else if (obj == ublindf) {
118 You("cannot use it while you're wearing it!");
119 return ECMD_OK;
120 } else if (obj->cursed) {
121 long old;
123 switch (rn2(3)) {
124 case 2:
125 old = (Glib & TIMEOUT);
126 make_glib((int) old + rn1(10, 3)); /* + 3..12 */
127 Your("%s %s!", makeplural(body_part(HAND)),
128 (old ? "are filthier than ever" : "get slimy"));
129 if (is_wet_towel(obj))
130 dry_a_towel(obj, -1, drying_feedback);
131 return ECMD_TIME;
132 case 1:
133 if (!ublindf) {
134 old = u.ucreamed;
135 u.ucreamed += rn1(10, 3);
136 pline("Yecch! Your %s %s gunk on it!", body_part(FACE),
137 (old ? "has more" : "now has"));
138 make_blinded(BlindedTimeout + (long) u.ucreamed - old, TRUE);
139 } else {
140 const char *what;
142 what = (ublindf->otyp == LENSES)
143 ? "lenses"
144 : (obj->otyp == ublindf->otyp) ? "other towel"
145 : "blindfold";
146 if (ublindf->cursed) {
147 You("push your %s %s.", what,
148 rn2(2) ? "cock-eyed" : "crooked");
149 } else {
150 struct obj *saved_ublindf = ublindf;
151 You("push your %s off.", what);
152 Blindf_off(ublindf);
153 dropx(saved_ublindf);
156 if (is_wet_towel(obj))
157 dry_a_towel(obj, -1, drying_feedback);
158 return ECMD_TIME;
159 case 0:
160 break;
164 if (Glib) {
165 make_glib(0);
166 You("wipe off your %s.",
167 !uarmg ? makeplural(body_part(HAND)) : gloves_simple_name(uarmg));
168 if (is_wet_towel(obj))
169 dry_a_towel(obj, -1, drying_feedback);
170 return ECMD_TIME;
171 } else if (u.ucreamed) {
172 incr_itimeout(&HBlinded, (-1 * (int) u.ucreamed));
173 u.ucreamed = 0;
174 if (!Blinded) {
175 pline("You've got the glop off.");
176 if (!gulp_blnd_check()) {
177 set_itimeout(&HBlinded, 1L);
178 make_blinded(0L, TRUE);
180 } else {
181 Your("%s feels clean now.", body_part(FACE));
183 if (is_wet_towel(obj))
184 dry_a_towel(obj, -1, drying_feedback);
185 return ECMD_TIME;
188 Your("%s and %s are already clean.", body_part(FACE),
189 makeplural(body_part(HAND)));
191 return ECMD_OK;
194 /* maybe give a stethoscope message based on floor objects */
195 staticfn boolean
196 its_dead(coordxy rx, coordxy ry, int *resp)
198 char buf[BUFSZ];
199 boolean more_corpses;
200 struct permonst *mptr;
201 struct obj *corpse = sobj_at(CORPSE, rx, ry),
202 *statue = sobj_at(STATUE, rx, ry);
204 if (!can_reach_floor(TRUE)) { /* levitation or unskilled riding */
205 corpse = 0; /* can't reach corpse on floor */
206 /* you can't reach tiny statues (even though you can fight
207 tiny monsters while levitating--consistency, what's that?) */
208 while (statue && mons[statue->corpsenm].msize == MZ_TINY)
209 statue = nxtobj(statue, STATUE, TRUE);
211 /* when both corpse and statue are present, pick the uppermost one */
212 if (corpse && statue) {
213 if (nxtobj(statue, CORPSE, TRUE) == corpse)
214 corpse = 0; /* corpse follows statue; ignore it */
215 else
216 statue = 0; /* corpse precedes statue; ignore statue */
218 more_corpses = (corpse && nxtobj(corpse, CORPSE, TRUE));
220 /* additional stethoscope messages from jyoung@apanix.apana.org.au */
221 if (!corpse && !statue) {
222 ; /* nothing to do */
224 } else if (Hallucination) {
225 if (!corpse) {
226 /* it's a statue */
227 Strcpy(buf, "You're both stoned");
228 } else if (corpse->quan == 1L && !more_corpses) {
229 int gndr = 2; /* neuter: "it" */
230 struct monst *mtmp = get_mtraits(corpse, FALSE);
232 /* (most corpses don't retain the monster's sex, so
233 we're usually forced to use generic pronoun here) */
234 if (mtmp) {
235 mtmp->data = &mons[mtmp->mnum];
236 gndr = pronoun_gender(mtmp, PRONOUN_NO_IT);
237 } else {
238 mptr = &mons[corpse->corpsenm];
239 if (is_female(mptr))
240 gndr = 1;
241 else if (is_male(mptr))
242 gndr = 0;
244 Sprintf(buf, "%s's dead", genders[gndr].he); /* "he"/"she"/"it" */
245 buf[0] = highc(buf[0]);
246 } else { /* plural */
247 Strcpy(buf, "They're dead");
249 /* variations on "He's dead, Jim." (Star Trek's Dr McCoy) */
250 You_hear("a voice say, \"%s, Jim.\"", buf);
251 *resp = ECMD_TIME;
252 return TRUE;
254 } else if (corpse) {
255 boolean here = u_at(rx, ry),
256 one = (corpse->quan == 1L && !more_corpses), reviver = FALSE;
257 int visglyph, corpseglyph;
259 visglyph = glyph_at(rx, ry);
260 corpseglyph = obj_to_glyph(corpse, rn2);
262 if (Blind && (visglyph != corpseglyph))
263 map_object(corpse, TRUE);
265 if (Role_if(PM_HEALER)) {
266 /* ok to reset `corpse' here; we're done with it */
267 do {
268 if (obj_has_timer(corpse, REVIVE_MON))
269 reviver = TRUE;
270 else
271 corpse = nxtobj(corpse, CORPSE, TRUE);
272 } while (corpse && !reviver);
274 You("determine that %s unfortunate being%s %s%s dead.",
275 one ? (here ? "this" : "that") : (here ? "these" : "those"),
276 one ? "" : "s", one ? "is" : "are", reviver ? " mostly" : "");
277 return TRUE;
279 } else { /* statue */
280 const char *what, *how;
282 mptr = &mons[statue->corpsenm];
283 if (Blind) { /* ignore statue->dknown; it'll always be set */
284 Sprintf(buf, "%s %s",
285 u_at(rx, ry) ? "This" : "That",
286 humanoid(mptr) ? "person" : "creature");
287 what = buf;
288 } else {
289 what = obj_pmname(statue);
290 if (!type_is_pname(mptr))
291 what = The(what);
293 how = "fine";
294 if (Role_if(PM_HEALER)) {
295 struct trap *ttmp = t_at(rx, ry);
297 if (ttmp && ttmp->ttyp == STATUE_TRAP)
298 how = "extraordinary";
299 else if (Has_contents(statue))
300 how = "remarkable";
303 pline("%s is in %s health for a statue.", what, how);
304 return TRUE;
306 return FALSE; /* no corpse or statue */
309 static const char hollow_str[] = "a hollow sound. This must be a secret %s!";
311 /* Strictly speaking it makes no sense for usage of a stethoscope to
312 not take any time; however, unless it did, the stethoscope would be
313 almost useless. As a compromise, one use per turn is free, another
314 uses up the turn; this makes curse status have a tangible effect. */
315 staticfn int
316 use_stethoscope(struct obj *obj)
318 struct monst *mtmp;
319 struct rm *lev;
320 int res;
321 coordxy rx, ry;
322 boolean interference = (u.uswallow && is_whirly(u.ustuck->data)
323 && !rn2(Role_if(PM_HEALER) ? 10 : 3));
325 if (nohands(gy.youmonst.data)) {
326 You("have no hands!"); /* not `body_part(HAND)' */
327 return ECMD_OK;
328 } else if (Deaf) {
329 You_cant("hear anything!");
330 return ECMD_OK;
331 } else if (!freehand()) {
332 You("have no free %s.", body_part(HAND));
333 return ECMD_OK;
335 if (!getdir((char *) 0))
336 return ECMD_CANCEL;
338 res = (gh.hero_seq == svc.context.stethoscope_seq) ? ECMD_TIME : ECMD_OK;
339 svc.context.stethoscope_seq = gh.hero_seq;
341 gb.bhitpos.x = u.ux, gb.bhitpos.y = u.uy; /* tentative, reset below */
342 gn.notonhead = u.uswallow;
343 if (u.usteed && u.dz > 0) {
344 if (interference) {
345 pline("%s interferes.", Monnam(u.ustuck));
346 mstatusline(u.ustuck);
347 } else
348 mstatusline(u.usteed);
349 return res;
350 } else if (u.uswallow && (u.dx || u.dy || u.dz)) {
351 mstatusline(u.ustuck);
352 return res;
353 } else if (u.uswallow && interference) {
354 pline("%s interferes.", Monnam(u.ustuck));
355 mstatusline(u.ustuck);
356 return res;
357 } else if (u.dz) {
358 if (Underwater) {
359 Soundeffect(se_faint_splashing, 35);
360 You_hear("faint splashing.");
361 } else if (u.dz < 0 || !can_reach_floor(TRUE)) {
362 cant_reach_floor(u.ux, u.uy, (u.dz < 0), TRUE);
363 } else if (its_dead(u.ux, u.uy, &res)) {
364 ; /* message already given */
365 } else if (Is_stronghold(&u.uz)) {
366 Soundeffect(se_crackling_of_hellfire, 35);
367 You_hear("the crackling of hellfire.");
368 } else {
369 pline_The("%s seems healthy enough.", surface(u.ux, u.uy));
371 return res;
372 } else if (obj->cursed && !rn2(2)) {
373 Soundeffect(se_heart_beat, 100);
374 You_hear("your heart beat.");
375 return res;
377 confdir(FALSE);
378 if (!u.dx && !u.dy) {
379 ustatusline();
380 return res;
382 rx = u.ux + u.dx;
383 ry = u.uy + u.dy;
384 if (!isok(rx, ry)) {
385 Soundeffect(se_typing_noise, 100);
386 You_hear("a faint typing noise.");
387 return ECMD_OK;
389 if ((mtmp = m_at(rx, ry)) != 0) {
390 const char *mnm = x_monnam(mtmp, ARTICLE_A, (const char *) 0,
391 SUPPRESS_IT | SUPPRESS_INVISIBLE, FALSE);
393 /* gb.bhitpos needed by mstatusline() iff mtmp is a long worm */
394 gb.bhitpos.x = rx, gb.bhitpos.y = ry;
395 gn.notonhead = (mtmp->mx != rx || mtmp->my != ry);
397 if (mtmp->mundetected) {
398 if (!canspotmon(mtmp))
399 There("is %s hidden there.", mnm);
400 mtmp->mundetected = 0;
401 newsym(mtmp->mx, mtmp->my);
402 } else if (mtmp->mappearance) {
403 const char *what = "thing";
404 boolean use_plural = FALSE;
405 struct obj dummyobj, *odummy;
407 switch (M_AP_TYPE(mtmp)) {
408 case M_AP_OBJECT:
409 /* FIXME?
410 * we should probably be using object_from_map() here
412 odummy = init_dummyobj(&dummyobj, mtmp->mappearance, 1L);
413 /* simple_typename() yields "fruit" for any named fruit;
414 we want the same thing '//' or ';' shows: "slime mold"
415 or "grape" or "slice of pizza" */
416 if (odummy->otyp == SLIME_MOLD && has_mcorpsenm(mtmp)) {
417 odummy->spe = MCORPSENM(mtmp);
418 what = simpleonames(odummy);
419 } else {
420 what = simple_typename(odummy->otyp);
422 use_plural = (is_boots(odummy) || is_gloves(odummy)
423 || odummy->otyp == LENSES);
424 break;
425 case M_AP_MONSTER: /* ignore Hallucination here */
426 what = pmname(&mons[mtmp->mappearance], Mgender(mtmp));
427 break;
428 case M_AP_FURNITURE:
429 what = defsyms[mtmp->mappearance].explanation;
430 break;
432 seemimic(mtmp);
433 pline("%s %s %s really %s.",
434 use_plural ? "Those" : "That", what,
435 use_plural ? "are" : "is", mnm);
436 } else if (flags.verbose && !canspotmon(mtmp)) {
437 There("is %s there.", mnm);
440 mstatusline(mtmp);
441 if (!canspotmon(mtmp))
442 map_invisible(rx, ry);
443 return res;
445 if (unmap_invisible(rx,ry))
446 pline_The("invisible monster must have moved.");
448 lev = &levl[rx][ry];
449 switch (lev->typ) {
450 case SDOOR:
451 Soundeffect(se_hollow_sound, 100);
452 You_hear(hollow_str, "door");
453 cvt_sdoor_to_door(lev); /* ->typ = DOOR */
454 recalc_block_point(rx, ry);
455 feel_newsym(rx, ry);
456 return res;
457 case SCORR:
458 You_hear(hollow_str, "passage");
459 lev->typ = CORR, lev->flags = 0;
460 unblock_point(rx, ry);
461 feel_newsym(rx, ry);
462 return res;
465 if (!its_dead(rx, ry, &res))
466 You("hear nothing special."); /* not You_hear() */
467 return res;
470 static const char whistle_str[] = "produce a %s whistling sound.",
471 alt_whistle_str[] = "produce a %s, sharp vibration.";
473 staticfn void
474 use_whistle(struct obj *obj)
476 if (!can_blow(&gy.youmonst)) {
477 You("are incapable of using the whistle.");
478 } else if (Underwater) {
479 You("blow bubbles through %s.", yname(obj));
480 } else {
481 if (Deaf)
482 You_feel("rushing air tickle your %s.", body_part(NOSE));
483 else
484 You(whistle_str, obj->cursed ? "shrill" : "high");
485 Soundeffect(se_shrill_whistle, 50);
486 wake_nearby(TRUE);
487 if (obj->cursed)
488 vault_summon_gd();
492 staticfn void
493 use_magic_whistle(struct obj *obj)
495 if (!can_blow(&gy.youmonst)) {
496 You("are incapable of using the whistle.");
497 } else if (obj->cursed && !rn2(2)) {
498 You("produce a %shigh-%s.", Underwater ? "very " : "",
499 Deaf ? "frequency vibration" : "pitched humming noise");
500 wake_nearby(TRUE);
501 } else {
502 /* it's magic! it works underwater too (at a higher pitch) */
503 You(Deaf ? alt_whistle_str : whistle_str,
504 Hallucination ? "normal"
505 : (Underwater && !Deaf) ? "strange, high-pitched"
506 : "strange");
507 Soundeffect(se_shrill_whistle, 80);
508 magic_whistled(obj);
512 /* 'obj' is assumed to be a magic whistle */
513 staticfn void
514 magic_whistled(struct obj *obj)
516 struct monst *mtmp, *nextmon;
517 char buf[BUFSZ], *mnam = 0,
518 shiftbuf[BUFSZ + sizeof "shifts location"],
519 appearbuf[BUFSZ + sizeof "appears"],
520 disappearbuf[BUFSZ + sizeof "disappears"];
521 boolean oseen, nseen,
522 already_discovered = objects[obj->otyp].oc_name_known != 0;
523 int omx, omy, shift = 0, appear = 0, disappear = 0, trapped = 0;
525 /* need to copy (up to 3) names as they're collected rather than just
526 save pointers to them, otherwise churning through every mbuf[] might
527 clobber the ones we care about */
528 shiftbuf[0] = appearbuf[0] = disappearbuf[0] = '\0';
530 for (mtmp = fmon; mtmp; mtmp = nextmon) {
531 nextmon = mtmp->nmon; /* trap might kill mon */
532 if (DEADMONSTER(mtmp))
533 continue;
534 /* only tame monsters are affected;
535 steed is already at your location, so not affected;
536 this avoids trap issues if you're on a trap location */
537 if (!mtmp->mtame || mtmp == u.usteed)
538 continue;
539 if (mtmp->mtrapped) {
540 /* no longer in previous trap (affects mintrap) */
541 mtmp->mtrapped = 0;
542 fill_pit(mtmp->mx, mtmp->my);
545 oseen = canspotmon(mtmp); /* old 'seen' status */
546 if (oseen) /* get name in case it's one we'll remember */
547 mnam = y_monnam(mtmp); /* before mnexto(); it might disappear */
548 /* mimic must be revealed before we know whether it
549 actually moves because line-of-sight may change */
550 if (M_AP_TYPE(mtmp))
551 seemimic(mtmp);
552 omx = mtmp->mx, omy = mtmp->my;
553 mnexto(mtmp, !already_discovered ? RLOC_MSG : RLOC_NONE);
555 if (mtmp->mx != omx || mtmp->my != omy) {
556 if (mtmp->mundetected) { /* reveal non-mimic hider that moved */
557 mtmp->mundetected = 0;
558 newsym(mtmp->mx, mtmp->my);
561 * FIXME:
562 * All relocated monsters should change positions essentially
563 * simultaneously but we're dealing with them sequentially.
564 * That could kill some off in the process, each time leaving
565 * their target position (which should be occupied at least
566 * momentarily) available as a potential death trap for others.
568 * Also, teleporting onto a trap introduces message sequencing
569 * issues. We try to avoid the most obvious non sequiturs by
570 * checking whether pline() got called during mintrap().
571 * iflags.last_msg will be changed from the value we set here
572 * to PLNMSG_UNKNOWN in that situation.
574 iflags.last_msg = PLNMSG_enum; /* not a specific message */
575 if (mintrap(mtmp, NO_TRAP_FLAGS) == Trap_Killed_Mon)
576 change_luck(-1);
577 if (iflags.last_msg != PLNMSG_enum) {
578 ++trapped;
579 continue;
581 /* dying while seen would have issued a message and not get here;
582 being sent to an unseen location and dying there should be
583 included in the disappeared case */
584 nseen = DEADMONSTER(mtmp) ? FALSE : canspotmon(mtmp);
586 if (nseen) {
587 mnam = y_monnam(mtmp);
588 if (oseen) {
589 if (++shift == 1)
590 Sprintf(shiftbuf, "%s shifts location", mnam);
591 } else {
592 if (++appear == 1)
593 Sprintf(appearbuf, "%s appears", mnam);
595 } else if (oseen) {
596 if (++disappear == 1)
597 Sprintf(disappearbuf, "%s disappears", mnam);
603 * If any pets changed location, (1) they might have been in view
604 * before and still in view after, (2) out of view before but in
605 * view after, (3) in view before but out of view after (perhaps
606 * on the far side of a boulder/door/wall), or (4) out of view
607 * before and still out of view after. The first two cases are
608 * the usual ones; the fourth will happen if the hero can't see.
610 * If the magic whistle hasn't been discovered yet, rloc() issued
611 * any applicable vanishing and/or appearing messages, and we make
612 * it become discovered now if any pets moved within or into view.
613 * If it has already been discovered, we told rloc() not to issue
614 * messages and will issue one cumulative message now (for any of
615 * the first three cases, not the fourth) to reduce verbosity for
616 * the first case of a single pet (avoid "vanishes and reappears")
617 * and greatly reduce verbosity for multiple pets regardless of
618 * each one's case.
620 buf[0] = '\0';
621 if (!already_discovered) {
622 /* message(s) were handled by rloc(); if only noticeable change was
623 pet(s) disappearing, the magic whistle won't become discovered */
624 if (shift + appear + trapped > 0)
625 makeknown(obj->otyp);
626 } else {
627 /* could use array of cardinal number names like wishcmdassist() but
628 extra precision above 3 or 4 seems pedantic; not used for 0 or 1 */
629 #define HowMany(n) (((n) < 2) ? "sqrt(-1)" \
630 : ((n) == 2) ? "two" \
631 : ((n) == 3) ? "three" \
632 : ((n) == 4) ? "four" \
633 : ((n) <= 7) ? "several" \
634 : "many")
635 /* magic whistle is already discovered so rloc() message(s)
636 were suppressed above; if any discernible relocation occurred,
637 construct a message now and issue it below */
638 if (shift > 0) {
639 if (shift > 1)
640 Sprintf(shiftbuf, "%s creatures shift locations",
641 HowMany(shift));
642 copynchars(buf, upstart(shiftbuf), (int) sizeof buf - 1);
644 if (appear > 0) {
645 if (appear > 1)
646 /* shift==0: N creatures appear;
647 shift==1: Foo shifts location and N other creatures appear;
648 shift >1: M creatures shift locations and N others appear */
649 Sprintf(appearbuf, "%s %s appear", HowMany(appear),
650 (shift == 0) ? "creatures"
651 : (shift == 1) ? "other creatures"
652 : "others");
653 if (shift == 0)
654 copynchars(buf, upstart(appearbuf), (int) sizeof buf - 1);
655 else
656 Snprintf(eos(buf), sizeof buf - strlen(buf), "%s %s",
657 /* to get here: appear > 0 and shift != 0,
658 so "shifters, appearers" if disappear != 0
659 with ", and disappearers" yet to be appended,
660 or "shifters and appearers" otherwise */
661 disappear ? "," : " and", appearbuf);
663 if (disappear > 0) {
664 if (disappear > 1)
665 Sprintf(disappearbuf, "%s %s disappear", HowMany(disappear),
666 (shift == 0 && appear == 0) ? "creatures"
667 : (shift < 2 && appear < 2) ? "other creatures"
668 : "others");
669 if (shift + appear == 0)
670 copynchars(buf, upstart(disappearbuf), (int) sizeof buf - 1);
671 else
672 Snprintf(eos(buf), sizeof buf - strlen(buf), "%s and %s",
673 (shift && appear) ? "," : "", disappearbuf);
676 if (*buf)
677 pline("%s.", buf);
678 return;
681 #undef HowMany
683 boolean
684 um_dist(coordxy x, coordxy y, xint16 n)
686 return (boolean) (abs(u.ux - x) > n || abs(u.uy - y) > n);
690 number_leashed(void)
692 int i = 0;
693 struct obj *obj;
695 for (obj = gi.invent; obj; obj = obj->nobj)
696 if (obj->otyp == LEASH && obj->leashmon != 0)
697 i++;
698 return i;
701 /* otmp is about to be destroyed or stolen */
702 void
703 o_unleash(struct obj *otmp)
705 struct monst *mtmp;
707 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
708 if (mtmp->m_id == (unsigned) otmp->leashmon) {
709 mtmp->mleashed = 0;
710 break;
712 otmp->leashmon = 0;
713 update_inventory();
716 /* mtmp is about to die, or become untame */
717 void
718 m_unleash(struct monst *mtmp, boolean feedback)
720 struct obj *otmp;
722 if (feedback) {
723 if (canseemon(mtmp))
724 pline_mon(mtmp, "%s pulls free of %s leash!",
725 Monnam(mtmp), mhis(mtmp));
726 else
727 Your("leash falls slack.");
729 if ((otmp = get_mleash(mtmp)) != 0) {
730 otmp->leashmon = 0;
731 update_inventory();
733 mtmp->mleashed = 0;
736 /* player is about to die (for bones) */
737 void
738 unleash_all(void)
740 struct obj *otmp;
741 struct monst *mtmp;
743 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
744 if (otmp->otyp == LEASH)
745 otmp->leashmon = 0;
746 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
747 mtmp->mleashed = 0;
750 #define MAXLEASHED 2
752 boolean
753 leashable(struct monst *mtmp)
755 return (boolean) (mtmp->mnum != PM_LONG_WORM
756 && !unsolid(mtmp->data)
757 && (!nolimbs(mtmp->data) || has_head(mtmp->data)));
760 staticfn int
761 use_leash(struct obj *obj)
763 coord cc;
764 struct monst *mtmp;
766 if (u.uswallow) {
767 /* if the leash isn't in use, assume we're trying to leash
768 the engulfer; if it is use, distinguish between removing
769 it from the engulfer versus from some other creature
770 (note: the two in-use cases can't actually occur; all
771 leashes are released when the hero gets engulfed) */
772 You_cant((!obj->leashmon
773 ? "leash %s from inside."
774 : (obj->leashmon == (int) u.ustuck->m_id)
775 ? "unleash %s from inside."
776 : "unleash anything from inside %s."),
777 noit_mon_nam(u.ustuck));
778 return ECMD_OK;
780 if (!obj->leashmon && number_leashed() >= MAXLEASHED) {
781 You("cannot leash any more pets.");
782 return ECMD_OK;
785 if (!get_adjacent_loc((char *) 0, (char *) 0, u.ux, u.uy, &cc))
786 return ECMD_OK;
788 if (u_at(cc.x, cc.y)) {
789 if (u.usteed && u.dz > 0) {
790 mtmp = u.usteed;
791 use_leash_core(obj, mtmp, &cc, 1);
792 return ECMD_TIME;
794 pline("Leash yourself? Very funny...");
795 return ECMD_OK;
799 * From here on out, return value is 1 == a move is used.
802 if (!(mtmp = m_at(cc.x, cc.y))) {
803 There("is no creature there.");
804 (void) unmap_invisible(cc.x, cc.y);
805 return ECMD_TIME;
808 use_leash_core(obj, mtmp, &cc, canspotmon(mtmp));
809 return ECMD_TIME;
812 staticfn void
813 use_leash_core(struct obj *obj, struct monst *mtmp, coord *cc, int spotmon)
815 if (!spotmon && !glyph_is_invisible(levl[cc->x][cc->y].glyph)) {
816 /* for the unleash case, we don't verify whether this unseen
817 monster is the creature attached to the current leash */
818 You("fail to %sleash something.", obj->leashmon ? "un" : "");
819 /* trying again will work provided the monster is tame
820 (and also that it doesn't change location by retry time) */
821 map_invisible(cc->x, cc->y);
822 } else if (!mtmp->mtame) {
823 pline("%s %s leashed!", Monnam(mtmp),
824 (!obj->leashmon) ? "cannot be" : "is not");
825 } else if (!obj->leashmon) {
826 /* applying a leash which isn't currently in use */
827 if (mtmp->mleashed) {
828 pline("This %s is already leashed.",
829 spotmon ? l_monnam(mtmp) : "creature");
830 } else if (unsolid(mtmp->data)) {
831 pline("The leash would just fall off.");
832 } else if (nolimbs(mtmp->data) && !has_head(mtmp->data)) {
833 pline("%s has no extremities the leash would fit.",
834 Monnam(mtmp));
835 } else if (!leashable(mtmp)) {
836 char lmonbuf[BUFSZ];
837 char *lmonnam = l_monnam(mtmp);
839 if (cc->x != mtmp->mx || cc->y != mtmp->my) {
840 Sprintf(lmonbuf, "%s tail", s_suffix(lmonnam));
841 lmonnam = lmonbuf;
843 pline("The leash won't fit onto %s%s.", spotmon ? "your " : "",
844 lmonnam);
845 } else {
846 You("slip the leash around %s%s.", spotmon ? "your " : "",
847 l_monnam(mtmp));
848 mtmp->mleashed = 1;
849 obj->leashmon = (int) mtmp->m_id;
850 mtmp->msleeping = 0;
851 update_inventory();
853 } else {
854 /* applying a leash which is currently in use */
855 if (obj->leashmon != (int) mtmp->m_id) {
856 pline("This leash is not attached to that creature.");
857 } else if (obj->cursed) {
858 pline_The("leash would not come off!");
859 set_bknown(obj, 1);
860 } else {
861 mtmp->mleashed = 0;
862 obj->leashmon = 0;
863 update_inventory();
864 You("remove the leash from %s%s.",
865 spotmon ? "your " : "", l_monnam(mtmp));
870 /* assuming mtmp->mleashed has been checked */
871 struct obj *
872 get_mleash(struct monst *mtmp)
874 struct obj *otmp;
876 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
877 if (otmp->otyp == LEASH && (unsigned) otmp->leashmon == mtmp->m_id)
878 break;
879 return otmp;
882 staticfn boolean
883 mleashed_next2u(struct monst *mtmp)
885 if (mtmp->mleashed) {
886 if (!m_next2u(mtmp))
887 mnexto(mtmp, RLOC_NOMSG);
888 if (!m_next2u(mtmp)) {
889 struct obj *otmp = get_mleash(mtmp);
891 if (!otmp) {
892 impossible("leashed-unleashed mon?");
893 return TRUE;
896 if (otmp->cursed)
897 return TRUE;
898 mtmp->mleashed = 0;
899 otmp->leashmon = 0;
900 update_inventory();
901 You_feel("%s leash go slack.",
902 (number_leashed() > 1) ? "a" : "the");
905 return FALSE;
908 #undef MAXLEASHED
910 boolean
911 next_to_u(void)
913 if (get_iter_mons(mleashed_next2u))
914 return FALSE;
916 /* no pack mules for the Amulet */
917 if (u.usteed && mon_has_amulet(u.usteed))
918 return FALSE;
919 return TRUE;
922 void
923 check_leash(coordxy x, coordxy y)
925 struct obj *otmp;
926 struct monst *mtmp;
928 for (otmp = gi.invent; otmp; otmp = otmp->nobj) {
929 if (otmp->otyp != LEASH || otmp->leashmon == 0)
930 continue;
931 mtmp = find_mid(otmp->leashmon, FM_FMON);
932 if (!mtmp) {
933 impossible("leash in use isn't attached to anything?");
934 otmp->leashmon = 0;
935 continue;
937 if (dist2(u.ux, u.uy, mtmp->mx, mtmp->my)
938 > dist2(x, y, mtmp->mx, mtmp->my)) {
939 if (!um_dist(mtmp->mx, mtmp->my, 3)) {
940 ; /* still close enough */
941 } else if (otmp->cursed && !breathless(mtmp->data)) {
942 if (um_dist(mtmp->mx, mtmp->my, 5)
943 || (mtmp->mhp -= rnd(2)) <= 0) {
944 long save_pacifism = u.uconduct.killer;
946 Your("leash chokes %s to death!", mon_nam(mtmp));
947 /* hero might not have intended to kill pet, but
948 that's the result of his actions; gain experience,
949 lose pacifism, take alignment and luck hit, make
950 corpse less likely to remain tame after revival */
951 xkilled(mtmp, XKILL_NOMSG);
952 /* life-saving doesn't ordinarily reset this */
953 if (!DEADMONSTER(mtmp))
954 u.uconduct.killer = save_pacifism;
955 } else {
956 pline_mon(mtmp, "%s is choked by the leash!",
957 Monnam(mtmp));
958 /* tameness eventually drops to 1 here (never 0) */
959 if (mtmp->mtame && rn2(mtmp->mtame))
960 mtmp->mtame--;
962 } else {
963 if (um_dist(mtmp->mx, mtmp->my, 5)) {
964 pline("%s leash snaps loose!", s_suffix(Monnam(mtmp)));
965 m_unleash(mtmp, FALSE);
966 } else {
967 You("pull on the leash.");
968 if (mtmp->data->msound != MS_SILENT)
969 switch (rn2(3)) {
970 case 0:
971 growl(mtmp);
972 break;
973 case 1:
974 yelp(mtmp);
975 break;
976 default:
977 whimper(mtmp);
978 break;
986 /* charisma is supposed to include qualities like leadership and personal
987 magnetism rather than just appearance, but it has devolved to this... */
988 const char *
989 beautiful(void)
991 const char *res;
992 int cha = ACURR(A_CHA);
994 /* don't bother complaining about the sexism; NetHack is not real life */
995 res = ((cha >= 25) ? "sublime" /* 25 is the maximum possible */
996 : (cha >= 19) ? "splendorous" /* note: not "splendiferous" */
997 : (cha >= 16) ? ((poly_gender() == 1) ? "beautiful" : "handsome")
998 : (cha >= 14) ? ((poly_gender() == 1) ? "winsome" : "amiable")
999 : (cha >= 11) ? "cute"
1000 : (cha >= 9) ? "plain"
1001 : (cha >= 6) ? "homely"
1002 : (cha >= 4) ? "ugly"
1003 : "hideous"); /* 3 is the minimum possible */
1004 return res;
1007 static const char look_str[] = "look %s.";
1009 staticfn int
1010 use_mirror(struct obj *obj)
1012 const char *mirror, *uvisage;
1013 struct monst *mtmp;
1014 unsigned how_seen;
1015 char mlet;
1016 boolean vis, invis_mirror, useeit, monable;
1018 if (!getdir((char *) 0))
1019 return ECMD_CANCEL;
1020 invis_mirror = Invis;
1021 useeit = !Blind && (!invis_mirror || See_invisible);
1022 uvisage = beautiful();
1023 mirror = simpleonames(obj); /* "mirror" or "looking glass" */
1024 if (obj->cursed && !rn2(2)) {
1025 if (!Blind)
1026 pline_The("%s fogs up and doesn't reflect!", mirror);
1027 else
1028 pline("%s", nothing_seems_to_happen);
1029 return ECMD_TIME;
1031 if (!u.dx && !u.dy && !u.dz) {
1032 if (!useeit) {
1033 You_cant("see your %s %s.", uvisage, body_part(FACE));
1034 } else {
1035 if (u.umonnum == PM_FLOATING_EYE) {
1036 if (Free_action) {
1037 You("stiffen momentarily under your gaze.");
1038 } else {
1039 if (Hallucination)
1040 pline("Yow! The %s stares back!", mirror);
1041 else
1042 pline("Yikes! You've frozen yourself!");
1043 if (!Hallucination || !rn2(4)) {
1044 nomul(-rnd(MAXULEV + 6 - u.ulevel));
1045 gm.multi_reason = "gazing into a mirror";
1047 gn.nomovemsg = 0; /* default, "you can move again" */
1049 } else if (is_vampire(gy.youmonst.data)
1050 || is_vampshifter(&gy.youmonst)) {
1051 You("don't have a reflection.");
1052 } else if (u.umonnum == PM_UMBER_HULK) {
1053 pline("Huh? That doesn't look like you!");
1054 make_confused(HConfusion + d(3, 4), FALSE);
1055 } else if (Hallucination) {
1056 You(look_str, hcolor((char *) 0));
1057 } else if (Sick) {
1058 You(look_str, "peaked");
1059 } else if (u.uhs >= WEAK) {
1060 You(look_str, "undernourished");
1061 } else if (Upolyd) {
1062 You("look like %s.", an(pmname(&mons[u.umonnum], Ugender)));
1063 } else {
1064 You("look as %s as ever.", uvisage);
1067 return ECMD_TIME;
1069 if (u.uswallow) {
1070 if (useeit)
1071 You("reflect %s %s.", s_suffix(mon_nam(u.ustuck)),
1072 mbodypart(u.ustuck, STOMACH));
1073 return ECMD_TIME;
1075 if (Underwater) {
1076 if (useeit)
1077 You("%s.",
1078 Hallucination ? "give the fish a chance to fix their makeup"
1079 : "reflect the murky water");
1080 return ECMD_TIME;
1082 if (u.dz) {
1083 if (useeit)
1084 You("reflect the %s.",
1085 (u.dz > 0) ? surface(u.ux, u.uy) : ceiling(u.ux, u.uy));
1086 return ECMD_TIME;
1088 mtmp = bhit(u.dx, u.dy, COLNO, INVIS_BEAM,
1089 (int (*) (MONST_P, OBJ_P)) 0,
1090 (int (*) (OBJ_P, OBJ_P)) 0, &obj);
1091 if (!mtmp || !haseyes(mtmp->data) || gn.notonhead)
1092 return ECMD_TIME;
1094 /* couldsee(mtmp->mx, mtmp->my) is implied by the fact that bhit()
1095 targeted it, so we can ignore possibility of X-ray vision */
1096 vis = canseemon(mtmp);
1097 /* ways to directly see monster (excludes X-ray vision, telepathy,
1098 extended detection, type-specific warning) */
1099 #define SEENMON (MONSEEN_NORMAL | MONSEEN_SEEINVIS | MONSEEN_INFRAVIS)
1100 how_seen = vis ? howmonseen(mtmp) : 0;
1101 /* whether monster is able to use its vision-based capabilities */
1102 monable = !mtmp->mcan && (!mtmp->minvis || perceives(mtmp->data));
1103 mlet = mtmp->data->mlet;
1104 if (mtmp->msleeping) {
1105 if (vis)
1106 pline("%s is too tired to look at your %s.", Monnam(mtmp),
1107 mirror);
1108 } else if (!mtmp->mcansee) {
1109 if (vis)
1110 pline("%s can't see anything right now.", Monnam(mtmp));
1111 } else if (invis_mirror && !perceives(mtmp->data)) {
1112 if (vis)
1113 pline("%s fails to notice your %s.", Monnam(mtmp), mirror);
1114 /* infravision doesn't produce an image in the mirror */
1115 } else if ((how_seen & SEENMON) == MONSEEN_INFRAVIS) {
1116 if (vis) /* (redundant) */
1117 pline("%s in the dark.",
1118 monverbself(mtmp, Monnam(mtmp), "are",
1119 "too far away to see"));
1120 /* some monsters do special things */
1121 } else if (mlet == S_VAMPIRE || mlet == S_GHOST || is_vampshifter(mtmp)) {
1122 if (vis)
1123 pline("%s doesn't have a reflection.", Monnam(mtmp));
1124 } else if (monable && mtmp->data == &mons[PM_MEDUSA]) {
1125 if (mon_reflects(mtmp, "The gaze is reflected away by %s %s!"))
1126 return ECMD_TIME;
1127 if (vis)
1128 pline("%s is turned to stone!", Monnam(mtmp));
1129 gs.stoned = TRUE;
1130 killed(mtmp);
1131 } else if (monable && mtmp->data == &mons[PM_FLOATING_EYE]) {
1132 int tmp = d((int) mtmp->m_lev, (int) mtmp->data->mattk[0].damd);
1133 if (!rn2(4))
1134 tmp = 120;
1135 if (vis)
1136 pline("%s is frozen by its reflection.", Monnam(mtmp));
1137 else
1138 You_hear("%s stop moving.", something);
1139 paralyze_monst(mtmp, (int) mtmp->mfrozen + tmp);
1140 } else if (monable && mtmp->data == &mons[PM_UMBER_HULK]) {
1141 if (vis)
1142 pline("%s confuses itself!", Monnam(mtmp));
1143 mtmp->mconf = 1;
1144 } else if (monable && (mlet == S_NYMPH
1145 || mtmp->data == &mons[PM_AMOROUS_DEMON])) {
1146 if (vis) {
1147 char buf[BUFSZ]; /* "She" or "He" */
1149 pline("%s in your %s.", /* "<mon> admires self in your mirror " */
1150 monverbself(mtmp, Monnam(mtmp), "admire", (char *) 0),
1151 mirror);
1152 pline("%s takes it!", upstart(strcpy(buf, mhe(mtmp))));
1153 } else
1154 pline("It steals your %s!", mirror);
1155 setnotworn(obj); /* in case mirror was wielded */
1156 freeinv(obj);
1157 (void) mpickobj(mtmp, obj);
1158 if (!tele_restrict(mtmp))
1159 (void) rloc(mtmp, RLOC_MSG);
1160 } else if (!is_unicorn(mtmp->data) && !humanoid(mtmp->data)
1161 && !is_demon(mtmp->data)
1162 && (!mtmp->minvis || perceives(mtmp->data)) && rn2(5)) {
1163 boolean do_react = TRUE;
1165 if (mtmp->mfrozen) {
1166 if (vis)
1167 You("discern no obvious reaction from %s.", mon_nam(mtmp));
1168 else
1169 You_feel(
1170 "a bit silly gesturing the mirror in that direction.");
1171 do_react = FALSE;
1173 if (do_react) {
1174 if (vis)
1175 pline("%s is frightened by its reflection.", Monnam(mtmp));
1176 monflee(mtmp, d(2, 4), FALSE, FALSE);
1178 } else if (!Blind) {
1179 if (mtmp->minvis && !See_invisible)
1181 else if ((mtmp->minvis && !perceives(mtmp->data))
1182 /* redundant: can't get here if these are true */
1183 || !haseyes(mtmp->data) || gn.notonhead || !mtmp->mcansee)
1184 pline("%s doesn't seem to notice %s reflection.", Monnam(mtmp),
1185 mhis(mtmp));
1186 else
1187 pline("%s ignores %s reflection.", Monnam(mtmp), mhis(mtmp));
1189 return ECMD_TIME;
1190 #undef SEENMON
1193 staticfn void
1194 use_bell(struct obj **optr)
1196 struct obj *obj = *optr;
1197 struct monst *mtmp;
1198 boolean wakem = FALSE, learno = FALSE,
1199 ordinary = (obj->otyp != BELL_OF_OPENING || !obj->spe),
1200 invoking = (obj->otyp == BELL_OF_OPENING
1201 && invocation_pos(u.ux, u.uy)
1202 && !On_stairs(u.ux, u.uy));
1204 Hero_playnotes(obj_to_instr(obj), "C", 100);
1205 You("ring %s.", the(xname(obj)));
1207 if (Underwater || (u.uswallow && ordinary)) {
1208 pline("But the sound is muffled.");
1210 } else if (invoking && ordinary) {
1211 /* needs to be recharged... */
1212 pline("But it makes no sound.");
1213 learno = TRUE; /* help player figure out why */
1215 } else if (ordinary) {
1216 if (obj->cursed && !rn2(4)
1217 /* note: once any of them are gone, we stop all of them */
1218 && !(svm.mvitals[PM_WOOD_NYMPH].mvflags & G_GONE)
1219 && !(svm.mvitals[PM_WATER_NYMPH].mvflags & G_GONE)
1220 && !(svm.mvitals[PM_MOUNTAIN_NYMPH].mvflags & G_GONE)
1221 && (mtmp = makemon(mkclass(S_NYMPH, 0), u.ux, u.uy,
1222 NO_MINVENT | MM_NOMSG)) != 0) {
1223 You("summon %s!", a_monnam(mtmp));
1224 if (!obj_resists(obj, 93, 100)) {
1225 pline("%s shattered!", Tobjnam(obj, "have"));
1226 useup(obj);
1227 *optr = 0;
1228 } else
1229 switch (rn2(3)) {
1230 default:
1231 break;
1232 case 1:
1233 mon_adjust_speed(mtmp, 2, (struct obj *) 0);
1234 break;
1235 case 2: /* no explanation; it just happens... */
1236 gn.nomovemsg = "";
1237 gm.multi_reason = NULL;
1238 nomul(-rnd(2));
1239 break;
1242 wakem = TRUE;
1244 } else {
1245 /* charged Bell of Opening */
1246 consume_obj_charge(obj, TRUE);
1248 if (u.uswallow) {
1249 if (!obj->cursed)
1250 (void) openit();
1251 else
1252 pline1(nothing_happens);
1254 } else if (obj->cursed) {
1255 coord mm;
1257 mm.x = u.ux;
1258 mm.y = u.uy;
1259 mkundead(&mm, FALSE, NO_MINVENT);
1260 wakem = TRUE;
1262 } else if (invoking) {
1263 pline("%s an unsettling shrill sound...", Tobjnam(obj, "issue"));
1264 obj->age = svm.moves;
1265 learno = TRUE;
1266 wakem = TRUE;
1268 } else if (obj->blessed) {
1269 int res = 0;
1271 if (uchain) {
1272 unpunish();
1273 res = 1;
1274 } else if (u.utrap && u.utraptype == TT_BURIEDBALL) {
1275 buried_ball_to_freedom();
1276 res = 1;
1278 res += openit();
1279 switch (res) {
1280 case 0:
1281 pline1(nothing_happens);
1282 break;
1283 case 1:
1284 pline("%s opens...", Something);
1285 learno = TRUE;
1286 break;
1287 default:
1288 pline("Things open around you...");
1289 learno = TRUE;
1290 break;
1293 } else { /* uncursed */
1294 if (findit() != 0)
1295 learno = TRUE;
1296 else
1297 pline1(nothing_happens);
1300 } /* charged BofO */
1302 if (learno) {
1303 makeknown(BELL_OF_OPENING);
1304 obj->known = 1;
1306 if (wakem)
1307 wake_nearby(TRUE);
1310 staticfn void
1311 use_candelabrum(struct obj *obj)
1313 const char *s = (obj->spe != 1) ? "candles" : "candle";
1315 if (obj->lamplit) {
1316 You("snuff the %s.", s);
1317 end_burn(obj, TRUE);
1318 return;
1320 if (obj->spe <= 0) {
1321 struct obj *otmp;
1323 pline("This %s has no %s.", xname(obj), s);
1324 /* only output tip if candles are in inventory */
1325 for (otmp = gi.invent; otmp; otmp = otmp->nobj)
1326 if (Is_candle(otmp))
1327 break;
1328 if (otmp)
1329 pline("To attach candles, apply them instead of the %s.",
1330 xname(obj));
1331 return;
1333 if (Underwater) {
1334 You("cannot make fire under water.");
1335 return;
1337 if (u.uswallow || obj->cursed) {
1338 if (!Blind)
1339 pline_The("%s %s for a moment, then %s.", s, vtense(s, "flicker"),
1340 vtense(s, "die"));
1341 return;
1343 if (obj->spe < 7) {
1344 There("%s only %d %s in %s.", vtense(s, "are"), obj->spe, s,
1345 the(xname(obj)));
1346 if (!Blind)
1347 pline("%s lit. %s dimly.", obj->spe == 1 ? "It is" : "They are",
1348 Tobjnam(obj, "shine"));
1349 } else {
1350 pline("%s's %s burn%s", The(xname(obj)), s,
1351 (Blind ? "." : " brightly!"));
1353 if (!invocation_pos(u.ux, u.uy) || On_stairs(u.ux, u.uy)) {
1354 pline_The("%s %s being rapidly consumed!", s, vtense(s, "are"));
1355 /* this used to be obj->age /= 2, rounding down; an age of
1356 1 would yield 0, confusing begin_burn() and producing an
1357 unlightable, unrefillable candelabrum; round up instead */
1358 obj->age = (obj->age + 1L) / 2L;
1360 /* to make absolutely sure the game doesn't become unwinnable as
1361 a consequence of a broken candelabrum */
1362 if (obj->age == 0) {
1363 impossible("Candelabrum with candles but no fuel?");
1364 obj->age = 1;
1366 } else {
1367 if (obj->spe == 7) {
1368 if (Blind)
1369 pline("%s a strange warmth!", Tobjnam(obj, "radiate"));
1370 else
1371 pline("%s with a strange light!", Tobjnam(obj, "glow"));
1373 obj->known = 1;
1375 begin_burn(obj, FALSE);
1378 staticfn void
1379 use_candle(struct obj **optr)
1381 struct obj *obj = *optr;
1382 struct obj *otmp;
1383 const char *s = (obj->quan != 1) ? "candles" : "candle";
1384 char qbuf[QBUFSZ], qsfx[QBUFSZ], *q;
1385 boolean was_lamplit;
1387 if (u.uswallow) {
1388 You(no_elbow_room);
1389 return;
1392 /* obj is the candle; otmp is the candelabrum */
1393 otmp = carrying(CANDELABRUM_OF_INVOCATION);
1394 if (!otmp || otmp->spe == 7) {
1395 use_lamp(obj);
1396 return;
1399 /* first, minimal candelabrum suffix for formatting candles */
1400 Sprintf(qsfx, " to\033%s?", thesimpleoname(otmp));
1401 /* next, format the candles as a prefix for the candelabrum */
1402 (void) safe_qbuf(qbuf, "Attach ", qsfx, obj, yname, thesimpleoname, s);
1403 /* strip temporary candelabrum suffix */
1404 if ((q = strstri(qbuf, " to\033")) != 0)
1405 Strcpy(q, " to ");
1406 /* last, format final "attach candles to candelabrum?" query */
1407 if (y_n(safe_qbuf(qbuf, qbuf, "?", otmp, yname, thesimpleoname, "it"))
1408 == 'n') {
1409 use_lamp(obj);
1410 return;
1411 } else {
1412 if ((long) otmp->spe + obj->quan > 7L) {
1413 obj = splitobj(obj, 7L - (long) otmp->spe);
1414 /* avoid a grammatical error if obj->quan gets
1415 reduced to 1 candle from more than one */
1416 s = (obj->quan != 1) ? "candles" : "candle";
1417 } else
1418 *optr = 0;
1420 /* The candle's age field doesn't correctly reflect the amount
1421 of fuel in it while it's lit, because the fuel is measured
1422 by the timer. So to get accurate age updating, we need to
1423 end the burn temporarily while attaching the candle. */
1424 was_lamplit = obj->lamplit;
1425 if (was_lamplit)
1426 end_burn(obj, TRUE);
1428 You("attach %ld%s %s to %s.", obj->quan, !otmp->spe ? "" : " more", s,
1429 the(xname(otmp)));
1430 if (!otmp->spe || otmp->age > obj->age)
1431 otmp->age = obj->age;
1432 otmp->spe += (int) obj->quan;
1433 if (otmp->lamplit && !was_lamplit)
1434 pline_The("new %s magically %s!", s, vtense(s, "ignite"));
1435 else if (!otmp->lamplit && was_lamplit)
1436 pline("%s out.", (obj->quan > 1L) ? "They go" : "It goes");
1437 if (obj->unpaid) {
1438 struct monst *shkp VOICEONLY
1439 = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE));
1441 SetVoice(shkp, 0, 80, 0);
1442 verbalize("You %s %s, you bought %s!",
1443 otmp->lamplit ? "burn" : "use",
1444 (obj->quan > 1L) ? "them" : "it",
1445 (obj->quan > 1L) ? "them" : "it");
1447 if (obj->quan < 7L && otmp->spe == 7)
1448 pline("%s now has seven%s candles attached.", The(xname(otmp)),
1449 otmp->lamplit ? " lit" : "");
1450 /* candelabrum's light range might increase */
1451 if (otmp->lamplit)
1452 obj_merge_light_sources(otmp, otmp);
1453 /* candles are no longer a separate light source */
1454 /* candles are now gone */
1455 useupall(obj);
1456 /* candelabrum's weight is changing */
1457 otmp->owt = weight(otmp);
1458 update_inventory();
1462 /* call in drop, throw, and put in box, etc. */
1463 boolean
1464 snuff_candle(struct obj *otmp)
1466 boolean candle = Is_candle(otmp);
1468 if ((candle || otmp->otyp == CANDELABRUM_OF_INVOCATION)
1469 && otmp->lamplit) {
1470 char buf[BUFSZ];
1471 coordxy x, y;
1472 boolean many = candle ? (otmp->quan > 1L) : (otmp->spe > 1);
1474 (void) get_obj_location(otmp, &x, &y, 0);
1475 if (otmp->where == OBJ_MINVENT ? cansee(x, y) : !Blind)
1476 pline("%s%scandle%s flame%s extinguished.", Shk_Your(buf, otmp),
1477 (candle ? "" : "candelabrum's "), (many ? "s'" : "'s"),
1478 (many ? "s are" : " is"));
1479 end_burn(otmp, TRUE);
1480 return TRUE;
1482 return FALSE;
1485 /* called when lit lamp is hit by water or put into a container or
1486 you've been swallowed by a monster; obj might be in transit while
1487 being thrown or dropped so don't assume that its location is valid */
1488 boolean
1489 snuff_lit(struct obj *obj)
1491 coordxy x, y;
1493 if (obj->lamplit) {
1494 if (obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP
1495 || obj->otyp == BRASS_LANTERN || obj->otyp == POT_OIL) {
1496 (void) get_obj_location(obj, &x, &y, 0);
1497 if (obj->where == OBJ_MINVENT ? cansee(x, y) : !Blind)
1498 pline("%s %s out!", Yname2(obj), otense(obj, "go"));
1499 end_burn(obj, TRUE);
1500 return TRUE;
1502 if (snuff_candle(obj))
1503 return TRUE;
1505 return FALSE;
1508 /* called when lit object is hit by water */
1509 boolean
1510 splash_lit(struct obj *obj)
1512 boolean result, dunk = FALSE;
1514 /* lantern won't be extinguished by a rust trap or rust monster attack
1515 but will be if submerged or placed into a container or swallowed by
1516 a monster (for mobile light source handling, not because it ought
1517 to stop being lit in all those situations...) */
1518 if (obj->lamplit && obj->otyp == BRASS_LANTERN) {
1519 struct monst *mtmp;
1520 boolean useeit = FALSE, uhearit = FALSE, snuff = TRUE;
1522 if (obj->where == OBJ_INVENT) {
1523 useeit = !Blind;
1524 uhearit = !Deaf;
1525 /* underwater light sources aren't allowed but if hero
1526 is just entering water, Underwater won't be set yet */
1527 dunk = (is_pool(u.ux, u.uy)
1528 && ((!Levitation && !Flying && !Wwalking)
1529 || Is_waterlevel(&u.uz)));
1530 snuff = FALSE;
1531 } else if (obj->where == OBJ_MINVENT
1532 /* don't assume that lit lantern has been swallowed;
1533 a nymph might have stolen it or picked it up */
1534 && ((mtmp = obj->ocarry), humanoid(mtmp->data))) {
1535 coordxy x, y;
1537 useeit = get_obj_location(obj, &x, &y, 0) && cansee(x, y);
1538 uhearit = couldsee(x, y) && distu(x, y) < 5 * 5;
1539 dunk = (is_pool(mtmp->mx, mtmp->my)
1540 && ((!is_flyer(mtmp->data) && !is_floater(mtmp->data))
1541 || Is_waterlevel(&u.uz)));
1542 snuff = FALSE;
1543 if (useeit)
1544 set_msg_xy(x, y);
1547 if (useeit || uhearit)
1548 pline("%s %s%s%s.", Yname2(obj),
1549 uhearit ? "crackles" : "",
1550 (uhearit && useeit) ? " and " : "",
1551 useeit ? "flickers" : "");
1552 if (!dunk && !snuff)
1553 return FALSE;
1556 result = snuff_lit(obj);
1558 /* this is simpler when we wait until after lantern has been snuffed */
1559 if (dunk) {
1560 /* drain some of the battery but don't short it out entirely */
1561 obj->age -= (obj->age > 200L) ? 100L : (obj->age / 2L);
1563 return result;
1566 /* Called when potentially lightable object is affected by fire_damage().
1567 Return TRUE if object becomes lit and FALSE otherwise --ALI */
1568 boolean
1569 catch_lit(struct obj *obj)
1571 coordxy x, y;
1573 if (!obj->lamplit && ignitable(obj) && get_obj_location(obj, &x, &y, 0)) {
1574 if (((obj->otyp == MAGIC_LAMP /* spe==0 => no djinni inside */
1575 /* spe==0 => no candles attached */
1576 || obj->otyp == CANDELABRUM_OF_INVOCATION) && obj->spe == 0)
1577 /* age_is_relative && age==0 && still-exists means out of fuel */
1578 || (age_is_relative(obj) && obj->age == 0)
1579 /* lantern is classified as ignitable() but not by fire */
1580 || obj->otyp == BRASS_LANTERN)
1581 return FALSE;
1582 if (obj->otyp == CANDELABRUM_OF_INVOCATION && obj->cursed)
1583 return FALSE;
1584 if ((obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP)
1585 /* once lit, cursed lamp is as good as non-cursed one, so failure
1586 to light is a minor inconvenience to make cursed be worse */
1587 && obj->cursed && !rn2(2))
1588 return FALSE;
1590 if (obj->where == OBJ_INVENT || cansee(x, y)) {
1591 if (obj->where == OBJ_FLOOR && cansee(x, y))
1592 set_msg_xy(x, y);
1593 pline("%s %s %s", Yname2(obj),
1594 /* "catches light!" or "feels warm." */
1595 otense(obj, Blind ? "feel" : "catch"),
1596 Blind ? "warm." : "light!");
1598 if (obj->otyp == POT_OIL)
1599 makeknown(obj->otyp);
1600 if (carried(obj) && obj->unpaid && costly_spot(u.ux, u.uy)) {
1601 struct monst *shkp VOICEONLY
1602 = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE));
1604 /* if it catches while you have it, then it's your tough luck */
1605 check_unpaid(obj);
1606 SetVoice(shkp, 0, 80, 0);
1607 verbalize("That's in addition to the cost of %s %s, of course.",
1608 yname(obj),
1609 (obj->quan == 1L) ? "itself" : "themselves");
1610 bill_dummy_object(obj);
1612 begin_burn(obj, FALSE);
1613 return TRUE;
1615 return FALSE;
1618 /* light a lamp or candle */
1619 staticfn void
1620 use_lamp(struct obj *obj)
1622 char buf[BUFSZ];
1623 const char *lamp = (obj->otyp == OIL_LAMP
1624 || obj->otyp == MAGIC_LAMP) ? "lamp"
1625 : (obj->otyp == BRASS_LANTERN) ? "lantern"
1626 : NULL;
1629 * When blind, lamps' and candles' on/off state can be distinguished
1630 * by heat. For brass lantern assume that there is an on/off switch
1631 * that can be felt.
1634 if (obj->lamplit) {
1635 if (lamp) /* lamp or lantern */
1636 pline("%s%s is now off.", Shk_Your(buf, obj), lamp);
1637 else
1638 You("snuff out %s.", yname(obj));
1639 end_burn(obj, TRUE);
1640 return;
1642 if (Underwater) {
1643 pline("%s.",
1644 !Is_candle(obj) ? "This is not a diving lamp"
1645 : "Sorry, fire and water don't mix");
1646 return;
1648 /* magic lamps with an spe == 0 (wished for) cannot be lit */
1649 if ((!Is_candle(obj) && obj->age == 0)
1650 || (obj->otyp == MAGIC_LAMP && obj->spe == 0)) {
1651 if (obj->otyp == BRASS_LANTERN) {
1652 if (!Blind)
1653 Your("lantern is out of power.");
1654 else
1655 pline("%s", nothing_seems_to_happen);
1656 } else {
1657 pline("This %s has no oil.", xname(obj));
1659 return;
1661 if (obj->cursed && !rn2(2)) {
1662 if ((obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP) && !rn2(3)) {
1663 pline_The("lamp spills and covers your %s with oil.",
1664 fingers_or_gloves(TRUE));
1665 make_glib((int) (Glib & TIMEOUT) + d(2, 10));
1666 } else if (!Blind) {
1667 pline("%s for a moment, then %s.", Tobjnam(obj, "flicker"),
1668 otense(obj, "die"));
1669 } else {
1670 pline("%s", nothing_seems_to_happen);
1672 } else {
1673 if (lamp) { /* lamp or lantern */
1674 check_unpaid(obj);
1675 pline("%s%s is now on.", Shk_Your(buf, obj), lamp);
1676 } else { /* candle(s) */
1677 pline("%s flame%s %s%s", s_suffix(Yname2(obj)), plur(obj->quan),
1678 otense(obj, "burn"), Blind ? "." : " brightly!");
1679 if (obj->unpaid && costly_spot(u.ux, u.uy)
1680 && obj->age == 20L * (long) objects[obj->otyp].oc_cost) {
1681 const char *ithem = (obj->quan > 1L) ? "them" : "it";
1682 struct monst *shkp VOICEONLY
1683 = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE));
1685 SetVoice(shkp, 0, 80, 0);
1686 verbalize("You burn %s, you bought %s!", ithem, ithem);
1687 bill_dummy_object(obj);
1690 begin_burn(obj, FALSE);
1694 staticfn void
1695 light_cocktail(struct obj **optr)
1697 struct obj *obj = *optr; /* obj is a potion of oil */
1698 char buf[BUFSZ];
1699 boolean split1off;
1701 if (u.uswallow) {
1702 You(no_elbow_room);
1703 return;
1706 if (obj->lamplit) {
1707 You("snuff the lit potion.");
1708 end_burn(obj, TRUE);
1710 * Free & add to re-merge potion. This will average the
1711 * age of the potions. Not exactly the best solution,
1712 * but its easy. Don't do that unless obj is not worn (uwep,
1713 * uswapwep, or uquiver) because if wielded and other oil is
1714 * quivered a "null obj after quiver merge" panic will occur.
1716 if (!obj->owornmask) {
1717 freeinv(obj);
1718 *optr = addinv(obj);
1720 return;
1721 } else if (Underwater) {
1722 There("is not enough oxygen to sustain a fire.");
1723 return;
1726 split1off = (obj->quan > 1L);
1727 if (split1off)
1728 obj = splitobj(obj, 1L);
1730 You("light %spotion.%s", shk_your(buf, obj),
1731 Blind ? "" : " It gives off a dim light.");
1733 if (obj->unpaid && costly_spot(u.ux, u.uy)) {
1734 struct monst *shkp VOICEONLY = shop_keeper(*in_rooms(u.ux, u.uy,
1735 SHOPBASE));
1737 /* Normally, we shouldn't both partially and fully charge
1738 * for an item, but (Yendorian Fuel) Taxes are inevitable...
1740 check_unpaid(obj);
1741 SetVoice(shkp, 0, 80, 0);
1742 verbalize("That's in addition to the cost of the potion, of course.");
1743 bill_dummy_object(obj);
1745 makeknown(obj->otyp);
1747 begin_burn(obj, FALSE); /* after shop billing */
1748 if (split1off) {
1749 obj_extract_self(obj); /* free from inv */
1750 obj->nomerge = 1;
1751 obj = hold_another_object(obj, "You drop %s!", doname(obj),
1752 (const char *) 0);
1753 if (obj)
1754 obj->nomerge = 0;
1756 *optr = obj;
1759 /* getobj callback for object to be rubbed - not selecting a secondary object
1760 to rub on a gray stone or rub jelly on */
1761 staticfn int
1762 rub_ok(struct obj *obj)
1764 if (!obj)
1765 return GETOBJ_EXCLUDE;
1767 if (obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP
1768 || obj->otyp == BRASS_LANTERN || is_graystone(obj)
1769 || obj->otyp == LUMP_OF_ROYAL_JELLY)
1770 return GETOBJ_SUGGEST;
1772 return GETOBJ_EXCLUDE;
1775 /* the #rub command */
1777 dorub(void)
1779 struct obj *obj;
1781 if (nohands(gy.youmonst.data)) {
1782 You("aren't able to rub anything without hands.");
1783 return ECMD_OK;
1785 obj = getobj("rub", rub_ok, GETOBJ_NOFLAGS);
1786 if (!obj)
1787 return ECMD_CANCEL;
1788 if (obj->oclass == GEM_CLASS || obj->oclass == FOOD_CLASS) {
1789 if (is_graystone(obj)) {
1790 return use_stone(obj);
1791 } else if (obj->otyp == LUMP_OF_ROYAL_JELLY) {
1792 return use_royal_jelly(&obj);
1793 } else {
1794 pline("Sorry, I don't know how to use that.");
1795 return ECMD_OK;
1798 if (obj != uwep) {
1799 if (wield_tool(obj, "rub")) {
1800 cmdq_add_ec(CQ_CANNED, dorub);
1801 cmdq_add_key(CQ_CANNED, obj->invlet);
1802 return ECMD_TIME;
1804 return ECMD_OK;
1807 /* now uwep is obj */
1808 if (uwep->otyp == MAGIC_LAMP) {
1809 if (uwep->spe > 0 && !rn2(3)) {
1810 check_unpaid_usage(uwep, TRUE); /* unusual item use */
1811 /* bones preparation: perform the lamp transformation
1812 before releasing the djinni in case the latter turns out
1813 to be fatal (a hostile djinni has no chance to attack yet,
1814 but an indebted one who grants a wish might bestow an
1815 artifact which blasts the hero with lethal results) */
1816 uwep->otyp = OIL_LAMP;
1817 uwep->spe = 0; /* for safety */
1818 uwep->age = rn1(500, 1000);
1819 if (uwep->lamplit)
1820 begin_burn(uwep, TRUE);
1821 djinni_from_bottle(uwep);
1822 makeknown(MAGIC_LAMP);
1823 update_inventory();
1824 } else if (rn2(2)) {
1825 You("%s smoke.", !Blind ? "see a puff of" : "smell");
1826 } else
1827 pline1(nothing_happens);
1828 } else if (obj->otyp == BRASS_LANTERN) {
1829 /* message from Adventure */
1830 pline("Rubbing the electric lamp is not particularly rewarding.");
1831 pline("Anyway, nothing exciting happens.");
1832 } else
1833 pline1(nothing_happens);
1834 return ECMD_TIME;
1837 /* the #jump command */
1839 dojump(void)
1841 /* Physical jump */
1842 return jump(0);
1845 enum jump_trajectory {
1846 jAny = 0, /* any direction => magical jump */
1847 jHorz = 1,
1848 jVert = 2,
1849 jDiag = 3 /* jHorz|jVert */
1852 /* callback routine for walk_path() */
1853 staticfn boolean
1854 check_jump(genericptr arg, coordxy x, coordxy y)
1856 int traj = *(int *) arg;
1857 struct rm *lev = &levl[x][y];
1859 if (Passes_walls)
1860 return TRUE;
1861 if (IS_STWALL(lev->typ))
1862 return FALSE;
1863 if (IS_DOOR(lev->typ)) {
1864 if (closed_door(x, y))
1865 return FALSE;
1866 if ((lev->doormask & D_ISOPEN) != 0 && traj != jAny
1867 /* reject diagonal jump into or out-of or through open door */
1868 && (traj == jDiag
1869 /* reject horizontal jump through horizontal open door
1870 and non-horizontal (ie, vertical) jump through
1871 non-horizontal (vertical) open door */
1872 || ((traj & jHorz) != 0) == (lev->horizontal != 0)))
1873 return FALSE;
1874 /* empty doorways aren't restricted */
1876 /* let giants jump over boulders (what about Flying?
1877 and is there really enough head room for giants to jump
1878 at all, let alone over something tall?) */
1879 if (sobj_at(BOULDER, x, y) && !throws_rocks(gy.youmonst.data))
1880 return FALSE;
1881 return TRUE;
1884 staticfn boolean
1885 is_valid_jump_pos(coordxy x, coordxy y, int magic, boolean showmsg)
1887 if (!magic && !(HJumping & ~INTRINSIC) && !EJumping && distu(x, y) != 5) {
1888 /* The Knight jumping restriction still applies when riding a
1889 * horse. After all, what shape is the knight piece in chess?
1891 if (showmsg)
1892 pline("Illegal move!");
1893 return FALSE;
1894 } else if (distu(x, y) > (magic ? 6 + magic * 3 : 9)) {
1895 if (showmsg)
1896 pline("Too far!");
1897 return FALSE;
1898 } else if (!isok(x, y)) {
1899 if (showmsg)
1900 You("cannot jump there!");
1901 return FALSE;
1902 } else if (!cansee(x, y)) {
1903 if (showmsg)
1904 You("cannot see where to land!");
1905 return FALSE;
1906 } else {
1907 coord uc, tc;
1908 struct rm *lev = &levl[u.ux][u.uy];
1909 /* we want to categorize trajectory for use in determining
1910 passage through doorways: horizontal, vertical, or diagonal;
1911 since knight's jump and other irregular directions are
1912 possible, we flatten those out to simplify door checks */
1913 int diag, traj;
1914 coordxy dx = x - u.ux, dy = y - u.uy,
1915 ax = abs(dx), ay = abs(dy);
1917 /* diag: any non-orthogonal destination classified as diagonal */
1918 diag = (magic || Passes_walls || (!dx && !dy)) ? jAny
1919 : !dy ? jHorz : !dx ? jVert : jDiag;
1920 /* traj: flatten out the trajectory => some diagonals re-classified */
1921 if (ax >= 2 * ay)
1922 ay = 0;
1923 else if (ay >= 2 * ax)
1924 ax = 0;
1925 traj = (magic || Passes_walls || (!ax && !ay)) ? jAny
1926 : !ay ? jHorz : !ax ? jVert : jDiag;
1927 /* walk_path doesn't process the starting spot;
1928 this is iffy: if you're starting on a closed door spot,
1929 you _can_ jump diagonally from doorway (without needing
1930 Passes_walls); that's intentional but is it correct? */
1931 if (diag == jDiag && IS_DOOR(lev->typ)
1932 && (lev->doormask & D_ISOPEN) != 0
1933 && (traj == jDiag
1934 || ((traj & jHorz) != 0) == (lev->horizontal != 0))) {
1935 if (showmsg)
1936 You_cant("jump diagonally out of a doorway.");
1937 return FALSE;
1939 uc.x = u.ux, uc.y = u.uy;
1940 tc.x = x, tc.y = y; /* target */
1941 if (!walk_path(&uc, &tc, check_jump, (genericptr_t) &traj)) {
1942 if (showmsg)
1943 There("is an obstacle preventing that jump.");
1944 return FALSE;
1947 return TRUE;
1950 staticfn boolean
1951 get_valid_jump_position(coordxy x, coordxy y)
1953 return (isok(x, y)
1954 && (ACCESSIBLE(levl[x][y].typ) || Passes_walls)
1955 && is_valid_jump_pos(x, y, gj.jumping_is_magic, FALSE));
1958 staticfn void
1959 display_jump_positions(boolean on_off)
1961 coordxy x, y, dx, dy;
1963 if (on_off) {
1964 /* on */
1965 tmp_at(DISP_BEAM, cmap_to_glyph(S_goodpos));
1966 for (dx = -4; dx <= 4; dx++)
1967 for (dy = -4; dy <= 4; dy++) {
1968 x = dx + u.ux;
1969 y = dy + u.uy;
1970 if (get_valid_jump_position(x, y) && !u_at(x, y))
1971 tmp_at(x, y);
1973 } else {
1974 /* off */
1975 tmp_at(DISP_END, 0);
1980 jump(int magic) /* 0=Physical, otherwise skill level */
1982 coord cc;
1984 /* attempt "jumping" spell if hero has no innate jumping ability */
1985 if (!magic && !Jumping && known_spell(SPE_JUMPING) >= spe_Fresh)
1986 return spelleffects(SPE_JUMPING, FALSE, FALSE);
1988 if (!magic && (nolimbs(gy.youmonst.data) || slithy(gy.youmonst.data))) {
1989 /* normally (nolimbs || slithy) implies !Jumping,
1990 but that isn't necessarily the case for knights */
1991 You_cant("jump; you have no legs!");
1992 return ECMD_OK;
1993 } else if (!magic && !Jumping) {
1994 You_cant("jump very far.");
1995 return ECMD_OK;
1997 /* if steed is immobile, can't do physical jump but can do spell one */
1998 } else if (!magic && u.usteed && stucksteed(FALSE)) {
1999 /* stucksteed gave "<steed> won't move" message */
2000 return ECMD_OK;
2001 } else if (u.uswallow) {
2002 if (magic) {
2003 You("bounce around a little.");
2004 return ECMD_TIME;
2006 pline("You've got to be kidding!");
2007 return ECMD_OK;
2008 } else if (u.uinwater) {
2009 if (magic) {
2010 You("swish around a little.");
2011 return ECMD_TIME;
2013 pline("This calls for swimming, not jumping!");
2014 return ECMD_OK;
2015 } else if (u.ustuck) {
2016 if (u.ustuck->mtame && !Conflict && !u.ustuck->mconf) {
2017 struct monst *mtmp = u.ustuck;
2019 set_ustuck((struct monst *) 0);
2020 You("pull free from %s.", mon_nam(mtmp));
2021 return ECMD_TIME;
2023 if (magic) {
2024 You("writhe a little in the grasp of %s!", mon_nam(u.ustuck));
2025 return ECMD_TIME;
2027 You("cannot escape from %s!", mon_nam(u.ustuck));
2028 return ECMD_OK;
2029 } else if (Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)) {
2030 if (magic) {
2031 You("flail around a little.");
2032 return ECMD_TIME;
2034 You("don't have enough traction to jump.");
2035 return ECMD_OK;
2036 } else if (!magic && near_capacity() > UNENCUMBERED) {
2037 You("are carrying too much to jump!");
2038 return ECMD_OK;
2039 } else if (!magic && (u.uhunger <= 100 || ACURR(A_STR) < 6)) {
2040 You("lack the strength to jump!");
2041 return ECMD_OK;
2042 } else if (!magic && Wounded_legs) {
2043 legs_in_no_shape("jumping", u.usteed != 0);
2044 return ECMD_OK;
2045 } else if (u.usteed && u.utrap) {
2046 pline("%s is stuck in a trap.", Monnam(u.usteed));
2047 return ECMD_OK;
2050 pline("Where do you want to jump?");
2051 cc.x = u.ux;
2052 cc.y = u.uy;
2053 gj.jumping_is_magic = magic;
2054 getpos_sethilite(display_jump_positions, get_valid_jump_position);
2055 if (getpos(&cc, TRUE, "the desired position") < 0)
2056 return ECMD_CANCEL; /* user pressed ESC */
2057 if (!is_valid_jump_pos(cc.x, cc.y, magic, TRUE)) {
2058 return ECMD_FAIL;
2059 } else if (u.usteed && u_at(cc.x, cc.y)) {
2060 pline("%s isn't capable of jumping in place.", YMonnam(u.usteed));
2061 return ECMD_FAIL;
2062 } else {
2063 coord uc;
2064 long side;
2065 int range, temp;
2066 boolean wastrapped = FALSE;
2068 if (u.utrap) {
2069 wastrapped = TRUE;
2070 switch (u.utraptype) {
2071 case TT_BEARTRAP:
2072 side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE;
2073 You("rip yourself free of the bear trap! Ouch!");
2074 losehp(Maybe_Half_Phys(rnd(10)), "jumping out of a bear trap",
2075 KILLED_BY);
2076 set_wounded_legs(side, rn1(1000, 500));
2077 break;
2078 case TT_PIT:
2079 You("leap from the pit!");
2080 break;
2081 case TT_WEB:
2082 You("tear the web apart as you pull yourself free!");
2083 deltrap(t_at(u.ux, u.uy));
2084 break;
2085 case TT_LAVA:
2086 You("pull yourself above the %s!", hliquid("lava"));
2087 cc.x = u.ux, cc.y = u.uy; /* take u_at() 'if' below */
2088 break;
2089 case TT_BURIEDBALL:
2090 case TT_INFLOOR:
2091 You("strain your %s, but you're still %s.",
2092 makeplural(body_part(LEG)),
2093 (u.utraptype == TT_INFLOOR)
2094 ? "stuck in the floor"
2095 : "attached to the buried ball");
2096 set_wounded_legs(LEFT_SIDE, rn1(10, 11));
2097 set_wounded_legs(RIGHT_SIDE, rn1(10, 11));
2098 return ECMD_TIME;
2099 default:
2100 impossible("Jumping out of strange trap (%d)?", u.utraptype);
2101 break;
2103 /* if we reach here, hero is no longer trapped */
2104 reset_utrap(TRUE);
2106 /* jumping on hero's same spot doesn't use walk_path() and isn't
2107 allowed when riding (handled above) */
2108 if (u_at(cc.x, cc.y)) {
2109 struct trap *t;
2111 /* escaping from a trap takes precedence over jumping in place */
2112 if (wastrapped) {
2113 morehungry(rnd(10));
2114 return ECMD_TIME;
2116 /* jumping in place on a trap will trigger it */
2117 if ((t = t_at(cc.x, cc.y)) != 0) {
2118 You("jump up and %s back down.", !Flying ? "come" : "fly");
2119 dotrap(t, FORCETRAP | TOOKPLUNGE);
2120 return ECMD_TIME;
2122 /* jumping in place takes no time and doesn't exercise anything */
2123 You("%s.", Hallucination ? "hop up and down a bit"
2124 : "decide not to jump after all");
2125 return ECMD_OK;
2129 * Check the path from uc to cc, calling hurtle_step at each
2130 * location. The final position actually reached will be
2131 * in cc.
2133 uc.x = u.ux;
2134 uc.y = u.uy;
2135 /* calculate max(abs(dx), abs(dy)) as the range */
2136 range = cc.x - uc.x;
2137 if (range < 0)
2138 range = -range;
2139 temp = cc.y - uc.y;
2140 if (temp < 0)
2141 temp = -temp;
2142 if (range < temp)
2143 range = temp;
2144 (void) walk_path(&uc, &cc, hurtle_jump, (genericptr_t) &range);
2145 /* hurtle_jump -> hurtle_step results in <u.ux,u.uy> == <cc.x,cc.y>
2146 * and usually moves the ball if punished, but does not handle all
2147 * the effects of landing on the final position.
2149 teleds(cc.x, cc.y, TELEDS_NO_FLAGS);
2150 nomul(-1);
2151 gm.multi_reason = "jumping around";
2152 gn.nomovemsg = "";
2153 morehungry(rnd(25));
2154 return ECMD_TIME;
2158 boolean
2159 tinnable(struct obj *corpse)
2161 if (corpse->oeaten)
2162 return 0;
2163 if (!mons[corpse->corpsenm].cnutrit)
2164 return 0;
2165 return 1;
2168 staticfn void
2169 use_tinning_kit(struct obj *obj)
2171 struct obj *corpse, *can;
2172 struct permonst *mptr;
2174 /* This takes only 1 move. If this is to be changed to take many
2175 * moves, we've got to deal with decaying corpses...
2177 if (obj->spe <= 0) {
2178 You("seem to be out of tins.");
2179 return;
2181 if (!(corpse = floorfood("tin", 2)))
2182 return;
2183 if (corpse->oeaten) {
2184 You("cannot tin %s which is partly eaten.", something);
2185 return;
2187 mptr = &mons[corpse->corpsenm];
2188 if (touch_petrifies(mptr) && !Stone_resistance && !uarmg) {
2189 char kbuf[BUFSZ];
2190 const char *corpse_name = an(cxname(corpse));
2192 if (poly_when_stoned(gy.youmonst.data)) {
2193 You("tin %s without wearing gloves.", corpse_name);
2194 kbuf[0] = '\0';
2195 } else {
2196 pline("Tinning %s without wearing gloves is a fatal mistake...",
2197 corpse_name);
2198 Sprintf(kbuf, "trying to tin %s without gloves", corpse_name);
2200 instapetrify(kbuf);
2202 if (is_rider(mptr)) {
2203 if (revive_corpse(corpse))
2204 verbalize("Yes... But War does not preserve its enemies...");
2205 else
2206 pline_The("corpse evades your grasp.");
2207 return;
2209 if (mptr->cnutrit == 0) {
2210 pline("That's too insubstantial to tin.");
2211 return;
2213 consume_obj_charge(obj, TRUE);
2215 if ((can = mksobj(TIN, FALSE, FALSE)) != 0) {
2216 static const char you_buy_it[] = "You tin it, you bought it!";
2218 can->corpsenm = corpse->corpsenm;
2219 can->cursed = obj->cursed;
2220 can->blessed = obj->blessed;
2221 can->owt = weight(can);
2222 can->known = 1;
2223 /* Mark tinned tins. No spinach allowed... */
2224 set_tin_variety(can, HOMEMADE_TIN);
2225 if (carried(corpse)) {
2226 if (corpse->unpaid) {
2227 struct monst *shkp VOICEONLY = shop_keeper(*in_rooms(
2228 u.ux, u.uy, SHOPBASE));
2230 SetVoice(shkp, 0, 80, 0);
2231 verbalize(you_buy_it);
2233 useup(corpse);
2234 } else {
2235 if (costly_spot(corpse->ox, corpse->oy) && !corpse->no_charge) {
2236 struct monst *shkp VOICEONLY
2237 = shop_keeper(*in_rooms(corpse->ox, corpse->oy, SHOPBASE));
2239 SetVoice(shkp, 0, 80, 0);
2240 verbalize(you_buy_it);
2242 useupf(corpse, 1L);
2244 (void) hold_another_object(can, "You make, but cannot pick up, %s.",
2245 doname(can), (const char *) 0);
2246 } else
2247 impossible("Tinning failed.");
2250 void
2251 use_unicorn_horn(struct obj **optr)
2253 #define PROP_COUNT 7 /* number of properties we're dealing with */
2254 int idx, val, val_limit, trouble_count, unfixable_trbl, did_prop;
2255 int trouble_list[PROP_COUNT];
2256 struct obj *obj = (optr ? *optr : (struct obj *) 0);
2258 if (obj && obj->cursed) {
2259 long lcount = (long) rn1(90, 10);
2261 switch (rn2(13) / 2) { /* case 6 is half as likely as the others */
2262 case 0:
2263 make_sick((Sick & TIMEOUT) ? (Sick & TIMEOUT) / 3L + 1L
2264 : (long) rn1(ACURR(A_CON), 20),
2265 xname(obj), TRUE, SICK_NONVOMITABLE);
2266 break;
2267 case 1:
2268 make_blinded(BlindedTimeout + lcount, TRUE);
2269 break;
2270 case 2:
2271 if (!Confusion)
2272 You("suddenly feel %s.",
2273 Hallucination ? "trippy" : "confused");
2274 make_confused((HConfusion & TIMEOUT) + lcount, TRUE);
2275 break;
2276 case 3:
2277 make_stunned((HStun & TIMEOUT) + lcount, TRUE);
2278 break;
2279 case 4:
2280 if (Vomiting)
2281 vomit();
2282 else
2283 make_vomiting(14L, FALSE);
2284 break;
2285 case 5:
2286 (void) make_hallucinated((HHallucination & TIMEOUT) + lcount,
2287 TRUE, 0L);
2288 break;
2289 case 6:
2290 if (Deaf) /* make_deaf() won't give feedback when already deaf */
2291 pline("%s", nothing_seems_to_happen);
2292 make_deaf((HDeaf & TIMEOUT) + lcount, TRUE);
2293 break;
2295 return;
2298 #define prop_trouble(X) trouble_list[trouble_count++] = (X)
2299 #define TimedTrouble(P) (((P) && !((P) & ~TIMEOUT)) ? ((P) & TIMEOUT) : 0L)
2301 trouble_count = unfixable_trbl = did_prop = 0;
2303 /* collect property troubles */
2304 if (TimedTrouble(Sick))
2305 prop_trouble(SICK);
2306 if (TimedTrouble(HBlinded) > (long) u.ucreamed
2307 && !(u.uswallow
2308 && attacktype_fordmg(u.ustuck->data, AT_ENGL, AD_BLND)))
2309 prop_trouble(BLINDED);
2310 if (TimedTrouble(HHallucination))
2311 prop_trouble(HALLUC);
2312 if (TimedTrouble(Vomiting))
2313 prop_trouble(VOMITING);
2314 if (TimedTrouble(HConfusion))
2315 prop_trouble(CONFUSION);
2316 if (TimedTrouble(HStun))
2317 prop_trouble(STUNNED);
2318 if (TimedTrouble(HDeaf))
2319 prop_trouble(DEAF);
2321 if (trouble_count == 0) {
2322 pline1(nothing_happens);
2323 return;
2324 } else if (trouble_count > 1)
2325 shuffle_int_array(trouble_list, trouble_count);
2328 * Chances for number of troubles to be fixed
2329 * 0 1 2 3 4 5 6 7
2330 * blessed: 22.7% 22.7% 19.5% 15.4% 10.7% 5.7% 2.6% 0.8%
2331 * uncursed: 35.4% 35.4% 22.9% 6.3% 0 0 0 0
2333 val_limit = rn2(d(2, (obj && obj->blessed) ? 4 : 2));
2334 if (val_limit > trouble_count)
2335 val_limit = trouble_count;
2337 /* fix [some of] the troubles */
2338 for (val = 0; val < val_limit; val++) {
2339 idx = trouble_list[val];
2341 switch (idx) {
2342 case SICK:
2343 make_sick(0L, (char *) 0, TRUE, SICK_ALL);
2344 did_prop++;
2345 break;
2346 case BLINDED:
2347 make_blinded((long) u.ucreamed, TRUE);
2348 did_prop++;
2349 break;
2350 case HALLUC:
2351 (void) make_hallucinated(0L, TRUE, 0L);
2352 did_prop++;
2353 break;
2354 case VOMITING:
2355 make_vomiting(0L, TRUE);
2356 did_prop++;
2357 break;
2358 case CONFUSION:
2359 make_confused(0L, TRUE);
2360 did_prop++;
2361 break;
2362 case STUNNED:
2363 make_stunned(0L, TRUE);
2364 did_prop++;
2365 break;
2366 case DEAF:
2367 make_deaf(0L, TRUE);
2368 did_prop++;
2369 break;
2370 default:
2371 impossible("use_unicorn_horn: bad trouble? (%d)", idx);
2372 break;
2376 if (did_prop)
2377 disp.botl = TRUE;
2378 else
2379 pline("%s", nothing_seems_to_happen);
2381 #undef PROP_COUNT
2382 #undef prop_trouble
2383 #undef TimedTrouble
2387 * Timer callback routine: turn figurine into monster
2389 void
2390 fig_transform(anything *arg, long timeout)
2392 struct obj *figurine = arg->a_obj;
2393 struct monst *mtmp;
2394 coord cc;
2395 boolean cansee_spot, silent, okay_spot;
2396 boolean redraw = FALSE;
2397 boolean suppress_see = FALSE;
2398 char monnambuf[BUFSZ], carriedby[BUFSZ];
2400 if (!figurine) {
2401 impossible("null figurine in fig_transform()");
2402 return;
2404 silent = (timeout != svm.moves); /* happened while away */
2405 okay_spot = get_obj_location(figurine, &cc.x, &cc.y, 0);
2406 if (figurine->where == OBJ_INVENT || figurine->where == OBJ_MINVENT)
2407 okay_spot = enexto(&cc, cc.x, cc.y, &mons[figurine->corpsenm]);
2408 if (!okay_spot || !figurine_location_checks(figurine, &cc, TRUE)) {
2409 /* reset the timer to try again later */
2410 (void) start_timer((long) rnd(5000), TIMER_OBJECT, FIG_TRANSFORM,
2411 obj_to_any(figurine));
2412 return;
2415 cansee_spot = cansee(cc.x, cc.y);
2416 mtmp = make_familiar(figurine, cc.x, cc.y, TRUE);
2417 if (mtmp) {
2418 char and_vanish[BUFSZ];
2419 struct obj *mshelter = svl.level.objects[mtmp->mx][mtmp->my];
2421 /* [m_monnam() yields accurate mon type, overriding hallucination] */
2422 Sprintf(monnambuf, "%s", an(m_monnam(mtmp)));
2423 and_vanish[0] = '\0';
2424 if ((mtmp->minvis && !See_invisible)
2425 || (mtmp->data->mlet == S_MIMIC
2426 && M_AP_TYPE(mtmp) != M_AP_NOTHING))
2427 suppress_see = TRUE;
2429 if (mtmp->mundetected) {
2430 if (hides_under(mtmp->data) && mshelter) {
2431 Sprintf(and_vanish, " and %s under %s",
2432 locomotion(mtmp->data, "crawl"), doname(mshelter));
2433 } else if (mtmp->data->mlet == S_MIMIC
2434 || mtmp->data->mlet == S_EEL) {
2435 suppress_see = TRUE;
2436 } else
2437 Strcpy(and_vanish, " and vanish");
2440 switch (figurine->where) {
2441 case OBJ_INVENT:
2442 if (Blind || suppress_see)
2443 You_feel("%s %s from your pack!", something,
2444 locomotion(mtmp->data, "drop"));
2445 else
2446 You_see("%s %s out of your pack%s!", monnambuf,
2447 locomotion(mtmp->data, "drop"), and_vanish);
2448 break;
2450 case OBJ_FLOOR:
2451 if (cansee_spot && !silent) {
2452 set_msg_xy(cc.x, cc.y);
2453 if (suppress_see)
2454 pline("%s suddenly vanishes!", an(xname(figurine)));
2455 else
2456 You_see("a figurine transform into %s%s!", monnambuf,
2457 and_vanish);
2458 redraw = TRUE; /* update figurine's map location */
2460 break;
2462 case OBJ_MINVENT:
2463 if (cansee_spot && !silent && !suppress_see) {
2464 struct monst *mon;
2466 mon = figurine->ocarry;
2467 /* figurine carrying monster might be invisible */
2468 if (canseemon(figurine->ocarry)
2469 && (!mon->wormno || cansee(mon->mx, mon->my)))
2470 Sprintf(carriedby, "%s pack", s_suffix(a_monnam(mon)));
2471 else if (is_pool(mon->mx, mon->my))
2472 Strcpy(carriedby, "empty water");
2473 else
2474 Strcpy(carriedby, "thin air");
2475 You_see("%s %s out of %s%s!", monnambuf,
2476 locomotion(mtmp->data, "drop"), carriedby,
2477 and_vanish);
2479 break;
2480 #if 0
2481 case OBJ_MIGRATING:
2482 break;
2483 #endif
2485 default:
2486 impossible("figurine came to life where? (%d)",
2487 (int) figurine->where);
2488 break;
2491 /* free figurine now */
2492 if (carried(figurine)) {
2493 useup(figurine);
2494 } else {
2495 obj_extract_self(figurine);
2496 obfree(figurine, (struct obj *) 0);
2498 if (redraw)
2499 newsym(cc.x, cc.y);
2502 staticfn boolean
2503 figurine_location_checks(struct obj *obj, coord *cc, boolean quietly)
2505 coordxy x, y;
2507 if (carried(obj) && u.uswallow) {
2508 if (!quietly)
2509 You("don't have enough room in here.");
2510 return FALSE;
2512 x = cc ? cc->x : u.ux;
2513 y = cc ? cc->y : u.uy;
2514 if (!isok(x, y)) {
2515 if (!quietly)
2516 You("cannot put the figurine there.");
2517 return FALSE;
2519 if (IS_OBSTRUCTED(levl[x][y].typ)
2520 && !(passes_walls(&mons[obj->corpsenm]) && may_passwall(x, y))) {
2521 if (!quietly)
2522 You("cannot place a figurine in %s!",
2523 IS_TREE(levl[x][y].typ) ? "a tree" : "solid rock");
2524 return FALSE;
2526 if (sobj_at(BOULDER, x, y) && !passes_walls(&mons[obj->corpsenm])
2527 && !throws_rocks(&mons[obj->corpsenm])) {
2528 if (!quietly)
2529 You("cannot fit the figurine on the boulder.");
2530 return FALSE;
2532 return TRUE;
2535 staticfn int
2536 use_figurine(struct obj **optr)
2538 struct obj *obj = *optr;
2539 coordxy x, y;
2540 coord cc;
2542 if (u.uswallow) {
2543 /* can't activate a figurine while swallowed */
2544 if (!figurine_location_checks(obj, (coord *) 0, FALSE))
2545 return ECMD_OK;
2547 if (!getdir((char *) 0)) {
2548 svc.context.move = gm.multi = 0;
2549 return ECMD_CANCEL;
2551 x = u.ux + u.dx;
2552 y = u.uy + u.dy;
2553 cc.x = x;
2554 cc.y = y;
2555 /* Passing FALSE arg here will result in messages displayed */
2556 if (!figurine_location_checks(obj, &cc, FALSE))
2557 return ECMD_TIME;
2558 You("%s and it %stransforms.",
2559 (u.dx || u.dy) ? "set the figurine beside you"
2560 : (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)
2561 || is_pool(cc.x, cc.y))
2562 ? "release the figurine"
2563 : (u.dz < 0 ? "toss the figurine into the air"
2564 : "set the figurine on the ground"),
2565 Blind ? "supposedly " : "");
2566 (void) make_familiar(obj, cc.x, cc.y, FALSE);
2567 (void) stop_timer(FIG_TRANSFORM, obj_to_any(obj));
2568 useup(obj);
2569 if (Blind)
2570 map_invisible(cc.x, cc.y);
2571 *optr = 0;
2572 return ECMD_TIME;
2575 /* getobj callback for object to apply grease to */
2576 staticfn int
2577 grease_ok(struct obj *obj)
2579 if (!obj)
2580 return GETOBJ_SUGGEST;
2582 if (obj->oclass == COIN_CLASS)
2583 return GETOBJ_EXCLUDE;
2585 if (inaccessible_equipment(obj, (const char *) 0, FALSE))
2586 return GETOBJ_EXCLUDE_INACCESS;
2588 /* Possible extension: don't suggest greasing objects which are already
2589 * greased. */
2590 return GETOBJ_SUGGEST;
2593 staticfn int
2594 use_grease(struct obj *obj)
2596 struct obj *otmp;
2598 if (Glib) {
2599 pline("%s from your %s.", Tobjnam(obj, "slip"),
2600 fingers_or_gloves(FALSE));
2601 dropx(obj);
2602 return ECMD_TIME;
2605 if (obj->spe > 0) {
2606 int oldglib;
2608 if ((obj->cursed || Fumbling) && !rn2(2)) {
2609 consume_obj_charge(obj, TRUE);
2611 pline("%s from your %s.", Tobjnam(obj, "slip"),
2612 fingers_or_gloves(FALSE));
2613 dropx(obj);
2614 return ECMD_TIME;
2616 otmp = getobj("grease", grease_ok, GETOBJ_PROMPT);
2617 if (!otmp)
2618 return ECMD_CANCEL;
2619 if (inaccessible_equipment(otmp, "grease", FALSE))
2620 return ECMD_OK;
2621 consume_obj_charge(obj, TRUE);
2623 oldglib = (int) (Glib & TIMEOUT);
2624 if (otmp != &hands_obj) {
2625 You("cover %s with a thick layer of grease.", yname(otmp));
2626 otmp->greased = 1;
2627 if (obj->cursed && !nohands(gy.youmonst.data)) {
2628 make_glib(oldglib + rn1(6, 10)); /* + 10..15 */
2629 pline("Some of the grease gets all over your %s.",
2630 fingers_or_gloves(TRUE));
2632 } else {
2633 make_glib(oldglib + rn1(11, 5)); /* + 5..15 */
2634 You("coat your %s with grease.", fingers_or_gloves(TRUE));
2636 } else {
2637 if (obj->known)
2638 pline("%s empty.", Tobjnam(obj, "are"));
2639 else
2640 pline("%s to be empty.", Tobjnam(obj, "seem"));
2642 update_inventory();
2643 return ECMD_TIME;
2646 /* getobj callback for object to rub on a known touchstone */
2647 staticfn int
2648 touchstone_ok(struct obj *obj)
2650 if (!obj)
2651 return GETOBJ_EXCLUDE;
2653 /* Gold being suggested as a rub target is questionable - it fits the
2654 * real-world historic use of touchstones, but doesn't do anything
2655 * significant in the game. */
2656 if (obj->oclass == COIN_CLASS)
2657 return GETOBJ_SUGGEST;
2659 /* don't suggest identified gems */
2660 if (obj->oclass == GEM_CLASS
2661 && !(obj->dknown && objects[obj->otyp].oc_name_known))
2662 return GETOBJ_SUGGEST;
2664 return GETOBJ_DOWNPLAY;
2668 /* touchstones - by Ken Arnold */
2669 staticfn int
2670 use_stone(struct obj *tstone)
2672 static const char scritch[] = "\"scritch, scritch\"";
2673 struct obj *obj;
2674 boolean do_scratch;
2675 const char *streak_color;
2676 char stonebuf[QBUFSZ];
2677 int oclass;
2678 boolean known;
2680 /* in case it was acquired while blinded */
2681 if (!Blind)
2682 tstone->dknown = 1;
2683 known = (tstone->otyp == TOUCHSTONE && tstone->dknown
2684 && objects[TOUCHSTONE].oc_name_known);
2685 Sprintf(stonebuf, "rub on the stone%s", plur(tstone->quan));
2686 /* when the touchstone is fully known, don't bother listing extra
2687 junk as likely candidates for rubbing */
2688 if ((obj = getobj(stonebuf, known ? touchstone_ok : any_obj_ok,
2689 GETOBJ_PROMPT)) == 0)
2690 return ECMD_CANCEL;
2692 if (obj == tstone && obj->quan == 1L) {
2693 You_cant("rub %s on itself.", the(xname(obj)));
2694 return ECMD_OK;
2697 if (tstone->otyp == TOUCHSTONE && tstone->cursed
2698 && obj->oclass == GEM_CLASS && !is_graystone(obj)
2699 && !obj_resists(obj, 80, 100)) {
2700 if (Blind)
2701 You_feel("something shatter.");
2702 else if (Hallucination)
2703 pline("Oh, wow, look at the pretty shards.");
2704 else
2705 pline("A sharp crack shatters %s%s.",
2706 (obj->quan > 1L) ? "one of " : "", the(xname(obj)));
2707 useup(obj);
2708 return ECMD_TIME;
2711 if (Blind) {
2712 pline(scritch);
2713 return ECMD_TIME;
2714 } else if (Hallucination) {
2715 pline("Oh wow, man: Fractals!");
2716 return ECMD_TIME;
2719 do_scratch = FALSE;
2720 streak_color = 0;
2722 oclass = obj->oclass;
2723 /* prevent non-gemstone rings from being treated like gems */
2724 if (oclass == RING_CLASS
2725 && objects[obj->otyp].oc_material != GEMSTONE
2726 && objects[obj->otyp].oc_material != MINERAL)
2727 oclass = RANDOM_CLASS; /* something that's neither gem nor ring */
2729 switch (oclass) {
2730 case GEM_CLASS: /* these have class-specific handling below */
2731 case RING_CLASS:
2732 if (tstone->otyp != TOUCHSTONE) {
2733 do_scratch = TRUE;
2734 } else if (obj->oclass == GEM_CLASS
2735 && (tstone->blessed
2736 || (!tstone->cursed && (Role_if(PM_ARCHEOLOGIST)
2737 || Race_if(PM_GNOME))))) {
2738 makeknown(TOUCHSTONE);
2739 makeknown(obj->otyp);
2740 prinv((char *) 0, obj, 0L);
2741 return ECMD_TIME;
2742 } else {
2743 /* either a ring or the touchstone was not effective */
2744 if (objects[obj->otyp].oc_material == GLASS) {
2745 do_scratch = TRUE;
2746 break;
2749 streak_color = c_obj_colors[objects[obj->otyp].oc_color];
2750 break; /* gem or ring */
2752 default:
2753 switch (objects[obj->otyp].oc_material) {
2754 case CLOTH:
2755 pline("%s a little more polished now.", Tobjnam(tstone, "look"));
2756 return ECMD_TIME;
2757 case LIQUID:
2758 if (!obj->known) /* note: not "whetstone" */
2759 You("must think this is a wetstone, do you?");
2760 else
2761 pline("%s a little wetter now.", Tobjnam(tstone, "are"));
2762 return ECMD_TIME;
2763 case WAX:
2764 streak_color = "waxy";
2765 break; /* okay even if not touchstone */
2766 case WOOD:
2767 streak_color = "wooden";
2768 break; /* okay even if not touchstone */
2769 case GOLD:
2770 do_scratch = TRUE; /* scratching and streaks */
2771 streak_color = "golden";
2772 break;
2773 case SILVER:
2774 do_scratch = TRUE; /* scratching and streaks */
2775 streak_color = "silvery";
2776 break;
2777 default:
2778 /* Objects passing the is_flimsy() test will not
2779 scratch a stone. They will leave streaks on
2780 non-touchstones and touchstones alike. */
2781 if (is_flimsy(obj))
2782 streak_color = c_obj_colors[objects[obj->otyp].oc_color];
2783 else
2784 do_scratch = (tstone->otyp != TOUCHSTONE);
2785 break;
2787 break; /* default oclass */
2790 Sprintf(stonebuf, "stone%s", plur(tstone->quan));
2791 if (do_scratch)
2792 You("make %s%sscratch marks on the %s.",
2793 streak_color ? streak_color : (const char *) "",
2794 streak_color ? " " : "", stonebuf);
2795 else if (streak_color)
2796 You_see("%s streaks on the %s.", streak_color, stonebuf);
2797 else
2798 pline(scritch);
2799 return ECMD_TIME;
2802 void
2803 reset_trapset(void)
2805 gt.trapinfo.tobj = 0;
2806 gt.trapinfo.force_bungle = 0;
2809 /* Place a landmine/bear trap. Helge Hafting */
2810 staticfn void
2811 use_trap(struct obj *otmp)
2813 int ttyp, tmp;
2814 const char *what = (char *) 0;
2815 char buf[BUFSZ];
2816 int levtyp = levl[u.ux][u.uy].typ;
2817 const char *occutext = "setting the trap";
2819 if (nohands(gy.youmonst.data))
2820 what = "without hands";
2821 else if (Stunned)
2822 what = "while stunned";
2823 else if (u.uswallow)
2824 what = digests(u.ustuck->data) ? "while swallowed" : "while engulfed";
2825 else if (Underwater)
2826 what = "underwater";
2827 else if (Levitation)
2828 what = "while levitating";
2829 else if (is_pool(u.ux, u.uy))
2830 what = "in water";
2831 else if (is_lava(u.ux, u.uy))
2832 what = "in lava";
2833 else if (On_stairs(u.ux, u.uy)) {
2834 stairway *stway = stairway_at(u.ux, u.uy);
2835 what = stway->isladder ? "on the ladder" : "on the stairs";
2836 } else if (IS_FURNITURE(levtyp) || IS_OBSTRUCTED(levtyp)
2837 || closed_door(u.ux, u.uy) || t_at(u.ux, u.uy))
2838 what = "here";
2839 else if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))
2840 what = (levtyp == AIR)
2841 ? "in midair"
2842 : (levtyp == CLOUD)
2843 ? "in a cloud"
2844 : "in this place"; /* Air/Water Plane catch-all */
2845 if (what) {
2846 You_cant("set a trap %s!", what);
2847 reset_trapset();
2848 return;
2850 ttyp = (otmp->otyp == LAND_MINE) ? LANDMINE : BEAR_TRAP;
2851 if (otmp == gt.trapinfo.tobj && u_at(gt.trapinfo.tx, gt.trapinfo.ty)) {
2852 You("resume setting %s%s.", shk_your(buf, otmp),
2853 trapname(ttyp, FALSE));
2854 set_occupation(set_trap, occutext, 0);
2855 return;
2857 gt.trapinfo.tobj = otmp;
2858 gt.trapinfo.tx = u.ux, gt.trapinfo.ty = u.uy;
2859 tmp = ACURR(A_DEX);
2860 gt.trapinfo.time_needed =
2861 (tmp > 17) ? 2 : (tmp > 12) ? 3 : (tmp > 7) ? 4 : 5;
2862 if (Blind)
2863 gt.trapinfo.time_needed *= 2;
2864 tmp = ACURR(A_STR);
2865 if (ttyp == BEAR_TRAP && tmp < 18)
2866 gt.trapinfo.time_needed += (tmp > 12) ? 1 : (tmp > 7) ? 2 : 4;
2867 /*[fumbling and/or confusion and/or cursed object check(s)
2868 should be incorporated here instead of in set_trap]*/
2869 if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) {
2870 boolean chance;
2872 if (Fumbling || otmp->cursed)
2873 chance = (rnl(10) > 3);
2874 else
2875 chance = (rnl(10) > 5);
2876 You("aren't very skilled at reaching from %s.", mon_nam(u.usteed));
2877 Sprintf(buf, "Continue your attempt to set %s?",
2878 the(trapname(ttyp, FALSE)));
2879 if (y_n(buf) == 'y') {
2880 if (chance) {
2881 switch (ttyp) {
2882 case LANDMINE: /* set it off */
2883 gt.trapinfo.time_needed = 0;
2884 gt.trapinfo.force_bungle = TRUE;
2885 break;
2886 case BEAR_TRAP: /* drop it without arming it */
2887 reset_trapset();
2888 You("drop %s!", the(trapname(ttyp, FALSE)));
2889 dropx(otmp);
2890 return;
2893 } else {
2894 reset_trapset();
2895 return;
2898 You("begin setting %s%s.", shk_your(buf, otmp), trapname(ttyp, FALSE));
2899 use_unpaid_trapobj(otmp, u.ux, u.uy);
2900 set_occupation(set_trap, occutext, 0);
2901 return;
2904 /* occupation routine called each turn while arming a beartrap or landmine */
2905 staticfn int
2906 set_trap(void)
2908 struct obj *otmp = gt.trapinfo.tobj;
2909 struct trap *ttmp;
2910 int ttyp;
2912 if (!otmp || !carried(otmp) || !u_at(gt.trapinfo.tx, gt.trapinfo.ty)) {
2913 /* trap object might have been stolen or hero teleported */
2914 reset_trapset();
2915 return 0;
2918 if (--gt.trapinfo.time_needed > 0)
2919 return 1; /* still busy */
2921 ttyp = (otmp->otyp == LAND_MINE) ? LANDMINE : BEAR_TRAP;
2922 ttmp = maketrap(u.ux, u.uy, ttyp);
2923 if (ttmp) {
2924 ttmp->madeby_u = 1;
2925 feeltrap(ttmp);
2926 if (*in_rooms(u.ux, u.uy, SHOPBASE)) {
2927 add_damage(u.ux, u.uy, 0L); /* schedule removal */
2929 if (!gt.trapinfo.force_bungle)
2930 You("finish arming %s.", the(trapname(ttyp, FALSE)));
2931 if (((otmp->cursed || Fumbling) && (rnl(10) > 5))
2932 || gt.trapinfo.force_bungle)
2933 dotrap(ttmp,
2934 (unsigned) (gt.trapinfo.force_bungle ? FORCEBUNGLE : 0));
2935 } else {
2936 /* this shouldn't happen */
2937 Your("trap setting attempt fails.");
2939 useup(otmp);
2940 reset_trapset();
2941 return 0;
2945 use_whip(struct obj *obj)
2947 char buf[BUFSZ];
2948 struct monst *mtmp;
2949 struct obj *otmp;
2950 int rx, ry, proficient, res = ECMD_OK;
2951 const char *msg_slipsfree = "The bullwhip slips free.";
2952 const char *msg_snap = "Snap!";
2954 if (obj != uwep) {
2955 if (wield_tool(obj, "lash")) {
2956 cmdq_add_ec(CQ_CANNED, doapply);
2957 cmdq_add_key(CQ_CANNED, obj->invlet);
2958 return ECMD_TIME;
2960 return ECMD_OK;
2962 if (!getdir((char *) 0))
2963 return (res|ECMD_CANCEL);
2965 if (u.uswallow) {
2966 mtmp = u.ustuck;
2967 rx = mtmp->mx;
2968 ry = mtmp->my;
2969 } else {
2970 confdir(FALSE);
2971 rx = u.ux + u.dx;
2972 ry = u.uy + u.dy;
2973 if (!isok(rx, ry)) {
2974 You("miss.");
2975 return res;
2977 mtmp = m_at(rx, ry);
2980 /* fake some proficiency checks */
2981 proficient = 0;
2982 if (Role_if(PM_ARCHEOLOGIST))
2983 ++proficient;
2984 if (ACURR(A_DEX) < 6)
2985 proficient--;
2986 else if (ACURR(A_DEX) >= 14)
2987 proficient += (ACURR(A_DEX) - 14);
2988 if (Fumbling)
2989 --proficient;
2990 if (proficient > 3)
2991 proficient = 3;
2992 if (proficient < 0)
2993 proficient = 0;
2995 if (u.uswallow) {
2996 There("is not enough room to flick your bullwhip.");
2998 } else if (Underwater) {
2999 There("is too much resistance to flick your bullwhip.");
3001 } else if (u.dz < 0) {
3002 You("flick a bug off of the %s.", ceiling(u.ux, u.uy));
3004 } else if (!u.dz && (IS_WATERWALL(levl[rx][ry].typ)
3005 || levl[rx][ry].typ == LAVAWALL)) {
3006 You("cause a small splash.");
3007 if (levl[rx][ry].typ == LAVAWALL)
3008 (void) fire_damage(uwep, FALSE, rx, ry);
3009 return ECMD_TIME;
3010 } else if ((!u.dx && !u.dy) || (u.dz > 0)) {
3011 int dam;
3013 /* Sometimes you hit your steed by mistake */
3014 if (u.usteed && !rn2(proficient + 2)) {
3015 You("whip %s!", mon_nam(u.usteed));
3016 kick_steed();
3017 return ECMD_TIME;
3019 if (is_pool_or_lava(u.ux, u.uy)
3020 || IS_WATERWALL(levl[rx][ry].typ)
3021 || levl[rx][ry].typ == LAVAWALL) {
3022 You("cause a small splash.");
3023 if (is_lava(u.ux, u.uy))
3024 (void) fire_damage(uwep, FALSE, u.ux, u.uy);
3025 return ECMD_TIME;
3027 if (Levitation || u.usteed || Flying) {
3028 /* Have a shot at snaring something on the floor. A flyer
3029 can reach the floor so could just pick an item up, but
3030 allow snagging by whip too. */
3031 otmp = svl.level.objects[u.ux][u.uy];
3032 if (otmp && otmp->otyp == CORPSE
3033 && (otmp->corpsenm == PM_HORSE
3034 || otmp->corpsenm == little_to_big(PM_HORSE) /* warhorse */
3035 || otmp->corpsenm == big_to_little(PM_HORSE))) { /* pony */
3036 pline("Why beat a dead horse?");
3037 return ECMD_TIME;
3039 if (otmp && proficient) {
3040 You("wrap your bullwhip around %s on the %s.",
3041 an(singular(otmp, xname)), surface(u.ux, u.uy));
3042 if (rnl(6) || pickup_object(otmp, 1L, TRUE) < 1)
3043 pline1(msg_slipsfree);
3044 return ECMD_TIME;
3047 dam = rnd(2) + dbon() + obj->spe;
3048 if (dam <= 0)
3049 dam = 1;
3050 You("hit your %s with your bullwhip.", body_part(FOOT));
3051 Sprintf(buf, "killed %sself with %s bullwhip", uhim(), uhis());
3052 losehp(Maybe_Half_Phys(dam), buf, NO_KILLER_PREFIX);
3053 return ECMD_TIME;
3055 } else if ((Fumbling || Glib) && !rn2(5)) {
3056 pline_The("bullwhip slips out of your %s.", body_part(HAND));
3057 dropx(obj);
3059 } else if (u.utrap && u.utraptype == TT_PIT) {
3061 * Assumptions:
3063 * if you're in a pit
3064 * - you are attempting to get out of the pit
3065 * - if there is no suitable boulder or furniture to target,
3066 * target a big monster for that, or if a small or medium
3067 * monster is present, attack it
3068 * [if both boulder and furniture are present, target the
3069 * former because it is on top of the latter]
3070 * else if you are applying it towards a monster
3071 * - if monster is concealed, reveal it and proceed;
3072 * - if it was not concealed and is wielding a weapon, attempt
3073 * to disarm it;
3074 * - otherwise attack it.
3076 * if you're confused (and thus off the mark)
3077 * - you only end up hitting.
3079 const char *wrapped_what = sobj_at(BOULDER, rx, ry) ? "a boulder"
3080 : IS_FURNITURE(levl[rx][ry].typ)
3081 ? something : (char *) 0;
3083 if (mtmp) {
3084 /* if a big monster is known to be present, target it in
3085 preference to boulder or furniture; if any small or medium
3086 monster is present, or an unseen big one, use the boulder
3087 or furniture if available, otherwise attack */
3088 if (bigmonst(mtmp->data) && canspotmon(mtmp))
3089 wrapped_what = strcpy(buf, mon_nam(mtmp));
3091 if (!wrapped_what)
3092 goto whipattack;
3094 if (wrapped_what) {
3095 coord cc;
3097 cc.x = rx;
3098 cc.y = ry;
3099 You("wrap your bullwhip around %s.", wrapped_what);
3100 if (proficient && rn2(proficient + 2)) {
3101 if (!mtmp || enexto(&cc, rx, ry, gy.youmonst.data)) {
3102 You("yank yourself out of the pit!");
3103 reset_utrap(TRUE); /* [was after teleds(); do this before
3104 * in case it has no alternative other
3105 * than to put hero in another trap] */
3106 teleds(cc.x, cc.y, TELEDS_ALLOW_DRAG);
3107 gv.vision_full_recalc = 1;
3109 } else {
3110 pline1(msg_slipsfree);
3112 if (mtmp)
3113 wakeup(mtmp, TRUE);
3114 } else
3115 pline1(msg_snap);
3117 } else if (mtmp) {
3118 whipattack:
3119 otmp = 0; /* if monster is unseen, can't attempt to disarm it */
3120 if (!canspotmon(mtmp)) {
3121 boolean spotitnow;
3123 mtmp->mundetected = 0; /* bring non-mimic hider out of hiding */
3124 /* check visibility again after mundetected=0 in case being
3125 brought out of hiding has exposed it (might not if hero is
3126 blind or formerly hidden monster is also invisible) */
3127 spotitnow = canspotmon(mtmp);
3128 if (spotitnow || !glyph_is_invisible(levl[rx][ry].glyph)) {
3129 pline("%s is there that you %s.",
3130 !spotitnow ? "A monster" : Amonnam(mtmp),
3131 !Blind ? "couldn't see" : "hadn't noticed");
3132 if (!spotitnow)
3133 map_invisible(rx, ry);
3134 else
3135 newsym(rx, ry);
3137 } else {
3138 /* monster is known so if it is wielding something, try to
3139 disarm it rather than make a direct attack */
3140 otmp = MON_WEP(mtmp);
3143 if (otmp) {
3144 char onambuf[BUFSZ];
3145 const char *mon_hand;
3146 boolean gotit = proficient && (!Fumbling || !rn2(10));
3148 Strcpy(onambuf, cxname(otmp));
3149 if (gotit) {
3150 mon_hand = mbodypart(mtmp, HAND);
3151 if (bimanual(otmp))
3152 mon_hand = makeplural(mon_hand);
3153 } else
3154 mon_hand = 0; /* lint suppression */
3156 You("wrap your bullwhip around %s.", yname(otmp));
3157 if (gotit && mwelded(otmp)) {
3158 pline("%s welded to %s %s%c",
3159 (otmp->quan == 1L) ? "It is" : "They are", mhis(mtmp),
3160 mon_hand, !otmp->bknown ? '!' : '.');
3161 set_bknown(otmp, 1);
3162 gotit = FALSE; /* can't pull it free */
3164 if (gotit) {
3165 obj_extract_self(otmp);
3166 possibly_unwield(mtmp, FALSE);
3167 setmnotwielded(mtmp, otmp);
3169 switch (rn2(proficient + 1)) {
3170 case 2:
3171 /* to floor near you */
3172 You("yank %s to the %s!", yname(otmp),
3173 surface(u.ux, u.uy));
3174 place_object(otmp, u.ux, u.uy);
3175 stackobj(otmp);
3176 break;
3177 case 3:
3178 #if 0
3179 /* right to you */
3180 if (!rn2(25)) {
3181 /* proficient with whip, but maybe not
3182 so proficient at catching weapons */
3183 int hitu, hitvalu;
3185 hitvalu = 8 + otmp->spe;
3186 hitu = thitu(hitvalu, dmgval(otmp, &gy.youmonst),
3187 &otmp, (char *) 0);
3188 if (hitu) {
3189 pline_The("%s hits you as you try to snatch it!",
3190 the(onambuf));
3192 place_object(otmp, u.ux, u.uy);
3193 stackobj(otmp);
3194 break;
3196 #endif /* 0 */
3197 /* right into your inventory */
3198 You("snatch %s!", yname(otmp));
3199 if (otmp->otyp == CORPSE
3200 && touch_petrifies(&mons[otmp->corpsenm]) && !uarmg
3201 && !Stone_resistance
3202 && !(poly_when_stoned(gy.youmonst.data)
3203 && polymon(PM_STONE_GOLEM))) {
3204 char kbuf[BUFSZ];
3206 Strcpy(kbuf, (otmp->quan == 1L) ? an(onambuf)
3207 : onambuf);
3208 pline("Snatching %s is a fatal mistake.", kbuf);
3209 /* corpse probably has a rot timer but is now
3210 OBJ_FREE; end of game cleanup will panic if
3211 it isn't part of current level; plus it would
3212 be missing from bones, so put it on the floor */
3213 place_object(otmp, u.ux, u.uy); /* but don't stack */
3215 instapetrify(kbuf);
3216 /* life-saved; free the corpse again */
3217 obj_extract_self(otmp);
3219 (void) hold_another_object(otmp, "You drop %s!",
3220 doname(otmp), (const char *) 0);
3221 break;
3222 default:
3223 /* to floor beneath mon */
3224 You("yank %s from %s %s!", the(onambuf),
3225 s_suffix(mon_nam(mtmp)), mon_hand);
3226 obj_no_longer_held(otmp);
3227 place_object(otmp, mtmp->mx, mtmp->my);
3228 stackobj(otmp);
3229 break;
3231 } else {
3232 pline1(msg_slipsfree);
3234 } else { /* mtmp isn't wielding a weapon; attack it */
3235 boolean do_snap = TRUE;
3237 if (M_AP_TYPE(mtmp) && !Protection_from_shape_changers
3238 && !sensemon(mtmp)) {
3239 stumble_onto_mimic(mtmp);
3240 do_snap = FALSE;
3241 } else {
3242 You("flick your bullwhip towards %s.", mon_nam(mtmp));
3244 if (proficient && force_attack(mtmp, FALSE))
3245 return ECMD_TIME;
3246 if (do_snap)
3247 pline1(msg_snap);
3249 /* regardless of mtmp's weapon or hero's proficiency */
3250 wakeup(mtmp, TRUE);
3252 } else if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)) {
3253 /* it must be air -- water checked above */
3254 You("snap your whip through thin air.");
3256 } else {
3257 pline1(msg_snap);
3259 return ECMD_TIME;
3262 static const char
3263 not_enough_room[] = "There's not enough room here to use that.",
3264 where_to_hit[] = "Where do you want to hit?",
3265 cant_see_spot[] = "won't hit anything if you can't see that spot.",
3266 cant_reach[] = "can't reach that spot from here.";
3268 #define glyph_is_poleable(G) \
3269 (glyph_is_monster(G) || glyph_is_invisible(G) || glyph_is_statue(G))
3271 /* find pos of monster in range, if only one monster */
3272 staticfn boolean
3273 find_poleable_mon(coord *pos, int min_range, int max_range)
3275 struct monst *mtmp;
3276 coord mpos = { 0, 0 }; /* no candidate location yet */
3277 boolean impaired;
3278 coordxy x, y, lo_x, hi_x, lo_y, hi_y, rt;
3279 int glyph;
3281 impaired = (Confusion || Stunned || Hallucination);
3282 rt = isqrt(max_range);
3283 lo_x = max(u.ux - rt, 1), hi_x = min(u.ux + rt, COLNO - 1);
3284 lo_y = max(u.uy - rt, 0), hi_y = min(u.uy + rt, ROWNO - 1);
3285 for (x = lo_x; x <= hi_x; ++x) {
3286 for (y = lo_y; y <= hi_y; ++y) {
3287 if (distu(x, y) < min_range || distu(x, y) > max_range
3288 || !isok(x, y) || !cansee(x, y))
3289 continue;
3290 glyph = glyph_at(x, y);
3291 if (!impaired
3292 && glyph_is_monster(glyph)
3293 && (mtmp = m_at(x, y)) != 0
3294 && (mtmp->mtame || (mtmp->mpeaceful && flags.confirm)))
3295 continue;
3296 if (glyph_is_poleable(glyph)
3297 && (!glyph_is_statue(glyph) || impaired)) {
3298 if (mpos.x)
3299 return FALSE; /* more than one candidate location */
3300 mpos.x = x, mpos.y = y;
3304 if (!mpos.x)
3305 return FALSE; /* no candidate location */
3306 *pos = mpos;
3307 return TRUE;
3310 staticfn boolean
3311 get_valid_polearm_position(coordxy x, coordxy y)
3313 int glyph;
3315 glyph = glyph_at(x, y);
3317 return (isok(x, y) && distu(x, y) >= gp.polearm_range_min
3318 && distu(x, y) <= gp.polearm_range_max
3319 && (cansee(x, y) || (couldsee(x, y)
3320 && glyph_is_poleable(glyph))));
3323 staticfn void
3324 display_polearm_positions(boolean on_off)
3326 coordxy x, y, dx, dy;
3328 if (on_off) {
3329 /* on */
3330 tmp_at(DISP_BEAM, cmap_to_glyph(S_goodpos));
3331 for (dx = -3; dx <= 3; dx++)
3332 for (dy = -3; dy <= 3; dy++) {
3333 x = dx + (int) u.ux;
3334 y = dy + (int) u.uy;
3335 if (get_valid_polearm_position(x, y)) {
3336 tmp_at(x, y);
3339 } else {
3340 /* off */
3341 tmp_at(DISP_END, 0);
3346 * Calculate allowable range (pole's reach is always 2 steps):
3347 * unskilled and basic: orthogonal direction, 4..4;
3348 * skilled: as basic, plus knight's jump position, 4..5;
3349 * expert: as skilled, plus diagonal, 4..8.
3350 * ...9...
3351 * .85458.
3352 * .52125.
3353 * 9410149
3354 * .52125.
3355 * .85458.
3356 * ...9...
3357 * (Note: no roles in NetHack can become expert or better
3358 * for polearm skill; Yeoman in slash'em can become expert.)
3360 staticfn void
3361 calc_pole_range(int *min_range, int *max_range)
3363 int typ = uwep_skill_type();
3365 *min_range = 4;
3366 if (typ == P_NONE || P_SKILL(typ) <= P_BASIC)
3367 *max_range = 4;
3368 else if (P_SKILL(typ) == P_SKILLED)
3369 *max_range = 5;
3370 else
3371 *max_range = 8; /* (P_SKILL(typ) >= P_EXPERT) */
3373 gp.polearm_range_min = *min_range;
3374 gp.polearm_range_max = *max_range;
3378 /* return TRUE if hero is wielding a polearm and there's
3379 at least one monster they could hit with it */
3380 boolean
3381 could_pole_mon(void)
3383 int min_range, max_range;
3384 coord cc;
3385 struct monst *hitm = svc.context.polearm.hitmon;
3387 if (!uwep || !is_pole(uwep))
3388 return FALSE;
3390 calc_pole_range(&min_range, &max_range);
3392 cc.x = u.ux;
3393 cc.y = u.uy;
3394 if (!find_poleable_mon(&cc, min_range, max_range)) {
3395 if (hitm && !DEADMONSTER(hitm) && sensemon(hitm)
3396 && mdistu(hitm) <= max_range && mdistu(hitm) >= min_range)
3397 return TRUE;
3398 } else {
3399 return TRUE;
3401 return FALSE;
3404 /* Distance attacks by pole-weapons */
3406 use_pole(struct obj *obj, boolean autohit)
3408 const char thump[] = "Thump! Your blow bounces harmlessly off the %s.";
3409 int res = ECMD_OK, max_range, min_range, glyph;
3410 coord cc;
3411 struct monst *mtmp;
3412 struct monst *hitm = svc.context.polearm.hitmon;
3414 /* Are you allowed to use the pole? */
3415 if (u.uswallow) {
3416 pline(not_enough_room);
3417 return ECMD_OK;
3419 if (obj != uwep) {
3420 if (wield_tool(obj, "swing")) {
3421 cmdq_add_ec(CQ_CANNED, doapply);
3422 cmdq_add_key(CQ_CANNED, obj->invlet);
3423 return ECMD_TIME;
3425 return ECMD_OK;
3427 /* assert(obj == uwep); */
3429 calc_pole_range(&min_range, &max_range);
3431 /* Prompt for a location */
3432 if (!autohit)
3433 pline(where_to_hit);
3434 cc.x = u.ux;
3435 cc.y = u.uy;
3436 if (!find_poleable_mon(&cc, min_range, max_range) && hitm
3437 && !DEADMONSTER(hitm) && sensemon(hitm)
3438 && mdistu(hitm) <= max_range && mdistu(hitm) >= min_range) {
3439 cc.x = hitm->mx;
3440 cc.y = hitm->my;
3442 if (!autohit) {
3443 getpos_sethilite(display_polearm_positions,
3444 get_valid_polearm_position);
3445 if (getpos(&cc, TRUE, "the spot to hit") < 0)
3446 /* ESC; uses turn iff polearm became wielded */
3447 return (res | ECMD_CANCEL);
3450 glyph = glyph_at(cc.x, cc.y);
3451 if (distu(cc.x, cc.y) > max_range) {
3452 pline("Too far!");
3453 return ECMD_FAIL;
3454 } else if (distu(cc.x, cc.y) < min_range) {
3455 if (autohit && u_at(cc.x, cc.y))
3456 pline("Don't know what to hit.");
3457 else
3458 pline("Too close!");
3459 return ECMD_FAIL;
3460 } else if (!cansee(cc.x, cc.y) && !glyph_is_poleable(glyph)) {
3461 You(cant_see_spot);
3462 return ECMD_FAIL;
3463 } else if (!couldsee(cc.x, cc.y)) { /* Eyes of the Overworld */
3464 You(cant_reach);
3465 return ECMD_FAIL;
3468 svc.context.polearm.hitmon = (struct monst *) 0;
3469 /* Attack the monster there */
3470 gb.bhitpos = cc;
3471 if ((mtmp = m_at(gb.bhitpos.x, gb.bhitpos.y)) != (struct monst *) 0) {
3472 if (attack_checks(mtmp, uwep)) /* can attack proceed? */
3473 /* no, abort the attack attempt; result depends on
3474 res: 1 => polearm became wielded, 0 => already wielded;
3475 svc.context.move: 1 => discovered hidden monster at target spot,
3476 0 => answered 'n' to "Really attack?" prompt */
3477 return res | (svc.context.move ? ECMD_TIME : ECMD_OK);
3478 if (overexertion())
3479 return ECMD_TIME; /* burn nutrition; maybe pass out */
3480 svc.context.polearm.hitmon = mtmp;
3481 check_caitiff(mtmp);
3482 gn.notonhead = (gb.bhitpos.x != mtmp->mx || gb.bhitpos.y != mtmp->my);
3483 (void) thitmonst(mtmp, uwep);
3484 } else if (glyph_is_statue(glyph) /* might be hallucinatory */
3485 && sobj_at(STATUE, gb.bhitpos.x, gb.bhitpos.y)) {
3486 struct trap *t = t_at(gb.bhitpos.x, gb.bhitpos.y);
3488 if (t && t->ttyp == STATUE_TRAP
3489 && activate_statue_trap(t, t->tx, t->ty, FALSE)) {
3490 ; /* feedback has been give by animate_statue() */
3491 } else {
3492 /* Since statues look like monsters now, we say something
3493 different from "you miss" or "there's nobody there".
3494 Note: we only do this when a statue is displayed here,
3495 because the player is probably attempting to attack it;
3496 other statues obscured by anything are just ignored. */
3497 pline(thump, "statue");
3498 wake_nearto(gb.bhitpos.x, gb.bhitpos.y, 25);
3500 } else {
3501 /* no monster here and no statue seen or remembered here */
3502 (void) unmap_invisible(gb.bhitpos.x, gb.bhitpos.y);
3504 if (glyph_to_obj(glyph) == BOULDER
3505 && sobj_at(BOULDER, gb.bhitpos.x, gb.bhitpos.y)) {
3506 pline(thump, "boulder");
3507 wake_nearto(gb.bhitpos.x, gb.bhitpos.y, 25);
3508 } else if (!accessible(gb.bhitpos.x, gb.bhitpos.y)
3509 || IS_FURNITURE(levl[gb.bhitpos.x][gb.bhitpos.y].typ)) {
3510 /* similar to 'F'orcefight with a melee weapon; we know that
3511 the spot can be seen or we wouldn't have gotten this far */
3512 You("uselessly attack %s.",
3513 (levl[gb.bhitpos.x][gb.bhitpos.y].typ == STONE
3514 || levl[gb.bhitpos.x][gb.bhitpos.y].typ == SCORR)
3515 ? "stone"
3516 : glyph_is_cmap(glyph)
3517 ? the(defsyms[glyph_to_cmap(glyph)].explanation)
3518 : (const char *) "an unknown obstacle");
3519 } else {
3520 You("miss; there is no one there to hit.");
3523 u_wipe_engr(2); /* same as for melee or throwing */
3524 return ECMD_TIME;
3527 #undef glyph_is_poleable
3529 staticfn int
3530 use_cream_pie(struct obj *obj)
3532 boolean wasblind = Blind;
3533 boolean wascreamed = u.ucreamed;
3534 boolean several = FALSE;
3536 if (obj->quan > 1L) {
3537 several = TRUE;
3538 obj = splitobj(obj, 1L);
3540 if (Hallucination)
3541 You("give yourself a facial.");
3542 else
3543 You("immerse your %s in %s%s.", body_part(FACE),
3544 several ? "one of " : "",
3545 several ? makeplural(the(xname(obj))) : the(xname(obj)));
3546 if (can_blnd((struct monst *) 0, &gy.youmonst, AT_WEAP, obj)) {
3547 int blindinc = rnd(25);
3549 u.ucreamed += blindinc;
3550 make_blinded(BlindedTimeout + (long) blindinc, FALSE);
3551 if (!Blind || (Blind && wasblind))
3552 pline("There's %ssticky goop all over your %s.",
3553 wascreamed ? "more " : "", body_part(FACE));
3554 else /* Blind && !wasblind */
3555 You_cant("see through all the sticky goop on your %s.",
3556 body_part(FACE));
3559 setnotworn(obj);
3560 /* useup() is appropriate, but we want costly_alteration()'s message */
3561 costly_alteration(obj, COST_SPLAT);
3562 obj_extract_self(obj);
3563 delobj(obj);
3564 return ECMD_OK;
3567 /* getobj callback for object to rub royal jelly on */
3568 staticfn int
3569 jelly_ok(struct obj *obj)
3571 if (obj && obj->otyp == EGG)
3572 return GETOBJ_SUGGEST;
3574 return GETOBJ_EXCLUDE;
3577 staticfn int
3578 use_royal_jelly(struct obj **optr)
3580 int oldcorpsenm;
3581 unsigned was_timed;
3582 struct obj *eobj, *obj = *optr;
3583 boolean splitit = (obj->quan > 1L);
3585 if (splitit)
3586 obj = splitobj(obj, 1L);
3587 /* remove from inventory so that it won't be offered as a choice
3588 to rub on itself */
3589 freeinv(obj);
3591 /* right now you can rub one royal jelly on an entire stack of eggs */
3592 eobj = getobj("rub the royal jelly on", jelly_ok, GETOBJ_PROMPT);
3593 if (!eobj) {
3594 if (splitit) {
3595 (void) unsplitobj(obj);
3596 update_inventory(); /* freeinv() updated perminv w/ obj omitted */
3597 } else {
3598 /* this lump was already separate; pervent merge */
3599 addinv_nomerge(obj); /* put unused lump back; updates perminv */
3601 return ECMD_CANCEL;
3604 You("smear royal jelly all over %s.", yname(eobj));
3605 if (eobj->otyp != EGG) {
3606 pline1(nothing_happens);
3607 goto useup_jelly;
3610 oldcorpsenm = eobj->corpsenm;
3611 if (eobj->corpsenm == PM_KILLER_BEE)
3612 eobj->corpsenm = PM_QUEEN_BEE;
3614 if (obj->cursed) {
3615 if (eobj->timed || eobj->corpsenm != oldcorpsenm)
3616 pline("The %s %s feebly.", xname(eobj), otense(eobj, "quiver"));
3617 else
3618 pline("%s", nothing_seems_to_happen);
3619 kill_egg(eobj);
3620 goto useup_jelly;
3623 was_timed = eobj->timed;
3624 if (eobj->corpsenm != NON_PM) {
3625 if (!eobj->timed)
3626 attach_egg_hatch_timeout(eobj, 0L);
3627 /* blessed royal jelly will make the hatched creature think
3628 you're the parent - but has no effect if you laid the egg */
3629 if (obj->blessed && !eobj->spe)
3630 eobj->spe = 2;
3633 if ((eobj->timed && !was_timed) || eobj->spe == 2
3634 || eobj->corpsenm != oldcorpsenm)
3635 pline("The %s %s briefly.", xname(eobj), otense(eobj, "quiver"));
3636 else
3637 pline("%s", nothing_seems_to_happen);
3639 useup_jelly:
3640 /* not useup() because we've already done freeinv() */
3641 setnotworn(obj);
3642 obfree(obj, (struct obj *) 0);
3643 *optr = 0;
3644 return ECMD_TIME;
3647 staticfn int
3648 grapple_range(void)
3650 int typ = uwep_skill_type();
3651 int max_range = 4;
3653 if (typ == P_NONE || P_SKILL(typ) <= P_BASIC)
3654 max_range = 4;
3655 else if (P_SKILL(typ) == P_SKILLED)
3656 max_range = 5;
3657 else
3658 max_range = 8;
3659 return max_range;
3662 staticfn boolean
3663 can_grapple_location(coordxy x, coordxy y)
3665 return (isok(x, y) && cansee(x, y) && distu(x, y) <= grapple_range());
3668 staticfn void
3669 display_grapple_positions(boolean on_off)
3671 coordxy x, y, dx, dy;
3673 if (on_off) {
3674 /* on */
3675 tmp_at(DISP_BEAM, cmap_to_glyph(S_goodpos));
3676 for (dx = -3; dx <= 3; dx++)
3677 for (dy = -3; dy <= 3; dy++) {
3678 x = dx + (int) u.ux;
3679 y = dy + (int) u.uy;
3680 if (can_grapple_location(x, y) && !u_at(x, y)) {
3681 tmp_at(x, y);
3684 } else {
3685 /* off */
3686 tmp_at(DISP_END, 0);
3690 staticfn int
3691 use_grapple(struct obj *obj)
3693 int res = ECMD_OK, typ, tohit;
3694 boolean save_confirm;
3695 coord cc;
3696 struct monst *mtmp;
3697 struct obj *otmp;
3699 /* Are you allowed to use the hook? */
3700 if (u.uswallow) {
3701 pline(not_enough_room);
3702 return ECMD_OK;
3704 if (obj != uwep) {
3705 /* "cast": grappling hook evolved from slash'em's fishing pole */
3706 if (wield_tool(obj, "cast")) {
3707 cmdq_add_ec(CQ_CANNED, doapply);
3708 cmdq_add_key(CQ_CANNED, obj->invlet);
3709 return ECMD_TIME;
3711 return ECMD_OK;
3713 /* assert(obj == uwep); */
3715 /* Prompt for a location */
3716 pline(where_to_hit);
3717 cc.x = u.ux;
3718 cc.y = u.uy;
3719 getpos_sethilite(display_grapple_positions, can_grapple_location);
3720 if (getpos(&cc, TRUE, "the spot to hit") < 0)
3721 /* ESC; uses turn iff grapnel became wielded */
3722 return (res | ECMD_CANCEL);
3724 /* Calculate range; unlike use_pole(), there's no minimum for range */
3725 typ = uwep_skill_type();
3726 if (distu(cc.x, cc.y) > grapple_range()) {
3727 pline("Too far!");
3728 return res;
3729 } else if (!cansee(cc.x, cc.y)) {
3730 You(cant_see_spot);
3731 return res;
3732 } else if (!couldsee(cc.x, cc.y)) { /* Eyes of the Overworld */
3733 You(cant_reach);
3734 return res;
3737 /* What do you want to hit? */
3738 tohit = rn2(5);
3739 if (typ != P_NONE && P_SKILL(typ) >= P_SKILLED) {
3740 winid tmpwin = create_nhwindow(NHW_MENU);
3741 anything any;
3742 char buf[BUFSZ];
3743 menu_item *selected;
3744 int clr = NO_COLOR;
3746 any = cg.zeroany; /* set all bits to zero */
3747 any.a_int = 1; /* use index+1 (can't use 0) as identifier */
3748 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
3749 any.a_int++;
3750 Sprintf(buf, "an object on the %s", surface(cc.x, cc.y));
3751 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE,
3752 clr, buf, MENU_ITEMFLAGS_NONE);
3753 any.a_int++;
3754 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE,
3755 clr, "a monster", MENU_ITEMFLAGS_NONE);
3756 any.a_int++;
3757 Sprintf(buf, "the %s", surface(cc.x, cc.y));
3758 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr,
3759 buf, MENU_ITEMFLAGS_NONE);
3760 end_menu(tmpwin, "Aim for what?");
3761 tohit = rn2(4);
3762 if (select_menu(tmpwin, PICK_ONE, &selected) > 0
3763 && rn2(P_SKILL(typ) > P_SKILLED ? 20 : 2))
3764 tohit = selected[0].item.a_int - 1;
3765 free((genericptr_t) selected);
3766 destroy_nhwindow(tmpwin);
3769 /* possibly scuff engraving at your feet;
3770 any engraving at the target location is unaffected */
3771 if (tohit == 2 || !rn2(2))
3772 u_wipe_engr(rnd(2));
3774 /* What did you hit? */
3775 switch (tohit) {
3776 case 0: /* Trap */
3777 /* FIXME -- untrap needs to deal with non-adjacent traps */
3778 break;
3779 case 1: /* Object */
3780 if ((otmp = svl.level.objects[cc.x][cc.y]) != 0) {
3781 You("snag an object from the %s!", surface(cc.x, cc.y));
3782 (void) pickup_object(otmp, 1L, FALSE);
3783 /* If pickup fails, leave it alone */
3784 newsym(cc.x, cc.y);
3785 return ECMD_TIME;
3787 break;
3788 case 2: /* Monster */
3789 gb.bhitpos = cc;
3790 if ((mtmp = m_at(cc.x, cc.y)) == (struct monst *) 0)
3791 break;
3792 gn.notonhead = (gb.bhitpos.x != mtmp->mx || gb.bhitpos.y != mtmp->my);
3793 save_confirm = flags.confirm;
3794 if (verysmall(mtmp->data) && !rn2(4)
3795 && enexto(&cc, u.ux, u.uy, (struct permonst *) 0)) {
3796 flags.confirm = FALSE;
3797 (void) attack_checks(mtmp, uwep);
3798 flags.confirm = save_confirm;
3799 check_caitiff(mtmp); /* despite fact there's no damage */
3800 You("pull in %s!", mon_nam(mtmp));
3801 mtmp->mundetected = 0;
3802 rloc_to(mtmp, cc.x, cc.y);
3803 return ECMD_TIME;
3804 } else if ((!bigmonst(mtmp->data) && !strongmonst(mtmp->data))
3805 || rn2(4)) {
3806 flags.confirm = FALSE;
3807 (void) attack_checks(mtmp, uwep);
3808 flags.confirm = save_confirm;
3809 check_caitiff(mtmp);
3810 (void) thitmonst(mtmp, uwep);
3811 return ECMD_TIME;
3813 FALLTHROUGH;
3814 /*FALLTHRU*/
3815 case 3: /* Surface */
3816 if (IS_AIR(levl[cc.x][cc.y].typ) || is_pool(cc.x, cc.y))
3817 pline_The("hook slices through the %s.", surface(cc.x, cc.y));
3818 else {
3819 You("are yanked toward the %s!", surface(cc.x, cc.y));
3820 hurtle(sgn(cc.x - u.ux), sgn(cc.y - u.uy), 1, FALSE);
3821 spoteffects(TRUE);
3823 return ECMD_TIME;
3824 default: /* Yourself (oops!) */
3825 if (P_SKILL(typ) <= P_BASIC) {
3826 You("hook yourself!");
3827 losehp(Maybe_Half_Phys(rn1(10, 10)), "a grappling hook",
3828 KILLED_BY);
3829 return ECMD_TIME;
3831 break;
3833 pline1(nothing_happens);
3834 return ECMD_TIME;
3837 staticfn void
3838 discard_broken_wand(void)
3840 struct obj *obj;
3842 obj = gc.current_wand; /* [see dozap() and destroy_items()] */
3843 gc.current_wand = 0;
3844 if (obj)
3845 delobj(obj);
3846 nomul(0);
3849 staticfn void
3850 broken_wand_explode(struct obj *obj, int dmg, int expltype)
3852 explode(u.ux, u.uy, -(obj->otyp), dmg, WAND_CLASS, expltype);
3853 makeknown(obj->otyp); /* explode describes the effect */
3854 discard_broken_wand();
3857 /* if x,y has lava or water, dunk any boulders at that location into it */
3858 void
3859 maybe_dunk_boulders(coordxy x, coordxy y)
3861 struct obj *otmp;
3863 while (is_pool_or_lava(x, y) && (otmp = sobj_at(BOULDER, x, y)) != 0) {
3864 obj_extract_self(otmp);
3865 (void) boulder_hits_pool(otmp, x,y, FALSE);
3869 /* return 1 if the wand is broken, hence some time elapsed */
3870 staticfn int
3871 do_break_wand(struct obj *obj)
3873 #define BY_OBJECT ((struct monst *) 0)
3874 static const char nothing_else_happens[] = "But nothing else happens...";
3875 int i;
3876 coordxy x, y;
3877 struct monst *mon;
3878 int dmg, damage;
3879 boolean affects_objects;
3880 boolean shop_damage = FALSE;
3881 boolean fillmsg = FALSE;
3882 char confirm[QBUFSZ], buf[BUFSZ];
3883 boolean is_fragile = (objdescr_is(obj, "balsa")
3884 || objdescr_is(obj, "glass"));
3886 if (nohands(gy.youmonst.data)) {
3887 You_cant("break %s without hands!", yname(obj));
3888 return ECMD_OK;
3889 } else if (!freehand()) {
3890 Your("%s are occupied!", makeplural(body_part(HAND)));
3891 return ECMD_OK;
3892 } else if (ACURR(A_STR) < (is_fragile ? 5 : 10)) {
3893 You("don't have the strength to break %s!", yname(obj));
3894 return ECMD_OK;
3896 if (!paranoid_query(ParanoidBreakwand,
3897 safe_qbuf(confirm,
3898 "Are you really sure you want to break ",
3899 "?", obj, yname, ysimple_name, "the wand")))
3900 return ECMD_OK;
3901 pline("Raising %s high above your %s, you %s it in two!", yname(obj),
3902 body_part(HEAD), is_fragile ? "snap" : "break");
3904 /* [ALI] Do this first so that wand is removed from bill. Otherwise,
3905 * the freeinv() below also hides it from setpaid() which causes problems.
3907 if (obj->unpaid) {
3908 check_unpaid(obj); /* Extra charge for use */
3909 costly_alteration(obj, COST_DSTROY);
3912 gc.current_wand = obj; /* destroy_items might reset this */
3913 freeinv(obj); /* hide it from destroy_items instead... */
3914 setnotworn(obj); /* so we need to do this ourselves */
3916 if (!zappable(obj)) {
3917 pline(nothing_else_happens);
3918 discard_broken_wand();
3919 return ECMD_TIME;
3921 /* successful call to zappable() consumes a charge; put it back */
3922 obj->spe++;
3923 /* might have "wrested" a final charge, taking it from 0 to -1;
3924 if so, we just brought it back up to 0, which wouldn't do much
3925 below so give it 1..3 charges now, usually making it stronger
3926 than an ordinary last charge (the wand is already gone from
3927 inventory, so perm_invent can't accidentally reveal this) */
3928 if (!obj->spe)
3929 obj->spe = rnd(3);
3931 obj->ox = u.ux;
3932 obj->oy = u.uy;
3933 dmg = obj->spe * 4;
3934 affects_objects = FALSE;
3936 switch (obj->otyp) {
3937 case WAN_OPENING:
3938 if (u.ustuck) {
3939 release_hold();
3940 if (obj->dknown)
3941 makeknown(WAN_OPENING);
3942 discard_broken_wand();
3943 return ECMD_TIME;
3945 FALLTHROUGH;
3946 /*FALLTHRU*/
3947 case WAN_WISHING:
3948 case WAN_NOTHING:
3949 case WAN_LOCKING:
3950 case WAN_PROBING:
3951 case WAN_ENLIGHTENMENT:
3952 case WAN_SECRET_DOOR_DETECTION:
3953 pline(nothing_else_happens);
3954 discard_broken_wand();
3955 return ECMD_TIME;
3956 case WAN_DEATH:
3957 case WAN_LIGHTNING:
3958 broken_wand_explode(obj, dmg * 4, EXPL_MAGICAL);
3959 return ECMD_TIME;
3960 case WAN_FIRE:
3961 broken_wand_explode(obj, dmg * 2, EXPL_FIERY);
3962 return ECMD_TIME;
3963 case WAN_COLD:
3964 broken_wand_explode(obj, dmg * 2, EXPL_FROSTY);
3965 return ECMD_TIME;
3966 case WAN_MAGIC_MISSILE:
3967 broken_wand_explode(obj, dmg, EXPL_MAGICAL);
3968 return ECMD_TIME;
3969 case WAN_STRIKING:
3970 /* we want this before the explosion instead of at the very end */
3971 Soundeffect(se_wall_of_force, 65);
3972 pline("A wall of force smashes down around you!");
3973 dmg = d(1 + obj->spe, 6); /* normally 2d12 */
3974 FALLTHROUGH;
3975 /*FALLTHRU*/
3976 case WAN_CANCELLATION:
3977 case WAN_POLYMORPH:
3978 case WAN_TELEPORTATION:
3979 case WAN_UNDEAD_TURNING:
3980 affects_objects = TRUE;
3981 break;
3982 default:
3983 break;
3986 /* magical explosion and its visual effect occur before specific effects
3988 /* [TODO? This really ought to prevent the explosion from being
3989 fatal so that we never leave a bones file where none of the
3990 surrounding targets (or underlying objects) got affected yet.] */
3991 explode(obj->ox, obj->oy, -(obj->otyp), rnd(dmg), WAND_CLASS,
3992 EXPL_MAGICAL);
3994 /* prepare for potential feedback from polymorph... */
3995 zapsetup();
3997 /* this makes it hit us last, so that we can see the action first */
3998 for (i = 0; i <= N_DIRS; i++) {
3999 gb.bhitpos.x = x = obj->ox + xdir[i];
4000 gb.bhitpos.y = y = obj->oy + ydir[i];
4001 if (!isok(x, y))
4002 continue;
4004 if (obj->otyp == WAN_DIGGING) {
4005 schar typ;
4006 enum digcheck_result dcres = dig_check(BY_OBJECT, x, y);
4008 if (dcres < DIGCHECK_FAILED || dcres == DIGCHECK_FAIL_BOULDER) {
4009 if (IS_WALL(levl[x][y].typ) || IS_DOOR(levl[x][y].typ)) {
4010 /* normally, pits and holes don't anger guards, but they
4011 * do if it's a wall or door that's being dug */
4012 watch_dig((struct monst *) 0, x, y, TRUE);
4013 if (*in_rooms(x, y, SHOPBASE))
4014 shop_damage = TRUE;
4017 * Let liquid flow into the newly created pits.
4018 * Adjust corresponding code in music.c for
4019 * drum of earthquake if you alter this sequence.
4021 typ = fillholetyp(x, y, FALSE);
4022 if (typ != ROOM) {
4023 levl[x][y].typ = typ, levl[x][y].flags = 0;
4024 liquid_flow(x, y, typ, t_at(x, y),
4025 fillmsg
4026 ? (char *) 0
4027 : "Some holes are quickly filled with %s!");
4028 fillmsg = TRUE;
4029 } else {
4030 digactualhole(x, y, BY_OBJECT,
4031 (rn2(obj->spe) < 3
4032 || (!Can_dig_down(&u.uz)
4033 && !levl[x][y].candig)) ? PIT : HOLE);
4036 fill_pit(x, y);
4037 maybe_dunk_boulders(x, y);
4038 recalc_block_point(x, y);
4039 continue;
4040 } else if (obj->otyp == WAN_CREATE_MONSTER) {
4041 /* u.ux,u.uy creates it near you--x,y might create it in rock */
4042 (void) makemon((struct permonst *) 0, u.ux, u.uy, NO_MM_FLAGS);
4043 continue;
4044 } else if (x != u.ux || y != u.uy) {
4046 * Wand breakage is targeting a square adjacent to the hero,
4047 * which might contain a monster or a pile of objects or both.
4048 * Handle objects last; avoids having undead turning raise an
4049 * undead's corpse and then attack resulting undead monster.
4050 * obj->bypass in bhitm() prevents the polymorphing of items
4051 * dropped due to monster's polymorph and prevents undead
4052 * turning that kills an undead from raising resulting corpse.
4054 if ((mon = m_at(x, y)) != 0) {
4055 (void) bhitm(mon, obj);
4056 /* if (disp.botl) bot(); */
4058 if (affects_objects && svl.level.objects[x][y]) {
4059 (void) bhitpile(obj, bhito, x, y, 0);
4060 if (disp.botl)
4061 bot(); /* potion effects */
4063 } else {
4065 * Wand breakage is targeting the hero. Using xdir[]+ydir[]
4066 * deltas for location selection causes this case to happen
4067 * after all the surrounding squares have been handled.
4068 * Process objects first, in case damage is fatal and leaves
4069 * bones, or teleportation sends one or more of the objects to
4070 * same destination as hero (lookhere/autopickup); also avoids
4071 * the polymorphing of gear dropped due to hero's transformation.
4072 * (Unlike with monsters being hit by zaps, we can't rely on use
4073 * of obj->bypass in the zap code to accomplish that last case
4074 * since it's also used by retouch_equipment() for polyself.)
4076 if (affects_objects && svl.level.objects[x][y]) {
4077 (void) bhitpile(obj, bhito, x, y, 0);
4078 if (disp.botl)
4079 bot(); /* potion effects */
4081 damage = zapyourself(obj, FALSE);
4082 if (damage) {
4083 Sprintf(buf, "killed %sself by breaking a wand", uhim());
4084 losehp(Maybe_Half_Phys(damage), buf, NO_KILLER_PREFIX);
4086 if (disp.botl)
4087 bot(); /* blindness */
4091 /* potentially give post zap/break feedback */
4092 zapwrapup();
4094 /* Note: if player fell thru, this call is a no-op.
4095 Damage is handled in digactualhole in that case */
4096 if (shop_damage)
4097 pay_for_damage("dig into", FALSE);
4099 if (obj->otyp == WAN_LIGHT)
4100 litroom(TRUE, obj); /* only needs to be done once */
4102 discard_broken_wand();
4103 return ECMD_TIME;
4104 #undef BY_OBJECT
4107 /* getobj callback for object to apply - this is more complex than most other
4108 * callbacks because there are a lot of appliables */
4109 staticfn int
4110 apply_ok(struct obj *obj)
4112 if (!obj)
4113 return GETOBJ_EXCLUDE;
4115 /* all tools, all wands (breaking), all spellbooks (flipping through -
4116 including blank/novel/Book of the Dead) */
4117 if (obj->oclass == TOOL_CLASS || obj->oclass == WAND_CLASS
4118 || obj->oclass == SPBOOK_CLASS)
4119 return GETOBJ_SUGGEST;
4121 /* applying coins to flip them is a minor easter egg, so do not suggest
4122 coin application to the player */
4123 if (obj->oclass == COIN_CLASS)
4124 return GETOBJ_DOWNPLAY;
4126 /* certain weapons */
4127 if (obj->oclass == WEAPON_CLASS
4128 && (is_pick(obj) || is_axe(obj) || is_pole(obj)
4129 || obj->otyp == BULLWHIP))
4130 return GETOBJ_SUGGEST;
4132 if (obj->oclass == POTION_CLASS) {
4133 /* permit applying unknown potions, but don't suggest them */
4134 if (!obj->dknown || !objects[obj->otyp].oc_name_known)
4135 return GETOBJ_DOWNPLAY;
4137 /* only applicable potion is oil, and it will only be suggested as a
4138 choice when already discovered */
4139 if (obj->otyp == POT_OIL)
4140 return GETOBJ_SUGGEST;
4143 /* certain foods */
4144 if (obj->otyp == CREAM_PIE || obj->otyp == EUCALYPTUS_LEAF
4145 || obj->otyp == LUMP_OF_ROYAL_JELLY)
4146 return GETOBJ_SUGGEST;
4148 if (obj->otyp == BANANA && Hallucination)
4149 return GETOBJ_DOWNPLAY;
4151 if (is_graystone(obj)) {
4152 /* The only case where we don't suggest a gray stone is if we KNOW it
4153 isn't a touchstone. */
4154 if (!obj->dknown)
4155 return GETOBJ_SUGGEST;
4157 if (obj->otyp != TOUCHSTONE
4158 && (objects[TOUCHSTONE].oc_name_known
4159 || objects[obj->otyp].oc_name_known))
4160 return GETOBJ_EXCLUDE_SELECTABLE;
4162 return GETOBJ_SUGGEST;
4165 /* item can't be applied; if picked anyway,
4166 _EXCLUDE would yield "That is a silly thing to apply.",
4167 _EXCLUDE_SELECTABLE yields "Sorry, I don't know how to use that." */
4168 return GETOBJ_EXCLUDE_SELECTABLE;
4171 /* the #apply command, 'a' */
4173 doapply(void)
4175 struct obj *obj;
4176 int res = ECMD_TIME;
4178 if (nohands(gy.youmonst.data)) {
4179 You("aren't able to use or apply tools in your current form.");
4180 return ECMD_OK;
4182 if (check_capacity((char *) 0))
4183 return ECMD_OK;
4185 obj = getobj("use or apply", apply_ok, GETOBJ_NOFLAGS);
4186 if (!obj)
4187 return ECMD_CANCEL;
4189 if (!retouch_object(&obj, FALSE))
4190 return ECMD_TIME; /* evading your grasp costs a turn; just be
4191 grateful that you don't drop it as well */
4193 if (obj->oclass == WAND_CLASS)
4194 return do_break_wand(obj);
4196 if (obj->oclass == SPBOOK_CLASS)
4197 return flip_through_book(obj);
4199 if (obj->oclass == COIN_CLASS)
4200 return flip_coin(obj);
4202 switch (obj->otyp) {
4203 case BLINDFOLD:
4204 case LENSES:
4205 if (obj == ublindf) {
4206 if (!cursed(obj))
4207 Blindf_off(obj);
4208 } else if (!ublindf) {
4209 Blindf_on(obj);
4210 } else {
4211 You("are already %s.",
4212 (ublindf->otyp == TOWEL) ? "covered by a towel"
4213 : (ublindf->otyp == BLINDFOLD) ? "wearing a blindfold"
4214 : "wearing lenses");
4216 break;
4217 case CREAM_PIE:
4218 res = use_cream_pie(obj);
4219 obj = (struct obj *) 0;
4220 break;
4221 case LUMP_OF_ROYAL_JELLY:
4222 res = use_royal_jelly(&obj);
4223 break;
4224 case BULLWHIP:
4225 res = use_whip(obj);
4226 break;
4227 case GRAPPLING_HOOK:
4228 res = use_grapple(obj);
4229 break;
4230 case LARGE_BOX:
4231 case CHEST:
4232 case ICE_BOX:
4233 case SACK:
4234 case BAG_OF_HOLDING:
4235 case OILSKIN_SACK:
4236 res = use_container(&obj, TRUE, FALSE);
4237 break;
4238 case BAG_OF_TRICKS:
4239 (void) bagotricks(obj, FALSE, (int *) 0);
4240 break;
4241 case CAN_OF_GREASE:
4242 res = use_grease(obj);
4243 break;
4244 case LOCK_PICK:
4245 case CREDIT_CARD:
4246 case SKELETON_KEY:
4247 res = (pick_lock(obj, 0, 0, NULL) != 0) ? ECMD_TIME : ECMD_OK;
4248 break;
4249 case PICK_AXE:
4250 case DWARVISH_MATTOCK:
4251 res = use_pick_axe(obj);
4252 break;
4253 case TINNING_KIT:
4254 use_tinning_kit(obj);
4255 break;
4256 case LEASH:
4257 res = use_leash(obj);
4258 break;
4259 case SADDLE:
4260 res = use_saddle(obj);
4261 break;
4262 case MAGIC_WHISTLE:
4263 use_magic_whistle(obj);
4264 break;
4265 case TIN_WHISTLE:
4266 use_whistle(obj);
4267 break;
4268 case EUCALYPTUS_LEAF:
4269 /* MRKR: Every Australian knows that a gum leaf makes an excellent
4270 * whistle, especially if your pet is a tame kangaroo named Skippy.
4272 if (obj->blessed) {
4273 use_magic_whistle(obj);
4274 /* sometimes the blessing will be worn off */
4275 if (!rn2(49)) {
4276 if (!Blind) {
4277 pline("%s %s.", Yobjnam2(obj, "glow"), hcolor("brown"));
4278 set_bknown(obj, 1);
4280 unbless(obj);
4282 } else {
4283 use_whistle(obj);
4285 break;
4286 case STETHOSCOPE:
4287 res = use_stethoscope(obj);
4288 break;
4289 case MIRROR:
4290 res = use_mirror(obj);
4291 break;
4292 case BELL:
4293 case BELL_OF_OPENING:
4294 use_bell(&obj);
4295 break;
4296 case CANDELABRUM_OF_INVOCATION:
4297 use_candelabrum(obj);
4298 break;
4299 case WAX_CANDLE:
4300 case TALLOW_CANDLE:
4301 use_candle(&obj);
4302 break;
4303 case OIL_LAMP:
4304 case MAGIC_LAMP:
4305 case BRASS_LANTERN:
4306 use_lamp(obj);
4307 break;
4308 case POT_OIL:
4309 light_cocktail(&obj);
4310 break;
4311 case EXPENSIVE_CAMERA:
4312 res = use_camera(obj);
4313 break;
4314 case TOWEL:
4315 res = use_towel(obj);
4316 break;
4317 case CRYSTAL_BALL:
4318 use_crystal_ball(&obj);
4319 break;
4320 case MAGIC_MARKER:
4321 res = dowrite(obj);
4322 break;
4323 case TIN_OPENER:
4324 res = use_tin_opener(obj);
4325 break;
4326 case FIGURINE:
4327 res = use_figurine(&obj);
4328 break;
4329 case UNICORN_HORN:
4330 use_unicorn_horn(&obj);
4331 break;
4332 case WOODEN_FLUTE:
4333 case MAGIC_FLUTE:
4334 case TOOLED_HORN:
4335 case FROST_HORN:
4336 case FIRE_HORN:
4337 case WOODEN_HARP:
4338 case MAGIC_HARP:
4339 case BUGLE:
4340 case LEATHER_DRUM:
4341 case DRUM_OF_EARTHQUAKE:
4342 res = do_play_instrument(obj);
4343 break;
4344 case HORN_OF_PLENTY: /* not a musical instrument */
4345 (void) hornoplenty(obj, FALSE, (struct obj *) 0);
4346 break;
4347 case LAND_MINE:
4348 case BEARTRAP:
4349 use_trap(obj);
4350 if (go.occupation == set_trap)
4351 obj = (struct obj *) 0; /* not gone yet but behave as if it was */
4352 break;
4353 case FLINT:
4354 case LUCKSTONE:
4355 case LOADSTONE:
4356 case TOUCHSTONE:
4357 res = use_stone(obj);
4358 break;
4359 case BANANA:
4360 if (Hallucination) {
4361 pline("It rings! ... But no-one answers.");
4362 break;
4364 FALLTHROUGH;
4365 /*FALLTHRU*/
4366 default:
4367 /* Pole-weapons can strike at a distance */
4368 if (is_pole(obj)) {
4369 res = use_pole(obj, FALSE);
4370 break;
4371 } else if (is_pick(obj) || is_axe(obj)) {
4372 res = use_pick_axe(obj);
4373 break;
4375 pline("Sorry, I don't know how to use that.");
4376 return ECMD_FAIL;
4378 /* This assumes that anything that potentially destroyed obj has kept
4379 * track of it and set obj to null before this point. */
4380 if (obj && obj->oartifact) {
4381 res |= arti_speak(obj); /* sets ECMD_TIME bit if artifact speaks */
4383 return res;
4386 /* Keep track of unfixable troubles for purposes of messages saying you feel
4387 * great.
4390 unfixable_trouble_count(boolean is_horn)
4392 int unfixable_trbl = 0;
4394 if (Stoned)
4395 unfixable_trbl++;
4396 if (Slimed)
4397 unfixable_trbl++;
4398 if (Strangled)
4399 unfixable_trbl++;
4400 if (ATEMP(A_DEX) < 0 && Wounded_legs)
4401 unfixable_trbl++;
4402 if (ATEMP(A_STR) < 0 && u.uhs >= WEAK)
4403 unfixable_trbl++;
4404 /* lycanthropy is undesirable, but it doesn't actually make you feel bad
4405 so don't count it as a trouble which can't be fixed */
4408 * Unicorn horn can fix these when they're timed but not when
4409 * they aren't. Potion of restore ability doesn't touch them,
4410 * so they're always unfixable for the not-unihorn case.
4411 * [Most of these are timed only, so always curable via horn.
4412 * An exception is Stunned, which can be forced On by certain
4413 * polymorph forms (stalker, bats).]
4415 if (Sick && (!is_horn || (Sick & ~TIMEOUT) != 0L))
4416 unfixable_trbl++;
4417 if (Stunned && (!is_horn || (HStun & ~TIMEOUT) != 0L))
4418 unfixable_trbl++;
4419 if (Confusion && (!is_horn || (HConfusion & ~TIMEOUT) != 0L))
4420 unfixable_trbl++;
4421 if (Hallucination && (!is_horn || (HHallucination & ~TIMEOUT) != 0L))
4422 unfixable_trbl++;
4423 if (Vomiting && (!is_horn || (Vomiting & ~TIMEOUT) != 0L))
4424 unfixable_trbl++;
4425 if (Deaf && (!is_horn || (HDeaf & ~TIMEOUT) != 0L))
4426 unfixable_trbl++;
4428 return unfixable_trbl;
4431 staticfn int
4432 flip_through_book(struct obj *obj)
4434 if (Underwater) {
4435 You("don't want to get the pages even more soggy, do you?");
4436 return ECMD_OK;
4439 You("flip through the pages of %s.", thesimpleoname(obj));
4441 if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
4442 if (!Deaf) {
4443 if (!Hallucination) {
4444 Soundeffect(se_rustling_paper, 50);
4446 You_hear("the pages make an unpleasant %s sound.",
4447 Hallucination ? "chuckling"
4448 : "rustling");
4449 } else if (!Blind) {
4450 You_see("the pages glow faintly %s.", hcolor(NH_RED));
4451 } else {
4452 You_feel("the pages tremble.");
4454 } else if (Blind) {
4455 pline("The pages feel %s.",
4456 Hallucination ? "freshly picked"
4457 : "rough and dry");
4458 } else if (obj->otyp == SPE_BLANK_PAPER) {
4459 pline("This spellbook %s.",
4460 Hallucination ? "doesn't have much of a plot"
4461 : "has nothing written in it");
4462 makeknown(obj->otyp);
4463 } else if (Hallucination) {
4464 You("enjoy the animated initials.");
4465 } else if (obj->otyp == SPE_NOVEL) {
4466 pline("This looks like it might be interesting to read.");
4467 } else {
4468 static const char *const fadeness[] = {
4469 "fresh",
4470 "slightly faded",
4471 "very faded",
4472 "extremely faded",
4473 "barely visible"
4475 int findx = min(obj->spestudied, MAX_SPELL_STUDY);
4477 pline("The%s ink in this spellbook is %s.",
4478 objects[obj->otyp].oc_magic ? " magical" : "",
4479 fadeness[findx]);
4482 return ECMD_TIME;
4485 staticfn int
4486 flip_coin(struct obj *obj)
4488 struct obj *otmp = obj;
4489 boolean lose_coin = FALSE;
4491 You("flip %s.", an(singular(obj, xname)));
4492 if (Underwater) {
4493 pline("It tumbles away.");
4494 lose_coin = TRUE;
4495 } else if (Glib || Fumbling
4496 || (ACURR(A_DEX) < 10 && !rn2(ACURR(A_DEX)))) {
4497 pline("It slips between your %s.", fingers_or_gloves(FALSE));
4498 lose_coin = TRUE;
4501 if (lose_coin) {
4502 if (otmp->quan > 1L)
4503 otmp = splitobj(otmp, 1L);
4504 dropx(otmp);
4505 return ECMD_TIME;
4507 if (Hallucination) {
4508 pline(rn2(100) ? "Wow, a double header!"
4509 /* edge case */
4510 : "The coin miraculously lands on its edge!");
4511 } else {
4512 pline("It comes up %s.", rn2(2) ? "heads" : "tails");
4514 return ECMD_TIME;
4517 /*apply.c*/