]> granicus.if.org Git - nethack/commitdiff
#tipping shop containers (trunk only)
authornethack.rankin <nethack.rankin>
Thu, 9 Nov 2006 06:36:36 +0000 (06:36 +0000)
committernethack.rankin <nethack.rankin>
Thu, 9 Nov 2006 06:36:36 +0000 (06:36 +0000)
     Using #tip (post-3.4.3 code) on a container that's on a shop floor
didn't handle ownership correctly.  Bag of tricks could be emptied for
free, and contents of other containers were being sold to the shop even
when the shop already owned them.  This fixes bag of tricks and makes a
first cut at doing so for regular containers.

     Message handling when #tipping any bag of tricks was also suboptimal
since the decision about message delivery was made again as each charge
released something instead of waiting until the whole bag was emptied.
So you could get inappropriate "nothing seems to happen" before or after
a monster visibily popped up if something unseen was also produced.

include/extern.h
src/apply.c
src/makemon.c
src/mkobj.c
src/pickup.c

index 3f5eb9f4863338fa6993a193fcb8cce0e9748fee..96a0435050be976136417686b997eda836782f16 100644 (file)
@@ -1038,7 +1038,7 @@ E void FDECL(mimic_hit_msg, (struct monst *, SHORT_P));
 #ifdef GOLDOBJ
 E void FDECL(mkmonmoney, (struct monst *, long));
 #endif
-E int FDECL(bagotricks, (struct obj *,BOOLEAN_P));
+E int FDECL(bagotricks, (struct obj *,BOOLEAN_P,int *));
 E boolean FDECL(propagate, (int, BOOLEAN_P,BOOLEAN_P));
 E boolean FDECL(usmellmon, (struct permonst *));
 
index 4010540c46ace6dcf06b4e45da2dac4023f62c8d..0b4103fd2cf4c4dcbb1ff712d691e1a53f71239f 100644 (file)
@@ -2996,7 +2996,7 @@ doapply()
                res = use_container(&obj, 1);
                break;
        case BAG_OF_TRICKS:
-               (void) bagotricks(obj, FALSE);
+               (void) bagotricks(obj, FALSE, (int *)0);
                break;
        case CAN_OF_GREASE:
                use_grease(obj);
index 3536dce429dfd6bd808cc903f05b3b4ddb115f32..047463261ad333377f7d54ef1e8ab351b73e0b79 100644 (file)
@@ -1818,9 +1818,10 @@ assign_sym:
 
 /* release a monster from a bag of tricks; return number of monsters created */
 int
-bagotricks(bag, tipping)
+bagotricks(bag, tipping, seencount)
 struct obj *bag;
 boolean tipping;  /* caller emptying entire contents; affects shop handling */
+int *seencount;   /* secondary output */
 {
     int moncount = 0;
 
@@ -1832,8 +1833,7 @@ boolean tipping;  /* caller emptying entire contents; affects shop handling */
        if (bag->dknown && objects[bag->otyp].oc_name_known) bag->cknown = 1;
     } else {
        struct monst *mtmp;
-       boolean sawone = FALSE;
-       int creatcnt = 1;
+       int creatcnt = 1, seecount = 0;
 
        consume_obj_charge(bag, !tipping);
 
@@ -1842,18 +1842,18 @@ boolean tipping;  /* caller emptying entire contents; affects shop handling */
            mtmp = makemon((struct permonst *)0, u.ux, u.uy, NO_MM_FLAGS);
            if (mtmp) {
                ++moncount;
-               if (canspotmon(mtmp)) sawone = TRUE;
+               if (canspotmon(mtmp)) ++seecount;
            }
        } while (--creatcnt > 0);
-       if (sawone) {
+       if (seecount) {
+           if (seencount) *seencount += seecount;
            /* don't set contents-known flag if we just used last charge 
               (such suppression doesn't actually gain us much since
               player can now deduce that the bag has become empty) */
            if (bag->spe > 0) bag->cknown = 1;
            if (bag->dknown) makeknown(BAG_OF_TRICKS);
-       } else {
-           /* #tip while blind can trigger this successive times */
-           Norep("Nothing seems to happen.");
+       } else if (!tipping) {
+           pline(!moncount ? nothing_happens : "Nothing seems to happen.");
        }
     }
     return moncount;
index 5eea5b698cb243f2c16ac289e91d479f681375b3..f0985d19b0e512be74904ebcca038bf87c820a91 100644 (file)
@@ -1828,8 +1828,7 @@ boolean tipping;  /* caller emptying entire contents; affects shop handling */
        obj->owt = weight(obj);
        /* using a shop's horn of plenty entails a usage fee and also
           confers ownership of the created item to the shopkeeper */
