1 | /* SCCS Id: @(#)read.c 3.3 2000/03/03 */
2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | #include "hack.h"
6 |
7 | /* KMH -- Copied from pray.c; this really belongs in a header file */
8 | #define DEVOUT 14
9 | #define STRIDENT 4
10 |
11 | #define Your_Own_Role(mndx) \
12 | ((mndx) == urole.malenum || \
13 | (urole.femalenum != NON_PM && (mndx) == urole.femalenum))
14 | #define Your_Own_Race(mndx) \
15 | ((mndx) == urace.malenum || \
16 | (urace.femalenum != NON_PM && (mndx) == urace.femalenum))
17 |
18 | #ifdef OVLB
19 |
20 | /* elven armor vibrates warningly when enchanted beyond a limit */
21 | #define is_elven_armor(optr) ((optr)->otyp == ELVEN_LEATHER_HELM\
22 | || (optr)->otyp == ELVEN_MITHRIL_COAT\
23 | || (optr)->otyp == ELVEN_CLOAK\
24 | || (optr)->otyp == ELVEN_SHIELD\
25 | || (optr)->otyp == ELVEN_BOOTS)
26 |
27 | boolean known;
28 |
29 | static NEARDATA const char readable[] =
30 | { ALL_CLASSES, SCROLL_CLASS, SPBOOK_CLASS, 0 };
31 | static const char all_count[] = { ALLOW_COUNT, ALL_CLASSES, 0 };
32 |
33 | static void FDECL(wand_explode, (struct obj *));
34 | static void NDECL(do_class_genocide);
35 | static void FDECL(stripspe,(struct obj *));
36 | static void FDECL(p_glow1,(struct obj *));
37 | static void FDECL(p_glow2,(struct obj *,const char *));
38 | static void FDECL(randomize,(int *, int));
39 | static void FDECL(forget_single_object, (int));
40 | static void FDECL(forget, (int));
41 |
42 | STATIC_PTR void FDECL(set_lit, (int,int,genericptr_t));
43 |
44 | int
45 | doread()
46 | {
47 | register struct obj *scroll;
48 | register boolean confused;
49 |
50 | known = FALSE;
51 | if(check_capacity((char *)0)) return (0);
52 | scroll = getobj(readable, "read");
53 | if(!scroll) return(0);
54 |
55 | /* outrumor has its own blindness check */
56 | if(scroll->otyp == FORTUNE_COOKIE) {
57 | if(flags.verbose)
58 | You("break up the cookie and throw away the pieces.");
59 | outrumor(bcsign(scroll), BY_COOKIE);
60 | if (!Blind) u.uconduct.literate++;
61 | useup(scroll);
62 | return(1);
63 | #ifdef TOURIST
64 | } else if (scroll->otyp == T_SHIRT) {
65 | char buf[BUFSZ];
66 | int erosion;
67 |
68 | if (Blind) {
69 | You_cant("feel any Braille writing.");
70 | return 0;
71 | }
72 | u.uconduct.literate++;
73 | if(flags.verbose)
74 | pline("It reads:");
75 | Sprintf(buf, "I explored the Dungeons of Doom, %s.",
76 | Hallucination ?
77 | (scroll == uarmu ?
78 | /* (force these two to have identical length) */
79 | "and never did any laundry..." :
80 | "and couldn't find my way out") :
81 | "but all I got was this lousy T-shirt");
82 | erosion = greatest_erosion(scroll);
83 | if (erosion)
84 | wipeout_text(buf,
85 | (int)(strlen(buf) * erosion / (2*MAX_ERODE)),
86 | scroll->o_id ^ (unsigned)u.ubirthday);
87 | pline("\"%s\"", buf);
88 | return 1;
89 | #endif /* TOURIST */
90 | } else if (scroll->oclass != SCROLL_CLASS
91 | && scroll->oclass != SPBOOK_CLASS) {
92 | pline(silly_thing_to, "read");
93 | return(0);
94 | } else if (Blind) {
95 | const char *what = 0;
96 | if (scroll->oclass == SPBOOK_CLASS)
97 | what = "mystic runes";
98 | else if (!scroll->dknown)
99 | what = "formula on the scroll";
100 | if (what) {
101 | pline("Being blind, you cannot read the %s.", what);
102 | return(0);
103 | }
104 | }
105 |
106 | /* Actions required to win the game aren't counted towards conduct */
107 | if (scroll->otyp != SPE_BOOK_OF_THE_DEAD &&
108 | scroll->otyp != SPE_BLANK_PAPER &&
109 | scroll->otyp != SCR_BLANK_PAPER)
110 | u.uconduct.literate++;
111 |
112 | confused = (Confusion != 0);
113 | #ifdef MAIL
114 | if (scroll->otyp == SCR_MAIL) confused = FALSE;
115 | #endif
116 | if(scroll->oclass == SPBOOK_CLASS) {
117 | if(confused) {
118 | You("cannot grasp the meaning of this tome.");
119 | return(0);
120 | } else
121 | return(study_book(scroll));
122 | }
123 | scroll->in_use = TRUE; /* scroll, not spellbook, now being read */
124 | if(scroll->otyp != SCR_BLANK_PAPER) {
125 | if(Blind)
126 | pline("As you pronounce the formula on it, the scroll disappears.");
127 | else
128 | pline("As you read the scroll, it disappears.");
129 | if(confused) {
130 | if (Hallucination)
131 | pline("Being so trippy, you screw up...");
132 | else
133 | pline("Being confused, you mispronounce the magic words...");
134 | }
135 | }
136 | if(!seffects(scroll)) {
137 | if(!objects[scroll->otyp].oc_name_known) {
138 | if(known) {
139 | makeknown(scroll->otyp);
140 | more_experienced(0,10);
141 | } else if(!objects[scroll->otyp].oc_uname)
142 | docall(scroll);
143 | }
144 | if(scroll->otyp != SCR_BLANK_PAPER)
145 | useup(scroll);
146 | else scroll->in_use = FALSE;
147 | }
148 | return(1);
149 | }
150 |
151 | static void
152 | stripspe(obj)
153 | register struct obj *obj;
154 | {
155 | if (obj->blessed) pline(nothing_happens);
156 | else {
157 | if (obj->spe > 0) {
158 | obj->spe = 0;
159 | if (obj->otyp == OIL_LAMP || obj->otyp == BRASS_LANTERN)
160 | obj->age = 0;
161 | Your("%s vibrates briefly.",xname(obj));
162 | } else pline(nothing_happens);
163 | }
164 | }
165 |
166 | static void
167 | p_glow1(otmp)
168 | register struct obj *otmp;
169 | {
170 | Your("%s %s briefly.", xname(otmp),
171 | Blind ? "vibrates" : "glows");
172 | }
173 |
174 | static void
175 | p_glow2(otmp,color)
176 | register struct obj *otmp;
177 | register const char *color;
178 | {
179 | Your("%s %s%s for a moment.",
180 | xname(otmp),
181 | Blind ? "vibrates" : "glows ",
182 | Blind ? (const char *)"" : hcolor(color));
183 | }
184 |
185 | /* Is the object chargeable? For purposes of inventory display; it is */
186 | /* possible to be able to charge things for which this returns FALSE. */
187 | boolean
188 | is_chargeable(obj)
189 | struct obj *obj;
190 | {
191 | if (obj->oclass == WAND_CLASS) return TRUE;
192 | /* known && !uname is possible after amnesia/mind flayer */
193 | if (obj->oclass == RING_CLASS)
194 | return (boolean)(objects[obj->otyp].oc_charged &&
195 | (obj->known || objects[obj->otyp].oc_uname));
196 | if (is_weptool(obj)) /* specific check before general tools */
197 | return FALSE;
198 | if (obj->oclass == TOOL_CLASS)
199 | return (boolean)(objects[obj->otyp].oc_charged);
200 | return FALSE; /* why are weapons/armor considered charged anyway? */
201 | }
202 |
203 | /*
204 | * recharge an object; curse_bless is -1 if the recharging implement
205 | * was cursed, +1 if blessed, 0 otherwise.
206 | */
207 | void
208 | recharge(obj, curse_bless)
209 | struct obj *obj;
210 | int curse_bless;
211 | {
212 | register int n;
213 | boolean is_cursed, is_blessed;
214 |
215 | is_cursed = curse_bless < 0;
216 | is_blessed = curse_bless > 0;
217 |
218 | if (obj->oclass == WAND_CLASS) {
219 | /* undo any prior cancellation, even when is_cursed */
220 | if (obj->spe == -1) obj->spe = 0;
221 |
222 | /*
223 | * Recharging might cause wands to explode.
224 | * v = number of previous recharges
225 | * v = percentage chance to explode on this attempt
226 | * v = cumulative odds for exploding
227 | * 0 : 0 0
228 | * 1 : 0.29 0.29
229 | * 2 : 2.33 2.62
230 | * 3 : 7.87 10.28
231 | * 4 : 18.66 27.02
232 | * 5 : 36.44 53.62
233 | * 6 : 62.97 82.83
234 | * 7 : 100 100
235 | */
236 | n = (int)obj->recharged;
237 | if (n > 0 && (obj->otyp == WAN_WISHING ||
238 | (n * n * n > rn2(7*7*7)))) { /* recharge_limit */
239 | wand_explode(obj);
240 | return;
241 | }
242 | /* didn't explode, so increment the recharge count */
243 | obj->recharged = (unsigned)(n + 1);
244 |
245 | /* now handle the actual recharging */
246 | if (is_cursed) {
247 | stripspe(obj);
248 | } else {
249 | int lim = (obj->otyp == WAN_WISHING) ? 3 :
250 | (objects[obj->otyp].oc_dir != NODIR) ? 8 : 15;
251 |
252 | n = (lim == 3) ? 3 : rn1(5, lim + 1 - 5);
253 | if (!is_blessed) n = rnd(n);
254 |
255 | if (obj->spe < n) obj->spe = n;
256 | else obj->spe++;
257 | if (obj->otyp == WAN_WISHING && obj->spe > 3) {
258 | wand_explode(obj);
259 | return;
260 | }
261 | if (obj->spe >= lim) p_glow2(obj,blue);
262 | else p_glow1(obj);
263 | }
264 |
265 | } else if (obj->oclass == RING_CLASS &&
266 | objects[obj->otyp].oc_charged) {
267 | /* charging does not affect ring's curse/bless status */
268 | int s = is_blessed ? rnd(3) : is_cursed ? -rnd(2) : 1;
269 | boolean is_on = (obj == uleft || obj == uright);
270 |
271 | /* destruction depends on current state, not adjustment */
272 | if (obj->spe > rn2(7) || obj->spe <= -5) {
273 | Your("%s pulsates momentarily, then explodes!",
274 | xname(obj));
275 | if (is_on) Ring_gone(obj);
276 | s = rnd(3 * abs(obj->spe)); /* amount of damage */
277 | useup(obj);
278 | losehp(s, "exploding ring", KILLED_BY_AN);
279 | } else {
280 | long mask = is_on ? (obj == uleft ? LEFT_RING :
281 | RIGHT_RING) : 0L;
282 | Your("%s spins %sclockwise for a moment.",
283 | xname(obj), s < 0 ? "counter" : "");
284 | /* cause attributes and/or properties to be updated */
285 | if (is_on) Ring_off(obj);
286 | obj->spe += s; /* update the ring while it's off */
287 | if (is_on) setworn(obj, mask), Ring_on(obj);
288 | /* oartifact: if a touch-sensitive artifact ring is
289 | ever created the above will need to be revised */
290 | }
291 |
292 | } else if (obj->oclass == TOOL_CLASS) {
293 | int rechrg = (int)obj->recharged;
294 |
295 | if (objects[obj->otyp].oc_charged) {
296 | /* tools don't have a limit, but the counter used does */
297 | if (rechrg < 7) /* recharge_limit */
298 | obj->recharged++;
299 | }
300 | switch(obj->otyp) {
301 | case BELL_OF_OPENING:
302 | if (is_cursed) stripspe(obj);
303 | else if (is_blessed) obj->spe += rnd(3);
304 | else obj->spe += 1;
305 | if (obj->spe > 5) obj->spe = 5;
306 | break;
307 | case MAGIC_MARKER:
308 | case TINNING_KIT:
309 | #ifdef TOURIST
310 | case EXPENSIVE_CAMERA:
311 | #endif
312 | if (is_cursed) stripspe(obj);
313 | else if (rechrg && obj->otyp == MAGIC_MARKER) { /* previously recharged */
314 | obj->recharged = 1; /* override increment done above */
315 | if (obj->spe < 3)
316 | Your("marker seems permanently dried out.");
317 | else
318 | pline(nothing_happens);
319 | } else if (is_blessed) {
320 | n = rn1(10,16); /* 10..25 */
321 | if (obj->spe + n <= 50)
322 | obj->spe = 50;
323 | else if (obj->spe + n <= 75)
324 | obj->spe = 75;
325 | else {
326 | int chrg = (int)obj->spe;
327 | if ((chrg + n) > 127)
328 | obj->spe = 127;
329 | else
330 | obj->spe += n;
331 | }
332 | p_glow2(obj,blue);
333 | } else {
334 | n = rn1(5,10); /* 5..15 */
335 | if (obj->spe + n <= 50)
336 | obj->spe = 50;
337 | else {
338 | int chrg = (int)obj->spe;
339 | if ((chrg + n) > 127)
340 | obj->spe = 127;
341 | else
342 | obj->spe += n;
343 | }
344 | p_glow2(obj,White);
345 | }
346 | break;
347 | case OIL_LAMP:
348 | case BRASS_LANTERN:
349 | if (is_cursed) {
350 | stripspe(obj);
351 | if (obj->lamplit) {
352 | if (!Blind)
353 | pline("%s goes out!", The(xname(obj)));
354 | end_burn(obj, TRUE);
355 | }
356 | } else if (is_blessed) {
357 | obj->spe = 1;
358 | obj->age = 1500;
359 | p_glow2(obj,blue);
360 | } else {
361 | obj->spe = 1;
362 | obj->age += 750;
363 | if (obj->age > 1500) obj->age = 1500;
364 | p_glow1(obj);
365 | }
366 | break;
367 | case CRYSTAL_BALL:
368 | if (is_cursed) stripspe(obj);
369 | else if (is_blessed) {
370 | obj->spe = 6;
371 | p_glow2(obj,blue);
372 | } else {
373 | if (obj->spe < 5) {
374 | obj->spe++;
375 | p_glow1(obj);
376 | } else pline(nothing_happens);
377 | }
378 | break;
379 | case HORN_OF_PLENTY:
380 | case BAG_OF_TRICKS:
381 | case CAN_OF_GREASE:
382 | if (is_cursed) stripspe(obj);
383 | else if (is_blessed) {
384 | if (obj->spe <= 10)
385 | obj->spe += rn1(10, 6);
386 | else obj->spe += rn1(5, 6);
387 | if (obj->spe > 50) obj->spe = 50;
388 | p_glow2(obj,blue);
389 | } else {
390 | obj->spe += rnd(5);
391 | if (obj->spe > 50) obj->spe = 50;
392 | p_glow1(obj);
393 | }
394 | break;
395 | case MAGIC_FLUTE:
396 | case MAGIC_HARP:
397 | case FROST_HORN:
398 | case FIRE_HORN:
399 | case DRUM_OF_EARTHQUAKE:
400 | if (is_cursed) {
401 | stripspe(obj);
402 | } else if (is_blessed) {
403 | obj->spe += d(2,4);
404 | if (obj->spe > 20) obj->spe = 20;
405 | p_glow2(obj,blue);
406 | } else {
407 | obj->spe += rnd(4);
408 | if (obj->spe > 20) obj->spe = 20;
409 | p_glow1(obj);
410 | }
411 | break;
412 | default:
413 | goto not_chargable;
414 | /*NOTREACHED*/
415 | break;
416 | } /* switch */
417 |
418 | } else {
419 | not_chargable:
420 | You("have a feeling of loss.");
421 | }
422 | }
423 |
424 |
425 | /* Forget known information about this object class. */
426 | static void
427 | forget_single_object(obj_id)
428 | int obj_id;
429 | {
430 | objects[obj_id].oc_name_known = 0;
431 | objects[obj_id].oc_pre_discovered = 0; /* a discovery when relearned */
432 | if (objects[obj_id].oc_uname) {
433 | /* this only works if oc_name_known is false */
434 | undiscover_object(obj_id);
435 |
436 | free((genericptr_t)objects[obj_id].oc_uname);
437 | objects[obj_id].oc_uname = 0;
438 | }
439 | /* clear & free object names from matching inventory items too? */
440 | }
441 |
442 |
443 | #if 0 /* here if anyone wants it.... */
444 | /* Forget everything known about a particular object class. */
445 | static void
446 | forget_objclass(oclass)
447 | int oclass;
448 | {
449 | int i;
450 |
451 | for (i=bases[oclass];
452 | i < NUM_OBJECTS && objects[i].oc_class==oclass; i++)
453 | forget_single_object(i);
454 | }
455 | #endif
456 |
457 |
458 | /* randomize the given list of numbers 0 <= i < count */
459 | static void
460 | randomize(indices, count)
461 | int *indices;
462 | int count;
463 | {
464 | int i, iswap, temp;
465 |
466 | for (i = count - 1; i > 0; i--) {
467 | if ((iswap = rn2(i + 1)) == i) continue;
468 | temp = indices[i];
469 | indices[i] = indices[iswap];
470 | indices[iswap] = temp;
471 | }
472 | }
473 |
474 |
475 | /* Forget % of known objects. */
476 | void
477 | forget_objects(percent)
478 | int percent;
479 | {
480 | int i, count;
481 | int indices[NUM_OBJECTS];
482 |
483 | if (percent == 0) return;
484 | if (percent <= 0 || percent > 100) {
485 | impossible("forget_objects: bad percent %d", percent);
486 | return;
487 | }
488 |
489 | for (count = 0, i = 1; i < NUM_OBJECTS; i++)
490 | if (OBJ_DESCR(objects[i]) &&
491 | (objects[i].oc_name_known || objects[i].oc_uname))
492 | indices[count++] = i;
493 |
494 | randomize(indices, count);
495 |
496 | /* forget first % of randomized indices */
497 | count = ((count * percent) + 50) / 100;
498 | for (i = 0; i < count; i++)
499 | forget_single_object(indices[i]);
500 | }
501 |
502 |
503 | /* Forget some or all of map (depends on parameters). */
504 | void
505 | forget_map(howmuch)
506 | int howmuch;
507 | {
508 | register int zx, zy;
509 |
510 | if (In_sokoban(&u.uz))
511 | return;
512 |
513 | known = TRUE;
514 | for(zx = 0; zx < COLNO; zx++) for(zy = 0; zy < ROWNO; zy++)
515 | if (howmuch & ALL_MAP || rn2(7)) {
516 | /* Zonk all memory of this location. */
517 | levl[zx][zy].seenv = 0;
518 | levl[zx][zy].waslit = 0;
519 | levl[zx][zy].glyph = cmap_to_glyph(S_stone);
520 | }
521 | }
522 |
523 | /* Forget all traps on the level. */
524 | void
525 | forget_traps()
526 | {
527 | register struct trap *trap;
528 |
529 | /* forget all traps (except the one the hero is in :-) */
530 | for (trap = ftrap; trap; trap = trap->ntrap)
531 | if ((trap->tx != u.ux || trap->ty != u.uy) && (trap->ttyp != HOLE))
532 | trap->tseen = 0;
533 | }
534 |
535 | /*
536 | * Forget given % of all levels that the hero has visited and not forgotten,
537 | * except this one.
538 | */
539 | void
540 | forget_levels(percent)
541 | int percent;
542 | {
543 | int i, count;
544 | xchar maxl, this_lev;
545 | int indices[MAXLINFO];
546 |
547 | if (percent == 0) return;
548 |
549 | if (percent <= 0 || percent > 100) {
550 | impossible("forget_levels: bad percent %d", percent);
551 | return;
552 | }
553 |
554 | this_lev = ledger_no(&u.uz);
555 | maxl = maxledgerno();
556 |
557 | /* count & save indices of non-forgotten visited levels */
558 | /* Sokoban levels are pre-mapped for the player, and should stay
559 | * so, or they become nearly impossible to solve. But try to
560 | * shift the forgetting elsewhere by fiddling with percent
561 | * instead of forgetting fewer levels.
562 | */
563 | for (count = 0, i = 0; i <= maxl; i++)
564 | if ((level_info[i].flags & VISITED) &&
565 | !(level_info[i].flags & FORGOTTEN) && i != this_lev) {
566 | if (ledger_to_dnum(i) == sokoban_dnum)
567 | percent += 2;
568 | else
569 | indices[count++] = i;
570 | }
571 |
572 | if (percent > 100) percent = 100;
573 |
574 | randomize(indices, count);
575 |
576 | /* forget first % of randomized indices */
577 | count = ((count * percent) + 50) / 100;
578 | for (i = 0; i < count; i++) {
579 | level_info[indices[i]].flags |= FORGOTTEN;
580 | }
581 | }
582 |
583 | /*
584 | * Forget some things (e.g. after reading a scroll of amnesia). When called,
585 | * the following are always forgotten:
586 | *
587 | * - felt ball & chain
588 | * - traps
589 | * - part (6 out of 7) of the map
590 | *
591 | * Other things are subject to flags:
592 | *
593 | * howmuch & ALL_MAP = forget whole map
594 | * howmuch & ALL_SPELLS = forget all spells
595 | */
596 | static void
597 | forget(howmuch)
598 | int howmuch;
599 | {
600 |
601 | if (Punished) u.bc_felt = 0; /* forget felt ball&chain */
602 |
603 | forget_map(howmuch);
604 | forget_traps();
605 |
606 | /* 1 in 3 chance of forgetting some levels */
607 | if (!rn2(3)) forget_levels(rn2(25));
608 |
609 | /* 1 in 3 chance of forgeting some objects */
610 | if (!rn2(3)) forget_objects(rn2(25));
611 |
612 | if (howmuch & ALL_SPELLS) losespells();
613 | /*
614 | * Make sure that what was seen is restored correctly. To do this,
615 | * we need to go blind for an instant --- turn off the display,
616 | * then restart it. All this work is needed to correctly handle
617 | * walls which are stone on one side and wall on the other. Turning
618 | * off the seen bits above will make the wall revert to stone, but
619 | * there are cases where we don't want this to happen. The easiest
620 | * thing to do is to run it through the vision system again, which
621 | * is always correct.
622 | */
623 | docrt(); /* this correctly will reset vision */
624 | }
625 |
626 | int
627 | seffects(sobj)
628 | register struct obj *sobj;
629 | {
630 | register int cval;
631 | register boolean confused = (Confusion != 0);
632 | register struct obj *otmp;
633 |
634 | if (objects[sobj->otyp].oc_magic)
635 | exercise(A_WIS, TRUE); /* just for trying */
636 | switch(sobj->otyp) {
637 | #ifdef MAIL
638 | case SCR_MAIL:
639 | known = TRUE;
640 | if (sobj->spe)
641 | pline("This seems to be junk mail addressed to the finder of the Eye of Larn.");
642 | /* note to the puzzled: the game Larn actually sends you junk
643 | * mail if you win!
644 | */
645 | else readmail(sobj);
646 | break;
647 | #endif
648 | case SCR_ENCHANT_ARMOR:
649 | {
650 | register schar s;
651 | boolean special_armor;
652 | boolean same_color;
653 |
654 | otmp = some_armor(&youmonst);
655 | if(!otmp) {
656 | strange_feeling(sobj,
657 | !Blind ? "Your skin glows then fades." :
658 | "Your skin feels warm for a moment.");
659 | exercise(A_CON, !sobj->cursed);
660 | exercise(A_STR, !sobj->cursed);
661 | return(1);
662 | }
663 | if(confused) {
664 | otmp->oerodeproof = !(sobj->cursed);
665 | if(Blind) {
666 | otmp->rknown = FALSE;
667 | Your("%s feels warm for a moment.",
668 | xname(otmp));
669 | } else {
670 | otmp->rknown = TRUE;
671 | Your("%s is covered by a %s %s %s!",
672 | xname(otmp),
673 | sobj->cursed ? "mottled" : "shimmering",
674 | hcolor(sobj->cursed ? Black : golden),
675 | sobj->cursed ? "glow" :
676 | (is_shield(otmp) ? "layer" : "shield"));
677 | }
678 | if (otmp->oerodeproof && (otmp->oeroded || otmp->oeroded2)) {
679 | otmp->oeroded = otmp->oeroded2 = 0;
680 | Your("%s %s as good as new!",
681 | xname(otmp), Blind ? "feels" : "looks");
682 | }
683 | break;
684 | }
685 | special_armor = is_elven_armor(otmp) ||
686 | (Role_if(PM_WIZARD) && otmp->otyp == CORNUTHAUM);
687 | if (sobj->cursed)
688 | same_color =
689 | (otmp->otyp == BLACK_DRAGON_SCALE_MAIL ||
690 | otmp->otyp == BLACK_DRAGON_SCALES);
691 | else
692 | same_color =
693 | (otmp->otyp == SILVER_DRAGON_SCALE_MAIL ||
694 | otmp->otyp == SILVER_DRAGON_SCALES ||
695 | otmp->otyp == SHIELD_OF_REFLECTION);
696 | if (Blind) same_color = FALSE;
697 |
698 | /* KMH -- catch underflow */
699 | s = sobj->cursed ? -otmp->spe : otmp->spe;
700 | if (s > (special_armor ? 5 : 3) && rn2(s)) {
701 | Your("%s violently %s%s%s for a while, then evaporates.",
702 | xname(otmp),
703 | Blind ? "vibrates" : "glows",
704 | (!Blind && !same_color) ? " " : nul,
705 | (Blind || same_color) ? nul : hcolor(sobj->cursed ? Black : silver));
706 | if(is_cloak(otmp)) (void) Cloak_off();
707 | if(is_boots(otmp)) (void) Boots_off();
708 | if(is_helmet(otmp)) (void) Helmet_off();
709 | if(is_gloves(otmp)) (void) Gloves_off();
710 | if(is_shield(otmp)) (void) Shield_off();
711 | if(otmp == uarm) (void) Armor_gone();
712 | useup(otmp);
713 | break;
714 | }
715 | s = sobj->cursed ? -1 :
716 | otmp->spe >= 9 ? (rn2(otmp->spe) == 0) :
717 | sobj->blessed ? rnd(3-otmp->spe/3) : 1;
718 | if (s >= 0 && otmp->otyp >= GRAY_DRAGON_SCALES &&
719 | otmp->otyp <= YELLOW_DRAGON_SCALES) {
720 | /* dragon scales get turned into dragon scale mail */
721 | Your("%s merges and hardens!", xname(otmp));
722 | setworn((struct obj *)0, W_ARM);
723 | /* assumes same order */
724 | otmp->otyp = GRAY_DRAGON_SCALE_MAIL +
725 | otmp->otyp - GRAY_DRAGON_SCALES;
726 | otmp->cursed = 0;
727 | if (sobj->blessed) {
728 | otmp->spe++;
729 | otmp->blessed = 1;
730 | }
731 | otmp->known = 1;
732 | setworn(otmp, W_ARM);
733 | break;
734 | }
735 | Your("%s %s%s%s%s for a %s.",
736 | xname(otmp),
737 | s == 0 ? "violently " : nul,
738 | Blind ? "vibrates" : "glows",
739 | (!Blind && !same_color) ? " " : nul,
740 | (Blind || same_color) ? nul : hcolor(sobj->cursed ? Black : silver),
741 | (s*s>1) ? "while" : "moment");
742 | otmp->cursed = sobj->cursed;
743 | if (!otmp->blessed || sobj->cursed)
744 | otmp->blessed = sobj->blessed;
745 | if (s) {
746 | otmp->spe += s;
747 | adj_abon(otmp, s);
748 | known = otmp->known;
749 | }
750 |
751 | if ((otmp->spe > (special_armor ? 5 : 3)) &&
752 | (special_armor || !rn2(7)))
753 | Your("%s suddenly vibrates %s.",
754 | xname(otmp),
755 | Blind ? "again" : "unexpectedly");
756 | break;
757 | }
758 | case SCR_DESTROY_ARMOR:
759 | {
760 | otmp = some_armor(&youmonst);
761 | if(confused) {
762 | if(!otmp) {
763 | strange_feeling(sobj,"Your bones itch.");
764 | exercise(A_STR, FALSE);
765 | exercise(A_CON, FALSE);
766 | return(1);
767 | }
768 | otmp->oerodeproof = sobj->cursed;
769 | p_glow2(otmp,purple);
770 | break;
771 | }
772 | if(!sobj->cursed || !otmp || !otmp->cursed) {
773 | if(!destroy_arm(otmp)) {
774 | strange_feeling(sobj,"Your skin itches.");
775 | exercise(A_STR, FALSE);
776 | exercise(A_CON, FALSE);
777 | return(1);
778 | } else
779 | known = TRUE;
780 | } else { /* armor and scroll both cursed */
781 | Your("%s vibrates.", xname(otmp));
782 | if (otmp->spe >= -6) otmp->spe--;
783 | make_stunned(HStun + rn1(10, 10), TRUE);
784 | }
785 | }
786 | break;
787 | case SCR_CONFUSE_MONSTER:
788 | case SPE_CONFUSE_MONSTER:
789 | if(youmonst.data->mlet != S_HUMAN || sobj->cursed) {
790 | if(!HConfusion) You_feel("confused.");
791 | make_confused(HConfusion + rnd(100),FALSE);
792 | } else if(confused) {
793 | if(!sobj->blessed) {
794 | Your("%s begin to %s%s.",
795 | makeplural(body_part(HAND)),
796 | Blind ? "tingle" : "glow ",
797 | Blind ? nul : hcolor(purple));
798 | make_confused(HConfusion + rnd(100),FALSE);
799 | } else {
800 | pline("A %s%s surrounds your %s.",
801 | Blind ? nul : hcolor(red),
802 | Blind ? "faint buzz" : " glow",
803 | body_part(HEAD));
804 | make_confused(0L,TRUE);
805 | }
806 | } else {
807 | if (!sobj->blessed) {
808 | Your("%s%s %s%s.",
809 | makeplural(body_part(HAND)),
810 | Blind ? "" : " begin to glow",
811 | Blind ? (const char *)"tingle" : hcolor(red),
812 | u.umconf ? " even more" : "");
813 | u.umconf++;
814 | } else {
815 | if (Blind)
816 | Your("%s tingle %s sharply.",
817 | makeplural(body_part(HAND)),
818 | u.umconf ? "even more" : "very");
819 | else
820 | Your("%s glow a%s brilliant %s.",
821 | makeplural(body_part(HAND)),
822 | u.umconf ? "n even more" : "",
823 | hcolor(red));
824 | u.umconf += rn1(8, 2);
825 | }
826 | }
827 | break;
828 | case SCR_SCARE_MONSTER:
829 | case SPE_CAUSE_FEAR:
830 | { register int ct = 0;
831 | register struct monst *mtmp;
832 |
833 | for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
834 | if (DEADMONSTER(mtmp)) continue;
835 | if(cansee(mtmp->mx,mtmp->my)) {
836 | if(confused || sobj->cursed) {
837 | mtmp->mflee = mtmp->mfrozen = mtmp->msleeping = 0;
838 | mtmp->mcanmove = 1;
839 | } else
840 | if (! resist(mtmp, sobj->oclass, 0, NOTELL))
841 | mtmp->mflee = 1;
842 | if(!mtmp->mtame) ct++; /* pets don't laugh at you */
843 | }
844 | }
845 | if(!ct)
846 | You_hear("%s in the distance.",
847 | (confused || sobj->cursed) ? "sad wailing" :
848 | "maniacal laughter");
849 | else if(sobj->otyp == SCR_SCARE_MONSTER)
850 | You_hear("%s close by.",
851 | (confused || sobj->cursed) ? "sad wailing" :
852 | "maniacal laughter");
853 | break;
854 | }
855 | case SCR_BLANK_PAPER:
856 | if (Blind)
857 | You("don't remember there being any magic words on this scroll.");
858 | else
859 | pline("This scroll seems to be blank.");
860 | known = TRUE;
861 | break;
862 | case SCR_REMOVE_CURSE:
863 | case SPE_REMOVE_CURSE:
864 | { register struct obj *obj;
865 | if(confused)
866 | if (Hallucination)
867 | You_feel("the power of the Force against you!");
868 | else
869 | You_feel("like you need some help.");
870 | else
871 | if (Hallucination)
872 | You_feel("in touch with the Universal Oneness.");
873 | else
874 | You_feel("like someone is helping you.");
875 |
876 | if(sobj->cursed) pline_The("scroll disintegrates.");
877 | else {
878 | for(obj = invent; obj ; obj = obj->nobj)
879 | if(sobj->blessed || obj->owornmask ||
880 | (obj->otyp == LOADSTONE)) {
881 | if(confused) blessorcurse(obj, 2);
882 | else uncurse(obj);
883 | }
884 | }
885 | if(Punished && !confused) unpunish();
886 | break;
887 | }
888 | case SCR_CREATE_MONSTER:
889 | case SPE_CREATE_MONSTER:
890 | if (create_critters(1 + ((confused || sobj->cursed) ? 12 : 0) +
891 | ((sobj->blessed || rn2(73)) ? 0 : rnd(4)),
892 | confused ? &mons[PM_ACID_BLOB] : (struct permonst *)0))
893 | known = TRUE;
894 | /* no need to flush monsters; we ask for identification only if the
895 | * monsters are not visible
896 | */
897 | break;
898 | case SCR_ENCHANT_WEAPON:
899 | if(uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep))
900 | && confused) {
901 | /* oclass check added 10/25/86 GAN */
902 | uwep->oerodeproof = !(sobj->cursed);
903 | if (Blind) {
904 | uwep->rknown = FALSE;
905 | Your("weapon feels warm for a moment.");
906 | } else {
907 | uwep->rknown = TRUE;
908 | Your("%s covered by a %s %s %s!",
909 | aobjnam(uwep, "are"),
910 | sobj->cursed ? "mottled" : "shimmering",
911 | hcolor(sobj->cursed ? purple : golden),
912 | sobj->cursed ? "glow" : "shield");
913 | }
914 | if (uwep->oerodeproof && (uwep->oeroded || uwep->oeroded2)) {
915 | uwep->oeroded = uwep->oeroded2 = 0;
916 | Your("%s as good as new!",
917 | aobjnam(uwep, Blind ? "feel" : "look"));
918 | }
919 | } else return !chwepon(sobj,
920 | sobj->cursed ? -1 :
921 | !uwep ? 1 :
922 | uwep->spe >= 9 ? (rn2(uwep->spe) == 0) :
923 | sobj->blessed ? rnd(3-uwep->spe/3) : 1);
924 | break;
925 | case SCR_TAMING:
926 | case SPE_CHARM_MONSTER:
927 | { register int i,j;
928 | register int bd = confused ? 5 : 1;
929 | register struct monst *mtmp;
930 |
931 | for(i = -bd; i <= bd; i++) for(j = -bd; j <= bd; j++)
932 | if(isok(u.ux+i, u.uy+j) && (mtmp = m_at(u.ux+i, u.uy+j))) {
933 | if(sobj->cursed) {
934 | setmangry(mtmp);
935 | } else {
936 | if (mtmp->isshk)
937 | make_happy_shk(mtmp, FALSE);
938 | else if (!resist(mtmp, sobj->oclass, 0, NOTELL))
939 | (void) tamedog(mtmp, (struct obj *) 0);
940 | }
941 | }
942 | break;
943 | }
944 | case SCR_GENOCIDE:
945 | You("have found a scroll of genocide!");
946 | known = TRUE;
947 | if (sobj->blessed) do_class_genocide();
948 | else do_genocide(!sobj->cursed | (2 * !!Confusion));
949 | break;
950 | case SCR_LIGHT:
951 | if(!Blind) known = TRUE;
952 | litroom(!confused && !sobj->cursed, sobj);
953 | break;
954 | case SCR_TELEPORTATION:
955 | if(confused || sobj->cursed) level_tele();
956 | else {
957 | if (sobj->blessed && !Teleport_control) {
958 | known = TRUE;
959 | if (yn("Do you wish to teleport?")=='n')
960 | break;
961 | }
962 | tele();
963 | if(Teleport_control || !couldsee(u.ux0, u.uy0) ||
964 | (distu(u.ux0, u.uy0) >= 16))
965 | known = TRUE;
966 | }
967 | break;
968 | case SCR_GOLD_DETECTION:
969 | if (confused || sobj->cursed) return(trap_detect(sobj));
970 | else return(gold_detect(sobj));
971 | case SCR_FOOD_DETECTION:
972 | case SPE_DETECT_FOOD:
973 | if (food_detect(sobj))
974 | return(1); /* nothing detected */
975 | break;
976 | case SPE_IDENTIFY:
977 | cval = rn2(5);
978 | goto id;
979 | case SCR_IDENTIFY:
980 | /* known = TRUE; */
981 | if(confused)
982 | You("identify this as an identify scroll.");
983 | else
984 | pline("This is an identify scroll.");
985 | if (sobj->blessed || (!sobj->cursed && !rn2(5))) {
986 | cval = rn2(5);
987 | /* Note: if rn2(5)==0, identify all items */
988 | if (cval == 1 && sobj->blessed && Luck > 0) ++cval;
989 | } else cval = 1;
990 | useup(sobj);
991 | makeknown(SCR_IDENTIFY);
992 | id:
993 | if(invent && !confused) {
994 | identify_pack(cval);
995 | }
996 | return(1);
997 | case SCR_CHARGING:
998 | if (confused) {
999 | You_feel("charged up!");
1000 | if (u.uen < u.uenmax)
1001 | u.uen = u.uenmax;
1002 | else
1003 | u.uen = (u.uenmax += d(5,4));
1004 | flags.botl = 1;
1005 | break;
1006 | }
1007 | known = TRUE;
1008 | pline("This is a charging scroll.");
1009 | otmp = getobj(all_count, "charge");
1010 | if (!otmp) break;
1011 | recharge(otmp, sobj->cursed ? -1 : (sobj->blessed ? 1 : 0));
1012 | break;
1013 | case SCR_MAGIC_MAPPING:
1014 | if (level.flags.nommap) {
1015 | Your("mind is filled with crazy lines!");
1016 | if (Hallucination)
1017 | pline("Wow! Modern art.");
1018 | else
1019 | Your("%s spins in bewilderment.", body_part(HEAD));
1020 | make_confused(HConfusion + rnd(30), FALSE);
1021 | break;
1022 | }
1023 | if (sobj->blessed) {
1024 | register int x, y;
1025 |
1026 | for (x = 1; x < COLNO; x++)
1027 | for (y = 0; y < ROWNO; y++)
1028 | if (levl[x][y].typ == SDOOR)
1029 | cvt_sdoor_to_door(&levl[x][y]);
1030 | /* do_mapping() already reveals secret passages */
1031 | }
1032 | known = TRUE;
1033 | case SPE_MAGIC_MAPPING:
1034 | if (level.flags.nommap) {
1035 | Your("%s spins as %s blocks the spell!", body_part(HEAD), something);
1036 | make_confused(HConfusion + rnd(30), FALSE);
1037 | break;
1038 | }
1039 | pline("A map coalesces in your mind!");
1040 | cval = (sobj->cursed && !confused);
1041 | if(cval) HConfusion = 1; /* to screw up map */
1042 | do_mapping();
1043 | if(cval) {
1044 | HConfusion = 0; /* restore */
1045 | pline("Unfortunately, you can't grasp the details.");
1046 | }
1047 | break;
1048 | case SCR_AMNESIA:
1049 | known = TRUE;
1050 | forget( (!sobj->blessed ? ALL_SPELLS : 0) |
1051 | (!confused || sobj->cursed ? ALL_MAP : 0) );
1052 | if (Hallucination) /* Ommmmmm! */
1053 | Your("mind releases itself from mundane concerns.");
1054 | else if (!strncmpi(plname, "Maud", 4))
1055 | pline("As your mind turns inward on itself, you forget everything else.");
1056 | else if (rn2(2))
1057 | pline("Who was that Maud person anyway?");
1058 | else
1059 | pline("Thinking of Maud you forget everything else.");
1060 | exercise(A_WIS, FALSE);
1061 | break;
1062 | case SCR_FIRE:
1063 | /*
1064 | * Note: Modifications have been made as of 3.0 to allow for
1065 | * some damage under all potential cases.
1066 | */
1067 | cval = bcsign(sobj);
1068 | useup(sobj);
1069 | makeknown(SCR_FIRE);
1070 | if(confused) {
1071 | if(Fire_resistance) {
1072 | shieldeff(u.ux, u.uy);
1073 | if(!Blind)
1074 | pline("Oh, look, what a pretty fire in your %s.",
1075 | makeplural(body_part(HAND)));
1076 | else You_feel("a pleasant warmth in your %s.",
1077 | makeplural(body_part(HAND)));
1078 | } else {
1079 | pline_The("scroll catches fire and you burn your %s.",
1080 | makeplural(body_part(HAND)));
1081 | losehp(1, "scroll of fire", KILLED_BY_AN);
1082 | }
1083 | return(1);
1084 | }
1085 | if (Underwater)
1086 | pline_The("water around you vaporizes violently!");
1087 | else {
1088 | pline_The("scroll erupts in a tower of flame!");
1089 | burn_away_slime();
1090 | }
1091 | explode(u.ux, u.uy, 11, (2*(rn1(3, 3) + 2 * cval) + 1)/3,
1092 | SCROLL_CLASS);
1093 | return(1);
1094 | case SCR_EARTH:
1095 | /* TODO: handle steeds */
1096 | if (
1097 | #ifdef REINCARNATION
1098 | !Is_rogue_level(&u.uz) &&
1099 | #endif
1100 | (!In_endgame(&u.uz) || Is_earthlevel(&u.uz))) {
1101 | register int x, y;
1102 |
1103 | /* Identify the scroll */
1104 | pline_The("%s rumbles %s you!", ceiling(u.ux,u.uy),
1105 | sobj->blessed ? "around" : "above");
1106 | known = 1;
1107 | if (In_sokoban(&u.uz))
1108 | change_luck(-1); /* Sokoban guilt */
1109 |
1110 | /* Loop through the surrounding squares */
1111 | if (!sobj->cursed) for (x = u.ux-1; x <= u.ux+1; x++) {
1112 | for (y = u.uy-1; y <= u.uy+1; y++) {
1113 |
1114 | /* Is this a suitable spot? */
1115 | if (isok(x, y) && !closed_door(x, y) &&
1116 | !IS_ROCK(levl[x][y].typ) &&
1117 | !IS_AIR(levl[x][y].typ) &&
1118 | (x != u.ux || y != u.uy)) {
1119 | register struct obj *otmp2;
1120 | register struct monst *mtmp;
1121 |
1122 | /* Make the object(s) */
1123 | otmp2 = mksobj(confused ? ROCK : BOULDER,
1124 | FALSE, FALSE);
1125 | if (!otmp2) continue; /* Shouldn't happen */
1126 | otmp2->quan = confused ? rn1(5,2) : 1;
1127 | otmp2->owt = weight(otmp2);
1128 |
1129 | /* Find the monster here (won't be player) */
1130 | mtmp = m_at(x, y);
1131 | if (mtmp && !amorphous(mtmp->data) &&
1132 | !passes_walls(mtmp->data) &&
1133 | !noncorporeal(mtmp->data) &&
1134 | !unsolid(mtmp->data)) {
1135 | struct obj *helmet = which_armor(mtmp, W_ARMH);
1136 | int mdmg;
1137 |
1138 | if (cansee(mtmp->mx, mtmp->my)) {
1139 | pline("%s is hit by %s!", Monnam(mtmp),
1140 | doname(otmp2));
1141 | if (mtmp->minvis && !canspotmon(mtmp))
1142 | map_invisible(mtmp->mx, mtmp->my);
1143 | }
1144 | mdmg = dmgval(otmp2, mtmp) * otmp2->quan;
1145 | if (helmet) {
1146 | if(is_metallic(helmet)) {
1147 | if (canspotmon(mtmp))
1148 | pline("Fortunately, %s is wearing a hard helmet.", mon_nam(mtmp));
1149 | else if (flags.soundok)
1150 | You_hear("a clanging sound.");
1151 | if (mdmg > 2) mdmg = 2;
1152 | } else {
1153 | if (canspotmon(mtmp))
1154 | pline("%s's %s does not protect %s.",
1155 | Monnam(mtmp), xname(helmet),
1156 | him[pronoun_gender(mtmp)]);
1157 | }
1158 | }
1159 | mtmp->mhp -= mdmg;
1160 | if (mtmp->mhp <= 0)
1161 | xkilled(mtmp, 1);
1162 | }
1163 | /* Drop the rock/boulder to the floor */
1164 | if (!flooreffects(otmp2, x, y, "fall")) {
1165 | place_object(otmp2, x, y);
1166 | stackobj(otmp2);
1167 | newsym(x, y); /* map the rock */
1168 | }
1169 | }
1170 | }
1171 | }
1172 | /* Attack the player */
1173 | if (!sobj->blessed) {
1174 | int dmg;
1175 | struct obj *otmp2;
1176 |
1177 | /* Okay, _you_ write this without repeating the code */
1178 | otmp2 = mksobj(confused ? ROCK : BOULDER,
1179 | FALSE, FALSE);
1180 | if (!otmp2) break;
1181 | otmp2->quan = confused ? rn1(5,2) : 1;
1182 | otmp2->owt = weight(otmp2);
1183 | if (!amorphous(youmonst.data) &&
1184 | !Passes_walls &&
1185 | !noncorporeal(youmonst.data) &&
1186 | !unsolid(youmonst.data)) {
1187 | You("are hit by %s!", doname(otmp2));
1188 | dmg = dmgval(otmp2, &youmonst) * otmp2->quan;
1189 | if (uarmh && !sobj->cursed) {
1190 | if(is_metallic(uarmh)) {
1191 | pline("Fortunately, you are wearing a hard helmet.");
1192 | if (dmg > 2) dmg = 2;
1193 | } else if (flags.verbose) {
1194 | Your("%s does not protect you.",
1195 | xname(uarmh));
1196 | }
1197 | }
1198 | } else
1199 | dmg = 0;
1200 | /* Must be before the losehp(), for bones files */
1201 | if (!flooreffects(otmp2, u.ux, u.uy, "fall")) {
1202 | place_object(otmp2, u.ux, u.uy);
1203 | stackobj(otmp2);
1204 | newsym(u.ux, u.uy);
1205 | }
1206 | if (dmg) losehp(dmg, "scroll of earth", KILLED_BY_AN);
1207 | }
1208 | }
1209 | break;
1210 | case SCR_PUNISHMENT:
1211 | known = TRUE;
1212 | if(confused || sobj->blessed) {
1213 | You_feel("guilty.");
1214 | break;
1215 | }
1216 | punish(sobj);
1217 | break;
1218 | case SCR_STINKING_CLOUD: {
1219 | coord cc;
1220 |
1221 | You("have found a scroll of stinking cloud!");
1222 | known = TRUE;
1223 | pline("Where do you want to center the cloud?");
1224 | cc.x = u.ux;
1225 | cc.y = u.uy;
1226 | if (getpos(&cc, TRUE, "the desired position") < 0) {
1227 | pline(Never_mind);
1228 | return 0;
1229 | }
1230 | if (!cansee(cc.x, cc.y) || distu(cc.x, cc.y) >= 32) {
1231 | You("smell rotten eggs.");
1232 | return 0;
1233 | }
1234 | (void) create_gas_cloud(cc.x, cc.y, 3+bcsign(sobj),
1235 | 8+4*bcsign(sobj));
1236 | break;
1237 | }
1238 | default:
1239 | impossible("What weird effect is this? (%u)", sobj->otyp);
1240 | }
1241 | return(0);
1242 | }
1243 |
1244 | static void
1245 | wand_explode(obj)
1246 | register struct obj *obj;
1247 | {
1248 | obj->in_use = TRUE; /* in case losehp() is fatal */
1249 | Your("%s vibrates violently, and explodes!",xname(obj));
1250 | nhbell();
1251 | losehp(rnd(2*(u.uhpmax+1)/3), "exploding wand", KILLED_BY_AN);
1252 | useup(obj);
1253 | exercise(A_STR, FALSE);
1254 | }
1255 |
1256 | /*
1257 | * Low-level lit-field update routine.
1258 | */
1259 | STATIC_PTR void
1260 | set_lit(x,y,val)
1261 | int x, y;
1262 | genericptr_t val;
1263 | {
1264 | if (val)
1265 | levl[x][y].lit = 1;
1266 | else {
1267 | levl[x][y].lit = 0;
1268 | snuff_light_source(x, y);
1269 | }
1270 | }
1271 |
1272 | void
1273 | litroom(on,obj)
1274 | register boolean on;
1275 | struct obj *obj;
1276 | {
1277 | char is_lit; /* value is irrelevant; we use its address
1278 | as a `not null' flag for set_lit() */
1279 |
1280 | /* first produce the text (provided you're not blind) */
1281 | if(!on) {
1282 | register struct obj *otmp;
1283 |
1284 | if (!Blind) {
1285 | if(u.uswallow) {
1286 | pline("It seems even darker in here than before.");
1287 | return;
1288 | }
1289 | You("are surrounded by darkness!");
1290 | }
1291 |
1292 | /* the magic douses lamps, et al, too */
1293 | for(otmp = invent; otmp; otmp = otmp->nobj)
1294 | if (otmp->lamplit)
1295 | (void) snuff_lit(otmp);
1296 | if (Blind) goto do_it;
1297 | } else {
1298 | if (Blind) goto do_it;
1299 | if(u.uswallow){
1300 | if (is_animal(u.ustuck->data))
1301 | pline("%s stomach is lit.",
1302 | s_suffix(Monnam(u.ustuck)));
1303 | else
1304 | if (is_whirly(u.ustuck->data))
1305 | pline("%s shines briefly.",
1306 | Monnam(u.ustuck));
1307 | else
1308 | pline("%s glistens.", Monnam(u.ustuck));
1309 | return;
1310 | }
1311 | pline("A lit field surrounds you!");
1312 | }
1313 |
1314 | do_it:
1315 | /* No-op in water - can only see the adjacent squares and that's it! */
1316 | if (Underwater || Is_waterlevel(&u.uz)) return;
1317 | /*
1318 | * If we are darkening the room and the hero is punished but not
1319 | * blind, then we have to pick up and replace the ball and chain so
1320 | * that we don't remember them if they are out of sight.
1321 | */
1322 | if (Punished && !on && !Blind)
1323 | move_bc(1, 0, uball->ox, uball->oy, uchain->ox, uchain->oy);
1324 |
1325 | #ifdef REINCARNATION
1326 | if (Is_rogue_level(&u.uz)) {
1327 | /* Can't use do_clear_area because MAX_RADIUS is too small */
1328 | /* rogue lighting must light the entire room */
1329 | int rnum = levl[u.ux][u.uy].roomno - ROOMOFFSET;
1330 | int rx, ry;
1331 | if(rnum >= 0) {
1332 | for(rx = rooms[rnum].lx-1; rx <= rooms[rnum].hx+1; rx++)
1333 | for(ry = rooms[rnum].ly-1; ry <= rooms[rnum].hy+1; ry++)
1334 | set_lit(rx, ry,
1335 | (genericptr_t)(on ? &is_lit : (char *)0));
1336 | rooms[rnum].rlit = on;
1337 | }
1338 | /* hallways remain dark on the rogue level */
1339 | } else
1340 | #endif
1341 | do_clear_area(u.ux,u.uy,
1342 | (obj && obj->oclass==SCROLL_CLASS && obj->blessed) ? 9 : 5,
1343 | set_lit, (genericptr_t)(on ? &is_lit : (char *)0));
1344 |
1345 | /*
1346 | * If we are not blind, then force a redraw on all positions in sight
1347 | * by temporarily blinding the hero. The vision recalculation will
1348 | * correctly update all previously seen positions *and* correctly
1349 | * set the waslit bit [could be messed up from above].
1350 | */
1351 | if (!Blind) {
1352 | vision_recalc(2);
1353 |
1354 | /* replace ball&chain */
1355 | if (Punished && !on)
1356 | move_bc(0, 0, uball->ox, uball->oy, uchain->ox, uchain->oy);
1357 | }
1358 |
1359 | vision_full_recalc = 1; /* delayed vision recalculation */
1360 | }
1361 |
1362 | static void
1363 | do_class_genocide()
1364 | {
1365 | register int i, j, immunecnt, gonecnt, goodcnt, class;
1366 | char buf[BUFSZ];
1367 | boolean gameover = FALSE; /* true iff killed self */
1368 |
1369 | for(j=0; ; j++) {
1370 | if (j >= 5) {
1371 | pline(thats_enough_tries);
1372 | return;
1373 | }
1374 | do {
1375 | getlin("What class of monsters do you wish to genocide?",
1376 | buf);
1377 | (void)mungspaces(buf);
1378 | } while (buf[0]=='\033' || !buf[0]);
1379 | if (strlen(buf) == 1) {
1380 | if (buf[0] == ILLOBJ_SYM)
1381 | buf[0] = def_monsyms[S_MIMIC];
1382 | class = def_char_to_monclass(buf[0]);
1383 | } else {
1384 | char buf2[BUFSZ];
1385 |
1386 | class = 0;
1387 | Strcpy(buf2, makesingular(buf));
1388 | Strcpy(buf, buf2);
1389 | }
1390 | immunecnt = gonecnt = goodcnt = 0;
1391 | for (i = LOW_PM; i < NUMMONS; i++) {
1392 | if (class == 0 &&
1393 | strstri(monexplain[(int)mons[i].mlet], buf) != 0)
1394 | class = mons[i].mlet;
1395 | if (mons[i].mlet == class) {
1396 | if (!(mons[i].geno & G_GENO)) immunecnt++;
1397 | else if(mvitals[i].mvflags & G_GENOD) gonecnt++;
1398 | else goodcnt++;
1399 | }
1400 | }
1401 | /*
1402 | * TODO[?]: If user's input doesn't match any class
1403 | * description, check individual species names.
1404 | */
1405 | if (!goodcnt && class != mons[urole.malenum].mlet &&
1406 | class != mons[urace.malenum].mlet) {
1407 | if (gonecnt)
1408 | pline("All such monsters are already nonexistent.");
1409 | else if (immunecnt ||
1410 | (buf[0] == DEF_INVISIBLE && buf[1] == '\0'))
1411 | You("aren't permitted to genocide such monsters.");
1412 | else
1413 | #ifdef WIZARD /* to aid in topology testing; remove pesky monsters */
1414 | if (wizard && buf[0] == '*') {
1415 | register struct monst *mtmp, *mtmp2;
1416 |
1417 | gonecnt = 0;
1418 | for (mtmp = fmon; mtmp; mtmp = mtmp2) {
1419 | mtmp2 = mtmp->nmon;
1420 | if (DEADMONSTER(mtmp)) continue;
1421 | mongone(mtmp);
1422 | gonecnt++;
1423 | }
1424 | pline("Eliminated %d monster%s.", gonecnt, plur(gonecnt));
1425 | return;
1426 | } else
1427 | #endif
1428 | pline("That symbol does not represent any monster.");
1429 | continue;
1430 | }
1431 |
1432 | for (i = LOW_PM; i < NUMMONS; i++) {
1433 | if(mons[i].mlet == class) {
1434 | char nam[BUFSZ];
1435 |
1436 | Strcpy(nam, makeplural(mons[i].mname));
1437 | /* Although "genus" is Latin for race, the hero benefits
1438 | * from both race and role; thus genocide affects either.
1439 | */
1440 | if (Your_Own_Role(i) || Your_Own_Race(i) ||
1441 | ((mons[i].geno & G_GENO)
1442 | && !(mvitals[i].mvflags & G_GENOD))) {
1443 | /* This check must be first since player monsters might
1444 | * have G_GENOD or !G_GENO.
1445 | */
1446 | mvitals[i].mvflags |= (G_GENOD|G_NOCORPSE);
1447 | reset_rndmonst(i);
1448 | kill_genocided_monsters();
1449 | update_inventory(); /* eggs & tins */
1450 | pline("Wiped out all %s.", nam);
1451 | if (Upolyd && i == u.umonnum) {
1452 | if (Unchanging) done(GENOCIDED);
1453 | rehumanize();
1454 | }
1455 | /* Self-genocide if it matches either your race or role */
1456 | /* Assumption: male and female forms share the same letter */
1457 | if (i == urole.malenum || i == urace.malenum) {
1458 | u.uhp = -1;
1459 | killer_format = KILLED_BY_AN;
1460 | killer = "scroll of genocide";
1461 | if (Upolyd)
1462 | You_feel("dead inside.");
1463 | else
1464 | gameover = TRUE;
1465 | }
1466 | } else if (mvitals[i].mvflags & G_GENOD) {
1467 | if (!gameover)
1468 | pline("All %s are already nonexistent.", nam);
1469 | } else if (!gameover) {
1470 | /* suppress feedback about quest beings except
1471 | for those applicable to our own role */
1472 | if ((mons[i].msound != MS_LEADER ||
1473 | quest_info(MS_LEADER) == i)
1474 | && (mons[i].msound != MS_NEMESIS ||
1475 | quest_info(MS_NEMESIS) == i)
1476 | && (mons[i].msound != MS_GUARDIAN ||
1477 | quest_info(MS_GUARDIAN) == i)
1478 | /* non-leader/nemesis/guardian role-specific monster */
1479 | && (i != PM_NINJA || /* nuisance */
1480 | Role_if(PM_SAMURAI))) {
1481 | boolean named, uniq;
1482 |
1483 | named = type_is_pname(&mons[i]) ? TRUE : FALSE;
1484 | uniq = (mons[i].geno & G_UNIQ) ? TRUE : FALSE;
1485 | /* one special case */
1486 | if (i == PM_HIGH_PRIEST) uniq = FALSE;
1487 |
1488 | You("aren't permitted to genocide %s%s.",
1489 | (uniq && !named) ? "the " : "",
1490 | (uniq || named) ? mons[i].mname : nam);
1491 | }
1492 | }
1493 | }
1494 | }
1495 | if (gameover) done(GENOCIDED);
1496 | return;
1497 | }
1498 | }
1499 |
1500 | #define REALLY 1
1501 | #define PLAYER 2
1502 | void
1503 | do_genocide(how)
1504 | int how;
1505 | /* 0 = no genocide; create monsters (cursed scroll) */
1506 | /* 1 = normal genocide */
1507 | /* 3 = forced genocide of player */
1508 | {
1509 | char buf[BUFSZ];
1510 | register int i, killplayer = 0;
1511 | register int mndx;
1512 | register struct permonst *ptr;
1513 | const char *which;
1514 |
1515 | if (how & PLAYER) {
1516 | mndx = u.umonster; /* non-polymorphed mon num */
1517 | ptr = &mons[mndx];
1518 | Strcpy(buf, ptr->mname);
1519 | killplayer++;
1520 | } else {
1521 | for(i = 0; ; i++) {
1522 | if(i >= 5) {
1523 | pline(thats_enough_tries);
1524 | return;
1525 | }
1526 | getlin("What monster do you want to genocide? [type the name]",
1527 | buf);
1528 |
1529 | mndx = name_to_mon(buf);
1530 | if (mndx == NON_PM || (mvitals[mndx].mvflags & G_GENOD)) {
1531 | pline("Such creatures %s exist in this world.",
1532 | (mndx == NON_PM) ? "do not" : "no longer");
1533 | continue;
1534 | }
1535 | ptr = &mons[mndx];
1536 | /* Although "genus" is Latin for race, the hero benefits
1537 | * from both race and role; thus genocide affects either.
1538 | */
1539 | if (Your_Own_Role(mndx) || Your_Own_Race(mndx)) {
1540 | killplayer++;
1541 | break;
1542 | }
1543 | if (is_human(ptr)) adjalign(-sgn(u.ualign.type));
1544 | if (is_demon(ptr)) adjalign(sgn(u.ualign.type));
1545 |
1546 | if(!(ptr->geno & G_GENO)) {
1547 | if(flags.soundok) {
1548 | /* fixme: unconditional "caverns" will be silly in some circumstances */
1549 | if(flags.verbose)
1550 | pline("A thunderous voice booms through the caverns:");
1551 | verbalize("No, mortal! That will not be done.");
1552 | }
1553 | continue;
1554 | }
1555 | /* KMH -- Unchanging prevents rehumanization */
1556 | if (Unchanging && ptr == youmonst.data)
1557 | killplayer++;
1558 | break;
1559 | }
1560 | }
1561 |
1562 | which = "all ";
1563 | if (Hallucination) {
1564 | if (Upolyd)
1565 | Strcpy(buf,youmonst.data->mname);
1566 | else {
1567 | Strcpy(buf, (flags.female && urole.name.f) ?
1568 | urole.name.f : urole.name.m);
1569 | buf[0] = lowc(buf[0]);
1570 | }
1571 | } else {
1572 | Strcpy(buf, ptr->mname); /* make sure we have standard singular */
1573 | if ((ptr->geno & G_UNIQ) && ptr != &mons[PM_HIGH_PRIEST])
1574 | which = !type_is_pname(ptr) ? "the " : "";
1575 | }
1576 | if (how & REALLY) {
1577 | /* setting no-corpse affects wishing and random tin generation */
1578 | mvitals[mndx].mvflags |= (G_GENOD | G_NOCORPSE);
1579 | pline("Wiped out %s%s.", which,
1580 | (*which != 'a') ? buf : makeplural(buf));
1581 |
1582 | if (killplayer) {
1583 | /* might need to wipe out dual role */
1584 | if (urole.femalenum != NON_PM && mndx == urole.malenum)
1585 | mvitals[urole.femalenum].mvflags |= (G_GENOD | G_NOCORPSE);
1586 | if (urole.femalenum != NON_PM && mndx == urole.femalenum)
1587 | mvitals[urole.malenum].mvflags |= (G_GENOD | G_NOCORPSE);
1588 | if (urace.femalenum != NON_PM && mndx == urace.malenum)
1589 | mvitals[urace.femalenum].mvflags |= (G_GENOD | G_NOCORPSE);
1590 | if (urace.femalenum != NON_PM && mndx == urace.femalenum)
1591 | mvitals[urace.malenum].mvflags |= (G_GENOD | G_NOCORPSE);
1592 |
1593 | u.uhp = -1;
1594 | killer_format = KILLED_BY_AN;
1595 | if (how & PLAYER)
1596 | killer = "genocidal confusion";
1597 | else /* selected player deliberately, not confused */
1598 | killer = "scroll of genocide";
1599 |
1600 | /* Polymorphed characters will die as soon as they're rehumanized. */
1601 | /* KMH -- Unchanging prevents rehumanization */
1602 | if (Upolyd && ptr != youmonst.data) {
1603 | delayed_killer = killer;
1604 | killer = 0;
1605 | You_feel("dead inside.");
1606 | } else
1607 | done(GENOCIDED);
1608 | } else if (ptr == youmonst.data) {
1609 | rehumanize();
1610 | }
1611 | reset_rndmonst(mndx);
1612 | kill_genocided_monsters();
1613 | update_inventory(); /* in case identified eggs were affected */
1614 | } else {
1615 | int cnt = 0;
1616 |
1617 | if (!(mons[mndx].geno & G_UNIQ) &&
1618 | !(mvitals[mndx].mvflags & (G_GENOD | G_EXTINCT)))
1619 | for (i = rn1(3, 4); i > 0; i--) {
1620 | if (!makemon(ptr, u.ux, u.uy, NO_MINVENT))
1621 | break; /* couldn't make one */
1622 | ++cnt;
1623 | if (mvitals[mndx].mvflags & G_EXTINCT)
1624 | break; /* just made last one */
1625 | }
1626 | if (cnt)
1627 | pline("Sent in some %s.", makeplural(buf));
1628 | else
1629 | pline(nothing_happens);
1630 | }
1631 | }
1632 |
1633 | void
1634 | punish(sobj)
1635 | register struct obj *sobj;
1636 | {
1637 | /* KMH -- Punishment is still okay when you are riding */
1638 | You("are being punished for your misbehavior!");
1639 | if(Punished){
1640 | Your("iron ball gets heavier.");
1641 | uball->owt += 160 * (1 + sobj->cursed);
1642 | return;
1643 | }
1644 | if (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data)) {
1645 | pline("A ball and chain appears, then falls away.");
1646 | dropy(mkobj(BALL_CLASS, TRUE));
1647 | return;
1648 | }
1649 | setworn(mkobj(CHAIN_CLASS, TRUE), W_CHAIN);
1650 | setworn(mkobj(BALL_CLASS, TRUE), W_BALL);
1651 | uball->spe = 1; /* special ball (see save) */
1652 |
1653 | /*
1654 | * Place ball & chain if not swallowed. If swallowed, the ball &
1655 | * chain variables will be set at the next call to placebc().
1656 | */
1657 | if (!u.uswallow) {
1658 | placebc();
1659 | if (Blind) set_bc(1); /* set up ball and chain variables */
1660 | newsym(u.ux,u.uy); /* see ball&chain if can't see self */
1661 | }
1662 | }
1663 |
1664 | void
1665 | unpunish()
1666 | { /* remove the ball and chain */
1667 | struct obj *savechain = uchain;
1668 |
1669 | obj_extract_self(uchain);
1670 | newsym(uchain->ox,uchain->oy);
1671 | setworn((struct obj *)0, W_CHAIN);
1672 | dealloc_obj(savechain);
1673 | uball->spe = 0;
1674 | setworn((struct obj *)0, W_BALL);
1675 | }
1676 |
1677 | /* some creatures have special data structures that only make sense in their
1678 | * normal locations -- if the player tries to create one elsewhere, or to revive
1679 | * one, the disoriented creature becomes a zombie
1680 | */
1681 | boolean
1682 | cant_create(mtype, revival)
1683 | int *mtype;
1684 | boolean revival;
1685 | {
1686 |
1687 | /* SHOPKEEPERS can be revived now */
1688 | if (*mtype==PM_GUARD || (*mtype==PM_SHOPKEEPER && !revival)
1689 | || *mtype==PM_ALIGNED_PRIEST || *mtype==PM_ANGEL) {
1690 | *mtype = PM_HUMAN_ZOMBIE;
1691 | return TRUE;
1692 | } else if (*mtype==PM_LONG_WORM_TAIL) { /* for create_particular() */
1693 | *mtype = PM_LONG_WORM;
1694 | return TRUE;
1695 | }
1696 | return FALSE;
1697 | }
1698 |
1699 | #ifdef WIZARD
1700 | boolean
1701 | create_particular()
1702 | {
1703 | char buf[BUFSZ];
1704 | int which, tries = 0;
1705 |
1706 | do {
1707 | getlin("Create what kind of monster? [type the name]", buf);
1708 | if (buf[0] == '\033') return FALSE;
1709 | which = name_to_mon(buf);
1710 | if (which < LOW_PM) pline("I've never heard of such monsters.");
1711 | else break;
1712 | } while (++tries < 5);
1713 | if (tries == 5) pline(thats_enough_tries);
1714 | else {
1715 | (void) cant_create(&which, FALSE);
1716 | return((boolean)(makemon(&mons[which],
1717 | u.ux, u.uy, NO_MM_FLAGS) != 0));
1718 | }
1719 | return FALSE;
1720 | }
1721 | #endif /* WIZARD */
1722 |
1723 | #endif /* OVLB */
1724 |
1725 | /*read.c*/