[comp.sources.games.bugs] Another NH3.0i Bug

jfernand@m.cs.uiuc.edu (07/27/90)

Yet Another NH3.0i (pl9) Bug:

The weight of a bag of holding must be recalculated when #dipped
into holy or unholy water, but is not.  Now, (1) it is possible to
make a bag of holding have negative weight by putting stuff in it
while it is blessed, #dipping it in a potion of unholy water, and
then removing the stuff from it.  Also, (2) if you're unlucky enough
to bless the bag while it contains stuff, your bag will be extra
heavy (weight > 3) after emptying it.

J.R.Fernandez

P.S. To the developers (and others interested):

Here's a partial fix:

In potions.c:
> int
> dodip()
> {
> 	...
> 				obj->cursed=0;
> 				obj->bknown=1;
> 	poof:
> 				if(!(objects[potion->otyp].oc_name_known) &&
> 				   !(objects[potion->otyp].oc_uname))
> 					docall(potion);
> 				useup(potion);
> 				return(1);
> 	...
> }

insert the following test right after poof:
>> 	poof:			if (obj->otyp == BAG_OF_HOLDING
>> 				    /* or anything else that changes weight
>> 					when blessed or cursed */
>> 				        && obj->bknown)
>> 				    obj->owt = weight(obj);

***************************************************************************
Also, a similar test must be made in *ALL* other places where an
object may be blessed or cursed (e.g. monster curse attacks).
(This is why I called it a partial fix).
***************************************************************************

In pickup.c, I noticed the following:
> #define DELTA_CWT(cont) ((cont)->cursed?(obj->owt*2):(obj->owt/((cont)->blessed?4:2)) + 1)
> 
> void
> dec_cwt(cobj, obj)
> register struct obj *cobj, *obj;
> {
> 	if (cobj->otyp == BAG_OF_HOLDING)
> 		cobj->owt -= DELTA_CWT(cobj); /* (A) */
> 	else	cobj->owt -= obj->owt;
> 
> 	if(cobj->owt < objects[cobj->otyp].oc_weight) /* (B) */
> 		cobj->owt = objects[cobj->otyp].oc_weight;
> }

I suspect that (B) was added to (partially) avoid the bug (1) I
mentioned above (without handling (2)).  Unfortunately, (A) can
result in a negative value for owt (even though it's unsigned--don't
ask me why).  Since owt is unsigned, if owt is negative, the test
at (B) will fail (a "negative" integer is a big unsigned integer;
also, oc_weight is a uchar).  To demonstrate:

% cat x.c
main ()
{
unsigned i=5;
int j=5;
i-=10;
j-=10;
printf("%d,%d\n%d,%d\n",i,i<3,j,j<3);
}
% cc x.c
% a.out
-5,0
-5,1
% exit

Assuming that the fix in dodip() is not sufficient (or the test at
(B) serves another purpose), then a correct way of handling these
unsigned quantities is as follows:

>> {
>>	int delta = (cobj->otyp == BAG_OF_HOLDING) ? DELTA_CWT(cobj) : obj->owt;
>> 
>> 	if(cobj->owt < objects[cobj->otyp].oc_weight + delta)
>>	/* same as (cobj->owt - delta < objects[cobj->otyp].oc_weight) */
>> 		cobj->owt = objects[cobj->otyp].oc_weight;
>>	else
>>		cobj->owt -= delta;
>> }

---end---