]> granicus.if.org Git - nethack/commitdiff
bag of tricks, horn of plenty, #tip (trunk only)
authornethack.rankin <nethack.rankin>
Sun, 18 Jun 2006 05:20:36 +0000 (05:20 +0000)
committernethack.rankin <nethack.rankin>
Sun, 18 Jun 2006 05:20:36 +0000 (05:20 +0000)
     <Someone> reported that he applied an unID'd bag and it became
discovered as a bag of tricks even though a spellbook appeared on the floor
next to him rather than having a monster show up (the monster was a mimic).
Suppress the bag discovery unless you can see or sense a monster appear.
(This doesn't really achieve much for most players, who'll recognize the
bag because they know that only one type of container doesn't prompt to
take things out and/or put things in, but I think it does make sense.)

     While mucking with bag of tricks I decided that to be consistent with
the behavior of other containers, the #tip command should release all the
monsters in the bag instead of just one.

     And after doing that, I realized that horn of plenty ought to behave
much the same, so #tip will operate on it now.  However, it won't be listed
as a likely candidate in the "which item?" prompt unless/until it has been
discovered.  (Attempting to empty any other type of horn yields "nothing
happens", same as for a horn of plenty with no charges left.)  Emptying a
horn of plenty in a shop can be extremely verbose, but I don't think that
qualifies as a bug and don't currently have any plans to alter it.

doc/fixes35.0
include/extern.h
src/apply.c
src/invent.c
src/makemon.c
src/mkobj.c
src/pickup.c
src/shk.c

index 771e13ade5adcd5672a7e9067cbce961e0f6b3cc..448f0a3c832b0e1db78ace790d15cf7ef5275ba2 100644 (file)
@@ -149,6 +149,7 @@ ensure that the punishment ball and chain make it into the save file after being
        temporarily orphaned from the normal chains in the swallowing code
 charge for thrown wand that shatters into a thousand pieces in a shop
 wielded light source susceptible to water gets extinguished when weapon rusts
+don't discover unknown bag of tricks when monster it releases is undetected
 
 
 Platform- and/or Interface-Specific Fixes
index e3404936c5fe03a1dab06e70df448223d35e6ff5..23a0a484dc5b8c1ffb660341db406f6bbf255c1e 100644 (file)
@@ -1004,7 +1004,7 @@ E void FDECL(mimic_hit_msg, (struct monst *, SHORT_P));
 #ifdef GOLDOBJ
 E void FDECL(mkmonmoney, (struct monst *, long));
 #endif
-E void FDECL(bagotricks, (struct obj *));
+E int FDECL(bagotricks, (struct obj *,BOOLEAN_P));
 E boolean FDECL(propagate, (int, BOOLEAN_P,BOOLEAN_P));
 E boolean FDECL(usmellmon, (struct permonst *));
 
@@ -1168,6 +1168,7 @@ E void FDECL(add_to_buried, (struct obj *));
 E void FDECL(dealloc_obj, (struct obj *));
 E void FDECL(obj_ice_effects, (int, int, BOOLEAN_P));
 E long FDECL(peek_at_iced_corpse_age, (struct obj *));
+E int FDECL(hornoplenty, (struct obj *,BOOLEAN_P));
 #ifdef WIZARD
 E void NDECL(obj_sanity_check);
 #endif
index cb3b7314a34588e1eb72f8a0feca5e939ed4896d..e61b42e91d7ca13159d7de6fa6c2ec47df2e4aad 100644 (file)
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)apply.c    3.5     2006/05/13      */
+/*     SCCS Id: @(#)apply.c    3.5     2006/06/17      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -2985,7 +2985,7 @@ doapply()
                res = use_container(&obj, 1);
                break;
        case BAG_OF_TRICKS:
-               bagotricks(obj);
+               (void) bagotricks(obj, FALSE);
                break;
        case CAN_OF_GREASE:
                use_grease(obj);
@@ -3107,40 +3107,7 @@ doapply()
                res = do_play_instrument(obj);
                break;
        case HORN_OF_PLENTY:    /* not a musical instrument */
