root/games/hack/hack.objnam.c
/*      $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 <hack@gnu.org>
 * 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 <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: hack.objnam.c,v 1.11 2011/08/07 06:03:45 dholland Exp $");
#endif                          /* not lint */

#include <stdlib.h>
#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);
        }
}