From 86c29f5b47f254a89a9242686665499fdd7722f9 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sun, 19 Nov 2023 01:16:40 +0000 Subject: [PATCH] Allow identifying stackable items by comparison to identical items NetHack has historically had problems (both in terms of interface and in terms of gameplay exploits) caused by unidentified items not stacking with identified items. The 3.7 change to cause items picked up by monsters to become unidentified has exacerbated this, e.g. there's an undesirable strategy in which players give ammunition to hostile monsters in order to unidentify it, allowing it to stack with other unidentified ammunition so that the whole stack can be enchanted with a single scroll. This commit makes it possible to stack items with different identification statuses (unles Blind), in effect causing the unidentified items to be identified by comparing them to the identified items (this is consistent with the mechanic via which two stacks of unidentified items can be determined to be the same as each other). The hope is that this will prevent any gain from exploits involving intentionally unidentifying (or intentionally failing to identify) items, that it will prevent interface clutter caused by (e.g.) a Ranger's main projectiles becoming partially unidentified as a nymph picks them up and subsequently failing to stack, and that it will reduce the incentive to backtrack to an altar to merge, e.g., a stack of formally-known-uncursed and a stack of informally-known-uncursed food rations to stack. --- doc/fixes3-7-0.txt | 2 ++ src/invent.c | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 0eb1a7ea8..d88831001 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -511,6 +511,8 @@ any blessed key was behaving as if was the rogue's Master Key when unlocking when an unseen non-pet picks up or uses an item, hero loses known/dknown/ bknown/cknown/lknown memory of that item (so becomes unidentified; in particular, player won't be asked what to call unseen thrown potion) +when picking up a stackable item, it can be identified by comparing it to + another identical item that is already identified wishing for a partly eaten wraith corpse yielded "partly eaten food (1) more nutritious than untouched food (0)" if PREFIXES_IN_USE was defined (and VAR_PLAYGROUND forces it to be) when diff --git a/src/invent.c b/src/invent.c index cf7f4f462..56110adef 100644 --- a/src/invent.c +++ b/src/invent.c @@ -707,6 +707,7 @@ int merged(struct obj **potmp, struct obj **pobj) { register struct obj *otmp = *potmp, *obj = *pobj; + boolean discovered = FALSE; if (mergable(otmp, obj)) { /* Approximate age: we do it this way because if we were to @@ -745,6 +746,29 @@ merged(struct obj **potmp, struct obj **pobj) if (obj->timed) obj_stop_timers(obj); /* follows lights */ + /* objects can be identified by comparing them (unless Blind, + but that is handled in mergable()); the object becomes + identified in a particular dimension if either object was + previously identified in that dimension, and if the + identification states don't match, one of them must have + previously been identified */ + if (obj->known != otmp->known) { + otmp->known = TRUE; + discovered = TRUE; + } + if (obj->rknown != otmp->rknown) { + otmp->rknown = TRUE; + if (otmp->oerodeproof) { + discovered = TRUE; + } + } + if (obj->bknown != otmp->bknown) { + otmp->bknown = TRUE; + if (!Role_if(PM_CLERIC)) { + discovered = TRUE; + } + } + /* fixup for `#adjust' merging wielded darts, daggers, &c */ if (obj->owornmask && carried(otmp)) { long wmask = otmp->owornmask | obj->owornmask; @@ -802,6 +826,15 @@ merged(struct obj **potmp, struct obj **pobj) return 1; } + /* Print a message if item comparison discovers more + information about the items (with the exception of thrown + items, where this would be too spammy as such items get + unidentified by monsters very frequently). */ + if (discovered && otmp->where == OBJ_INVENT && + !obj->was_thrown && !otmp->was_thrown) { + pline("You learn more about your items by comparing them."); + } + obfree(obj, otmp); /* free(obj), bill->otmp */ return 1; } @@ -4548,14 +4581,15 @@ mergable( return FALSE; if (obj->dknown != otmp->dknown - || (obj->bknown != otmp->bknown && !Role_if(PM_CLERIC)) + || (obj->bknown != otmp->bknown && !Role_if(PM_CLERIC) && + (Blind || Hallucination)) || obj->oeroded != otmp->oeroded || obj->oeroded2 != otmp->oeroded2 || obj->greased != otmp->greased) return FALSE; if ((obj->oclass == WEAPON_CLASS || obj->oclass == ARMOR_CLASS) && (obj->oerodeproof != otmp->oerodeproof - || obj->rknown != otmp->rknown)) + || (obj->rknown != otmp->rknown && (Blind || Hallucination)))) return FALSE; if (obj->otyp == CORPSE || obj->otyp == EGG || obj->otyp == TIN) { @@ -4614,7 +4648,10 @@ mergable( if (obj->oartifact != otmp->oartifact) return FALSE; - return (obj->known == otmp->known) ? TRUE : FALSE; + if (obj->known != otmp->known && (Blind || Hallucination)) + return FALSE; + + return TRUE; } /* the #showgold command */