-       if (carried(horn) ? horn->unpaid :
-               (costly_spot(u.ux, u.uy) && !horn->no_charge))
+       if (horn->unpaid)
            addtobill(obj, FALSE, FALSE, tipping);
        /* if it ended up on bill, we don't want "(unpaid, N zorkids)"
           being included in its formatted name during next message */
index 320efa8497b937db7f974a2e51af16efed82ee7a..61315c3c67fe6e751a7f6924cb16ef843262353c 100644 (file)
@@ -2529,7 +2529,21 @@ STATIC_OVL void
 tipcontainer(box)
 struct obj *box;       /* or bag */
 {
-    boolean empty_it = FALSE;
+    xchar ox = u.ux, oy = u.uy; /* #tip only works at hero's location */
+    boolean empty_it = FALSE,
+         /* Shop handling:  can't rely on the container's own unpaid
+            or no_charge status because contents might differ with it.
+            A carried container's contents will be flagged as unpaid
+            or not, as appropriate, and need no special handling here.
+            Items owned by the hero get sold to the shop without
+            confirmation as with other uncontrolled drops.  A floor
+            container's contents will be marked no_charge if owned by
+            hero, otherwise they're owned by the shop.  By passing
+            the contents through shop billing, they end up getting
+            treated the same as in the carried case.   We do so one
+            item at a time instead of doing whole container at once
+            to reduce the chance of exhausting shk's billing capacity. */
+           maybeshopgoods = !carried(box) && costly_spot(ox, oy);
 
     /* caveat: this assumes that cknown, lknown, olocked, and otrapped
        fields haven't been overloaded to mean something special for the
@@ -2547,22 +2561,29 @@ struct obj *box;        /* or bag */
        }
     } else if (box->otyp == BAG_OF_TRICKS || box->otyp == HORN_OF_PLENTY) {
        boolean bag = box->otyp == BAG_OF_TRICKS;
-       int old_spe = box->spe;
+       int old_spe = box->spe, seen = 0;
 
+       if (maybeshopgoods && !box->no_charge)
+           addtobill(box, FALSE, FALSE, TRUE);
        /* apply this bag/horn until empty or monster/object creation fails
           (if the latter occurs, force the former...) */
        do {
-           if (!(bag ? bagotricks(box, TRUE) : hornoplenty(box, TRUE)))
+           if (!(bag ? bagotricks(box, TRUE, &seen) : hornoplenty(box, TRUE)))
                break;
        } while (box->spe > 0);
 
        if (box->spe < old_spe) {
+           if (bag) pline((seen == 0) ? "Nothing seems to happen." :
+                          (seen == 1) ? "A monster appears." :
+                          "Monsters appear!");
            /* check_unpaid wants to see a non-zero charge count */
            box->spe = old_spe;
            check_unpaid_usage(box, TRUE);
            box->spe = 0;       /* empty */
            box->cknown = 1;
        }
+       if (maybeshopgoods && !box->no_charge)
+           subfrombill(box, shop_keeper(*in_rooms(ox, oy, SHOPBASE)));
     } else if (box->spe) {
        char yourbuf[BUFSZ];
 
@@ -2584,7 +2605,7 @@ struct obj *box;  /* or bag */
        struct obj *otmp, *nobj;
        boolean verbose = FALSE,
                highdrop = !can_reach_floor(TRUE),
-               altarizing = IS_ALTAR(levl[u.ux][u.uy].typ),
+               altarizing = IS_ALTAR(levl[ox][oy].typ),
                cursed_mbag = (Is_mbag(box) && box->cursed);
        int held = carried(box);
        long loss = 0L;
@@ -2600,7 +2621,15 @@ struct obj *box; /* or bag */
                loss += mbag_item_gone(held, otmp);
                /* abbreviated drop format is no longer appropriate */
                verbose = TRUE;
-           } else if (highdrop) {
+               continue;
+           }
+
+           if (maybeshopgoods) {
+               addtobill(otmp, FALSE, FALSE, TRUE);
+               iflags.suppress_price++;        /* doname formatting */
+           }
+
+           if (highdrop) {
                /* might break or fall down stairs; handles altars itself */
                hitfloor(otmp);
            } else {
@@ -2608,11 +2637,12 @@ struct obj *box;        /* or bag */
                    doaltarobj(otmp);
                else if (verbose)
                    pline("%s %s to the %s.", Doname2(otmp),
-                         otense(otmp, "drop"), surface(u.ux, u.uy));
+                         otense(otmp, "drop"), surface(ox, oy));
                else
                    pline("%s%c", doname(otmp), nobj ? ',' : '.');
                dropy(otmp);
            }
+           if (maybeshopgoods) iflags.suppress_price--;        /* reset */
        }
        if (loss)       /* magic bag lost some shop goods */
            You("owe %ld %s for lost merchandise.", loss, currency(loss));