From: nethack.rankin Date: Thu, 3 Apr 2003 10:56:46 +0000 (+0000) Subject: #tip command - container access X-Git-Tag: MOVE2GIT~2038 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d0e4d1e31b9a613143b09a822fa24329e47c7c50;p=nethack #tip command - container access 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.... --- diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index dcb9a5e5f..aa995b359 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -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 diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index f4cadd8c4..eb49989cb 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -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 diff --git a/doc/fixes35.0 b/doc/fixes35.0 index df20675cc..3994c9062 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -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 diff --git a/include/extern.h b/include/extern.h index cfc172a67..2de637de3 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 ### */ diff --git a/src/cmd.c b/src/cmd.c index 3384d758c..38f6b7ba0 100644 --- 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}, diff --git a/src/invent.c b/src/invent.c index 0ac4fbb45..64eee6991 100644 --- a/src/invent.c +++ b/src/invent.c @@ -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--; diff --git a/src/pickup.c b/src/pickup.c index 1a626bad5..43ed52e32 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -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*/