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