1 /* $NetBSD: hack.objnam.c,v 1.11 2011/08/07 06:03:45 dholland Exp $ */ 2 3 /* 4 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica, 5 * Amsterdam 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are 10 * met: 11 * 12 * - Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * - Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * - Neither the name of the Stichting Centrum voor Wiskunde en 20 * Informatica, nor the names of its contributors may be used to endorse or 21 * promote products derived from this software without specific prior 22 * written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 28 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 /* 38 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org> 39 * All rights reserved. 40 * 41 * Redistribution and use in source and binary forms, with or without 42 * modification, are permitted provided that the following conditions 43 * are met: 44 * 1. Redistributions of source code must retain the above copyright 45 * notice, this list of conditions and the following disclaimer. 46 * 2. Redistributions in binary form must reproduce the above copyright 47 * notice, this list of conditions and the following disclaimer in the 48 * documentation and/or other materials provided with the distribution. 49 * 3. The name of the author may not be used to endorse or promote products 50 * derived from this software without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 53 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 54 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 55 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 56 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 57 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 58 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 59 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 60 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 61 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 62 */ 63 64 #include <stdlib.h> 65 #include "hack.h" 66 #include "extern.h" 67 #define Snprintf (void) snprintf 68 #define Strcat (void) strcat 69 #define Strcpy (void) strcpy 70 #define PREFIX 15 71 72 static char *strprepend(char *, char *); 73 static char *sitoa(int); 74 75 static char * 76 strprepend(char *s, char *pref) 77 { 78 int i = strlen(pref); 79 if (i > PREFIX) { 80 pline("WARNING: prefix too short."); 81 return (s); 82 } 83 s -= i; 84 (void) strncpy(s, pref, i); /* do not copy trailing 0 */ 85 return (s); 86 } 87 88 static char * 89 sitoa(int a) 90 { 91 static char buf[13]; 92 Snprintf(buf, sizeof(buf), (a < 0) ? "%d" : "+%d", a); 93 return (buf); 94 } 95 96 char * 97 typename(int otyp) 98 { 99 static char buf[BUFSZ]; 100 size_t bufpos; 101 struct objclass *ocl = &objects[otyp]; 102 const char *an = ocl->oc_name; 103 const char *dn = ocl->oc_descr; 104 char *un = ocl->oc_uname; 105 int nn = ocl->oc_name_known; 106 switch (ocl->oc_olet) { 107 case POTION_SYM: 108 Strcpy(buf, "potion"); 109 break; 110 case SCROLL_SYM: 111 Strcpy(buf, "scroll"); 112 break; 113 case WAND_SYM: 114 Strcpy(buf, "wand"); 115 break; 116 case RING_SYM: 117 Strcpy(buf, "ring"); 118 break; 119 default: 120 if (nn) { 121 Strcpy(buf, an); 122 if (otyp >= TURQUOISE && otyp <= JADE) 123 Strcat(buf, " stone"); 124 if (un) { 125 bufpos = strlen(buf); 126 Snprintf(buf+bufpos, sizeof(buf)-bufpos, 127 " called %s", un); 128 } 129 if (dn) { 130 bufpos = strlen(buf); 131 Snprintf(buf+bufpos, sizeof(buf)-bufpos, 132 " (%s)", dn); 133 } 134 } else { 135 strlcpy(buf, dn ? dn : an, sizeof(buf)); 136 if (ocl->oc_olet == GEM_SYM) { 137 strlcat(buf, " gem", sizeof(buf)); 138 } 139 if (un) { 140 bufpos = strlen(buf); 141 Snprintf(buf+bufpos, sizeof(buf)-bufpos, 142 " called %s", un); 143 } 144 } 145 return (buf); 146 } 147 /* here for ring/scroll/potion/wand */ 148 if (nn) { 149 bufpos = strlen(buf); 150 Snprintf(buf+bufpos, sizeof(buf)-bufpos, " of %s", an); 151 } 152 if (un) { 153 bufpos = strlen(buf); 154 Snprintf(buf+bufpos, sizeof(buf)-bufpos, " called %s", un); 155 } 156 if (dn) { 157 bufpos = strlen(buf); 158 Snprintf(buf+bufpos, sizeof(buf)-bufpos, " (%s)", dn); 159 } 160 return (buf); 161 } 162 163 char * 164 xname(struct obj *obj) 165 { 166 static char bufr[BUFSZ]; 167 /* caution: doname() and aobjnam() below "know" these sizes */ 168 char *buf = &(bufr[PREFIX]); /* leave room for "17 -3 " */ 169 size_t bufmax = sizeof(bufr) - PREFIX; 170 int nn = objects[obj->otyp].oc_name_known; 171 const char *an = objects[obj->otyp].oc_name; 172 const char *dn = objects[obj->otyp].oc_descr; 173 char *un = objects[obj->otyp].oc_uname; 174 int pl = (obj->quan != 1); 175 176 if (!obj->dknown && !Blind) 177 obj->dknown = 1;/* %% doesnt belong here */ 178 switch (obj->olet) { 179 case AMULET_SYM: 180 Strcpy(buf, (obj->spe < 0 && obj->known) 181 ? "cheap plastic imitation of the " : ""); 182 Strcat(buf, "Amulet of Yendor"); 183 break; 184 case TOOL_SYM: 185 if (!nn) { 186 strlcpy(buf, dn, bufmax); 187 break; 188 } 189 strlcpy(buf, an, bufmax); 190 break; 191 case FOOD_SYM: 192 if (obj->otyp == DEAD_HOMUNCULUS && pl) { 193 pl = 0; 194 Strcpy(buf, "dead homunculi"); 195 break; 196 } 197 /* fungis ? */ 198 /* FALLTHROUGH */ 199 case WEAPON_SYM: 200 if (obj->otyp == WORM_TOOTH && pl) { 201 pl = 0; 202 Strcpy(buf, "worm teeth"); 203 break; 204 } 205 if (obj->otyp == CRYSKNIFE && pl) { 206 pl = 0; 207 Strcpy(buf, "crysknives"); 208 break; 209 } 210 /* FALLTHROUGH */ 211 case ARMOR_SYM: 212 case CHAIN_SYM: 213 case ROCK_SYM: 214 strlcpy(buf, an, bufmax); 215 break; 216 case BALL_SYM: 217 Snprintf(buf, bufmax, "%sheavy iron ball", 218 (obj->owt > objects[obj->otyp].oc_weight) ? "very " : ""); 219 break; 220 case POTION_SYM: 221 if (nn || un || !obj->dknown) { 222 Strcpy(buf, "potion"); 223 if (pl) { 224 pl = 0; 225 Strcat(buf, "s"); 226 } 227 if (!obj->dknown) 228 break; 229 if (un) { 230 Strcat(buf, " called "); 231 strlcat(buf, un, bufmax); 232 } else { 233 Strcat(buf, " of "); 234 strlcat(buf, an, bufmax); 235 } 236 } else { 237 strlcpy(buf, dn, bufmax); 238 strlcat(buf, " potion", bufmax); 239 } 240 break; 241 case SCROLL_SYM: 242 Strcpy(buf, "scroll"); 243 if (pl) { 244 pl = 0; 245 Strcat(buf, "s"); 246 } 247 if (!obj->dknown) 248 break; 249 if (nn) { 250 Strcat(buf, " of "); 251 strlcat(buf, an, bufmax); 252 } else if (un) { 253 Strcat(buf, " called "); 254 strlcat(buf, un, bufmax); 255 } else { 256 Strcat(buf, " labeled "); 257 strlcat(buf, dn, bufmax); 258 } 259 break; 260 case WAND_SYM: 261 if (!obj->dknown) 262 Snprintf(buf, bufmax, "wand"); 263 else if (nn) 264 Snprintf(buf, bufmax, "wand of %s", an); 265 else if (un) 266 Snprintf(buf, bufmax, "wand called %s", un); 267 else 268 Snprintf(buf, bufmax, "%s wand", dn); 269 break; 270 case RING_SYM: 271 if (!obj->dknown) 272 Snprintf(buf, bufmax, "ring"); 273 else if (nn) 274 Snprintf(buf, bufmax, "ring of %s", an); 275 else if (un) 276 Snprintf(buf, bufmax, "ring called %s", un); 277 else 278 Snprintf(buf, bufmax, "%s ring", dn); 279 break; 280 case GEM_SYM: 281 if (!obj->dknown) { 282 Strcpy(buf, "gem"); 283 break; 284 } 285 if (!nn) { 286 Snprintf(buf, bufmax, "%s gem", dn); 287 break; 288 } 289 strlcpy(buf, an, bufmax); 290 if (obj->otyp >= TURQUOISE && obj->otyp <= JADE) 291 strlcat(buf, " stone", bufmax); 292 break; 293 default: 294 Snprintf(buf, bufmax, "glorkum %c (0%o) %u %d", 295 obj->olet, obj->olet, obj->otyp, obj->spe); 296 } 297 if (pl) { 298 char *p; 299 300 for (p = buf; *p; p++) { 301 if (!strncmp(" of ", p, 4)) { 302 /* pieces of, cloves of, lumps of */ 303 int c1, c2 = 's'; 304 305 do { 306 c1 = c2; 307 c2 = *p; 308 *p++ = c1; 309 } while (c1); 310 goto nopl; 311 } 312 } 313 p = eos(buf) - 1; 314 if (*p == 's' || *p == 'z' || *p == 'x' || 315 (*p == 'h' && p[-1] == 's')) { 316 /* boxes */ 317 strlcat(buf, "es", bufmax); 318 } else if (*p == 'y' && !strchr(vowels, p[-1])) { 319 /* rubies, zruties */ 320 *p = '\0'; 321 strlcat(buf, "ies", bufmax); 322 } else { 323 strlcat(buf, "s", bufmax); 324 } 325 } 326 nopl: 327 if (obj->onamelth) { 328 strlcat(buf, " named ", bufmax); 329 strlcat(buf, ONAME(obj), bufmax); 330 } 331 return (buf); 332 } 333 334 char * 335 doname(struct obj *obj) 336 { 337 char prefix[PREFIX]; 338 char *bp = xname(obj); 339 size_t bppos, bpmax; 340 341 /* XXX do this better somehow w/o knowing internals of xname() */ 342 bpmax = BUFSZ - PREFIX; 343 344 if (obj->quan != 1) 345 Snprintf(prefix, sizeof(prefix), "%u ", obj->quan); 346 else 347 Strcpy(prefix, "a "); 348 switch (obj->olet) { 349 case AMULET_SYM: 350 if (strncmp(bp, "cheap ", 6)) 351 Strcpy(prefix, "the "); 352 break; 353 case ARMOR_SYM: 354 if (obj->owornmask & W_ARMOR) 355 strlcat(bp, " (being worn)", bpmax); 356 /* FALLTHROUGH */ 357 case WEAPON_SYM: 358 if (obj->known) { 359 strlcat(prefix, sitoa(obj->spe), sizeof(prefix)); 360 strlcat(prefix, " ", sizeof(prefix)); 361 } 362 break; 363 case WAND_SYM: 364 if (obj->known) { 365 bppos = strlen(bp); 366 Snprintf(bp+bppos, bpmax-bppos, " (%d)", obj->spe); 367 } 368 break; 369 case RING_SYM: 370 if (obj->owornmask & W_RINGR) 371 strlcat(bp, " (on right hand)", bpmax); 372 if (obj->owornmask & W_RINGL) 373 strlcat(bp, " (on left hand)", bpmax); 374 if (obj->known && (objects[obj->otyp].bits & SPEC)) { 375 strlcat(prefix, sitoa(obj->spe), sizeof(prefix)); 376 strlcat(prefix, " ", sizeof(prefix)); 377 } 378 break; 379 } 380 if (obj->owornmask & W_WEP) 381 strlcat(bp, " (weapon in hand)", bpmax); 382 if (obj->unpaid) 383 strlcat(bp, " (unpaid)", bpmax); 384 if (!strcmp(prefix, "a ") && strchr(vowels, *bp)) 385 Strcpy(prefix, "an "); 386 bp = strprepend(bp, prefix); 387 return (bp); 388 } 389 390 /* used only in hack.fight.c (thitu) */ 391 void 392 setan(const char *str, char *buf, size_t bufmax) 393 { 394 if (strchr(vowels, *str)) 395 Snprintf(buf, bufmax, "an %s", str); 396 else 397 Snprintf(buf, bufmax, "a %s", str); 398 } 399 400 char * 401 aobjnam(struct obj *otmp, const char *verb) 402 { 403 char *bp = xname(otmp); 404 char prefix[PREFIX]; 405 size_t bpmax; 406 407 /* XXX do this better somehow w/o knowing internals of xname() */ 408 bpmax = BUFSZ - PREFIX; 409 410 if (otmp->quan != 1) { 411 Snprintf(prefix, sizeof(prefix), "%u ", otmp->quan); 412 bp = strprepend(bp, prefix); 413 } 414 if (verb) { 415 /* verb is given in plural (i.e., without trailing s) */ 416 strlcat(bp, " ", bpmax); 417 if (otmp->quan != 1) 418 strlcat(bp, verb, bpmax); 419 else if (!strcmp(verb, "are")) 420 strlcat(bp, "is", bpmax); 421 else { 422 strlcat(bp, verb, bpmax); 423 strlcat(bp, "s", bpmax); 424 } 425 } 426 return (bp); 427 } 428 429 char * 430 Doname(struct obj *obj) 431 { 432 char *s = doname(obj); 433 434 if ('a' <= *s && *s <= 'z') 435 *s -= ('a' - 'A'); 436 return (s); 437 } 438 439 static const char *const wrp[] = {"wand", "ring", "potion", "scroll", "gem"}; 440 static const char wrpsym[] = {WAND_SYM, RING_SYM, POTION_SYM, SCROLL_SYM, GEM_SYM}; 441 442 struct obj * 443 readobjnam(char *bp) 444 { 445 char *p; 446 unsigned ii; 447 int i; 448 int cnt, spe, spesgn, typ, heavy; 449 char let; 450 char *un, *dn, *an; 451 /* int the = 0; char *oname = 0; */ 452 cnt = spe = spesgn = typ = heavy = 0; 453 let = 0; 454 an = dn = un = 0; 455 for (p = bp; *p; p++) 456 if ('A' <= *p && *p <= 'Z') 457 *p += 'a' - 'A'; 458 if (!strncmp(bp, "the ", 4)) { 459 /* the = 1; */ 460 bp += 4; 461 } else if (!strncmp(bp, "an ", 3)) { 462 cnt = 1; 463 bp += 3; 464 } else if (!strncmp(bp, "a ", 2)) { 465 cnt = 1; 466 bp += 2; 467 } 468 if (!cnt && digit(*bp)) { 469 cnt = atoi(bp); 470 while (digit(*bp)) 471 bp++; 472 while (*bp == ' ') 473 bp++; 474 } 475 if (!cnt) 476 cnt = 1; /* %% what with "gems" etc. ? */ 477 478 if (*bp == '+' || *bp == '-') { 479 spesgn = (*bp++ == '+') ? 1 : -1; 480 spe = atoi(bp); 481 while (digit(*bp)) 482 bp++; 483 while (*bp == ' ') 484 bp++; 485 } else { 486 p = strrchr(bp, '('); 487 if (p) { 488 if (p > bp && p[-1] == ' ') 489 p[-1] = 0; 490 else 491 *p = 0; 492 p++; 493 spe = atoi(p); 494 while (digit(*p)) 495 p++; 496 if (strcmp(p, ")")) 497 spe = 0; 498 else 499 spesgn = 1; 500 } 501 } 502 /* 503 * now we have the actual name, as delivered by xname, say green 504 * potions called whisky scrolls labeled "QWERTY" egg dead zruties 505 * fortune cookies very heavy iron ball named hoei wand of wishing 506 * elven cloak 507 */ 508 for (p = bp; *p; p++) 509 if (!strncmp(p, " named ", 7)) { 510 *p = 0; 511 /* oname = p+7; */ 512 } 513 for (p = bp; *p; p++) 514 if (!strncmp(p, " called ", 8)) { 515 *p = 0; 516 un = p + 8; 517 } 518 for (p = bp; *p; p++) 519 if (!strncmp(p, " labeled ", 9)) { 520 *p = 0; 521 dn = p + 9; 522 } 523 /* first change to singular if necessary */ 524 if (cnt != 1) { 525 /* find "cloves of garlic", "worthless pieces of blue glass" */ 526 for (p = bp; *p; p++) 527 if (!strncmp(p, "s of ", 5)) { 528 while ((*p = p[1]) != '\0') 529 p++; 530 goto sing; 531 } 532 /* remove -s or -es (boxes) or -ies (rubies, zruties) */ 533 p = eos(bp); 534 if (p[-1] == 's') { 535 if (p[-2] == 'e') { 536 if (p[-3] == 'i') { 537 if (!strcmp(p - 7, "cookies")) 538 goto mins; 539 Strcpy(p - 3, "y"); 540 goto sing; 541 } 542 /* note: cloves / knives from clove / knife */ 543 if (!strcmp(p - 6, "knives")) { 544 Strcpy(p - 3, "fe"); 545 goto sing; 546 } 547 /* note: nurses, axes but boxes */ 548 if (!strcmp(p - 5, "boxes")) { 549 p[-2] = 0; 550 goto sing; 551 } 552 } 553 mins: 554 p[-1] = 0; 555 } else { 556 if (!strcmp(p - 9, "homunculi")) { 557 Strcpy(p - 1, "us"); /* !! makes string 558 * longer */ 559 goto sing; 560 } 561 if (!strcmp(p - 5, "teeth")) { 562 Strcpy(p - 5, "tooth"); 563 goto sing; 564 } 565 /* here we cannot find the plural suffix */ 566 } 567 } 568 sing: 569 if (!strcmp(bp, "amulet of yendor")) { 570 typ = AMULET_OF_YENDOR; 571 goto typfnd; 572 } 573 p = eos(bp); 574 if (!strcmp(p - 5, " mail")) { /* Note: ring mail is not a ring ! */ 575 let = ARMOR_SYM; 576 an = bp; 577 goto srch; 578 } 579 for (ii = 0; ii < sizeof(wrpsym); ii++) { 580 int j = strlen(wrp[ii]); 581 if (!strncmp(bp, wrp[ii], j)) { 582 let = wrpsym[ii]; 583 bp += j; 584 if (!strncmp(bp, " of ", 4)) 585 an = bp + 4; 586 /* else if(*bp) ?? */ 587 goto srch; 588 } 589 if (!strcmp(p - j, wrp[ii])) { 590 let = wrpsym[ii]; 591 p -= j; 592 *p = 0; 593 if (p[-1] == ' ') 594 p[-1] = 0; 595 dn = bp; 596 goto srch; 597 } 598 } 599 if (!strcmp(p - 6, " stone")) { 600 p[-6] = 0; 601 let = GEM_SYM; 602 an = bp; 603 goto srch; 604 } 605 if (!strcmp(bp, "very heavy iron ball")) { 606 heavy = 1; 607 typ = HEAVY_IRON_BALL; 608 goto typfnd; 609 } 610 an = bp; 611 srch: 612 if (!an && !dn && !un) 613 goto any; 614 i = 1; 615 if (let) 616 i = bases[letindex(let)]; 617 while (i <= NROFOBJECTS && (!let || objects[i].oc_olet == let)) { 618 const char *zn = objects[i].oc_name; 619 620 if (!zn) 621 goto nxti; 622 if (an && strcmp(an, zn)) 623 goto nxti; 624 if (dn && (!(zn = objects[i].oc_descr) || strcmp(dn, zn))) 625 goto nxti; 626 if (un && (!(zn = objects[i].oc_uname) || strcmp(un, zn))) 627 goto nxti; 628 typ = i; 629 goto typfnd; 630 nxti: 631 i++; 632 } 633 any: 634 if (!let) 635 let = wrpsym[rn2(sizeof(wrpsym))]; 636 typ = probtype(let); 637 typfnd: 638 { 639 struct obj *otmp; 640 let = objects[typ].oc_olet; 641 otmp = mksobj(typ); 642 if (heavy) 643 otmp->owt += 15; 644 if (cnt > 0 && strchr("%?!*)", let) && 645 (cnt < 4 || (let == WEAPON_SYM && typ <= ROCK && cnt < 20))) 646 otmp->quan = cnt; 647 648 if (spe > 3 && spe > otmp->spe) 649 spe = 0; 650 else if (let == WAND_SYM) 651 spe = otmp->spe; 652 if (spe == 3 && u.uluck < 0) 653 spesgn = -1; 654 if (let != WAND_SYM && spesgn == -1) 655 spe = -spe; 656 if (let == BALL_SYM) 657 spe = 0; 658 else if (let == AMULET_SYM) 659 spe = -1; 660 else if (typ == WAN_WISHING && rn2(10)) 661 spe = (rn2(10) ? -1 : 0); 662 otmp->spe = spe; 663 664 if (spesgn == -1) 665 otmp->cursed = 1; 666 667 return (otmp); 668 } 669 } 670