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