E boolean FDECL(obj_is_pname, (struct obj *));
E char *FDECL(distant_name, (struct obj *, char *(*)(OBJ_P)));
E char *FDECL(fruitname, (BOOLEAN_P));
+E struct fruit *FDECL(fruit_from_indx, (int));
+E struct fruit *FDECL(fruit_from_name, (const char *, BOOLEAN_P, int *));
+E void FDECL(reorder_fruit, (BOOLEAN_P));
E char *FDECL(xname, (struct obj *));
E char *FDECL(mshot_xname, (struct obj *));
E boolean FDECL(the_unique_obj, (struct obj *));
goodfruit(id)
int id;
{
- register struct fruit *f;
+ struct fruit *f = fruit_from_indx(-id);
- for (f = ffruit; f; f = f->nextf) {
- if (f->fid == -id) {
- f->fid = id;
- return;
- }
- }
+ if (f)
+ f->fid = id;
}
STATIC_OVL void
/* named fruit debugging (doesn't really belong here...); to enable,
include 'fruit' in DEBUGFILES list (even though it isn't a file...) */
if (wizard && explicitdebug("fruit")) {
- int fcount = 0;
struct fruit *f;
- char buf2[BUFSZ];
+ reorder_fruit(TRUE); /* sort by fruit index, from low to high;
+ * this modifies the ffruit chain, so could
+ * possibly mask or even introduce a problem,
+ * but it does useful sanity checking */
for (f = ffruit; f; f = f->nextf) {
- Sprintf(buf, "Fruit %d ", ++fcount);
- Sprintf(buf2, "%s (id %d)", f->fname, f->fid);
- enl_msg(buf, "is ", "was ", buf2, "");
+ Sprintf(buf, "Fruit #%d ", f->fid);
+ enl_msg(buf, "is ", "was ", f->fname, "");
}
enl_msg("The current fruit ", "is ", "was ", pl_fruit, "");
Sprintf(buf, "%d", flags.made_fruit);
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;
break;
case FOOD_CLASS:
if (typ == SLIME_MOLD) {
- register struct fruit *f;
+ struct fruit *f = fruit_from_indx(obj->spe);
- for (f = ffruit; f; f = f->nextf) {
- if (f->fid == obj->spe) {
- Strcpy(buf, f->fname);
- break;
- }
- }
if (!f) {
impossible("Bad fruit #%d?", obj->spe);
Strcpy(buf, "fruit");
- } else 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;
+ } 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;
}
return;
if (!initial) {
struct fruit *f;
+ int fnum = 0;
- num = 0;
- for (f = ffruit; f; f = f->nextf) {
- if (!strcmp(op, f->fname))
- break;
- num++;
- }
- if (!flags.made_fruit) {
- for (forig = ffruit; forig; forig = forig->nextf) {
- if (!strcmp(pl_fruit, forig->fname)) {
- break;
- }
+ /* count number of named fruits; if 'op' is found among them,
+ then the count doesn't matter because we won't be adding it */
+ f = fruit_from_name(op, FALSE, &fnum);
+ if (!f) {
+ if (!flags.made_fruit)
+ forig = fruit_from_name(pl_fruit, FALSE, (int *) 0);
+
+ if (!forig && fnum >= 100) {
+ pline("Doing that so many times isn't very fruitful.");
+ return;
}
}
- if (!forig && num >= 100) {
- pline("Doing that so many times isn't very fruitful.");
- return;
- }
}
goodfruit:
nmcpy(pl_fruit, op, PL_FSIZ);
sanitize_name(pl_fruit);
- /* OBJ_NAME(objects[SLIME_MOLD]) won't work after initialization */
+ /* OBJ_NAME(objects[SLIME_MOLD]) won't work for this after
+ initialization; it gets changed to generic "fruit" */
if (!*pl_fruit)
nmcpy(pl_fruit, "slime mold", PL_FSIZ);
if (!initial) {
+ /* if 'forig' is nonNull, we replace it rather than add
+ a new fruit; it can only be nonNull if no fruits have
+ been created since the previous name was put in place */
(void) fruitadd(pl_fruit, forig);
pline("Fruit is now \"%s\".", pl_fruit);
}
/* disallow naming after other foods (since it'd be impossible
* to tell the difference)
*/
-
for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; i++) {
if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
found = TRUE;
{
char *c;
- c = pl_fruit;
-
for (c = pl_fruit; *c >= '0' && *c <= '9'; c++)
- ;
+ continue;
if (isspace((uchar) *c) || *c == 0)
numeric = TRUE;
}
- if (found || numeric || !strncmp(str, "cursed ", 7)
- || !strncmp(str, "uncursed ", 9) || !strncmp(str, "blessed ", 8)
+ if (found || numeric
+ || !strncmp(str, "cursed ", 7)
+ || !strncmp(str, "uncursed ", 9)
+ || !strncmp(str, "blessed ", 8)
|| !strncmp(str, "partly eaten ", 13)
|| (!strncmp(str, "tin of ", 7)
&& (!strcmp(str + 7, "spinach")
*/
flags.made_fruit = FALSE;
if (replace_fruit) {
- for (f = ffruit; f; f = f->nextf) {
- if (f == replace_fruit) {
- copynchars(f->fname, str, PL_FSIZ - 1);
- goto nonew;
- }
- }
+ /* replace_fruit is already part of the fruit chain;
+ update it in place rather than looking it up again */
+ f = replace_fruit;
+ copynchars(f->fname, str, PL_FSIZ - 1);
+ goto nonew;
}
} else {
/* not user_supplied, so assumed to be from bones */
copynchars(altname, str, PL_FSIZ - 1);
sanitize_name(altname);
flags.made_fruit = TRUE; /* for safety. Any fruit name added from a
- bones level should exist anyway. */
- }
- for (f = ffruit; f; f = f->nextf) {
- if (f->fid > highest_fruit_id)
- highest_fruit_id = f->fid;
- if (!strncmp(str, f->fname, PL_FSIZ - 1)
- || (*altname && !strcmp(altname, f->fname)))
- goto nonew;
+ * bones level should exist anyway. */
}
- /* if adding another fruit would overflow spe, use a random
- fruit instead... we've got a lot to choose from.
+ f = fruit_from_name(*altname ? altname : str, FALSE, &highest_fruit_id);
+ if (f)
+ goto nonew;
+
+ /* Maximum number of named fruits is 127, even if obj->spe can
+ handle bigger values. If adding another fruit would overflow,
+ use a random fruit instead... we've got a lot to choose from.
current_fruit remains as is. */
if (highest_fruit_id >= 127)
return rnd(127);
f = newfruit();
- (void) memset((genericptr_t)f, 0, sizeof(struct fruit));
+ (void) memset((genericptr_t) f, 0, sizeof (struct fruit));
copynchars(f->fname, *altname ? altname : str, PL_FSIZ - 1);
f->fid = ++highest_fruit_id;
/* we used to go out of our way to add it at the end of the list,
but the order is arbitrary so use simpler insertion at start */
f->nextf = ffruit;
ffruit = f;
-nonew:
+ nonew:
if (user_specified)
context.current_fruit = f->fid;
return f->fid;