-               if (obj->spe > 0) {
-                   struct obj *otmp;
-                   const char *what;
-
-                   consume_obj_charge(obj, TRUE);
-                   if (!rn2(13)) {
-                       otmp = mkobj(POTION_CLASS, FALSE);
-                       if (objects[otmp->otyp].oc_magic) do {
-                           otmp->otyp = rnd_class(POT_BOOZE, POT_WATER);
-                       } while (otmp->otyp == POT_SICKNESS);
-                       what = "A potion";
-                   } else {
-                       otmp = mkobj(FOOD_CLASS, FALSE);
-                       if (otmp->otyp == FOOD_RATION && !rn2(7))
-                           otmp->otyp = LUMP_OF_ROYAL_JELLY;
-                       what = "Some food";
-                   }
-                   pline("%s spills out.", what);
-                   otmp->blessed = obj->blessed;
-                   otmp->cursed = obj->cursed;
-                   otmp->owt = weight(otmp);
-                   otmp = hold_another_object(otmp, u.uswallow ?
-                                      "Oops!  %s out of your reach!" :
-                                       (Is_airlevel(&u.uz) ||
-                                        Is_waterlevel(&u.uz) ||
-                                        levl[u.ux][u.uy].typ < IRONBARS ||
-                                        levl[u.ux][u.uy].typ >= ICE) ?
-                                              "Oops!  %s away from you!" :
-                                              "Oops!  %s to the floor!",
-                                              The(aobjnam(otmp, "slip")),
-                                              (const char *)0);
-                   makeknown(HORN_OF_PLENTY);
-               } else
-                   pline(nothing_happens);
+               (void) hornoplenty(obj, FALSE);
                break;
        case LAND_MINE:
        case BEARTRAP:
index f6cf34fc2aec9ffac5d2ff2258087b4c1882b1f4..36b6de999bf682d435ecf8ff9adbf328073e2820 100644 (file)
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)invent.c   3.5     2006/05/17      */
+/*     SCCS Id: @(#)invent.c   3.5     2006/06/17      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -896,7 +896,10 @@ register const char *let,*word;
                      (otmp->dknown && objects[OIL_LAMP].oc_name_known))))
                || (!strcmp(word, "untrap with") &&
                    (otmp->oclass == TOOL_CLASS && otyp != CAN_OF_GREASE))
-               || (!strcmp(word, "tip") && !Is_container(otmp))
+               || (!strcmp(word, "tip") && !Is_container(otmp) &&
+                  /* include horn of plenty if sufficiently discovered */
+                   (otmp->otyp != HORN_OF_PLENTY || !otmp->dknown ||
+                       !objects[HORN_OF_PLENTY].oc_name_known))
                || (!strcmp(word, "charge") && !is_chargeable(otmp))
                    )
                        foo--;
index 49d8197f188b00ce304460088a206e684bb61d37..86f991a34beca5565720a8d6f25280caeb44d50f 100644 (file)
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)makemon.c  3.5     2006/04/14      */
+/*     SCCS Id: @(#)makemon.c  3.5     2006/06/17      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -1805,28 +1805,47 @@ assign_sym:
        mtmp->mappearance = appear;
 }
 
-/* release a monster from a bag of tricks */
-void
-bagotricks(bag)
+/* release a monster from a bag of tricks; return number of monsters created */
+int
+bagotricks(bag, tipping)
 struct obj *bag;
