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---