From: nethack.rankin Date: Sat, 14 Aug 2010 00:31:59 +0000 (+0000) Subject: fix #H2161 - writing scrolls based on description (trunk only) X-Git-Tag: MOVE2GIT~294 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=fefeda5422b8b016325606dc638a884bf90722e2;p=nethack fix #H2161 - writing scrolls based on description (trunk only) From a bug report, you could write scrolls by type name ("magic mapping") if you had that type of scroll in your discoveries list via assigning a name to an unknown scroll ("scroll labeled FOOBIE BLETCH called foo"). Being on that list was enough to treat the type as known when writing scrolls and books. And he fealt that it was abusive to be able to collect and name a lot of unknown scrolls and then write favorite ones which had good odds of being in the collected set. This changes it to the original intent: if your discoveries list has FOOBIE BLETCH on it, you can write a scroll by that label (since we decided way back when that a scroll's label was its magic, to explain how a blind hero can read any scroll whose description is known even though they aren't constructed in braille). If you have identified the type ("scroll of magic mapping labeled FOOBIE BLETCH") then you can write by type or by description, but you can no longer write one by type when only the description is known. There is a potential can-of-worms bug here: if you walked across a "scroll labeled YUM YUM" but have not assigned it any name, you've still learned its magic words and ought to be able to write a scroll of YUM YUM. We don't have any mechanism to track items which have been observed but not been put on the discoveries list. This patch plugs one obvious hole, by scanning inventory to treat any seen scroll labels there as an extension of the discoveries list. But the more general case of something once seen but not named or currently held is ignored. This also adds writing scrolls by the user-assigned name, so if your discoveries list has "scroll labeled FOOBIE BLETCH called foo" you can write either foo or FOOBIE BLETCH to get the scroll. I'm not sure the bug report advocated that--parts of it were a bit confusing, at least to me--and I'm not completely sure that we want to have it, but it does work. Without it, you got "no such thing as \"foo\"", which seems counter-intuitive when "foo" is there in plain sight on your discoveries list. The new code chooses randomly if multiple scrolls have been called "foo". And if you've called something by an actual object name, it uses your knowledge of that object rather than anything you've given its name to. In other words, if you have "scroll labeled YUM YUM called magic mapping" and try to write magic mapping, it will use your knowledge--or lack of same--about scroll of magic mapping rather than scroll labeled YUM YUM to decide whether you'll succeed. There is also a minor tweak in the chance to write a completely unknown scroll or book. Wizards almost never failed once their Luck was 5 or more; using rnl(5) instead of rnl(3) requires Luck 11 rather than just 5 to get that ~39/40 chance of success. Non-wizards didn't change. Lastly, this fixes an unrelated bug when writing spellbooks. The message "the spellbook warps strangely, then turns " works okay when is "red" or even "ragged", but not so well when it's "vellum". A handful of book descriptions refer to the item composition rather than the appearance of the cover, and turning into a new composition needs different phrasing. I just tweaked it to be "turns into vellum", which is probably suboptimal (particularly for the book description "cloth" :-). --- diff --git a/doc/fixes35.0 b/doc/fixes35.0 index 4fe748022..1387e900e 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -333,6 +333,10 @@ wearing elven cloak auto-discovered it even when already stealthy putting on ring of stealth never auto-discovered it forgetting spells due to amnesia now sets memory retention to zero instead of removing them from hero's list of known spells +shouldn't have been able write scrolls by guessing type name when they're + only partly known via name assignment +scrolls given names can be written by assigned name as well as by description +fix writing feedback "the spellbook warps strangely, then turns parchment" Platform- and/or Interface-Specific Fixes diff --git a/src/write.c b/src/write.c index b3e3cfa6c..c2aef53d7 100644 --- a/src/write.c +++ b/src/write.c @@ -4,6 +4,8 @@ #include "hack.h" STATIC_DCL int FDECL(cost,(struct obj *)); +STATIC_DCL boolean FDECL(label_known, (int,struct obj *)); +STATIC_DCL char *FDECL(new_book_description, (int,char *)); /* * returns basecost of a scroll or a spellbook @@ -64,6 +66,36 @@ register struct obj *otmp; return(1000); } +/* decide whether the hero knowns a particular scroll's label; + unfortunately, we can't track things are haven't been added to + the discoveries list and aren't present in current inventory, + so some scrolls with ought to yield True will end up False */ +STATIC_OVL boolean +label_known(scrolltype, objlist) +int scrolltype; +struct obj *objlist; +{ + struct obj *otmp; + + /* only scrolls */ + if (objects[scrolltype].oc_class != SCROLL_CLASS) + return FALSE; + /* type known implies full discovery; otherwise, + user-assigned name implies partial discovery */ + if (objects[scrolltype].oc_name_known || objects[scrolltype].oc_uname) + return TRUE; + /* check inventory, including carried containers with known contents */ + for (otmp = objlist; otmp; otmp = otmp->nobj) { + if (otmp->otyp == scrolltype && otmp->dknown) + return TRUE; + if (Has_contents(otmp) && otmp->cknown && + label_known(scrolltype, otmp->cobj)) + return TRUE; + } + /* not found */ + return FALSE; +} + static NEARDATA const char write_on[] = { SCROLL_CLASS, SPBOOK_CLASS, 0 }; int @@ -76,7 +108,7 @@ register struct obj *pen; int basecost, actualcost; int curseval; char qbuf[QBUFSZ]; - int first, last, i; + int first, last, i, deferred, deferralchance; boolean by_descr = FALSE; const char *typeword; @@ -129,6 +161,8 @@ register struct obj *pen; (void)mungspaces(bp + 1); /* remove the extra space */ } + deferred = 0; /* not any scroll or book */ + deferralchance = 0; /* incremented for each oc_uname match */ first = bases[(int)paper->oclass]; last = bases[(int)paper->oclass + 1] - 1; for (i = first; i <= last; i++) { @@ -141,6 +175,29 @@ register struct obj *pen; by_descr = TRUE; goto found; } + /* user-assigned name might match real name of a later + entry, so we don't simply use first match with it; + also, player might assign same name multiple times + and if so, we choose one of those matches randomly */ + if (objects[i].oc_uname && !strcmpi(objects[i].oc_uname, nm) && + /* first match: chance incremented to 1, + !rn2(1) is 1, we remember i; + second match: chance incremented to 2, + !rn2(2) has 1/2 chance to replace i; + third match: chance incremented to 3, + !rn2(3) has 1/3 chance to replace i + and 2/3 chance to keep previous 50:50 + choice; so on for higher match counts */ + !rn2(++deferralchance)) + deferred = i; + } + /* writing by user-assigned name is same as by description: + fails for books, works for scrolls (having an assigned + type name guarantees presence on discoveries list) */ + if (deferred) { + i = deferred; + by_descr = TRUE; + goto found; } There("is no such %s!", typeword); @@ -202,10 +259,35 @@ found: } pen->spe -= actualcost; - /* can't write if we don't know it - unless we're lucky */ + /* + * Writing by name requires that the hero knows the scroll or + * book type. One has previously been read (and its effect + * was evident) or been ID'd via scroll/spell/throne and it + * will be on the discoveries list. + * (Previous versions allowed scrolls and books to be written + * by type name if they were on the discoveries list via being + * given a user-assigned name, even though doing the latter + * doesn't--and shouldn't--make the actual type become known.) + * + * Writing by description requires that the hero knows the + * description (a scroll's label, that is, since books by_descr + * are rejected above). BUG: We can only do this for known + * scrolls and for the case where the player has assigned a + * name to put it onto the discoveries list; we lack a way to + * track other scrolls which have been seen closely enough to + * read the label without then being ID'd or named. The only + * exception is for currently carried inventory, where we can + * check for one [with its dknown bit set] of the same type. + * + * Normal requirements can be overridden if hero is Lucky. + */ + + /* if known, then either by-name or by-descr works */ if (!objects[new_obj->otyp].oc_name_known && - !objects[new_obj->otyp].oc_uname && - rnl(Role_if(PM_WIZARD) ? 3 : 15)) { + /* else if named, then only by-descr works */ + !(by_descr && label_known(new_obj->otyp, invent)) && + /* and Luck might override after both checks have failed */ + rnl(Role_if(PM_WIZARD) ? 5 : 15)) { You("%s to write that.", by_descr ? "fail" : "don't know how"); /* scrolls disappear, spellbooks don't */ if (paper->oclass == SPBOOK_CLASS) { @@ -245,7 +327,7 @@ found: if (new_obj->oclass == SPBOOK_CLASS) { /* acknowledge the change in the object's description... */ pline_The("spellbook warps strangely, then turns %s.", - OBJ_DESCR(objects[new_obj->otyp])); + new_book_description(new_obj->otyp, namebuf)); } new_obj->blessed = (curseval > 0); new_obj->cursed = (curseval < 0); @@ -258,4 +340,37 @@ found: return(1); } +/* most book descriptions refer to cover appearance, so we can issue a + message for converting a plain book into one of those with something + like "the spellbook turns red" or "the spellbook turns ragged"; + but some descriptions refer to composition and "the book turns vellum" + looks funny, so we want to insert "into " prior to such descriptions; + even that's rather iffy, indicating that such descriptions probably + ought to be eliminated (especially "cloth"!) */ +STATIC_OVL char * +new_book_description(booktype, outbuf) +int booktype; +char *outbuf; +{ + /* subset of description strings from objects.c; if it grows + much, we may need to add a new flag field to objects[] instead */ + static const char *const compositions[] = { + "parchment", "vellum", "cloth", +#if 0 + "canvas", "hardcover", /* not used */ + "papyrus", /* not applicable--can't be produced via writing */ +#endif /*0*/ + 0 + }; + const char *descr, *const *comp_p; + int idx; + + descr = OBJ_DESCR(objects[booktype]); + for (comp_p = compositions; *comp_p; ++comp_p) + if (!strcmpi(descr, *comp_p)) break; + + Sprintf(outbuf, "%s%s", *comp_p ? "into " : "", descr); + return outbuf; +} + /*write.c*/