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