named-fruit manipulation
Add some new routines for dealing with fruit. I had hoped they would let the existing fruit handling be simplified quite a bit, but the improvement wasn't great. However, they're also groundwork for fixing an old bug.
This commit is contained in:
@@ -1621,6 +1621,9 @@ E char *FDECL(simple_typename, (int));
|
|||||||
E boolean FDECL(obj_is_pname, (struct obj *));
|
E boolean FDECL(obj_is_pname, (struct obj *));
|
||||||
E char *FDECL(distant_name, (struct obj *, char *(*)(OBJ_P)));
|
E char *FDECL(distant_name, (struct obj *, char *(*)(OBJ_P)));
|
||||||
E char *FDECL(fruitname, (BOOLEAN_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(xname, (struct obj *));
|
||||||
E char *FDECL(mshot_xname, (struct obj *));
|
E char *FDECL(mshot_xname, (struct obj *));
|
||||||
E boolean FDECL(the_unique_obj, (struct obj *));
|
E boolean FDECL(the_unique_obj, (struct obj *));
|
||||||
|
|||||||
10
src/bones.c
10
src/bones.c
@@ -45,14 +45,10 @@ STATIC_OVL void
|
|||||||
goodfruit(id)
|
goodfruit(id)
|
||||||
int id;
|
int id;
|
||||||
{
|
{
|
||||||
register struct fruit *f;
|
struct fruit *f = fruit_from_indx(-id);
|
||||||
|
|
||||||
for (f = ffruit; f; f = f->nextf) {
|
if (f)
|
||||||
if (f->fid == -id) {
|
f->fid = id;
|
||||||
f->fid = id;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC_OVL void
|
STATIC_OVL void
|
||||||
|
|||||||
11
src/cmd.c
11
src/cmd.c
@@ -2471,14 +2471,15 @@ int final;
|
|||||||
/* named fruit debugging (doesn't really belong here...); to enable,
|
/* named fruit debugging (doesn't really belong here...); to enable,
|
||||||
include 'fruit' in DEBUGFILES list (even though it isn't a file...) */
|
include 'fruit' in DEBUGFILES list (even though it isn't a file...) */
|
||||||
if (wizard && explicitdebug("fruit")) {
|
if (wizard && explicitdebug("fruit")) {
|
||||||
int fcount = 0;
|
|
||||||
struct fruit *f;
|
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) {
|
for (f = ffruit; f; f = f->nextf) {
|
||||||
Sprintf(buf, "Fruit %d ", ++fcount);
|
Sprintf(buf, "Fruit #%d ", f->fid);
|
||||||
Sprintf(buf2, "%s (id %d)", f->fname, f->fid);
|
enl_msg(buf, "is ", "was ", f->fname, "");
|
||||||
enl_msg(buf, "is ", "was ", buf2, "");
|
|
||||||
}
|
}
|
||||||
enl_msg("The current fruit ", "is ", "was ", pl_fruit, "");
|
enl_msg("The current fruit ", "is ", "was ", pl_fruit, "");
|
||||||
Sprintf(buf, "%d", flags.made_fruit);
|
Sprintf(buf, "%d", flags.made_fruit);
|
||||||
|
|||||||
152
src/objnam.c
152
src/objnam.c
@@ -251,6 +251,135 @@ boolean juice; /* whether or not to append " juice" to the name */
|
|||||||
return buf;
|
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 *
|
char *
|
||||||
xname(obj)
|
xname(obj)
|
||||||
struct obj *obj;
|
struct obj *obj;
|
||||||
@@ -388,23 +517,20 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */
|
|||||||
break;
|
break;
|
||||||
case FOOD_CLASS:
|
case FOOD_CLASS:
|
||||||
if (typ == SLIME_MOLD) {
|
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) {
|
if (!f) {
|
||||||
impossible("Bad fruit #%d?", obj->spe);
|
impossible("Bad fruit #%d?", obj->spe);
|
||||||
Strcpy(buf, "fruit");
|
Strcpy(buf, "fruit");
|
||||||
} else if (pluralize) {
|
} else {
|
||||||
/* ick; already pluralized fruit names
|
Strcpy(buf, f->fname);
|
||||||
are allowed--we want to try to avoid
|
if (pluralize) {
|
||||||
adding a redundant plural suffix */
|
/* ick; already pluralized fruit names
|
||||||
Strcpy(buf, makeplural(makesingular(buf)));
|
are allowed--we want to try to avoid
|
||||||
pluralize = FALSE;
|
adding a redundant plural suffix */
|
||||||
|
Strcpy(buf, makeplural(makesingular(buf)));
|
||||||
|
pluralize = FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2341,32 +2341,32 @@ boolean tinitial, tfrom_file;
|
|||||||
return;
|
return;
|
||||||
if (!initial) {
|
if (!initial) {
|
||||||
struct fruit *f;
|
struct fruit *f;
|
||||||
|
int fnum = 0;
|
||||||
|
|
||||||
num = 0;
|
/* count number of named fruits; if 'op' is found among them,
|
||||||
for (f = ffruit; f; f = f->nextf) {
|
then the count doesn't matter because we won't be adding it */
|
||||||
if (!strcmp(op, f->fname))
|
f = fruit_from_name(op, FALSE, &fnum);
|
||||||
break;
|
if (!f) {
|
||||||
num++;
|
if (!flags.made_fruit)
|
||||||
}
|
forig = fruit_from_name(pl_fruit, FALSE, (int *) 0);
|
||||||
if (!flags.made_fruit) {
|
|
||||||
for (forig = ffruit; forig; forig = forig->nextf) {
|
if (!forig && fnum >= 100) {
|
||||||
if (!strcmp(pl_fruit, forig->fname)) {
|
pline("Doing that so many times isn't very fruitful.");
|
||||||
break;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!forig && num >= 100) {
|
|
||||||
pline("Doing that so many times isn't very fruitful.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
goodfruit:
|
goodfruit:
|
||||||
nmcpy(pl_fruit, op, PL_FSIZ);
|
nmcpy(pl_fruit, op, PL_FSIZ);
|
||||||
sanitize_name(pl_fruit);
|
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)
|
if (!*pl_fruit)
|
||||||
nmcpy(pl_fruit, "slime mold", PL_FSIZ);
|
nmcpy(pl_fruit, "slime mold", PL_FSIZ);
|
||||||
if (!initial) {
|
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);
|
(void) fruitadd(pl_fruit, forig);
|
||||||
pline("Fruit is now \"%s\".", pl_fruit);
|
pline("Fruit is now \"%s\".", pl_fruit);
|
||||||
}
|
}
|
||||||
@@ -5574,7 +5574,6 @@ struct fruit *replace_fruit;
|
|||||||
/* disallow naming after other foods (since it'd be impossible
|
/* disallow naming after other foods (since it'd be impossible
|
||||||
* to tell the difference)
|
* to tell the difference)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; i++) {
|
for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; i++) {
|
||||||
if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
|
if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
|
||||||
found = TRUE;
|
found = TRUE;
|
||||||
@@ -5584,15 +5583,15 @@ struct fruit *replace_fruit;
|
|||||||
{
|
{
|
||||||
char *c;
|
char *c;
|
||||||
|
|
||||||
c = pl_fruit;
|
|
||||||
|
|
||||||
for (c = pl_fruit; *c >= '0' && *c <= '9'; c++)
|
for (c = pl_fruit; *c >= '0' && *c <= '9'; c++)
|
||||||
;
|
continue;
|
||||||
if (isspace((uchar) *c) || *c == 0)
|
if (isspace((uchar) *c) || *c == 0)
|
||||||
numeric = TRUE;
|
numeric = TRUE;
|
||||||
}
|
}
|
||||||
if (found || numeric || !strncmp(str, "cursed ", 7)
|
if (found || numeric
|
||||||
|| !strncmp(str, "uncursed ", 9) || !strncmp(str, "blessed ", 8)
|
|| !strncmp(str, "cursed ", 7)
|
||||||
|
|| !strncmp(str, "uncursed ", 9)
|
||||||
|
|| !strncmp(str, "blessed ", 8)
|
||||||
|| !strncmp(str, "partly eaten ", 13)
|
|| !strncmp(str, "partly eaten ", 13)
|
||||||
|| (!strncmp(str, "tin of ", 7)
|
|| (!strncmp(str, "tin of ", 7)
|
||||||
&& (!strcmp(str + 7, "spinach")
|
&& (!strcmp(str + 7, "spinach")
|
||||||
@@ -5615,42 +5614,39 @@ struct fruit *replace_fruit;
|
|||||||
*/
|
*/
|
||||||
flags.made_fruit = FALSE;
|
flags.made_fruit = FALSE;
|
||||||
if (replace_fruit) {
|
if (replace_fruit) {
|
||||||
for (f = ffruit; f; f = f->nextf) {
|
/* replace_fruit is already part of the fruit chain;
|
||||||
if (f == replace_fruit) {
|
update it in place rather than looking it up again */
|
||||||
copynchars(f->fname, str, PL_FSIZ - 1);
|
f = replace_fruit;
|
||||||
goto nonew;
|
copynchars(f->fname, str, PL_FSIZ - 1);
|
||||||
}
|
goto nonew;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* not user_supplied, so assumed to be from bones */
|
/* not user_supplied, so assumed to be from bones */
|
||||||
copynchars(altname, str, PL_FSIZ - 1);
|
copynchars(altname, str, PL_FSIZ - 1);
|
||||||
sanitize_name(altname);
|
sanitize_name(altname);
|
||||||
flags.made_fruit = TRUE; /* for safety. Any fruit name added from a
|
flags.made_fruit = TRUE; /* for safety. Any fruit name added from a
|
||||||
bones level should exist anyway. */
|
* bones level should exist anyway. */
|
||||||
}
|
}
|
||||||
for (f = ffruit; f; f = f->nextf) {
|
f = fruit_from_name(*altname ? altname : str, FALSE, &highest_fruit_id);
|
||||||
if (f->fid > highest_fruit_id)
|
if (f)
|
||||||
highest_fruit_id = f->fid;
|
goto nonew;
|
||||||
if (!strncmp(str, f->fname, PL_FSIZ - 1)
|
|
||||||
|| (*altname && !strcmp(altname, f->fname)))
|
/* Maximum number of named fruits is 127, even if obj->spe can
|
||||||
goto nonew;
|
handle bigger values. If adding another fruit would overflow,
|
||||||
}
|
use a random fruit instead... we've got a lot to choose from.
|
||||||
/* if adding another fruit would overflow spe, use a random
|
|
||||||
fruit instead... we've got a lot to choose from.
|
|
||||||
current_fruit remains as is. */
|
current_fruit remains as is. */
|
||||||
if (highest_fruit_id >= 127)
|
if (highest_fruit_id >= 127)
|
||||||
return rnd(127);
|
return rnd(127);
|
||||||
|
|
||||||
f = newfruit();
|
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);
|
copynchars(f->fname, *altname ? altname : str, PL_FSIZ - 1);
|
||||||
f->fid = ++highest_fruit_id;
|
f->fid = ++highest_fruit_id;
|
||||||
/* we used to go out of our way to add it at the end of the list,
|
/* 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 */
|
but the order is arbitrary so use simpler insertion at start */
|
||||||
f->nextf = ffruit;
|
f->nextf = ffruit;
|
||||||
ffruit = f;
|
ffruit = f;
|
||||||
nonew:
|
nonew:
|
||||||
if (user_specified)
|
if (user_specified)
|
||||||
context.current_fruit = f->fid;
|
context.current_fruit = f->fid;
|
||||||
return f->fid;
|
return f->fid;
|
||||||
|
|||||||
Reference in New Issue
Block a user