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