/* $NetBSD: hack.objnam.c,v 1.11 2011/08/07 06:03:45 dholland Exp $ */ /* * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica, * Amsterdam * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of the Stichting Centrum voor Wiskunde en * Informatica, nor the names of its contributors may be used to endorse or * promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1982 Jay Fenlason * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #ifndef lint __RCSID("$NetBSD: hack.objnam.c,v 1.11 2011/08/07 06:03:45 dholland Exp $"); #endif /* not lint */ #include #include "hack.h" #include "extern.h" #define Snprintf (void) snprintf #define Strcat (void) strcat #define Strcpy (void) strcpy #define PREFIX 15 static char *strprepend(char *, char *); static char *sitoa(int); static char * strprepend(char *s, char *pref) { int i = strlen(pref); if (i > PREFIX) { pline("WARNING: prefix too short."); return (s); } s -= i; (void) strncpy(s, pref, i); /* do not copy trailing 0 */ return (s); } static char * sitoa(int a) { static char buf[13]; Snprintf(buf, sizeof(buf), (a < 0) ? "%d" : "+%d", a); return (buf); } char * typename(int otyp) { static char buf[BUFSZ]; size_t bufpos; struct objclass *ocl = &objects[otyp]; const char *an = ocl->oc_name; const char *dn = ocl->oc_descr; char *un = ocl->oc_uname; int nn = ocl->oc_name_known; switch (ocl->oc_olet) { case POTION_SYM: Strcpy(buf, "potion"); break; case SCROLL_SYM: Strcpy(buf, "scroll"); break; case WAND_SYM: Strcpy(buf, "wand"); break; case RING_SYM: Strcpy(buf, "ring"); break; default: if (nn) { Strcpy(buf, an); if (otyp >= TURQUOISE && otyp <= JADE) Strcat(buf, " stone"); if (un) { bufpos = strlen(buf); Snprintf(buf+bufpos, sizeof(buf)-bufpos, " called %s", un); } if (dn) { bufpos = strlen(buf); Snprintf(buf+bufpos, sizeof(buf)-bufpos, " (%s)", dn); } } else { strlcpy(buf, dn ? dn : an, sizeof(buf)); if (ocl->oc_olet == GEM_SYM) { strlcat(buf, " gem", sizeof(buf)); } if (un) { bufpos = strlen(buf); Snprintf(buf+bufpos, sizeof(buf)-bufpos, " called %s", un); } } return (buf); } /* here for ring/scroll/potion/wand */ if (nn) { bufpos = strlen(buf); Snprintf(buf+bufpos, sizeof(buf)-bufpos, " of %s", an); } if (un) { bufpos = strlen(buf); Snprintf(buf+bufpos, sizeof(buf)-bufpos, " called %s", un); } if (dn) { bufpos = strlen(buf); Snprintf(buf+bufpos, sizeof(buf)-bufpos, " (%s)", dn); } return (buf); } char * xname(struct obj *obj) { static char bufr[BUFSZ]; /* caution: doname() and aobjnam() below "know" these sizes */ char *buf = &(bufr[PREFIX]); /* leave room for "17 -3 " */ size_t bufmax = sizeof(bufr) - PREFIX; int nn = objects[obj->otyp].oc_name_known; const char *an = objects[obj->otyp].oc_name; const char *dn = objects[obj->otyp].oc_descr; char *un = objects[obj->otyp].oc_uname; int pl = (obj->quan != 1); if (!obj->dknown && !Blind) obj->dknown = 1;/* %% doesnt belong here */ switch (obj->olet) { case AMULET_SYM: Strcpy(buf, (obj->spe < 0 && obj->known) ? "cheap plastic imitation of the " : ""); Strcat(buf, "Amulet of Yendor"); break; case TOOL_SYM: if (!nn) { strlcpy(buf, dn, bufmax); break; } strlcpy(buf, an, bufmax); break; case FOOD_SYM: if (obj->otyp == DEAD_HOMUNCULUS && pl) { pl = 0; Strcpy(buf, "dead homunculi"); break; } /* fungis ? */ /* FALLTHROUGH */ case WEAPON_SYM: if (obj->otyp == WORM_TOOTH && pl) { pl = 0; Strcpy(buf, "worm teeth"); break; } if (obj->otyp == CRYSKNIFE && pl) { pl = 0; Strcpy(buf, "crysknives"); break; } /* FALLTHROUGH */ case ARMOR_SYM: case CHAIN_SYM: case ROCK_SYM: strlcpy(buf, an, bufmax); break; case BALL_SYM: Snprintf(buf, bufmax, "%sheavy iron ball", (obj->owt > objects[obj->otyp].oc_weight) ? "very " : ""); break; case POTION_SYM: if (nn || un || !obj->dknown) { Strcpy(buf, "potion"); if (pl) { pl = 0; Strcat(buf, "s"); } if (!obj->dknown) break; if (un) { Strcat(buf, " called "); strlcat(buf, un, bufmax); } else { Strcat(buf, " of "); strlcat(buf, an, bufmax); } } else { strlcpy(buf, dn, bufmax); strlcat(buf, " potion", bufmax); } break; case SCROLL_SYM: Strcpy(buf, "scroll"); if (pl) { pl = 0; Strcat(buf, "s"); } if (!obj->dknown) break; if (nn) { Strcat(buf, " of "); strlcat(buf, an, bufmax); } else if (un) { Strcat(buf, " called "); strlcat(buf, un, bufmax); } else { Strcat(buf, " labeled "); strlcat(buf, dn, bufmax); } break; case WAND_SYM: if (!obj->dknown) Snprintf(buf, bufmax, "wand"); else if (nn) Snprintf(buf, bufmax, "wand of %s", an); else if (un) Snprintf(buf, bufmax, "wand called %s", un); else Snprintf(buf, bufmax, "%s wand", dn); break; case RING_SYM: if (!obj->dknown) Snprintf(buf, bufmax, "ring"); else if (nn) Snprintf(buf, bufmax, "ring of %s", an); else if (un) Snprintf(buf, bufmax, "ring called %s", un); else Snprintf(buf, bufmax, "%s ring", dn); break; case GEM_SYM: if (!obj->dknown) { Strcpy(buf, "gem"); break; } if (!nn) { Snprintf(buf, bufmax, "%s gem", dn); break; } strlcpy(buf, an, bufmax); if (obj->otyp >= TURQUOISE && obj->otyp <= JADE) strlcat(buf, " stone", bufmax); break; default: Snprintf(buf, bufmax, "glorkum %c (0%o) %u %d", obj->olet, obj->olet, obj->otyp, obj->spe); } if (pl) { char *p; for (p = buf; *p; p++) { if (!strncmp(" of ", p, 4)) { /* pieces of, cloves of, lumps of */ int c1, c2 = 's'; do { c1 = c2; c2 = *p; *p++ = c1; } while (c1); goto nopl; } } p = eos(buf) - 1; if (*p == 's' || *p == 'z' || *p == 'x' || (*p == 'h' && p[-1] == 's')) { /* boxes */ strlcat(buf, "es", bufmax); } else if (*p == 'y' && !strchr(vowels, p[-1])) { /* rubies, zruties */ *p = '\0'; strlcat(buf, "ies", bufmax); } else { strlcat(buf, "s", bufmax); } } nopl: if (obj->onamelth) { strlcat(buf, " named ", bufmax); strlcat(buf, ONAME(obj), bufmax); } return (buf); } char * doname(struct obj *obj) { char prefix[PREFIX]; char *bp = xname(obj); size_t bppos, bpmax; /* XXX do this better somehow w/o knowing internals of xname() */ bpmax = BUFSZ - PREFIX; if (obj->quan != 1) Snprintf(prefix, sizeof(prefix), "%u ", obj->quan); else Strcpy(prefix, "a "); switch (obj->olet) { case AMULET_SYM: if (strncmp(bp, "cheap ", 6)) Strcpy(prefix, "the "); break; case ARMOR_SYM: if (obj->owornmask & W_ARMOR) strlcat(bp, " (being worn)", bpmax); /* FALLTHROUGH */ case WEAPON_SYM: if (obj->known) { strlcat(prefix, sitoa(obj->spe), sizeof(prefix)); strlcat(prefix, " ", sizeof(prefix)); } break; case WAND_SYM: if (obj->known) { bppos = strlen(bp); Snprintf(bp+bppos, bpmax-bppos, " (%d)", obj->spe); } break; case RING_SYM: if (obj->owornmask & W_RINGR) strlcat(bp, " (on right hand)", bpmax); if (obj->owornmask & W_RINGL) strlcat(bp, " (on left hand)", bpmax); if (obj->known && (objects[obj->otyp].bits & SPEC)) { strlcat(prefix, sitoa(obj->spe), sizeof(prefix)); strlcat(prefix, " ", sizeof(prefix)); } break; } if (obj->owornmask & W_WEP) strlcat(bp, " (weapon in hand)", bpmax); if (obj->unpaid) strlcat(bp, " (unpaid)", bpmax); if (!strcmp(prefix, "a ") && strchr(vowels, *bp)) Strcpy(prefix, "an "); bp = strprepend(bp, prefix); return (bp); } /* used only in hack.fight.c (thitu) */ void setan(const char *str, char *buf, size_t bufmax) { if (strchr(vowels, *str)) Snprintf(buf, bufmax, "an %s", str); else Snprintf(buf, bufmax, "a %s", str); } char * aobjnam(struct obj *otmp, const char *verb) { char *bp = xname(otmp); char prefix[PREFIX]; size_t bpmax; /* XXX do this better somehow w/o knowing internals of xname() */ bpmax = BUFSZ - PREFIX; if (otmp->quan != 1) { Snprintf(prefix, sizeof(prefix), "%u ", otmp->quan); bp = strprepend(bp, prefix); } if (verb) { /* verb is given in plural (i.e., without trailing s) */ strlcat(bp, " ", bpmax); if (otmp->quan != 1) strlcat(bp, verb, bpmax); else if (!strcmp(verb, "are")) strlcat(bp, "is", bpmax); else { strlcat(bp, verb, bpmax); strlcat(bp, "s", bpmax); } } return (bp); } char * Doname(struct obj *obj) { char *s = doname(obj); if ('a' <= *s && *s <= 'z') *s -= ('a' - 'A'); return (s); } static const char *const wrp[] = {"wand", "ring", "potion", "scroll", "gem"}; static const char wrpsym[] = {WAND_SYM, RING_SYM, POTION_SYM, SCROLL_SYM, GEM_SYM}; struct obj * readobjnam(char *bp) { char *p; unsigned ii; int i; int cnt, spe, spesgn, typ, heavy; char let; char *un, *dn, *an; /* int the = 0; char *oname = 0; */ cnt = spe = spesgn = typ = heavy = 0; let = 0; an = dn = un = 0; for (p = bp; *p; p++) if ('A' <= *p && *p <= 'Z') *p += 'a' - 'A'; if (!strncmp(bp, "the ", 4)) { /* the = 1; */ bp += 4; } else if (!strncmp(bp, "an ", 3)) { cnt = 1; bp += 3; } else if (!strncmp(bp, "a ", 2)) { cnt = 1; bp += 2; } if (!cnt && digit(*bp)) { cnt = atoi(bp); while (digit(*bp)) bp++; while (*bp == ' ') bp++; } if (!cnt) cnt = 1; /* %% what with "gems" etc. ? */ if (*bp == '+' || *bp == '-') { spesgn = (*bp++ == '+') ? 1 : -1; spe = atoi(bp); while (digit(*bp)) bp++; while (*bp == ' ') bp++; } else { p = strrchr(bp, '('); if (p) { if (p > bp && p[-1] == ' ') p[-1] = 0; else *p = 0; p++; spe = atoi(p); while (digit(*p)) p++; if (strcmp(p, ")")) spe = 0; else spesgn = 1; } } /* * now we have the actual name, as delivered by xname, say green * potions called whisky scrolls labeled "QWERTY" egg dead zruties * fortune cookies very heavy iron ball named hoei wand of wishing * elven cloak */ for (p = bp; *p; p++) if (!strncmp(p, " named ", 7)) { *p = 0; /* oname = p+7; */ } for (p = bp; *p; p++) if (!strncmp(p, " called ", 8)) { *p = 0; un = p + 8; } for (p = bp; *p; p++) if (!strncmp(p, " labeled ", 9)) { *p = 0; dn = p + 9; } /* first change to singular if necessary */ if (cnt != 1) { /* find "cloves of garlic", "worthless pieces of blue glass" */ for (p = bp; *p; p++) if (!strncmp(p, "s of ", 5)) { while ((*p = p[1]) != '\0') p++; goto sing; } /* remove -s or -es (boxes) or -ies (rubies, zruties) */ p = eos(bp); if (p[-1] == 's') { if (p[-2] == 'e') { if (p[-3] == 'i') { if (!strcmp(p - 7, "cookies")) goto mins; Strcpy(p - 3, "y"); goto sing; } /* note: cloves / knives from clove / knife */ if (!strcmp(p - 6, "knives")) { Strcpy(p - 3, "fe"); goto sing; } /* note: nurses, axes but boxes */ if (!strcmp(p - 5, "boxes")) { p[-2] = 0; goto sing; } } mins: p[-1] = 0; } else { if (!strcmp(p - 9, "homunculi")) { Strcpy(p - 1, "us"); /* !! makes string * longer */ goto sing; } if (!strcmp(p - 5, "teeth")) { Strcpy(p - 5, "tooth"); goto sing; } /* here we cannot find the plural suffix */ } } sing: if (!strcmp(bp, "amulet of yendor")) { typ = AMULET_OF_YENDOR; goto typfnd; } p = eos(bp); if (!strcmp(p - 5, " mail")) { /* Note: ring mail is not a ring ! */ let = ARMOR_SYM; an = bp; goto srch; } for (ii = 0; ii < sizeof(wrpsym); ii++) { int j = strlen(wrp[ii]); if (!strncmp(bp, wrp[ii], j)) { let = wrpsym[ii]; bp += j; if (!strncmp(bp, " of ", 4)) an = bp + 4; /* else if(*bp) ?? */ goto srch; } if (!strcmp(p - j, wrp[ii])) { let = wrpsym[ii]; p -= j; *p = 0; if (p[-1] == ' ') p[-1] = 0; dn = bp; goto srch; } } if (!strcmp(p - 6, " stone")) { p[-6] = 0; let = GEM_SYM; an = bp; goto srch; } if (!strcmp(bp, "very heavy iron ball")) { heavy = 1; typ = HEAVY_IRON_BALL; goto typfnd; } an = bp; srch: if (!an && !dn && !un) goto any; i = 1; if (let) i = bases[letindex(let)]; while (i <= NROFOBJECTS && (!let || objects[i].oc_olet == let)) { const char *zn = objects[i].oc_name; if (!zn) goto nxti; if (an && strcmp(an, zn)) goto nxti; if (dn && (!(zn = objects[i].oc_descr) || strcmp(dn, zn))) goto nxti; if (un && (!(zn = objects[i].oc_uname) || strcmp(un, zn))) goto nxti; typ = i; goto typfnd; nxti: i++; } any: if (!let) let = wrpsym[rn2(sizeof(wrpsym))]; typ = probtype(let); typfnd: { struct obj *otmp; let = objects[typ].oc_olet; otmp = mksobj(typ); if (heavy) otmp->owt += 15; if (cnt > 0 && strchr("%?!*)", let) && (cnt < 4 || (let == WEAPON_SYM && typ <= ROCK && cnt < 20))) otmp->quan = cnt; if (spe > 3 && spe > otmp->spe) spe = 0; else if (let == WAND_SYM) spe = otmp->spe; if (spe == 3 && u.uluck < 0) spesgn = -1; if (let != WAND_SYM && spesgn == -1) spe = -spe; if (let == BALL_SYM) spe = 0; else if (let == AMULET_SYM) spe = -1; else if (typ == WAN_WISHING && rn2(10)) spe = (rn2(10) ? -1 : 0); otmp->spe = spe; if (spesgn == -1) otmp->cursed = 1; return (otmp); } }