1/*	SCCS Id: @(#)engrave.c	3.4	2001/11/04	*/
2/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3/* NetHack may be freely redistributed.  See license for details. */
4
5#include "hack.h"
6#include "lev.h"
7#include <ctype.h>
8
9STATIC_VAR NEARDATA struct engr *head_engr;
10
11#ifdef OVLB
12/* random engravings */
13static const char *random_mesg[] = {
14	"Elbereth",
15	/* trap engravings */
16	"Vlad was here", "ad aerarium",
17	/* take-offs and other famous engravings */
18	"Owlbreath", "Galadriel",
19	"Kilroy was here",
20	"A.S. ->", "<- A.S.", /* Journey to the Center of the Earth */
21	"You won't get it up the steps", /* Adventure */
22	"Lasciate ogni speranza o voi ch'entrate.", /* Inferno */
23	"Well Come", /* Prisoner */
24	"We apologize for the inconvenience.", /* So Long... */
25	"See you next Wednesday", /* Thriller */
26	"notary sojak", /* Smokey Stover */
27	"For a good time call 8?7-5309",
28	"Please don't feed the animals.", /* Various zoos around the world */
29	"Madam, in Eden, I'm Adam.", /* A palindrome */
30	"Two thumbs up!", /* Siskel & Ebert */
31	"Hello, World!", /* The First C Program */
32#ifdef MAIL
33	"You've got mail!", /* AOL */
34#endif
35	"As if!", /* Clueless */
36};
37
38char *
39random_engraving(outbuf)
40char *outbuf;
41{
42	const char *rumor;
43
44	/* a random engraving may come from the "rumors" file,
45	   or from the list above */
46	if (!rn2(4) || !(rumor = getrumor(0, outbuf, TRUE)) || !*rumor)
47	    Strcpy(outbuf, random_mesg[rn2(SIZE(random_mesg))]);
48
49	wipeout_text(outbuf, (int)(strlen(outbuf) / 4), 0);
50	return outbuf;
51}
52
53/* Partial rubouts for engraving characters. -3. */
54static const struct {
55	char		wipefrom;
56	const char *	wipeto;
57} rubouts[] = {
58	{'A', "^"},     {'B', "Pb["},   {'C', "("},     {'D', "|)["},
59	{'E', "|FL[_"}, {'F', "|-"},    {'G', "C("},    {'H', "|-"},
60	{'I', "|"},     {'K', "|<"},    {'L', "|_"},    {'M', "|"},
61	{'N', "|\\"},   {'O', "C("},    {'P', "F"},     {'Q', "C("},
62	{'R', "PF"},    {'T', "|"},     {'U', "J"},     {'V', "/\\"},
63	{'W', "V/\\"},  {'Z', "/"},
64	{'b', "|"},     {'d', "c|"},    {'e', "c"},     {'g', "c"},
65	{'h', "n"},     {'j', "i"},     {'k', "|"},     {'l', "|"},
66	{'m', "nr"},    {'n', "r"},     {'o', "c"},     {'q', "c"},
67	{'w', "v"},     {'y', "v"},
68	{':', "."},     {';', ","},
69	{'0', "C("},    {'1', "|"},     {'6', "o"},     {'7', "/"},
70	{'8', "3o"}
71};
72
73void
74wipeout_text(engr, cnt, seed)
75char *engr;
76int cnt;
77unsigned seed;		/* for semi-controlled randomization */
78{
79	char *s;
80	int i, j, nxt, use_rubout, lth = (int)strlen(engr);
81
82	if (lth && cnt > 0) {
83	    while (cnt--) {
84		/* pick next character */
85		if (!seed) {
86		    /* random */
87		    nxt = rn2(lth);
88		    use_rubout = rn2(4);
89		} else {
90		    /* predictable; caller can reproduce the same sequence by
91		       supplying the same arguments later, or a pseudo-random
92		       sequence by varying any of them */
93		    nxt = seed % lth;
94		    seed *= 31,  seed %= (BUFSZ-1);
95		    use_rubout = seed & 3;
96		}
97		s = &engr[nxt];
98		if (*s == ' ') continue;
99
100		/* rub out unreadable & small punctuation marks */
101		if (index("?.,'`-|_", *s)) {
102		    *s = ' ';
103		    continue;
104		}
105
106		if (!use_rubout)
107		    i = SIZE(rubouts);
108		else
109		    for (i = 0; i < SIZE(rubouts); i++)
110			if (*s == rubouts[i].wipefrom) {
111			    /*
112			     * Pick one of the substitutes at random.
113			     */
114			    if (!seed)
115				j = rn2(strlen(rubouts[i].wipeto));
116			    else {
117				seed *= 31,  seed %= (BUFSZ-1);
118				j = seed % (strlen(rubouts[i].wipeto));
119			    }
120			    *s = rubouts[i].wipeto[j];
121			    break;
122			}
123
124		/* didn't pick rubout; use '?' for unreadable character */
125		if (i == SIZE(rubouts)) *s = '?';
126	    }
127	}
128
129	/* trim trailing spaces */
130	while (lth && engr[lth-1] == ' ') engr[--lth] = 0;
131}
132
133boolean
134can_reach_floor()
135{
136	return (boolean)(!u.uswallow &&
137#ifdef STEED
138			/* Restricted/unskilled riders can't reach the floor */
139			!(u.usteed && P_SKILL(P_RIDING) < P_BASIC) &&
140#endif
141			 (!Levitation ||
142			  Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)));
143}
144#endif /* OVLB */
145#ifdef OVL0
146
147const char *
148surface(x, y)
149register int x, y;
150{
151	register struct rm *lev = &levl[x][y];
152
153	if ((x == u.ux) && (y == u.uy) && u.uswallow &&
154		is_animal(u.ustuck->data))
155	    return "maw";
156	else if (IS_AIR(lev->typ) && Is_airlevel(&u.uz))
157	    return "air";
158	else if (is_pool(x,y))
159	    return (Underwater && !Is_waterlevel(&u.uz)) ? "bottom" : "water";
160	else if (is_ice(x,y))
161	    return "ice";
162	else if (is_lava(x,y))
163	    return "lava";
164	else if (lev->typ == DRAWBRIDGE_DOWN)
165	    return "bridge";
166	else if(IS_ALTAR(levl[x][y].typ))
167	    return "altar";
168	else if(IS_GRAVE(levl[x][y].typ))
169	    return "headstone";
170	else if(IS_FOUNTAIN(levl[x][y].typ))
171	    return "fountain";
172	else if ((IS_ROOM(lev->typ) && !Is_earthlevel(&u.uz)) ||
173		 IS_WALL(lev->typ) || IS_DOOR(lev->typ) || lev->typ == SDOOR)
174	    return "floor";
175	else
176	    return "ground";
177}
178
179const char *
180ceiling(x, y)
181register int x, y;
182{
183	register struct rm *lev = &levl[x][y];
184	const char *what;
185
186	/* other room types will no longer exist when we're interested --
187	 * see check_special_room()
188	 */
189	if (*in_rooms(x,y,VAULT))
190	    what = "vault's ceiling";
191	else if (*in_rooms(x,y,TEMPLE))
192	    what = "temple's ceiling";
193	else if (*in_rooms(x,y,SHOPBASE))
194	    what = "shop's ceiling";
195	else if (IS_AIR(lev->typ))
196	    what = "sky";
197	else if (Underwater)
198	    what = "water's surface";
199	else if ((IS_ROOM(lev->typ) && !Is_earthlevel(&u.uz)) ||
200		 IS_WALL(lev->typ) || IS_DOOR(lev->typ) || lev->typ == SDOOR)
201	    what = "ceiling";
202	else
203	    what = "rock above";
204
205	return what;
206}
207
208struct engr *
209engr_at(x, y)
210xchar x, y;
211{
212	register struct engr *ep = head_engr;
213
214	while(ep) {
215		if(x == ep->engr_x && y == ep->engr_y)
216			return(ep);
217		ep = ep->nxt_engr;
218	}
219	return((struct engr *) 0);
220}
221
222#ifdef ELBERETH
223/* Decide whether a particular string is engraved at a specified
224 * location; a case-insensitive substring match used.
225 * Ignore headstones, in case the player names herself "Elbereth".
226 */
227int
228sengr_at(s, x, y)
229	const char *s;
230	xchar x, y;
231{
232	register struct engr *ep = engr_at(x,y);
233
234	return (ep && ep->engr_type != HEADSTONE &&
235		ep->engr_time <= moves && strstri(ep->engr_txt, s) != 0);
236}
237#endif /* ELBERETH */
238
239#endif /* OVL0 */
240#ifdef OVL2
241
242void
243u_wipe_engr(cnt)
244register int cnt;
245{
246	if (can_reach_floor())
247		wipe_engr_at(u.ux, u.uy, cnt);
248}
249
250#endif /* OVL2 */
251#ifdef OVL1
252
253void
254wipe_engr_at(x,y,cnt)
255register xchar x,y,cnt;
256{
257	register struct engr *ep = engr_at(x,y);
258
259	/* Headstones are indelible */
260	if(ep && ep->engr_type != HEADSTONE){
261	    if(ep->engr_type != BURN || is_ice(x,y)) {
262		if(ep->engr_type != DUST && ep->engr_type != ENGR_BLOOD) {
263			cnt = rn2(1 + 50/(cnt+1)) ? 0 : 1;
264		}
265		wipeout_text(ep->engr_txt, (int)cnt, 0);
266		while(ep->engr_txt[0] == ' ')
267			ep->engr_txt++;
268		if(!ep->engr_txt[0]) del_engr(ep);
269	    }
270	}
271}
272
273#endif /* OVL1 */
274#ifdef OVL2
275
276void
277read_engr_at(x,y)
278register int x,y;
279{
280	register struct engr *ep = engr_at(x,y);
281	register int	sensed = 0;
282	char buf[BUFSZ];
283
284	/* Sensing an engraving does not require sight,
285	 * nor does it necessarily imply comprehension (literacy).
286	 */
287	if(ep && ep->engr_txt[0]) {
288	    switch(ep->engr_type) {
289	    case DUST:
290		if(!Blind) {
291			sensed = 1;
292			pline("%s is written here in the %s.", Something,
293				is_ice(x,y) ? "frost" : "dust");
294		}
295		break;
296	    case ENGRAVE:
297	    case HEADSTONE:
298		if (!Blind || can_reach_floor()) {
299			sensed = 1;
300			pline("%s is engraved here on the %s.",
301				Something,
302				surface(x,y));
303		}
304		break;
305	    case BURN:
306		if (!Blind || can_reach_floor()) {
307			sensed = 1;
308			pline("Some text has been %s into the %s here.",
309				is_ice(x,y) ? "melted" : "burned",
310				surface(x,y));
311		}
312		break;
313	    case MARK:
314		if(!Blind) {
315			sensed = 1;
316			pline("There's some graffiti on the %s here.",
317				surface(x,y));
318		}
319		break;
320	    case ENGR_BLOOD:
321		/* "It's a message!  Scrawled in blood!"
322		 * "What's it say?"
323		 * "It says... `See you next Wednesday.'" -- Thriller
324		 */
325		if(!Blind) {
326			sensed = 1;
327			You("see a message scrawled in blood here.");
328		}
329		break;
330	    default:
331		impossible("%s is written in a very strange way.",
332				Something);
333		sensed = 1;
334	    }
335	    if (sensed) {
336	    	char *et;
337	    	unsigned maxelen = BUFSZ - sizeof("You feel the words: \"\". ");
338	    	if (strlen(ep->engr_txt) > maxelen) {
339	    		(void) strncpy(buf,  ep->engr_txt, (int)maxelen);
340			buf[maxelen] = '\0';
341			et = buf;
342		} else
343			et = ep->engr_txt;
344		You("%s: \"%s\".",
345		      (Blind) ? "feel the words" : "read",  et);
346		if(flags.run > 1) nomul(0);
347	    }
348	}
349}
350
351#endif /* OVL2 */
352#ifdef OVLB
353
354void
355make_engr_at(x,y,s,e_time,e_type)
356register int x,y;
357register const char *s;
358register long e_time;
359register xchar e_type;
360{
361	register struct engr *ep;
362
363	if ((ep = engr_at(x,y)) != 0)
364	    del_engr(ep);
365	ep = newengr(strlen(s) + 1);
366	ep->nxt_engr = head_engr;
367	head_engr = ep;
368	ep->engr_x = x;
369	ep->engr_y = y;
370	ep->engr_txt = (char *)(ep + 1);
371	Strcpy(ep->engr_txt, s);
372	/* engraving Elbereth shows wisdom */
373	if (!in_mklev && !strcmp(s, "Elbereth")) exercise(A_WIS, TRUE);
374	ep->engr_time = e_time;
375	ep->engr_type = e_type > 0 ? e_type : rnd(N_ENGRAVE-1);
376	ep->engr_lth = strlen(s) + 1;
377}
378
379/* delete any engraving at location <x,y> */
380void
381del_engr_at(x, y)
382int x, y;
383{
384	register struct engr *ep = engr_at(x, y);
385
386	if (ep) del_engr(ep);
387}
388
389/*
390 *	freehand - returns true if player has a free hand
391 */
392int
393freehand()
394{
395	return(!uwep || !welded(uwep) ||
396	   (!bimanual(uwep) && (!uarms || !uarms->cursed)));
397/*	if ((uwep && bimanual(uwep)) ||
398	    (uwep && uarms))
399		return(0);
400	else
401		return(1);*/
402}
403
404static NEARDATA const char styluses[] =
405	{ ALL_CLASSES, ALLOW_NONE, TOOL_CLASS, WEAPON_CLASS, WAND_CLASS,
406	  GEM_CLASS, RING_CLASS, 0 };
407
408/* Mohs' Hardness Scale:
409 *  1 - Talc		 6 - Orthoclase
410 *  2 - Gypsum		 7 - Quartz
411 *  3 - Calcite		 8 - Topaz
412 *  4 - Fluorite	 9 - Corundum
413 *  5 - Apatite		10 - Diamond
414 *
415 * Since granite is a igneous rock hardness ~ 7, anything >= 8 should
416 * probably be able to scratch the rock.
417 * Devaluation of less hard gems is not easily possible because obj struct
418 * does not contain individual oc_cost currently. 7/91
419 *
420 * steel     -	5-8.5	(usu. weapon)
421 * diamond    - 10			* jade	     -	5-6	 (nephrite)
422 * ruby       -  9	(corundum)	* turquoise  -	5-6
423 * sapphire   -  9	(corundum)	* opal	     -	5-6
424 * topaz      -  8			* glass      - ~5.5
425 * emerald    -  7.5-8	(beryl)		* dilithium  -	4-5??
426 * aquamarine -  7.5-8	(beryl)		* iron	     -	4-5
427 * garnet     -  7.25	(var. 6.5-8)	* fluorite   -	4
428 * agate      -  7	(quartz)	* brass      -	3-4
429 * amethyst   -  7	(quartz)	* gold	     -	2.5-3
430 * jasper     -  7	(quartz)	* silver     -	2.5-3
431 * onyx       -  7	(quartz)	* copper     -	2.5-3
432 * moonstone  -  6	(orthoclase)	* amber      -	2-2.5
433 */
434
435/* return 1 if action took 1 (or more) moves, 0 if error or aborted */
436int
437doengrave()
438{
439	boolean dengr = FALSE;	/* TRUE if we wipe out the current engraving */
440	boolean doblind = FALSE;/* TRUE if engraving blinds the player */
441	boolean doknown = FALSE;/* TRUE if we identify the stylus */
442	boolean eow = FALSE;	/* TRUE if we are overwriting oep */
443	boolean jello = FALSE;	/* TRUE if we are engraving in slime */
444	boolean ptext = TRUE;	/* TRUE if we must prompt for engrave text */
445	boolean teleengr =FALSE;/* TRUE if we move the old engraving */
446	boolean zapwand = FALSE;/* TRUE if we remove a wand charge */
447	xchar type = DUST;	/* Type of engraving made */
448	char buf[BUFSZ];	/* Buffer for final/poly engraving text */
449	char ebuf[BUFSZ];	/* Buffer for initial engraving text */
450	char qbuf[QBUFSZ];	/* Buffer for query text */
451	char post_engr_text[BUFSZ]; /* Text displayed after engraving prompt */
452	const char *everb;	/* Present tense of engraving type */
453	const char *eloc;	/* Where the engraving is (ie dust/floor/...) */
454	char *sp;		/* Place holder for space count of engr text */
455	int len;		/* # of nonspace chars of new engraving text */
456	int maxelen;		/* Max allowable length of engraving text */
457	struct engr *oep = engr_at(u.ux,u.uy);
458				/* The current engraving */
459	struct obj *otmp;	/* Object selected with which to engrave */
460	char *writer;
461
462	multi = 0;		/* moves consumed */
463	nomovemsg = (char *)0;	/* occupation end message */
464
465	buf[0] = (char)0;
466	ebuf[0] = (char)0;
467	post_engr_text[0] = (char)0;
468	maxelen = BUFSZ - 1;
469	if (is_demon(youmonst.data) || youmonst.data->mlet == S_VAMPIRE)
470	    type = ENGR_BLOOD;
471
472	/* Can the adventurer engrave at all? */
473
474	if(u.uswallow) {
475		if (is_animal(u.ustuck->data)) {
476			pline("What would you write?  \"Jonah was here\"?");
477			return(0);
478		} else if (is_whirly(u.ustuck->data)) {
479			You_cant("reach the %s.", surface(u.ux,u.uy));
480			return(0);
481		} else
482			jello = TRUE;
483	} else if (is_lava(u.ux, u.uy)) {
484		You_cant("write on the lava!");
485		return(0);
486	} else if (is_pool(u.ux,u.uy) || IS_FOUNTAIN(levl[u.ux][u.uy].typ)) {
487		You_cant("write on the water!");
488		return(0);
489	}
490	if(Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)/* in bubble */) {
491		You_cant("write in thin air!");
492		return(0);
493	}
494	if (cantwield(youmonst.data)) {
495		You_cant("even hold anything!");
496		return(0);
497	}
498	if (check_capacity((char *)0)) return (0);
499
500	/* One may write with finger, or weapon, or wand, or..., or...
501	 * Edited by GAN 10/20/86 so as not to change weapon wielded.
502	 */
503
504	otmp = getobj(styluses, "write with");
505	if(!otmp) return(0);		/* otmp == zeroobj if fingers */
506
507	if (otmp == &zeroobj) writer = makeplural(body_part(FINGER));
508	else writer = xname(otmp);
509
510	/* There's no reason you should be able to write with a wand
511	 * while both your hands are tied up.
512	 */
513	if (!freehand() && otmp != uwep && !otmp->owornmask) {
514		You("have no free %s to write with!", body_part(HAND));
515		return(0);
516	}
517
518	if (jello) {
519		You("tickle %s with your %s.", mon_nam(u.ustuck), writer);
520		Your("message dissolves...");
521		return(0);
522	}
523	if (otmp->oclass != WAND_CLASS && !can_reach_floor()) {
524		You_cant("reach the %s!", surface(u.ux,u.uy));
525		return(0);
526	}
527	if (IS_ALTAR(levl[u.ux][u.uy].typ)) {
528		You("make a motion towards the altar with your %s.", writer);
529		altar_wrath(u.ux, u.uy);
530		return(0);
531	}
532	if (IS_GRAVE(levl[u.ux][u.uy].typ)) {
533	    if (otmp == &zeroobj) { /* using only finger */
534		You("would only make a small smudge on the %s.",
535			surface(u.ux, u.uy));
536		return(0);
537	    } else if (!levl[u.ux][u.uy].disturbed) {
538		You("disturb the undead!");
539		levl[u.ux][u.uy].disturbed = 1;
540		(void) makemon(&mons[PM_GHOUL], u.ux, u.uy, NO_MM_FLAGS);
541		exercise(A_WIS, FALSE);
542		return(1);
543	    }
544	}
545
546	/* SPFX for items */
547
548	switch (otmp->oclass) {
549	    default:
550	    case AMULET_CLASS:
551	    case CHAIN_CLASS:
552	    case POTION_CLASS:
553	    case COIN_CLASS:
554		break;
555
556	    case RING_CLASS:
557		/* "diamond" rings and others should work */
558	    case GEM_CLASS:
559		/* diamonds & other hard gems should work */
560		if (objects[otmp->otyp].oc_tough) {
561			type = ENGRAVE;
562			break;
563		}
564		break;
565
566	    case ARMOR_CLASS:
567		if (is_boots(otmp)) {
568			type = DUST;
569			break;
570		}
571		/* fall through */
572	    /* Objects too large to engrave with */
573	    case BALL_CLASS:
574	    case ROCK_CLASS:
575		You_cant("engrave with such a large object!");
576		ptext = FALSE;
577		break;
578
579	    /* Objects too silly to engrave with */
580	    case FOOD_CLASS:
581	    case SCROLL_CLASS:
582	    case SPBOOK_CLASS:
583		Your("%s would get %s.", xname(otmp),
584			is_ice(u.ux,u.uy) ? "all frosty" : "too dirty");
585		ptext = FALSE;
586		break;
587
588	    case RANDOM_CLASS:	/* This should mean fingers */
589		break;
590
591	    /* The charge is removed from the wand before prompting for
592	     * the engraving text, because all kinds of setup decisions
593	     * and pre-engraving messages are based upon knowing what type
594	     * of engraving the wand is going to do.  Also, the player
595	     * will have potentially seen "You wrest .." message, and
596	     * therefore will know they are using a charge.
597	     */
598	    case WAND_CLASS:
599		if (zappable(otmp)) {
600		    check_unpaid(otmp);
601		    zapwand = TRUE;
602		    if (Levitation) ptext = FALSE;
603
604		    switch (otmp->otyp) {
605		    /* DUST wands */
606		    default:
607			break;
608
609			/* NODIR wands */
610		    case WAN_LIGHT:
611		    case WAN_SECRET_DOOR_DETECTION:
612		    case WAN_CREATE_MONSTER:
613		    case WAN_WISHING:
614		    case WAN_ENLIGHTENMENT:
615			zapnodir(otmp);
616			break;
617
618			/* IMMEDIATE wands */
619			/* If wand is "IMMEDIATE", remember to affect the
620			 * previous engraving even if turning to dust.
621			 */
622		    case WAN_STRIKING:
623			Strcpy(post_engr_text,
624			"The wand unsuccessfully fights your attempt to write!"
625			);
626			break;
627		    case WAN_SLOW_MONSTER:
628			if (!Blind) {
629			   Sprintf(post_engr_text,
630				   "The bugs on the %s slow down!",
631				   surface(u.ux, u.uy));
632			}
633			break;
634		    case WAN_SPEED_MONSTER:
635			if (!Blind) {
636			   Sprintf(post_engr_text,
637				   "The bugs on the %s speed up!",
638				   surface(u.ux, u.uy));
639			}
640			break;
641		    case WAN_POLYMORPH:
642			if(oep)  {
643			    if (!Blind) {
644				type = (xchar)0;	/* random */
645				(void) random_engraving(buf);
646			    }
647			    dengr = TRUE;
648			}
649			break;
650		    case WAN_NOTHING:
651		    case WAN_UNDEAD_TURNING:
652		    case WAN_OPENING:
653		    case WAN_LOCKING:
654		    case WAN_PROBING:
655			break;
656
657			/* RAY wands */
658		    case WAN_MAGIC_MISSILE:
659			ptext = TRUE;
660			if (!Blind) {
661			   Sprintf(post_engr_text,
662				   "The %s is riddled by bullet holes!",
663				   surface(u.ux, u.uy));
664			}
665			break;
666
667		    /* can't tell sleep from death - Eric Backus */
668		    case WAN_SLEEP:
669		    case WAN_DEATH:
670			if (!Blind) {
671			   Sprintf(post_engr_text,
672				   "The bugs on the %s stop moving!",
673				   surface(u.ux, u.uy));
674			}
675			break;
676
677		    case WAN_COLD:
678			if (!Blind)
679			    Strcpy(post_engr_text,
680				"A few ice cubes drop from the wand.");
681			if(!oep || (oep->engr_type != BURN))
682			    break;
683		    case WAN_CANCELLATION:
684		    case WAN_MAKE_INVISIBLE:
685			if (oep && oep->engr_type != HEADSTONE) {
686			    if (!Blind)
687				pline_The("engraving on the %s vanishes!",
688					surface(u.ux,u.uy));
689			    dengr = TRUE;
690			}
691			break;
692		    case WAN_TELEPORTATION:
693			if (oep && oep->engr_type != HEADSTONE) {
694			    if (!Blind)
695				pline_The("engraving on the %s vanishes!",
696					surface(u.ux,u.uy));
697			    teleengr = TRUE;
698			}
699			break;
700
701		    /* type = ENGRAVE wands */
702		    case WAN_DIGGING:
703			ptext = TRUE;
704			type  = ENGRAVE;
705			if(!objects[otmp->otyp].oc_name_known) {
706			    if (flags.verbose)
707				pline("This %s is a wand of digging!",
708				xname(otmp));
709			    doknown = TRUE;
710			}
711			if (!Blind)
712			    Strcpy(post_engr_text,
713				IS_GRAVE(levl[u.ux][u.uy].typ) ?
714				"Chips fly out from the headstone." :
715				is_ice(u.ux,u.uy) ?
716				"Ice chips fly up from the ice surface!" :
717				"Gravel flies up from the floor.");
718			else
719			    Strcpy(post_engr_text, "You hear drilling!");
720			break;
721
722		    /* type = BURN wands */
723		    case WAN_FIRE:
724			ptext = TRUE;
725			type  = BURN;
726			if(!objects[otmp->otyp].oc_name_known) {
727			if (flags.verbose)
728			    pline("This %s is a wand of fire!", xname(otmp));
729			    doknown = TRUE;
730			}
731			Strcpy(post_engr_text,
732				Blind ? "You feel the wand heat up." :
733					"Flames fly from the wand.");
734			break;
735		    case WAN_LIGHTNING:
736			ptext = TRUE;
737			type  = BURN;
738			if(!objects[otmp->otyp].oc_name_known) {
739			    if (flags.verbose)
740				pline("This %s is a wand of lightning!",
741					xname(otmp));
742			    doknown = TRUE;
743			}
744			if (!Blind) {
745			    Strcpy(post_engr_text,
746				    "Lightning arcs from the wand.");
747			    doblind = TRUE;
748			} else
749			    Strcpy(post_engr_text, "You hear crackling!");
750			break;
751
752		    /* type = MARK wands */
753		    /* type = ENGR_BLOOD wands */
754		    }
755		} else /* end if zappable */
756		    if (!can_reach_floor()) {
757			You_cant("reach the %s!", surface(u.ux,u.uy));
758			return(0);
759		    }
760		break;
761
762	    case WEAPON_CLASS:
763		if (is_blade(otmp)) {
764		    if ((int)otmp->spe > -3)
765			type = ENGRAVE;
766		    else
767			Your("%s too dull for engraving.", aobjnam(otmp,"are"));
768		}
769		break;
770
771	    case TOOL_CLASS:
772		if(otmp == ublindf) {
773		    pline(
774		"That is a bit difficult to engrave with, don't you think?");
775		    return(0);
776		}
777		switch (otmp->otyp)  {
778		    case MAGIC_MARKER:
779			if (otmp->spe <= 0)
780			    Your("marker has dried out.");
781			else
782			    type = MARK;
783			break;
784		    case TOWEL:
785			/* Can't really engrave with a towel */
786			ptext = FALSE;
787			if (oep)
788			    if ((oep->engr_type == DUST ) ||
789				(oep->engr_type == ENGR_BLOOD) ||
790				(oep->engr_type == MARK )) {
791				if (!Blind)
792				    You("wipe out the message here.");
793				else
794				    Your("%s %s %s.", xname(otmp),
795					 otense(otmp, "get"),
796					 is_ice(u.ux,u.uy) ?
797					 "frosty" : "dusty");
798				dengr = TRUE;
799			    } else
800				Your("%s can't wipe out this engraving.",
801				     xname(otmp));
802			else
803			    Your("%s %s %s.", xname(otmp), otense(otmp, "get"),
804				  is_ice(u.ux,u.uy) ? "frosty" : "dusty");
805			break;
806		    default:
807			break;
808		}
809		break;
810
811	    case VENOM_CLASS:
812#ifdef WIZARD
813		if (wizard) {
814		    pline("Writing a poison pen letter??");
815		    break;
816		}
817#endif
818	    case ILLOBJ_CLASS:
819		impossible("You're engraving with an illegal object!");
820		break;
821	}
822
823	if (IS_GRAVE(levl[u.ux][u.uy].typ)) {
824	    if (type == ENGRAVE || type == 0)
825		type = HEADSTONE;
826	    else {
827		/* ensures the "cannot wipe out" case */
828		type = DUST;
829		dengr = FALSE;
830		teleengr = FALSE;
831		buf[0] = (char)0;
832	    }
833	}
834
835	/* End of implement setup */
836
837	/* Identify stylus */
838	if (doknown) {
839	    makeknown(otmp->otyp);
840	    more_experienced(0,10);
841	}
842
843	if (teleengr) {
844	    rloc_engr(oep);
845	    oep = (struct engr *)0;
846	}
847
848	if (dengr) {
849	    del_engr(oep);
850	    oep = (struct engr *)0;
851	}
852
853	/* Something has changed the engraving here */
854	if (*buf) {
855	    make_engr_at(u.ux, u.uy, buf, moves, type);
856	    pline_The("engraving now reads: \"%s\".", buf);
857	    ptext = FALSE;
858	}
859
860	if (zapwand && (otmp->spe < 0)) {
861	    pline("%s %sturns to dust.",
862		  The(xname(otmp)), Blind ? "" : "glows violently, then ");
863	    if (!IS_GRAVE(levl[u.ux][u.uy].typ))
864		You("are not going to get anywhere trying to write in the %s with your dust.",
865		    is_ice(u.ux,u.uy) ? "frost" : "dust");
866	    useup(otmp);
867	    ptext = FALSE;
868	}
869
870	if (!ptext) {		/* Early exit for some implements. */
871	    if (otmp->oclass == WAND_CLASS && !can_reach_floor())
872		You_cant("reach the %s!", surface(u.ux,u.uy));
873	    return(1);
874	}
875
876	/* Special effects should have deleted the current engraving (if
877	 * possible) by now.
878	 */
879
880	if (oep) {
881	    register char c = 'n';
882
883	    /* Give player the choice to add to engraving. */
884
885	    if (type == HEADSTONE) {
886		/* no choice, only append */
887		c = 'y';
888	    } else if ( (type == oep->engr_type) && (!Blind ||
889		 (oep->engr_type == BURN) || (oep->engr_type == ENGRAVE)) ) {
890		c = yn_function("Do you want to add to the current engraving?",
891				ynqchars, 'y');
892		if (c == 'q') {
893		    pline(Never_mind);
894		    return(0);
895		}
896	    }
897
898	    if (c == 'n' || Blind) {
899
900		if( (oep->engr_type == DUST) || (oep->engr_type == ENGR_BLOOD) ||
901		    (oep->engr_type == MARK) ) {
902		    if (!Blind) {
903			You("wipe out the message that was %s here.",
904			    ((oep->engr_type == DUST)  ? "written in the dust" :
905			    ((oep->engr_type == ENGR_BLOOD) ? "scrawled in blood"   :
906							 "written")));
907			del_engr(oep);
908			oep = (struct engr *)0;
909		    } else
910		   /* Don't delete engr until after we *know* we're engraving */
911			eow = TRUE;
912		} else
913		    if ( (type == DUST) || (type == MARK) || (type == ENGR_BLOOD) ) {
914			You(
915			 "cannot wipe out the message that is %s the %s here.",
916			 oep->engr_type == BURN ?
917			   (is_ice(u.ux,u.uy) ? "melted into" : "burned into") :
918			   "engraved in", surface(u.ux,u.uy));
919			return(1);
920		    } else
921			if ( (type != oep->engr_type) || (c == 'n') ) {
922			    if (!Blind || can_reach_floor())
923				You("will overwrite the current message.");
924			    eow = TRUE;
925			}
926	    }
927	}
928
929	eloc = surface(u.ux,u.uy);
930	switch(type){
931	    default:
932		everb = (oep && !eow ? "add to the weird writing on" :
933				       "write strangely on");
934		break;
935	    case DUST:
936		everb = (oep && !eow ? "add to the writing in" :
937				       "write in");
938		eloc = is_ice(u.ux,u.uy) ? "frost" : "dust";
939		break;
940	    case HEADSTONE:
941		everb = (oep && !eow ? "add to the epitaph on" :
942				       "engrave on");
943		break;
944	    case ENGRAVE:
945		everb = (oep && !eow ? "add to the engraving in" :
946				       "engrave in");
947		break;
948	    case BURN:
949		everb = (oep && !eow ?
950			( is_ice(u.ux,u.uy) ? "add to the text melted into" :
951					      "add to the text burned into") :
952			( is_ice(u.ux,u.uy) ? "melt into" : "burn into"));
953		break;
954	    case MARK:
955		everb = (oep && !eow ? "add to the graffiti on" :
956				       "scribble on");
957		break;
958	    case ENGR_BLOOD:
959		everb = (oep && !eow ? "add to the scrawl on" :
960				       "scrawl on");
961		break;
962	}
963
964	/* Tell adventurer what is going on */
965	if (otmp != &zeroobj)
966	    You("%s the %s with %s.", everb, eloc, doname(otmp));
967	else
968	    You("%s the %s with your %s.", everb, eloc,
969		makeplural(body_part(FINGER)));
970
971	/* Prompt for engraving! */
972	Sprintf(qbuf,"What do you want to %s the %s here?", everb, eloc);
973	getlin(qbuf, ebuf);
974
975	/* Count the actual # of chars engraved not including spaces */
976	len = strlen(ebuf);
977	for (sp = ebuf; *sp; sp++) if (isspace(*sp)) len -= 1;
978
979	if (len == 0 || index(ebuf, '\033')) {
980	    if (zapwand) {
981		if (!Blind)
982		    pline("%s, then %s.",
983			  Tobjnam(otmp, "glow"), otense(otmp, "fade"));
984		return(1);
985	    } else {
986		pline(Never_mind);
987		return(0);
988	    }
989	}
990
991	/* A single `x' is the traditional signature of an illiterate person */
992	if (len != 1 || (!index(ebuf, 'x') && !index(ebuf, 'X')))
993	    u.uconduct.literate++;
994
995	/* Mix up engraving if surface or state of mind is unsound.
996	   Note: this won't add or remove any spaces. */
997	for (sp = ebuf; *sp; sp++) {
998	    if (isspace(*sp)) continue;
999	    if (((type == DUST || type == ENGR_BLOOD) && !rn2(25)) ||
1000		    (Blind && !rn2(11)) || (Confusion && !rn2(7)) ||
1001		    (Stunned && !rn2(4)) || (Hallucination && !rn2(2)))
1002		*sp = ' ' + rnd(96 - 2);	/* ASCII '!' thru '~'
1003						   (excludes ' ' and DEL) */
1004	}
1005
1006	/* Previous engraving is overwritten */
1007	if (eow) {
1008	    del_engr(oep);
1009	    oep = (struct engr *)0;
1010	}
1011
1012	/* Figure out how long it took to engrave, and if player has
1013	 * engraved too much.
1014	 */
1015	switch(type){
1016	    default:
1017		multi = -(len/10);
1018		if (multi) nomovemsg = "You finish your weird engraving.";
1019		break;
1020	    case DUST:
1021		multi = -(len/10);
1022		if (multi) nomovemsg = "You finish writing in the dust.";
1023		break;
1024	    case HEADSTONE:
1025	    case ENGRAVE:
1026		multi = -(len/10);
1027		if ((otmp->oclass == WEAPON_CLASS) &&
1028		    ((otmp->otyp != ATHAME) || otmp->cursed)) {
1029		    multi = -len;
1030		    maxelen = ((otmp->spe + 3) * 2) + 1;
1031			/* -2 = 3, -1 = 5, 0 = 7, +1 = 9, +2 = 11
1032			 * Note: this does not allow a +0 anything (except
1033			 *	 an athame) to engrave "Elbereth" all at once.
1034			 *	 However, you could now engrave "Elb", then
1035			 *	 "ere", then "th".
1036			 */
1037		    Your("%s dull.", aobjnam(otmp, "get"));
1038		    if (otmp->unpaid) {
1039			struct monst *shkp = shop_keeper(*u.ushops);
1040			if (shkp) {
1041			    You("damage it, you pay for it!");
1042			    bill_dummy_object(otmp);
1043			}
1044		    }
1045		    if (len > maxelen) {
1046			multi = -maxelen;
1047			otmp->spe = -3;
1048		    } else if (len > 1)
1049			otmp->spe -= len >> 1;
1050		    else otmp->spe -= 1; /* Prevent infinite engraving */
1051		} else
1052		    if ( (otmp->oclass == RING_CLASS) ||
1053			 (otmp->oclass == GEM_CLASS) )
1054			multi = -len;
1055		if (multi) nomovemsg = "You finish engraving.";
1056		break;
1057	    case BURN:
1058		multi = -(len/10);
1059		if (multi)
1060		    nomovemsg = is_ice(u.ux,u.uy) ?
1061			"You finish melting your message into the ice.":
1062			"You finish burning your message into the floor.";
1063		break;
1064	    case MARK:
1065		multi = -(len/10);
1066		if ((otmp->oclass == TOOL_CLASS) &&
1067		    (otmp->otyp == MAGIC_MARKER)) {
1068		    maxelen = (otmp->spe) * 2; /* one charge / 2 letters */
1069		    if (len > maxelen) {
1070			Your("marker dries out.");
1071			otmp->spe = 0;
1072			multi = -(maxelen/10);
1073		    } else
1074			if (len > 1) otmp->spe -= len >> 1;
1075			else otmp->spe -= 1; /* Prevent infinite grafitti */
1076		}
1077		if (multi) nomovemsg = "You finish defacing the dungeon.";
1078		break;
1079	    case ENGR_BLOOD:
1080		multi = -(len/10);
1081		if (multi) nomovemsg = "You finish scrawling.";
1082		break;
1083	}
1084
1085	/* Chop engraving down to size if necessary */
1086	if (len > maxelen) {
1087	    for (sp = ebuf; (maxelen && *sp); sp++)
1088		if (!isspace(*sp)) maxelen--;
1089	    if (!maxelen && *sp) {
1090		*sp = (char)0;
1091		if (multi) nomovemsg = "You cannot write any more.";
1092		You("only are able to write \"%s\"", ebuf);
1093	    }
1094	}
1095
1096	/* Add to existing engraving */
1097	if (oep) Strcpy(buf, oep->engr_txt);
1098
1099	(void) strncat(buf, ebuf, (BUFSZ - (int)strlen(buf) - 1));
1100
1101	make_engr_at(u.ux, u.uy, buf, (moves - multi), type);
1102
1103	if (post_engr_text[0]) pline(post_engr_text);
1104
1105	if (doblind && !resists_blnd(&youmonst)) {
1106	    You("are blinded by the flash!");
1107	    make_blinded((long)rnd(50),FALSE);
1108	    if (!Blind) Your(vision_clears);
1109	}
1110
1111	return(1);
1112}
1113
1114void
1115save_engravings(fd, mode)
1116int fd, mode;
1117{
1118	register struct engr *ep = head_engr;
1119	register struct engr *ep2;
1120	unsigned no_more_engr = 0;
1121
1122	while (ep) {
1123	    ep2 = ep->nxt_engr;
1124	    if (ep->engr_lth && ep->engr_txt[0] && perform_bwrite(mode)) {
1125		bwrite(fd, (genericptr_t)&(ep->engr_lth), sizeof(ep->engr_lth));
1126		bwrite(fd, (genericptr_t)ep, sizeof(struct engr) + ep->engr_lth);
1127	    }
1128	    if (release_data(mode))
1129		dealloc_engr(ep);
1130	    ep = ep2;
1131	}
1132	if (perform_bwrite(mode))
1133	    bwrite(fd, (genericptr_t)&no_more_engr, sizeof no_more_engr);
1134	if (release_data(mode))
1135	    head_engr = 0;
1136}
1137
1138void
1139rest_engravings(fd)
1140int fd;
1141{
1142	register struct engr *ep;
1143	unsigned lth;
1144
1145	head_engr = 0;
1146	while(1) {
1147		mread(fd, (genericptr_t) &lth, sizeof(unsigned));
1148		if(lth == 0) return;
1149		ep = newengr(lth);
1150		mread(fd, (genericptr_t) ep, sizeof(struct engr) + lth);
1151		ep->nxt_engr = head_engr;
1152		head_engr = ep;
1153		ep->engr_txt = (char *) (ep + 1);	/* Andreas Bormann */
1154		/* mark as finished for bones levels -- no problem for
1155		 * normal levels as the player must have finished engraving
1156		 * to be able to move again */
1157		ep->engr_time = moves;
1158	}
1159}
1160
1161void
1162del_engr(ep)
1163register struct engr *ep;
1164{
1165	if (ep == head_engr) {
1166		head_engr = ep->nxt_engr;
1167	} else {
1168		register struct engr *ept;
1169
1170		for (ept = head_engr; ept; ept = ept->nxt_engr)
1171		    if (ept->nxt_engr == ep) {
1172			ept->nxt_engr = ep->nxt_engr;
1173			break;
1174		    }
1175		if (!ept) {
1176		    impossible("Error in del_engr?");
1177		    return;
1178		}
1179	}
1180	dealloc_engr(ep);
1181}
1182
1183/* randomly relocate an engraving */
1184void
1185rloc_engr(ep)
1186struct engr *ep;
1187{
1188	int tx, ty, tryct = 200;
1189
1190	do  {
1191	    if (--tryct < 0) return;
1192	    tx = rn1(COLNO-3,2);
1193	    ty = rn2(ROWNO);
1194	} while (engr_at(tx, ty) ||
1195		!goodpos(tx, ty, (struct monst *)0, 0));
1196
1197	ep->engr_x = tx;
1198	ep->engr_y = ty;
1199}
1200
1201
1202/* Epitaphs for random headstones */
1203static const char *epitaphs[] = {
1204	"Rest in peace",
1205	"R.I.P.",
1206	"Rest In Pieces",
1207	"Note -- there are NO valuable items in this grave",
1208	"1994-1995. The Longest-Lived Hacker Ever",
1209	"The Grave of the Unknown Hacker",
1210	"We weren't sure who this was, but we buried him here anyway",
1211	"Sparky -- he was a very good dog",
1212	"Beware of Electric Third Rail",
1213	"Made in Taiwan",
1214	"Og friend. Og good dude. Og died. Og now food",
1215	"Beetlejuice Beetlejuice Beetlejuice",
1216	"Look out below!",
1217	"Please don't dig me up. I'm perfectly happy down here. -- Resident",
1218	"Postman, please note forwarding address: Gehennom, Asmodeus's Fortress, fifth lemure on the left",
1219	"Mary had a little lamb/Its fleece was white as snow/When Mary was in trouble/The lamb was first to go",
1220	"Be careful, or this could happen to you!",
1221	"Soon you'll join this fellow in hell! -- the Wizard of Yendor",
1222	"Caution! This grave contains toxic waste",
1223	"Sum quod eris",
1224	"Here lies an Atheist, all dressed up and no place to go",
1225	"Here lies Ezekiel, age 102.  The good die young.",
1226	"Here lies my wife: Here let her lie! Now she's at rest and so am I.",
1227	"Here lies Johnny Yeast. Pardon me for not rising.",
1228	"He always lied while on the earth and now he's lying in it",
1229	"I made an ash of myself",
1230	"Soon ripe. Soon rotten. Soon gone. But not forgotten.",
1231	"Here lies the body of Jonathan Blake. Stepped on the gas instead of the brake.",
1232	"Go away!"
1233};
1234
1235/* Create a headstone at the given location.
1236 * The caller is responsible for newsym(x, y).
1237 */
1238void
1239make_grave(x, y, str)
1240int x, y;
1241const char *str;
1242{
1243	/* Can we put a grave here? */
1244	if ((levl[x][y].typ != ROOM && levl[x][y].typ != GRAVE) || t_at(x,y)) return;
1245
1246	/* Make the grave */
1247	levl[x][y].typ = GRAVE;
1248
1249	/* Engrave the headstone */
1250	if (!str) str = epitaphs[rn2(SIZE(epitaphs))];
1251	del_engr_at(x, y);
1252	make_engr_at(x, y, str, 0L, HEADSTONE);
1253	return;
1254}
1255
1256
1257#endif /* OVLB */
1258
1259/*engrave.c*/
1260