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