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