#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....
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ### */
|
||||
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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--;
|
||||
|
||||
167
src/pickup.c
167
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*/
|
||||
|
||||
Reference in New Issue
Block a user