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*/