+boolean tipping;  /* caller emptying entire contents; affects shop handling */
 {
+    int moncount = 0;
+
     if (!bag || bag->otyp != BAG_OF_TRICKS) {
        impossible("bad bag o' tricks");
     } else if (bag->spe < 1) {
        pline(nothing_happens);
+       /* now known to be empty if sufficiently discovered */
+       if (bag->dknown && objects[bag->otyp].oc_name_known) bag->cknown = 1;
     } else {
-       boolean gotone = FALSE;
-       int cnt = 1;
-
-       consume_obj_charge(bag, TRUE);
-
-       if (!rn2(23)) cnt += rn1(7, 1);
-       while (cnt-- > 0) {
-           if (makemon((struct permonst *)0, u.ux, u.uy, NO_MM_FLAGS))
-               gotone = TRUE;
+       struct monst *mtmp;
+       boolean sawone = FALSE;
+       int creatcnt = 1;
+
+       consume_obj_charge(bag, !tipping);
+
+       if (!rn2(23)) creatcnt += rnd(7);
+       do {
+           mtmp = makemon((struct permonst *)0, u.ux, u.uy, NO_MM_FLAGS);
+           if (mtmp) {
+               ++moncount;
+               if (canspotmon(mtmp)) sawone = TRUE;
+           }
+       } while (--creatcnt > 0);
+       if (sawone) {
+           /* 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.");
        }
-       if (gotone) makeknown(BAG_OF_TRICKS);
     }
+    return moncount;
 }
 
 /*makemon.c*/
index 1f0e22273a5081c2d26fd0e8a841923b77889ef8..0df94c5037d0149a9c70cb561b92aa73ad1b0b80 100644 (file)
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)mkobj.c    3.5     2006/05/09      */
+/*     SCCS Id: @(#)mkobj.c    3.5     2006/06/17      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -1768,6 +1768,67 @@ dealloc_obj(obj)
     free((genericptr_t) obj);
 }
 
+/* create an object from a horn of plenty; mirrors bagotricks(makemon.c) */
+int
+hornoplenty(horn, tipping)
+struct obj *horn;
+boolean tipping;  /* caller emptying entire contents; affects shop handling */
+{
+    int objcount = 0;
+
+    if (!horn || horn->otyp != HORN_OF_PLENTY) {
+       impossible("bad horn o' plenty");
+    } else if (horn->spe < 1) {
+       pline(nothing_happens);
+    } else {
+       struct obj *obj;
+       const char *what;
+
+       consume_obj_charge(horn, !tipping);
+       if (!rn2(13)) {
+           obj = mkobj(POTION_CLASS, FALSE);
+           if (objects[obj->otyp].oc_magic) do {
+               obj->otyp = rnd_class(POT_BOOZE, POT_WATER);
+           } while (obj->otyp == POT_SICKNESS);
+           what = (obj->quan > 1L) ? "Some potions" : "A potion";
+       } else {
+           obj = mkobj(FOOD_CLASS, FALSE);
+           if (obj->otyp == FOOD_RATION && !rn2(7))
+               obj->otyp = LUMP_OF_ROYAL_JELLY;
+           what = "Some food";
+       }
+       ++objcount;
+       pline("%s %s out.", what, vtense(what, "spill"));
+       obj->blessed = horn->blessed;
+       obj->cursed = horn->cursed;
+       obj->owt = weight(obj);
+       if (!tipping) {
+           obj = hold_another_object(obj, u.uswallow ?
+                                     "Oops!  %s out of your reach!" :
+                                       (Is_airlevel(&u.uz) ||
+                                        Is_waterlevel(&u.uz) ||
+                                        levl[u.ux][u.uy].typ < IRONBARS ||
+                                        levl[u.ux][u.uy].typ >= ICE) ?
+                                         "Oops!  %s away from you!" :
+                                           "Oops!  %s to the floor!",
+                                     The(aobjnam(obj, "slip")),
+                                     (const char *)0);
+       } else {
+           /* assumes this is taking place at hero's location */
+           if (!can_reach_floor(TRUE))
+               hitfloor(obj);
+           else if (IS_ALTAR(levl[u.ux][u.uy].typ))
+               doaltarobj(obj);
+           else
+               pline("%s %s to the %s.", Doname2(obj),
+                     otense(obj, "drop"), surface(u.ux, u.uy));
+           dropy(obj);
+       }
+       if (horn->dknown) makeknown(HORN_OF_PLENTY);
+    }
+    return objcount;
+}
+
 #ifdef WIZARD
 /* Check all object lists for consistency. */
 void
index 23072b4007b9750847e3cebce6eaf30fd6384ada..db4fa6a2938e1ac12be9357b62b8c6d0e74a9e80 100644 (file)
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)pickup.c   3.5     2006/02/03      */
+/*     SCCS Id: @(#)pickup.c   3.5     2006/06/17      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -2502,7 +2502,7 @@ dotip()
     if (!cobj) return 0;
 
     /* normal case */
-    if (Is_container(cobj)) {
+    if (Is_container(cobj) || cobj->otyp == HORN_OF_PLENTY) {
        tipcontainer(cobj);
        return 1;
     }
@@ -2555,6 +2555,9 @@ struct obj *box;  /* or bag */
 {
     boolean empty_it = FALSE;
 
+    /* caveat: this assumes that cknown, lknown, olocked, and otrapped
+       fields haven't been overloaded to mean something special for the
+       non-standard "container" horn of plenty */
     box->lknown = 1;
     if (box->olocked) {
        pline("It's locked.");
@@ -2566,10 +2569,24 @@ struct obj *box;        /* or bag */
            nomul(-1);
            nomovemsg = "";
        }
-    } else if (box->otyp == BAG_OF_TRICKS && box->spe > 0) {
-       /* apply (not loot) this bag; uses up one charge */
-       bagotricks(box);
-       box->cknown = 1;
+    } else if (box->otyp == BAG_OF_TRICKS || box->otyp == HORN_OF_PLENTY) {
+       boolean bag = box->otyp == BAG_OF_TRICKS;
+       int old_spe = box->spe;
+
+       /* 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)))
+               break;
+       } while (box->spe > 0);
+
+       if (box->spe < old_spe) {
+           /* 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;
+       }
     } else if (box->spe) {
        char yourbuf[BUFSZ];
 
index 2837e7879268da3f84117531fe198ff4e3df4e3b..e32c8848df6b8fb4455afa05aca8c285df0c8b40 100644 (file)
--- a/src/shk.c
+++ b/src/shk.c
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)shk.c      3.5     2006/05/20      */
+/*     SCCS Id: @(#)shk.c      3.5     2006/06/17      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -3897,7 +3897,8 @@ boolean altusage; /* some items have an "alternate" use with different cost */
                tmp /= 2L;
        } else if(otmp->otyp == BAG_OF_TRICKS ||         /* 1 - 20 */
                  otmp->otyp == HORN_OF_PLENTY) {
-               tmp /= 5L;
+               /* altusage: emptying of all the contents at once */
+               if (!altusage) tmp /= 5L;
        } else if(otmp->otyp == CRYSTAL_BALL ||          /* 1 - 5 */
                  otmp->otyp == OIL_LAMP ||              /* 1 - 10 */
                  otmp->otyp == BRASS_LANTERN ||
@@ -3949,16 +3950,22 @@ boolean altusage;
            arg2 = ESHK(shkp)->debit > 0L ? " an additional" : "";
        } else if (otmp->otyp == POT_OIL) {
            fmt = "%s%sThat will cost you %ld %s (Yendorian Fuel Tax).";
+       } else if (altusage &&
+             (otmp->otyp == BAG_OF_TRICKS || otmp->otyp == HORN_OF_PLENTY)) {
+           fmt = "%s%sEmptying that will cost you %ld %s.";
+           if (!rn2(3)) arg1 = "Whoa!  ";
+           if (!rn2(3)) arg1 = "Watch it!  ";
        } else {
            fmt = "%s%sUsage fee, %ld %s.";
            if (!rn2(3)) arg1 = "Hey!  ";
            if (!rn2(3)) arg2 = "Ahem.  ";
        }
 
-       if (shkp->mcanmove || !shkp->msleeping)
+       if (!muteshk(shkp)) {
            verbalize(fmt, arg1, arg2, tmp, currency(tmp));
+           exercise(A_WIS, TRUE);      /* you just got info */
+       }
        ESHK(shkp)->debit += tmp;
-       exercise(A_WIS, TRUE);          /* you just got info */
 }
 
 /* for using charges of unpaid objects "used in the normal manner" */