From a41176d22b33f2d308ba64f52a5716ad01c53efe Mon Sep 17 00:00:00 2001 From: Michael Meyer Date: Fri, 16 Jul 2021 08:16:22 -0400 Subject: [PATCH 1/2] Fix #436: writing type-named scrolls When using a marker, it is possible to write a scroll based on the type-name assigned to it by the user. Somewhat unintuitively, this system broke down if the assigned name was identical to the real name of a scroll type: trying to write a scroll by its previously-assigned name 'scare mon' or 'id' would be guaranteed to succeed, but this wouldn't be the case if the user-assigned name was 'scare monster' or 'identify'. Revise dowrite(write.c) to prefer a user-assigned type-name to the real name of a scroll that isn't already formally known, while continuing to prefer the real name of an identified scroll to both. Fixes #436 --- src/write.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/write.c b/src/write.c index 1614735ea..20c4ba3bd 100644 --- a/src/write.c +++ b/src/write.c @@ -179,8 +179,20 @@ dowrite(struct obj *pen) if (!OBJ_NAME(objects[i])) continue; - if (!strcmpi(OBJ_NAME(objects[i]), nm)) - goto found; + if (!strcmpi(OBJ_NAME(objects[i]), nm)) { + if (objects[i].oc_name_known + /* spellbooks can only be written by_name, so no need to + hold out for a 'better' by_descr match */ + || paper->oclass == SPBOOK_CLASS) { + by_descr = FALSE; + goto found; + } else if (!deferralchance) { + /* save item in case there are no better by_descr matches; + don't increment deferralchance so that the first uname + match will always override this */ + deferred = i; + } + } if (!strcmpi(OBJ_DESCR(objects[i]), nm)) { by_descr = TRUE; goto found; @@ -200,15 +212,17 @@ dowrite(struct obj *pen) * and 2/3 chance to keep previous 50:50 * choice; so on for higher match counts. */ - && !rn2(++deferralchance)) + && !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) */ + by_descr = TRUE; + } } - /* 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; } From c9cef70d51173cb8079e77a5b82c947fe8e3cdd1 Mon Sep 17 00:00:00 2001 From: Michael Meyer Date: Thu, 28 Oct 2021 17:18:20 -0400 Subject: [PATCH 2/2] Writing scrolls by type-name followup Don't allow type-name of a scroll that has since been formally identified to override a real scroll name. For example, if a user typenames a scroll of fire 'earth', then IDs fire but doesn't change the typename (reasonable, since it doesn't show up anywhere in inventory at that point), if they try to write 'earth' with a magic marker, don't produce a scroll of fire just because it's still type-named 'earth'. At that point they know it's not a scroll of earth and it's no longer reasonable to assume they mean to use the typename. In other words, if a user-assigned typename matches a real scroll type, write the scroll with the typename -- unless the player knows for sure it doesn't match the actual scroll (whether because the actual scroll is already IDed, or the typenamed scroll has been IDed as something else). In that case write (or attempt to write) the actual scroll as though the typename didn't exist. --- src/write.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/write.c b/src/write.c index 20c4ba3bd..6ccd45546 100644 --- a/src/write.c +++ b/src/write.c @@ -108,7 +108,7 @@ dowrite(struct obj *pen) int basecost, actualcost; int curseval; char qbuf[QBUFSZ]; - int first, last, i, deferred, deferralchance; + int first, last, i, deferred, deferralchance, real; boolean by_descr = FALSE; const char *typeword; @@ -170,10 +170,11 @@ dowrite(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 */ + deferred = real = 0; /* not any scroll or book */ + deferralchance = 0; /* incremented for each oc_uname match */ first = g.bases[(int) paper->oclass]; last = g.bases[(int) paper->oclass + 1] - 1; + /* first loop: look for match with name/description */ for (i = first; i <= last; i++) { /* extra shufflable descr not representing a real object */ if (!OBJ_NAME(objects[i])) @@ -184,24 +185,30 @@ dowrite(struct obj *pen) /* spellbooks can only be written by_name, so no need to hold out for a 'better' by_descr match */ || paper->oclass == SPBOOK_CLASS) { - by_descr = FALSE; goto found; - } else if (!deferralchance) { - /* save item in case there are no better by_descr matches; - don't increment deferralchance so that the first uname - match will always override this */ - deferred = i; + } else { + /* save item in case there are no better by_descr matches */ + real = deferred = i; + break; } } + if (!strcmpi(OBJ_DESCR(objects[i]), nm)) { 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 */ + } + /* second loop: look for match with user-assigned name */ + /* we will get here if 'nm' isn't a real scroll name/descr, or is the name + * of a real scroll that hasn't been formally IDed. */ + for (i = first; i <= last; i++) { + /* 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) + /* prefer attempting to write the real scroll type if + the typename clobbers a real scroll and is known to + be incorrect */ + && !(real && objects[i].oc_name_known) /* * First match: chance incremented to 1, * !rn2(1) is 1, we remember i;