NetHack->aNetHack
[aNetHack.git] / src / engrave.c
blob6bb71b9cd1d6a4c132d326e5fe6d79d09c524b37
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))
189 ? "bottom" : hliquid("water");
190 else if (is_ice(x, y))
191 return "ice";
192 else if (is_lava(x, y))
193 return hliquid("lava");
194 else if (lev->typ == DRAWBRIDGE_DOWN)
195 return "bridge";
196 else if (IS_ALTAR(levl[x][y].typ))
197 return "altar";
198 else if (IS_GRAVE(levl[x][y].typ))
199 return "headstone";
200 else if (IS_FOUNTAIN(levl[x][y].typ))
201 return "fountain";
202 else if ((IS_ROOM(lev->typ) && !Is_earthlevel(&u.uz))
203 || IS_WALL(lev->typ) || IS_DOOR(lev->typ) || lev->typ == SDOOR)
204 return "floor";
205 else
206 return "ground";
209 const char *
210 ceiling(x, y)
211 register int x, y;
213 register struct rm *lev = &levl[x][y];
214 const char *what;
216 /* other room types will no longer exist when we're interested --
217 * see check_special_room()
219 if (*in_rooms(x, y, VAULT))
220 what = "vault's ceiling";
221 else if (*in_rooms(x, y, TEMPLE))
222 what = "temple's ceiling";
223 else if (*in_rooms(x, y, SHOPBASE))
224 what = "shop's ceiling";
225 else if (Is_waterlevel(&u.uz))
226 /* water plane has no surface; its air bubbles aren't below sky */
227 what = "water above";
228 else if (IS_AIR(lev->typ))
229 what = "sky";
230 else if (Underwater)
231 what = "water's surface";
232 else if ((IS_ROOM(lev->typ) && !Is_earthlevel(&u.uz))
233 || IS_WALL(lev->typ) || IS_DOOR(lev->typ) || lev->typ == SDOOR)
234 what = "ceiling";
235 else
236 what = "rock cavern";
238 return what;
241 struct engr *
242 engr_at(x, y)
243 xchar x, y;
245 register struct engr *ep = head_engr;
247 while (ep) {
248 if (x == ep->engr_x && y == ep->engr_y)
249 return ep;
250 ep = ep->nxt_engr;
252 return (struct engr *) 0;
255 /* Decide whether a particular string is engraved at a specified
256 * location; a case-insensitive substring match is used.
257 * Ignore headstones, in case the player names herself "Elbereth".
259 * If strict checking is requested, the word is only considered to be
260 * present if it is intact and is the entire content of the engraving.
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 ? (fuzzymatch(ep->engr_txt, s, "", TRUE))
272 : (strstri(ep->engr_txt, s) != 0);
275 return FALSE;
278 void
279 u_wipe_engr(cnt)
280 int cnt;
282 if (can_reach_floor(TRUE))
283 wipe_engr_at(u.ux, u.uy, cnt, FALSE);
286 void
287 wipe_engr_at(x, y, cnt, magical)
288 xchar x, y, cnt, magical;
290 register struct engr *ep = engr_at(x, y);
292 /* Headstones are indelible */
293 if (ep && ep->engr_type != HEADSTONE) {
294 debugpline1("asked to erode %d characters", cnt);
295 if (ep->engr_type != BURN || is_ice(x, y) || (magical && !rn2(2))) {
296 if (ep->engr_type != DUST && ep->engr_type != ENGR_BLOOD) {
297 cnt = rn2(1 + 50 / (cnt + 1)) ? 0 : 1;
298 debugpline1("actually eroding %d characters", cnt);
300 wipeout_text(ep->engr_txt, (int) cnt, 0);
301 while (ep->engr_txt[0] == ' ')
302 ep->engr_txt++;
303 if (!ep->engr_txt[0])
304 del_engr(ep);
309 void
310 read_engr_at(x, y)
311 int x, y;
313 register struct engr *ep = engr_at(x, y);
314 int sensed = 0;
315 char buf[BUFSZ];
317 /* Sensing an engraving does not require sight,
318 * nor does it necessarily imply comprehension (literacy).
320 if (ep && ep->engr_txt[0]) {
321 switch (ep->engr_type) {
322 case DUST:
323 if (!Blind) {
324 sensed = 1;
325 pline("%s is written here in the %s.", Something,
326 is_ice(x, y) ? "frost" : "dust");
328 break;
329 case ENGRAVE:
330 case HEADSTONE:
331 if (!Blind || can_reach_floor(TRUE)) {
332 sensed = 1;
333 pline("%s is engraved here on the %s.", Something,
334 surface(x, y));
336 break;
337 case BURN:
338 if (!Blind || can_reach_floor(TRUE)) {
339 sensed = 1;
340 pline("Some text has been %s into the %s here.",
341 is_ice(x, y) ? "melted" : "burned", surface(x, y));
343 break;
344 case MARK:
345 if (!Blind) {
346 sensed = 1;
347 pline("There's some graffiti on the %s here.", surface(x, y));
349 break;
350 case ENGR_BLOOD:
351 /* "It's a message! Scrawled in blood!"
352 * "What's it say?"
353 * "It says... `See you next Wednesday.'" -- Thriller
355 if (!Blind) {
356 sensed = 1;
357 You_see("a message scrawled in blood here.");
359 break;
360 default:
361 impossible("%s is written in a very strange way.", Something);
362 sensed = 1;
364 if (sensed) {
365 char *et;
366 unsigned maxelen = BUFSZ - sizeof("You feel the words: \"\". ");
367 if (strlen(ep->engr_txt) > maxelen) {
368 (void) strncpy(buf, ep->engr_txt, (int) maxelen);
369 buf[maxelen] = '\0';
370 et = buf;
371 } else
372 et = ep->engr_txt;
373 You("%s: \"%s\".", (Blind) ? "feel the words" : "read", et);
374 if (context.run > 1)
375 nomul(0);
380 void
381 make_engr_at(x, y, s, e_time, e_type)
382 int x, y;
383 const char *s;
384 long e_time;
385 xchar e_type;
387 struct engr *ep;
388 unsigned smem = strlen(s) + 1;
390 if ((ep = engr_at(x, y)) != 0)
391 del_engr(ep);
392 ep = newengr(smem);
393 (void) memset((genericptr_t)ep, 0, smem + sizeof(struct engr));
394 ep->nxt_engr = head_engr;
395 head_engr = ep;
396 ep->engr_x = x;
397 ep->engr_y = y;
398 ep->engr_txt = (char *) (ep + 1);
399 Strcpy(ep->engr_txt, s);
400 /* engraving Elbereth shows wisdom */
401 if (!in_mklev && !strcmp(s, "Elbereth"))
402 exercise(A_WIS, TRUE);
403 ep->engr_time = e_time;
404 ep->engr_type = e_type > 0 ? e_type : rnd(N_ENGRAVE - 1);
405 ep->engr_lth = smem;
408 /* delete any engraving at location <x,y> */
409 void
410 del_engr_at(x, y)
411 int x, y;
413 register struct engr *ep = engr_at(x, y);
415 if (ep)
416 del_engr(ep);
420 * freehand - returns true if player has a free hand
423 freehand()
425 return (!uwep || !welded(uwep)
426 || (!bimanual(uwep) && (!uarms || !uarms->cursed)));
429 static NEARDATA const char styluses[] = { ALL_CLASSES, ALLOW_NONE,
430 TOOL_CLASS, WEAPON_CLASS,
431 WAND_CLASS, GEM_CLASS,
432 RING_CLASS, 0 };
434 /* Mohs' Hardness Scale:
435 * 1 - Talc 6 - Orthoclase
436 * 2 - Gypsum 7 - Quartz
437 * 3 - Calcite 8 - Topaz
438 * 4 - Fluorite 9 - Corundum
439 * 5 - Apatite 10 - Diamond
441 * Since granite is an igneous rock hardness ~ 7, anything >= 8 should
442 * probably be able to scratch the rock.
443 * Devaluation of less hard gems is not easily possible because obj struct
444 * does not contain individual oc_cost currently. 7/91
446 * steel - 5-8.5 (usu. weapon)
447 * diamond - 10 * jade - 5-6 (nephrite)
448 * ruby - 9 (corundum) * turquoise - 5-6
449 * sapphire - 9 (corundum) * opal - 5-6
450 * topaz - 8 * glass - ~5.5
451 * emerald - 7.5-8 (beryl) * dilithium - 4-5??
452 * aquamarine - 7.5-8 (beryl) * iron - 4-5
453 * garnet - 7.25 (var. 6.5-8) * fluorite - 4
454 * agate - 7 (quartz) * brass - 3-4
455 * amethyst - 7 (quartz) * gold - 2.5-3
456 * jasper - 7 (quartz) * silver - 2.5-3
457 * onyx - 7 (quartz) * copper - 2.5-3
458 * moonstone - 6 (orthoclase) * amber - 2-2.5
461 /* return 1 if action took 1 (or more) moves, 0 if error or aborted */
463 doengrave()
465 boolean dengr = FALSE; /* TRUE if we wipe out the current engraving */
466 boolean doblind = FALSE; /* TRUE if engraving blinds the player */
467 boolean doknown = FALSE; /* TRUE if we identify the stylus */
468 boolean eow = FALSE; /* TRUE if we are overwriting oep */
469 boolean jello = FALSE; /* TRUE if we are engraving in slime */
470 boolean ptext = TRUE; /* TRUE if we must prompt for engrave text */
471 boolean teleengr = FALSE; /* TRUE if we move the old engraving */
472 boolean zapwand = FALSE; /* TRUE if we remove a wand charge */
473 xchar type = DUST; /* Type of engraving made */
474 char buf[BUFSZ]; /* Buffer for final/poly engraving text */
475 char ebuf[BUFSZ]; /* Buffer for initial engraving text */
476 char fbuf[BUFSZ]; /* Buffer for "your fingers" */
477 char qbuf[QBUFSZ]; /* Buffer for query text */
478 char post_engr_text[BUFSZ]; /* Text displayed after engraving prompt */
479 const char *everb; /* Present tense of engraving type */
480 const char *eloc; /* Where the engraving is (ie dust/floor/...) */
481 char *sp; /* Place holder for space count of engr text */
482 int len; /* # of nonspace chars of new engraving text */
483 int maxelen; /* Max allowable length of engraving text */
484 struct engr *oep = engr_at(u.ux, u.uy);
485 /* The current engraving */
486 struct obj *otmp; /* Object selected with which to engrave */
487 char *writer;
489 multi = 0; /* moves consumed */
490 nomovemsg = (char *) 0; /* occupation end message */
492 buf[0] = (char) 0;
493 ebuf[0] = (char) 0;
494 post_engr_text[0] = (char) 0;
495 maxelen = BUFSZ - 1;
496 if (is_demon(youmonst.data) || youmonst.data->mlet == S_VAMPIRE)
497 type = ENGR_BLOOD;
499 /* Can the adventurer engrave at all? */
501 if (u.uswallow) {
502 if (is_animal(u.ustuck->data)) {
503 pline("What would you write? \"Jonah was here\"?");
504 return 0;
505 } else if (is_whirly(u.ustuck->data)) {
506 cant_reach_floor(u.ux, u.uy, FALSE, FALSE);
507 return 0;
508 } else
509 jello = TRUE;
510 } else if (is_lava(u.ux, u.uy)) {
511 You_cant("write on the %s!", surface(u.ux, u.uy));
512 return 0;
513 } else if (is_pool(u.ux, u.uy) || IS_FOUNTAIN(levl[u.ux][u.uy].typ)) {
514 You_cant("write on the %s!", surface(u.ux, u.uy));
515 return 0;
517 if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) /* in bubble */) {
518 You_cant("write in thin air!");
519 return 0;
520 } else if (!accessible(u.ux, u.uy)) {
521 /* stone, tree, wall, secret corridor, pool, lava, bars */
522 You_cant("write here.");
523 return 0;
525 if (cantwield(youmonst.data)) {
526 You_cant("even hold anything!");
527 return 0;
529 if (check_capacity((char *) 0))
530 return 0;
532 /* One may write with finger, or weapon, or wand, or..., or...
533 * Edited by GAN 10/20/86 so as not to change weapon wielded.
536 otmp = getobj(styluses, "write with");
537 if (!otmp) /* otmp == zeroobj if fingers */
538 return 0;
540 if (otmp == &zeroobj) {
541 Strcat(strcpy(fbuf, "your "), body_part(FINGERTIP));
542 writer = fbuf;
543 } else
544 writer = yname(otmp);
546 /* There's no reason you should be able to write with a wand
547 * while both your hands are tied up.
549 if (!freehand() && otmp != uwep && !otmp->owornmask) {
550 You("have no free %s to write with!", body_part(HAND));
551 return 0;
554 if (jello) {
555 You("tickle %s with %s.", mon_nam(u.ustuck), writer);
556 Your("message dissolves...");
557 return 0;
559 if (otmp->oclass != WAND_CLASS && !can_reach_floor(TRUE)) {
560 cant_reach_floor(u.ux, u.uy, FALSE, TRUE);
561 return 0;
563 if (IS_ALTAR(levl[u.ux][u.uy].typ)) {
564 You("make a motion towards the altar with %s.", writer);
565 altar_wrath(u.ux, u.uy);
566 return 0;
568 if (IS_GRAVE(levl[u.ux][u.uy].typ)) {
569 if (otmp == &zeroobj) { /* using only finger */
570 You("would only make a small smudge on the %s.",
571 surface(u.ux, u.uy));
572 return 0;
573 } else if (!levl[u.ux][u.uy].disturbed) {
574 You("disturb the undead!");
575 levl[u.ux][u.uy].disturbed = 1;
576 (void) makemon(&mons[PM_GHOUL], u.ux, u.uy, NO_MM_FLAGS);
577 exercise(A_WIS, FALSE);
578 return 1;
582 /* SPFX for items */
584 switch (otmp->oclass) {
585 default:
586 case AMULET_CLASS:
587 case CHAIN_CLASS:
588 case POTION_CLASS:
589 case COIN_CLASS:
590 break;
591 case RING_CLASS:
592 /* "diamond" rings and others should work */
593 case GEM_CLASS:
594 /* diamonds & other hard gems should work */
595 if (objects[otmp->otyp].oc_tough) {
596 type = ENGRAVE;
597 break;
599 break;
600 case ARMOR_CLASS:
601 if (is_boots(otmp)) {
602 type = DUST;
603 break;
605 /*FALLTHRU*/
606 /* Objects too large to engrave with */
607 case BALL_CLASS:
608 case ROCK_CLASS:
609 You_cant("engrave with such a large object!");
610 ptext = FALSE;
611 break;
612 /* Objects too silly to engrave with */
613 case FOOD_CLASS:
614 case SCROLL_CLASS:
615 case SPBOOK_CLASS:
616 pline("%s would get %s.", Yname2(otmp),
617 is_ice(u.ux, u.uy) ? "all frosty" : "too dirty");
618 ptext = FALSE;
619 break;
620 case RANDOM_CLASS: /* This should mean fingers */
621 break;
623 /* The charge is removed from the wand before prompting for
624 * the engraving text, because all kinds of setup decisions
625 * and pre-engraving messages are based upon knowing what type
626 * of engraving the wand is going to do. Also, the player
627 * will have potentially seen "You wrest .." message, and
628 * therefore will know they are using a charge.
630 case WAND_CLASS:
631 if (zappable(otmp)) {
632 check_unpaid(otmp);
633 if (otmp->cursed && !rn2(WAND_BACKFIRE_CHANCE)) {
634 wand_explode(otmp, 0);
635 return 1;
637 zapwand = TRUE;
638 if (!can_reach_floor(TRUE))
639 ptext = FALSE;
641 switch (otmp->otyp) {
642 /* DUST wands */
643 default:
644 break;
645 /* NODIR wands */
646 case WAN_LIGHT:
647 case WAN_SECRET_DOOR_DETECTION:
648 case WAN_CREATE_MONSTER:
649 case WAN_WISHING:
650 case WAN_ENLIGHTENMENT:
651 zapnodir(otmp);
652 break;
653 /* IMMEDIATE wands */
654 /* If wand is "IMMEDIATE", remember to affect the
655 * previous engraving even if turning to dust.
657 case WAN_STRIKING:
658 Strcpy(post_engr_text,
659 "The wand unsuccessfully fights your attempt to write!");
660 break;
661 case WAN_SLOW_MONSTER:
662 if (!Blind) {
663 Sprintf(post_engr_text, "The bugs on the %s slow down!",
664 surface(u.ux, u.uy));
666 break;
667 case WAN_SPEED_MONSTER:
668 if (!Blind) {
669 Sprintf(post_engr_text, "The bugs on the %s speed up!",
670 surface(u.ux, u.uy));
672 break;
673 case WAN_POLYMORPH:
674 if (oep) {
675 if (!Blind) {
676 type = (xchar) 0; /* random */
677 (void) random_engraving(buf);
679 dengr = TRUE;
681 break;
682 case WAN_NOTHING:
683 case WAN_UNDEAD_TURNING:
684 case WAN_OPENING:
685 case WAN_LOCKING:
686 case WAN_PROBING:
687 break;
688 /* RAY wands */
689 case WAN_MAGIC_MISSILE:
690 ptext = TRUE;
691 if (!Blind) {
692 Sprintf(post_engr_text,
693 "The %s is riddled by bullet holes!",
694 surface(u.ux, u.uy));
696 break;
697 /* can't tell sleep from death - Eric Backus */
698 case WAN_SLEEP:
699 case WAN_DEATH:
700 if (!Blind) {
701 Sprintf(post_engr_text, "The bugs on the %s stop moving!",
702 surface(u.ux, u.uy));
704 break;
705 case WAN_COLD:
706 if (!Blind)
707 Strcpy(post_engr_text,
708 "A few ice cubes drop from the wand.");
709 if (!oep || (oep->engr_type != BURN))
710 break;
711 case WAN_CANCELLATION:
712 case WAN_MAKE_INVISIBLE:
713 if (oep && oep->engr_type != HEADSTONE) {
714 if (!Blind)
715 pline_The("engraving on the %s vanishes!",
716 surface(u.ux, u.uy));
717 dengr = TRUE;
719 break;
720 case WAN_TELEPORTATION:
721 if (oep && oep->engr_type != HEADSTONE) {
722 if (!Blind)
723 pline_The("engraving on the %s vanishes!",
724 surface(u.ux, u.uy));
725 teleengr = TRUE;
727 break;
728 /* type = ENGRAVE wands */
729 case WAN_DIGGING:
730 ptext = TRUE;
731 type = ENGRAVE;
732 if (!objects[otmp->otyp].oc_name_known) {
733 if (flags.verbose)
734 pline("This %s is a wand of digging!", xname(otmp));
735 doknown = TRUE;
737 Strcpy(post_engr_text,
738 (Blind && !Deaf)
739 ? "You hear drilling!"
740 : Blind
741 ? "You feel tremors."
742 : IS_GRAVE(levl[u.ux][u.uy].typ)
743 ? "Chips fly out from the headstone."
744 : is_ice(u.ux, u.uy)
745 ? "Ice chips fly up from the ice surface!"
746 : (level.locations[u.ux][u.uy].typ
747 == DRAWBRIDGE_DOWN)
748 ? "Splinters fly up from the bridge."
749 : "Gravel flies up from the floor.");
750 break;
751 /* type = BURN wands */
752 case WAN_FIRE:
753 ptext = TRUE;
754 type = BURN;
755 if (!objects[otmp->otyp].oc_name_known) {
756 if (flags.verbose)
757 pline("This %s is a wand of fire!", xname(otmp));
758 doknown = TRUE;
760 Strcpy(post_engr_text, Blind ? "You feel the wand heat up."
761 : "Flames fly from the wand.");
762 break;
763 case WAN_LIGHTNING:
764 ptext = TRUE;
765 type = BURN;
766 if (!objects[otmp->otyp].oc_name_known) {
767 if (flags.verbose)
768 pline("This %s is a wand of lightning!", xname(otmp));
769 doknown = TRUE;
771 if (!Blind) {
772 Strcpy(post_engr_text, "Lightning arcs from the wand.");
773 doblind = TRUE;
774 } else
775 Strcpy(post_engr_text, !Deaf
776 ? "You hear crackling!"
777 : "Your hair stands up!");
778 break;
780 /* type = MARK wands */
781 /* type = ENGR_BLOOD wands */
783 } else { /* end if zappable */
784 /* failing to wrest one last charge takes time */
785 ptext = FALSE; /* use "early exit" below, return 1 */
786 /* give feedback here if we won't be getting the
787 "can't reach floor" message below */
788 if (can_reach_floor(TRUE)) {
789 /* cancelled wand turns to dust */
790 if (otmp->spe < 0)
791 zapwand = TRUE;
792 /* empty wand just doesn't write */
793 else
794 pline_The("wand is too worn out to engrave.");
797 break;
799 case WEAPON_CLASS:
800 if (is_blade(otmp)) {
801 if ((int) otmp->spe > -3)
802 type = ENGRAVE;
803 else
804 pline("%s too dull for engraving.", Yobjnam2(otmp, "are"));
806 break;
808 case TOOL_CLASS:
809 if (otmp == ublindf) {
810 pline(
811 "That is a bit difficult to engrave with, don't you think?");
812 return 0;
814 switch (otmp->otyp) {
815 case MAGIC_MARKER:
816 if (otmp->spe <= 0)
817 Your("marker has dried out.");
818 else
819 type = MARK;
820 break;
821 case TOWEL:
822 /* Can't really engrave with a towel */
823 ptext = FALSE;
824 if (oep)
825 if (oep->engr_type == DUST
826 || oep->engr_type == ENGR_BLOOD
827 || oep->engr_type == MARK) {
828 if (is_wet_towel(otmp))
829 dry_a_towel(otmp, -1, TRUE);
830 if (!Blind)
831 You("wipe out the message here.");
832 else
833 pline("%s %s.", Yobjnam2(otmp, "get"),
834 is_ice(u.ux, u.uy) ? "frosty" : "dusty");
835 dengr = TRUE;
836 } else
837 pline("%s can't wipe out this engraving.", Yname2(otmp));
838 else
839 pline("%s %s.", Yobjnam2(otmp, "get"),
840 is_ice(u.ux, u.uy) ? "frosty" : "dusty");
841 break;
842 default:
843 break;
845 break;
847 case VENOM_CLASS:
848 if (wizard) {
849 pline("Writing a poison pen letter??");
850 break;
852 /*FALLTHRU*/
853 case ILLOBJ_CLASS:
854 impossible("You're engraving with an illegal object!");
855 break;
858 if (IS_GRAVE(levl[u.ux][u.uy].typ)) {
859 if (type == ENGRAVE || type == 0) {
860 type = HEADSTONE;
861 } else {
862 /* ensures the "cannot wipe out" case */
863 type = DUST;
864 dengr = FALSE;
865 teleengr = FALSE;
866 buf[0] = '\0';
871 * End of implement setup
874 /* Identify stylus */
875 if (doknown) {
876 learnwand(otmp);
877 if (objects[otmp->otyp].oc_name_known)
878 more_experienced(0, 10);
880 if (teleengr) {
881 rloc_engr(oep);
882 oep = (struct engr *) 0;
884 if (dengr) {
885 del_engr(oep);
886 oep = (struct engr *) 0;
888 /* Something has changed the engraving here */
889 if (*buf) {
890 make_engr_at(u.ux, u.uy, buf, moves, type);
891 pline_The("engraving now reads: \"%s\".", buf);
892 ptext = FALSE;
894 if (zapwand && (otmp->spe < 0)) {
895 pline("%s %sturns to dust.", The(xname(otmp)),
896 Blind ? "" : "glows violently, then ");
897 if (!IS_GRAVE(levl[u.ux][u.uy].typ))
898 You(
899 "are not going to get anywhere trying to write in the %s with your dust.",
900 is_ice(u.ux, u.uy) ? "frost" : "dust");
901 useup(otmp);
902 otmp = 0; /* wand is now gone */
903 ptext = FALSE;
905 /* Early exit for some implements. */
906 if (!ptext) {
907 if (otmp && otmp->oclass == WAND_CLASS && !can_reach_floor(TRUE))
908 cant_reach_floor(u.ux, u.uy, FALSE, TRUE);
909 return 1;
912 * Special effects should have deleted the current engraving (if
913 * possible) by now.
915 if (oep) {
916 register char c = 'n';
918 /* Give player the choice to add to engraving. */
919 if (type == HEADSTONE) {
920 /* no choice, only append */
921 c = 'y';
922 } else if (type == oep->engr_type
923 && (!Blind || oep->engr_type == BURN
924 || oep->engr_type == ENGRAVE)) {
925 c = yn_function("Do you want to add to the current engraving?",
926 ynqchars, 'y');
927 if (c == 'q') {
928 pline1(Never_mind);
929 return 0;
933 if (c == 'n' || Blind) {
934 if (oep->engr_type == DUST
935 || oep->engr_type == ENGR_BLOOD
936 || oep->engr_type == MARK) {
937 if (!Blind) {
938 You("wipe out the message that was %s here.",
939 (oep->engr_type == DUST)
940 ? "written in the dust"
941 : (oep->engr_type == ENGR_BLOOD)
942 ? "scrawled in blood"
943 : "written");
944 del_engr(oep);
945 oep = (struct engr *) 0;
946 } else
947 /* Don't delete engr until after we *know* we're engraving
949 eow = TRUE;
950 } else if (type == DUST || type == MARK || type == ENGR_BLOOD) {
951 You("cannot wipe out the message that is %s the %s here.",
952 oep->engr_type == BURN
953 ? (is_ice(u.ux, u.uy) ? "melted into" : "burned into")
954 : "engraved in",
955 surface(u.ux, u.uy));
956 return 1;
957 } else if (type != oep->engr_type || c == 'n') {
958 if (!Blind || can_reach_floor(TRUE))
959 You("will overwrite the current message.");
960 eow = TRUE;
965 eloc = surface(u.ux, u.uy);
966 switch (type) {
967 default:
968 everb = (oep && !eow ? "add to the weird writing on"
969 : "write strangely on");
970 break;
971 case DUST:
972 everb = (oep && !eow ? "add to the writing in" : "write in");
973 eloc = is_ice(u.ux, u.uy) ? "frost" : "dust";
974 break;
975 case HEADSTONE:
976 everb = (oep && !eow ? "add to the epitaph on" : "engrave on");
977 break;
978 case ENGRAVE:
979 everb = (oep && !eow ? "add to the engraving in" : "engrave in");
980 break;
981 case BURN:
982 everb = (oep && !eow
983 ? (is_ice(u.ux, u.uy) ? "add to the text melted into"
984 : "add to the text burned into")
985 : (is_ice(u.ux, u.uy) ? "melt into" : "burn into"));
986 break;
987 case MARK:
988 everb = (oep && !eow ? "add to the graffiti on" : "scribble on");
989 break;
990 case ENGR_BLOOD:
991 everb = (oep && !eow ? "add to the scrawl on" : "scrawl on");
992 break;
995 /* Tell adventurer what is going on */
996 if (otmp != &zeroobj)
997 You("%s the %s with %s.", everb, eloc, doname(otmp));
998 else
999 You("%s the %s with your %s.", everb, eloc, body_part(FINGERTIP));
1001 /* Prompt for engraving! */
1002 Sprintf(qbuf, "What do you want to %s the %s here?", everb, eloc);
1003 getlin(qbuf, ebuf);
1004 /* convert tabs to spaces and condense consecutive spaces to one */
1005 mungspaces(ebuf);
1007 /* Count the actual # of chars engraved not including spaces */
1008 len = strlen(ebuf);
1009 for (sp = ebuf; *sp; sp++)
1010 if (*sp == ' ')
1011 len -= 1;
1013 if (len == 0 || index(ebuf, '\033')) {
1014 if (zapwand) {
1015 if (!Blind)
1016 pline("%s, then %s.", Tobjnam(otmp, "glow"),
1017 otense(otmp, "fade"));
1018 return 1;
1019 } else {
1020 pline1(Never_mind);
1021 return 0;
1025 /* A single `x' is the traditional signature of an illiterate person */
1026 if (len != 1 || (!index(ebuf, 'x') && !index(ebuf, 'X')))
1027 u.uconduct.literate++;
1029 /* Mix up engraving if surface or state of mind is unsound.
1030 Note: this won't add or remove any spaces. */
1031 for (sp = ebuf; *sp; sp++) {
1032 if (*sp == ' ')
1033 continue;
1034 if (((type == DUST || type == ENGR_BLOOD) && !rn2(25))
1035 || (Blind && !rn2(11)) || (Confusion && !rn2(7))
1036 || (Stunned && !rn2(4)) || (Hallucination && !rn2(2)))
1037 *sp = ' ' + rnd(96 - 2); /* ASCII '!' thru '~'
1038 (excludes ' ' and DEL) */
1041 /* Previous engraving is overwritten */
1042 if (eow) {
1043 del_engr(oep);
1044 oep = (struct engr *) 0;
1047 /* Figure out how long it took to engrave, and if player has
1048 * engraved too much.
1050 switch (type) {
1051 default:
1052 multi = -(len / 10);
1053 if (multi)
1054 nomovemsg = "You finish your weird engraving.";
1055 break;
1056 case DUST:
1057 multi = -(len / 10);
1058 if (multi)
1059 nomovemsg = "You finish writing in the dust.";
1060 break;
1061 case HEADSTONE:
1062 case ENGRAVE:
1063 multi = -(len / 10);
1064 if (otmp->oclass == WEAPON_CLASS
1065 && (otmp->otyp != ATHAME || otmp->cursed)) {
1066 multi = -len;
1067 maxelen = ((otmp->spe + 3) * 2) + 1;
1068 /* -2 => 3, -1 => 5, 0 => 7, +1 => 9, +2 => 11
1069 * Note: this does not allow a +0 anything (except an athame)
1070 * to engrave "Elbereth" all at once.
1071 * However, you can engrave "Elb", then "ere", then "th".
1073 pline("%s dull.", Yobjnam2(otmp, "get"));
1074 costly_alteration(otmp, COST_DEGRD);
1075 if (len > maxelen) {
1076 multi = -maxelen;
1077 otmp->spe = -3;
1078 } else if (len > 1)
1079 otmp->spe -= len >> 1;
1080 else
1081 otmp->spe -= 1; /* Prevent infinite engraving */
1082 } else if (otmp->oclass == RING_CLASS || otmp->oclass == GEM_CLASS) {
1083 multi = -len;
1085 if (multi)
1086 nomovemsg = "You finish engraving.";
1087 break;
1088 case BURN:
1089 multi = -(len / 10);
1090 if (multi)
1091 nomovemsg = is_ice(u.ux, u.uy)
1092 ? "You finish melting your message into the ice."
1093 : "You finish burning your message into the floor.";
1094 break;
1095 case MARK:
1096 multi = -(len / 10);
1097 if (otmp->otyp == MAGIC_MARKER) {
1098 maxelen = otmp->spe * 2; /* one charge / 2 letters */
1099 if (len > maxelen) {
1100 Your("marker dries out.");
1101 otmp->spe = 0;
1102 multi = -(maxelen / 10);
1103 } else if (len > 1)
1104 otmp->spe -= len >> 1;
1105 else
1106 otmp->spe -= 1; /* Prevent infinite graffiti */
1108 if (multi)
1109 nomovemsg = "You finish defacing the dungeon.";
1110 break;
1111 case ENGR_BLOOD:
1112 multi = -(len / 10);
1113 if (multi)
1114 nomovemsg = "You finish scrawling.";
1115 break;
1118 /* Chop engraving down to size if necessary */
1119 if (len > maxelen) {
1120 for (sp = ebuf; maxelen && *sp; sp++)
1121 if (*sp == ' ')
1122 maxelen--;
1123 if (!maxelen && *sp) {
1124 *sp = '\0';
1125 if (multi)
1126 nomovemsg = "You cannot write any more.";
1127 You("are only able to write \"%s\".", ebuf);
1131 if (oep) /* add to existing engraving */
1132 Strcpy(buf, oep->engr_txt);
1133 (void) strncat(buf, ebuf, BUFSZ - (int) strlen(buf) - 1);
1134 /* Put the engraving onto the map */
1135 make_engr_at(u.ux, u.uy, buf, moves - multi, type);
1137 if (post_engr_text[0])
1138 pline("%s", post_engr_text);
1139 if (doblind && !resists_blnd(&youmonst)) {
1140 You("are blinded by the flash!");
1141 make_blinded((long) rnd(50), FALSE);
1142 if (!Blind)
1143 Your1(vision_clears);
1145 return 1;
1148 /* while loading bones, clean up text which might accidentally
1149 or maliciously disrupt player's terminal when displayed */
1150 void
1151 sanitize_engravings()
1153 struct engr *ep;
1155 for (ep = head_engr; ep; ep = ep->nxt_engr) {
1156 sanitize_name(ep->engr_txt);
1160 void
1161 save_engravings(fd, mode)
1162 int fd, mode;
1164 struct engr *ep, *ep2;
1165 unsigned no_more_engr = 0;
1167 for (ep = head_engr; ep; ep = ep2) {
1168 ep2 = ep->nxt_engr;
1169 if (ep->engr_lth && ep->engr_txt[0] && perform_bwrite(mode)) {
1170 bwrite(fd, (genericptr_t) &ep->engr_lth, sizeof ep->engr_lth);
1171 bwrite(fd, (genericptr_t) ep, sizeof (struct engr) + ep->engr_lth);
1173 if (release_data(mode))
1174 dealloc_engr(ep);
1176 if (perform_bwrite(mode))
1177 bwrite(fd, (genericptr_t) &no_more_engr, sizeof no_more_engr);
1178 if (release_data(mode))
1179 head_engr = 0;
1182 void
1183 rest_engravings(fd)
1184 int fd;
1186 struct engr *ep;
1187 unsigned lth;
1189 head_engr = 0;
1190 while (1) {
1191 mread(fd, (genericptr_t) &lth, sizeof lth);
1192 if (lth == 0)
1193 return;
1194 ep = newengr(lth);
1195 mread(fd, (genericptr_t) ep, sizeof (struct engr) + lth);
1196 ep->nxt_engr = head_engr;
1197 head_engr = ep;
1198 ep->engr_txt = (char *) (ep + 1); /* Andreas Bormann */
1199 /* Mark as finished for bones levels -- no problem for
1200 * normal levels as the player must have finished engraving
1201 * to be able to move again.
1203 ep->engr_time = moves;
1207 /* to support '#stats' wizard-mode command */
1208 void
1209 engr_stats(hdrfmt, hdrbuf, count, size)
1210 const char *hdrfmt;
1211 char *hdrbuf;
1212 long *count, *size;
1214 struct engr *ep;
1216 Sprintf(hdrbuf, hdrfmt, (long) sizeof (struct engr));
1217 *count = *size = 0L;
1218 for (ep = head_engr; ep; ep = ep->nxt_engr) {
1219 ++*count;
1220 *size += (long) sizeof *ep + (long) ep->engr_lth;
1224 void
1225 del_engr(ep)
1226 register struct engr *ep;
1228 if (ep == head_engr) {
1229 head_engr = ep->nxt_engr;
1230 } else {
1231 register struct engr *ept;
1233 for (ept = head_engr; ept; ept = ept->nxt_engr)
1234 if (ept->nxt_engr == ep) {
1235 ept->nxt_engr = ep->nxt_engr;
1236 break;
1238 if (!ept) {
1239 impossible("Error in del_engr?");
1240 return;
1243 dealloc_engr(ep);
1246 /* randomly relocate an engraving */
1247 void
1248 rloc_engr(ep)
1249 struct engr *ep;
1251 int tx, ty, tryct = 200;
1253 do {
1254 if (--tryct < 0)
1255 return;
1256 tx = rn1(COLNO - 3, 2);
1257 ty = rn2(ROWNO);
1258 } while (engr_at(tx, ty) || !goodpos(tx, ty, (struct monst *) 0, 0));
1260 ep->engr_x = tx;
1261 ep->engr_y = ty;
1264 /* Create a headstone at the given location.
1265 * The caller is responsible for newsym(x, y).
1267 void
1268 make_grave(x, y, str)
1269 int x, y;
1270 const char *str;
1272 char buf[BUFSZ];
1274 /* Can we put a grave here? */
1275 if ((levl[x][y].typ != ROOM && levl[x][y].typ != GRAVE) || t_at(x, y))
1276 return;
1277 /* Make the grave */
1278 levl[x][y].typ = GRAVE;
1279 /* Engrave the headstone */
1280 del_engr_at(x, y);
1281 if (!str)
1282 str = get_rnd_text(EPITAPHFILE, buf);
1283 make_engr_at(x, y, str, 0L, HEADSTONE);
1284 return;
1287 /*engrave.c*/