]> granicus.if.org Git - nethack/commitdiff
#tip command - container access
authornethack.rankin <nethack.rankin>
Thu, 3 Apr 2003 10:56:46 +0000 (10:56 +0000)
committernethack.rankin <nethack.rankin>
Thu, 3 Apr 2003 10:56:46 +0000 (10:56 +0000)
     Another fix to address the complaints about two-handed weapons being
rendered useless by 3.4.1's change to require free hands in order to apply
containers.  Some players now fear to wield two-handed weapons because a
curse would make accessing their bag impossible, which is doubly nasty if
that's where they have scrolls of remove curse or potions of holy water
intended to deal with cursed items.  The same situation applies for cursed
one-handed weapon combined with cursed shield, so some are now claiming
that 3.4.1 has made two-weapon combat be even more attractive than before.

     This implements #tip, a new command that causes a container at the
current location or carried in inventory to have its contents emptied
onto the floor.  Hero's hands don't need to be free at the time but tipping
a floor container requires limbs; tipping an inventory container doesn't
need hands or even limbs.  The contained items don't pass through inventory
during the process, so don't cause objects (loadstones, crysknives, scrolls
of scare monster?) to go through their special handling unless it's part of
normally dropping to the floor.  Tipping a bag of tricks behaves the same
as applying it (one monster is released, and it only becomes empty if
that happened to be the last charge) and items tipped out of a cursed bag
of holding have their normal cursed bag chance (1/13) of being destroyed.
Tipping an inventory container while levitating or during unskilled riding
behaves similar to normal drop--from a height, so some fragile items break.

     Players have wanted this feature to get gray stones out of chests or
heavy corpses out of ice boxes but I didn't care much about that; losing
access to your bag is more significant.  I'm pretty sure that there was a
user patch to do something like this floating around at one time, but I
couldn't find it when I looked, so I implemented #tip totally from scratch.

     Bug?  Extended commands which lack meta-key shortcuts are not listed
in the help files displayed by the '?' command....

doc/Guidebook.mn
doc/Guidebook.tex
doc/fixes35.0
include/extern.h
src/cmd.c
src/invent.c
src/pickup.c

index dcb9a5e5f72de8faf04d5e5e005bb9504ef9567d..aa995b3595250f78c22bcb86f03e995a67114eaa 100644 (file)
@@ -5,7 +5,7 @@
 .ds vr "NetHack 3.4
 .ds f0 "\*(vr
 .ds f1
-.ds f2 "March 14, 2003
+.ds f2 "April 2, 2003
 .mt
 A Guide to the Mazes of Menace
 (Guidebook for NetHack)
@@ -707,6 +707,8 @@ Ride (or stop riding) a monster.
 Rub a lamp or a stone.
 .lp #sit
 Sit down.
+.lp #tip
+Tip over a container to pour out its contents.
 .lp #turn
 Turn undead.
 .lp #twoweapon
index f4cadd8c4ab05f9198009356977f52637f86d70e..eb49989cbe6740b56ec8f6291a2168bf33c0fe11 100644 (file)
@@ -27,7 +27,7 @@
 \begin{document}
 %
 % input file: guidebook.mn
-% $Revision: 1.61 $ $Date: 2003/02/13 04:55:28 $
+% $Revision: 1.62 $ $Date: 2003/03/14 13:50:58 $
 %
 %.ds h0 "
 %.ds h1 %.ds h2 \%
@@ -40,7 +40,7 @@
 %.au
 \author{Eric S. Raymond\\
 (Extensively edited and expanded for 3.4)}
-\date{March 14, 2003}
+\date{April 2, 2003}
 
 \maketitle
 
