Skip to content

Commit

Permalink
fix #H2161 - writing scrolls based on description (trunk only)
Browse files Browse the repository at this point in the history
     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 <new description>"
works okay when <new description> 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" :-).
  • Loading branch information
nethack.rankin committed Aug 14, 2010
1 parent 0ee120b commit fefeda5
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 5 deletions.
4 changes: 4 additions & 0 deletions doc/fixes35.0
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
125 changes: 120 additions & 5 deletions src/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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;

Expand Down Expand Up @@ -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++) {
Expand All @@ -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);
Expand Down Expand Up @@ -202,10 +259,35 @@ register struct obj *pen;
}
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) {
Expand Down Expand Up @@ -245,7 +327,7 @@ register struct obj *pen;
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);
Expand All @@ -258,4 +340,37 @@ register struct obj *pen;
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*/

0 comments on commit fefeda5

Please sign in to comment.