+++ /dev/null
-/* NetHack 3.6 objnam.c $NHDT-Date: 1521377345 2018/03/18 12:49:05 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.194 $ */
-/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
-/* NetHack may be freely redistributed. See license for details. */
-
-#include "hack.h"
-
-/* "an uncursed greased partly eaten guardian naga hatchling [corpse]" */
-#define PREFIX 80 /* (56) */
-#define SCHAR_LIM 127
-#define NUMOBUF 12
-
-STATIC_DCL char *FDECL(strprepend, (char *, const char *));
-STATIC_DCL short FDECL(rnd_otyp_by_wpnskill, (SCHAR_P));
-STATIC_DCL short FDECL(rnd_otyp_by_namedesc, (char *, CHAR_P));
-STATIC_DCL boolean FDECL(wishymatch, (const char *, const char *, BOOLEAN_P));
-STATIC_DCL char *NDECL(nextobuf);
-STATIC_DCL void FDECL(releaseobuf, (char *));
-STATIC_DCL char *FDECL(minimal_xname, (struct obj *));
-STATIC_DCL void FDECL(add_erosion_words, (struct obj *, char *));
-STATIC_DCL char *FDECL(doname_base, (struct obj *obj, unsigned));
-STATIC_DCL boolean FDECL(singplur_lookup, (char *, char *, BOOLEAN_P,
- const char *const *));
-STATIC_DCL char *FDECL(singplur_compound, (char *));
-STATIC_DCL char *FDECL(xname_flags, (struct obj *, unsigned));
-STATIC_DCL boolean FDECL(badman, (const char *, BOOLEAN_P));
-
-struct Jitem {
- int item;
- const char *name;
-};
-
-#define BSTRCMPI(base, ptr, str) ((ptr) < base || strcmpi((ptr), str))
-#define BSTRNCMPI(base, ptr, str, num) \
- ((ptr) < base || strncmpi((ptr), str, num))
-#define Strcasecpy(dst, src) (void) strcasecpy(dst, src)
-
-/* true for gems/rocks that should have " stone" appended to their names */
-#define GemStone(typ) \
- (typ == FLINT \
- || (objects[typ].oc_material == GEMSTONE \
- && (typ != DILITHIUM_CRYSTAL && typ != RUBY && typ != DIAMOND \
- && typ != SAPPHIRE && typ != BLACK_OPAL && typ != EMERALD \
- && typ != OPAL)))
-
-STATIC_OVL struct Jitem Japanese_items[] = { { SHORT_SWORD, "wakizashi" },
- { BROADSWORD, "ninja-to" },
- { FLAIL, "nunchaku" },
- { GLAIVE, "naginata" },
- { LOCK_PICK, "osaku" },
- { WOODEN_HARP, "koto" },
- { KNIFE, "shito" },
- { PLATE_MAIL, "tanko" },
- { HELMET, "kabuto" },
- { LEATHER_GLOVES, "yugake" },
- { FOOD_RATION, "gunyoki" },
- { POT_BOOZE, "sake" },
- { 0, "" } };
-
-STATIC_DCL const char *FDECL(Japanese_item_name, (int i));
-
-STATIC_OVL char *
-strprepend(s, pref)
-register char *s;
-register const char *pref;
-{
- register int i = (int) strlen(pref);
-
- if (i > PREFIX) {
- impossible("PREFIX too short (for %d).", i);
- return s;
- }
- s -= i;
- (void) strncpy(s, pref, i); /* do not copy trailing 0 */
- return s;
-}
-
-/* manage a pool of BUFSZ buffers, so callers don't have to */
-static char NEARDATA obufs[NUMOBUF][BUFSZ];
-static int obufidx = 0;
-
-STATIC_OVL char *
-nextobuf()
-{
- obufidx = (obufidx + 1) % NUMOBUF;
- return obufs[obufidx];
-}
-
-/* put the most recently allocated buffer back if possible */
-STATIC_OVL void
-releaseobuf(bufp)
-char *bufp;
-{
- /* caller may not know whether bufp is the most recently allocated
- buffer; if it isn't, do nothing; note that because of the somewhat
- obscure PREFIX handling for object name formatting by xname(),
- the pointer our caller has and is passing to us might be into the
- middle of an obuf rather than the address returned by nextobuf() */
- if (bufp >= obufs[obufidx]
- && bufp < obufs[obufidx] + sizeof obufs[obufidx]) /* obufs[][BUFSZ] */
- obufidx = (obufidx - 1 + NUMOBUF) % NUMOBUF;
-}
-
-char *
-obj_typename(otyp)
-register int otyp;
-{
- char *buf = nextobuf();
- struct objclass *ocl = &objects[otyp];
- const char *actualn = OBJ_NAME(*ocl);
- const char *dn = OBJ_DESCR(*ocl);
- const char *un = ocl->oc_uname;
- int nn = ocl->oc_name_known;
-
- if (Role_if(PM_SAMURAI) && Japanese_item_name(otyp))
- actualn = Japanese_item_name(otyp);
- switch (ocl->oc_class) {
- case COIN_CLASS:
- Strcpy(buf, "coin");
- break;
- case POTION_CLASS:
- Strcpy(buf, "potion");
- break;
- case SCROLL_CLASS:
- Strcpy(buf, "scroll");
- break;
- case WAND_CLASS:
- Strcpy(buf, "wand");
- break;
- case SPBOOK_CLASS:
- if (otyp != SPE_NOVEL) {
- Strcpy(buf, "spellbook");
- } else {
- Strcpy(buf, !nn ? "book" : "novel");
- nn = 0;
- }
- break;
- case RING_CLASS:
- Strcpy(buf, "ring");
- break;
- case AMULET_CLASS:
- if (nn)
- Strcpy(buf, actualn);
- else
- Strcpy(buf, "amulet");
- if (un)
- Sprintf(eos(buf), " called %s", un);
- if (dn)
- Sprintf(eos(buf), " (%s)", dn);
- return buf;
- default:
- if (nn) {
- Strcpy(buf, actualn);
- if (GemStone(otyp))
- Strcat(buf, " stone");
- if (un)
- Sprintf(eos(buf), " called %s", un);
- if (dn)
- Sprintf(eos(buf), " (%s)", dn);
- } else {
- Strcpy(buf, dn ? dn : actualn);
- if (ocl->oc_class == GEM_CLASS)
- Strcat(buf,
- (ocl->oc_material == MINERAL) ? " stone" : " gem");
- if (un)
- Sprintf(eos(buf), " called %s", un);
- }
- return buf;
- }
- /* here for ring/scroll/potion/wand */
- if (nn) {
- if (ocl->oc_unique)
- Strcpy(buf, actualn); /* avoid spellbook of Book of the Dead */
- else
- Sprintf(eos(buf), " of %s", actualn);
- }
- if (un)
- Sprintf(eos(buf), " called %s", un);
- if (dn)
- Sprintf(eos(buf), " (%s)", dn);
- return buf;
-}
-
-/* less verbose result than obj_typename(); either the actual name
- or the description (but not both); user-assigned name is ignored */
-char *
-simple_typename(otyp)
-int otyp;
-{
- char *bufp, *pp, *save_uname = objects[otyp].oc_uname;
-
- objects[otyp].oc_uname = 0; /* suppress any name given by user */
- bufp = obj_typename(otyp);
- objects[otyp].oc_uname = save_uname;
- if ((pp = strstri(bufp, " (")) != 0)
- *pp = '\0'; /* strip the appended description */
- return bufp;
-}
-
-boolean
-obj_is_pname(obj)
-struct obj *obj;
-{
- if (!obj->oartifact || !has_oname(obj))
- return FALSE;
- if (!program_state.gameover && !iflags.override_ID) {
- if (not_fully_identified(obj))
- return FALSE;
- }
- return TRUE;
-}
-
-/* used by distant_name() to pass extra information to xname_flags();
- it would be much cleaner if this were a parameter, but that would
- require all of the xname() and doname() calls to be modified */
-static int distantname = 0;
-
-/* Give the name of an object seen at a distance. Unlike xname/doname,
- * we don't want to set dknown if it's not set already.
- */
-char *
-distant_name(obj, func)
-struct obj *obj;
-char *FDECL((*func), (OBJ_P));
-{
- char *str;
-
- /* 3.6.1: this used to save Blind, set it, make the call, then restore
- * the saved value; but the Eyes of the Overworld override blindness
- * and let characters wearing them get dknown set for distant items.
- *
- * TODO? if the hero is wearing those Eyes, figure out whether the
- * object is within X-ray radius and only treat it as distant when
- * beyond that radius. Logic is iffy but result might be interesting.
- */
- ++distantname;
- str = (*func)(obj);
- --distantname;
- return str;
-}
-
-/* convert player specified fruit name into corresponding fruit juice name
- ("slice of pizza" -> "pizza juice" rather than "slice of pizza juice") */
-char *
-fruitname(juice)
-boolean juice; /* whether or not to append " juice" to the name */
-{
- char *buf = nextobuf();
- const char *fruit_nam = strstri(pl_fruit, " of ");
-
- if (fruit_nam)
- fruit_nam += 4; /* skip past " of " */
- else
- fruit_nam = pl_fruit; /* use it as is */
-
- Sprintf(buf, "%s%s", makesingular(fruit_nam), juice ? " juice" : "");
- return buf;
-}
-
-/* look up a named fruit by index (1..127) */
-struct fruit *
-fruit_from_indx(indx)
-int indx;
-{
- struct fruit *f;
-
- for (f = ffruit; f; f = f->nextf)
- if (f->fid == indx)
- break;
- return f;
-}
-
-/* look up a named fruit by name */
-struct fruit *
-fruit_from_name(fname, exact, highest_fid)
-const char *fname;
-boolean exact; /* False => prefix or exact match, True = exact match only */
-int *highest_fid; /* optional output; only valid if 'fname' isn't found */
-{
- struct fruit *f, *tentativef;
- char *altfname;
- unsigned k;
- /*
- * note: named fruits are case-senstive...
- */
-
- if (highest_fid)
- *highest_fid = 0;
- /* first try for an exact match */
- for (f = ffruit; f; f = f->nextf)
- if (!strcmp(f->fname, fname))
- return f;
- else if (highest_fid && f->fid > *highest_fid)
- *highest_fid = f->fid;
-
- /* didn't match as-is; if caller is willing to accept a prefix
- match, try to find one; we want to find the longest prefix that
- matches, not the first */
- if (!exact) {
- tentativef = 0;
- for (f = ffruit; f; f = f->nextf) {
- k = strlen(f->fname);
- if (!strncmp(f->fname, fname, k)
- && (!fname[k] || fname[k] == ' ')
- && (!tentativef || k > strlen(tentativef->fname)))
- tentativef = f;
- }
- f = tentativef;
- }
- /* if we still don't have a match, try singularizing the target;
- for exact match, that's trivial, but for prefix, it's hard */
- if (!f) {
- altfname = makesingular(fname);
- for (f = ffruit; f; f = f->nextf) {
- if (!strcmp(f->fname, altfname))
- break;
- }
- releaseobuf(altfname);
- }
- if (!f && !exact) {
- char fnamebuf[BUFSZ], *p;
- unsigned fname_k = strlen(fname); /* length of assumed plural fname */
-
- tentativef = 0;
- for (f = ffruit; f; f = f->nextf) {
- k = strlen(f->fname);
- /* reload fnamebuf[] each iteration in case it gets modified;
- there's no need to recalculate fname_k */
- Strcpy(fnamebuf, fname);
- /* bug? if singular of fname is longer than plural,
- failing the 'fname_k > k' test could skip a viable
- candidate; unfortunately, we can't singularize until
- after stripping off trailing stuff and we can't get
- accurate fname_k until fname has been singularized;
- compromise and use 'fname_k >= k' instead of '>',
- accepting 1 char length discrepancy without risking
- false match (I hope...) */
- if (fname_k >= k && (p = index(&fnamebuf[k], ' ')) != 0) {
- *p = '\0'; /* truncate at 1st space past length of f->fname */
- altfname = makesingular(fnamebuf);
- k = strlen(altfname); /* actually revised 'fname_k' */
- if (!strcmp(f->fname, altfname)
- && (!tentativef || k > strlen(tentativef->fname)))
- tentativef = f;
- releaseobuf(altfname); /* avoid churning through all obufs */
- }
- }
- f = tentativef;
- }
- return f;
-}
-
-/* sort the named-fruit linked list by fruit index number */
-void
-reorder_fruit(forward)
-boolean forward;
-{
- struct fruit *f, *allfr[1 + 127];
- int i, j, k = SIZE(allfr);
-
- for (i = 0; i < k; ++i)
- allfr[i] = (struct fruit *) 0;
- for (f = ffruit; f; f = f->nextf) {
- /* without sanity checking, this would reduce to 'allfr[f->fid]=f' */
- j = f->fid;
- if (j < 1 || j >= k) {
- impossible("reorder_fruit: fruit index (%d) out of range", j);
- return; /* don't sort after all; should never happen... */
- } else if (allfr[j]) {
- impossible("reorder_fruit: duplicate fruit index (%d)", j);
- return;
- }
- allfr[j] = f;
- }
- ffruit = 0; /* reset linked list; we're rebuilding it from scratch */
- /* slot [0] will always be empty; must start 'i' at 1 to avoid
- [k - i] being out of bounds during first iteration */
- for (i = 1; i < k; ++i) {
- /* for forward ordering, go through indices from high to low;
- for backward ordering, go from low to high */
- j = forward ? (k - i) : i;
- if (allfr[j]) {
- allfr[j]->nextf = ffruit;
- ffruit = allfr[j];
- }
- }
-}
-
-char *
-xname(obj)
-struct obj *obj;
-{
- return xname_flags(obj, CXN_NORMAL);
-}
-
-char *
-xname_flags(obj, cxn_flags)
-register struct obj *obj;
-unsigned cxn_flags; /* bitmask of CXN_xxx values */
-{
- register char *buf;
- register int typ = obj->otyp;
- register struct objclass *ocl = &objects[typ];
- int nn = ocl->oc_name_known, omndx = obj->corpsenm;
- const char *actualn = OBJ_NAME(*ocl);
- const char *dn = OBJ_DESCR(*ocl) ? OBJ_DESCR(*ocl) : actualn;
- const char *un = ocl->oc_uname;
- boolean pluralize = (obj->quan != 1L) && !(cxn_flags & CXN_SINGULAR);
- boolean known, dknown, bknown;
-
- buf = nextobuf() + PREFIX; /* leave room for "17 -3 " */
- if (Role_if(PM_SAMURAI) && Japanese_item_name(typ))
- actualn = Japanese_item_name(typ);
-
- buf[0] = '\0';
- /*
- * clean up known when it's tied to oc_name_known, eg after AD_DRIN
- * This is only required for unique objects since the article
- * printed for the object is tied to the combination of the two
- * and printing the wrong article gives away information.
- */
- if (!nn && ocl->oc_uses_known && ocl->oc_unique)
- obj->known = 0;
- if (!Blind && !distantname)
- obj->dknown = TRUE;
- if (Role_if(PM_PRIEST))
- obj->bknown = TRUE;
-
- if (iflags.override_ID) {
- known = dknown = bknown = TRUE;
- nn = 1;
- } else {
- known = obj->known;
- dknown = obj->dknown;
- bknown = obj->bknown;
- }
-
- if (obj_is_pname(obj))
- goto nameit;
- switch (obj->oclass) {
- case AMULET_CLASS:
- if (!dknown)
- Strcpy(buf, "amulet");
- else if (typ == AMULET_OF_YENDOR || typ == FAKE_AMULET_OF_YENDOR)
- /* each must be identified individually */
- Strcpy(buf, known ? actualn : dn);
- else if (nn)
- Strcpy(buf, actualn);
- else if (un)
- Sprintf(buf, "amulet called %s", un);
- else
- Sprintf(buf, "%s amulet", dn);
- break;
- case WEAPON_CLASS:
- if (is_poisonable(obj) && obj->opoisoned)
- Strcpy(buf, "poisoned ");
- case VENOM_CLASS:
- case TOOL_CLASS:
- if (typ == LENSES)
- Strcpy(buf, "pair of ");
- else if (is_wet_towel(obj))
- Strcpy(buf, (obj->spe < 3) ? "moist " : "wet ");
-
- if (!dknown)
- Strcat(buf, dn);
- else if (nn)
- Strcat(buf, actualn);
- else if (un) {
- Strcat(buf, dn);
- Strcat(buf, " called ");
- Strcat(buf, un);
- } else
- Strcat(buf, dn);
- /* If we use an() here we'd have to remember never to use */
- /* it whenever calling doname() or xname(). */
- if (typ == FIGURINE && omndx != NON_PM) {
- Sprintf(eos(buf), " of a%s %s",
- index(vowels, *mons[omndx].mname) ? "n" : "",
- mons[omndx].mname);
- } else if (is_wet_towel(obj)) {
- if (wizard)
- Sprintf(eos(buf), " (%d)", obj->spe);
- }
- break;
- case ARMOR_CLASS:
- /* depends on order of the dragon scales objects */
- if (typ >= GRAY_DRAGON_SCALES && typ <= YELLOW_DRAGON_SCALES) {
- Sprintf(buf, "set of %s", actualn);
- break;
- }
- if (is_boots(obj) || is_gloves(obj))
- Strcpy(buf, "pair of ");
-
- if (obj->otyp >= ELVEN_SHIELD && obj->otyp <= ORCISH_SHIELD
- && !dknown) {
- Strcpy(buf, "shield");
- break;
- }
- if (obj->otyp == SHIELD_OF_REFLECTION && !dknown) {
- Strcpy(buf, "smooth shield");
- break;
- }
-
- if (nn)
- Strcat(buf, actualn);
- else if (un) {
- if (is_boots(obj))
- Strcat(buf, "boots");
- else if (is_gloves(obj))
- Strcat(buf, "gloves");
- else if (is_cloak(obj))
- Strcpy(buf, "cloak");
- else if (is_helmet(obj))
- Strcpy(buf, "helmet");
- else if (is_shield(obj))
- Strcpy(buf, "shield");
- else
- Strcpy(buf, "armor");
- Strcat(buf, " called ");
- Strcat(buf, un);
- } else
- Strcat(buf, dn);
- break;
- case FOOD_CLASS:
- if (typ == SLIME_MOLD) {
- struct fruit *f = fruit_from_indx(obj->spe);
-
- if (!f) {
- impossible("Bad fruit #%d?", obj->spe);
- Strcpy(buf, "fruit");
- } else {
- Strcpy(buf, f->fname);
- if (pluralize) {
- /* ick; already pluralized fruit names
- are allowed--we want to try to avoid
- adding a redundant plural suffix */
- Strcpy(buf, makeplural(makesingular(buf)));
- pluralize = FALSE;
- }
- }
- break;
- }
- if (obj->globby) {
- Sprintf(buf, "%s%s",
- (obj->owt <= 100)
- ? "small "
- : (obj->owt > 500)
- ? "very large "
- : (obj->owt > 300)
- ? "large "
- : "",
- actualn);
- break;
- }
-
- Strcpy(buf, actualn);
- if (typ == TIN && known)
- tin_details(obj, omndx, buf);
- break;
- case COIN_CLASS:
- case CHAIN_CLASS:
- Strcpy(buf, actualn);
- break;
- case ROCK_CLASS:
- if (typ == STATUE && omndx != NON_PM)
- Sprintf(buf, "%s%s of %s%s",
- (Role_if(PM_ARCHEOLOGIST) && (obj->spe & STATUE_HISTORIC))
- ? "historic "
- : "",
- actualn,
- type_is_pname(&mons[omndx])
- ? ""
- : the_unique_pm(&mons[omndx])
- ? "the "
- : index(vowels, *mons[omndx].mname)
- ? "an "
- : "a ",
- mons[omndx].mname);
- else
- Strcpy(buf, actualn);
- break;
- case BALL_CLASS:
- Sprintf(buf, "%sheavy iron ball",
- (obj->owt > ocl->oc_weight) ? "very " : "");
- break;
- case POTION_CLASS:
- if (dknown && obj->odiluted)
- Strcpy(buf, "diluted ");
- if (nn || un || !dknown) {
- Strcat(buf, "potion");
- if (!dknown)
- break;
- if (nn) {
- Strcat(buf, " of ");
- if (typ == POT_WATER && bknown
- && (obj->blessed || obj->cursed)) {
- Strcat(buf, obj->blessed ? "holy " : "unholy ");
- }
- Strcat(buf, actualn);
- } else {
- Strcat(buf, " called ");
- Strcat(buf, un);
- }
- } else {
- Strcat(buf, dn);
- Strcat(buf, " potion");
- }
- break;
- case SCROLL_CLASS:
- Strcpy(buf, "scroll");
- if (!dknown)
- break;
- if (nn) {
- Strcat(buf, " of ");
- Strcat(buf, actualn);
- } else if (un) {
- Strcat(buf, " called ");
- Strcat(buf, un);
- } else if (ocl->oc_magic) {
- Strcat(buf, " labeled ");
- Strcat(buf, dn);
- } else {
- Strcpy(buf, dn);
- Strcat(buf, " scroll");
- }
- break;
- case WAND_CLASS:
- if (!dknown)
- Strcpy(buf, "wand");
- else if (nn)
- Sprintf(buf, "wand of %s", actualn);
- else if (un)
- Sprintf(buf, "wand called %s", un);
- else
- Sprintf(buf, "%s wand", dn);
- break;
- case SPBOOK_CLASS:
- if (typ == SPE_NOVEL) { /* 3.6 tribute */
- if (!dknown)
- Strcpy(buf, "book");
- else if (nn)
- Strcpy(buf, actualn);
- else if (un)
- Sprintf(buf, "novel called %s", un);
- else
- Sprintf(buf, "%s book", dn);
- break;
- /* end of tribute */
- } else if (!dknown) {
- Strcpy(buf, "spellbook");
- } else if (nn) {
- if (typ != SPE_BOOK_OF_THE_DEAD)
- Strcpy(buf, "spellbook of ");
- Strcat(buf, actualn);
- } else if (un) {
- Sprintf(buf, "spellbook called %s", un);
- } else
- Sprintf(buf, "%s spellbook", dn);
- break;
- case RING_CLASS:
- if (!dknown)
- Strcpy(buf, "ring");
- else if (nn)
- Sprintf(buf, "ring of %s", actualn);
- else if (un)
- Sprintf(buf, "ring called %s", un);
- else
- Sprintf(buf, "%s ring", dn);
- break;
- case GEM_CLASS: {
- const char *rock = (ocl->oc_material == MINERAL) ? "stone" : "gem";
-
- if (!dknown) {
- Strcpy(buf, rock);
- } else if (!nn) {
- if (un)
- Sprintf(buf, "%s called %s", rock, un);
- else
- Sprintf(buf, "%s %s", dn, rock);
- } else {
- Strcpy(buf, actualn);
- if (GemStone(typ))
- Strcat(buf, " stone");
- }
- break;
- }
- default:
- Sprintf(buf, "glorkum %d %d %d", obj->oclass, typ, obj->spe);
- }
- if (pluralize)
- Strcpy(buf, makeplural(buf));
-
- if (obj->otyp == T_SHIRT && program_state.gameover) {
- char tmpbuf[BUFSZ];
-
- Sprintf(eos(buf), " with text \"%s\"", tshirt_text(obj, tmpbuf));
- }
-
- if (has_oname(obj) && dknown) {
- Strcat(buf, " named ");
- nameit:
- Strcat(buf, ONAME(obj));
- }
-
- if (!strncmpi(buf, "the ", 4))
- buf += 4;
- return buf;
-}
-
-/* similar to simple_typename but minimal_xname operates on a particular
- object rather than its general type; it formats the most basic info:
- potion -- if description not known
- brown potion -- if oc_name_known not set
- potion of object detection -- if discovered
- */
-STATIC_OVL char *
-minimal_xname(obj)
-struct obj *obj;
-{
- char *bufp;
- struct obj bareobj;
- struct objclass saveobcls;
- int otyp = obj->otyp;
-
- /* suppress user-supplied name */
- saveobcls.oc_uname = objects[otyp].oc_uname;
- objects[otyp].oc_uname = 0;
- /* suppress actual name if object's description is unknown */
- saveobcls.oc_name_known = objects[otyp].oc_name_known;
- if (!obj->dknown)
- objects[otyp].oc_name_known = 0;
-
- /* caveat: this makes a lot of assumptions about which fields
- are required in order for xname() to yield a sensible result */
- bareobj = zeroobj;
- bareobj.otyp = otyp;
- bareobj.oclass = obj->oclass;
- bareobj.dknown = obj->dknown;
- /* suppress known except for amulets (needed for fakes and real A-of-Y) */
- bareobj.known = (obj->oclass == AMULET_CLASS)
- ? obj->known
- /* default is "on" for types which don't use it */
- : !objects[otyp].oc_uses_known;
- bareobj.quan = 1L; /* don't want plural */
- bareobj.corpsenm = NON_PM; /* suppress statue and figurine details */
- /* but suppressing fruit details leads to "bad fruit #0"
- [perhaps we should force "slime mold" rather than use xname?] */
- if (obj->otyp == SLIME_MOLD)
- bareobj.spe = obj->spe;
-
- bufp = distant_name(&bareobj, xname); /* xname(&bareobj) */
- if (!strncmp(bufp, "uncursed ", 9))
- bufp += 9; /* Role_if(PM_PRIEST) */
-
- objects[otyp].oc_uname = saveobcls.oc_uname;
- objects[otyp].oc_name_known = saveobcls.oc_name_known;
- return bufp;
-}
-
-/* xname() output augmented for multishot missile feedback */
-char *
-mshot_xname(obj)
-struct obj *obj;
-{
- char tmpbuf[BUFSZ];
- char *onm = xname(obj);
-
- if (m_shot.n > 1 && m_shot.o == obj->otyp) {
- /* "the Nth arrow"; value will eventually be passed to an() or
- The(), both of which correctly handle this "the " prefix */
- Sprintf(tmpbuf, "the %d%s ", m_shot.i, ordin(m_shot.i));
- onm = strprepend(onm, tmpbuf);
- }
- return onm;
-}
-
-/* used for naming "the unique_item" instead of "a unique_item" */
-boolean
-the_unique_obj(obj)
-struct obj *obj;
-{
- boolean known = (obj->known || iflags.override_ID);
-
- if (!obj->dknown && !iflags.override_ID)
- return FALSE;
- else if (obj->otyp == FAKE_AMULET_OF_YENDOR && !known)
- return TRUE; /* lie */
- else
- return (boolean) (objects[obj->otyp].oc_unique
- && (known || obj->otyp == AMULET_OF_YENDOR));
-}
-
-/* should monster type be prefixed with "the"? (mostly used for corpses) */
-boolean
-the_unique_pm(ptr)
-struct permonst *ptr;
-{
- boolean uniq;
-
- /* even though monsters with personal names are unique, we want to
- describe them as "Name" rather than "the Name" */
- if (type_is_pname(ptr))
- return FALSE;
-
- uniq = (ptr->geno & G_UNIQ) ? TRUE : FALSE;
- /* high priest is unique if it includes "of <deity>", otherwise not
- (caller needs to handle the 1st possibility; we assume the 2nd);
- worm tail should be irrelevant but is included for completeness */
- if (ptr == &mons[PM_HIGH_PRIEST] || ptr == &mons[PM_LONG_WORM_TAIL])
- uniq = FALSE;
- /* Wizard no longer needs this; he's flagged as unique these days */
- if (ptr == &mons[PM_WIZARD_OF_YENDOR])
- uniq = TRUE;
- return uniq;
-}
-
-STATIC_OVL void
-add_erosion_words(obj, prefix)
-struct obj *obj;
-char *prefix;
-{
- boolean iscrys = (obj->otyp == CRYSKNIFE);
- boolean rknown;
-
- rknown = (iflags.override_ID == 0) ? obj->rknown : TRUE;
-
- if (!is_damageable(obj) && !iscrys)
- return;
-
- /* The only cases where any of these bits do double duty are for
- * rotted food and diluted potions, which are all not is_damageable().
- */
- if (obj->oeroded && !iscrys) {
- switch (obj->oeroded) {
- case 2:
- Strcat(prefix, "very ");
- break;
- case 3:
- Strcat(prefix, "thoroughly ");
- break;
- }
- Strcat(prefix, is_rustprone(obj) ? "rusty " : "burnt ");
- }
- if (obj->oeroded2 && !iscrys) {
- switch (obj->oeroded2) {
- case 2:
- Strcat(prefix, "very ");
- break;
- case 3:
- Strcat(prefix, "thoroughly ");
- break;
- }
- Strcat(prefix, is_corrodeable(obj) ? "corroded " : "rotted ");
- }
- if (rknown && obj->oerodeproof)
- Strcat(prefix, iscrys
- ? "fixed "
- : is_rustprone(obj)
- ? "rustproof "
- : is_corrodeable(obj)
- ? "corrodeproof " /* "stainless"? */
- : is_flammable(obj)
- ? "fireproof "
- : "");
-}
-
-/* used to prevent rust on items where rust makes no difference */
-boolean
-erosion_matters(obj)
-struct obj *obj;
-{
- switch (obj->oclass) {
- case TOOL_CLASS:
- /* it's possible for a rusty weptool to be polymorphed into some
- non-weptool iron tool, in which case the rust implicitly goes
- away, but it's also possible for it to be polymorphed into a
- non-iron tool, in which case rust also implicitly goes away,
- so there's no particular reason to try to handle the first
- instance differently [this comment belongs in poly_obj()...] */
- return is_weptool(obj) ? TRUE : FALSE;
- case WEAPON_CLASS:
- case ARMOR_CLASS:
- case BALL_CLASS:
- case CHAIN_CLASS:
- return TRUE;
- default:
- break;
- }
- return FALSE;
-}
-
-#define DONAME_WITH_PRICE 1
-#define DONAME_VAGUE_QUAN 2
-
-STATIC_OVL char *
-doname_base(obj, doname_flags)
-struct obj *obj;
-unsigned doname_flags;
-{
- boolean ispoisoned = FALSE,
- with_price = (doname_flags & DONAME_WITH_PRICE) != 0,
- vague_quan = (doname_flags & DONAME_VAGUE_QUAN) != 0;
- boolean known, dknown, cknown, bknown, lknown;
- int omndx = obj->corpsenm;
- char prefix[PREFIX];
- char tmpbuf[PREFIX + 1]; /* for when we have to add something at
- the start of prefix instead of the
- end (Strcat is used on the end) */
- register char *bp = xname(obj);
-
- if (iflags.override_ID) {
- known = dknown = cknown = bknown = lknown = TRUE;
- } else {
- known = obj->known;
- dknown = obj->dknown;
- cknown = obj->cknown;
- bknown = obj->bknown;
- lknown = obj->lknown;
- }
-
- /* When using xname, we want "poisoned arrow", and when using
- * doname, we want "poisoned +0 arrow". This kludge is about the only
- * way to do it, at least until someone overhauls xname() and doname(),
- * combining both into one function taking a parameter.
- */
- /* must check opoisoned--someone can have a weirdly-named fruit */
- if (!strncmp(bp, "poisoned ", 9) && obj->opoisoned) {
- bp += 9;
- ispoisoned = TRUE;
- }
-
- if (obj->quan != 1L) {
- if (dknown || !vague_quan)
- Sprintf(prefix, "%ld ", obj->quan);
- else
- Strcpy(prefix, "some ");
- } else if (obj->otyp == CORPSE) {
- /* skip article prefix for corpses [else corpse_xname()
- would have to be taught how to strip it off again] */
- *prefix = '\0';
- } else if (obj_is_pname(obj) || the_unique_obj(obj)) {
- if (!strncmpi(bp, "the ", 4))
- bp += 4;
- Strcpy(prefix, "the ");
- } else {
- Strcpy(prefix, "a ");
- }
-
- /* "empty" goes at the beginning, but item count goes at the end */
- if (cknown
- /* bag of tricks: include "empty" prefix if it's known to
- be empty but its precise number of charges isn't known
- (when that is known, suffix of "(n:0)" will be appended,
- making the prefix be redundant; note that 'known' flag
- isn't set when emptiness gets discovered because then
- charging magic would yield known number of new charges) */
- && ((obj->otyp == BAG_OF_TRICKS)
- ? (obj->spe == 0 && !obj->known)
- /* not bag of tricks: empty if container which has no contents */
- : ((Is_container(obj) || obj->otyp == STATUE)
- && !Has_contents(obj))))
- Strcat(prefix, "empty ");
-
- if (bknown && obj->oclass != COIN_CLASS
- && (obj->otyp != POT_WATER || !objects[POT_WATER].oc_name_known
- || (!obj->cursed && !obj->blessed))) {
- /* allow 'blessed clear potion' if we don't know it's holy water;
- * always allow "uncursed potion of water"
- */
- if (obj->cursed)
- Strcat(prefix, "cursed ");
- else if (obj->blessed)
- Strcat(prefix, "blessed ");
- else if (!iflags.implicit_uncursed
- /* For most items with charges or +/-, if you know how many
- * charges are left or what the +/- is, then you must have
- * totally identified the item, so "uncursed" is unnecessary,
- * because an identified object not described as "blessed" or
- * "cursed" must be uncursed.
- *
- * If the charges or +/- is not known, "uncursed" must be
- * printed to avoid ambiguity between an item whose curse
- * status is unknown, and an item known to be uncursed.
- */
- || ((!known || !objects[obj->otyp].oc_charged
- || obj->oclass == ARMOR_CLASS
- || obj->oclass == RING_CLASS)
-#ifdef MAIL
- && obj->otyp != SCR_MAIL
-#endif
- && obj->otyp != FAKE_AMULET_OF_YENDOR
- && obj->otyp != AMULET_OF_YENDOR
- && !Role_if(PM_PRIEST)))
- Strcat(prefix, "uncursed ");
- }
-
- if (lknown && Is_box(obj)) {
- if (obj->obroken)
- /* 3.6.0 used an "unlockable" prefix here but that could be
- misunderstood to mean "capable of being unlocked" rather
- than the intended "not capable of being locked" */
- Strcat(bp, " with a broken lock");
- else if (obj->olocked)
- Strcat(prefix, "locked ");
- else
- Strcat(prefix, "unlocked ");
- }
-
- if (obj->greased)
- Strcat(prefix, "greased ");
-
- if (cknown && Has_contents(obj)) {
- /* we count the number of separate stacks, which corresponds
- to the number of inventory slots needed to be able to take
- everything out if no merges occur */
- long itemcount = count_contents(obj, FALSE, FALSE, TRUE);
-
- Sprintf(eos(bp), " containing %ld item%s", itemcount,
- plur(itemcount));
- }
-
- switch (is_weptool(obj) ? WEAPON_CLASS : obj->oclass) {
- case AMULET_CLASS:
- if (obj->owornmask & W_AMUL)
- Strcat(bp, " (being worn)");
- break;
- case ARMOR_CLASS:
- if (obj->owornmask & W_ARMOR)
- Strcat(bp, (obj == uskin) ? " (embedded in your skin)"
- : " (being worn)");
- /*FALLTHRU*/
- case WEAPON_CLASS:
- if (ispoisoned)
- Strcat(prefix, "poisoned ");
- add_erosion_words(obj, prefix);
- if (known) {
- Strcat(prefix, sitoa(obj->spe));
- Strcat(prefix, " ");
- }
- break;
- case TOOL_CLASS:
- if (obj->owornmask & (W_TOOL | W_SADDLE)) { /* blindfold */
- Strcat(bp, " (being worn)");
- break;
- }
- if (obj->otyp == LEASH && obj->leashmon != 0) {
- struct monst *mlsh = find_mid(obj->leashmon, FM_FMON);
-
- if (!mlsh) {
- impossible("leashed monster not on this level");
- obj->leashmon = 0;
- } else {
- Sprintf(eos(bp), " (attached to %s)",
- a_monnam(mlsh));
- }
- break;
- }
- if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
- if (!obj->spe)
- Strcpy(tmpbuf, "no");
- else
- Sprintf(tmpbuf, "%d", obj->spe);
- Sprintf(eos(bp), " (%s candle%s%s)", tmpbuf, plur(obj->spe),
- !obj->lamplit ? " attached" : ", lit");
- break;
- } else if (obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP
- || obj->otyp == BRASS_LANTERN || Is_candle(obj)) {
- if (Is_candle(obj)
- && obj->age < 20L * (long) objects[obj->otyp].oc_cost)
- Strcat(prefix, "partly used ");
- if (obj->lamplit)
- Strcat(bp, " (lit)");
- break;
- }
- if (objects[obj->otyp].oc_charged)
- goto charges;
- break;
- case WAND_CLASS:
- charges:
- if (known)
- Sprintf(eos(bp), " (%d:%d)", (int) obj->recharged, obj->spe);
- break;
- case POTION_CLASS:
- if (obj->otyp == POT_OIL && obj->lamplit)
- Strcat(bp, " (lit)");
- break;
- case RING_CLASS:
- ring:
- if (obj->owornmask & W_RINGR)
- Strcat(bp, " (on right ");
- if (obj->owornmask & W_RINGL)
- Strcat(bp, " (on left ");
- if (obj->owornmask & W_RING) {
- Strcat(bp, body_part(HAND));
- Strcat(bp, ")");
- }
- if (known && objects[obj->otyp].oc_charged) {
- Strcat(prefix, sitoa(obj->spe));
- Strcat(prefix, " ");
- }
- break;
- case FOOD_CLASS:
- if (obj->oeaten)
- Strcat(prefix, "partly eaten ");
- if (obj->otyp == CORPSE) {
- /* (quan == 1) => want corpse_xname() to supply article,
- (quan != 1) => already have count or "some" as prefix;
- "corpse" is already in the buffer returned by xname() */
- unsigned cxarg = (((obj->quan != 1L) ? 0 : CXN_ARTICLE)
- | CXN_NOCORPSE);
- char *cxstr = corpse_xname(obj, prefix, cxarg);
-
- Sprintf(prefix, "%s ", cxstr);
- /* avoid having doname(corpse) consume an extra obuf */
- releaseobuf(cxstr);
- } else if (obj->otyp == EGG) {
-#if 0 /* corpses don't tell if they're stale either */
- if (known && stale_egg(obj))
- Strcat(prefix, "stale ");
-#endif
- if (omndx >= LOW_PM
- && (known || (mvitals[omndx].mvflags & MV_KNOWS_EGG))) {
- Strcat(prefix, mons[omndx].mname);
- Strcat(prefix, " ");
- if (obj->spe)
- Strcat(bp, " (laid by you)");
- }
- }
- if (obj->otyp == MEAT_RING)
- goto ring;
- break;
- case BALL_CLASS:
- case CHAIN_CLASS:
- add_erosion_words(obj, prefix);
- if (obj->owornmask & W_BALL)
- Strcat(bp, " (chained to you)");
- break;
- }
-
- if ((obj->owornmask & W_WEP) && !mrg_to_wielded) {
- if (obj->quan != 1L) {
- Strcat(bp, " (wielded)");
- } else {
- const char *hand_s = body_part(HAND);
-
- if (bimanual(obj))
- hand_s = makeplural(hand_s);
- Sprintf(eos(bp), " (weapon in %s)", hand_s);
-
- if (warn_obj_cnt && obj == uwep && (EWarn_of_mon & W_WEP) != 0L) {
- /* presumably can be felt when blind */
- Strcat(bp, " (glowing");
- if (!Blind)
- Sprintf(eos(bp), " %s", glow_color(obj->oartifact));
- Strcat(bp, ")");
- }
- }
- }
- if (obj->owornmask & W_SWAPWEP) {
- if (u.twoweap)
- Sprintf(eos(bp), " (wielded in other %s)", body_part(HAND));
- else
- Strcat(bp, " (alternate weapon; not wielded)");
- }
- if (obj->owornmask & W_QUIVER) {
- switch (obj->oclass) {
- case WEAPON_CLASS:
- if (is_ammo(obj)) {
- if (objects[obj->otyp].oc_skill == -P_BOW) {
- /* Ammo for a bow */
- Strcat(bp, " (in quiver)");
- break;
- } else {
- /* Ammo not for a bow */
- Strcat(bp, " (in quiver pouch)");
- break;
- }
- } else {
- /* Weapons not considered ammo */
- Strcat(bp, " (at the ready)");
- break;
- }
- /* Small things and ammo not for a bow */
- case RING_CLASS:
- case AMULET_CLASS:
- case WAND_CLASS:
- case COIN_CLASS:
- case GEM_CLASS:
- Strcat(bp, " (in quiver pouch)");
- break;
- default: /* odd things */
- Strcat(bp, " (at the ready)");
- }
- }
- if (!iflags.suppress_price && is_unpaid(obj)) {
- long quotedprice = unpaid_cost(obj, TRUE);
-
- Sprintf(eos(bp), " (%s, %ld %s)",
- obj->unpaid ? "unpaid" : "contents",
- quotedprice, currency(quotedprice));
- } else if (with_price) {
- long price = get_cost_of_shop_item(obj);
-
- if (price > 0)
- Sprintf(eos(bp), " (%ld %s)", price, currency(price));
- }
- if (!strncmp(prefix, "a ", 2)
- && index(vowels, *(prefix + 2) ? *(prefix + 2) : *bp)
- && (*(prefix + 2)
- || (strncmp(bp, "uranium", 7) && strncmp(bp, "unicorn", 7)
- && strncmp(bp, "eucalyptus", 10)))) {
- Strcpy(tmpbuf, prefix);
- Strcpy(prefix, "an ");
- Strcpy(prefix + 3, tmpbuf + 2);
- }
-
- /* show weight for items (debug tourist info)
- * aum is stolen from Crawl's "Arbitrary Unit of Measure" */
- if (wizard && iflags.wizweight) {
- Sprintf(eos(bp), " (%d aum)", obj->owt);
- }
- bp = strprepend(bp, prefix);
- return bp;
-}
-
-char *
-doname(obj)
-struct obj *obj;
-{
- return doname_base(obj, (unsigned) 0);
-}
-
-/* Name of object including price. */
-char *
-doname_with_price(obj)
-struct obj *obj;
-{
- return doname_base(obj, DONAME_WITH_PRICE);
-}
-
-/* "some" instead of precise quantity if obj->dknown not set */
-char *
-doname_vague_quan(obj)
-struct obj *obj;
-{
- /* Used by farlook.
- * If it hasn't been seen up close and quantity is more than one,
- * use "some" instead of the quantity: "some gold pieces" rather
- * than "25 gold pieces". This is suboptimal, to put it mildly,
- * because lookhere and pickup report the precise amount.
- * Picking the item up while blind also shows the precise amount
- * for inventory display, then dropping it while still blind leaves
- * obj->dknown unset so the count reverts to "some" for farlook.
- *
- * TODO: add obj->qknown flag for 'quantity known' on stackable
- * items; it could overlay obj->cknown since no containers stack.
- */
- return doname_base(obj, DONAME_VAGUE_QUAN);
-}
-
-/* used from invent.c */
-boolean
-not_fully_identified(otmp)
-struct obj *otmp;
-{
- /* gold doesn't have any interesting attributes [yet?] */
- if (otmp->oclass == COIN_CLASS)
- return FALSE; /* always fully ID'd */
- /* check fundamental ID hallmarks first */
- if (!otmp->known || !otmp->dknown
-#ifdef MAIL
- || (!otmp->bknown && otmp->otyp != SCR_MAIL)
-#else
- || !otmp->bknown
-#endif
- || !objects[otmp->otyp].oc_name_known)
- return TRUE;
- if ((!otmp->cknown && (Is_container(otmp) || otmp->otyp == STATUE))
- || (!otmp->lknown && Is_box(otmp)))
- return TRUE;
- if (otmp->oartifact && undiscovered_artifact(otmp->oartifact))
- return TRUE;
- /* otmp->rknown is the only item of interest if we reach here */
- /*
- * Note: if a revision ever allows scrolls to become fireproof or
- * rings to become shockproof, this checking will need to be revised.
- * `rknown' ID only matters if xname() will provide the info about it.
- */
- if (otmp->rknown
- || (otmp->oclass != ARMOR_CLASS && otmp->oclass != WEAPON_CLASS
- && !is_weptool(otmp) /* (redundant) */
- && otmp->oclass != BALL_CLASS)) /* (useless) */
- return FALSE;
- else /* lack of `rknown' only matters for vulnerable objects */
- return (boolean) (is_rustprone(otmp) || is_corrodeable(otmp)
- || is_flammable(otmp));
-}
-
-/* format a corpse name (xname() omits monster type; doname() calls us);
- eatcorpse() also uses us for death reason when eating tainted glob */
-char *
-corpse_xname(otmp, adjective, cxn_flags)
-struct obj *otmp;
-const char *adjective;
-unsigned cxn_flags; /* bitmask of CXN_xxx values */
-{
- char *nambuf = nextobuf();
- int omndx = otmp->corpsenm;
- boolean ignore_quan = (cxn_flags & CXN_SINGULAR) != 0,
- /* suppress "the" from "the unique monster corpse" */
- no_prefix = (cxn_flags & CXN_NO_PFX) != 0,
- /* include "the" for "the woodchuck corpse */
- the_prefix = (cxn_flags & CXN_PFX_THE) != 0,
- /* include "an" for "an ogre corpse */
- any_prefix = (cxn_flags & CXN_ARTICLE) != 0,
- /* leave off suffix (do_name() appends "corpse" itself) */
- omit_corpse = (cxn_flags & CXN_NOCORPSE) != 0,
- possessive = FALSE,
- glob = (otmp->otyp != CORPSE && otmp->globby);
- const char *mname;
-
- if (glob) {
- mname = OBJ_NAME(objects[otmp->otyp]); /* "glob of <monster>" */
- } else if (omndx == NON_PM) { /* paranoia */
- mname = "thing";
- /* [Possible enhancement: check whether corpse has monster traits
- attached in order to use priestname() for priests and minions.] */
- } else if (omndx == PM_ALIGNED_PRIEST) {
- /* avoid "aligned priest"; it just exposes internal details */
- mname = "priest";
- } else {
- mname = mons[omndx].mname;
- if (the_unique_pm(&mons[omndx]) || type_is_pname(&mons[omndx])) {
- mname = s_suffix(mname);
- possessive = TRUE;
- /* don't precede personal name like "Medusa" with an article */
- if (type_is_pname(&mons[omndx]))
- no_prefix = TRUE;
- /* always precede non-personal unique monster name like
- "Oracle" with "the" unless explicitly overridden */
- else if (the_unique_pm(&mons[omndx]) && !no_prefix)
- the_prefix = TRUE;
- }
- }
- if (no_prefix)
- the_prefix = any_prefix = FALSE;
- else if (the_prefix)
- any_prefix = FALSE; /* mutually exclusive */
-
- *nambuf = '\0';
- /* can't use the() the way we use an() below because any capitalized
- Name causes it to assume a personal name and return Name as-is;
- that's usually the behavior wanted, but here we need to force "the"
- to precede capitalized unique monsters (pnames are handled above) */
- if (the_prefix)
- Strcat(nambuf, "the ");
-
- if (!adjective || !*adjective) {
- /* normal case: newt corpse */
- Strcat(nambuf, mname);
- } else {
- /* adjective positioning depends upon format of monster name */
- if (possessive) /* Medusa's cursed partly eaten corpse */
- Sprintf(eos(nambuf), "%s %s", mname, adjective);
- else /* cursed partly eaten troll corpse */
- Sprintf(eos(nambuf), "%s %s", adjective, mname);
- /* in case adjective has a trailing space, squeeze it out */
- mungspaces(nambuf);
- /* doname() might include a count in the adjective argument;
- if so, don't prepend an article */
- if (digit(*adjective))
- any_prefix = FALSE;
- }
-
- if (glob) {
- ; /* omit_corpse doesn't apply; quantity is always 1 */
- } else if (!omit_corpse) {
- Strcat(nambuf, " corpse");
- /* makeplural(nambuf) => append "s" to "corpse" */
- if (otmp->quan > 1L && !ignore_quan) {
- Strcat(nambuf, "s");
- any_prefix = FALSE; /* avoid "a newt corpses" */
- }
- }
-
- /* it's safe to overwrite our nambuf after an() has copied
- its old value into another buffer */
- if (any_prefix)
- Strcpy(nambuf, an(nambuf));
-
- return nambuf;
-}
-
-/* xname doesn't include monster type for "corpse"; cxname does */
-char *
-cxname(obj)
-struct obj *obj;
-{
- if (obj->otyp == CORPSE)
- return corpse_xname(obj, (const char *) 0, CXN_NORMAL);
- return xname(obj);
-}
-
-/* like cxname, but ignores quantity */
-char *
-cxname_singular(obj)
-struct obj *obj;
-{
- if (obj->otyp == CORPSE)
- return corpse_xname(obj, (const char *) 0, CXN_SINGULAR);
- return xname_flags(obj, CXN_SINGULAR);
-}
-
-/* treat an object as fully ID'd when it might be used as reason for death */
-char *
-killer_xname(obj)
-struct obj *obj;
-{
- struct obj save_obj;
- unsigned save_ocknown;
- char *buf, *save_ocuname, *save_oname = (char *) 0;
-
- /* bypass object twiddling for artifacts */
- if (obj->oartifact)
- return bare_artifactname(obj);
-
- /* remember original settings for core of the object;
- oextra structs other than oname don't matter here--since they
- aren't modified they don't need to be saved and restored */
- save_obj = *obj;
- if (has_oname(obj))
- save_oname = ONAME(obj);
-
- /* killer name should be more specific than general xname; however, exact
- info like blessed/cursed and rustproof makes things be too verbose */
- obj->known = obj->dknown = 1;
- obj->bknown = obj->rknown = obj->greased = 0;
- /* if character is a priest[ess], bknown will get toggled back on */
- if (obj->otyp != POT_WATER)
- obj->blessed = obj->cursed = 0;
- else
- obj->bknown = 1; /* describe holy/unholy water as such */
- /* "killed by poisoned <obj>" would be misleading when poison is
- not the cause of death and "poisoned by poisoned <obj>" would
- be redundant when it is, so suppress "poisoned" prefix */
- obj->opoisoned = 0;
- /* strip user-supplied name; artifacts keep theirs */
- if (!obj->oartifact && save_oname)
- ONAME(obj) = (char *) 0;
- /* temporarily identify the type of object */
- save_ocknown = objects[obj->otyp].oc_name_known;
- objects[obj->otyp].oc_name_known = 1;
- save_ocuname = objects[obj->otyp].oc_uname;
- objects[obj->otyp].oc_uname = 0; /* avoid "foo called bar" */
-
- /* format the object */
- if (obj->otyp == CORPSE) {
- buf = nextobuf();
- Strcpy(buf, corpse_xname(obj, (const char *) 0, CXN_NORMAL));
- } else if (obj->otyp == SLIME_MOLD) {
- /* concession to "most unique deaths competition" in the annual
- devnull tournament, suppress player supplied fruit names because
- those can be used to fake other objects and dungeon features */
- buf = nextobuf();
- Sprintf(buf, "deadly slime mold%s", plur(obj->quan));
- } else {
- buf = xname(obj);
- }
- /* apply an article if appropriate; caller should always use KILLED_BY */
- if (obj->quan == 1L && !strstri(buf, "'s ") && !strstri(buf, "s' "))
- buf = (obj_is_pname(obj) || the_unique_obj(obj)) ? the(buf) : an(buf);
-
- objects[obj->otyp].oc_name_known = save_ocknown;
- objects[obj->otyp].oc_uname = save_ocuname;
- *obj = save_obj; /* restore object's core settings */
- if (!obj->oartifact && save_oname)
- ONAME(obj) = save_oname;
-
- return buf;
-}
-
-/* xname,doname,&c with long results reformatted to omit some stuff */
-char *
-short_oname(obj, func, altfunc, lenlimit)
-struct obj *obj;
-char *FDECL((*func), (OBJ_P)), /* main formatting routine */
- *FDECL((*altfunc), (OBJ_P)); /* alternate for shortest result */
-unsigned lenlimit;
-{
- struct obj save_obj;
- char unamebuf[12], onamebuf[12], *save_oname, *save_uname, *outbuf;
-
- outbuf = (*func)(obj);
- if ((unsigned) strlen(outbuf) <= lenlimit)
- return outbuf;
-
- /* shorten called string to fairly small amount */
- save_uname = objects[obj->otyp].oc_uname;
- if (save_uname && strlen(save_uname) >= sizeof unamebuf) {
- (void) strncpy(unamebuf, save_uname, sizeof unamebuf - 4);
- Strcpy(unamebuf + sizeof unamebuf - 4, "...");
- objects[obj->otyp].oc_uname = unamebuf;
- releaseobuf(outbuf);
- outbuf = (*func)(obj);
- objects[obj->otyp].oc_uname = save_uname; /* restore called string */
- if ((unsigned) strlen(outbuf) <= lenlimit)
- return outbuf;
- }
-
- /* shorten named string to fairly small amount */
- save_oname = has_oname(obj) ? ONAME(obj) : 0;
- if (save_oname && strlen(save_oname) >= sizeof onamebuf) {
- (void) strncpy(onamebuf, save_oname, sizeof onamebuf - 4);
- Strcpy(onamebuf + sizeof onamebuf - 4, "...");
- ONAME(obj) = onamebuf;
- releaseobuf(outbuf);
- outbuf = (*func)(obj);
- ONAME(obj) = save_oname; /* restore named string */
- if ((unsigned) strlen(outbuf) <= lenlimit)
- return outbuf;
- }
-
- /* shorten both called and named strings;
- unamebuf and onamebuf have both already been populated */
- if (save_uname && strlen(save_uname) >= sizeof unamebuf && save_oname
- && strlen(save_oname) >= sizeof onamebuf) {
- objects[obj->otyp].oc_uname = unamebuf;
- ONAME(obj) = onamebuf;
- releaseobuf(outbuf);
- outbuf = (*func)(obj);
- if ((unsigned) strlen(outbuf) <= lenlimit) {
- objects[obj->otyp].oc_uname = save_uname;
- ONAME(obj) = save_oname;
- return outbuf;
- }
- }
-
- /* still long; strip several name-lengthening attributes;
- called and named strings are still in truncated form */
- save_obj = *obj;
- obj->bknown = obj->rknown = obj->greased = 0;
- obj->oeroded = obj->oeroded2 = 0;
- releaseobuf(outbuf);
- outbuf = (*func)(obj);
- if (altfunc && (unsigned) strlen(outbuf) > lenlimit) {
- /* still long; use the alternate function (usually one of
- the jackets around minimal_xname()) */
- releaseobuf(outbuf);
- outbuf = (*altfunc)(obj);
- }
- /* restore the object */
- *obj = save_obj;
- if (save_oname)
- ONAME(obj) = save_oname;
- if (save_uname)
- objects[obj->otyp].oc_uname = save_uname;
-
- /* use whatever we've got, whether it's too long or not */
- return outbuf;
-}
-
-/*
- * Used if only one of a collection of objects is named (e.g. in eat.c).
- */
-const char *
-singular(otmp, func)
-register struct obj *otmp;
-char *FDECL((*func), (OBJ_P));
-{
- long savequan;
- char *nam;
-
- /* using xname for corpses does not give the monster type */
- if (otmp->otyp == CORPSE && func == xname)
- func = cxname;
-
- savequan = otmp->quan;
- otmp->quan = 1L;
- nam = (*func)(otmp);
- otmp->quan = savequan;
- return nam;
-}
-
-char *
-an(str)
-register const char *str;
-{
- char *buf = nextobuf();
-
- buf[0] = '\0';
-
- if (strncmpi(str, "the ", 4) && strcmp(str, "molten lava")
- && strcmp(str, "iron bars") && strcmp(str, "ice")) {
- if (index(vowels, *str) && strncmp(str, "one-", 4)
- && strncmp(str, "useful", 6) && strncmp(str, "unicorn", 7)
- && strncmp(str, "uranium", 7) && strncmp(str, "eucalyptus", 10))
- Strcpy(buf, "an ");
- else
- Strcpy(buf, "a ");
- }
-
- Strcat(buf, str);
- return buf;
-}
-
-char *
-An(str)
-const char *str;
-{
- char *tmp = an(str);
-
- *tmp = highc(*tmp);
- return tmp;
-}
-
-/*
- * Prepend "the" if necessary; assumes str is a subject derived from xname.
- * Use type_is_pname() for monster names, not the(). the() is idempotent.
- */
-char *
-the(str)
-const char *str;
-{
- char *buf = nextobuf();
- boolean insert_the = FALSE;
-
- if (!strncmpi(str, "the ", 4)) {
- buf[0] = lowc(*str);
- Strcpy(&buf[1], str + 1);
- return buf;
- } else if (*str < 'A' || *str > 'Z'
- /* treat named fruit as not a proper name, even if player
- has assigned a capitalized proper name as his/her fruit */
- || fruit_from_name(str, TRUE, (int *) 0)) {
- /* not a proper name, needs an article */
- insert_the = TRUE;
- } else {
- /* Probably a proper name, might not need an article */
- register char *tmp, *named, *called;
- int l;
-
- /* some objects have capitalized adjectives in their names */
- if (((tmp = rindex(str, ' ')) != 0 || (tmp = rindex(str, '-')) != 0)
- && (tmp[1] < 'A' || tmp[1] > 'Z')) {
- insert_the = TRUE;
- } else if (tmp && index(str, ' ') < tmp) { /* has spaces */
- /* it needs an article if the name contains "of" */
- tmp = strstri(str, " of ");
- named = strstri(str, " named ");
- called = strstri(str, " called ");
- if (called && (!named || called < named))
- named = called;
-
- if (tmp && (!named || tmp < named)) /* found an "of" */
- insert_the = TRUE;
- /* stupid special case: lacks "of" but needs "the" */
- else if (!named && (l = strlen(str)) >= 31
- && !strcmp(&str[l - 31],
- "Platinum Yendorian Express Card"))
- insert_the = TRUE;
- }
- }
- if (insert_the)
- Strcpy(buf, "the ");
- else
- buf[0] = '\0';
- Strcat(buf, str);
-
- return buf;
-}
-
-char *
-The(str)
-const char *str;
-{
- char *tmp = the(str);
-
- *tmp = highc(*tmp);
- return tmp;
-}
-
-/* returns "count cxname(otmp)" or just cxname(otmp) if count == 1 */
-char *
-aobjnam(otmp, verb)
-struct obj *otmp;
-const char *verb;
-{
- char prefix[PREFIX];
- char *bp = cxname(otmp);
-
- if (otmp->quan != 1L) {
- Sprintf(prefix, "%ld ", otmp->quan);
- bp = strprepend(bp, prefix);
- }
- if (verb) {
- Strcat(bp, " ");
- Strcat(bp, otense(otmp, verb));
- }
- return bp;
-}
-
-/* combine yname and aobjnam eg "your count cxname(otmp)" */
-char *
-yobjnam(obj, verb)
-struct obj *obj;
-const char *verb;
-{
- char *s = aobjnam(obj, verb);
-
- /* leave off "your" for most of your artifacts, but prepend
- * "your" for unique objects and "foo of bar" quest artifacts */
- if (!carried(obj) || !obj_is_pname(obj)
- || obj->oartifact >= ART_ORB_OF_DETECTION) {
- char *outbuf = shk_your(nextobuf(), obj);
- int space_left = BUFSZ - 1 - strlen(outbuf);
-
- s = strncat(outbuf, s, space_left);
- }
- return s;
-}
-
-/* combine Yname2 and aobjnam eg "Your count cxname(otmp)" */
-char *
-Yobjnam2(obj, verb)
-struct obj *obj;
-const char *verb;
-{
- register char *s = yobjnam(obj, verb);
-
- *s = highc(*s);
- return s;
-}
-
-/* like aobjnam, but prepend "The", not count, and use xname */
-char *
-Tobjnam(otmp, verb)
-struct obj *otmp;
-const char *verb;
-{
- char *bp = The(xname(otmp));
-
- if (verb) {
- Strcat(bp, " ");
- Strcat(bp, otense(otmp, verb));
- }
- return bp;
-}
-
-/* capitalized variant of doname() */
-char *
-Doname2(obj)
-struct obj *obj;
-{
- char *s = doname(obj);
-
- *s = highc(*s);
- return s;
-}
-
-/* returns "[your ]xname(obj)" or "Foobar's xname(obj)" or "the xname(obj)" */
-char *
-yname(obj)
-struct obj *obj;
-{
- char *s = cxname(obj);
-
- /* leave off "your" for most of your artifacts, but prepend
- * "your" for unique objects and "foo of bar" quest artifacts */
- if (!carried(obj) || !obj_is_pname(obj)
- || obj->oartifact >= ART_ORB_OF_DETECTION) {
- char *outbuf = shk_your(nextobuf(), obj);
- int space_left = BUFSZ - 1 - strlen(outbuf);
-
- s = strncat(outbuf, s, space_left);
- }
-
- return s;
-}
-
-/* capitalized variant of yname() */
-char *
-Yname2(obj)
-struct obj *obj;
-{
- char *s = yname(obj);
-
- *s = highc(*s);
- return s;
-}
-
-/* returns "your minimal_xname(obj)"
- * or "Foobar's minimal_xname(obj)"
- * or "the minimal_xname(obj)"
- */
-char *
-ysimple_name(obj)
-struct obj *obj;
-{
- char *outbuf = nextobuf();
- char *s = shk_your(outbuf, obj); /* assert( s == outbuf ); */
- int space_left = BUFSZ - 1 - strlen(s);
-
- return strncat(s, minimal_xname(obj), space_left);
-}
-
-/* capitalized variant of ysimple_name() */
-char *
-Ysimple_name2(obj)
-struct obj *obj;
-{
- char *s = ysimple_name(obj);
-
- *s = highc(*s);
- return s;
-}
-
-/* "scroll" or "scrolls" */
-char *
-simpleonames(obj)
-struct obj *obj;
-{
- char *simpleoname = minimal_xname(obj);
-
- if (obj->quan != 1L)
- simpleoname = makeplural(simpleoname);
- return simpleoname;
-}
-
-/* "a scroll" or "scrolls"; "a silver bell" or "the Bell of Opening" */
-char *
-ansimpleoname(obj)
-struct obj *obj;
-{
- char *simpleoname = simpleonames(obj);
- int otyp = obj->otyp;
-
- /* prefix with "the" if a unique item, or a fake one imitating same,
- has been formatted with its actual name (we let typename() handle
- any `known' and `dknown' checking necessary) */
- if (otyp == FAKE_AMULET_OF_YENDOR)
- otyp = AMULET_OF_YENDOR;
- if (objects[otyp].oc_unique
- && !strcmp(simpleoname, OBJ_NAME(objects[otyp])))
- return the(simpleoname);
-
- /* simpleoname is singular if quan==1, plural otherwise */
- if (obj->quan == 1L)
- simpleoname = an(simpleoname);
- return simpleoname;
-}
-
-/* "the scroll" or "the scrolls" */
-char *
-thesimpleoname(obj)
-struct obj *obj;
-{
- char *simpleoname = simpleonames(obj);
-
- return the(simpleoname);
-}
-
-/* artifact's name without any object type or known/dknown/&c feedback */
-char *
-bare_artifactname(obj)
-struct obj *obj;
-{
- char *outbuf;
-
- if (obj->oartifact) {
- outbuf = nextobuf();
- Strcpy(outbuf, artiname(obj->oartifact));
- if (!strncmp(outbuf, "The ", 4))
- outbuf[0] = lowc(outbuf[0]);
- } else {
- outbuf = xname(obj);
- }
- return outbuf;
-}
-
-static const char *wrp[] = {
- "wand", "ring", "potion", "scroll", "gem",
- "amulet", "spellbook", "spell book",
- /* for non-specific wishes */
- "weapon", "armor", "tool", "food", "comestible",
-};
-static const char wrpsym[] = { WAND_CLASS, RING_CLASS, POTION_CLASS,
- SCROLL_CLASS, GEM_CLASS, AMULET_CLASS,
- SPBOOK_CLASS, SPBOOK_CLASS, WEAPON_CLASS,
- ARMOR_CLASS, TOOL_CLASS, FOOD_CLASS,
- FOOD_CLASS };
-
-/* return form of the verb (input plural) if xname(otmp) were the subject */
-char *
-otense(otmp, verb)
-struct obj *otmp;
-const char *verb;
-{
- char *buf;
-
- /*
- * verb is given in plural (without trailing s). Return as input
- * if the result of xname(otmp) would be plural. Don't bother
- * recomputing xname(otmp) at this time.
- */
- if (!is_plural(otmp))
- return vtense((char *) 0, verb);
-
- buf = nextobuf();
- Strcpy(buf, verb);
- return buf;
-}
-
-/* various singular words that vtense would otherwise categorize as plural;
- also used by makesingular() to catch some special cases */
-static const char *const special_subjs[] = {
- "erinys", "manes", /* this one is ambiguous */
- "Cyclops", "Hippocrates", "Pelias", "aklys",
- "amnesia", "detect monsters", "paralysis", "shape changers",
- "nemesis", 0
- /* note: "detect monsters" and "shape changers" are normally
- caught via "<something>(s) of <whatever>", but they can be
- wished for using the shorter form, so we include them here
- to accommodate usage by makesingular during wishing */
-};
-
-/* return form of the verb (input plural) for present tense 3rd person subj */
-char *
-vtense(subj, verb)
-register const char *subj;
-register const char *verb;
-{
- char *buf = nextobuf(), *bspot;
- int len, ltmp;
- const char *sp, *spot;
- const char *const *spec;
-
- /*
- * verb is given in plural (without trailing s). Return as input
- * if subj appears to be plural. Add special cases as necessary.
- * Many hard cases can already be handled by using otense() instead.
- * If this gets much bigger, consider decomposing makeplural.
- * Note: monster names are not expected here (except before corpse).
- *
- * Special case: allow null sobj to get the singular 3rd person
- * present tense form so we don't duplicate this code elsewhere.
- */
- if (subj) {
- if (!strncmpi(subj, "a ", 2) || !strncmpi(subj, "an ", 3))
- goto sing;
- spot = (const char *) 0;
- for (sp = subj; (sp = index(sp, ' ')) != 0; ++sp) {
- if (!strncmpi(sp, " of ", 4) || !strncmpi(sp, " from ", 6)
- || !strncmpi(sp, " called ", 8) || !strncmpi(sp, " named ", 7)
- || !strncmpi(sp, " labeled ", 9)) {
- if (sp != subj)
- spot = sp - 1;
- break;
- }
- }
- len = (int) strlen(subj);
- if (!spot)
- spot = subj + len - 1;
-
- /*
- * plural: anything that ends in 's', but not '*us' or '*ss'.
- * Guess at a few other special cases that makeplural creates.
- */
- if ((lowc(*spot) == 's' && spot != subj
- && !index("us", lowc(*(spot - 1))))
- || !BSTRNCMPI(subj, spot - 3, "eeth", 4)
- || !BSTRNCMPI(subj, spot - 3, "feet", 4)
- || !BSTRNCMPI(subj, spot - 1, "ia", 2)
- || !BSTRNCMPI(subj, spot - 1, "ae", 2)) {
- /* check for special cases to avoid false matches */
- len = (int) (spot - subj) + 1;
- for (spec = special_subjs; *spec; spec++) {
- ltmp = strlen(*spec);
- if (len == ltmp && !strncmpi(*spec, subj, len))
- goto sing;
- /* also check for <prefix><space><special_subj>
- to catch things like "the invisible erinys" */
- if (len > ltmp && *(spot - ltmp) == ' '
- && !strncmpi(*spec, spot - ltmp + 1, ltmp))
- goto sing;
- }
-
- return strcpy(buf, verb);
- }
- /*
- * 3rd person plural doesn't end in telltale 's';
- * 2nd person singular behaves as if plural.
- */
- if (!strcmpi(subj, "they") || !strcmpi(subj, "you"))
- return strcpy(buf, verb);
- }
-
-sing:
- Strcpy(buf, verb);
- len = (int) strlen(buf);
- bspot = buf + len - 1;
-
- if (!strcmpi(buf, "are")) {
- Strcasecpy(buf, "is");
- } else if (!strcmpi(buf, "have")) {
- Strcasecpy(bspot - 1, "s");
- } else if (index("zxs", lowc(*bspot))
- || (len >= 2 && lowc(*bspot) == 'h'
- && index("cs", lowc(*(bspot - 1))))
- || (len == 2 && lowc(*bspot) == 'o')) {
- /* Ends in z, x, s, ch, sh; add an "es" */
- Strcasecpy(bspot + 1, "es");
- } else if (lowc(*bspot) == 'y' && !index(vowels, lowc(*(bspot - 1)))) {
- /* like "y" case in makeplural */
- Strcasecpy(bspot, "ies");
- } else {
- Strcasecpy(bspot + 1, "s");
- }
-
- return buf;
-}
-
-struct sing_plur {
- const char *sing, *plur;
-};
-
-/* word pairs that don't fit into formula-based transformations;
- also some suffices which have very few--often one--matches or
- which aren't systematically reversible (knives, staves) */
-static struct sing_plur one_off[] = {
- { "child",
- "children" }, /* (for wise guys who give their food funny names) */
- { "cubus", "cubi" }, /* in-/suc-cubus */
- { "culus", "culi" }, /* homunculus */
- { "djinni", "djinn" },
- { "erinys", "erinyes" },
- { "foot", "feet" },
- { "fungus", "fungi" },
- { "goose", "geese" },
- { "knife", "knives" },
- { "labrum", "labra" }, /* candelabrum */
- { "louse", "lice" },
- { "mouse", "mice" },
- { "mumak", "mumakil" },
- { "nemesis", "nemeses" },
- { "ovum", "ova" },
- { "ox", "oxen" },
- { "passerby", "passersby" },
- { "rtex", "rtices" }, /* vortex */
- { "serum", "sera" },
- { "staff", "staves" },
- { "tooth", "teeth" },
- { 0, 0 }
-};
-
-static const char *const as_is[] = {
- /* makesingular() leaves these plural due to how they're used */
- "boots", "shoes", "gloves", "lenses", "scales",
- "eyes", "gauntlets", "iron bars",
- /* both singular and plural are spelled the same */
- "bison", "deer", "elk", "fish", "fowl",
- "tuna", "yaki", "-hai", "krill", "manes",
- "moose", "ninja", "sheep", "ronin", "roshi",
- "shito", "tengu", "ki-rin", "Nazgul", "gunyoki",
- "piranha", "samurai", "shuriken", 0,
- /* Note: "fish" and "piranha" are collective plurals, suitable
- for "wiped out all <foo>". For "3 <foo>", they should be
- "fishes" and "piranhas" instead. We settle for collective
- variant instead of attempting to support both. */
-};
-
-/* singularize/pluralize decisions common to both makesingular & makeplural */
-STATIC_OVL boolean
-singplur_lookup(basestr, endstring, to_plural, alt_as_is)
-char *basestr, *endstring; /* base string, pointer to eos(string) */
-boolean to_plural; /* true => makeplural, false => makesingular */
-const char *const *alt_as_is; /* another set like as_is[] */
-{
- const struct sing_plur *sp;
- const char *same, *other, *const *as;
- int al;
-
- for (as = as_is; *as; ++as) {
- al = (int) strlen(*as);
- if (!BSTRCMPI(basestr, endstring - al, *as))
- return TRUE;
- }
- if (alt_as_is) {
- for (as = alt_as_is; *as; ++as) {
- al = (int) strlen(*as);
- if (!BSTRCMPI(basestr, endstring - al, *as))
- return TRUE;
- }
- }
-
- /* avoid false hit on one_off[].plur == "lice" or .sing == "goose";
- if more of these turn up, one_off[] entries will need to flagged
- as to which are whole words and which are matchable as suffices
- then matching in the loop below will end up becoming more complex */
- if (!strcmpi(basestr, "slice")
- || !strcmpi(basestr, "mongoose")) {
- if (to_plural)
- Strcasecpy(endstring, "s");
- return TRUE;
- }
- /* skip "ox" -> "oxen" entry when pluralizing "<something>ox"
- unless it is muskox */
- if (to_plural && strlen(basestr) > 2 && !strcmpi(endstring - 2, "ox")
- && strcmpi(endstring - 6, "muskox")) {
- /* "fox" -> "foxes" */
- Strcasecpy(endstring, "es");
- return TRUE;
- }
- if (to_plural) {
- if (!strcmpi(endstring - 3, "man")
- && badman(basestr, to_plural)) {
- Strcasecpy(endstring, "s");
- return TRUE;
- }
- } else {
- if (!strcmpi(endstring - 3, "men")
- && badman(basestr, to_plural))
- return TRUE;
- }
- for (sp = one_off; sp->sing; sp++) {
- /* check whether endstring already matches */
- same = to_plural ? sp->plur : sp->sing;
- al = (int) strlen(same);
- if (!BSTRCMPI(basestr, endstring - al, same))
- return TRUE; /* use as-is */
- /* check whether it matches the inverse; if so, transform it */
- other = to_plural ? sp->sing : sp->plur;
- al = (int) strlen(other);
- if (!BSTRCMPI(basestr, endstring - al, other)) {
- Strcasecpy(endstring - al, same);
- return TRUE; /* one_off[] transformation */
- }
- }
- return FALSE;
-}
-
-/* searches for common compounds, ex. lump of royal jelly */
-STATIC_OVL char *
-singplur_compound(str)
-char *str;
-{
- /* if new entries are added, be sure to keep compound_start[] in sync */
- static const char *const compounds[] =
- {
- " of ", " labeled ", " called ",
- " named ", " above", /* lurkers above */
- " versus ", " from ", " in ",
- " on ", " a la ", " with", /* " with "? */
- " de ", " d'", " du ",
- "-in-", "-at-", 0
- }, /* list of first characters for all compounds[] entries */
- compound_start[] = " -";
-
- const char *const *cmpd;
- char *p;
-
- for (p = str; *p; ++p) {
- /* substring starting at p can only match if *p is found
- within compound_start[] */
- if (!index(compound_start, *p))
- continue;
-
- /* check current substring against all words in the compound[] list */
- for (cmpd = compounds; *cmpd; ++cmpd)
- if (!strncmpi(p, *cmpd, (int) strlen(*cmpd)))
- return p;
- }
- /* wasn't recognized as a compound phrase */
- return 0;
-}
-
-/* Plural routine; once upon a time it may have been chiefly used for
- * user-defined fruits, but it is now used extensively throughout the
- * program.
- *
- * For fruit, we have to try to account for everything reasonable the
- * player has; something unreasonable can still break the code.
- * However, it's still a lot more accurate than "just add an 's' at the
- * end", which Rogue uses...
- *
- * Also used for plural monster names ("Wiped out all homunculi." or the
- * vanquished monsters list) and body parts. A lot of unique monsters have
- * names which get mangled by makeplural and/or makesingular. They're not
- * genocidable, and vanquished-mon handling does its own special casing
- * (for uniques who've been revived and re-killed), so we don't bother
- * trying to get those right here.
- *
- * Also misused by muse.c to convert 1st person present verbs to 2nd person.
- * 3.6.0: made case-insensitive.
- */
-char *
-makeplural(oldstr)
-const char *oldstr;
-{
- register char *spot;
- char lo_c, *str = nextobuf();
- const char *excess = (char *) 0;
- int len;
-
- if (oldstr)
- while (*oldstr == ' ')
- oldstr++;
- if (!oldstr || !*oldstr) {
- impossible("plural of null?");
- Strcpy(str, "s");
- return str;
- }
- Strcpy(str, oldstr);
-
- /*
- * Skip changing "pair of" to "pairs of". According to Webster, usual
- * English usage is use pairs for humans, e.g. 3 pairs of dancers,
- * and pair for objects and non-humans, e.g. 3 pair of boots. We don't
- * refer to pairs of humans in this game so just skip to the bottom.
- */
- if (!strncmpi(str, "pair of ", 8))
- goto bottom;
-
- /* look for "foo of bar" so that we can focus on "foo" */
- if ((spot = singplur_compound(str)) != 0) {
- excess = oldstr + (int) (spot - str);
- *spot = '\0';
- } else
- spot = eos(str);
-
- spot--;
- while (spot > str && *spot == ' ')
- spot--; /* Strip blanks from end */
- *(spot + 1) = '\0';
- /* Now spot is the last character of the string */
-
- len = strlen(str);
-
- /* Single letters */
- if (len == 1 || !letter(*spot)) {
- Strcpy(spot + 1, "'s");
- goto bottom;
- }
-
- /* dispense with some words which don't need pluralization */
- {
- static const char *const already_plural[] = {
- "ae", /* algae, larvae, &c */
- "matzot", 0,
- };
-
- /* spot+1: synch up with makesingular's usage */
- if (singplur_lookup(str, spot + 1, TRUE, already_plural))
- goto bottom;
-
- /* more of same, but not suitable for blanket loop checking */
- if ((len == 2 && !strcmpi(str, "ya"))
- || (len >= 3 && !strcmpi(spot - 2, " ya")))
- goto bottom;
- }
-
- /* man/men ("Wiped out all cavemen.") */
- if (len >= 3 && !strcmpi(spot - 2, "man")
- /* exclude shamans and humans etc */
- && !badman(str, TRUE)) {
- Strcasecpy(spot - 1, "en");
- goto bottom;
- }
- if (lowc(*spot) == 'f') { /* (staff handled via one_off[]) */
- lo_c = lowc(*(spot - 1));
- if (len >= 3 && !strcmpi(spot - 2, "erf")) {
- /* avoid "nerf" -> "nerves", "serf" -> "serves" */
- ; /* fall through to default (append 's') */
- } else if (index("lr", lo_c) || index(vowels, lo_c)) {
- /* [aeioulr]f to [aeioulr]ves */
- Strcasecpy(spot, "ves");
- goto bottom;
- }
- }
- /* ium/ia (mycelia, baluchitheria) */
- if (len >= 3 && !strcmpi(spot - 2, "ium")) {
- Strcasecpy(spot - 2, "ia");
- goto bottom;
- }
- /* algae, larvae, hyphae (another fungus part) */
- if ((len >= 4 && !strcmpi(spot - 3, "alga"))
- || (len >= 5
- && (!strcmpi(spot - 4, "hypha") || !strcmpi(spot - 4, "larva")))
- || (len >= 6 && !strcmpi(spot - 5, "amoeba"))
- || (len >= 8 && (!strcmpi(spot - 7, "vertebra")))) {
- /* a to ae */
- Strcasecpy(spot + 1, "e");
- goto bottom;
- }
- /* fungus/fungi, homunculus/homunculi, but buses, lotuses, wumpuses */
- if (len > 3 && !strcmpi(spot - 1, "us")
- && !((len >= 5 && !strcmpi(spot - 4, "lotus"))
- || (len >= 6 && !strcmpi(spot - 5, "wumpus")))) {
- Strcasecpy(spot - 1, "i");
- goto bottom;
- }
- /* sis/ses (nemesis) */
- if (len >= 3 && !strcmpi(spot - 2, "sis")) {
- Strcasecpy(spot - 1, "es");
- goto bottom;
- }
- /* matzoh/matzot, possible food name */
- if (len >= 6
- && (!strcmpi(spot - 5, "matzoh") || !strcmpi(spot - 5, "matzah"))) {
- Strcasecpy(spot - 1, "ot"); /* oh/ah -> ot */
- goto bottom;
- }
- if (len >= 5
- && (!strcmpi(spot - 4, "matzo") || !strcmpi(spot - 4, "matza"))) {
- Strcasecpy(spot, "ot"); /* o/a -> ot */
- goto bottom;
- }
-
- /* note: -eau/-eaux (gateau, bordeau...) */
- /* note: ox/oxen, VAX/VAXen, goose/geese */
-
- lo_c = lowc(*spot);
-
- /* Ends in z, x, s, ch, sh; add an "es" */
- if (index("zxs", lo_c)
- || (len >= 2 && lo_c == 'h' && index("cs", lowc(*(spot - 1))))
- /* Kludge to get "tomatoes" and "potatoes" right */
- || (len >= 4 && !strcmpi(spot - 2, "ato"))
- || (len >= 5 && !strcmpi(spot - 4, "dingo"))) {
- Strcasecpy(spot + 1, "es"); /* append es */
- goto bottom;
- }
- /* Ends in y preceded by consonant (note: also "qu") change to "ies" */
- if (lo_c == 'y' && !index(vowels, lowc(*(spot - 1)))) {
- Strcasecpy(spot, "ies"); /* y -> ies */
- goto bottom;
- }
- /* Default: append an 's' */
- Strcasecpy(spot + 1, "s");
-
-bottom:
- if (excess)
- Strcat(str, excess);
- return str;
-}
-
-/*
- * Singularize a string the user typed in; this helps reduce the complexity
- * of readobjnam, and is also used in pager.c to singularize the string
- * for which help is sought.
- *
- * "Manes" is ambiguous: monster type (keep s), or horse body part (drop s)?
- * Its inclusion in as_is[]/special_subj[] makes it get treated as the former.
- *
- * A lot of unique monsters have names ending in s; plural, or singular
- * from plural, doesn't make much sense for them so we don't bother trying.
- * 3.6.0: made case-insensitive.
- */
-char *
-makesingular(oldstr)
-const char *oldstr;
-{
- register char *p, *bp;
- const char *excess = 0;
- char *str = nextobuf();
-
- if (oldstr)
- while (*oldstr == ' ')
- oldstr++;
- if (!oldstr || !*oldstr) {
- impossible("singular of null?");
- str[0] = '\0';
- return str;
- }
-
- bp = strcpy(str, oldstr);
-
- /* check for "foo of bar" so that we can focus on "foo" */
- if ((p = singplur_compound(bp)) != 0) {
- excess = oldstr + (int) (p - bp);
- *p = '\0';
- } else
- p = eos(bp);
-
- /* dispense with some words which don't need singularization */
- if (singplur_lookup(bp, p, FALSE, special_subjs))
- goto bottom;
-
- /* remove -s or -es (boxes) or -ies (rubies) */
- if (p >= bp + 1 && lowc(p[-1]) == 's') {
- if (p >= bp + 2 && lowc(p[-2]) == 'e') {
- if (p >= bp + 3 && lowc(p[-3]) == 'i') { /* "ies" */
- if (!BSTRCMPI(bp, p - 7, "cookies")
- || !BSTRCMPI(bp, p - 4, "pies")
- || !BSTRCMPI(bp, p - 5, "mbies") /* zombie */
- || !BSTRCMPI(bp, p - 5, "yries")) /* valkyrie */
- goto mins;
- Strcasecpy(p - 3, "y"); /* ies -> y */
- goto bottom;
- }
- /* wolves, but f to ves isn't fully reversible */
- if (p - 4 >= bp && (index("lr", lowc(*(p - 4)))
- || index(vowels, lowc(*(p - 4))))
- && !BSTRCMPI(bp, p - 3, "ves")) {
- if (!BSTRCMPI(bp, p - 6, "cloves")
- || !BSTRCMPI(bp, p - 6, "nerves"))
- goto mins;
- Strcasecpy(p - 3, "f"); /* ves -> f */
- goto bottom;
- }
- /* note: nurses, axes but boxes, wumpuses */
- if (!BSTRCMPI(bp, p - 4, "eses")
- || !BSTRCMPI(bp, p - 4, "oxes") /* boxes, foxes */
- || !BSTRCMPI(bp, p - 4, "nxes") /* lynxes */
- || !BSTRCMPI(bp, p - 4, "ches")
- || !BSTRCMPI(bp, p - 4, "uses") /* lotuses */
- || !BSTRCMPI(bp, p - 4, "sses") /* priestesses */
- || !BSTRCMPI(bp, p - 5, "atoes") /* tomatoes */
- || !BSTRCMPI(bp, p - 7, "dingoes")
- || !BSTRCMPI(bp, p - 7, "Aleaxes")) {
- *(p - 2) = '\0'; /* drop es */
- goto bottom;
- } /* else fall through to mins */
-
- /* ends in 's' but not 'es' */
- } else if (!BSTRCMPI(bp, p - 2, "us")) { /* lotus, fungus... */
- if (BSTRCMPI(bp, p - 6, "tengus") /* but not these... */
- && BSTRCMPI(bp, p - 7, "hezrous"))
- goto bottom;
- } else if (!BSTRCMPI(bp, p - 2, "ss")
- || !BSTRCMPI(bp, p - 5, " lens")
- || (p - 4 == bp && !strcmpi(p - 4, "lens"))) {
- goto bottom;
- }
- mins:
- *(p - 1) = '\0'; /* drop s */
-
- } else { /* input doesn't end in 's' */
-
- if (!BSTRCMPI(bp, p - 3, "men")
- && !badman(bp, FALSE)) {
- Strcasecpy(p - 2, "an");
- goto bottom;
- }
- /* matzot -> matzo, algae -> alga */
- if (!BSTRCMPI(bp, p - 6, "matzot") || !BSTRCMPI(bp, p - 2, "ae")) {
- *(p - 1) = '\0'; /* drop t/e */
- goto bottom;
- }
- /* balactheria -> balactherium */
- if (p - 4 >= bp && !strcmpi(p - 2, "ia")
- && index("lr", lowc(*(p - 3))) && lowc(*(p - 4)) == 'e') {
- Strcasecpy(p - 1, "um"); /* a -> um */
- }
-
- /* here we cannot find the plural suffix */
- }
-
-bottom:
- /* if we stripped off a suffix (" of bar" from "foo of bar"),
- put it back now [strcat() isn't actually 100% safe here...] */
- if (excess)
- Strcat(bp, excess);
-
- return bp;
-}
-
-boolean
-badman(basestr, to_plural)
-const char *basestr;
-boolean to_plural; /* true => makeplural, false => makesingular */
-{
- int i, al;
- char *endstr, *spot;
- /* these are all the prefixes for *man that don't have a *men plural */
- const char *no_men[] = {
- "albu", "antihu", "anti", "ata", "auto", "bildungsro", "cai",
- "cay", "ceru", "corner", "decu", "des", "dura", "fir",
- "glass", "hanu", "het", "infrahu", "inhu", "land",
- "meat", "nonhu", "otto", "out", "prehu", "protohu",
- "subhu", "superhu", "talis", "unhu", "sha",
- "hu", "un", "le", "re", "so", "to", "at", "a",
- };
- /* these are all the prefixes for *men that don't have a *man singular */
- const char *no_man[] = {
- "abdo", "acu", "agno", "ceru", "cogno", "cycla", "fleh", "grava",
- "hegu", "preno", "sonar", "dai", "exa", "fla", "sta", "teg", "tegu",
- "vela", "da", "hy", "lu", "no", "nu", "ra", "ru", "se", "vi", "ya",
- "o", "a",
- };
-
- if (!basestr || strlen(basestr) < 4)
- return FALSE;
-
- endstr = eos((char *)basestr);
-
- if (to_plural) {
- for (i = 0; i < SIZE(no_men); i++) {
- al = (int) strlen(no_men[i]);
- spot = endstr - (al + 3);
- if (!BSTRNCMPI(basestr, spot, no_men[i], al)
- && (spot == basestr || *(spot - 1) == ' '))
- return TRUE;
- }
- } else {
- for (i = 0; i < SIZE(no_man); i++) {
- al = (int) strlen(no_man[i]);
- spot = endstr - (al + 3);
- if (!BSTRNCMPI(basestr, spot, no_man[i], al)
- && (spot == basestr || *(spot - 1) == ' '))
- return TRUE;
- }
- }
- return FALSE;
-}
-
-/* compare user string against object name string using fuzzy matching */
-STATIC_OVL boolean
-wishymatch(u_str, o_str, retry_inverted)
-const char *u_str; /* from user, so might be variant spelling */
-const char *o_str; /* from objects[], so is in canonical form */
-boolean retry_inverted; /* optional extra "of" handling */
-{
- static NEARDATA const char detect_SP[] = "detect ",
- SP_detection[] = " detection";
- char *p, buf[BUFSZ];
-
- /* ignore spaces & hyphens and upper/lower case when comparing */
- if (fuzzymatch(u_str, o_str, " -", TRUE))
- return TRUE;
-
- if (retry_inverted) {
- const char *u_of, *o_of;
-
- /* when just one of the strings is in the form "foo of bar",
- convert it into "bar foo" and perform another comparison */
- u_of = strstri(u_str, " of ");
- o_of = strstri(o_str, " of ");
- if (u_of && !o_of) {
- Strcpy(buf, u_of + 4);
- p = eos(strcat(buf, " "));
- while (u_str < u_of)
- *p++ = *u_str++;
- *p = '\0';
- return fuzzymatch(buf, o_str, " -", TRUE);
- } else if (o_of && !u_of) {
- Strcpy(buf, o_of + 4);
- p = eos(strcat(buf, " "));
- while (o_str < o_of)
- *p++ = *o_str++;
- *p = '\0';
- return fuzzymatch(u_str, buf, " -", TRUE);
- }
- }
-
- /* [note: if something like "elven speed boots" ever gets added, these
- special cases should be changed to call wishymatch() recursively in
- order to get the "of" inversion handling] */
- if (!strncmp(o_str, "dwarvish ", 9)) {
- if (!strncmpi(u_str, "dwarven ", 8))
- return fuzzymatch(u_str + 8, o_str + 9, " -", TRUE);
- } else if (!strncmp(o_str, "elven ", 6)) {
- if (!strncmpi(u_str, "elvish ", 7))
- return fuzzymatch(u_str + 7, o_str + 6, " -", TRUE);
- else if (!strncmpi(u_str, "elfin ", 6))
- return fuzzymatch(u_str + 6, o_str + 6, " -", TRUE);
- } else if (!strncmp(o_str, detect_SP, sizeof detect_SP - 1)) {
- /* check for "detect <foo>" vs "<foo> detection" */
- if ((p = strstri(u_str, SP_detection)) != 0
- && !*(p + sizeof SP_detection - 1)) {
- /* convert "<foo> detection" into "detect <foo>" */
- *p = '\0';
- Strcat(strcpy(buf, detect_SP), u_str);
- /* "detect monster" -> "detect monsters" */
- if (!strcmpi(u_str, "monster"))
- Strcat(buf, "s");
- *p = ' ';
- return fuzzymatch(buf, o_str, " -", TRUE);
- }
- } else if (strstri(o_str, SP_detection)) {
- /* and the inverse, "<foo> detection" vs "detect <foo>" */
- if (!strncmpi(u_str, detect_SP, sizeof detect_SP - 1)) {
- /* convert "detect <foo>s" into "<foo> detection" */
- p = makesingular(u_str + sizeof detect_SP - 1);
- Strcat(strcpy(buf, p), SP_detection);
- /* caller may be looping through objects[], so avoid
- churning through all the obufs */
- releaseobuf(p);
- return fuzzymatch(buf, o_str, " -", TRUE);
- }
- } else if (strstri(o_str, "ability")) {
- /* when presented with "foo of bar", makesingular() used to
- singularize both foo & bar, but now only does so for foo */
- /* catch "{potion(s),ring} of {gain,restore,sustain} abilities" */
- if ((p = strstri(u_str, "abilities")) != 0
- && !*(p + sizeof "abilities" - 1)) {
- (void) strncpy(buf, u_str, (unsigned) (p - u_str));
- Strcpy(buf + (p - u_str), "ability");
- return fuzzymatch(buf, o_str, " -", TRUE);
- }
- } else if (!strcmp(o_str, "aluminum")) {
- /* this special case doesn't really fit anywhere else... */
- /* (note that " wand" will have been stripped off by now) */
- if (!strcmpi(u_str, "aluminium"))
- return fuzzymatch(u_str + 9, o_str + 8, " -", TRUE);
- }
-
- return FALSE;
-}
-
-struct o_range {
- const char *name, oclass;
- int f_o_range, l_o_range;
-};
-
-/* wishable subranges of objects */
-STATIC_OVL NEARDATA const struct o_range o_ranges[] = {
- { "bag", TOOL_CLASS, SACK, BAG_OF_TRICKS },
- { "lamp", TOOL_CLASS, OIL_LAMP, MAGIC_LAMP },
- { "candle", TOOL_CLASS, TALLOW_CANDLE, WAX_CANDLE },
- { "horn", TOOL_CLASS, TOOLED_HORN, HORN_OF_PLENTY },
- { "shield", ARMOR_CLASS, SMALL_SHIELD, SHIELD_OF_REFLECTION },
- { "hat", ARMOR_CLASS, FEDORA, DUNCE_CAP },
- { "helm", ARMOR_CLASS, ELVEN_LEATHER_HELM, HELM_OF_TELEPATHY },
- { "gloves", ARMOR_CLASS, LEATHER_GLOVES, GAUNTLETS_OF_DEXTERITY },
- { "gauntlets", ARMOR_CLASS, LEATHER_GLOVES, GAUNTLETS_OF_DEXTERITY },
- { "boots", ARMOR_CLASS, LOW_BOOTS, LEVITATION_BOOTS },
- { "shoes", ARMOR_CLASS, LOW_BOOTS, IRON_SHOES },
- { "cloak", ARMOR_CLASS, MUMMY_WRAPPING, CLOAK_OF_DISPLACEMENT },
- { "shirt", ARMOR_CLASS, HAWAIIAN_SHIRT, T_SHIRT },
- { "dragon scales", ARMOR_CLASS, GRAY_DRAGON_SCALES,
- YELLOW_DRAGON_SCALES },
- { "dragon scale mail", ARMOR_CLASS, GRAY_DRAGON_SCALE_MAIL,
- YELLOW_DRAGON_SCALE_MAIL },
- { "sword", WEAPON_CLASS, SHORT_SWORD, KATANA },
- { "venom", VENOM_CLASS, BLINDING_VENOM, ACID_VENOM },
- { "gray stone", GEM_CLASS, LUCKSTONE, FLINT },
- { "grey stone", GEM_CLASS, LUCKSTONE, FLINT },
-};
-
-/* alternate spellings; if the difference is only the presence or
- absence of spaces and/or hyphens (such as "pickaxe" vs "pick axe"
- vs "pick-axe") then there is no need for inclusion in this list;
- likewise for ``"of" inversions'' ("boots of speed" vs "speed boots") */
-struct alt_spellings {
- const char *sp;
- int ob;
-} spellings[] = {
- { "pickax", PICK_AXE },
- { "whip", BULLWHIP },
- { "saber", SILVER_SABER },
- { "silver sabre", SILVER_SABER },
- { "smooth shield", SHIELD_OF_REFLECTION },
- { "grey dragon scale mail", GRAY_DRAGON_SCALE_MAIL },
- { "grey dragon scales", GRAY_DRAGON_SCALES },
- { "iron ball", HEAVY_IRON_BALL },
- { "lantern", BRASS_LANTERN },
- { "mattock", DWARVISH_MATTOCK },
- { "amulet of poison resistance", AMULET_VERSUS_POISON },
- { "potion of sleep", POT_SLEEPING },
- { "stone", ROCK },
- { "camera", EXPENSIVE_CAMERA },
- { "tee shirt", T_SHIRT },
- { "can", TIN },
- { "can opener", TIN_OPENER },
- { "kelp", KELP_FROND },
- { "eucalyptus", EUCALYPTUS_LEAF },
- { "royal jelly", LUMP_OF_ROYAL_JELLY },
- { "lembas", LEMBAS_WAFER },
- { "marker", MAGIC_MARKER },
- { "hook", GRAPPLING_HOOK },
- { "grappling iron", GRAPPLING_HOOK },
- { "grapnel", GRAPPLING_HOOK },
- { "grapple", GRAPPLING_HOOK },
- { "protection from shape shifters", RIN_PROTECTION_FROM_SHAPE_CHAN },
- /* normally we wouldn't have to worry about unnecessary <space>, but
- " stone" will get stripped off, preventing a wishymatch; that actually
- lets "flint stone" be a match, so we also accept bogus "flintstone" */
- { "luck stone", LUCKSTONE },
- { "load stone", LOADSTONE },
- { "touch stone", TOUCHSTONE },
- { "flintstone", FLINT },
- { (const char *) 0, 0 },
-};
-
-STATIC_OVL short
-rnd_otyp_by_wpnskill(skill)
-schar skill;
-{
- int i, n = 0;
- short otyp = STRANGE_OBJECT;
- for (i = bases[WEAPON_CLASS];
- i < NUM_OBJECTS && objects[i].oc_class == WEAPON_CLASS; i++)
- if (objects[i].oc_skill == skill) {
- n++;
- otyp = i;
- }
- if (n > 0) {
- n = rn2(n);
- for (i = bases[WEAPON_CLASS];
- i < NUM_OBJECTS && objects[i].oc_class == WEAPON_CLASS; i++)
- if (objects[i].oc_skill == skill)
- if (--n < 0)
- return i;
- }
- return otyp;
-}
-
-STATIC_OVL short
-rnd_otyp_by_namedesc(name, oclass)
-char *name;
-char oclass;
-{
- int i, n = 0;
- short validobjs[NUM_OBJECTS];
- register const char *zn;
- long maxprob = 0;
-
- if (!name)
- return STRANGE_OBJECT;
-
- memset((genericptr_t) validobjs, 0, sizeof(validobjs));
-
- for (i = oclass ? bases[(int)oclass] : STRANGE_OBJECT + 1;
- i < NUM_OBJECTS && (!oclass || objects[i].oc_class == oclass);
- ++i) {
- /* don't match extra descriptions (w/o real name) */
- if ((zn = OBJ_NAME(objects[i])) == 0)
- continue;
- if (wishymatch(name, zn, TRUE)
- || ((zn = OBJ_DESCR(objects[i])) != 0
- && wishymatch(name, zn, FALSE))
- || ((zn = objects[i].oc_uname) != 0
- && wishymatch(name, zn, FALSE))) {
- validobjs[n++] = (short) i;
- maxprob += (objects[i].oc_prob + 1);
- }
- }
-
- if (n > 0 && maxprob) {
- long prob = rn2(maxprob);
-
- i = 0;
- while (i < n - 1
- && (prob -= (objects[validobjs[i]].oc_prob + 1)) >= 0)
- i++;
- return validobjs[i];
- }
- return STRANGE_OBJECT;
-}
-
-/*
- * Return something wished for. Specifying a null pointer for
- * the user request string results in a random object. Otherwise,
- * if asking explicitly for "nothing" (or "nil") return no_wish;
- * if not an object return &zeroobj; if an error (no matching object),
- * return null.
- */
-struct obj *
-readobjnam(bp, no_wish)
-register char *bp;
-struct obj *no_wish;
-{
- register char *p;
- register int i;
- register struct obj *otmp;
- int cnt, spe, spesgn, typ, very, rechrg;
- int blessed, uncursed, iscursed, ispoisoned, isgreased;
- int eroded, eroded2, erodeproof;
- int halfeaten, mntmp, contents;
- int islit, unlabeled, ishistoric, isdiluted, trapped;
- int tmp, tinv, tvariety;
- int wetness, gsize = 0;
- struct fruit *f;
- int ftype = context.current_fruit;
- char fruitbuf[BUFSZ];
- /* Fruits may not mess up the ability to wish for real objects (since
- * you can leave a fruit in a bones file and it will be added to
- * another person's game), so they must be checked for last, after
- * stripping all the possible prefixes and seeing if there's a real
- * name in there. So we have to save the full original name. However,
- * it's still possible to do things like "uncursed burnt Alaska",
- * or worse yet, "2 burned 5 course meals", so we need to loop to
- * strip off the prefixes again, this time stripping only the ones
- * possible on food.
- * We could get even more detailed so as to allow food names with
- * prefixes that _are_ possible on food, so you could wish for
- * "2 3 alarm chilis". Currently this isn't allowed; options.c
- * automatically sticks 'candied' in front of such names.
- */
- char oclass;
- char *un, *dn, *actualn, *origbp = bp;
- const char *name = 0;
-
- cnt = spe = spesgn = typ = very = rechrg = blessed = uncursed = iscursed =
- ispoisoned = isgreased = eroded = eroded2 = erodeproof = halfeaten =
- islit = unlabeled = ishistoric = isdiluted = trapped = 0;
- tvariety = RANDOM_TIN;
- mntmp = NON_PM;
-#define UNDEFINED 0
-#define EMPTY 1
-#define SPINACH 2
- contents = UNDEFINED;
- oclass = 0;
- actualn = dn = un = 0;
- wetness = 0;
-
- if (!bp)
- goto any;
- /* first, remove extra whitespace they may have typed */
- (void) mungspaces(bp);
- /* allow wishing for "nothing" to preserve wishless conduct...
- [now requires "wand of nothing" if that's what was really wanted] */
- if (!strcmpi(bp, "nothing") || !strcmpi(bp, "nil")
- || !strcmpi(bp, "none"))
- return no_wish;
- /* save the [nearly] unmodified choice string */
- Strcpy(fruitbuf, bp);
-
- for (;;) {
- register int l;
-
- if (!bp || !*bp)
- goto any;
- if (!strncmpi(bp, "an ", l = 3) || !strncmpi(bp, "a ", l = 2)) {
- cnt = 1;
- } else if (!strncmpi(bp, "the ", l = 4)) {
- ; /* just increment `bp' by `l' below */
- } else if (!cnt && digit(*bp) && strcmp(bp, "0")) {
- cnt = atoi(bp);
- while (digit(*bp))
- bp++;
- while (*bp == ' ')
- bp++;
- l = 0;
- } else if (*bp == '+' || *bp == '-') {
- spesgn = (*bp++ == '+') ? 1 : -1;
- spe = atoi(bp);
- while (digit(*bp))
- bp++;
- while (*bp == ' ')
- bp++;
- l = 0;
- } else if (!strncmpi(bp, "blessed ", l = 8)
- || !strncmpi(bp, "holy ", l = 5)) {
- blessed = 1;
- } else if (!strncmpi(bp, "moist ", l = 6)
- || !strncmpi(bp, "wet ", l = 4)) {
- if (!strncmpi(bp, "wet ", 4))
- wetness = rn2(3) + 3;
- else
- wetness = rnd(2);
- } else if (!strncmpi(bp, "cursed ", l = 7)
- || !strncmpi(bp, "unholy ", l = 7)) {
- iscursed = 1;
- } else if (!strncmpi(bp, "uncursed ", l = 9)) {
- uncursed = 1;
- } else if (!strncmpi(bp, "rustproof ", l = 10)
- || !strncmpi(bp, "erodeproof ", l = 11)
- || !strncmpi(bp, "corrodeproof ", l = 13)
- || !strncmpi(bp, "fixed ", l = 6)
- || !strncmpi(bp, "fireproof ", l = 10)
- || !strncmpi(bp, "rotproof ", l = 9)) {
- erodeproof = 1;
- } else if (!strncmpi(bp, "lit ", l = 4)
- || !strncmpi(bp, "burning ", l = 8)) {
- islit = 1;
- } else if (!strncmpi(bp, "unlit ", l = 6)
- || !strncmpi(bp, "extinguished ", l = 13)) {
- islit = 0;
- /* "unlabeled" and "blank" are synonymous */
- } else if (!strncmpi(bp, "unlabeled ", l = 10)
- || !strncmpi(bp, "unlabelled ", l = 11)
- || !strncmpi(bp, "blank ", l = 6)) {
- unlabeled = 1;
- } else if (!strncmpi(bp, "poisoned ", l = 9)) {
- ispoisoned = 1;
- /* "trapped" recognized but not honored outside wizard mode */
- } else if (!strncmpi(bp, "trapped ", l = 8)) {
- trapped = 0; /* undo any previous "untrapped" */
- if (wizard)
- trapped = 1;
- } else if (!strncmpi(bp, "untrapped ", l = 10)) {
- trapped = 2; /* not trapped */
- } else if (!strncmpi(bp, "greased ", l = 8)) {
- isgreased = 1;
- } else if (!strncmpi(bp, "very ", l = 5)) {
- /* very rusted very heavy iron ball */
- very = 1;
- } else if (!strncmpi(bp, "thoroughly ", l = 11)) {
- very = 2;
- } else if (!strncmpi(bp, "rusty ", l = 6)
- || !strncmpi(bp, "rusted ", l = 7)
- || !strncmpi(bp, "burnt ", l = 6)
- || !strncmpi(bp, "burned ", l = 7)) {
- eroded = 1 + very;
- very = 0;
- } else if (!strncmpi(bp, "corroded ", l = 9)
- || !strncmpi(bp, "rotted ", l = 7)) {
- eroded2 = 1 + very;
- very = 0;
- } else if (!strncmpi(bp, "partly eaten ", l = 13)
- || !strncmpi(bp, "partially eaten ", l = 16)) {
- halfeaten = 1;
- } else if (!strncmpi(bp, "historic ", l = 9)) {
- ishistoric = 1;
- } else if (!strncmpi(bp, "diluted ", l = 8)) {
- isdiluted = 1;
- } else if (!strncmpi(bp, "empty ", l = 6)) {
- contents = EMPTY;
- } else if (!strncmpi(bp, "small ", l = 6)) { /* glob sizes */
- gsize = 1;
- } else if (!strncmpi(bp, "medium ", l = 7)) {
- /* xname() doesn't display "medium" but without this
- there'd be no way to ask for the intermediate size */
- gsize = 2;
- } else if (!strncmpi(bp, "large ", l = 6)) {
- /* "very large " had "very " peeled off on previous iteration */
- gsize = (very != 1) ? 3 : 4;
- } else
- break;
- bp += l;
- }
- if (!cnt)
- cnt = 1; /* %% what with "gems" etc. ? */
- if (strlen(bp) > 1 && (p = rindex(bp, '(')) != 0) {
- boolean keeptrailingchars = TRUE;
-
- p[(p > bp && p[-1] == ' ') ? -1 : 0] = '\0'; /*terminate bp */
- ++p; /* advance past '(' */
- if (!strncmpi(p, "lit)", 4)) {
- islit = 1;
- p += 4 - 1; /* point at ')' */
- } else {
- spe = atoi(p);
- while (digit(*p))
- p++;
- if (*p == ':') {
- p++;
- rechrg = spe;
- spe = atoi(p);
- while (digit(*p))
- p++;
- }
- if (*p != ')') {
- spe = rechrg = 0;
- /* mis-matched parentheses; rest of string will be ignored
- * [probably we should restore everything back to '('
- * instead since it might be part of "named ..."]
- */
- keeptrailingchars = FALSE;
- } else {
- spesgn = 1;
- }
- }
- if (keeptrailingchars) {
- char *pp = eos(bp);
-
- /* 'pp' points at 'pb's terminating '\0',
- 'p' points at ')' and will be incremented past it */
- do {
- *pp++ = *++p;
- } while (*p);
- }
- }
- /*
- * otmp->spe is type schar, so we don't want spe to be any bigger or
- * smaller. Also, spe should always be positive --some cheaters may
- * try to confuse atoi().
- */
- if (spe < 0) {
- spesgn = -1; /* cheaters get what they deserve */
- spe = abs(spe);
- }
- if (spe > SCHAR_LIM)
- spe = SCHAR_LIM;
- if (rechrg < 0 || rechrg > 7)
- rechrg = 7; /* recharge_limit */
-
- /* now we have the actual name, as delivered by xname, say
- * green potions called whisky
- * scrolls labeled "QWERTY"
- * egg
- * fortune cookies
- * very heavy iron ball named hoei
- * wand of wishing
- * elven cloak
- */
- if ((p = strstri(bp, " named ")) != 0) {
- *p = 0;
- name = p + 7;
- }
- if ((p = strstri(bp, " called ")) != 0) {
- *p = 0;
- un = p + 8;
- /* "helmet called telepathy" is not "helmet" (a specific type)
- * "shield called reflection" is not "shield" (a general type)
- */
- for (i = 0; i < SIZE(o_ranges); i++)
- if (!strcmpi(bp, o_ranges[i].name)) {
- oclass = o_ranges[i].oclass;
- goto srch;
- }
- }
- if ((p = strstri(bp, " labeled ")) != 0) {
- *p = 0;
- dn = p + 9;
- } else if ((p = strstri(bp, " labelled ")) != 0) {
- *p = 0;
- dn = p + 10;
- }
- if ((p = strstri(bp, " of spinach")) != 0) {
- *p = 0;
- contents = SPINACH;
- }
-
- /*
- * Skip over "pair of ", "pairs of", "set of" and "sets of".
- *
- * Accept "3 pair of boots" as well as "3 pairs of boots". It is
- * valid English either way. See makeplural() for more on pair/pairs.
- *
- * We should only double count if the object in question is not
- * referred to as a "pair of". E.g. We should double if the player
- * types "pair of spears", but not if the player types "pair of
- * lenses". Luckily (?) all objects that are referred to as pairs
- * -- boots, gloves, and lenses -- are also not mergable, so cnt is
- * ignored anyway.
- */
- if (!strncmpi(bp, "pair of ", 8)) {
- bp += 8;
- cnt *= 2;
- } else if (!strncmpi(bp, "pairs of ", 9)) {
- bp += 9;
- if (cnt > 1)
- cnt *= 2;
- } else if (!strncmpi(bp, "set of ", 7)) {
- bp += 7;
- } else if (!strncmpi(bp, "sets of ", 8)) {
- bp += 8;
- }
-
- /* intercept pudding globs here; they're a valid wish target,
- * but we need them to not get treated like a corpse.
- *
- * also don't let player wish for multiple globs.
- */
- if ((p = strstri(bp, "glob of ")) != 0
- || (p = strstri(bp, "globs of ")) != 0) {
- int globoffset = (*(p + 4) == 's') ? 9 : 8;
-
- if ((mntmp = name_to_mon(p + globoffset)) >= PM_GRAY_OOZE
- && mntmp <= PM_BLACK_PUDDING) {
- mntmp = NON_PM; /* lie to ourselves */
- cnt = 0; /* force only one */
- }
- } else {
- /*
- * Find corpse type using "of" (figurine of an orc, tin of orc meat)
- * Don't check if it's a wand or spellbook.
- * (avoid "wand/finger of death" confusion).
- */
- if (!strstri(bp, "wand ") && !strstri(bp, "spellbook ")
- && !strstri(bp, "finger ")) {
- if ((p = strstri(bp, "tin of ")) != 0) {
- if (!strcmpi(p + 7, "spinach")) {
- contents = SPINACH;
- mntmp = NON_PM;
- } else {
- tmp = tin_variety_txt(p + 7, &tinv);
- tvariety = tinv;
- mntmp = name_to_mon(p + 7 + tmp);
- }
- typ = TIN;
- goto typfnd;
- } else if ((p = strstri(bp, " of ")) != 0
- && (mntmp = name_to_mon(p + 4)) >= LOW_PM)
- *p = 0;
- }
- }
- /* Find corpse type w/o "of" (red dragon scale mail, yeti corpse) */
- if (strncmpi(bp, "samurai sword", 13) /* not the "samurai" monster! */
- && strncmpi(bp, "wizard lock", 11) /* not the "wizard" monster! */
- && strncmpi(bp, "ninja-to", 8) /* not the "ninja" rank */
- && strncmpi(bp, "master key", 10) /* not the "master" rank */
- && strncmpi(bp, "magenta", 7)) { /* not the "mage" rank */
- if (mntmp < LOW_PM && strlen(bp) > 2
- && (mntmp = name_to_mon(bp)) >= LOW_PM) {
- int mntmptoo, mntmplen; /* double check for rank title */
- char *obp = bp;
-
- mntmptoo = title_to_mon(bp, (int *) 0, &mntmplen);
- bp += (mntmp != mntmptoo) ? (int) strlen(mons[mntmp].mname)
- : mntmplen;
- if (*bp == ' ') {
- bp++;
- } else if (!strncmpi(bp, "s ", 2)) {
- bp += 2;
- } else if (!strncmpi(bp, "es ", 3)) {
- bp += 3;
- } else if (!*bp && !actualn && !dn && !un && !oclass) {
- /* no referent; they don't really mean a monster type */
- bp = obp;
- mntmp = NON_PM;
- }
- }
- }
-
- /* first change to singular if necessary */
- if (*bp) {
- char *sng = makesingular(bp);
- if (strcmp(bp, sng)) {
- if (cnt == 1)
- cnt = 2;
- Strcpy(bp, sng);
- }
- }
-
- /* Alternate spellings (pick-ax, silver sabre, &c) */
- {
- struct alt_spellings *as = spellings;
-
- while (as->sp) {
- if (fuzzymatch(bp, as->sp, " -", TRUE)) {
- typ = as->ob;
- goto typfnd;
- }
- as++;
- }
- /* can't use spellings list for this one due to shuffling */
- if (!strncmpi(bp, "grey spell", 10))
- *(bp + 2) = 'a';
-
- if ((p = strstri(bp, "armour")) != 0) {
- /* skip past "armo", then copy remainder beyond "u" */
- p += 4;
- while ((*p = *(p + 1)) != '\0')
- ++p; /* self terminating */
- }
- }
-
- /* dragon scales - assumes order of dragons */
- if (!strcmpi(bp, "scales") && mntmp >= PM_GRAY_DRAGON
- && mntmp <= PM_YELLOW_DRAGON) {
- typ = GRAY_DRAGON_SCALES + mntmp - PM_GRAY_DRAGON;
- mntmp = NON_PM; /* no monster */
- goto typfnd;
- }
-
- p = eos(bp);
- if (!BSTRCMPI(bp, p - 10, "holy water")) {
- typ = POT_WATER;
- if ((p - bp) >= 12 && *(p - 12) == 'u')
- iscursed = 1; /* unholy water */
- else
- blessed = 1;
- goto typfnd;
- }
- if (unlabeled && !BSTRCMPI(bp, p - 6, "scroll")) {
- typ = SCR_BLANK_PAPER;
- goto typfnd;
- }
- if (unlabeled && !BSTRCMPI(bp, p - 9, "spellbook")) {
- typ = SPE_BLANK_PAPER;
- goto typfnd;
- }
- /*
- * NOTE: Gold pieces are handled as objects nowadays, and therefore
- * this section should probably be reconsidered as well as the entire
- * gold/money concept. Maybe we want to add other monetary units as
- * well in the future. (TH)
- */
- if (!BSTRCMPI(bp, p - 10, "gold piece")
- || !BSTRCMPI(bp, p - 7, "zorkmid")
- || !strcmpi(bp, "gold") || !strcmpi(bp, "money")
- || !strcmpi(bp, "coin") || *bp == GOLD_SYM) {
- if (cnt > 5000 && !wizard)
- cnt = 5000;
- else if (cnt < 1)
- cnt = 1;
- otmp = mksobj(GOLD_PIECE, FALSE, FALSE);
- otmp->quan = (long) cnt;
- otmp->owt = weight(otmp);
- context.botl = 1;
- return otmp;
- }
-
- /* check for single character object class code ("/" for wand, &c) */
- if (strlen(bp) == 1 && (i = def_char_to_objclass(*bp)) < MAXOCLASSES
- && i > ILLOBJ_CLASS && (i != VENOM_CLASS || wizard)) {
- oclass = i;
- goto any;
- }
-
- /* Search for class names: XXXXX potion, scroll of XXXXX. Avoid */
- /* false hits on, e.g., rings for "ring mail". */
- if (strncmpi(bp, "enchant ", 8)
- && strncmpi(bp, "destroy ", 8)
- && strncmpi(bp, "detect food", 11)
- && strncmpi(bp, "food detection", 14)
- && strncmpi(bp, "ring mail", 9)
- && strncmpi(bp, "studded leather armor", 21)
- && strncmpi(bp, "leather armor", 13)
- && strncmpi(bp, "tooled horn", 11)
- && strncmpi(bp, "food ration", 11)
- && strncmpi(bp, "meat ring", 9))
- for (i = 0; i < (int) (sizeof wrpsym); i++) {
- register int j = strlen(wrp[i]);
-
- if (!strncmpi(bp, wrp[i], j)) {
- oclass = wrpsym[i];
- if (oclass != AMULET_CLASS) {
- bp += j;
- if (!strncmpi(bp, " of ", 4))
- actualn = bp + 4;
- /* else if(*bp) ?? */
- } else
- actualn = bp;
- goto srch;
- }
- if (!BSTRCMPI(bp, p - j, wrp[i])) {
- oclass = wrpsym[i];
- p -= j;
- *p = 0;
- if (p > bp && p[-1] == ' ')
- p[-1] = 0;
- actualn = dn = bp;
- goto srch;
- }
- }
-
- /* Wishing in wizard mode can create traps and furniture.
- * Part I: distinguish between trap and object for the two
- * types of traps which have corresponding objects: bear trap
- * and land mine. "beartrap" (object) and "bear trap" (trap)
- * have a difference in spelling which we used to exploit by
- * adding a special case in wishymatch(), but "land mine" is
- * spelled the same either way so needs different handing.
- * Since we need something else for land mine, we've dropped
- * the bear trap hack so that both are handled exactly the
- * same. To get an armed trap instead of a disarmed object,
- * the player can prefix either the object name or the trap
- * name with "trapped " (which ordinarily applies to chests
- * and tins), or append something--anything at all except for
- * " object", but " trap" is suggested--to either the trap
- * name or the object name.
- */
- if (wizard && (!strncmpi(bp, "bear", 4) || !strncmpi(bp, "land", 4))) {
- boolean beartrap = (lowc(*bp) == 'b');
- char *zp = bp + 4; /* skip "bear"/"land" */
-
- if (*zp == ' ')
- ++zp; /* embedded space is optional */
- if (!strncmpi(zp, beartrap ? "trap" : "mine", 4)) {
- zp += 4;
- if (trapped == 2 || !strcmpi(zp, " object")) {
- /* "untrapped <foo>" or "<foo> object" */
- typ = beartrap ? BEARTRAP : LAND_MINE;
- goto typfnd;
- } else if (trapped == 1 || *zp != '\0') {
- /* "trapped <foo>" or "<foo> trap" (actually "<foo>*") */
- int idx = trap_to_defsym(beartrap ? BEAR_TRAP : LANDMINE);
-
- /* use canonical trap spelling, skip object matching */
- Strcpy(bp, defsyms[idx].explanation);
- goto wiztrap;
- }
- /* [no prefix or suffix; we're going to end up matching
- the object name and getting a disarmed trap object] */
- }
- }
-
-retry:
- /* "grey stone" check must be before general "stone" */
- for (i = 0; i < SIZE(o_ranges); i++)
- if (!strcmpi(bp, o_ranges[i].name)) {
- typ = rnd_class(o_ranges[i].f_o_range, o_ranges[i].l_o_range);
- goto typfnd;
- }
-
- if (!BSTRCMPI(bp, p - 6, " stone") || !BSTRCMPI(bp, p - 4, " gem")) {
- p[!strcmpi(p - 4, " gem") ? -4 : -6] = '\0';
- oclass = GEM_CLASS;
- dn = actualn = bp;
- goto srch;
- } else if (!strcmpi(bp, "looking glass")) {
- ; /* avoid false hit on "* glass" */
- } else if (!BSTRCMPI(bp, p - 6, " glass") || !strcmpi(bp, "glass")) {
- register char *g = bp;
- if (strstri(g, "broken"))
- return (struct obj *) 0;
- if (!strncmpi(g, "worthless ", 10))
- g += 10;
- if (!strncmpi(g, "piece of ", 9))
- g += 9;
- if (!strncmpi(g, "colored ", 8))
- g += 8;
- else if (!strncmpi(g, "coloured ", 9))
- g += 9;
- if (!strcmpi(g, "glass")) { /* choose random color */
- /* 9 different kinds */
- typ = LAST_GEM + rnd(9);
- if (objects[typ].oc_class == GEM_CLASS)
- goto typfnd;
- else
- typ = 0; /* somebody changed objects[]? punt */
- } else { /* try to construct canonical form */
- char tbuf[BUFSZ];
-
- Strcpy(tbuf, "worthless piece of ");
- Strcat(tbuf, g); /* assume it starts with the color */
- Strcpy(bp, tbuf);
- }
- }
-
- actualn = bp;
- if (!dn)
- dn = actualn; /* ex. "skull cap" */
-srch:
- /* check real names of gems first */
- if (!oclass && actualn) {
- for (i = bases[GEM_CLASS]; i <= LAST_GEM; i++) {
- register const char *zn;
-
- if ((zn = OBJ_NAME(objects[i])) != 0 && !strcmpi(actualn, zn)) {
- typ = i;
- goto typfnd;
- }
- }
- /* "tin of foo" would be caught above, but plain "tin" has
- a random chance of yielding "tin wand" unless we do this */
- if (!strcmpi(actualn, "tin")) {
- typ = TIN;
- goto typfnd;
- }
- }
-
- if (((typ = rnd_otyp_by_namedesc(actualn, oclass)) != STRANGE_OBJECT)
- || ((typ = rnd_otyp_by_namedesc(dn, oclass)) != STRANGE_OBJECT)
- || ((typ = rnd_otyp_by_namedesc(un, oclass)) != STRANGE_OBJECT)
- || ((typ = rnd_otyp_by_namedesc(origbp, oclass)) != STRANGE_OBJECT))
- goto typfnd;
- typ = 0;
-
- if (actualn) {
- struct Jitem *j = Japanese_items;
-
- while (j->item) {
- if (actualn && !strcmpi(actualn, j->name)) {
- typ = j->item;
- goto typfnd;
- }
- j++;
- }
- }
- /* if we've stripped off "armor" and failed to match anything
- in objects[], append "mail" and try again to catch misnamed
- requests like "plate armor" and "yellow dragon scale armor" */
- if (oclass == ARMOR_CLASS && !strstri(bp, "mail")) {
- /* modifying bp's string is ok; we're about to resort
- to random armor if this also fails to match anything */
- Strcat(bp, " mail");
- goto retry;
- }
- if (!strcmpi(bp, "spinach")) {
- contents = SPINACH;
- typ = TIN;
- goto typfnd;
- }
- /* Note: not strcmpi. 2 fruits, one capital, one not, are possible.
- Also not strncmp. We used to ignore trailing text with it, but
- that resulted in "grapefruit" matching "grape" if the latter came
- earlier than the former in the fruit list. */
- {
- char *fp;
- int l, cntf;
- int blessedf, iscursedf, uncursedf, halfeatenf;
-
- blessedf = iscursedf = uncursedf = halfeatenf = 0;
- cntf = 0;
-
- fp = fruitbuf;
- for (;;) {
- if (!fp || !*fp)
- break;
- if (!strncmpi(fp, "an ", l = 3) || !strncmpi(fp, "a ", l = 2)) {
- cntf = 1;
- } else if (!cntf && digit(*fp)) {
- cntf = atoi(fp);
- while (digit(*fp))
- fp++;
- while (*fp == ' ')
- fp++;
- l = 0;
- } else if (!strncmpi(fp, "blessed ", l = 8)) {
- blessedf = 1;
- } else if (!strncmpi(fp, "cursed ", l = 7)) {
- iscursedf = 1;
- } else if (!strncmpi(fp, "uncursed ", l = 9)) {
- uncursedf = 1;
- } else if (!strncmpi(fp, "partly eaten ", l = 13)
- || !strncmpi(fp, "partially eaten ", l = 16)) {
- halfeatenf = 1;
- } else
- break;
- fp += l;
- }
-
- for (f = ffruit; f; f = f->nextf) {
- /* match type: 0=none, 1=exact, 2=singular, 3=plural */
- int ftyp = 0;
-
- if (!strcmp(fp, f->fname))
- ftyp = 1;
- else if (!strcmp(fp, makesingular(f->fname)))
- ftyp = 2;
- else if (!strcmp(fp, makeplural(f->fname)))
- ftyp = 3;
- if (ftyp) {
- typ = SLIME_MOLD;
- blessed = blessedf;
- iscursed = iscursedf;
- uncursed = uncursedf;
- halfeaten = halfeatenf;
- /* adjust count if user explicitly asked for
- singular amount (can't happen unless fruit
- has been given an already pluralized name)
- or for plural amount */
- if (ftyp == 2 && !cntf)
- cntf = 1;
- else if (ftyp == 3 && !cntf)
- cntf = 2;
- cnt = cntf;
- ftype = f->fid;
- goto typfnd;
- }
- }
- }
-
- if (!oclass && actualn) {
- short objtyp;
-
- /* Perhaps it's an artifact specified by name, not type */
- name = artifact_name(actualn, &objtyp);
- if (name) {
- typ = objtyp;
- goto typfnd;
- }
- }
-/* Let wizards wish for traps and furniture.
- * Must come after objects check so wizards can still wish for
- * trap objects like beartraps.
- * Disallow such topology tweaks for WIZKIT startup wishes.
- */
-wiztrap:
- if (wizard && !program_state.wizkit_wishing) {
- struct rm *lev;
- int trap, x = u.ux, y = u.uy;
-
- for (trap = NO_TRAP + 1; trap < TRAPNUM; trap++) {
- struct trap *t;
- const char *tname;
-
- tname = defsyms[trap_to_defsym(trap)].explanation;
- if (strncmpi(tname, bp, strlen(tname)))
- continue;
- /* found it; avoid stupid mistakes */
- if ((trap == TRAPDOOR || trap == HOLE) && !Can_fall_thru(&u.uz))
- trap = ROCKTRAP;
- if ((t = maketrap(x, y, trap)) != 0) {
- trap = t->ttyp;
- tname = defsyms[trap_to_defsym(trap)].explanation;
- pline("%s%s.", An(tname),
- (trap != MAGIC_PORTAL) ? "" : " to nowhere");
- } else
- pline("Creation of %s failed.", an(tname));
- return &zeroobj;
- }
-
- /* furniture and terrain */
- lev = &levl[x][y];
- p = eos(bp);
- if (!BSTRCMPI(bp, p - 8, "fountain")) {
- lev->typ = FOUNTAIN;
- level.flags.nfountains++;
- if (!strncmpi(bp, "magic ", 6))
- lev->blessedftn = 1;
- pline("A %sfountain.", lev->blessedftn ? "magic " : "");
- newsym(x, y);
- return &zeroobj;
- }
- if (!BSTRCMPI(bp, p - 6, "throne")) {
- lev->typ = THRONE;
- pline("A throne.");
- newsym(x, y);
- return &zeroobj;
- }
- if (!BSTRCMPI(bp, p - 4, "sink")) {
- lev->typ = SINK;
- level.flags.nsinks++;
- pline("A sink.");
- newsym(x, y);
- return &zeroobj;
- }
- /* ("water" matches "potion of water" rather than terrain) */
- if (!BSTRCMPI(bp, p - 4, "pool") || !BSTRCMPI(bp, p - 4, "moat")) {
- lev->typ = !BSTRCMPI(bp, p - 4, "pool") ? POOL : MOAT;
- del_engr_at(x, y);
- pline("A %s.", (lev->typ == POOL) ? "pool" : "moat");
- /* Must manually make kelp! */
- water_damage_chain(level.objects[x][y], TRUE);
- newsym(x, y);
- return &zeroobj;
- }
- if (!BSTRCMPI(bp, p - 4, "lava")) { /* also matches "molten lava" */
- lev->typ = LAVAPOOL;
- del_engr_at(x, y);
- pline("A pool of molten lava.");
- if (!(Levitation || Flying))
- (void) lava_effects();
- newsym(x, y);
- return &zeroobj;
- }
-
- if (!BSTRCMPI(bp, p - 5, "altar")) {
- aligntyp al;
-
- lev->typ = ALTAR;
- if (!strncmpi(bp, "chaotic ", 8))
- al = A_CHAOTIC;
- else if (!strncmpi(bp, "neutral ", 8))
- al = A_NEUTRAL;
- else if (!strncmpi(bp, "lawful ", 7))
- al = A_LAWFUL;
- else if (!strncmpi(bp, "unaligned ", 10))
- al = A_NONE;
- else /* -1 - A_CHAOTIC, 0 - A_NEUTRAL, 1 - A_LAWFUL */
- al = (!rn2(6)) ? A_NONE : rn2((int) A_LAWFUL + 2) - 1;
- lev->altarmask = Align2amask(al);
- pline("%s altar.", An(align_str(al)));
- newsym(x, y);
- return &zeroobj;
- }
-
- if (!BSTRCMPI(bp, p - 5, "grave")
- || !BSTRCMPI(bp, p - 9, "headstone")) {
- make_grave(x, y, (char *) 0);
- pline("%s.", IS_GRAVE(lev->typ) ? "A grave"
- : "Can't place a grave here");
- newsym(x, y);
- return &zeroobj;
- }
-
- if (!BSTRCMPI(bp, p - 4, "tree")) {
- lev->typ = TREE;
- pline("A tree.");
- newsym(x, y);
- block_point(x, y);
- return &zeroobj;
- }
-
- if (!BSTRCMPI(bp, p - 4, "bars")) {
- lev->typ = IRONBARS;
- pline("Iron bars.");
- newsym(x, y);
- return &zeroobj;
- }
- }
-
- if (!oclass && !typ) {
- if (!strncmpi(bp, "polearm", 7)) {
- typ = rnd_otyp_by_wpnskill(P_POLEARMS);
- goto typfnd;
- } else if (!strncmpi(bp, "hammer", 6)) {
- typ = rnd_otyp_by_wpnskill(P_HAMMER);
- goto typfnd;
- }
- }
-
- if (!oclass)
- return ((struct obj *) 0);
-any:
- if (!oclass)
- oclass = wrpsym[rn2((int) sizeof(wrpsym))];
-typfnd:
- if (typ)
- oclass = objects[typ].oc_class;
-
- /* handle some objects that are only allowed in wizard mode */
- if (typ && !wizard) {
- switch (typ) {
- case AMULET_OF_YENDOR:
- typ = FAKE_AMULET_OF_YENDOR;
- break;
- case CANDELABRUM_OF_INVOCATION:
- typ = rnd_class(TALLOW_CANDLE, WAX_CANDLE);
- break;
- case BELL_OF_OPENING:
- typ = BELL;
- break;
- case SPE_BOOK_OF_THE_DEAD:
- typ = SPE_BLANK_PAPER;
- break;
- case MAGIC_LAMP:
- typ = OIL_LAMP;
- break;
- default:
- /* catch any other non-wishable objects (venom) */
- if (objects[typ].oc_nowish)
- return (struct obj *) 0;
- break;
- }
- }
-
- /*
- * Create the object, then fine-tune it.
- */
- otmp = typ ? mksobj(typ, TRUE, FALSE) : mkobj(oclass, FALSE);
- typ = otmp->otyp, oclass = otmp->oclass; /* what we actually got */
-
- if (islit && (typ == OIL_LAMP || typ == MAGIC_LAMP || typ == BRASS_LANTERN
- || Is_candle(otmp) || typ == POT_OIL)) {
- place_object(otmp, u.ux, u.uy); /* make it viable light source */
- begin_burn(otmp, FALSE);
- obj_extract_self(otmp); /* now release it for caller's use */
- }
-
- /* if player specified a reasonable count, maybe honor it */
- if (cnt > 0 && objects[typ].oc_merge
- && (wizard || cnt < rnd(6) || (cnt <= 7 && Is_candle(otmp))
- || (cnt <= 20 && ((oclass == WEAPON_CLASS && is_ammo(otmp))
- || typ == ROCK || is_missile(otmp)))))
- otmp->quan = (long) cnt;
-
- if (oclass == VENOM_CLASS)
- otmp->spe = 1;
-
- if (spesgn == 0) {
- spe = otmp->spe;
- } else if (wizard) {
- ; /* no alteration to spe */
- } else if (oclass == ARMOR_CLASS || oclass == WEAPON_CLASS
- || is_weptool(otmp)
- || (oclass == RING_CLASS && objects[typ].oc_charged)) {
- if (spe > rnd(5) && spe > otmp->spe)
- spe = 0;
- if (spe > 2 && Luck < 0)
- spesgn = -1;
- } else {
- if (oclass == WAND_CLASS) {
- if (spe > 1 && spesgn == -1)
- spe = 1;
- } else {
- if (spe > 0 && spesgn == -1)
- spe = 0;
- }
- if (spe > otmp->spe)
- spe = otmp->spe;
- }
-
- if (spesgn == -1)
- spe = -spe;
-
- /* set otmp->spe. This may, or may not, use spe... */
- switch (typ) {
- case TIN:
- if (contents == EMPTY) {
- otmp->corpsenm = NON_PM;
- otmp->spe = 0;
- } else if (contents == SPINACH) {
- otmp->corpsenm = NON_PM;
- otmp->spe = 1;
- }
- break;
- case TOWEL:
- if (wetness)
- otmp->spe = wetness;
- break;
- case SLIME_MOLD:
- otmp->spe = ftype;
- /* Fall through */
- case SKELETON_KEY:
- case CHEST:
- case LARGE_BOX:
- case HEAVY_IRON_BALL:
- case IRON_CHAIN:
- case STATUE:
- /* otmp->cobj already done in mksobj() */
- break;
-#ifdef MAIL
- case SCR_MAIL:
- /* 0: delivered in-game via external event (or randomly for fake mail);
- 1: from bones or wishing; 2: written with marker */
- otmp->spe = 1;
- break;
-#endif
- case WAN_WISHING:
- if (!wizard) {
- otmp->spe = (rn2(10) ? -1 : 0);
- break;
- }
- /* fall through, if wizard */
- default:
- otmp->spe = spe;
- }
-
- /* set otmp->corpsenm or dragon scale [mail] */
- if (mntmp >= LOW_PM) {
- if (mntmp == PM_LONG_WORM_TAIL)
- mntmp = PM_LONG_WORM;
-
- switch (typ) {
- case TIN:
- otmp->spe = 0; /* No spinach */
- if (dead_species(mntmp, FALSE)) {
- otmp->corpsenm = NON_PM; /* it's empty */
- } else if ((!(mons[mntmp].geno & G_UNIQ) || wizard)
- && !(mvitals[mntmp].mvflags & G_NOCORPSE)
- && mons[mntmp].cnutrit != 0) {
- otmp->corpsenm = mntmp;
- }
- break;
- case CORPSE:
- if ((!(mons[mntmp].geno & G_UNIQ) || wizard)
- && !(mvitals[mntmp].mvflags & G_NOCORPSE)) {
- if (mons[mntmp].msound == MS_GUARDIAN)
- mntmp = genus(mntmp, 1);
- set_corpsenm(otmp, mntmp);
- }
- break;
- case EGG:
- mntmp = can_be_hatched(mntmp);
- /* this also sets hatch timer if appropriate */
- set_corpsenm(otmp, mntmp);
- break;
- case FIGURINE:
- if (!(mons[mntmp].geno & G_UNIQ) && !is_human(&mons[mntmp])
-#ifdef MAIL
- && mntmp != PM_MAIL_DAEMON
-#endif
- )
- otmp->corpsenm = mntmp;
- break;
- case STATUE:
- otmp->corpsenm = mntmp;
- if (Has_contents(otmp) && verysmall(&mons[mntmp]))
- delete_contents(otmp); /* no spellbook */
- otmp->spe = ishistoric ? STATUE_HISTORIC : 0;
- break;
- case SCALE_MAIL:
- /* Dragon mail - depends on the order of objects & dragons. */
- if (mntmp >= PM_GRAY_DRAGON && mntmp <= PM_YELLOW_DRAGON)
- otmp->otyp = GRAY_DRAGON_SCALE_MAIL + mntmp - PM_GRAY_DRAGON;
- break;
- }
- }
-
- /* set blessed/cursed -- setting the fields directly is safe
- * since weight() is called below and addinv() will take care
- * of luck */
- if (iscursed) {
- curse(otmp);
- } else if (uncursed) {
- otmp->blessed = 0;
- otmp->cursed = (Luck < 0 && !wizard);
- } else if (blessed) {
- otmp->blessed = (Luck >= 0 || wizard);
- otmp->cursed = (Luck < 0 && !wizard);
- } else if (spesgn < 0) {
- curse(otmp);
- }
-
- /* set eroded and erodeproof */
- if (erosion_matters(otmp)) {
- if (eroded && (is_flammable(otmp) || is_rustprone(otmp)))
- otmp->oeroded = eroded;
- if (eroded2 && (is_corrodeable(otmp) || is_rottable(otmp)))
- otmp->oeroded2 = eroded2;
- /*
- * 3.6.1: earlier versions included `&& !eroded && !eroded2' here,
- * but damageproof combined with damaged is feasible (eroded
- * armor modified by confused reading of cursed destroy armor)
- * so don't prevent player from wishing for such a combination.
- */
- if (erodeproof && (is_damageable(otmp) || otmp->otyp == CRYSKNIFE))
- otmp->oerodeproof = (Luck >= 0 || wizard);
- }
-
- /* set otmp->recharged */
- if (oclass == WAND_CLASS) {
- /* prevent wishing abuse */
- if (otmp->otyp == WAN_WISHING && !wizard)
- rechrg = 1;
- otmp->recharged = (unsigned) rechrg;
- }
-
- /* set poisoned */
- if (ispoisoned) {
- if (is_poisonable(otmp))
- otmp->opoisoned = (Luck >= 0);
- else if (oclass == FOOD_CLASS)
- /* try to taint by making it as old as possible */
- otmp->age = 1L;
- }
- /* and [un]trapped */
- if (trapped) {
- if (Is_box(otmp) || typ == TIN)
- otmp->otrapped = (trapped == 1);
- }
-
- if (isgreased)
- otmp->greased = 1;
-
- if (isdiluted && otmp->oclass == POTION_CLASS && otmp->otyp != POT_WATER)
- otmp->odiluted = 1;
-
- /* set tin variety */
- if (otmp->otyp == TIN && tvariety >= 0 && (rn2(4) || wizard))
- set_tin_variety(otmp, tvariety);
-
- if (name) {
- const char *aname;
- short objtyp;
-
- /* an artifact name might need capitalization fixing */
- aname = artifact_name(name, &objtyp);
- if (aname && objtyp == otmp->otyp)
- name = aname;
-
- /* 3.6 tribute - fix up novel */
- if (otmp->otyp == SPE_NOVEL) {
- const char *novelname;
-
- novelname = lookup_novel(name, &otmp->novelidx);
- if (novelname)
- name = novelname;
- }
-
- otmp = oname(otmp, name);
- /* name==aname => wished for artifact (otmp->oartifact => got it) */
- if (otmp->oartifact || name == aname) {
- otmp->quan = 1L;
- u.uconduct.wisharti++; /* KMH, conduct */
- }
- }
-
- /* more wishing abuse: don't allow wishing for certain artifacts */
- /* and make them pay; charge them for the wish anyway! */
- if ((is_quest_artifact(otmp)
- || (otmp->oartifact && rn2(nartifact_exist()) > 1)) && !wizard) {
- artifact_exists(otmp, safe_oname(otmp), FALSE);
- obfree(otmp, (struct obj *) 0);
- otmp = &zeroobj;
- pline("For a moment, you feel %s in your %s, but it disappears!",
- something, makeplural(body_part(HAND)));
- }
-
- if (halfeaten && otmp->oclass == FOOD_CLASS) {
- if (otmp->otyp == CORPSE)
- otmp->oeaten = mons[otmp->corpsenm].cnutrit;
- else
- otmp->oeaten = objects[otmp->otyp].oc_nutrition;
- /* (do this adjustment before setting up object's weight) */
- consume_oeaten(otmp, 1);
- }
- otmp->owt = weight(otmp);
- if (very && otmp->otyp == HEAVY_IRON_BALL)
- otmp->owt += IRON_BALL_W_INCR;
- else if (gsize > 1 && otmp->globby)
- /* 0: unspecified => small; 1: small => keep default owt of 20;
- 2: medium => 120; 3: large => 320; 4: very large => 520 */
- otmp->owt += 100 + (gsize - 2) * 200;
-
- return otmp;
-}
-
-int
-rnd_class(first, last)
-int first, last;
-{
- int i, x, sum = 0;
-
- if (first == last)
- return first;
- for (i = first; i <= last; i++)
- sum += objects[i].oc_prob;
- if (!sum) /* all zero */
- return first + rn2(last - first + 1);
- x = rnd(sum);
- for (i = first; i <= last; i++)
- if (objects[i].oc_prob && (x -= objects[i].oc_prob) <= 0)
- return i;
- return 0;
-}
-
-STATIC_OVL const char *
-Japanese_item_name(i)
-int i;
-{
- struct Jitem *j = Japanese_items;
-
- while (j->item) {
- if (i == j->item)
- return j->name;
- j++;
- }
- return (const char *) 0;
-}
-
-const char *
-suit_simple_name(suit)
-struct obj *suit;
-{
- const char *suitnm, *esuitp;
-
- if (Is_dragon_mail(suit))
- return "dragon mail"; /* <color> dragon scale mail */
- else if (Is_dragon_scales(suit))
- return "dragon scales";
- suitnm = OBJ_NAME(objects[suit->otyp]);
- esuitp = eos((char *) suitnm);
- if (strlen(suitnm) > 5 && !strcmp(esuitp - 5, " mail"))
- return "mail"; /* most suits fall into this category */
- else if (strlen(suitnm) > 7 && !strcmp(esuitp - 7, " jacket"))
- return "jacket"; /* leather jacket */
- /* suit is lame but armor is ambiguous and body armor is absurd */
- return "suit";
-}
-
-const char *
-cloak_simple_name(cloak)
-struct obj *cloak;
-{
- if (cloak) {
- switch (cloak->otyp) {
- case ROBE:
- return "robe";
- case MUMMY_WRAPPING:
- return "wrapping";
- case ALCHEMY_SMOCK:
- return (objects[cloak->otyp].oc_name_known && cloak->dknown)
- ? "smock"
- : "apron";
- default:
- break;
- }
- }
- return "cloak";
-}
-
-/* helm vs hat for messages */
-const char *
-helm_simple_name(helmet)
-struct obj *helmet;
-{
- /*
- * There is some wiggle room here; the result has been chosen
- * for consistency with the "protected by hard helmet" messages
- * given for various bonks on the head: headgear that provides
- * such protection is a "helm", that which doesn't is a "hat".
- *
- * elven leather helm / leather hat -> hat
- * dwarvish iron helm / hard hat -> helm
- * The rest are completely straightforward:
- * fedora, cornuthaum, dunce cap -> hat
- * all other types of helmets -> helm
- */
- return (helmet && !is_metallic(helmet)) ? "hat" : "helm";
-}
-
-const char *
-mimic_obj_name(mtmp)
-struct monst *mtmp;
-{
- if (mtmp->m_ap_type == M_AP_OBJECT) {
- if (mtmp->mappearance == GOLD_PIECE)
- return "gold";
- if (mtmp->mappearance != STRANGE_OBJECT)
- return simple_typename(mtmp->mappearance);
- }
- return "whatcha-may-callit";
-}
-
-/*
- * Construct a query prompt string, based around an object name, which is
- * guaranteed to fit within [QBUFSZ]. Takes an optional prefix, three
- * choices for filling in the middle (two object formatting functions and a
- * last resort literal which should be very short), and an optional suffix.
- */
-char *
-safe_qbuf(qbuf, qprefix, qsuffix, obj, func, altfunc, lastR)
-char *qbuf; /* output buffer */
-const char *qprefix, *qsuffix;
-struct obj *obj;
-char *FDECL((*func), (OBJ_P)), *FDECL((*altfunc), (OBJ_P));
-const char *lastR;
-{
- char *bufp, *endp;
- /* convert size_t (or int for ancient systems) to ordinary unsigned */
- unsigned len, lenlimit,
- len_qpfx = (unsigned) (qprefix ? strlen(qprefix) : 0),
- len_qsfx = (unsigned) (qsuffix ? strlen(qsuffix) : 0),
- len_lastR = (unsigned) strlen(lastR);
-
- lenlimit = QBUFSZ - 1;
- endp = qbuf + lenlimit;
- /* sanity check, aimed mainly at paniclog (it's conceivable for
- the result of short_oname() to be shorter than the length of
- the last resort string, but we ignore that possibility here) */
- if (len_qpfx > lenlimit)
- impossible("safe_qbuf: prefix too long (%u characters).", len_qpfx);
- else if (len_qpfx + len_qsfx > lenlimit)
- impossible("safe_qbuf: suffix too long (%u + %u characters).",
- len_qpfx, len_qsfx);
- else if (len_qpfx + len_lastR + len_qsfx > lenlimit)
- impossible("safe_qbuf: filler too long (%u + %u + %u characters).",
- len_qpfx, len_lastR, len_qsfx);
-
- /* the output buffer might be the same as the prefix if caller
- has already partially filled it */
- if (qbuf == qprefix) {
- /* prefix is already in the buffer */
- *endp = '\0';
- } else if (qprefix) {
- /* put prefix into the buffer */
- (void) strncpy(qbuf, qprefix, lenlimit);
- *endp = '\0';
- } else {
- /* no prefix; output buffer starts out empty */
- qbuf[0] = '\0';
- }
- len = (unsigned) strlen(qbuf);
-
- if (len + len_lastR + len_qsfx > lenlimit) {
- /* too long; skip formatting, last resort output is truncated */
- if (len < lenlimit) {
- (void) strncpy(&qbuf[len], lastR, lenlimit - len);
- *endp = '\0';
- len = (unsigned) strlen(qbuf);
- if (qsuffix && len < lenlimit) {
- (void) strncpy(&qbuf[len], qsuffix, lenlimit - len);
- *endp = '\0';
- /* len = (unsigned) strlen(qbuf); */
- }
- }
- } else {
- /* suffix and last resort are guaranteed to fit */
- len += len_qsfx; /* include the pending suffix */
- /* format the object */
- bufp = short_oname(obj, func, altfunc, lenlimit - len);
- if (len + strlen(bufp) <= lenlimit)
- Strcat(qbuf, bufp); /* formatted name fits */
- else
- Strcat(qbuf, lastR); /* use last resort */
- releaseobuf(bufp);
-
- if (qsuffix)
- Strcat(qbuf, qsuffix);
- }
- /* assert( strlen(qbuf) < QBUFSZ ); */
- return qbuf;
-}
-
-/*objnam.c*/