1 /* NetHack 3.6	read.c	$NHDT-Date: 1561485713 2019/06/25 18:01:53 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.172 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2012. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 #include "hack.h"
7 
8 #define Your_Own_Role(mndx)  \
9     ((mndx) == urole.malenum \
10      || (urole.femalenum != NON_PM && (mndx) == urole.femalenum))
11 #define Your_Own_Race(mndx)  \
12     ((mndx) == urace.malenum \
13      || (urace.femalenum != NON_PM && (mndx) == urace.femalenum))
14 
15 boolean known;
16 
17 static NEARDATA const char readable[] = { ALL_CLASSES, SCROLL_CLASS,
18                                           SPBOOK_CLASS, 0 };
19 static const char all_count[] = { ALLOW_COUNT, ALL_CLASSES, 0 };
20 
21 STATIC_DCL boolean FDECL(learnscrolltyp, (SHORT_P));
22 STATIC_DCL char *FDECL(erode_obj_text, (struct obj *, char *));
23 STATIC_DCL char *FDECL(apron_text, (struct obj *, char *buf));
24 STATIC_DCL void FDECL(stripspe, (struct obj *));
25 STATIC_DCL void FDECL(p_glow1, (struct obj *));
26 STATIC_DCL void FDECL(p_glow2, (struct obj *, const char *));
27 STATIC_DCL void FDECL(forget_single_object, (int));
28 #if 0 /* not used */
29 STATIC_DCL void FDECL(forget_objclass, (int));
30 #endif
31 STATIC_DCL void FDECL(randomize, (int *, int));
32 STATIC_DCL void FDECL(forget, (int));
33 STATIC_DCL int FDECL(maybe_tame, (struct monst *, struct obj *));
34 STATIC_DCL boolean FDECL(get_valid_stinking_cloud_pos, (int, int));
35 STATIC_DCL boolean FDECL(is_valid_stinking_cloud_pos, (int, int, BOOLEAN_P));
36 STATIC_PTR void FDECL(display_stinking_cloud_positions, (int));
37 STATIC_PTR void FDECL(set_lit, (int, int, genericptr));
38 STATIC_DCL void NDECL(do_class_genocide);
39 
40 STATIC_OVL boolean
learnscrolltyp(scrolltyp)41 learnscrolltyp(scrolltyp)
42 short scrolltyp;
43 {
44     if (!objects[scrolltyp].oc_name_known) {
45         makeknown(scrolltyp);
46         more_experienced(0, 10);
47         return TRUE;
48     } else
49         return FALSE;
50 }
51 
52 /* also called from teleport.c for scroll of teleportation */
53 void
learnscroll(sobj)54 learnscroll(sobj)
55 struct obj *sobj;
56 {
57     /* it's implied that sobj->dknown is set;
58        we couldn't be reading this scroll otherwise */
59     if (sobj->oclass != SPBOOK_CLASS)
60         (void) learnscrolltyp(sobj->otyp);
61 }
62 
63 STATIC_OVL char *
erode_obj_text(otmp,buf)64 erode_obj_text(otmp, buf)
65 struct obj *otmp;
66 char *buf;
67 {
68     int erosion = greatest_erosion(otmp);
69 
70     if (erosion)
71         wipeout_text(buf, (int) (strlen(buf) * erosion / (2 * MAX_ERODE)),
72                      otmp->o_id ^ (unsigned) ubirthday);
73     return buf;
74 }
75 
76 char *
tshirt_text(tshirt,buf)77 tshirt_text(tshirt, buf)
78 struct obj *tshirt;
79 char *buf;
80 {
81     static const char *shirt_msgs[] = {
82         /* Scott Bigham */
83       "I explored the Dungeons of Doom and all I got was this lousy T-shirt!",
84         "Is that Mjollnir in your pocket or are you just happy to see me?",
85       "It's not the size of your sword, it's how #enhance'd you are with it.",
86         "Madame Elvira's House O' Succubi Lifetime Customer",
87         "Madame Elvira's House O' Succubi Employee of the Month",
88         "Ludios Vault Guards Do It In Small, Dark Rooms",
89         "Yendor Military Soldiers Do It In Large Groups",
90         "I survived Yendor Military Boot Camp",
91         "Ludios Accounting School Intra-Mural Lacrosse Team",
92         "Oracle(TM) Fountains 10th Annual Wet T-Shirt Contest",
93         "Hey, black dragon!  Disintegrate THIS!",
94         "I'm With Stupid -->",
95         "Don't blame me, I voted for Izchak!",
96         "Don't Panic", /* HHGTTG */
97         "Furinkan High School Athletic Dept.",                /* Ranma 1/2 */
98         "Hel-LOOO, Nurse!",                                   /* Animaniacs */
99         "=^.^=",
100         "100% goblin hair - do not wash",
101         "Aberzombie and Fitch",
102         "cK -- Cockatrice touches the Kop",
103         "Don't ask me, I only adventure here",
104         "Down with pants!",
105         "d, your dog or a killer?",
106         "FREE PUG AND NEWT!",
107         "Go team ant!",
108         "Got newt?",
109         "Hello, my darlings!", /* Charlie Drake */
110         "Hey!  Nymphs!  Steal This T-Shirt!",
111         "I <3 Dungeon of Doom",
112         "I <3 Maud",
113         "I am a Valkyrie.  If you see me running, try to keep up.",
114         "I am not a pack rat - I am a collector",
115         "I bounced off a rubber tree",         /* Monkey Island */
116         "Plunder Island Brimstone Beach Club", /* Monkey Island */
117         "If you can read this, I can hit you with my polearm",
118         "I'm confused!",
119         "I scored with the princess",
120         "I want to live forever or die in the attempt.",
121         "Lichen Park",
122         "LOST IN THOUGHT - please send search party",
123         "Meat is Mordor",
124         "Minetown Better Business Bureau",
125         "Minetown Watch",
126  "Ms. Palm's House of Negotiable Affection -- A Very Reputable House Of Disrepute",
127         "Protection Racketeer",
128         "Real men love Crom",
129         "Somebody stole my Mojo!",
130         "The Hellhound Gang",
131         "The Werewolves",
132         "They Might Be Storm Giants",
133         "Weapons don't kill people, I kill people",
134         "White Zombie",
135         "You're killing me!",
136         "Anhur State University - Home of the Fighting Fire Ants!",
137         "FREE HUGS",
138         "Serial Ascender",
139         "Real men are valkyries",
140         "Young Men's Cavedigging Association",
141         "Occupy Fort Ludios",
142         "I couldn't afford this T-shirt so I stole it!",
143         "Mind flayers suck",
144         "I'm not wearing any pants",
145         "Down with the living!",
146         "Pudding farmer",
147         "Vegetarian",
148         "Hello, I'm War!",
149         "It is better to light a candle than to curse the darkness",
150         "It is easier to curse the darkness than to light a candle",
151         /* expanded "rock--paper--scissors" featured in TV show "Big Bang
152            Theory" although they didn't create it (and an actual T-shirt
153            with pentagonal diagram showing which choices defeat which) */
154         "rock--paper--scissors--lizard--Spock!",
155         /* "All men must die -- all men must serve" challange and response
156            from book series _A_Song_of_Ice_and_Fire_ by George R.R. Martin,
157            TV show "Game of Thrones" (probably an actual T-shirt too...) */
158         "/Valar morghulis/ -- /Valar dohaeris/",
159     };
160 
161     Strcpy(buf, shirt_msgs[tshirt->o_id % SIZE(shirt_msgs)]);
162     return erode_obj_text(tshirt, buf);
163 }
164 
165 STATIC_OVL char *
apron_text(apron,buf)166 apron_text(apron, buf)
167 struct obj *apron;
168 char *buf;
169 {
170     static const char *apron_msgs[] = {
171         "Kiss the cook",
172         "I'm making SCIENCE!",
173         "Don't mess with the chef",
174         "Don't make me poison you",
175         "Gehennom's Kitchen",
176         "Rat: The other white meat",
177         "If you can't stand the heat, get out of Gehennom!",
178         "If we weren't meant to eat animals, why are they made out of meat?",
179         "If you don't like the food, I'll stab you",
180     };
181 
182     Strcpy(buf, apron_msgs[apron->o_id % SIZE(apron_msgs)]);
183     return erode_obj_text(apron, buf);
184 }
185 
186 int
doread()187 doread()
188 {
189     register struct obj *scroll;
190     boolean confused, nodisappear;
191 
192     known = FALSE;
193     if (check_capacity((char *) 0))
194         return 0;
195     scroll = getobj(readable, "read");
196     if (!scroll)
197         return 0;
198 
199     /* outrumor has its own blindness check */
200     if (scroll->otyp == FORTUNE_COOKIE) {
201         if (flags.verbose)
202             You("break up the cookie and throw away the pieces.");
203         outrumor(bcsign(scroll), BY_COOKIE);
204         if (!Blind)
205             u.uconduct.literate++;
206         useup(scroll);
207         return 1;
208     } else if (scroll->otyp == T_SHIRT || scroll->otyp == ALCHEMY_SMOCK) {
209         char buf[BUFSZ], *mesg;
210         const char *endpunct;
211 
212         if (Blind) {
213             You_cant("feel any Braille writing.");
214             return 0;
215         }
216         /* can't read shirt worn under suit (under cloak is ok though) */
217         if (scroll->otyp == T_SHIRT && uarm && scroll == uarmu) {
218             pline("%s shirt is obscured by %s%s.",
219                   scroll->unpaid ? "That" : "Your", shk_your(buf, uarm),
220                   suit_simple_name(uarm));
221             return 0;
222         }
223         u.uconduct.literate++;
224         /* populate 'buf[]' */
225         mesg = (scroll->otyp == T_SHIRT) ? tshirt_text(scroll, buf)
226                                          : apron_text(scroll, buf);
227         endpunct = "";
228         if (flags.verbose) {
229             int ln = (int) strlen(mesg);
230 
231             /* we will be displaying a sentence; need ending punctuation */
232             if (ln > 0 && !index(".!?", mesg[ln - 1]))
233                 endpunct = ".";
234             pline("It reads:");
235         }
236         pline("\"%s\"%s", mesg, endpunct);
237         return 1;
238     } else if (scroll->otyp == CREDIT_CARD) {
239         static const char *card_msgs[] = {
240             "Leprechaun Gold Tru$t - Shamrock Card",
241             "Magic Memory Vault Charge Card",
242             "Larn National Bank",                /* Larn */
243             "First Bank of Omega",               /* Omega */
244             "Bank of Zork - Frobozz Magic Card", /* Zork */
245             "Ankh-Morpork Merchant's Guild Barter Card",
246             "Ankh-Morpork Thieves' Guild Unlimited Transaction Card",
247             "Ransmannsby Moneylenders Association",
248             "Bank of Gehennom - 99% Interest Card",
249             "Yendorian Express - Copper Card",
250             "Yendorian Express - Silver Card",
251             "Yendorian Express - Gold Card",
252             "Yendorian Express - Mithril Card",
253             "Yendorian Express - Platinum Card", /* must be last */
254         };
255 
256         if (Blind) {
257             You("feel the embossed numbers:");
258         } else {
259             if (flags.verbose)
260                 pline("It reads:");
261             pline("\"%s\"",
262                   scroll->oartifact
263                       ? card_msgs[SIZE(card_msgs) - 1]
264                       : card_msgs[scroll->o_id % (SIZE(card_msgs) - 1)]);
265         }
266         /* Make a credit card number */
267         pline("\"%d0%d %ld%d1 0%d%d0\"%s",
268               (((int) scroll->o_id % 89) + 10),
269               ((int) scroll->o_id % 4),
270               ((((long) scroll->o_id * 499L) % 899999L) + 100000L),
271               ((int) scroll->o_id % 10),
272               (!((int) scroll->o_id % 3)),
273               (((int) scroll->o_id * 7) % 10),
274               (flags.verbose || Blind) ? "." : "");
275         u.uconduct.literate++;
276         return 1;
277     } else if (scroll->otyp == CAN_OF_GREASE) {
278         pline("This %s has no label.", singular(scroll, xname));
279         return 0;
280     } else if (scroll->otyp == MAGIC_MARKER) {
281         if (Blind) {
282             You_cant("feel any Braille writing.");
283             return 0;
284         }
285         if (flags.verbose)
286             pline("It reads:");
287         pline("\"Magic Marker(TM) Red Ink Marker Pen.  Water Soluble.\"");
288         u.uconduct.literate++;
289         return 1;
290     } else if (scroll->oclass == COIN_CLASS) {
291         if (Blind)
292             You("feel the embossed words:");
293         else if (flags.verbose)
294             You("read:");
295         pline("\"1 Zorkmid.  857 GUE.  In Frobs We Trust.\"");
296         u.uconduct.literate++;
297         return 1;
298     } else if (scroll->oartifact == ART_ORB_OF_FATE) {
299         if (Blind)
300             You("feel the engraved signature:");
301         else
302             pline("It is signed:");
303         pline("\"Odin.\"");
304         u.uconduct.literate++;
305         return 1;
306     } else if (scroll->otyp == CANDY_BAR) {
307         static const char *wrapper_msgs[] = {
308             "Apollo",       /* Lost */
309             "Moon Crunchy", /* South Park */
310             "Snacky Cake",    "Chocolate Nuggie", "The Small Bar",
311             "Crispy Yum Yum", "Nilla Crunchie",   "Berry Bar",
312             "Choco Nummer",   "Om-nom", /* Cat Macro */
313             "Fruity Oaty",              /* Serenity */
314             "Wonka Bar" /* Charlie and the Chocolate Factory */
315         };
316 
317         if (Blind) {
318             You_cant("feel any Braille writing.");
319             return 0;
320         }
321         pline("The wrapper reads: \"%s\".",
322               wrapper_msgs[scroll->o_id % SIZE(wrapper_msgs)]);
323         u.uconduct.literate++;
324         return 1;
325     } else if (scroll->oclass != SCROLL_CLASS
326                && scroll->oclass != SPBOOK_CLASS) {
327         pline(silly_thing_to, "read");
328         return 0;
329     } else if (Blind && (scroll->otyp != SPE_BOOK_OF_THE_DEAD)) {
330         const char *what = 0;
331 
332         if (scroll->oclass == SPBOOK_CLASS)
333             what = "mystic runes";
334         else if (!scroll->dknown)
335             what = "formula on the scroll";
336         if (what) {
337             pline("Being blind, you cannot read the %s.", what);
338             return 0;
339         }
340     }
341 
342     confused = (Confusion != 0);
343 #ifdef MAIL
344     if (scroll->otyp == SCR_MAIL) {
345         confused = FALSE; /* override */
346         /* reading mail is a convenience for the player and takes
347            place outside the game, so shouldn't affect gameplay;
348            on the other hand, it starts by explicitly making the
349            hero actively read something, which is pretty hard
350            to simply ignore; as a compromise, if the player has
351            maintained illiterate conduct so far, and this mail
352            scroll didn't come from bones, ask for confirmation */
353         if (!u.uconduct.literate) {
354             if (!scroll->spe && yn(
355              "Reading mail will violate \"illiterate\" conduct.  Read anyway?"
356                                    ) != 'y')
357                 return 0;
358         }
359     }
360 #endif
361 
362     /* Actions required to win the game aren't counted towards conduct */
363     /* Novel conduct is handled in read_tribute so exclude it too*/
364     if (scroll->otyp != SPE_BOOK_OF_THE_DEAD
365         && scroll->otyp != SPE_BLANK_PAPER && scroll->otyp != SCR_BLANK_PAPER
366         && scroll->otyp != SPE_NOVEL)
367         u.uconduct.literate++;
368 
369     if (scroll->oclass == SPBOOK_CLASS) {
370         return study_book(scroll);
371     }
372     scroll->in_use = TRUE; /* scroll, not spellbook, now being read */
373     if (scroll->otyp != SCR_BLANK_PAPER) {
374         boolean silently = !can_chant(&youmonst);
375 
376         /* a few scroll feedback messages describe something happening
377            to the scroll itself, so avoid "it disappears" for those */
378         nodisappear = (scroll->otyp == SCR_FIRE
379                        || (scroll->otyp == SCR_REMOVE_CURSE
380                            && scroll->cursed));
381         if (Blind)
382             pline(nodisappear
383                       ? "You %s the formula on the scroll."
384                       : "As you %s the formula on it, the scroll disappears.",
385                   silently ? "cogitate" : "pronounce");
386         else
387             pline(nodisappear ? "You read the scroll."
388                               : "As you read the scroll, it disappears.");
389         if (confused) {
390             if (Hallucination)
391                 pline("Being so trippy, you screw up...");
392             else
393                 pline("Being confused, you %s the magic words...",
394                       silently ? "misunderstand" : "mispronounce");
395         }
396     }
397     if (!seffects(scroll)) {
398         if (!objects[scroll->otyp].oc_name_known) {
399             if (known)
400                 learnscroll(scroll);
401             else if (!objects[scroll->otyp].oc_uname)
402                 docall(scroll);
403         }
404         scroll->in_use = FALSE;
405         if (scroll->otyp != SCR_BLANK_PAPER)
406             useup(scroll);
407     }
408     return 1;
409 }
410 
411 STATIC_OVL void
stripspe(obj)412 stripspe(obj)
413 register struct obj *obj;
414 {
415     if (obj->blessed || obj->spe <= 0) {
416         pline1(nothing_happens);
417     } else {
418         /* order matters: message, shop handling, actual transformation */
419         pline("%s briefly.", Yobjnam2(obj, "vibrate"));
420         costly_alteration(obj, COST_UNCHRG);
421         obj->spe = 0;
422         if (obj->otyp == OIL_LAMP || obj->otyp == BRASS_LANTERN)
423             obj->age = 0;
424     }
425 }
426 
427 STATIC_OVL void
p_glow1(otmp)428 p_glow1(otmp)
429 register struct obj *otmp;
430 {
431     pline("%s briefly.", Yobjnam2(otmp, Blind ? "vibrate" : "glow"));
432 }
433 
434 STATIC_OVL void
p_glow2(otmp,color)435 p_glow2(otmp, color)
436 register struct obj *otmp;
437 register const char *color;
438 {
439     pline("%s%s%s for a moment.", Yobjnam2(otmp, Blind ? "vibrate" : "glow"),
440           Blind ? "" : " ", Blind ? "" : hcolor(color));
441 }
442 
443 /* Is the object chargeable?  For purposes of inventory display; it is
444    possible to be able to charge things for which this returns FALSE. */
445 boolean
is_chargeable(obj)446 is_chargeable(obj)
447 struct obj *obj;
448 {
449     if (obj->oclass == WAND_CLASS)
450         return TRUE;
451     /* known && !oc_name_known is possible after amnesia/mind flayer */
452     if (obj->oclass == RING_CLASS)
453         return (boolean) (objects[obj->otyp].oc_charged
454                           && (obj->known
455                               || (obj->dknown
456                                   && objects[obj->otyp].oc_name_known)));
457     if (is_weptool(obj)) /* specific check before general tools */
458         return FALSE;
459     if (obj->oclass == TOOL_CLASS)
460         return (boolean) objects[obj->otyp].oc_charged;
461     return FALSE; /* why are weapons/armor considered charged anyway? */
462 }
463 
464 /* recharge an object; curse_bless is -1 if the recharging implement
465    was cursed, +1 if blessed, 0 otherwise. */
466 void
recharge(obj,curse_bless)467 recharge(obj, curse_bless)
468 struct obj *obj;
469 int curse_bless;
470 {
471     register int n;
472     boolean is_cursed, is_blessed;
473 
474     is_cursed = curse_bless < 0;
475     is_blessed = curse_bless > 0;
476 
477     if (obj->oclass == WAND_CLASS) {
478         int lim = (obj->otyp == WAN_WISHING)
479                       ? 3
480                       : (objects[obj->otyp].oc_dir != NODIR) ? 8 : 15;
481 
482         /* undo any prior cancellation, even when is_cursed */
483         if (obj->spe == -1)
484             obj->spe = 0;
485 
486         /*
487          * Recharging might cause wands to explode.
488          *      v = number of previous recharges
489          *            v = percentage chance to explode on this attempt
490          *                    v = cumulative odds for exploding
491          *      0 :   0       0
492          *      1 :   0.29    0.29
493          *      2 :   2.33    2.62
494          *      3 :   7.87   10.28
495          *      4 :  18.66   27.02
496          *      5 :  36.44   53.62
497          *      6 :  62.97   82.83
498          *      7 : 100     100
499          */
500         n = (int) obj->recharged;
501         if (n > 0 && (obj->otyp == WAN_WISHING
502                       || (n * n * n > rn2(7 * 7 * 7)))) { /* recharge_limit */
503             wand_explode(obj, rnd(lim));
504             return;
505         }
506         /* didn't explode, so increment the recharge count */
507         obj->recharged = (unsigned) (n + 1);
508 
509         /* now handle the actual recharging */
510         if (is_cursed) {
511             stripspe(obj);
512         } else {
513             n = (lim == 3) ? 3 : rn1(5, lim + 1 - 5);
514             if (!is_blessed)
515                 n = rnd(n);
516 
517             if (obj->spe < n)
518                 obj->spe = n;
519             else
520                 obj->spe++;
521             if (obj->otyp == WAN_WISHING && obj->spe > 3) {
522                 wand_explode(obj, 1);
523                 return;
524             }
525             if (obj->spe >= lim)
526                 p_glow2(obj, NH_BLUE);
527             else
528                 p_glow1(obj);
529 #if 0 /*[shop price doesn't vary by charge count]*/
530             /* update shop bill to reflect new higher price */
531             if (obj->unpaid)
532                 alter_cost(obj, 0L);
533 #endif
534         }
535 
536     } else if (obj->oclass == RING_CLASS && objects[obj->otyp].oc_charged) {
537         /* charging does not affect ring's curse/bless status */
538         int s = is_blessed ? rnd(3) : is_cursed ? -rnd(2) : 1;
539         boolean is_on = (obj == uleft || obj == uright);
540 
541         /* destruction depends on current state, not adjustment */
542         if (obj->spe > rn2(7) || obj->spe <= -5) {
543             pline("%s momentarily, then %s!", Yobjnam2(obj, "pulsate"),
544                   otense(obj, "explode"));
545             if (is_on)
546                 Ring_gone(obj);
547             s = rnd(3 * abs(obj->spe)); /* amount of damage */
548             useup(obj);
549             losehp(Maybe_Half_Phys(s), "exploding ring", KILLED_BY_AN);
550         } else {
551             long mask = is_on ? (obj == uleft ? LEFT_RING : RIGHT_RING) : 0L;
552 
553             pline("%s spins %sclockwise for a moment.", Yname2(obj),
554                   s < 0 ? "counter" : "");
555             if (s < 0)
556                 costly_alteration(obj, COST_DECHNT);
557             /* cause attributes and/or properties to be updated */
558             if (is_on)
559                 Ring_off(obj);
560             obj->spe += s; /* update the ring while it's off */
561             if (is_on)
562                 setworn(obj, mask), Ring_on(obj);
563             /* oartifact: if a touch-sensitive artifact ring is
564                ever created the above will need to be revised  */
565             /* update shop bill to reflect new higher price */
566             if (s > 0 && obj->unpaid)
567                 alter_cost(obj, 0L);
568         }
569 
570     } else if (obj->oclass == TOOL_CLASS) {
571         int rechrg = (int) obj->recharged;
572 
573         if (objects[obj->otyp].oc_charged) {
574             /* tools don't have a limit, but the counter used does */
575             if (rechrg < 7) /* recharge_limit */
576                 obj->recharged++;
577         }
578         switch (obj->otyp) {
579         case BELL_OF_OPENING:
580             if (is_cursed)
581                 stripspe(obj);
582             else if (is_blessed)
583                 obj->spe += rnd(3);
584             else
585                 obj->spe += 1;
586             if (obj->spe > 5)
587                 obj->spe = 5;
588             break;
589         case MAGIC_MARKER:
590         case TINNING_KIT:
591         case EXPENSIVE_CAMERA:
592             if (is_cursed)
593                 stripspe(obj);
594             else if (rechrg
595                      && obj->otyp
596                             == MAGIC_MARKER) { /* previously recharged */
597                 obj->recharged = 1; /* override increment done above */
598                 if (obj->spe < 3)
599                     Your("marker seems permanently dried out.");
600                 else
601                     pline1(nothing_happens);
602             } else if (is_blessed) {
603                 n = rn1(16, 15); /* 15..30 */
604                 if (obj->spe + n <= 50)
605                     obj->spe = 50;
606                 else if (obj->spe + n <= 75)
607                     obj->spe = 75;
608                 else {
609                     int chrg = (int) obj->spe;
610                     if ((chrg + n) > 127)
611                         obj->spe = 127;
612                     else
613                         obj->spe += n;
614                 }
615                 p_glow2(obj, NH_BLUE);
616             } else {
617                 n = rn1(11, 10); /* 10..20 */
618                 if (obj->spe + n <= 50)
619                     obj->spe = 50;
620                 else {
621                     int chrg = (int) obj->spe;
622                     if ((chrg + n) > 127)
623                         obj->spe = 127;
624                     else
625                         obj->spe += n;
626                 }
627                 p_glow2(obj, NH_WHITE);
628             }
629             break;
630         case OIL_LAMP:
631         case BRASS_LANTERN:
632             if (is_cursed) {
633                 stripspe(obj);
634                 if (obj->lamplit) {
635                     if (!Blind)
636                         pline("%s out!", Tobjnam(obj, "go"));
637                     end_burn(obj, TRUE);
638                 }
639             } else if (is_blessed) {
640                 obj->spe = 1;
641                 obj->age = 1500;
642                 p_glow2(obj, NH_BLUE);
643             } else {
644                 obj->spe = 1;
645                 obj->age += 750;
646                 if (obj->age > 1500)
647                     obj->age = 1500;
648                 p_glow1(obj);
649             }
650             break;
651         case CRYSTAL_BALL:
652             if (is_cursed) {
653                 stripspe(obj);
654             } else if (is_blessed) {
655                 obj->spe = 6;
656                 p_glow2(obj, NH_BLUE);
657             } else {
658                 if (obj->spe < 5) {
659                     obj->spe++;
660                     p_glow1(obj);
661                 } else
662                     pline1(nothing_happens);
663             }
664             break;
665         case HORN_OF_PLENTY:
666         case BAG_OF_TRICKS:
667         case CAN_OF_GREASE:
668             if (is_cursed) {
669                 stripspe(obj);
670             } else if (is_blessed) {
671                 if (obj->spe <= 10)
672                     obj->spe += rn1(10, 6);
673                 else
674                     obj->spe += rn1(5, 6);
675                 if (obj->spe > 50)
676                     obj->spe = 50;
677                 p_glow2(obj, NH_BLUE);
678             } else {
679                 obj->spe += rnd(5);
680                 if (obj->spe > 50)
681                     obj->spe = 50;
682                 p_glow1(obj);
683             }
684             break;
685         case MAGIC_FLUTE:
686         case MAGIC_HARP:
687         case FROST_HORN:
688         case FIRE_HORN:
689         case DRUM_OF_EARTHQUAKE:
690             if (is_cursed) {
691                 stripspe(obj);
692             } else if (is_blessed) {
693                 obj->spe += d(2, 4);
694                 if (obj->spe > 20)
695                     obj->spe = 20;
696                 p_glow2(obj, NH_BLUE);
697             } else {
698                 obj->spe += rnd(4);
699                 if (obj->spe > 20)
700                     obj->spe = 20;
701                 p_glow1(obj);
702             }
703             break;
704         default:
705             goto not_chargable;
706             /*NOTREACHED*/
707             break;
708         } /* switch */
709 
710     } else {
711     not_chargable:
712         You("have a feeling of loss.");
713     }
714 }
715 
716 /* Forget known information about this object type. */
717 STATIC_OVL void
forget_single_object(obj_id)718 forget_single_object(obj_id)
719 int obj_id;
720 {
721     objects[obj_id].oc_name_known = 0;
722     objects[obj_id].oc_pre_discovered = 0; /* a discovery when relearned */
723     if (objects[obj_id].oc_uname) {
724         free((genericptr_t) objects[obj_id].oc_uname);
725         objects[obj_id].oc_uname = 0;
726     }
727     undiscover_object(obj_id); /* after clearing oc_name_known */
728 
729     /* clear & free object names from matching inventory items too? */
730 }
731 
732 #if 0 /* here if anyone wants it.... */
733 /* Forget everything known about a particular object class. */
734 STATIC_OVL void
735 forget_objclass(oclass)
736 int oclass;
737 {
738     int i;
739 
740     for (i = bases[oclass];
741          i < NUM_OBJECTS && objects[i].oc_class == oclass; i++)
742         forget_single_object(i);
743 }
744 #endif
745 
746 /* randomize the given list of numbers  0 <= i < count */
747 STATIC_OVL void
randomize(indices,count)748 randomize(indices, count)
749 int *indices;
750 int count;
751 {
752     int i, iswap, temp;
753 
754     for (i = count - 1; i > 0; i--) {
755         if ((iswap = rn2(i + 1)) == i)
756             continue;
757         temp = indices[i];
758         indices[i] = indices[iswap];
759         indices[iswap] = temp;
760     }
761 }
762 
763 /* Forget % of known objects. */
764 void
forget_objects(percent)765 forget_objects(percent)
766 int percent;
767 {
768     int i, count;
769     int indices[NUM_OBJECTS];
770 
771     if (percent == 0)
772         return;
773     if (percent <= 0 || percent > 100) {
774         impossible("forget_objects: bad percent %d", percent);
775         return;
776     }
777 
778     indices[0] = 0; /* lint suppression */
779     for (count = 0, i = 1; i < NUM_OBJECTS; i++)
780         if (OBJ_DESCR(objects[i])
781             && (objects[i].oc_name_known || objects[i].oc_uname))
782             indices[count++] = i;
783 
784     if (count > 0) {
785         randomize(indices, count);
786 
787         /* forget first % of randomized indices */
788         count = ((count * percent) + rn2(100)) / 100;
789         for (i = 0; i < count; i++)
790             forget_single_object(indices[i]);
791     }
792 }
793 
794 /* Forget some or all of map (depends on parameters). */
795 void
forget_map(howmuch)796 forget_map(howmuch)
797 int howmuch;
798 {
799     register int zx, zy;
800 
801     if (Sokoban)
802         return;
803 
804     known = TRUE;
805     for (zx = 0; zx < COLNO; zx++)
806         for (zy = 0; zy < ROWNO; zy++)
807             if (howmuch & ALL_MAP || rn2(7)) {
808                 /* Zonk all memory of this location. */
809                 levl[zx][zy].seenv = 0;
810                 levl[zx][zy].waslit = 0;
811                 levl[zx][zy].glyph = cmap_to_glyph(S_stone);
812                 lastseentyp[zx][zy] = STONE;
813             }
814     /* forget overview data for this level */
815     forget_mapseen(ledger_no(&u.uz));
816 }
817 
818 /* Forget all traps on the level. */
819 void
forget_traps()820 forget_traps()
821 {
822     register struct trap *trap;
823 
824     /* forget all traps (except the one the hero is in :-) */
825     for (trap = ftrap; trap; trap = trap->ntrap)
826         if ((trap->tx != u.ux || trap->ty != u.uy) && (trap->ttyp != HOLE))
827             trap->tseen = 0;
828 }
829 
830 /*
831  * Forget given % of all levels that the hero has visited and not forgotten,
832  * except this one.
833  */
834 void
forget_levels(percent)835 forget_levels(percent)
836 int percent;
837 {
838     int i, count;
839     xchar maxl, this_lev;
840     int indices[MAXLINFO];
841 
842     if (percent == 0)
843         return;
844 
845     if (percent <= 0 || percent > 100) {
846         impossible("forget_levels: bad percent %d", percent);
847         return;
848     }
849 
850     this_lev = ledger_no(&u.uz);
851     maxl = maxledgerno();
852 
853     /* count & save indices of non-forgotten visited levels */
854     /* Sokoban levels are pre-mapped for the player, and should stay
855      * so, or they become nearly impossible to solve.  But try to
856      * shift the forgetting elsewhere by fiddling with percent
857      * instead of forgetting fewer levels.
858      */
859     indices[0] = 0; /* lint suppression */
860     for (count = 0, i = 0; i <= maxl; i++)
861         if ((level_info[i].flags & VISITED)
862             && !(level_info[i].flags & FORGOTTEN) && i != this_lev) {
863             if (ledger_to_dnum(i) == sokoban_dnum)
864                 percent += 2;
865             else
866                 indices[count++] = i;
867         }
868 
869     if (percent > 100)
870         percent = 100;
871 
872     if (count > 0) {
873         randomize(indices, count);
874 
875         /* forget first % of randomized indices */
876         count = ((count * percent) + 50) / 100;
877         for (i = 0; i < count; i++) {
878             level_info[indices[i]].flags |= FORGOTTEN;
879             forget_mapseen(indices[i]);
880         }
881     }
882 }
883 
884 /*
885  * Forget some things (e.g. after reading a scroll of amnesia).  When called,
886  * the following are always forgotten:
887  *      - felt ball & chain
888  *      - traps
889  *      - part (6 out of 7) of the map
890  *
891  * Other things are subject to flags:
892  *      howmuch & ALL_MAP       = forget whole map
893  *      howmuch & ALL_SPELLS    = forget all spells
894  */
895 STATIC_OVL void
forget(howmuch)896 forget(howmuch)
897 int howmuch;
898 {
899     if (Punished)
900         u.bc_felt = 0; /* forget felt ball&chain */
901 
902     forget_map(howmuch);
903     forget_traps();
904 
905     /* 1 in 3 chance of forgetting some levels */
906     if (!rn2(3))
907         forget_levels(rn2(25));
908 
909     /* 1 in 3 chance of forgetting some objects */
910     if (!rn2(3))
911         forget_objects(rn2(25));
912 
913     if (howmuch & ALL_SPELLS)
914         losespells();
915     /*
916      * Make sure that what was seen is restored correctly.  To do this,
917      * we need to go blind for an instant --- turn off the display,
918      * then restart it.  All this work is needed to correctly handle
919      * walls which are stone on one side and wall on the other.  Turning
920      * off the seen bits above will make the wall revert to stone,  but
921      * there are cases where we don't want this to happen.  The easiest
922      * thing to do is to run it through the vision system again, which
923      * is always correct.
924      */
925     docrt(); /* this correctly will reset vision */
926 }
927 
928 /* monster is hit by scroll of taming's effect */
929 STATIC_OVL int
maybe_tame(mtmp,sobj)930 maybe_tame(mtmp, sobj)
931 struct monst *mtmp;
932 struct obj *sobj;
933 {
934     int was_tame = mtmp->mtame;
935     unsigned was_peaceful = mtmp->mpeaceful;
936 
937     if (sobj->cursed) {
938         setmangry(mtmp, FALSE);
939         if (was_peaceful && !mtmp->mpeaceful)
940             return -1;
941     } else {
942         if (mtmp->isshk)
943             make_happy_shk(mtmp, FALSE);
944         else if (!resist(mtmp, sobj->oclass, 0, NOTELL))
945             (void) tamedog(mtmp, (struct obj *) 0);
946         if ((!was_peaceful && mtmp->mpeaceful) || (!was_tame && mtmp->mtame))
947             return 1;
948     }
949     return 0;
950 }
951 
952 STATIC_OVL boolean
get_valid_stinking_cloud_pos(x,y)953 get_valid_stinking_cloud_pos(x,y)
954 int x,y;
955 {
956     return (!(!isok(x,y) || !cansee(x, y)
957               || !ACCESSIBLE(levl[x][y].typ)
958               || distu(x, y) >= 32));
959 }
960 
961 STATIC_OVL boolean
is_valid_stinking_cloud_pos(x,y,showmsg)962 is_valid_stinking_cloud_pos(x, y, showmsg)
963 int x, y;
964 boolean showmsg;
965 {
966     if (!get_valid_stinking_cloud_pos(x,y)) {
967         if (showmsg)
968             You("smell rotten eggs.");
969         return FALSE;
970     }
971     return TRUE;
972 }
973 
974 STATIC_PTR void
display_stinking_cloud_positions(state)975 display_stinking_cloud_positions(state)
976 int state;
977 {
978     if (state == 0) {
979         tmp_at(DISP_BEAM, cmap_to_glyph(S_goodpos));
980     } else if (state == 1) {
981         int x, y, dx, dy;
982         int dist = 6;
983 
984         for (dx = -dist; dx <= dist; dx++)
985             for (dy = -dist; dy <= dist; dy++) {
986                 x = u.ux + dx;
987                 y = u.uy + dy;
988                 if (get_valid_stinking_cloud_pos(x,y))
989                     tmp_at(x, y);
990             }
991     } else {
992         tmp_at(DISP_END, 0);
993     }
994 }
995 
996 /* scroll effects; return 1 if we use up the scroll and possibly make it
997    become discovered, 0 if caller should take care of those side-effects */
998 int
seffects(sobj)999 seffects(sobj)
1000 struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */
1001 {
1002     int cval, otyp = sobj->otyp;
1003     boolean confused = (Confusion != 0), sblessed = sobj->blessed,
1004             scursed = sobj->cursed, already_known, old_erodeproof,
1005             new_erodeproof;
1006     struct obj *otmp;
1007 
1008     if (objects[otyp].oc_magic)
1009         exercise(A_WIS, TRUE);                       /* just for trying */
1010     already_known = (sobj->oclass == SPBOOK_CLASS /* spell */
1011                      || objects[otyp].oc_name_known);
1012 
1013     switch (otyp) {
1014 #ifdef MAIL
1015     case SCR_MAIL:
1016         known = TRUE;
1017         if (sobj->spe == 2)
1018             /* "stamped scroll" created via magic marker--without a stamp */
1019             pline("This scroll is marked \"postage due\".");
1020         else if (sobj->spe)
1021             /* scroll of mail obtained from bones file or from wishing;
1022              * note to the puzzled: the game Larn actually sends you junk
1023              * mail if you win!
1024              */
1025             pline(
1026     "This seems to be junk mail addressed to the finder of the Eye of Larn.");
1027         else
1028             readmail(sobj);
1029         break;
1030 #endif
1031     case SCR_ENCHANT_ARMOR: {
1032         register schar s;
1033         boolean special_armor;
1034         boolean same_color;
1035 
1036         otmp = some_armor(&youmonst);
1037         if (!otmp) {
1038             strange_feeling(sobj, !Blind
1039                                       ? "Your skin glows then fades."
1040                                       : "Your skin feels warm for a moment.");
1041             sobj = 0; /* useup() in strange_feeling() */
1042             exercise(A_CON, !scursed);
1043             exercise(A_STR, !scursed);
1044             break;
1045         }
1046         if (confused) {
1047             old_erodeproof = (otmp->oerodeproof != 0);
1048             new_erodeproof = !scursed;
1049             otmp->oerodeproof = 0; /* for messages */
1050             if (Blind) {
1051                 otmp->rknown = FALSE;
1052                 pline("%s warm for a moment.", Yobjnam2(otmp, "feel"));
1053             } else {
1054                 otmp->rknown = TRUE;
1055                 pline("%s covered by a %s %s %s!", Yobjnam2(otmp, "are"),
1056                       scursed ? "mottled" : "shimmering",
1057                       hcolor(scursed ? NH_BLACK : NH_GOLDEN),
1058                       scursed ? "glow"
1059                               : (is_shield(otmp) ? "layer" : "shield"));
1060             }
1061             if (new_erodeproof && (otmp->oeroded || otmp->oeroded2)) {
1062                 otmp->oeroded = otmp->oeroded2 = 0;
1063                 pline("%s as good as new!",
1064                       Yobjnam2(otmp, Blind ? "feel" : "look"));
1065             }
1066             if (old_erodeproof && !new_erodeproof) {
1067                 /* restore old_erodeproof before shop charges */
1068                 otmp->oerodeproof = 1;
1069                 costly_alteration(otmp, COST_DEGRD);
1070             }
1071             otmp->oerodeproof = new_erodeproof ? 1 : 0;
1072             break;
1073         }
1074         /* elven armor vibrates warningly when enchanted beyond a limit */
1075         special_armor = is_elven_armor(otmp)
1076                         || (Role_if(PM_WIZARD) && otmp->otyp == CORNUTHAUM);
1077         if (scursed)
1078             same_color = (otmp->otyp == BLACK_DRAGON_SCALE_MAIL
1079                           || otmp->otyp == BLACK_DRAGON_SCALES);
1080         else
1081             same_color = (otmp->otyp == SILVER_DRAGON_SCALE_MAIL
1082                           || otmp->otyp == SILVER_DRAGON_SCALES
1083                           || otmp->otyp == SHIELD_OF_REFLECTION);
1084         if (Blind)
1085             same_color = FALSE;
1086 
1087         /* KMH -- catch underflow */
1088         s = scursed ? -otmp->spe : otmp->spe;
1089         if (s > (special_armor ? 5 : 3) && rn2(s)) {
1090             otmp->in_use = TRUE;
1091             pline("%s violently %s%s%s for a while, then %s.", Yname2(otmp),
1092                   otense(otmp, Blind ? "vibrate" : "glow"),
1093                   (!Blind && !same_color) ? " " : "",
1094                   (Blind || same_color) ? "" : hcolor(scursed ? NH_BLACK
1095                                                               : NH_SILVER),
1096                   otense(otmp, "evaporate"));
1097             remove_worn_item(otmp, FALSE);
1098             useup(otmp);
1099             break;
1100         }
1101         s = scursed ? -1
1102                     : (otmp->spe >= 9)
1103                        ? (rn2(otmp->spe) == 0)
1104                        : sblessed
1105                           ? rnd(3 - otmp->spe / 3)
1106                           : 1;
1107         if (s >= 0 && Is_dragon_scales(otmp)) {
1108             /* dragon scales get turned into dragon scale mail */
1109             pline("%s merges and hardens!", Yname2(otmp));
1110             setworn((struct obj *) 0, W_ARM);
1111             /* assumes same order */
1112             otmp->otyp += GRAY_DRAGON_SCALE_MAIL - GRAY_DRAGON_SCALES;
1113             if (sblessed) {
1114                 otmp->spe++;
1115                 if (!otmp->blessed)
1116                     bless(otmp);
1117             } else if (otmp->cursed)
1118                 uncurse(otmp);
1119             otmp->known = 1;
1120             setworn(otmp, W_ARM);
1121             if (otmp->unpaid)
1122                 alter_cost(otmp, 0L); /* shop bill */
1123             break;
1124         }
1125         pline("%s %s%s%s%s for a %s.", Yname2(otmp),
1126               s == 0 ? "violently " : "",
1127               otense(otmp, Blind ? "vibrate" : "glow"),
1128               (!Blind && !same_color) ? " " : "",
1129               (Blind || same_color)
1130                  ? "" : hcolor(scursed ? NH_BLACK : NH_SILVER),
1131               (s * s > 1) ? "while" : "moment");
1132         /* [this cost handling will need updating if shop pricing is
1133            ever changed to care about curse/bless status of armor] */
1134         if (s < 0)
1135             costly_alteration(otmp, COST_DECHNT);
1136         if (scursed && !otmp->cursed)
1137             curse(otmp);
1138         else if (sblessed && !otmp->blessed)
1139             bless(otmp);
1140         else if (!scursed && otmp->cursed)
1141             uncurse(otmp);
1142         if (s) {
1143             otmp->spe += s;
1144             adj_abon(otmp, s);
1145             known = otmp->known;
1146             /* update shop bill to reflect new higher price */
1147             if (s > 0 && otmp->unpaid)
1148                 alter_cost(otmp, 0L);
1149         }
1150 
1151         if ((otmp->spe > (special_armor ? 5 : 3))
1152             && (special_armor || !rn2(7)))
1153             pline("%s %s.", Yobjnam2(otmp, "suddenly vibrate"),
1154                   Blind ? "again" : "unexpectedly");
1155         break;
1156     }
1157     case SCR_DESTROY_ARMOR: {
1158         otmp = some_armor(&youmonst);
1159         if (confused) {
1160             if (!otmp) {
1161                 strange_feeling(sobj, "Your bones itch.");
1162                 sobj = 0; /* useup() in strange_feeling() */
1163                 exercise(A_STR, FALSE);
1164                 exercise(A_CON, FALSE);
1165                 break;
1166             }
1167             old_erodeproof = (otmp->oerodeproof != 0);
1168             new_erodeproof = scursed;
1169             otmp->oerodeproof = 0; /* for messages */
1170             p_glow2(otmp, NH_PURPLE);
1171             if (old_erodeproof && !new_erodeproof) {
1172                 /* restore old_erodeproof before shop charges */
1173                 otmp->oerodeproof = 1;
1174                 costly_alteration(otmp, COST_DEGRD);
1175             }
1176             otmp->oerodeproof = new_erodeproof ? 1 : 0;
1177             break;
1178         }
1179         if (!scursed || !otmp || !otmp->cursed) {
1180             if (!destroy_arm(otmp)) {
1181                 strange_feeling(sobj, "Your skin itches.");
1182                 sobj = 0; /* useup() in strange_feeling() */
1183                 exercise(A_STR, FALSE);
1184                 exercise(A_CON, FALSE);
1185                 break;
1186             } else
1187                 known = TRUE;
1188         } else { /* armor and scroll both cursed */
1189             pline("%s.", Yobjnam2(otmp, "vibrate"));
1190             if (otmp->spe >= -6) {
1191                 otmp->spe += -1;
1192                 adj_abon(otmp, -1);
1193             }
1194             make_stunned((HStun & TIMEOUT) + (long) rn1(10, 10), TRUE);
1195         }
1196     } break;
1197     case SCR_CONFUSE_MONSTER:
1198     case SPE_CONFUSE_MONSTER:
1199         if (youmonst.data->mlet != S_HUMAN || scursed) {
1200             if (!HConfusion)
1201                 You_feel("confused.");
1202             make_confused(HConfusion + rnd(100), FALSE);
1203         } else if (confused) {
1204             if (!sblessed) {
1205                 Your("%s begin to %s%s.", makeplural(body_part(HAND)),
1206                      Blind ? "tingle" : "glow ",
1207                      Blind ? "" : hcolor(NH_PURPLE));
1208                 make_confused(HConfusion + rnd(100), FALSE);
1209             } else {
1210                 pline("A %s%s surrounds your %s.",
1211                       Blind ? "" : hcolor(NH_RED),
1212                       Blind ? "faint buzz" : " glow", body_part(HEAD));
1213                 make_confused(0L, TRUE);
1214             }
1215         } else {
1216             if (!sblessed) {
1217                 Your("%s%s %s%s.", makeplural(body_part(HAND)),
1218                      Blind ? "" : " begin to glow",
1219                      Blind ? (const char *) "tingle" : hcolor(NH_RED),
1220                      u.umconf ? " even more" : "");
1221                 u.umconf++;
1222             } else {
1223                 if (Blind)
1224                     Your("%s tingle %s sharply.", makeplural(body_part(HAND)),
1225                          u.umconf ? "even more" : "very");
1226                 else
1227                     Your("%s glow a%s brilliant %s.",
1228                          makeplural(body_part(HAND)),
1229                          u.umconf ? "n even more" : "", hcolor(NH_RED));
1230                 /* after a while, repeated uses become less effective */
1231                 if (u.umconf >= 40)
1232                     u.umconf++;
1233                 else
1234                     u.umconf += rn1(8, 2);
1235             }
1236         }
1237         break;
1238     case SCR_SCARE_MONSTER:
1239     case SPE_CAUSE_FEAR: {
1240         register int ct = 0;
1241         register struct monst *mtmp;
1242 
1243         for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
1244             if (DEADMONSTER(mtmp))
1245                 continue;
1246             if (cansee(mtmp->mx, mtmp->my)) {
1247                 if (confused || scursed) {
1248                     mtmp->mflee = mtmp->mfrozen = mtmp->msleeping = 0;
1249                     mtmp->mcanmove = 1;
1250                 } else if (!resist(mtmp, sobj->oclass, 0, NOTELL))
1251                     monflee(mtmp, 0, FALSE, FALSE);
1252                 if (!mtmp->mtame)
1253                     ct++; /* pets don't laugh at you */
1254             }
1255         }
1256         if (otyp == SCR_SCARE_MONSTER || !ct)
1257             You_hear("%s %s.", (confused || scursed) ? "sad wailing"
1258                                                      : "maniacal laughter",
1259                      !ct ? "in the distance" : "close by");
1260         break;
1261     }
1262     case SCR_BLANK_PAPER:
1263         if (Blind)
1264             You("don't remember there being any magic words on this scroll.");
1265         else
1266             pline("This scroll seems to be blank.");
1267         known = TRUE;
1268         break;
1269     case SCR_REMOVE_CURSE:
1270     case SPE_REMOVE_CURSE: {
1271         register struct obj *obj;
1272 
1273         You_feel(!Hallucination
1274                      ? (!confused ? "like someone is helping you."
1275                                   : "like you need some help.")
1276                      : (!confused ? "in touch with the Universal Oneness."
1277                                   : "the power of the Force against you!"));
1278 
1279         if (scursed) {
1280             pline_The("scroll disintegrates.");
1281         } else {
1282             for (obj = invent; obj; obj = obj->nobj) {
1283                 long wornmask;
1284 
1285                 /* gold isn't subject to cursing and blessing */
1286                 if (obj->oclass == COIN_CLASS)
1287                     continue;
1288                 /* hide current scroll from itself so that perm_invent won't
1289                    show known blessed scroll losing bknown when confused */
1290                 if (obj == sobj && obj->quan == 1L)
1291                     continue;
1292                 wornmask = (obj->owornmask & ~(W_BALL | W_ART | W_ARTI));
1293                 if (wornmask && !sblessed) {
1294                     /* handle a couple of special cases; we don't
1295                        allow auxiliary weapon slots to be used to
1296                        artificially increase number of worn items */
1297                     if (obj == uswapwep) {
1298                         if (!u.twoweap)
1299                             wornmask = 0L;
1300                     } else if (obj == uquiver) {
1301                         if (obj->oclass == WEAPON_CLASS) {
1302                             /* mergeable weapon test covers ammo,
1303                                missiles, spears, daggers & knives */
1304                             if (!objects[obj->otyp].oc_merge)
1305                                 wornmask = 0L;
1306                         } else if (obj->oclass == GEM_CLASS) {
1307                             /* possibly ought to check whether
1308                                alternate weapon is a sling... */
1309                             if (!uslinging())
1310                                 wornmask = 0L;
1311                         } else {
1312                             /* weptools don't merge and aren't
1313                                reasonable quivered weapons */
1314                             wornmask = 0L;
1315                         }
1316                     }
1317                 }
1318                 if (sblessed || wornmask || obj->otyp == LOADSTONE
1319                     || (obj->otyp == LEASH && obj->leashmon)) {
1320                     /* water price varies by curse/bless status */
1321                     boolean shop_h2o = (obj->unpaid && obj->otyp == POT_WATER);
1322 
1323                     if (confused) {
1324                         blessorcurse(obj, 2);
1325                         /* lose knowledge of this object's curse/bless
1326                            state (even if it didn't actually change) */
1327                         obj->bknown = 0;
1328                         /* blessorcurse() only affects uncursed items
1329                            so no need to worry about price of water
1330                            going down (hence no costly_alteration) */
1331                         if (shop_h2o && (obj->cursed || obj->blessed))
1332                             alter_cost(obj, 0L); /* price goes up */
1333                     } else if (obj->cursed) {
1334                         if (shop_h2o)
1335                             costly_alteration(obj, COST_UNCURS);
1336                         uncurse(obj);
1337                     }
1338                 }
1339             }
1340         }
1341         if (Punished && !confused)
1342             unpunish();
1343         if (u.utrap && u.utraptype == TT_BURIEDBALL) {
1344             buried_ball_to_freedom();
1345             pline_The("clasp on your %s vanishes.", body_part(LEG));
1346         }
1347         update_inventory();
1348         break;
1349     }
1350     case SCR_CREATE_MONSTER:
1351     case SPE_CREATE_MONSTER:
1352         if (create_critters(1 + ((confused || scursed) ? 12 : 0)
1353                                 + ((sblessed || rn2(73)) ? 0 : rnd(4)),
1354                             confused ? &mons[PM_ACID_BLOB]
1355                                      : (struct permonst *) 0,
1356                             FALSE))
1357             known = TRUE;
1358         /* no need to flush monsters; we ask for identification only if the
1359          * monsters are not visible
1360          */
1361         break;
1362     case SCR_ENCHANT_WEAPON:
1363         /* [What about twoweapon mode?  Proofing/repairing/enchanting both
1364            would be too powerful, but shouldn't we choose randomly between
1365            primary and secondary instead of always acting on primary?] */
1366         if (confused && uwep
1367             && erosion_matters(uwep) && uwep->oclass != ARMOR_CLASS) {
1368             old_erodeproof = (uwep->oerodeproof != 0);
1369             new_erodeproof = !scursed;
1370             uwep->oerodeproof = 0; /* for messages */
1371             if (Blind) {
1372                 uwep->rknown = FALSE;
1373                 Your("weapon feels warm for a moment.");
1374             } else {
1375                 uwep->rknown = TRUE;
1376                 pline("%s covered by a %s %s %s!", Yobjnam2(uwep, "are"),
1377                       scursed ? "mottled" : "shimmering",
1378                       hcolor(scursed ? NH_PURPLE : NH_GOLDEN),
1379                       scursed ? "glow" : "shield");
1380             }
1381             if (new_erodeproof && (uwep->oeroded || uwep->oeroded2)) {
1382                 uwep->oeroded = uwep->oeroded2 = 0;
1383                 pline("%s as good as new!",
1384                       Yobjnam2(uwep, Blind ? "feel" : "look"));
1385             }
1386             if (old_erodeproof && !new_erodeproof) {
1387                 /* restore old_erodeproof before shop charges */
1388                 uwep->oerodeproof = 1;
1389                 costly_alteration(uwep, COST_DEGRD);
1390             }
1391             uwep->oerodeproof = new_erodeproof ? 1 : 0;
1392             break;
1393         }
1394         if (!chwepon(sobj, scursed ? -1
1395                              : !uwep ? 1
1396                                : (uwep->spe >= 9) ? !rn2(uwep->spe)
1397                                  : sblessed ? rnd(3 - uwep->spe / 3)
1398                                    : 1))
1399             sobj = 0; /* nothing enchanted: strange_feeling -> useup */
1400         break;
1401     case SCR_TAMING:
1402     case SPE_CHARM_MONSTER: {
1403         int candidates, res, results, vis_results;
1404 
1405         if (u.uswallow) {
1406             candidates = 1;
1407             results = vis_results = maybe_tame(u.ustuck, sobj);
1408         } else {
1409             int i, j, bd = confused ? 5 : 1;
1410             struct monst *mtmp;
1411 
1412             /* note: maybe_tame() can return either positive or
1413                negative values, but not both for the same scroll */
1414             candidates = results = vis_results = 0;
1415             for (i = -bd; i <= bd; i++)
1416                 for (j = -bd; j <= bd; j++) {
1417                     if (!isok(u.ux + i, u.uy + j))
1418                         continue;
1419                     if ((mtmp = m_at(u.ux + i, u.uy + j)) != 0
1420                         || (!i && !j && (mtmp = u.usteed) != 0)) {
1421                         ++candidates;
1422                         res = maybe_tame(mtmp, sobj);
1423                         results += res;
1424                         if (canspotmon(mtmp))
1425                             vis_results += res;
1426                     }
1427                 }
1428         }
1429         if (!results) {
1430             pline("Nothing interesting %s.",
1431                   !candidates ? "happens" : "seems to happen");
1432         } else {
1433             pline_The("neighborhood %s %sfriendlier.",
1434                       vis_results ? "is" : "seems",
1435                       (results < 0) ? "un" : "");
1436             if (vis_results > 0)
1437                 known = TRUE;
1438         }
1439         break;
1440     }
1441     case SCR_GENOCIDE:
1442         if (!already_known)
1443             You("have found a scroll of genocide!");
1444         known = TRUE;
1445         if (sblessed)
1446             do_class_genocide();
1447         else
1448             do_genocide((!scursed) | (2 * !!Confusion));
1449         break;
1450     case SCR_LIGHT:
1451         if (!confused || rn2(5)) {
1452             if (!Blind)
1453                 known = TRUE;
1454             litroom(!confused && !scursed, sobj);
1455             if (!confused && !scursed) {
1456                 if (lightdamage(sobj, TRUE, 5))
1457                     known = TRUE;
1458             }
1459         } else {
1460             /* could be scroll of create monster, don't set known ...*/
1461             (void) create_critters(1, !scursed ? &mons[PM_YELLOW_LIGHT]
1462                                                : &mons[PM_BLACK_LIGHT],
1463                                    TRUE);
1464         }
1465         break;
1466     case SCR_TELEPORTATION:
1467         if (confused || scursed) {
1468             level_tele();
1469         } else {
1470             known = scrolltele(sobj);
1471         }
1472         break;
1473     case SCR_GOLD_DETECTION:
1474         if ((confused || scursed) ? trap_detect(sobj) : gold_detect(sobj))
1475             sobj = 0; /* failure: strange_feeling() -> useup() */
1476         break;
1477     case SCR_FOOD_DETECTION:
1478     case SPE_DETECT_FOOD:
1479         if (food_detect(sobj))
1480             sobj = 0; /* nothing detected: strange_feeling -> useup */
1481         break;
1482     case SCR_IDENTIFY:
1483         /* known = TRUE; -- handled inline here */
1484         /* use up the scroll first, before makeknown() performs a
1485            perm_invent update; also simplifies empty invent check */
1486         useup(sobj);
1487         sobj = 0; /* it's gone */
1488         if (confused)
1489             You("identify this as an identify scroll.");
1490         else if (!already_known || !invent)
1491             /* force feedback now if invent became
1492                empty after using up this scroll */
1493             pline("This is an identify scroll.");
1494         if (!already_known)
1495             (void) learnscrolltyp(SCR_IDENTIFY);
1496         /*FALLTHRU*/
1497     case SPE_IDENTIFY:
1498         cval = 1;
1499         if (sblessed || (!scursed && !rn2(5))) {
1500             cval = rn2(5);
1501             /* note: if cval==0, identify all items */
1502             if (cval == 1 && sblessed && Luck > 0)
1503                 ++cval;
1504         }
1505         if (invent && !confused) {
1506             identify_pack(cval, !already_known);
1507         } else if (otyp == SPE_IDENTIFY) {
1508             /* when casting a spell we know we're not confused,
1509                so inventory must be empty (another message has
1510                already been given above if reading a scroll) */
1511             pline("You're not carrying anything to be identified.");
1512         }
1513         break;
1514     case SCR_CHARGING:
1515         if (confused) {
1516             if (scursed) {
1517                 You_feel("discharged.");
1518                 u.uen = 0;
1519             } else {
1520                 You_feel("charged up!");
1521                 u.uen += d(sblessed ? 6 : 4, 4);
1522                 if (u.uen > u.uenmax) /* if current energy is already at   */
1523                     u.uenmax = u.uen; /* or near maximum, increase maximum */
1524                 else
1525                     u.uen = u.uenmax; /* otherwise restore current to max  */
1526             }
1527             context.botl = 1;
1528             break;
1529         }
1530         /* known = TRUE; -- handled inline here */
1531         if (!already_known) {
1532             pline("This is a charging scroll.");
1533             learnscroll(sobj);
1534         }
1535         /* use it up now to prevent it from showing in the
1536            getobj picklist because the "disappears" message
1537            was already delivered */
1538         useup(sobj);
1539         sobj = 0; /* it's gone */
1540         otmp = getobj(all_count, "charge");
1541         if (otmp)
1542             recharge(otmp, scursed ? -1 : sblessed ? 1 : 0);
1543         break;
1544     case SCR_MAGIC_MAPPING:
1545         if (level.flags.nommap) {
1546             Your("mind is filled with crazy lines!");
1547             if (Hallucination)
1548                 pline("Wow!  Modern art.");
1549             else
1550                 Your("%s spins in bewilderment.", body_part(HEAD));
1551             make_confused(HConfusion + rnd(30), FALSE);
1552             break;
1553         }
1554         if (sblessed) {
1555             register int x, y;
1556 
1557             for (x = 1; x < COLNO; x++)
1558                 for (y = 0; y < ROWNO; y++)
1559                     if (levl[x][y].typ == SDOOR)
1560                         cvt_sdoor_to_door(&levl[x][y]);
1561             /* do_mapping() already reveals secret passages */
1562         }
1563         known = TRUE;
1564         /*FALLTHRU*/
1565     case SPE_MAGIC_MAPPING:
1566         if (level.flags.nommap) {
1567             Your("%s spins as %s blocks the spell!", body_part(HEAD),
1568                  something);
1569             make_confused(HConfusion + rnd(30), FALSE);
1570             break;
1571         }
1572         pline("A map coalesces in your mind!");
1573         cval = (scursed && !confused);
1574         if (cval)
1575             HConfusion = 1; /* to screw up map */
1576         do_mapping();
1577         if (cval) {
1578             HConfusion = 0; /* restore */
1579             pline("Unfortunately, you can't grasp the details.");
1580         }
1581         break;
1582     case SCR_AMNESIA:
1583         known = TRUE;
1584         forget((!sblessed ? ALL_SPELLS : 0)
1585                | (!confused || scursed ? ALL_MAP : 0));
1586         if (Hallucination) /* Ommmmmm! */
1587             Your("mind releases itself from mundane concerns.");
1588         else if (!strncmpi(plname, "Maud", 4))
1589             pline(
1590           "As your mind turns inward on itself, you forget everything else.");
1591         else if (rn2(2))
1592             pline("Who was that Maud person anyway?");
1593         else
1594             pline("Thinking of Maud you forget everything else.");
1595         exercise(A_WIS, FALSE);
1596         break;
1597     case SCR_FIRE: {
1598         coord cc;
1599         int dam;
1600 
1601         cc.x = u.ux;
1602         cc.y = u.uy;
1603         cval = bcsign(sobj);
1604         dam = (2 * (rn1(3, 3) + 2 * cval) + 1) / 3;
1605         useup(sobj);
1606         sobj = 0; /* it's gone */
1607         if (!already_known)
1608             (void) learnscrolltyp(SCR_FIRE);
1609         if (confused) {
1610             if (Fire_resistance) {
1611                 shieldeff(u.ux, u.uy);
1612                 if (!Blind)
1613                     pline("Oh, look, what a pretty fire in your %s.",
1614                           makeplural(body_part(HAND)));
1615                 else
1616                     You_feel("a pleasant warmth in your %s.",
1617                              makeplural(body_part(HAND)));
1618             } else {
1619                 pline_The("scroll catches fire and you burn your %s.",
1620                           makeplural(body_part(HAND)));
1621                 losehp(1, "scroll of fire", KILLED_BY_AN);
1622             }
1623             break;
1624         }
1625         if (Underwater) {
1626             pline_The("%s around you vaporizes violently!", hliquid("water"));
1627         } else {
1628             if (sblessed) {
1629                 if (!already_known)
1630                     pline("This is a scroll of fire!");
1631                 dam *= 5;
1632                 pline("Where do you want to center the explosion?");
1633                 getpos_sethilite(display_stinking_cloud_positions,
1634                                  get_valid_stinking_cloud_pos);
1635                 (void) getpos(&cc, TRUE, "the desired position");
1636                 if (!is_valid_stinking_cloud_pos(cc.x, cc.y, FALSE)) {
1637                     /* try to reach too far, get burned */
1638                     cc.x = u.ux;
1639                     cc.y = u.uy;
1640                 }
1641             }
1642             if (cc.x == u.ux && cc.y == u.uy) {
1643                 pline_The("scroll erupts in a tower of flame!");
1644                 iflags.last_msg = PLNMSG_TOWER_OF_FLAME; /* for explode() */
1645                 burn_away_slime();
1646             }
1647         }
1648         explode(cc.x, cc.y, 11, dam, SCROLL_CLASS, EXPL_FIERY);
1649         break;
1650     }
1651     case SCR_EARTH:
1652         /* TODO: handle steeds */
1653         if (!Is_rogue_level(&u.uz) && has_ceiling(&u.uz)
1654             && (!In_endgame(&u.uz) || Is_earthlevel(&u.uz))) {
1655             register int x, y;
1656             int nboulders = 0;
1657 
1658             /* Identify the scroll */
1659             if (u.uswallow)
1660                 You_hear("rumbling.");
1661             else
1662                 pline_The("%s rumbles %s you!", ceiling(u.ux, u.uy),
1663                           sblessed ? "around" : "above");
1664             known = 1;
1665             sokoban_guilt();
1666 
1667             /* Loop through the surrounding squares */
1668             if (!scursed)
1669                 for (x = u.ux - 1; x <= u.ux + 1; x++) {
1670                     for (y = u.uy - 1; y <= u.uy + 1; y++) {
1671                         /* Is this a suitable spot? */
1672                         if (isok(x, y) && !closed_door(x, y)
1673                             && !IS_ROCK(levl[x][y].typ)
1674                             && !IS_AIR(levl[x][y].typ)
1675                             && (x != u.ux || y != u.uy)) {
1676                             nboulders +=
1677                                 drop_boulder_on_monster(x, y, confused, TRUE);
1678                         }
1679                     }
1680                 }
1681             /* Attack the player */
1682             if (!sblessed) {
1683                 drop_boulder_on_player(confused, !scursed, TRUE, FALSE);
1684             } else if (!nboulders)
1685                 pline("But nothing else happens.");
1686         }
1687         break;
1688     case SCR_PUNISHMENT:
1689         known = TRUE;
1690         if (confused || sblessed) {
1691             You_feel("guilty.");
1692             break;
1693         }
1694         punish(sobj);
1695         break;
1696     case SCR_STINKING_CLOUD: {
1697         coord cc;
1698 
1699         if (!already_known)
1700             You("have found a scroll of stinking cloud!");
1701         known = TRUE;
1702         pline("Where do you want to center the %scloud?",
1703               already_known ? "stinking " : "");
1704         cc.x = u.ux;
1705         cc.y = u.uy;
1706         getpos_sethilite(display_stinking_cloud_positions,
1707                          get_valid_stinking_cloud_pos);
1708         if (getpos(&cc, TRUE, "the desired position") < 0) {
1709             pline1(Never_mind);
1710             break;
1711         }
1712         if (!is_valid_stinking_cloud_pos(cc.x, cc.y, TRUE))
1713             break;
1714         (void) create_gas_cloud(cc.x, cc.y, 3 + bcsign(sobj),
1715                                 8 + 4 * bcsign(sobj));
1716         break;
1717     }
1718     default:
1719         impossible("What weird effect is this? (%u)", otyp);
1720     }
1721     /* if sobj is gone, we've already called useup() above and the
1722        update_inventory() that it performs might have come too soon
1723        (before charging an item, for instance) */
1724     if (!sobj)
1725         update_inventory();
1726     return sobj ? 0 : 1;
1727 }
1728 
1729 void
drop_boulder_on_player(confused,helmet_protects,byu,skip_uswallow)1730 drop_boulder_on_player(confused, helmet_protects, byu, skip_uswallow)
1731 boolean confused, helmet_protects, byu, skip_uswallow;
1732 {
1733     int dmg;
1734     struct obj *otmp2;
1735 
1736     /* hit monster if swallowed */
1737     if (u.uswallow && !skip_uswallow) {
1738         drop_boulder_on_monster(u.ux, u.uy, confused, byu);
1739         return;
1740     }
1741 
1742     otmp2 = mksobj(confused ? ROCK : BOULDER, FALSE, FALSE);
1743     if (!otmp2)
1744         return;
1745     otmp2->quan = confused ? rn1(5, 2) : 1;
1746     otmp2->owt = weight(otmp2);
1747     if (!amorphous(youmonst.data) && !Passes_walls
1748         && !noncorporeal(youmonst.data) && !unsolid(youmonst.data)) {
1749         You("are hit by %s!", doname(otmp2));
1750         dmg = dmgval(otmp2, &youmonst) * otmp2->quan;
1751         if (uarmh && helmet_protects) {
1752             if (is_metallic(uarmh)) {
1753                 pline("Fortunately, you are wearing a hard helmet.");
1754                 if (dmg > 2)
1755                     dmg = 2;
1756             } else if (flags.verbose) {
1757                 pline("%s does not protect you.", Yname2(uarmh));
1758             }
1759         }
1760     } else
1761         dmg = 0;
1762     wake_nearto(u.ux, u.uy, 4 * 4);
1763     /* Must be before the losehp(), for bones files */
1764     if (!flooreffects(otmp2, u.ux, u.uy, "fall")) {
1765         place_object(otmp2, u.ux, u.uy);
1766         stackobj(otmp2);
1767         newsym(u.ux, u.uy);
1768     }
1769     if (dmg)
1770         losehp(Maybe_Half_Phys(dmg), "scroll of earth", KILLED_BY_AN);
1771 }
1772 
1773 boolean
drop_boulder_on_monster(x,y,confused,byu)1774 drop_boulder_on_monster(x, y, confused, byu)
1775 int x, y;
1776 boolean confused, byu;
1777 {
1778     register struct obj *otmp2;
1779     register struct monst *mtmp;
1780 
1781     /* Make the object(s) */
1782     otmp2 = mksobj(confused ? ROCK : BOULDER, FALSE, FALSE);
1783     if (!otmp2)
1784         return FALSE; /* Shouldn't happen */
1785     otmp2->quan = confused ? rn1(5, 2) : 1;
1786     otmp2->owt = weight(otmp2);
1787 
1788     /* Find the monster here (won't be player) */
1789     mtmp = m_at(x, y);
1790     if (mtmp && !amorphous(mtmp->data) && !passes_walls(mtmp->data)
1791         && !noncorporeal(mtmp->data) && !unsolid(mtmp->data)) {
1792         struct obj *helmet = which_armor(mtmp, W_ARMH);
1793         int mdmg;
1794 
1795         if (cansee(mtmp->mx, mtmp->my)) {
1796             pline("%s is hit by %s!", Monnam(mtmp), doname(otmp2));
1797             if (mtmp->minvis && !canspotmon(mtmp))
1798                 map_invisible(mtmp->mx, mtmp->my);
1799         } else if (u.uswallow && mtmp == u.ustuck)
1800             You_hear("something hit %s %s over your %s!",
1801                      s_suffix(mon_nam(mtmp)), mbodypart(mtmp, STOMACH),
1802                      body_part(HEAD));
1803 
1804         mdmg = dmgval(otmp2, mtmp) * otmp2->quan;
1805         if (helmet) {
1806             if (is_metallic(helmet)) {
1807                 if (canspotmon(mtmp))
1808                     pline("Fortunately, %s is wearing a hard helmet.",
1809                           mon_nam(mtmp));
1810                 else if (!Deaf)
1811                     You_hear("a clanging sound.");
1812                 if (mdmg > 2)
1813                     mdmg = 2;
1814             } else {
1815                 if (canspotmon(mtmp))
1816                     pline("%s's %s does not protect %s.", Monnam(mtmp),
1817                           xname(helmet), mhim(mtmp));
1818             }
1819         }
1820         mtmp->mhp -= mdmg;
1821         if (DEADMONSTER(mtmp)) {
1822             if (byu) {
1823                 killed(mtmp);
1824             } else {
1825                 pline("%s is killed.", Monnam(mtmp));
1826                 mondied(mtmp);
1827             }
1828         } else {
1829             wakeup(mtmp, byu);
1830         }
1831         wake_nearto(x, y, 4 * 4);
1832     } else if (u.uswallow && mtmp == u.ustuck) {
1833         obfree(otmp2, (struct obj *) 0);
1834         /* fall through to player */
1835         drop_boulder_on_player(confused, TRUE, FALSE, TRUE);
1836         return 1;
1837     }
1838     /* Drop the rock/boulder to the floor */
1839     if (!flooreffects(otmp2, x, y, "fall")) {
1840         place_object(otmp2, x, y);
1841         stackobj(otmp2);
1842         newsym(x, y); /* map the rock */
1843     }
1844     return TRUE;
1845 }
1846 
1847 /* overcharging any wand or zapping/engraving cursed wand */
1848 void
wand_explode(obj,chg)1849 wand_explode(obj, chg)
1850 struct obj *obj;
1851 int chg; /* recharging */
1852 {
1853     const char *expl = !chg ? "suddenly" : "vibrates violently and";
1854     int dmg, n, k;
1855 
1856     /* number of damage dice */
1857     if (!chg)
1858         chg = 2; /* zap/engrave adjustment */
1859     n = obj->spe + chg;
1860     if (n < 2)
1861         n = 2; /* arbitrary minimum */
1862     /* size of damage dice */
1863     switch (obj->otyp) {
1864     case WAN_WISHING:
1865         k = 12;
1866         break;
1867     case WAN_CANCELLATION:
1868     case WAN_DEATH:
1869     case WAN_POLYMORPH:
1870     case WAN_UNDEAD_TURNING:
1871         k = 10;
1872         break;
1873     case WAN_COLD:
1874     case WAN_FIRE:
1875     case WAN_LIGHTNING:
1876     case WAN_MAGIC_MISSILE:
1877         k = 8;
1878         break;
1879     case WAN_NOTHING:
1880         k = 4;
1881         break;
1882     default:
1883         k = 6;
1884         break;
1885     }
1886     /* inflict damage and destroy the wand */
1887     dmg = d(n, k);
1888     obj->in_use = TRUE; /* in case losehp() is fatal (or --More--^C) */
1889     pline("%s %s explodes!", Yname2(obj), expl);
1890     losehp(Maybe_Half_Phys(dmg), "exploding wand", KILLED_BY_AN);
1891     useup(obj);
1892     /* obscure side-effect */
1893     exercise(A_STR, FALSE);
1894 }
1895 
1896 /* used to collect gremlins being hit by light so that they can be processed
1897    after vision for the entire lit area has been brought up to date */
1898 struct litmon {
1899     struct monst *mon;
1900     struct litmon *nxt;
1901 };
1902 STATIC_VAR struct litmon *gremlins = 0;
1903 
1904 /*
1905  * Low-level lit-field update routine.
1906  */
1907 STATIC_PTR void
set_lit(x,y,val)1908 set_lit(x, y, val)
1909 int x, y;
1910 genericptr_t val;
1911 {
1912     struct monst *mtmp;
1913     struct litmon *gremlin;
1914 
1915     if (val) {
1916         levl[x][y].lit = 1;
1917         if ((mtmp = m_at(x, y)) != 0 && mtmp->data == &mons[PM_GREMLIN]) {
1918             gremlin = (struct litmon *) alloc(sizeof *gremlin);
1919             gremlin->mon = mtmp;
1920             gremlin->nxt = gremlins;
1921             gremlins = gremlin;
1922         }
1923     } else {
1924         levl[x][y].lit = 0;
1925         snuff_light_source(x, y);
1926     }
1927 }
1928 
1929 void
litroom(on,obj)1930 litroom(on, obj)
1931 register boolean on;
1932 struct obj *obj;
1933 {
1934     char is_lit; /* value is irrelevant; we use its address
1935                     as a `not null' flag for set_lit() */
1936 
1937     /* first produce the text (provided you're not blind) */
1938     if (!on) {
1939         register struct obj *otmp;
1940 
1941         if (!Blind) {
1942             if (u.uswallow) {
1943                 pline("It seems even darker in here than before.");
1944             } else {
1945                 if (uwep && artifact_light(uwep) && uwep->lamplit)
1946                     pline("Suddenly, the only light left comes from %s!",
1947                           the(xname(uwep)));
1948                 else
1949                     You("are surrounded by darkness!");
1950             }
1951         }
1952 
1953         /* the magic douses lamps, et al, too */
1954         for (otmp = invent; otmp; otmp = otmp->nobj)
1955             if (otmp->lamplit)
1956                 (void) snuff_lit(otmp);
1957     } else { /* on */
1958         if (u.uswallow) {
1959             if (Blind)
1960                 ; /* no feedback */
1961             else if (is_animal(u.ustuck->data))
1962                 pline("%s %s is lit.", s_suffix(Monnam(u.ustuck)),
1963                       mbodypart(u.ustuck, STOMACH));
1964             else if (is_whirly(u.ustuck->data))
1965                 pline("%s shines briefly.", Monnam(u.ustuck));
1966             else
1967                 pline("%s glistens.", Monnam(u.ustuck));
1968         } else if (!Blind)
1969             pline("A lit field surrounds you!");
1970     }
1971 
1972     /* No-op when swallowed or in water */
1973     if (u.uswallow || Underwater || Is_waterlevel(&u.uz))
1974         return;
1975     /*
1976      *  If we are darkening the room and the hero is punished but not
1977      *  blind, then we have to pick up and replace the ball and chain so
1978      *  that we don't remember them if they are out of sight.
1979      */
1980     if (Punished && !on && !Blind)
1981         move_bc(1, 0, uball->ox, uball->oy, uchain->ox, uchain->oy);
1982 
1983     if (Is_rogue_level(&u.uz)) {
1984         /* Can't use do_clear_area because MAX_RADIUS is too small */
1985         /* rogue lighting must light the entire room */
1986         int rnum = levl[u.ux][u.uy].roomno - ROOMOFFSET;
1987         int rx, ry;
1988 
1989         if (rnum >= 0) {
1990             for (rx = rooms[rnum].lx - 1; rx <= rooms[rnum].hx + 1; rx++)
1991                 for (ry = rooms[rnum].ly - 1; ry <= rooms[rnum].hy + 1; ry++)
1992                     set_lit(rx, ry,
1993                             (genericptr_t) (on ? &is_lit : (char *) 0));
1994             rooms[rnum].rlit = on;
1995         }
1996         /* hallways remain dark on the rogue level */
1997     } else
1998         do_clear_area(u.ux, u.uy,
1999                       (obj && obj->oclass == SCROLL_CLASS && obj->blessed)
2000                          ? 9 : 5,
2001                       set_lit, (genericptr_t) (on ? &is_lit : (char *) 0));
2002 
2003     /*
2004      *  If we are not blind, then force a redraw on all positions in sight
2005      *  by temporarily blinding the hero.  The vision recalculation will
2006      *  correctly update all previously seen positions *and* correctly
2007      *  set the waslit bit [could be messed up from above].
2008      */
2009     if (!Blind) {
2010         vision_recalc(2);
2011 
2012         /* replace ball&chain */
2013         if (Punished && !on)
2014             move_bc(0, 0, uball->ox, uball->oy, uchain->ox, uchain->oy);
2015     }
2016 
2017     vision_full_recalc = 1; /* delayed vision recalculation */
2018     if (gremlins) {
2019         struct litmon *gremlin;
2020 
2021         /* can't delay vision recalc after all */
2022         vision_recalc(0);
2023         /* after vision has been updated, monsters who are affected
2024            when hit by light can now be hit by it */
2025         do {
2026             gremlin = gremlins;
2027             gremlins = gremlin->nxt;
2028             light_hits_gremlin(gremlin->mon, rnd(5));
2029             free((genericptr_t) gremlin);
2030         } while (gremlins);
2031     }
2032 }
2033 
2034 STATIC_OVL void
do_class_genocide()2035 do_class_genocide()
2036 {
2037     int i, j, immunecnt, gonecnt, goodcnt, class, feel_dead = 0;
2038     char buf[BUFSZ] = DUMMY;
2039     boolean gameover = FALSE; /* true iff killed self */
2040 
2041     for (j = 0;; j++) {
2042         if (j >= 5) {
2043             pline1(thats_enough_tries);
2044             return;
2045         }
2046         do {
2047             getlin("What class of monsters do you wish to genocide?", buf);
2048             (void) mungspaces(buf);
2049         } while (!*buf);
2050         /* choosing "none" preserves genocideless conduct */
2051         if (*buf == '\033' || !strcmpi(buf, "none")
2052             || !strcmpi(buf, "nothing"))
2053             return;
2054 
2055         class = name_to_monclass(buf, (int *) 0);
2056         if (class == 0 && (i = name_to_mon(buf)) != NON_PM)
2057             class = mons[i].mlet;
2058         immunecnt = gonecnt = goodcnt = 0;
2059         for (i = LOW_PM; i < NUMMONS; i++) {
2060             if (mons[i].mlet == class) {
2061                 if (!(mons[i].geno & G_GENO))
2062                     immunecnt++;
2063                 else if (mvitals[i].mvflags & G_GENOD)
2064                     gonecnt++;
2065                 else
2066                     goodcnt++;
2067             }
2068         }
2069         if (!goodcnt && class != mons[urole.malenum].mlet
2070             && class != mons[urace.malenum].mlet) {
2071             if (gonecnt)
2072                 pline("All such monsters are already nonexistent.");
2073             else if (immunecnt || class == S_invisible)
2074                 You("aren't permitted to genocide such monsters.");
2075             else if (wizard && buf[0] == '*') {
2076                 register struct monst *mtmp, *mtmp2;
2077 
2078                 gonecnt = 0;
2079                 for (mtmp = fmon; mtmp; mtmp = mtmp2) {
2080                     mtmp2 = mtmp->nmon;
2081                     if (DEADMONSTER(mtmp))
2082                         continue;
2083                     mongone(mtmp);
2084                     gonecnt++;
2085                 }
2086                 pline("Eliminated %d monster%s.", gonecnt, plur(gonecnt));
2087                 return;
2088             } else
2089                 pline("That %s does not represent any monster.",
2090                       strlen(buf) == 1 ? "symbol" : "response");
2091             continue;
2092         }
2093 
2094         for (i = LOW_PM; i < NUMMONS; i++) {
2095             if (mons[i].mlet == class) {
2096                 char nam[BUFSZ];
2097 
2098                 Strcpy(nam, makeplural(mons[i].mname));
2099                 /* Although "genus" is Latin for race, the hero benefits
2100                  * from both race and role; thus genocide affects either.
2101                  */
2102                 if (Your_Own_Role(i) || Your_Own_Race(i)
2103                     || ((mons[i].geno & G_GENO)
2104                         && !(mvitals[i].mvflags & G_GENOD))) {
2105                     /* This check must be first since player monsters might
2106                      * have G_GENOD or !G_GENO.
2107                      */
2108                     mvitals[i].mvflags |= (G_GENOD | G_NOCORPSE);
2109                     reset_rndmonst(i);
2110                     kill_genocided_monsters();
2111                     update_inventory(); /* eggs & tins */
2112                     pline("Wiped out all %s.", nam);
2113                     if (Upolyd && i == u.umonnum) {
2114                         u.mh = -1;
2115                         if (Unchanging) {
2116                             if (!feel_dead++)
2117                                 You("die.");
2118                             /* finish genociding this class of
2119                                monsters before ultimately dying */
2120                             gameover = TRUE;
2121                         } else
2122                             rehumanize();
2123                     }
2124                     /* Self-genocide if it matches either your race
2125                        or role.  Assumption:  male and female forms
2126                        share same monster class. */
2127                     if (i == urole.malenum || i == urace.malenum) {
2128                         u.uhp = -1;
2129                         if (Upolyd) {
2130                             if (!feel_dead++)
2131                                 You_feel("%s inside.", udeadinside());
2132                         } else {
2133                             if (!feel_dead++)
2134                                 You("die.");
2135                             gameover = TRUE;
2136                         }
2137                     }
2138                 } else if (mvitals[i].mvflags & G_GENOD) {
2139                     if (!gameover)
2140                         pline("All %s are already nonexistent.", nam);
2141                 } else if (!gameover) {
2142                     /* suppress feedback about quest beings except
2143                        for those applicable to our own role */
2144                     if ((mons[i].msound != MS_LEADER
2145                          || quest_info(MS_LEADER) == i)
2146                         && (mons[i].msound != MS_NEMESIS
2147                             || quest_info(MS_NEMESIS) == i)
2148                         && (mons[i].msound != MS_GUARDIAN
2149                             || quest_info(MS_GUARDIAN) == i)
2150                         /* non-leader/nemesis/guardian role-specific monster
2151                            */
2152                         && (i != PM_NINJA /* nuisance */
2153                             || Role_if(PM_SAMURAI))) {
2154                         boolean named, uniq;
2155 
2156                         named = type_is_pname(&mons[i]) ? TRUE : FALSE;
2157                         uniq = (mons[i].geno & G_UNIQ) ? TRUE : FALSE;
2158                         /* one special case */
2159                         if (i == PM_HIGH_PRIEST)
2160                             uniq = FALSE;
2161 
2162                         You("aren't permitted to genocide %s%s.",
2163                             (uniq && !named) ? "the " : "",
2164                             (uniq || named) ? mons[i].mname : nam);
2165                     }
2166                 }
2167             }
2168         }
2169         if (gameover || u.uhp == -1) {
2170             killer.format = KILLED_BY_AN;
2171             Strcpy(killer.name, "scroll of genocide");
2172             if (gameover)
2173                 done(GENOCIDED);
2174         }
2175         return;
2176     }
2177 }
2178 
2179 #define REALLY 1
2180 #define PLAYER 2
2181 #define ONTHRONE 4
2182 void
do_genocide(how)2183 do_genocide(how)
2184 int how;
2185 /* 0 = no genocide; create monsters (cursed scroll) */
2186 /* 1 = normal genocide */
2187 /* 3 = forced genocide of player */
2188 /* 5 (4 | 1) = normal genocide from throne */
2189 {
2190     char buf[BUFSZ] = DUMMY;
2191     register int i, killplayer = 0;
2192     register int mndx;
2193     register struct permonst *ptr;
2194     const char *which;
2195 
2196     if (how & PLAYER) {
2197         mndx = u.umonster; /* non-polymorphed mon num */
2198         ptr = &mons[mndx];
2199         Strcpy(buf, ptr->mname);
2200         killplayer++;
2201     } else {
2202         for (i = 0;; i++) {
2203             if (i >= 5) {
2204                 /* cursed effect => no free pass (unless rndmonst() fails) */
2205                 if (!(how & REALLY) && (ptr = rndmonst()) != 0)
2206                     break;
2207 
2208                 pline1(thats_enough_tries);
2209                 return;
2210             }
2211             getlin("What monster do you want to genocide? [type the name]",
2212                    buf);
2213             (void) mungspaces(buf);
2214             /* choosing "none" preserves genocideless conduct */
2215             if (*buf == '\033' || !strcmpi(buf, "none")
2216                 || !strcmpi(buf, "nothing")) {
2217                 /* ... but no free pass if cursed */
2218                 if (!(how & REALLY) && (ptr = rndmonst()) != 0)
2219                     break; /* remaining checks don't apply */
2220 
2221                 return;
2222             }
2223 
2224             mndx = name_to_mon(buf);
2225             if (mndx == NON_PM || (mvitals[mndx].mvflags & G_GENOD)) {
2226                 pline("Such creatures %s exist in this world.",
2227                       (mndx == NON_PM) ? "do not" : "no longer");
2228                 continue;
2229             }
2230             ptr = &mons[mndx];
2231             /* Although "genus" is Latin for race, the hero benefits
2232              * from both race and role; thus genocide affects either.
2233              */
2234             if (Your_Own_Role(mndx) || Your_Own_Race(mndx)) {
2235                 killplayer++;
2236                 break;
2237             }
2238             if (is_human(ptr))
2239                 adjalign(-sgn(u.ualign.type));
2240             if (is_demon(ptr))
2241                 adjalign(sgn(u.ualign.type));
2242 
2243             if (!(ptr->geno & G_GENO)) {
2244                 if (!Deaf) {
2245                     /* FIXME: unconditional "caverns" will be silly in some
2246                      * circumstances.  Who's speaking?  Divine pronouncements
2247                      * aren't supposed to be hampered by deafness....
2248                      */
2249                     if (flags.verbose)
2250                         pline("A thunderous voice booms through the caverns:");
2251                     verbalize("No, mortal!  That will not be done.");
2252                 }
2253                 continue;
2254             }
2255             /* KMH -- Unchanging prevents rehumanization */
2256             if (Unchanging && ptr == youmonst.data)
2257                 killplayer++;
2258             break;
2259         }
2260         mndx = monsndx(ptr); /* needed for the 'no free pass' cases */
2261     }
2262 
2263     which = "all ";
2264     if (Hallucination) {
2265         if (Upolyd)
2266             Strcpy(buf, youmonst.data->mname);
2267         else {
2268             Strcpy(buf, (flags.female && urole.name.f) ? urole.name.f
2269                                                        : urole.name.m);
2270             buf[0] = lowc(buf[0]);
2271         }
2272     } else {
2273         Strcpy(buf, ptr->mname); /* make sure we have standard singular */
2274         if ((ptr->geno & G_UNIQ) && ptr != &mons[PM_HIGH_PRIEST])
2275             which = !type_is_pname(ptr) ? "the " : "";
2276     }
2277     if (how & REALLY) {
2278         /* setting no-corpse affects wishing and random tin generation */
2279         mvitals[mndx].mvflags |= (G_GENOD | G_NOCORPSE);
2280         pline("Wiped out %s%s.", which,
2281               (*which != 'a') ? buf : makeplural(buf));
2282 
2283         if (killplayer) {
2284             /* might need to wipe out dual role */
2285             if (urole.femalenum != NON_PM && mndx == urole.malenum)
2286                 mvitals[urole.femalenum].mvflags |= (G_GENOD | G_NOCORPSE);
2287             if (urole.femalenum != NON_PM && mndx == urole.femalenum)
2288                 mvitals[urole.malenum].mvflags |= (G_GENOD | G_NOCORPSE);
2289             if (urace.femalenum != NON_PM && mndx == urace.malenum)
2290                 mvitals[urace.femalenum].mvflags |= (G_GENOD | G_NOCORPSE);
2291             if (urace.femalenum != NON_PM && mndx == urace.femalenum)
2292                 mvitals[urace.malenum].mvflags |= (G_GENOD | G_NOCORPSE);
2293 
2294             u.uhp = -1;
2295             if (how & PLAYER) {
2296                 killer.format = KILLED_BY;
2297                 Strcpy(killer.name, "genocidal confusion");
2298             } else if (how & ONTHRONE) {
2299                 /* player selected while on a throne */
2300                 killer.format = KILLED_BY_AN;
2301                 Strcpy(killer.name, "imperious order");
2302             } else { /* selected player deliberately, not confused */
2303                 killer.format = KILLED_BY_AN;
2304                 Strcpy(killer.name, "scroll of genocide");
2305             }
2306 
2307             /* Polymorphed characters will die as soon as they're rehumanized.
2308              */
2309             /* KMH -- Unchanging prevents rehumanization */
2310             if (Upolyd && ptr != youmonst.data) {
2311                 delayed_killer(POLYMORPH, killer.format, killer.name);
2312                 You_feel("%s inside.", udeadinside());
2313             } else
2314                 done(GENOCIDED);
2315         } else if (ptr == youmonst.data) {
2316             rehumanize();
2317         }
2318         reset_rndmonst(mndx);
2319         kill_genocided_monsters();
2320         update_inventory(); /* in case identified eggs were affected */
2321     } else {
2322         int cnt = 0, census = monster_census(FALSE);
2323 
2324         if (!(mons[mndx].geno & G_UNIQ)
2325             && !(mvitals[mndx].mvflags & (G_GENOD | G_EXTINCT)))
2326             for (i = rn1(3, 4); i > 0; i--) {
2327                 if (!makemon(ptr, u.ux, u.uy, NO_MINVENT))
2328                     break; /* couldn't make one */
2329                 ++cnt;
2330                 if (mvitals[mndx].mvflags & G_EXTINCT)
2331                     break; /* just made last one */
2332             }
2333         if (cnt) {
2334             /* accumulated 'cnt' doesn't take groups into account;
2335                assume bringing in new mon(s) didn't remove any old ones */
2336             cnt = monster_census(FALSE) - census;
2337             pline("Sent in %s%s.", (cnt > 1) ? "some " : "",
2338                   (cnt > 1) ? makeplural(buf) : an(buf));
2339         } else
2340             pline1(nothing_happens);
2341     }
2342 }
2343 
2344 void
punish(sobj)2345 punish(sobj)
2346 struct obj *sobj;
2347 {
2348     struct obj *reuse_ball = (sobj && sobj->otyp == HEAVY_IRON_BALL)
2349                                 ? sobj : (struct obj *) 0;
2350 
2351     /* KMH -- Punishment is still okay when you are riding */
2352     if (!reuse_ball)
2353         You("are being punished for your misbehavior!");
2354     if (Punished) {
2355         Your("iron ball gets heavier.");
2356         uball->owt += IRON_BALL_W_INCR * (1 + sobj->cursed);
2357         return;
2358     }
2359     if (amorphous(youmonst.data) || is_whirly(youmonst.data)
2360         || unsolid(youmonst.data)) {
2361         if (!reuse_ball) {
2362             pline("A ball and chain appears, then falls away.");
2363             dropy(mkobj(BALL_CLASS, TRUE));
2364         } else {
2365             dropy(reuse_ball);
2366         }
2367         return;
2368     }
2369     setworn(mkobj(CHAIN_CLASS, TRUE), W_CHAIN);
2370     if (!reuse_ball)
2371         setworn(mkobj(BALL_CLASS, TRUE), W_BALL);
2372     else
2373         setworn(reuse_ball, W_BALL);
2374     uball->spe = 1; /* special ball (see save) */
2375 
2376     /*
2377      *  Place ball & chain if not swallowed.  If swallowed, the ball & chain
2378      *  variables will be set at the next call to placebc().
2379      */
2380     if (!u.uswallow) {
2381         placebc();
2382         if (Blind)
2383             set_bc(1);      /* set up ball and chain variables */
2384         newsym(u.ux, u.uy); /* see ball&chain if can't see self */
2385     }
2386 }
2387 
2388 /* remove the ball and chain */
2389 void
unpunish()2390 unpunish()
2391 {
2392     struct obj *savechain = uchain;
2393 
2394     /* chain goes away */
2395     obj_extract_self(uchain);
2396     newsym(uchain->ox, uchain->oy);
2397     setworn((struct obj *) 0, W_CHAIN); /* sets 'uchain' to Null */
2398     dealloc_obj(savechain);
2399     /* ball persists */
2400     uball->spe = 0;
2401     setworn((struct obj *) 0, W_BALL); /* sets 'uball' to Null */
2402 }
2403 
2404 /* some creatures have special data structures that only make sense in their
2405  * normal locations -- if the player tries to create one elsewhere, or to
2406  * revive one, the disoriented creature becomes a zombie
2407  */
2408 boolean
cant_revive(mtype,revival,from_obj)2409 cant_revive(mtype, revival, from_obj)
2410 int *mtype;
2411 boolean revival;
2412 struct obj *from_obj;
2413 {
2414     /* SHOPKEEPERS can be revived now */
2415     if (*mtype == PM_GUARD || (*mtype == PM_SHOPKEEPER && !revival)
2416         || *mtype == PM_HIGH_PRIEST || *mtype == PM_ALIGNED_PRIEST
2417         || *mtype == PM_ANGEL) {
2418         *mtype = PM_HUMAN_ZOMBIE;
2419         return TRUE;
2420     } else if (*mtype == PM_LONG_WORM_TAIL) { /* for create_particular() */
2421         *mtype = PM_LONG_WORM;
2422         return TRUE;
2423     } else if (unique_corpstat(&mons[*mtype])
2424                && (!from_obj || !has_omonst(from_obj))) {
2425         /* unique corpses (from bones or wizard mode wish) or
2426            statues (bones or any wish) end up as shapechangers */
2427         *mtype = PM_DOPPELGANGER;
2428         return TRUE;
2429     }
2430     return FALSE;
2431 }
2432 
2433 struct _create_particular_data {
2434     int which;
2435     int fem;
2436     char monclass;
2437     boolean randmonst;
2438     boolean maketame, makepeaceful, makehostile;
2439     boolean sleeping, saddled, invisible, hidden;
2440 };
2441 
2442 boolean
create_particular_parse(str,d)2443 create_particular_parse(str, d)
2444 char *str;
2445 struct _create_particular_data *d;
2446 {
2447     char *bufp = str;
2448     char *tmpp;
2449 
2450     d->monclass = MAXMCLASSES;
2451     d->which = urole.malenum; /* an arbitrary index into mons[] */
2452     d->fem = -1; /* gender not specified */
2453     d->randmonst = FALSE;
2454     d->maketame = d->makepeaceful = d->makehostile = FALSE;
2455     d->sleeping = d->saddled = d->invisible = d->hidden = FALSE;
2456 
2457     if ((tmpp = strstri(bufp, "saddled ")) != 0) {
2458         d->saddled = TRUE;
2459         (void) memset(tmpp, ' ', sizeof "saddled " - 1);
2460     }
2461     if ((tmpp = strstri(bufp, "sleeping ")) != 0) {
2462         d->sleeping = TRUE;
2463         (void) memset(tmpp, ' ', sizeof "sleeping " - 1);
2464     }
2465     if ((tmpp = strstri(bufp, "invisible ")) != 0) {
2466         d->invisible = TRUE;
2467         (void) memset(tmpp, ' ', sizeof "invisible " - 1);
2468     }
2469     if ((tmpp = strstri(bufp, "hidden ")) != 0) {
2470         d->hidden = TRUE;
2471         (void) memset(tmpp, ' ', sizeof "hidden " - 1);
2472     }
2473     /* check "female" before "male" to avoid false hit mid-word */
2474     if ((tmpp = strstri(bufp, "female ")) != 0) {
2475         d->fem = 1;
2476         (void) memset(tmpp, ' ', sizeof "female " - 1);
2477     }
2478     if ((tmpp = strstri(bufp, "male ")) != 0) {
2479         d->fem = 0;
2480         (void) memset(tmpp, ' ', sizeof "male " - 1);
2481     }
2482     bufp = mungspaces(bufp); /* after potential memset(' ') */
2483     /* allow the initial disposition to be specified */
2484     if (!strncmpi(bufp, "tame ", 5)) {
2485         bufp += 5;
2486         d->maketame = TRUE;
2487     } else if (!strncmpi(bufp, "peaceful ", 9)) {
2488         bufp += 9;
2489         d->makepeaceful = TRUE;
2490     } else if (!strncmpi(bufp, "hostile ", 8)) {
2491         bufp += 8;
2492         d->makehostile = TRUE;
2493     }
2494     /* decide whether a valid monster was chosen */
2495     if (wizard && (!strcmp(bufp, "*") || !strcmp(bufp, "random"))) {
2496         d->randmonst = TRUE;
2497         return TRUE;
2498     }
2499     d->which = name_to_mon(bufp);
2500     if (d->which >= LOW_PM)
2501         return TRUE; /* got one */
2502     d->monclass = name_to_monclass(bufp, &d->which);
2503 
2504     if (d->which >= LOW_PM) {
2505         d->monclass = MAXMCLASSES; /* matters below */
2506         return TRUE;
2507     } else if (d->monclass == S_invisible) { /* not an actual monster class */
2508         d->which = PM_STALKER;
2509         d->monclass = MAXMCLASSES;
2510         return TRUE;
2511     } else if (d->monclass == S_WORM_TAIL) { /* empty monster class */
2512         d->which = PM_LONG_WORM;
2513         d->monclass = MAXMCLASSES;
2514         return TRUE;
2515     } else if (d->monclass > 0) {
2516         d->which = urole.malenum; /* reset from NON_PM */
2517         return TRUE;
2518     }
2519     return FALSE;
2520 }
2521 
2522 boolean
create_particular_creation(d)2523 create_particular_creation(d)
2524 struct _create_particular_data *d;
2525 {
2526     struct permonst *whichpm = NULL;
2527     int i, mx, my, firstchoice = NON_PM;
2528     struct monst *mtmp;
2529     boolean madeany = FALSE;
2530 
2531     if (!d->randmonst) {
2532         firstchoice = d->which;
2533         if (cant_revive(&d->which, FALSE, (struct obj *) 0)
2534             && firstchoice != PM_LONG_WORM_TAIL) {
2535             /* wizard mode can override handling of special monsters */
2536             char buf[BUFSZ];
2537 
2538             Sprintf(buf, "Creating %s instead; force %s?",
2539                     mons[d->which].mname, mons[firstchoice].mname);
2540             if (yn(buf) == 'y')
2541                 d->which = firstchoice;
2542         }
2543         whichpm = &mons[d->which];
2544     }
2545     for (i = 0; i <= multi; i++) {
2546         if (d->monclass != MAXMCLASSES)
2547             whichpm = mkclass(d->monclass, 0);
2548         else if (d->randmonst)
2549             whichpm = rndmonst();
2550         mtmp = makemon(whichpm, u.ux, u.uy, NO_MM_FLAGS);
2551         if (!mtmp) {
2552             /* quit trying if creation failed and is going to repeat */
2553             if (d->monclass == MAXMCLASSES && !d->randmonst)
2554                 break;
2555             /* otherwise try again */
2556             continue;
2557         }
2558         mx = mtmp->mx, my = mtmp->my;
2559         /* 'is_FOO()' ought to be called 'always_FOO()' */
2560         if (d->fem != -1 && !is_male(mtmp->data) && !is_female(mtmp->data))
2561             mtmp->female = d->fem; /* ignored for is_neuter() */
2562         if (d->maketame) {
2563             (void) tamedog(mtmp, (struct obj *) 0);
2564         } else if (d->makepeaceful || d->makehostile) {
2565             mtmp->mtame = 0; /* sanity precaution */
2566             mtmp->mpeaceful = d->makepeaceful ? 1 : 0;
2567             set_malign(mtmp);
2568         }
2569         if (d->saddled && can_saddle(mtmp) && !which_armor(mtmp, W_SADDLE)) {
2570             struct obj *otmp = mksobj(SADDLE, TRUE, FALSE);
2571 
2572             put_saddle_on_mon(otmp, mtmp);
2573         }
2574         if (d->invisible) {
2575             mon_set_minvis(mtmp);
2576             if (does_block(mx, my, &levl[mx][my]))
2577                 block_point(mx, my);
2578             else
2579                 unblock_point(mx, my);
2580         }
2581        if (d->hidden
2582            && ((is_hider(mtmp->data) && mtmp->data->mlet != S_MIMIC)
2583                || (hides_under(mtmp->data) && OBJ_AT(mx, my))
2584                || (mtmp->data->mlet == S_EEL && is_pool(mx, my))))
2585             mtmp->mundetected = 1;
2586         if (d->sleeping)
2587             mtmp->msleeping = 1;
2588         /* iff asking for 'hidden', show locaton of every created monster
2589            that can't be seen--whether that's due to successfully hiding
2590            or vision issues (line-of-sight, invisibility, blindness) */
2591         if (d->hidden && !canspotmon(mtmp)) {
2592             int count = couldsee(mx, my) ? 8 : 4;
2593             char saveviz = viz_array[my][mx];
2594 
2595             if (!flags.sparkle)
2596                 count /= 2;
2597             viz_array[my][mx] |= (IN_SIGHT | COULD_SEE);
2598             flash_glyph_at(mx, my, mon_to_glyph(mtmp, newsym_rn2), count);
2599             viz_array[my][mx] = saveviz;
2600             newsym(mx, my);
2601         }
2602         madeany = TRUE;
2603         /* in case we got a doppelganger instead of what was asked
2604            for, make it start out looking like what was asked for */
2605         if (mtmp->cham != NON_PM && firstchoice != NON_PM
2606             && mtmp->cham != firstchoice)
2607             (void) newcham(mtmp, &mons[firstchoice], FALSE, FALSE);
2608     }
2609     return madeany;
2610 }
2611 
2612 /*
2613  * Make a new monster with the type controlled by the user.
2614  *
2615  * Note:  when creating a monster by class letter, specifying the
2616  * "strange object" (']') symbol produces a random monster rather
2617  * than a mimic.  This behavior quirk is useful so don't "fix" it
2618  * (use 'm'--or "mimic"--to create a random mimic).
2619  *
2620  * Used in wizard mode only (for ^G command and for scroll or spell
2621  * of create monster).  Once upon a time, an earlier incarnation of
2622  * this code was also used for the scroll/spell in explore mode.
2623  */
2624 boolean
create_particular()2625 create_particular()
2626 {
2627     char buf[BUFSZ] = DUMMY, *bufp;
2628     int  tryct = 5;
2629     struct _create_particular_data d;
2630 
2631     do {
2632         getlin("Create what kind of monster? [type the name or symbol]", buf);
2633         bufp = mungspaces(buf);
2634         if (*bufp == '\033')
2635             return FALSE;
2636 
2637         if (create_particular_parse(bufp, &d))
2638             break;
2639 
2640         /* no good; try again... */
2641         pline("I've never heard of such monsters.");
2642     } while (--tryct > 0);
2643 
2644     if (!tryct)
2645         pline1(thats_enough_tries);
2646     else
2647         return create_particular_creation(&d);
2648 
2649     return FALSE;
2650 }
2651 
2652 /*read.c*/
2653