@@ -941,6 +941,9 @@ Rub a lamp or a stone.
 \item[\tb{\#sit}]
 Sit down.
 %.lp
+\item[\tb{\#tip}]
+Tip over a container to pour out its contents.
+%.lp
 \item[\tb{\#turn}]
 Turn undead.
 %.lp
index df20675cc8e9adb884ee51a8482fc198ca1ceb22..3994c90625d7c148f9aede6b2cfd0a17eae20c4c 100644 (file)
@@ -37,6 +37,7 @@ win32gui: better handling of "more" prompt for messages that would have scrolled
 General New Features
 --------------------
 burying a punishment ball no longer ends your punishment
+#tip command--pay a modest gratuity
 
 
 Platform- and/or Interface-Specific New Features
index cfc172a67f977504b01304cd32d58e4c2ae7fd9d..2de637de33ed5b7a073a37ac5b48b4b5dc22189c 100644 (file)
@@ -1477,6 +1477,7 @@ E int NDECL(encumber_msg);
 E int NDECL(doloot);
 E int FDECL(use_container, (struct obj *,int));
 E int FDECL(loot_mon, (struct monst *,int *,boolean *));
+E int NDECL(dotip);
 
 /* ### pline.c ### */
 
index 3384d758c5b0e48c532a4ddb523fa5b4aaeb137a..38f6b7ba099e61284e66cdb42f81ef400c83f9ae 100644 (file)
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)cmd.c      3.4     2003/02/06      */
+/*     SCCS Id: @(#)cmd.c      3.4     2003/04/02      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -77,6 +77,7 @@ extern int NDECL(dodrink); /**/
 extern int NDECL(dodip); /**/
 extern int NDECL(dosacrifice); /**/
 extern int NDECL(dopray); /**/
+extern int NDECL(dotip); /**/
 extern int NDECL(doturn); /**/
 extern int NDECL(doredraw); /**/
 extern int NDECL(doread); /**/
@@ -1486,6 +1487,7 @@ struct ext_func_tab extcmdlist[] = {
 #endif
        {"rub", "rub a lamp or a stone", dorub, FALSE},
        {"sit", "sit down", dosit, FALSE},
+       {"tip", "empty a container", dotip, FALSE},
        {"turn", "turn undead", doturn, TRUE},
        {"twoweapon", "toggle two-weapon combat", dotwoweapon, FALSE},
        {"untrap", "untrap something", dountrap, FALSE},
index 0ac4fbb455a8f5bb9c696da5d8cfa5feca2e11c0..64eee699150021c14d06fd3f8619c83086d75acb 100644 (file)
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)invent.c   3.4     2003/01/24      */
+/*     SCCS Id: @(#)invent.c   3.4     2003/04/02      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -890,6 +890,7 @@ 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, "charge") && !is_chargeable(otmp))
                    )
                        foo--;
index 1a626bad522f40af307e1c57e9550ead208448f8..43ed52e32aa9e5f58fe91ccce140ee4bbec24d59 100644 (file)
@@ -35,8 +35,9 @@ STATIC_DCL long FDECL(mbag_item_gone, (int,struct obj *));
 STATIC_DCL int FDECL(menu_loot, (int, struct obj *, BOOLEAN_P));
 STATIC_DCL int FDECL(in_or_out_menu, (const char *,struct obj *, BOOLEAN_P, BOOLEAN_P));
 STATIC_DCL int FDECL(container_at, (int, int, BOOLEAN_P));
-STATIC_DCL boolean FDECL(able_to_loot, (int, int));
+STATIC_DCL boolean FDECL(able_to_loot, (int,int,BOOLEAN_P));
 STATIC_DCL boolean FDECL(mon_beside, (int, int));
+STATIC_DCL void FDECL(tipcontainer, (struct obj *));
 
 /* define for query_objlist() and autopickup() */
 #define FOLLOW(curr, flags) \
@@ -1374,9 +1375,12 @@ boolean countem;
 }
 
 STATIC_OVL boolean
-able_to_loot(x, y)
+able_to_loot(x, y, looting)
 int x, y;
+boolean looting;       /* loot vs tip */
 {
+       const char *verb = looting ? "loot" : "tip";
+
        if (!can_reach_floor()) {
 #ifdef STEED
                if (u.usteed && P_SKILL(P_RIDING) < P_BASIC)
@@ -1385,13 +1389,15 @@ int x, y;
 #endif
                        You("cannot reach the %s.", surface(x, y));
                return FALSE;
-       } else if (is_pool(x, y) || is_lava(x, y)) {
-               /* at present, can't loot in water even when Underwater */
-               You("cannot loot things that are deep in the %s.",
-                   is_lava(x, y) ? "lava" : "water");
+       } else if ((is_pool(x, y) && (looting || !Underwater)) ||
+                   is_lava(x, y)) {
+               /* at present, can't loot in water even when Underwater;
+                  can tip underwater, but not when over--or stuck in--lava */
+               You("cannot %s things that are deep in the %s.",
+                   verb, is_lava(x, y) ? "lava" : "water");
                return FALSE;
        } else if (nolimbs(youmonst.data)) {
-               pline("Without limbs, you cannot loot anything.");
+               pline("Without limbs, you cannot %s anything.", verb);
                return FALSE;
        }
        return TRUE;
@@ -1441,7 +1447,7 @@ lootcont:
     if (container_at(cc.x, cc.y, FALSE)) {
        boolean any = FALSE;
 
-       if (!able_to_loot(cc.x, cc.y)) return 0;
+       if (!able_to_loot(cc.x, cc.y, TRUE)) return 0;
        for (cobj = level.objects[cc.x][cc.y]; cobj; cobj = nobj) {
            nobj = cobj->nexthere;
 
@@ -2297,4 +2303,149 @@ boolean outokay, inokay;
     return n;
 }
 
+static const char tippables[] = { ALL_CLASSES, TOOL_CLASS, 0 };
+
+/* #tip command -- empty container contents onto floor */
+int
+dotip()
+{
+    struct obj *cobj, *nobj;
+    coord cc;
+    int boxes;
+    char c, buf[BUFSZ];
+    const char *spillage = 0;
+
+    /*
+     * doesn't require free hands;
+     * limbs are needed to tip floor containers
+     */
+
+    /* at present, can only tip things at current spot, not adjacent ones */
+    cc.x = u.ux, cc.y = u.uy;
+
+    /* check floor container(s) first; at most one will be accessed */
+    if ((boxes = container_at(cc.x, cc.y, TRUE)) > 0) {
+       if (flags.verbose)
+           pline("There %s here.",
+                 (boxes > 1) ? "are containers" : "is a container");
+       Sprintf(buf, "You can't tip %s while carrying so much.",
+               !flags.verbose ? "a container" : (boxes > 1) ? "one" : "it");
+       if (!check_capacity(buf) && able_to_loot(cc.x, cc.y, FALSE)) {
+           for (cobj = level.objects[cc.x][cc.y]; cobj; cobj = nobj) {
+               nobj = cobj->nexthere;
+               if (!Is_container(cobj)) continue;
+
+               Sprintf(buf, "There is %s here, tip it?", doname(cobj));
+               c = ynq(buf);
+               if (c == 'q') return 0;
+               if (c == 'n') continue;
+
+               tipcontainer(cobj);
+               return 1;
+           }   /* next cobj */
+       }
+    }
+
+    /* either no floor container(s) or couldn't tip one or didn't tip any */
+    cobj = getobj(tippables, "tip");
+    if (!cobj) return 0;
+
+    /* normal case */
+    if (Is_container(cobj)) {
+       tipcontainer(cobj);
+       return 1;
+    }
+    /* assorted other cases */
+    if (Is_candle(cobj) && cobj->lamplit) {
+       /* note "wax" even for tallow candles to avoid giving away info */
+       spillage = "wax";
+    } else if ((cobj->otyp == POT_OIL && cobj->lamplit) ||
+           (cobj->otyp == OIL_LAMP && cobj->age != 0L) || 
+           (cobj->otyp == MAGIC_LAMP && cobj->spe != 0)) {
+       spillage = "oil";
+       /* todo: reduce potion's remaining burn timer or oil lamp's fuel */
+    } else if (cobj->otyp == CAN_OF_GREASE && cobj->spe > 0) {
+       /* charged consumed below */
+       spillage = "grease";
+    } else if (cobj->otyp == FOOD_RATION ||
+           cobj->otyp == CRAM_RATION ||
+           cobj->otyp == LEMBAS_WAFER) {
+       spillage = "crumbs";
+    } else if (cobj->oclass == VENOM_CLASS) {
+       spillage = "venom";
+    }
+    if (spillage) {
+       buf[0] = '\0';
+       if (is_pool(u.ux, u.uy))
+           Sprintf(buf, " and gradually %s", vtense(spillage, "dissipate"));
+       else if (is_lava(u.ux, u.uy))
+           Sprintf(buf, " and immediately %s away", vtense(spillage, "burn"));
+       pline("Some %s %s onto the %s%s.",
+             spillage, vtense(spillage, "spill"),
+             surface(u.ux, u.uy), buf);
+       /* shop usage message comes after the spill message */
+       if (cobj->otyp == CAN_OF_GREASE && cobj->spe > 0) {
+           check_unpaid(cobj);
+           cobj->spe--;                /* doesn't affect cobj->owt */
+       }
+       /* something [useless] happened */
+       return 1;
+    }
+    /* anything not covered yet */
+    if (cobj->oclass == POTION_CLASS)  /* can't pour potions... */
+       pline_The("%s %s securely sealed.", xname(cobj), otense(cobj, "are"));
+    else
+       pline(nothing_happens);
+    return 0;
+}
+
+STATIC_OVL void
+tipcontainer(box)
+struct obj *box;       /* or bag */
+{
+    if (box->olocked) {
+       pline("It's locked.");
+    } else if (box->otyp == BAG_OF_TRICKS && box->spe > 0) {
+       /* apply (not loot) this bag; uses up one charge */
+       bagotricks(box);
+    } else if (!Has_contents(box)) {
+       pline("It's empty.");
+    } else {
+       struct obj *otmp, *nobj;
+       boolean verbose = FALSE,
+               highdrop = !can_reach_floor(),
+               altarizing = IS_ALTAR(levl[u.ux][u.uy].typ),
+               cursed_mbag = (Is_mbag(box) && box->cursed);
+       int held = carried(box);
+       long loss = 0L;
+
+       pline("%s out%c",
+             box->cobj->nobj ? "Objects spill" : "An object spills",
+             !(highdrop || altarizing) ? ':' : '.');  
+       for (otmp = box->cobj; otmp; otmp = nobj) {
+           nobj = otmp->nobj;
+           obj_extract_self(otmp);
+           if (cursed_mbag && !rn2(13)) {
+               loss += mbag_item_gone(held, otmp);
+               /* abbreviated drop format is no longer appropriate */
+               verbose = TRUE;
+           } else if (highdrop) {
+               /* might break or fall down stairs; handles altars itself */
+               hitfloor(otmp);
+           } else {
+               if (altarizing)
+                   doaltarobj(otmp);
+               else if (verbose)
+                   pline("%s %s to the %s.", Doname2(otmp),
+                         otense(otmp, "drop"), surface(u.ux, u.uy));
+               else
+                   pline("%s%c", doname(otmp), nobj ? ',' : '.');
+               dropy(otmp);
+           }
+       }
+       if (loss)
+           You("owe %ld %s for lost merchandise.", loss, currency(loss));
+    }
+}
+
 /*pickup.c*/