From: PatR Date: Tue, 4 Jul 2017 01:57:50 +0000 (-0700) Subject: named-fruit manipulation X-Git-Tag: NetHack-3.6.1_RC01~456 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ca84c8e0ffc8b54163d5249fa6a3983497b652e8;p=nethack 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. --- diff --git a/include/extern.h b/include/extern.h index 705b19cdc..10e8ea54a 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1621,6 +1621,9 @@ E char *FDECL(simple_typename, (int)); 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 *)); diff --git a/src/bones.c b/src/bones.c index 6784259c6..6f35d2631 100644 --- a/src/bones.c +++ b/src/bones.c @@ -45,14 +45,10 @@ STATIC_OVL void 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 diff --git a/src/cmd.c b/src/cmd.c index ade3352cd..6d1a0f113 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -2471,14 +2471,15 @@ int final; /* 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); diff --git a/src/objnam.c b/src/objnam.c index e5cbbc009..e583d7a5c 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -251,6 +251,135 @@ boolean juice; /* whether or not to append " juice" to the name */ 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; @@ -388,23 +517,20 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */ 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; } diff --git a/src/options.c b/src/options.c index b7ee5f0ba..38d258c6f 100644 --- a/src/options.c +++ b/src/options.c @@ -2341,32 +2341,32 @@ boolean tinitial, tfrom_file; 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); } @@ -5574,7 +5574,6 @@ struct fruit *replace_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; @@ -5584,15 +5583,15 @@ struct fruit *replace_fruit; { 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") @@ -5615,42 +5614,39 @@ struct fruit *replace_fruit; */ 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;