option parsing buffer overflow vulnerability
[aNetHack.git] / src / engrave.c
blob5347d4968146ee1ca380095abdd16a0e648ec361
1 /* NetHack 3.6 engrave.c $NHDT-Date: 1456304550 2016/02/24 09:02:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.61 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
6 #include "lev.h"
8 STATIC_VAR NEARDATA struct engr *head_engr;
10 char *
11 random_engraving(outbuf)
12 char *outbuf;
14 const char *rumor;
16 /* a random engraving may come from the "rumors" file,
17 or from the "engrave" file (formerly in an array here) */
18 if (!rn2(4) || !(rumor = getrumor(0, outbuf, TRUE)) || !*rumor)
19 (void) get_rnd_text(ENGRAVEFILE, outbuf);
21 wipeout_text(outbuf, (int) (strlen(outbuf) / 4), 0);
22 return outbuf;
25 /* Partial rubouts for engraving characters. -3. */
26 static const struct {
27 char wipefrom;
28 const char *wipeto;
29 } rubouts[] = { { 'A', "^" },
30 { 'B', "Pb[" },
31 { 'C', "(" },
32 { 'D', "|)[" },
33 { 'E', "|FL[_" },
34 { 'F', "|-" },
35 { 'G', "C(" },
36 { 'H', "|-" },
37 { 'I', "|" },
38 { 'K', "|<" },
39 { 'L', "|_" },
40 { 'M', "|" },
41 { 'N', "|\\" },
42 { 'O', "C(" },
43 { 'P', "F" },
44 { 'Q', "C(" },
45 { 'R', "PF" },
46 { 'T', "|" },
47 { 'U', "J" },
48 { 'V', "/\\" },
49 { 'W', "V/\\" },
50 { 'Z', "/" },
51 { 'b', "|" },
52 { 'd', "c|" },
53 { 'e', "c" },
54 { 'g', "c" },
55 { 'h', "n" },
56 { 'j', "i" },
57 { 'k', "|" },
58 { 'l', "|" },
59 { 'm', "nr" },
60 { 'n', "r" },
61 { 'o', "c" },
62 { 'q', "c" },
63 { 'w', "v" },
64 { 'y', "v" },
65 { ':', "." },
66 { ';', ",:" },
67 { ',', "." },
68 { '=', "-" },
69 { '+', "-|" },
70 { '*', "+" },
71 { '@', "0" },
72 { '0', "C(" },
73 { '1', "|" },
74 { '6', "o" },
75 { '7', "/" },
76 { '8', "3o" } };
78 /* degrade some of the characters in a string */
79 void
80 wipeout_text(engr, cnt, seed)
81 char *engr;
82 int cnt;
83 unsigned seed; /* for semi-controlled randomization */
85 char *s;
86 int i, j, nxt, use_rubout, lth = (int) strlen(engr);
88 if (lth && cnt > 0) {
89 while (cnt--) {
90 /* pick next character */
91 if (!seed) {
92 /* random */
93 nxt = rn2(lth);
94 use_rubout = rn2(4);
95 } else {
96 /* predictable; caller can reproduce the same sequence by
97 supplying the same arguments later, or a pseudo-random
98 sequence by varying any of them */
99 nxt = seed % lth;
100 seed *= 31, seed %= (BUFSZ - 1);
101 use_rubout = seed & 3;
103 s = &engr[nxt];
104 if (*s == ' ')
105 continue;
107 /* rub out unreadable & small punctuation marks */
108 if (index("?.,'`-|_", *s)) {
109 *s = ' ';
110 continue;
113 if (!use_rubout)
114 i = SIZE(rubouts);
115 else
116 for (i = 0; i < SIZE(rubouts); i++)
117 if (*s == rubouts[i].wipefrom) {
119 * Pick one of the substitutes at random.
121 if (!seed)
122 j = rn2(strlen(rubouts[i].wipeto));
123 else {
124 seed *= 31, seed %= (BUFSZ - 1);
125 j = seed % (strlen(rubouts[i].wipeto));
127 *s = rubouts[i].wipeto[j];
128 break;
131 /* didn't pick rubout; use '?' for unreadable character */
132 if (i == SIZE(rubouts))
133 *s = '?';
137 /* trim trailing spaces */
138 while (lth && engr[lth - 1] == ' ')
139 engr[--lth] = '\0';
142 /* check whether hero can reach something at ground level */
143 boolean
144 can_reach_floor(check_pit)
145 boolean check_pit;
147 struct trap *t;
149 if (u.uswallow)
150 return FALSE;
151 /* Restricted/unskilled riders can't reach the floor */
152 if (u.usteed && P_SKILL(P_RIDING) < P_BASIC)
153 return FALSE;
154 if (check_pit && !Flying
155 && (t = t_at(u.ux, u.uy)) != 0 && uteetering_at_seen_pit(t))
156 return FALSE;
158 return (boolean) ((!Levitation || Is_airlevel(&u.uz)
159 || Is_waterlevel(&u.uz))
160 && (!u.uundetected || !is_hider(youmonst.data)
161 || u.umonnum == PM_TRAPPER));
164 /* give a message after caller has determined that hero can't reach */
165 void
166 cant_reach_floor(x, y, up, check_pit)
167 int x, y;
168 boolean up, check_pit;
170 You("can't reach the %s.",
171 up ? ceiling(x, y)
172 : (check_pit && can_reach_floor(FALSE))
173 ? "bottom of the pit"
174 : surface(x, y));
177 const char *
178 surface(x, y)
179 register int x, y;
181 register struct rm *lev = &levl[x][y];
183 if (x == u.ux && y == u.uy && u.uswallow && is_animal(u.ustuck->data))
184 return "maw";
185 else if (IS_AIR(lev->typ) && Is_airlevel(&u.uz))
186 return "air";
187 else if (is_pool(x, y))
188 return (Underwater && !Is_waterlevel(&u.uz)) ? "bottom" : "water";
189 else if (is_ice(x, y))
190 return "ice";
191 else if (is_lava(x, y))
192 return "lava";
193 else if (lev->typ == DRAWBRIDGE_DOWN)
194 return "bridge";
195 else if (IS_ALTAR(levl[x][y].typ))
196 return "altar";
197 else if (IS_GRAVE(levl[x][y].typ))
198 return "headstone";
199 else if (IS_FOUNTAIN(levl[x][y].typ))
200 return "fountain";
201 else if ((IS_ROOM(lev->typ) && !Is_earthlevel(&u.uz))
202 || IS_WALL(lev->typ) || IS_DOOR(lev->typ) || lev->typ == SDOOR)
203 return "floor";
204 else
205 return "ground";
208 const char *
209 ceiling(x, y)
210 register int x, y;
212 register struct rm *lev = &levl[x][y];
213 const char *what;
215 /* other room types will no longer exist when we're interested --
216 * see check_special_room()
218 if (*in_rooms(x, y, VAULT))
219 what = "vault's ceiling";
220 else if (*in_rooms(x, y, TEMPLE))
221 what = "temple's ceiling";
222 else if (*in_rooms(x, y, SHOPBASE))
223 what = "shop's ceiling";
224 else if (Is_waterlevel(&u.uz))
225 /* water plane has no surface; its air bubbles aren't below sky */
226 what = "water above";
227 else if (IS_AIR(lev->typ))
228 what = "sky";
229 else if (Underwater)
230 what = "water's surface";
231 else if ((IS_ROOM(lev->typ) && !Is_earthlevel(&u.uz))
232 || IS_WALL(lev->typ) || IS_DOOR(lev->typ) || lev->typ == SDOOR)
233 what = "ceiling";
234 else
235 what = "rock cavern";
237 return what;
240 struct engr *
241 engr_at(x, y)
242 xchar x, y;
244 register struct engr *ep = head_engr;
246 while (ep) {
247 if (x == ep->engr_x && y == ep->engr_y)
248 return ep;
249 ep = ep->nxt_engr;
251 return (struct engr *) 0;
254 /* Decide whether a particular string is engraved at a specified
255 * location; a case-insensitive substring match is used.
256 * Ignore headstones, in case the player names herself "Elbereth".
258 * If strict checking is requested, the word is only considered to be
259 * present if it is intact and is the first word in the engraving.
260 * ("Elbereth burrito" matches; "o Elbereth" does not.)
263 sengr_at(s, x, y, strict)
264 const char *s;
265 xchar x, y;
266 boolean strict;
268 register struct engr *ep = engr_at(x, y);
270 if (ep && ep->engr_type != HEADSTONE && ep->engr_time <= moves) {
271 return strict ? (strncmpi(ep->engr_txt, s, strlen(s)) == 0)
272 : (strstri(ep->engr_txt, s) != 0);
274 return FALSE;
277 void
278 u_wipe_engr(cnt)
279 int cnt;
281 if (can_reach_floor(TRUE))
282 wipe_engr_at(u.ux, u.uy, cnt, FALSE);
285 void
286 wipe_engr_at(x, y, cnt, magical)
287 xchar x, y, cnt, magical;
289 register struct engr *ep = engr_at(x, y);
291 /* Headstones are indelible */
292 if (ep && ep->engr_type != HEADSTONE) {
293 debugpline1("asked to erode %d characters", cnt);
294 if (ep->engr_type != BURN || is_ice(x, y) || (magical && !rn2(2))) {
295 if (ep->engr_type != DUST && ep->engr_type != ENGR_BLOOD) {
296 cnt = rn2(1 + 50 / (cnt + 1)) ? 0 : 1;
297 debugpline1("actually eroding %d characters", cnt);
299 wipeout_text(ep->engr_txt, (int) cnt, 0);
300 while (ep->engr_txt[0] == ' ')
301 ep->engr_txt++;
302 if (!ep->engr_txt[0])
303 del_engr(ep);
308 void
309 read_engr_at(x, y)
310 int x, y;
312 register struct engr *ep = engr_at(x, y);
313 int sensed = 0;
314 char buf[BUFSZ];
316 /* Sensing an engraving does not require sight,
317 * nor does it necessarily imply comprehension (literacy).
319 if (ep && ep->engr_txt[0]) {
320 switch (ep->engr_type) {
321 case DUST:
322 if (!Blind) {
323 sensed = 1;
324 pline("%s is written here in the %s.", Something,
325 is_ice(x, y) ? "frost" : "dust");
327 break;
328 case ENGRAVE:
329 case HEADSTONE:
330 if (!Blind || can_reach_floor(TRUE)) {
331 sensed = 1;
332 pline("%s is engraved here on the %s.", Something,
333 surface(x, y));
335 break;
336 case BURN:
337 if (!Blind || can_reach_floor(TRUE)) {
338 sensed = 1;
339 pline("Some text has been %s into the %s here.",
340 is_ice(x, y) ? "melted" : "burned", surface(x, y));
342 break;
343 case MARK:
344 if (!Blind) {
345 sensed = 1;
346 pline("There's some graffiti on the %s here.", surface(x, y));
348 break;
349 case ENGR_BLOOD:
350 /* "It's a message! Scrawled in blood!"
351 * "What's it say?"
352 * "It says... `See you next Wednesday.'" -- Thriller
354 if (!Blind) {
355 sensed = 1;
356 You_see("a message scrawled in blood here.");
358 break;
359 default:
360 impossible("%s is written in a very strange way.", Something);
361 sensed = 1;
363 if (sensed) {
364 char *et;
365 unsigned maxelen = BUFSZ - sizeof("You feel the words: \"\". ");
366 if (strlen(ep->engr_txt) > maxelen) {
367 (void) strncpy(buf, ep->engr_txt, (int) maxelen);
368 buf[maxelen] = '\0';
369 et = buf;
370 } else
371 et = ep->engr_txt;
372 You("%s: \"%s\".", (Blind) ? "feel the words" : "read", et);
373 if (context.run > 1)
374 nomul(0);
379 void
380 make_engr_at(x, y, s, e_time, e_type)
381 int x, y;
382 const char *s;
383 long e_time;
384 xchar e_type;
386 struct engr *ep;
388 if ((ep = engr_at(x, y)) != 0)
389 del_engr(ep);
390 ep = newengr(strlen(s) + 1);
391 ep->nxt_engr = head_engr;
392 head_engr = ep;
393 ep->engr_x = x;
394 ep->engr_y = y;
395 ep->engr_txt = (char *) (ep + 1);
396 Strcpy(ep->engr_txt, s);
397 /* engraving Elbereth shows wisdom */
398 if (!in_mklev && !strcmp(s, "Elbereth"))
399 exercise(A_WIS, TRUE);
400 ep->engr_time = e_time;
401 ep->engr_type = e_type > 0 ? e_type : rnd(N_ENGRAVE - 1);
402 ep->engr_lth = strlen(s) + 1;
405 /* delete any engraving at location <x,y> */
406 void
407 del_engr_at(x, y)
408 int x, y;
410 register struct engr *ep = engr_at(x, y);
412 if (ep)
413 del_engr(ep);
417 * freehand - returns true if player has a free hand
420 freehand()
422 return (!uwep || !welded(uwep)
423 || (!bimanual(uwep) && (!uarms || !uarms->cursed)));
426 static NEARDATA const char styluses[] = { ALL_CLASSES, ALLOW_NONE,
427 TOOL_CLASS, WEAPON_CLASS,
428 WAND_CLASS, GEM_CLASS,
429 RING_CLASS, 0 };
431 /* Mohs' Hardness Scale:
432 * 1 - Talc 6 - Orthoclase
433 * 2 - Gypsum 7 - Quartz
434 * 3 - Calcite 8 - Topaz
435 * 4 - Fluorite 9 - Corundum
436 * 5 - Apatite 10 - Diamond
438 * Since granite is an igneous rock hardness ~ 7, anything >= 8 should
439 * probably be able to scratch the rock.
440 * Devaluation of less hard gems is not easily possible because obj struct
441 * does not contain individual oc_cost currently. 7/91
443 * steel - 5-8.5 (usu. weapon)
444 * diamond - 10 * jade - 5-6 (nephrite)
445 * ruby - 9 (corundum) * turquoise - 5-6
446 * sapphire - 9 (corundum) * opal - 5-6
447 * topaz - 8 * glass - ~5.5
448 * emerald - 7.5-8 (beryl) * dilithium - 4-5??
449 * aquamarine - 7.5-8 (beryl) * iron - 4-5
450 * garnet - 7.25 (var. 6.5-8) * fluorite - 4
451 * agate - 7 (quartz) * brass - 3-4
452 * amethyst - 7 (quartz) * gold - 2.5-3
453 * jasper - 7 (quartz) * silver - 2.5-3
454 * onyx - 7 (quartz) * copper - 2.5-3
455 * moonstone - 6 (orthoclase) * amber - 2-2.5
458 /* return 1 if action took 1 (or more) moves, 0 if error or aborted */
460 doengrave()
462 boolean dengr = FALSE; /* TRUE if we wipe out the current engraving */
463 boolean doblind = FALSE; /* TRUE if engraving blinds the player */
464 boolean doknown = FALSE; /* TRUE if we identify the stylus */
465 boolean eow = FALSE; /* TRUE if we are overwriting oep */
466 boolean jello = FALSE; /* TRUE if we are engraving in slime */
467 boolean ptext = TRUE; /* TRUE if we must prompt for engrave text */
468 boolean teleengr = FALSE; /* TRUE if we move the old engraving */
469 boolean zapwand = FALSE; /* TRUE if we remove a wand charge */
470 xchar type = DUST; /* Type of engraving made */
471 char buf[BUFSZ]; /* Buffer for final/poly engraving text */
472 char ebuf[BUFSZ]; /* Buffer for initial engraving text */
473 char fbuf[BUFSZ]; /* Buffer for "your fingers" */
474 char qbuf[QBUFSZ]; /* Buffer for query text */
475 char post_engr_text[BUFSZ]; /* Text displayed after engraving prompt */
476 const char *everb; /* Present tense of engraving type */
477 const char *eloc; /* Where the engraving is (ie dust/floor/...) */
478 char *sp; /* Place holder for space count of engr text */
479 int len; /* # of nonspace chars of new engraving text */
480 int maxelen; /* Max allowable length of engraving text */
481 struct engr *oep = engr_at(u.ux, u.uy);
482 /* The current engraving */
483 struct obj *otmp; /* Object selected with which to engrave */
484 char *writer;
486 multi = 0; /* moves consumed */
487 nomovemsg = (char *) 0; /* occupation end message */
489 buf[0] = (char) 0;
490 ebuf[0] = (char) 0;
491 post_engr_text[0] = (char) 0;
492 maxelen = BUFSZ - 1;
493 if (is_demon(youmonst.data) || youmonst.data->mlet == S_VAMPIRE)
494 type = ENGR_BLOOD;
496 /* Can the adventurer engrave at all? */
498 if (u.uswallow) {
499 if (is_animal(u.ustuck->data)) {
500 pline("What would you write? \"Jonah was here\"?");
501 return 0;
502 } else if (is_whirly(u.ustuck->data)) {
503 cant_reach_floor(u.ux, u.uy, FALSE, FALSE);
504 return 0;
505 } else
506 jello = TRUE;
507 } else if (is_lava(u.ux, u.uy)) {
508 You_cant("write on the %s!", surface(u.ux, u.uy));
509 return 0;
510 } else if (is_pool(u.ux, u.uy) || IS_FOUNTAIN(levl[u.ux][u.uy].typ)) {
511 You_cant("write on the %s!", surface(u.ux, u.uy));
512 return 0;
514 if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) /* in bubble */) {
515 You_cant("write in thin air!");
516 return 0;
517 } else if (!accessible(u.ux, u.uy)) {
518 /* stone, tree, wall, secret corridor, pool, lava, bars */
519 You_cant("write here.");
520 return 0;
522 if (cantwield(youmonst.data)) {
523 You_cant("even hold anything!");
524 return 0;
526 if (check_capacity((char *) 0))
527 return 0;
529 /* One may write with finger, or weapon, or wand, or..., or...
530 * Edited by GAN 10/20/86 so as not to change weapon wielded.
533 otmp = getobj(styluses, "write with");
534 if (!otmp) /* otmp == zeroobj if fingers */
535 return 0;
537 if (otmp == &zeroobj) {
538 Strcat(strcpy(fbuf, "your "), body_part(FINGERTIP));
539 writer = fbuf;
540 } else
541 writer = yname(otmp);
543 /* There's no reason you should be able to write with a wand
544 * while both your hands are tied up.
546 if (!freehand() && otmp != uwep && !otmp->owornmask) {
547 You("have no free %s to write with!", body_part(HAND));
548 return 0;
551 if (jello) {
552 You("tickle %s with %s.", mon_nam(u.ustuck), writer);
553 Your("message dissolves...");
554 return 0;
556 if (otmp->oclass != WAND_CLASS && !can_reach_floor(TRUE)) {
557 cant_reach_floor(u.ux, u.uy, FALSE, TRUE);
558 return 0;
560 if (IS_ALTAR(levl[u.ux][u.uy].typ)) {
561 You("make a motion towards the altar with %s.", writer);
562 altar_wrath(u.ux, u.uy);
563 return 0;
565 if (IS_GRAVE(levl[u.ux][u.uy].typ)) {
566 if (otmp == &zeroobj) { /* using only finger */
567 You("would only make a small smudge on the %s.",
568 surface(u.ux, u.uy));
569 return 0;
570 } else if (!levl[u.ux][u.uy].disturbed) {
571 You("disturb the undead!");
572 levl[u.ux][u.uy].disturbed = 1;
573 (void) makemon(&mons[PM_GHOUL], u.ux, u.uy, NO_MM_FLAGS);
574 exercise(A_WIS, FALSE);
575 return 1;
579 /* SPFX for items */
581 switch (otmp->oclass) {
582 default:
583 case AMULET_CLASS:
584 case CHAIN_CLASS:
585 case POTION_CLASS:
586 case COIN_CLASS:
587 break;
588 case RING_CLASS:
589 /* "diamond" rings and others should work */
590 case GEM_CLASS:
591 /* diamonds & other hard gems should work */
592 if (objects[otmp->otyp].oc_tough) {
593 type = ENGRAVE;
594 break;
596 break;
597 case ARMOR_CLASS:
598 if (is_boots(otmp)) {
599 type = DUST;
600 break;
602 /*FALLTHRU*/
603 /* Objects too large to engrave with */
604 case BALL_CLASS:
605 case ROCK_CLASS:
606 You_cant("engrave with such a large object!");
607 ptext = FALSE;
608 break;
609 /* Objects too silly to engrave with */
610 case FOOD_CLASS:
611 case SCROLL_CLASS:
612 case SPBOOK_CLASS:
613 pline("%s would get %s.", Yname2(otmp),
614 is_ice(u.ux, u.uy) ? "all frosty" : "too dirty");
615 ptext = FALSE;
616 break;
617 case RANDOM_CLASS: /* This should mean fingers */
618 break;
620 /* The charge is removed from the wand before prompting for
621 * the engraving text, because all kinds of setup decisions
622 * and pre-engraving messages are based upon knowing what type
623 * of engraving the wand is going to do. Also, the player
624 * will have potentially seen "You wrest .." message, and
625 * therefore will know they are using a charge.
627 case WAND_CLASS:
628 if (zappable(otmp)) {
629 check_unpaid(otmp);
630 if (otmp->cursed && !rn2(WAND_BACKFIRE_CHANCE)) {
631 wand_explode(otmp, 0);
632 return 1;
634 zapwand = TRUE;
635 if (!can_reach_floor(TRUE))
636 ptext = FALSE;
638 switch (otmp->otyp) {
639 /* DUST wands */
640 default:
641 break;
642 /* NODIR wands */
643 case WAN_LIGHT:
644 case WAN_SECRET_DOOR_DETECTION:
645 case WAN_CREATE_MONSTER:
646 case WAN_WISHING:
647 case WAN_ENLIGHTENMENT:
648 zapnodir(otmp);
649 break;
650 /* IMMEDIATE wands */
651 /* If wand is "IMMEDIATE", remember to affect the
652 * previous engraving even if turning to dust.
654 case WAN_STRIKING:
655 Strcpy(post_engr_text,
656 "The wand unsuccessfully fights your attempt to write!");
657 break;
658 case WAN_SLOW_MONSTER:
659 if (!Blind) {
660 Sprintf(post_engr_text, "The bugs on the %s slow down!",
661 surface(u.ux, u.uy));
663 break;
664 case WAN_SPEED_MONSTER:
665 if (!Blind) {
666 Sprintf(post_engr_text, "The bugs on the %s speed up!",
667 surface(u.ux, u.uy));
669 break;
670 case WAN_POLYMORPH:
671 if (oep) {
672 if (!Blind) {
673 type = (xchar) 0; /* random */
674 (void) random_engraving(buf);
676 dengr = TRUE;
678 break;
679 case WAN_NOTHING:
680 case WAN_UNDEAD_TURNING:
681 case WAN_OPENING:
682 case WAN_LOCKING:
683 case WAN_PROBING:
684 break;
685 /* RAY wands */
686 case WAN_MAGIC_MISSILE:
687 ptext = TRUE;
688 if (!Blind) {
689 Sprintf(post_engr_text,
690 "The %s is riddled by bullet holes!",
691 surface(u.ux, u.uy));
693 break;
694 /* can't tell sleep from death - Eric Backus */
695 case WAN_SLEEP:
696 case WAN_DEATH:
697 if (!Blind) {
698 Sprintf(post_engr_text, "The bugs on the %s stop moving!",
699 surface(u.ux, u.uy));
701 break;
702 case WAN_COLD:
703 if (!Blind)
704 Strcpy(post_engr_text,
705 "A few ice cubes drop from the wand.");
706 if (!oep || (oep->engr_type != BURN))
707 break;
708 case WAN_CANCELLATION:
709 case WAN_MAKE_INVISIBLE:
710 if (oep && oep->engr_type != HEADSTONE) {
711 if (!Blind)
712 pline_The("engraving on the %s vanishes!",
713 surface(u.ux, u.uy));
714 dengr = TRUE;
716 break;
717 case WAN_TELEPORTATION:
718 if (oep && oep->engr_type != HEADSTONE) {
719 if (!Blind)
720 pline_The("engraving on the %s vanishes!",
721 surface(u.ux, u.uy));
722 teleengr = TRUE;
724 break;
725 /* type = ENGRAVE wands */
726 case WAN_DIGGING:
727 ptext = TRUE;
728 type = ENGRAVE;
729 if (!objects[otmp->otyp].oc_name_known) {
730 if (flags.verbose)
731 pline("This %s is a wand of digging!", xname(otmp));
732 doknown = TRUE;
734 Strcpy(post_engr_text,
735 (Blind && !Deaf)
736 ? "You hear drilling!"
737 : Blind
738 ? "You feel tremors."
739 : IS_GRAVE(levl[u.ux][u.uy].typ)
740 ? "Chips fly out from the headstone."
741 : is_ice(u.ux, u.uy)
742 ? "Ice chips fly up from the ice surface!"
743 : (level.locations[u.ux][u.uy].typ
744 == DRAWBRIDGE_DOWN)
745 ? "Splinters fly up from the bridge."
746 : "Gravel flies up from the floor.");
747 break;
748 /* type = BURN wands */
749 case WAN_FIRE:
750 ptext = TRUE;
751 type = BURN;
752 if (!objects[otmp->otyp].oc_name_known) {
753 if (flags.verbose)
754 pline("This %s is a wand of fire!", xname(otmp));
755 doknown = TRUE;
757 Strcpy(post_engr_text, Blind ? "You feel the wand heat up."
758 : "Flames fly from the wand.");
759 break;
760 case WAN_LIGHTNING:
761 ptext = TRUE;
762 type = BURN;
763 if (!objects[otmp->otyp].oc_name_known) {
764 if (flags.verbose)
765 pline("This %s is a wand of lightning!", xname(otmp));
766 doknown = TRUE;
768 if (!Blind) {
769 Strcpy(post_engr_text, "Lightning arcs from the wand.");
770 doblind = TRUE;
771 } else
772 Strcpy(post_engr_text, !Deaf
773 ? "You hear crackling!"
774 : "Your hair stands up!");
775 break;
777 /* type = MARK wands */
778 /* type = ENGR_BLOOD wands */
780 } else { /* end if zappable */
781 /* failing to wrest one last charge takes time */
782 ptext = FALSE; /* use "early exit" below, return 1 */
783 /* give feedback here if we won't be getting the
784 "can't reach floor" message below */
785 if (can_reach_floor(TRUE)) {
786 /* cancelled wand turns to dust */
787 if (otmp->spe < 0)
788 zapwand = TRUE;
789 /* empty wand just doesn't write */
790 else
791 pline_The("wand is too worn out to engrave.");
794 break;
796 case WEAPON_CLASS:
797 if (is_blade(otmp)) {
798 if ((int) otmp->spe > -3)
799 type = ENGRAVE;
800 else
801 pline("%s too dull for engraving.", Yobjnam2(otmp, "are"));
803 break;
805 case TOOL_CLASS:
806 if (otmp == ublindf) {
807 pline(
808 "That is a bit difficult to engrave with, don't you think?");
809 return 0;
811 switch (otmp->otyp) {
812 case MAGIC_MARKER:
813 if (otmp->spe <= 0)
814 Your("marker has dried out.");
815 else
816 type = MARK;
817 break;
818 case TOWEL:
819 /* Can't really engrave with a towel */
820 ptext = FALSE;
821 if (oep)
822 if (oep->engr_type == DUST
823 || oep->engr_type == ENGR_BLOOD
824 || oep->engr_type == MARK) {
825 if (is_wet_towel(otmp))
826 dry_a_towel(otmp, -1, TRUE);
827 if (!Blind)
828 You("wipe out the message here.");
829 else
830 pline("%s %s.", Yobjnam2(otmp, "get"),
831 is_ice(u.ux, u.uy) ? "frosty" : "dusty");
832 dengr = TRUE;
833 } else
834 pline("%s can't wipe out this engraving.", Yname2(otmp));
835 else
836 pline("%s %s.", Yobjnam2(otmp, "get"),
837 is_ice(u.ux, u.uy) ? "frosty" : "dusty");
838 break;
839 default:
840 break;
842 break;
844 case VENOM_CLASS:
845 if (wizard) {
846 pline("Writing a poison pen letter??");
847 break;
849 /*FALLTHRU*/
850 case ILLOBJ_CLASS:
851 impossible("You're engraving with an illegal object!");
852 break;
855 if (IS_GRAVE(levl[u.ux][u.uy].typ)) {
856 if (type == ENGRAVE || type == 0) {
857 type = HEADSTONE;
858 } else {
859 /* ensures the "cannot wipe out" case */
860 type = DUST;
861 dengr = FALSE;
862 teleengr = FALSE;
863 buf[0] = '\0';
868 * End of implement setup
871 /* Identify stylus */
872 if (doknown) {
873 learnwand(otmp);
874 if (objects[otmp->otyp].oc_name_known)
875 more_experienced(0, 10);
877 if (teleengr) {
878 rloc_engr(oep);
879 oep = (struct engr *) 0;
881 if (dengr) {
882 del_engr(oep);
883 oep = (struct engr *) 0;
885 /* Something has changed the engraving here */
886 if (*buf) {
887 make_engr_at(u.ux, u.uy, buf, moves, type);
888 pline_The("engraving now reads: \"%s\".", buf);
889 ptext = FALSE;
891 if (zapwand && (otmp->spe < 0)) {
892 pline("%s %sturns to dust.", The(xname(otmp)),
893 Blind ? "" : "glows violently, then ");
894 if (!IS_GRAVE(levl[u.ux][u.uy].typ))
895 You(
896 "are not going to get anywhere trying to write in the %s with your dust.",
897 is_ice(u.ux, u.uy) ? "frost" : "dust");
898 useup(otmp);
899 otmp = 0; /* wand is now gone */
900 ptext = FALSE;
902 /* Early exit for some implements. */
903 if (!ptext) {
904 if (otmp && otmp->oclass == WAND_CLASS && !can_reach_floor(TRUE))
905 cant_reach_floor(u.ux, u.uy, FALSE, TRUE);
906 return 1;
909 * Special effects should have deleted the current engraving (if
910 * possible) by now.
912 if (oep) {
913 register char c = 'n';
915 /* Give player the choice to add to engraving. */
916 if (type == HEADSTONE) {
917 /* no choice, only append */
918 c = 'y';
919 } else if (type == oep->engr_type
920 && (!Blind || oep->engr_type == BURN
921 || oep->engr_type == ENGRAVE)) {
922 c = yn_function("Do you want to add to the current engraving?",
923 ynqchars, 'y');
924 if (c == 'q') {
925 pline1(Never_mind);
926 return 0;
930 if (c == 'n' || Blind) {
931 if (oep->engr_type == DUST
932 || oep->engr_type == ENGR_BLOOD
933 || oep->engr_type == MARK) {
934 if (!Blind) {
935 You("wipe out the message that was %s here.",
936 (oep->engr_type == DUST)
937 ? "written in the dust"
938 : (oep->engr_type == ENGR_BLOOD)
939 ? "scrawled in blood"
940 : "written");
941 del_engr(oep);
942 oep = (struct engr *) 0;
943 } else
944 /* Don't delete engr until after we *know* we're engraving
946 eow = TRUE;
947 } else if (type == DUST || type == MARK || type == ENGR_BLOOD) {
948 You("cannot wipe out the message that is %s the %s here.",
949 oep->engr_type == BURN
950 ? (is_ice(u.ux, u.uy) ? "melted into" : "burned into")
951 : "engraved in",
952 surface(u.ux, u.uy));
953 return 1;
954 } else if (type != oep->engr_type || c == 'n') {
955 if (!Blind || can_reach_floor(TRUE))
956 You("will overwrite the current message.");
957 eow = TRUE;
962 eloc = surface(u.ux, u.uy);
963 switch (type) {
964 default:
965 everb = (oep && !eow ? "add to the weird writing on"
966 : "write strangely on");
967 break;
968 case DUST:
969 everb = (oep && !eow ? "add to the writing in" : "write in");
970 eloc = is_ice(u.ux, u.uy) ? "frost" : "dust";
971 break;
972 case HEADSTONE:
973 everb = (oep && !eow ? "add to the epitaph on" : "engrave on");
974 break;
975 case ENGRAVE:
976 everb = (oep && !eow ? "add to the engraving in" : "engrave in");
977 break;
978 case BURN:
979 everb = (oep && !eow
980 ? (is_ice(u.ux, u.uy) ? "add to the text melted into"
981 : "add to the text burned into")
982 : (is_ice(u.ux, u.uy) ? "melt into" : "burn into"));
983 break;
984 case MARK:
985 everb = (oep && !eow ? "add to the graffiti on" : "scribble on");
986 break;
987 case ENGR_BLOOD:
988 everb = (oep && !eow ? "add to the scrawl on" : "scrawl on");
989 break;
992 /* Tell adventurer what is going on */
993 if (otmp != &zeroobj)
994 You("%s the %s with %s.", everb, eloc, doname(otmp));
995 else
996 You("%s the %s with your %s.", everb, eloc, body_part(FINGERTIP));
998 /* Prompt for engraving! */
999 Sprintf(qbuf, "What do you want to %s the %s here?", everb, eloc);
1000 getlin(qbuf, ebuf);
1001 /* convert tabs to spaces and condense consecutive spaces to one */
1002 mungspaces(ebuf);
1004 /* Count the actual # of chars engraved not including spaces */
1005 len = strlen(ebuf);
1006 for (sp = ebuf; *sp; sp++)
1007 if (*sp == ' ')
1008 len -= 1;
1010 if (len == 0 || index(ebuf, '\033')) {
1011 if (zapwand) {
1012 if (!Blind)
1013 pline("%s, then %s.", Tobjnam(otmp, "glow"),
1014 otense(otmp, "fade"));
1015 return 1;
1016 } else {
1017 pline1(Never_mind);
1018 return 0;
1022 /* A single `x' is the traditional signature of an illiterate person */
1023 if (len != 1 || (!index(ebuf, 'x') && !index(ebuf, 'X')))
1024 u.uconduct.literate++;
1026 /* Mix up engraving if surface or state of mind is unsound.
1027 Note: this won't add or remove any spaces. */
1028 for (sp = ebuf; *sp; sp++) {
1029 if (*sp == ' ')
1030 continue;
1031 if (((type == DUST || type == ENGR_BLOOD) && !rn2(25))
1032 || (Blind && !rn2(11)) || (Confusion && !rn2(7))
1033 || (Stunned && !rn2(4)) || (Hallucination && !rn2(2)))
1034 *sp = ' ' + rnd(96 - 2); /* ASCII '!' thru '~'
1035 (excludes ' ' and DEL) */
1038 /* Previous engraving is overwritten */
1039 if (eow) {
1040 del_engr(oep);
1041 oep = (struct engr *) 0;
1044 /* Figure out how long it took to engrave, and if player has
1045 * engraved too much.
1047 switch (type) {
1048 default:
1049 multi = -(len / 10);
1050 if (multi)
1051 nomovemsg = "You finish your weird engraving.";
1052 break;
1053 case DUST:
1054 multi = -(len / 10);
1055 if (multi)
1056 nomovemsg = "You finish writing in the dust.";
1057 break;
1058 case HEADSTONE:
1059 case ENGRAVE:
1060 multi = -(len / 10);
1061 if (otmp->oclass == WEAPON_CLASS
1062 && (otmp->otyp != ATHAME || otmp->cursed)) {
1063 multi = -len;
1064 maxelen = ((otmp->spe + 3) * 2) + 1;
1065 /* -2 => 3, -1 => 5, 0 => 7, +1 => 9, +2 => 11
1066 * Note: this does not allow a +0 anything (except an athame)
1067 * to engrave "Elbereth" all at once.
1068 * However, you can engrave "Elb", then "ere", then "th".
1070 pline("%s dull.", Yobjnam2(otmp, "get"));
1071 costly_alteration(otmp, COST_DEGRD);
1072 if (len > maxelen) {
1073 multi = -maxelen;
1074 otmp->spe = -3;
1075 } else if (len > 1)
1076 otmp->spe -= len >> 1;
1077 else
1078 otmp->spe -= 1; /* Prevent infinite engraving */
1079 } else if (otmp->oclass == RING_CLASS || otmp->oclass == GEM_CLASS) {
1080 multi = -len;
1082 if (multi)
1083 nomovemsg = "You finish engraving.";
1084 break;
1085 case BURN:
1086 multi = -(len / 10);
1087 if (multi)
1088 nomovemsg = is_ice(u.ux, u.uy)
1089 ? "You finish melting your message into the ice."
1090 : "You finish burning your message into the floor.";
1091 break;
1092 case MARK:
1093 multi = -(len / 10);
1094 if (otmp->otyp == MAGIC_MARKER) {
1095 maxelen = otmp->spe * 2; /* one charge / 2 letters */
1096 if (len > maxelen) {
1097 Your("marker dries out.");
1098 otmp->spe = 0;
1099 multi = -(maxelen / 10);
1100 } else if (len > 1)
1101 otmp->spe -= len >> 1;
1102 else
1103 otmp->spe -= 1; /* Prevent infinite graffiti */
1105 if (multi)
1106 nomovemsg = "You finish defacing the dungeon.";
1107 break;
1108 case ENGR_BLOOD:
1109 multi = -(len / 10);
1110 if (multi)
1111 nomovemsg = "You finish scrawling.";
1112 break;
1115 /* Chop engraving down to size if necessary */
1116 if (len > maxelen) {
1117 for (sp = ebuf; maxelen && *sp; sp++)
1118 if (*sp == ' ')
1119 maxelen--;
1120 if (!maxelen && *sp) {
1121 *sp = '\0';
1122 if (multi)
1123 nomovemsg = "You cannot write any more.";
1124 You("are only able to write \"%s\".", ebuf);
1128 if (oep) /* add to existing engraving */
1129 Strcpy(buf, oep->engr_txt);
1130 (void) strncat(buf, ebuf, BUFSZ - (int) strlen(buf) - 1);
1131 /* Put the engraving onto the map */
1132 make_engr_at(u.ux, u.uy, buf, moves - multi, type);
1134 if (post_engr_text[0])
1135 pline("%s", post_engr_text);
1136 if (doblind && !resists_blnd(&youmonst)) {
1137 You("are blinded by the flash!");
1138 make_blinded((long) rnd(50), FALSE);
1139 if (!Blind)
1140 Your1(vision_clears);
1142 return 1;
1145 /* while loading bones, clean up text which might accidentally
1146 or maliciously disrupt player's terminal when displayed */
1147 void
1148 sanitize_engravings()
1150 struct engr *ep;
1152 for (ep = head_engr; ep; ep = ep->nxt_engr) {
1153 sanitize_name(ep->engr_txt);
1157 void
1158 save_engravings(fd, mode)
1159 int fd, mode;
1161 struct engr *ep, *ep2;
1162 unsigned no_more_engr = 0;
1164 for (ep = head_engr; ep; ep = ep2) {
1165 ep2 = ep->nxt_engr;
1166 if (ep->engr_lth && ep->engr_txt[0] && perform_bwrite(mode)) {
1167 bwrite(fd, (genericptr_t) &ep->engr_lth, sizeof ep->engr_lth);
1168 bwrite(fd, (genericptr_t) ep, sizeof (struct engr) + ep->engr_lth);
1170 if (release_data(mode))
1171 dealloc_engr(ep);
1173 if (perform_bwrite(mode))
1174 bwrite(fd, (genericptr_t) &no_more_engr, sizeof no_more_engr);
1175 if (release_data(mode))
1176 head_engr = 0;
1179 void
1180 rest_engravings(fd)
1181 int fd;
1183 struct engr *ep;
1184 unsigned lth;
1186 head_engr = 0;
1187 while (1) {
1188 mread(fd, (genericptr_t) &lth, sizeof lth);
1189 if (lth == 0)
1190 return;
1191 ep = newengr(lth);
1192 mread(fd, (genericptr_t) ep, sizeof (struct engr) + lth);
1193 ep->nxt_engr = head_engr;
1194 head_engr = ep;
1195 ep->engr_txt = (char *) (ep + 1); /* Andreas Bormann */
1196 /* Mark as finished for bones levels -- no problem for
1197 * normal levels as the player must have finished engraving
1198 * to be able to move again.
1200 ep->engr_time = moves;
1204 /* to support '#stats' wizard-mode command */
1205 void
1206 engr_stats(hdrfmt, hdrbuf, count, size)
1207 const char *hdrfmt;
1208 char *hdrbuf;
1209 long *count, *size;
1211 struct engr *ep;
1213 Sprintf(hdrbuf, hdrfmt, (long) sizeof (struct engr));
1214 *count = *size = 0L;
1215 for (ep = head_engr; ep; ep = ep->nxt_engr) {
1216 ++*count;
1217 *size += (long) sizeof *ep + (long) ep->engr_lth;
1221 void
1222 del_engr(ep)
1223 register struct engr *ep;
1225 if (ep == head_engr) {
1226 head_engr = ep->nxt_engr;
1227 } else {
1228 register struct engr *ept;
1230 for (ept = head_engr; ept; ept = ept->nxt_engr)
1231 if (ept->nxt_engr == ep) {
1232 ept->nxt_engr = ep->nxt_engr;
1233 break;
1235 if (!ept) {
1236 impossible("Error in del_engr?");
1237 return;
1240 dealloc_engr(ep);
1243 /* randomly relocate an engraving */
1244 void
1245 rloc_engr(ep)
1246 struct engr *ep;
1248 int tx, ty, tryct = 200;
1250 do {
1251 if (--tryct < 0)
1252 return;
1253 tx = rn1(COLNO - 3, 2);
1254 ty = rn2(ROWNO);
1255 } while (engr_at(tx, ty) || !goodpos(tx, ty, (struct monst *) 0, 0));
1257 ep->engr_x = tx;
1258 ep->engr_y = ty;
1261 /* Create a headstone at the given location.
1262 * The caller is responsible for newsym(x, y).
1264 void
1265 make_grave(x, y, str)
1266 int x, y;
1267 const char *str;
1269 char buf[BUFSZ];
1271 /* Can we put a grave here? */
1272 if ((levl[x][y].typ != ROOM && levl[x][y].typ != GRAVE) || t_at(x, y))
1273 return;
1274 /* Make the grave */
1275 levl[x][y].typ = GRAVE;
1276 /* Engrave the headstone */
1277 del_engr_at(x, y);
1278 if (!str)
1279 str = get_rnd_text(EPITAPHFILE, buf);
1280 make_engr_at(x, y, str, 0L, HEADSTONE);
1281 return;
1284 /*engrave.c*/