Move getpos return values to header
[aNetHack.git] / src / engrave.c
blob0c2670af2095f04e56665f61b4fbf51fc0c7235a
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 first word in the engraving.
261 * ("Elbereth burrito" matches; "o Elbereth" does not.)
264 sengr_at(s, x, y, strict)
265 const char *s;
266 xchar x, y;
267 boolean strict;
269 register struct engr *ep = engr_at(x, y);
271 if (ep && ep->engr_type != HEADSTONE && ep->engr_time <= moves) {
272 return strict ? (strncmpi(ep->engr_txt, s, strlen(s)) == 0)
273 : (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;
389 if ((ep = engr_at(x, y)) != 0)
390 del_engr(ep);
391 ep = newengr(strlen(s) + 1);
392 (void) memset((genericptr_t)ep, 0, sizeof(struct engr));
393 ep->nxt_engr = head_engr;
394 head_engr = ep;
395 ep->engr_x = x;
396 ep->engr_y = y;
397 ep->engr_txt = (char *) (ep + 1);
398 Strcpy(ep->engr_txt, s);
399 /* engraving Elbereth shows wisdom */
400 if (!in_mklev && !strcmp(s, "Elbereth"))
401 exercise(A_WIS, TRUE);
402 ep->engr_time = e_time;
403 ep->engr_type = e_type > 0 ? e_type : rnd(N_ENGRAVE - 1);
404 ep->engr_lth = strlen(s) + 1;
407 /* delete any engraving at location <x,y> */
408 void
409 del_engr_at(x, y)
410 int x, y;
412 register struct engr *ep = engr_at(x, y);
414 if (ep)
415 del_engr(ep);
419 * freehand - returns true if player has a free hand
422 freehand()
424 return (!uwep || !welded(uwep)
425 || (!bimanual(uwep) && (!uarms || !uarms->cursed)));
428 static NEARDATA const char styluses[] = { ALL_CLASSES, ALLOW_NONE,
429 TOOL_CLASS, WEAPON_CLASS,
430 WAND_CLASS, GEM_CLASS,
431 RING_CLASS, 0 };
433 /* Mohs' Hardness Scale:
434 * 1 - Talc 6 - Orthoclase
435 * 2 - Gypsum 7 - Quartz
436 * 3 - Calcite 8 - Topaz
437 * 4 - Fluorite 9 - Corundum
438 * 5 - Apatite 10 - Diamond
440 * Since granite is an igneous rock hardness ~ 7, anything >= 8 should
441 * probably be able to scratch the rock.
442 * Devaluation of less hard gems is not easily possible because obj struct
443 * does not contain individual oc_cost currently. 7/91
445 * steel - 5-8.5 (usu. weapon)
446 * diamond - 10 * jade - 5-6 (nephrite)
447 * ruby - 9 (corundum) * turquoise - 5-6
448 * sapphire - 9 (corundum) * opal - 5-6
449 * topaz - 8 * glass - ~5.5
450 * emerald - 7.5-8 (beryl) * dilithium - 4-5??
451 * aquamarine - 7.5-8 (beryl) * iron - 4-5
452 * garnet - 7.25 (var. 6.5-8) * fluorite - 4
453 * agate - 7 (quartz) * brass - 3-4
454 * amethyst - 7 (quartz) * gold - 2.5-3
455 * jasper - 7 (quartz) * silver - 2.5-3
456 * onyx - 7 (quartz) * copper - 2.5-3
457 * moonstone - 6 (orthoclase) * amber - 2-2.5
460 /* return 1 if action took 1 (or more) moves, 0 if error or aborted */
462 doengrave()
464 boolean dengr = FALSE; /* TRUE if we wipe out the current engraving */
465 boolean doblind = FALSE; /* TRUE if engraving blinds the player */
466 boolean doknown = FALSE; /* TRUE if we identify the stylus */
467 boolean eow = FALSE; /* TRUE if we are overwriting oep */
468 boolean jello = FALSE; /* TRUE if we are engraving in slime */
469 boolean ptext = TRUE; /* TRUE if we must prompt for engrave text */
470 boolean teleengr = FALSE; /* TRUE if we move the old engraving */
471 boolean zapwand = FALSE; /* TRUE if we remove a wand charge */
472 xchar type = DUST; /* Type of engraving made */
473 char buf[BUFSZ]; /* Buffer for final/poly engraving text */
474 char ebuf[BUFSZ]; /* Buffer for initial engraving text */
475 char fbuf[BUFSZ]; /* Buffer for "your fingers" */
476 char qbuf[QBUFSZ]; /* Buffer for query text */
477 char post_engr_text[BUFSZ]; /* Text displayed after engraving prompt */
478 const char *everb; /* Present tense of engraving type */
479 const char *eloc; /* Where the engraving is (ie dust/floor/...) */
480 char *sp; /* Place holder for space count of engr text */
481 int len; /* # of nonspace chars of new engraving text */
482 int maxelen; /* Max allowable length of engraving text */
483 struct engr *oep = engr_at(u.ux, u.uy);
484 /* The current engraving */
485 struct obj *otmp; /* Object selected with which to engrave */
486 char *writer;
488 multi = 0; /* moves consumed */
489 nomovemsg = (char *) 0; /* occupation end message */
491 buf[0] = (char) 0;
492 ebuf[0] = (char) 0;
493 post_engr_text[0] = (char) 0;
494 maxelen = BUFSZ - 1;
495 if (is_demon(youmonst.data) || youmonst.data->mlet == S_VAMPIRE)
496 type = ENGR_BLOOD;
498 /* Can the adventurer engrave at all? */
500 if (u.uswallow) {
501 if (is_animal(u.ustuck->data)) {
502 pline("What would you write? \"Jonah was here\"?");
503 return 0;
504 } else if (is_whirly(u.ustuck->data)) {
505 cant_reach_floor(u.ux, u.uy, FALSE, FALSE);
506 return 0;
507 } else
508 jello = TRUE;
509 } else if (is_lava(u.ux, u.uy)) {
510 You_cant("write on the %s!", surface(u.ux, u.uy));
511 return 0;
512 } else if (is_pool(u.ux, u.uy) || IS_FOUNTAIN(levl[u.ux][u.uy].typ)) {
513 You_cant("write on the %s!", surface(u.ux, u.uy));
514 return 0;
516 if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) /* in bubble */) {
517 You_cant("write in thin air!");
518 return 0;
519 } else if (!accessible(u.ux, u.uy)) {
520 /* stone, tree, wall, secret corridor, pool, lava, bars */
521 You_cant("write here.");
522 return 0;
524 if (cantwield(youmonst.data)) {
525 You_cant("even hold anything!");
526 return 0;
528 if (check_capacity((char *) 0))
529 return 0;
531 /* One may write with finger, or weapon, or wand, or..., or...
532 * Edited by GAN 10/20/86 so as not to change weapon wielded.
535 otmp = getobj(styluses, "write with");
536 if (!otmp) /* otmp == zeroobj if fingers */
537 return 0;
539 if (otmp == &zeroobj) {
540 Strcat(strcpy(fbuf, "your "), body_part(FINGERTIP));
541 writer = fbuf;
542 } else
543 writer = yname(otmp);
545 /* There's no reason you should be able to write with a wand
546 * while both your hands are tied up.
548 if (!freehand() && otmp != uwep && !otmp->owornmask) {
549 You("have no free %s to write with!", body_part(HAND));
550 return 0;
553 if (jello) {
554 You("tickle %s with %s.", mon_nam(u.ustuck), writer);
555 Your("message dissolves...");
556 return 0;
558 if (otmp->oclass != WAND_CLASS && !can_reach_floor(TRUE)) {
559 cant_reach_floor(u.ux, u.uy, FALSE, TRUE);
560 return 0;
562 if (IS_ALTAR(levl[u.ux][u.uy].typ)) {
563 You("make a motion towards the altar with %s.", writer);
564 altar_wrath(u.ux, u.uy);
565 return 0;
567 if (IS_GRAVE(levl[u.ux][u.uy].typ)) {
568 if (otmp == &zeroobj) { /* using only finger */
569 You("would only make a small smudge on the %s.",
570 surface(u.ux, u.uy));
571 return 0;
572 } else if (!levl[u.ux][u.uy].disturbed) {
573 You("disturb the undead!");
574 levl[u.ux][u.uy].disturbed = 1;
575 (void) makemon(&mons[PM_GHOUL], u.ux, u.uy, NO_MM_FLAGS);
576 exercise(A_WIS, FALSE);
577 return 1;
581 /* SPFX for items */
583 switch (otmp->oclass) {
584 default:
585 case AMULET_CLASS:
586 case CHAIN_CLASS:
587 case POTION_CLASS:
588 case COIN_CLASS:
589 break;
590 case RING_CLASS:
591 /* "diamond" rings and others should work */
592 case GEM_CLASS:
593 /* diamonds & other hard gems should work */
594 if (objects[otmp->otyp].oc_tough) {
595 type = ENGRAVE;
596 break;
598 break;
599 case ARMOR_CLASS:
600 if (is_boots(otmp)) {
601 type = DUST;
602 break;
604 /*FALLTHRU*/
605 /* Objects too large to engrave with */
606 case BALL_CLASS:
607 case ROCK_CLASS:
608 You_cant("engrave with such a large object!");
609 ptext = FALSE;
610 break;
611 /* Objects too silly to engrave with */
612 case FOOD_CLASS:
613 case SCROLL_CLASS:
614 case SPBOOK_CLASS:
615 pline("%s would get %s.", Yname2(otmp),
616 is_ice(u.ux, u.uy) ? "all frosty" : "too dirty");
617 ptext = FALSE;
618 break;
619 case RANDOM_CLASS: /* This should mean fingers */
620 break;
622 /* The charge is removed from the wand before prompting for
623 * the engraving text, because all kinds of setup decisions
624 * and pre-engraving messages are based upon knowing what type
625 * of engraving the wand is going to do. Also, the player
626 * will have potentially seen "You wrest .." message, and
627 * therefore will know they are using a charge.
629 case WAND_CLASS:
630 if (zappable(otmp)) {
631 check_unpaid(otmp);
632 if (otmp->cursed && !rn2(WAND_BACKFIRE_CHANCE)) {
633 wand_explode(otmp, 0);
634 return 1;
636 zapwand = TRUE;
637 if (!can_reach_floor(TRUE))
638 ptext = FALSE;
640 switch (otmp->otyp) {
641 /* DUST wands */
642 default:
643 break;
644 /* NODIR wands */
645 case WAN_LIGHT:
646 case WAN_SECRET_DOOR_DETECTION:
647 case WAN_CREATE_MONSTER:
648 case WAN_WISHING:
649 case WAN_ENLIGHTENMENT:
650 zapnodir(otmp);
651 break;
652 /* IMMEDIATE wands */
653 /* If wand is "IMMEDIATE", remember to affect the
654 * previous engraving even if turning to dust.
656 case WAN_STRIKING:
657 Strcpy(post_engr_text,
658 "The wand unsuccessfully fights your attempt to write!");
659 break;
660 case WAN_SLOW_MONSTER:
661 if (!Blind) {
662 Sprintf(post_engr_text, "The bugs on the %s slow down!",
663 surface(u.ux, u.uy));
665 break;
666 case WAN_SPEED_MONSTER:
667 if (!Blind) {
668 Sprintf(post_engr_text, "The bugs on the %s speed up!",
669 surface(u.ux, u.uy));
671 break;
672 case WAN_POLYMORPH:
673 if (oep) {
674 if (!Blind) {
675 type = (xchar) 0; /* random */
676 (void) random_engraving(buf);
678 dengr = TRUE;
680 break;
681 case WAN_NOTHING:
682 case WAN_UNDEAD_TURNING:
683 case WAN_OPENING:
684 case WAN_LOCKING:
685 case WAN_PROBING:
686 break;
687 /* RAY wands */
688 case WAN_MAGIC_MISSILE:
689 ptext = TRUE;
690 if (!Blind) {
691 Sprintf(post_engr_text,
692 "The %s is riddled by bullet holes!",
693 surface(u.ux, u.uy));
695 break;
696 /* can't tell sleep from death - Eric Backus */
697 case WAN_SLEEP:
698 case WAN_DEATH:
699 if (!Blind) {
700 Sprintf(post_engr_text, "The bugs on the %s stop moving!",
701 surface(u.ux, u.uy));
703 break;
704 case WAN_COLD:
705 if (!Blind)
706 Strcpy(post_engr_text,
707 "A few ice cubes drop from the wand.");
708 if (!oep || (oep->engr_type != BURN))
709 break;
710 case WAN_CANCELLATION:
711 case WAN_MAKE_INVISIBLE:
712 if (oep && oep->engr_type != HEADSTONE) {
713 if (!Blind)
714 pline_The("engraving on the %s vanishes!",
715 surface(u.ux, u.uy));
716 dengr = TRUE;
718 break;
719 case WAN_TELEPORTATION:
720 if (oep && oep->engr_type != HEADSTONE) {
721 if (!Blind)
722 pline_The("engraving on the %s vanishes!",
723 surface(u.ux, u.uy));
724 teleengr = TRUE;
726 break;
727 /* type = ENGRAVE wands */
728 case WAN_DIGGING:
729 ptext = TRUE;
730 type = ENGRAVE;
731 if (!objects[otmp->otyp].oc_name_known) {
732 if (flags.verbose)
733 pline("This %s is a wand of digging!", xname(otmp));
734 doknown = TRUE;
736 Strcpy(post_engr_text,
737 (Blind && !Deaf)
738 ? "You hear drilling!"
739 : Blind
740 ? "You feel tremors."
741 : IS_GRAVE(levl[u.ux][u.uy].typ)
742 ? "Chips fly out from the headstone."
743 : is_ice(u.ux, u.uy)
744 ? "Ice chips fly up from the ice surface!"
745 : (level.locations[u.ux][u.uy].typ
746 == DRAWBRIDGE_DOWN)
747 ? "Splinters fly up from the bridge."
748 : "Gravel flies up from the floor.");
749 break;
750 /* type = BURN wands */
751 case WAN_FIRE:
752 ptext = TRUE;
753 type = BURN;
754 if (!objects[otmp->otyp].oc_name_known) {
755 if (flags.verbose)
756 pline("This %s is a wand of fire!", xname(otmp));
757 doknown = TRUE;
759 Strcpy(post_engr_text, Blind ? "You feel the wand heat up."
760 : "Flames fly from the wand.");
761 break;
762 case WAN_LIGHTNING:
763 ptext = TRUE;
764 type = BURN;
765 if (!objects[otmp->otyp].oc_name_known) {
766 if (flags.verbose)
767 pline("This %s is a wand of lightning!", xname(otmp));
768 doknown = TRUE;
770 if (!Blind) {
771 Strcpy(post_engr_text, "Lightning arcs from the wand.");
772 doblind = TRUE;
773 } else
774 Strcpy(post_engr_text, !Deaf
775 ? "You hear crackling!"
776 : "Your hair stands up!");
777 break;
779 /* type = MARK wands */
780 /* type = ENGR_BLOOD wands */
782 } else { /* end if zappable */
783 /* failing to wrest one last charge takes time */
784 ptext = FALSE; /* use "early exit" below, return 1 */
785 /* give feedback here if we won't be getting the
786 "can't reach floor" message below */
787 if (can_reach_floor(TRUE)) {
788 /* cancelled wand turns to dust */
789 if (otmp->spe < 0)
790 zapwand = TRUE;
791 /* empty wand just doesn't write */
792 else
793 pline_The("wand is too worn out to engrave.");
796 break;
798 case WEAPON_CLASS:
799 if (is_blade(otmp)) {
800 if ((int) otmp->spe > -3)
801 type = ENGRAVE;
802 else
803 pline("%s too dull for engraving.", Yobjnam2(otmp, "are"));
805 break;
807 case TOOL_CLASS:
808 if (otmp == ublindf) {
809 pline(
810 "That is a bit difficult to engrave with, don't you think?");
811 return 0;
813 switch (otmp->otyp) {
814 case MAGIC_MARKER:
815 if (otmp->spe <= 0)
816 Your("marker has dried out.");
817 else
818 type = MARK;
819 break;
820 case TOWEL:
821 /* Can't really engrave with a towel */
822 ptext = FALSE;
823 if (oep)
824 if (oep->engr_type == DUST
825 || oep->engr_type == ENGR_BLOOD
826 || oep->engr_type == MARK) {
827 if (is_wet_towel(otmp))
828 dry_a_towel(otmp, -1, TRUE);
829 if (!Blind)
830 You("wipe out the message here.");
831 else
832 pline("%s %s.", Yobjnam2(otmp, "get"),
833 is_ice(u.ux, u.uy) ? "frosty" : "dusty");
834 dengr = TRUE;
835 } else
836 pline("%s can't wipe out this engraving.", Yname2(otmp));
837 else
838 pline("%s %s.", Yobjnam2(otmp, "get"),
839 is_ice(u.ux, u.uy) ? "frosty" : "dusty");
840 break;
841 default:
842 break;
844 break;
846 case VENOM_CLASS:
847 if (wizard) {
848 pline("Writing a poison pen letter??");
849 break;
851 /*FALLTHRU*/
852 case ILLOBJ_CLASS:
853 impossible("You're engraving with an illegal object!");
854 break;
857 if (IS_GRAVE(levl[u.ux][u.uy].typ)) {
858 if (type == ENGRAVE || type == 0) {
859 type = HEADSTONE;
860 } else {
861 /* ensures the "cannot wipe out" case */
862 type = DUST;
863 dengr = FALSE;
864 teleengr = FALSE;
865 buf[0] = '\0';
870 * End of implement setup
873 /* Identify stylus */
874 if (doknown) {
875 learnwand(otmp);
876 if (objects[otmp->otyp].oc_name_known)
877 more_experienced(0, 10);
879 if (teleengr) {
880 rloc_engr(oep);
881 oep = (struct engr *) 0;
883 if (dengr) {
884 del_engr(oep);
885 oep = (struct engr *) 0;
887 /* Something has changed the engraving here */
888 if (*buf) {
889 make_engr_at(u.ux, u.uy, buf, moves, type);
890 pline_The("engraving now reads: \"%s\".", buf);
891 ptext = FALSE;
893 if (zapwand && (otmp->spe < 0)) {
894 pline("%s %sturns to dust.", The(xname(otmp)),
895 Blind ? "" : "glows violently, then ");
896 if (!IS_GRAVE(levl[u.ux][u.uy].typ))
897 You(
898 "are not going to get anywhere trying to write in the %s with your dust.",
899 is_ice(u.ux, u.uy) ? "frost" : "dust");
900 useup(otmp);
901 otmp = 0; /* wand is now gone */
902 ptext = FALSE;
904 /* Early exit for some implements. */
905 if (!ptext) {
906 if (otmp && otmp->oclass == WAND_CLASS && !can_reach_floor(TRUE))
907 cant_reach_floor(u.ux, u.uy, FALSE, TRUE);
908 return 1;
911 * Special effects should have deleted the current engraving (if
912 * possible) by now.
914 if (oep) {
915 register char c = 'n';
917 /* Give player the choice to add to engraving. */
918 if (type == HEADSTONE) {
919 /* no choice, only append */
920 c = 'y';
921 } else if (type == oep->engr_type
922 && (!Blind || oep->engr_type == BURN
923 || oep->engr_type == ENGRAVE)) {
924 c = yn_function("Do you want to add to the current engraving?",
925 ynqchars, 'y');
926 if (c == 'q') {
927 pline1(Never_mind);
928 return 0;
932 if (c == 'n' || Blind) {
933 if (oep->engr_type == DUST
934 || oep->engr_type == ENGR_BLOOD
935 || oep->engr_type == MARK) {
936 if (!Blind) {
937 You("wipe out the message that was %s here.",
938 (oep->engr_type == DUST)
939 ? "written in the dust"
940 : (oep->engr_type == ENGR_BLOOD)
941 ? "scrawled in blood"
942 : "written");
943 del_engr(oep);
944 oep = (struct engr *) 0;
945 } else
946 /* Don't delete engr until after we *know* we're engraving
948 eow = TRUE;
949 } else if (type == DUST || type == MARK || type == ENGR_BLOOD) {
950 You("cannot wipe out the message that is %s the %s here.",
951 oep->engr_type == BURN
952 ? (is_ice(u.ux, u.uy) ? "melted into" : "burned into")
953 : "engraved in",
954 surface(u.ux, u.uy));
955 return 1;
956 } else if (type != oep->engr_type || c == 'n') {
957 if (!Blind || can_reach_floor(TRUE))
958 You("will overwrite the current message.");
959 eow = TRUE;
964 eloc = surface(u.ux, u.uy);
965 switch (type) {
966 default:
967 everb = (oep && !eow ? "add to the weird writing on"
968 : "write strangely on");
969 break;
970 case DUST:
971 everb = (oep && !eow ? "add to the writing in" : "write in");
972 eloc = is_ice(u.ux, u.uy) ? "frost" : "dust";
973 break;
974 case HEADSTONE:
975 everb = (oep && !eow ? "add to the epitaph on" : "engrave on");
976 break;
977 case ENGRAVE:
978 everb = (oep && !eow ? "add to the engraving in" : "engrave in");
979 break;
980 case BURN:
981 everb = (oep && !eow
982 ? (is_ice(u.ux, u.uy) ? "add to the text melted into"
983 : "add to the text burned into")
984 : (is_ice(u.ux, u.uy) ? "melt into" : "burn into"));
985 break;
986 case MARK:
987 everb = (oep && !eow ? "add to the graffiti on" : "scribble on");
988 break;
989 case ENGR_BLOOD:
990 everb = (oep && !eow ? "add to the scrawl on" : "scrawl on");
991 break;
994 /* Tell adventurer what is going on */
995 if (otmp != &zeroobj)
996 You("%s the %s with %s.", everb, eloc, doname(otmp));
997 else
998 You("%s the %s with your %s.", everb, eloc, body_part(FINGERTIP));
1000 /* Prompt for engraving! */
1001 Sprintf(qbuf, "What do you want to %s the %s here?", everb, eloc);
1002 getlin(qbuf, ebuf);
1003 /* convert tabs to spaces and condense consecutive spaces to one */
1004 mungspaces(ebuf);
1006 /* Count the actual # of chars engraved not including spaces */
1007 len = strlen(ebuf);
1008 for (sp = ebuf; *sp; sp++)
1009 if (*sp == ' ')
1010 len -= 1;
1012 if (len == 0 || index(ebuf, '\033')) {
1013 if (zapwand) {
1014 if (!Blind)
1015 pline("%s, then %s.", Tobjnam(otmp, "glow"),
1016 otense(otmp, "fade"));
1017 return 1;
1018 } else {
1019 pline1(Never_mind);
1020 return 0;
1024 /* A single `x' is the traditional signature of an illiterate person */
1025 if (len != 1 || (!index(ebuf, 'x') && !index(ebuf, 'X')))
1026 u.uconduct.literate++;
1028 /* Mix up engraving if surface or state of mind is unsound.
1029 Note: this won't add or remove any spaces. */
1030 for (sp = ebuf; *sp; sp++) {
1031 if (*sp == ' ')
1032 continue;
1033 if (((type == DUST || type == ENGR_BLOOD) && !rn2(25))
1034 || (Blind && !rn2(11)) || (Confusion && !rn2(7))
1035 || (Stunned && !rn2(4)) || (Hallucination && !rn2(2)))
1036 *sp = ' ' + rnd(96 - 2); /* ASCII '!' thru '~'
1037 (excludes ' ' and DEL) */
1040 /* Previous engraving is overwritten */
1041 if (eow) {
1042 del_engr(oep);
1043 oep = (struct engr *) 0;
1046 /* Figure out how long it took to engrave, and if player has
1047 * engraved too much.
1049 switch (type) {
1050 default:
1051 multi = -(len / 10);
1052 if (multi)
1053 nomovemsg = "You finish your weird engraving.";
1054 break;
1055 case DUST:
1056 multi = -(len / 10);
1057 if (multi)
1058 nomovemsg = "You finish writing in the dust.";
1059 break;
1060 case HEADSTONE:
1061 case ENGRAVE:
1062 multi = -(len / 10);
1063 if (otmp->oclass == WEAPON_CLASS
1064 && (otmp->otyp != ATHAME || otmp->cursed)) {
1065 multi = -len;
1066 maxelen = ((otmp->spe + 3) * 2) + 1;
1067 /* -2 => 3, -1 => 5, 0 => 7, +1 => 9, +2 => 11
1068 * Note: this does not allow a +0 anything (except an athame)
1069 * to engrave "Elbereth" all at once.
1070 * However, you can engrave "Elb", then "ere", then "th".
1072 pline("%s dull.", Yobjnam2(otmp, "get"));
1073 costly_alteration(otmp, COST_DEGRD);
1074 if (len > maxelen) {
1075 multi = -maxelen;
1076 otmp->spe = -3;
1077 } else if (len > 1)
1078 otmp->spe -= len >> 1;
1079 else
1080 otmp->spe -= 1; /* Prevent infinite engraving */
1081 } else if (otmp->oclass == RING_CLASS || otmp->oclass == GEM_CLASS) {
1082 multi = -len;
1084 if (multi)
1085 nomovemsg = "You finish engraving.";
1086 break;
1087 case BURN:
1088 multi = -(len / 10);
1089 if (multi)
1090 nomovemsg = is_ice(u.ux, u.uy)
1091 ? "You finish melting your message into the ice."
1092 : "You finish burning your message into the floor.";
1093 break;
1094 case MARK:
1095 multi = -(len / 10);
1096 if (otmp->otyp == MAGIC_MARKER) {
1097 maxelen = otmp->spe * 2; /* one charge / 2 letters */
1098 if (len > maxelen) {
1099 Your("marker dries out.");
1100 otmp->spe = 0;
1101 multi = -(maxelen / 10);
1102 } else if (len > 1)
1103 otmp->spe -= len >> 1;
1104 else
1105 otmp->spe -= 1; /* Prevent infinite graffiti */
1107 if (multi)
1108 nomovemsg = "You finish defacing the dungeon.";
1109 break;
1110 case ENGR_BLOOD:
1111 multi = -(len / 10);
1112 if (multi)
1113 nomovemsg = "You finish scrawling.";
1114 break;
1117 /* Chop engraving down to size if necessary */
1118 if (len > maxelen) {
1119 for (sp = ebuf; maxelen && *sp; sp++)
1120 if (*sp == ' ')
1121 maxelen--;
1122 if (!maxelen && *sp) {
1123 *sp = '\0';
1124 if (multi)
1125 nomovemsg = "You cannot write any more.";
1126 You("are only able to write \"%s\".", ebuf);
1130 if (oep) /* add to existing engraving */
1131 Strcpy(buf, oep->engr_txt);
1132 (void) strncat(buf, ebuf, BUFSZ - (int) strlen(buf) - 1);
1133 /* Put the engraving onto the map */
1134 make_engr_at(u.ux, u.uy, buf, moves - multi, type);
1136 if (post_engr_text[0])
1137 pline("%s", post_engr_text);
1138 if (doblind && !resists_blnd(&youmonst)) {
1139 You("are blinded by the flash!");
1140 make_blinded((long) rnd(50), FALSE);
1141 if (!Blind)
1142 Your1(vision_clears);
1144 return 1;
1147 /* while loading bones, clean up text which might accidentally
1148 or maliciously disrupt player's terminal when displayed */
1149 void
1150 sanitize_engravings()
1152 struct engr *ep;
1154 for (ep = head_engr; ep; ep = ep->nxt_engr) {
1155 sanitize_name(ep->engr_txt);
1159 void
1160 save_engravings(fd, mode)
1161 int fd, mode;
1163 struct engr *ep, *ep2;
1164 unsigned no_more_engr = 0;
1166 for (ep = head_engr; ep; ep = ep2) {
1167 ep2 = ep->nxt_engr;
1168 if (ep->engr_lth && ep->engr_txt[0] && perform_bwrite(mode)) {
1169 bwrite(fd, (genericptr_t) &ep->engr_lth, sizeof ep->engr_lth);
1170 bwrite(fd, (genericptr_t) ep, sizeof (struct engr) + ep->engr_lth);
1172 if (release_data(mode))
1173 dealloc_engr(ep);
1175 if (perform_bwrite(mode))
1176 bwrite(fd, (genericptr_t) &no_more_engr, sizeof no_more_engr);
1177 if (release_data(mode))
1178 head_engr = 0;
1181 void
1182 rest_engravings(fd)
1183 int fd;
1185 struct engr *ep;
1186 unsigned lth;
1188 head_engr = 0;
1189 while (1) {
1190 mread(fd, (genericptr_t) &lth, sizeof lth);
1191 if (lth == 0)
1192 return;
1193 ep = newengr(lth);
1194 mread(fd, (genericptr_t) ep, sizeof (struct engr) + lth);
1195 ep->nxt_engr = head_engr;
1196 head_engr = ep;
1197 ep->engr_txt = (char *) (ep + 1); /* Andreas Bormann */
1198 /* Mark as finished for bones levels -- no problem for
1199 * normal levels as the player must have finished engraving
1200 * to be able to move again.
1202 ep->engr_time = moves;
1206 /* to support '#stats' wizard-mode command */
1207 void
1208 engr_stats(hdrfmt, hdrbuf, count, size)
1209 const char *hdrfmt;
1210 char *hdrbuf;
1211 long *count, *size;
1213 struct engr *ep;
1215 Sprintf(hdrbuf, hdrfmt, (long) sizeof (struct engr));
1216 *count = *size = 0L;
1217 for (ep = head_engr; ep; ep = ep->nxt_engr) {
1218 ++*count;
1219 *size += (long) sizeof *ep + (long) ep->engr_lth;
1223 void
1224 del_engr(ep)
1225 register struct engr *ep;
1227 if (ep == head_engr) {
1228 head_engr = ep->nxt_engr;
1229 } else {
1230 register struct engr *ept;
1232 for (ept = head_engr; ept; ept = ept->nxt_engr)
1233 if (ept->nxt_engr == ep) {
1234 ept->nxt_engr = ep->nxt_engr;
1235 break;
1237 if (!ept) {
1238 impossible("Error in del_engr?");
1239 return;
1242 dealloc_engr(ep);
1245 /* randomly relocate an engraving */
1246 void
1247 rloc_engr(ep)
1248 struct engr *ep;
1250 int tx, ty, tryct = 200;
1252 do {
1253 if (--tryct < 0)
1254 return;
1255 tx = rn1(COLNO - 3, 2);
1256 ty = rn2(ROWNO);
1257 } while (engr_at(tx, ty) || !goodpos(tx, ty, (struct monst *) 0, 0));
1259 ep->engr_x = tx;
1260 ep->engr_y = ty;
1263 /* Create a headstone at the given location.
1264 * The caller is responsible for newsym(x, y).
1266 void
1267 make_grave(x, y, str)
1268 int x, y;
1269 const char *str;
1271 char buf[BUFSZ];
1273 /* Can we put a grave here? */
1274 if ((levl[x][y].typ != ROOM && levl[x][y].typ != GRAVE) || t_at(x, y))
1275 return;
1276 /* Make the grave */
1277 levl[x][y].typ = GRAVE;
1278 /* Engrave the headstone */
1279 del_engr_at(x, y);
1280 if (!str)
1281 str = get_rnd_text(EPITAPHFILE, buf);
1282 make_engr_at(x, y, str, 0L, HEADSTONE);
1283 return;
1286 /*engrave.c*/