OSDN Git Service

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