fix issue #872 - container-to-container #tip
Reported by k2: tipping one container's contents directly into another container allowed transferring a wand of cancellation (not mentioned: or a bag of holding or a bag of tricks) into a bag of holding without blowing it up. That's now fixed. There are other issues that this doesn't touch: I think it's odd that you can transfer stuff from one carried container to another but not from a carried container to a floor container nor from one floor container to another one at same spot. I didn't test shop billing so an not sure what happens when #tip blows up a bag of holding and there are some unpaid items involved. Using #tip on horn of plenty treats it like a container, but doing that when it's carried doesn't offer the chance to tip its contents directly into a carried container. Tipping a carried container does not require free hands or even limbs (for playability) but tipping such into another container should require at least one free hand. Fixes #872
This commit is contained in:
@@ -1380,6 +1380,9 @@ command line processing interleaved with options file processing is complex:
|
||||
when the Astral level got created, any fake player character monsters that
|
||||
were seen or sensed when created were reported as "<mon> suddenly
|
||||
appears!" rather than be treated as part of level's initial population
|
||||
tipping contents of one container directly into another allowed transferring
|
||||
wands of cancellation and bags of holding or tricks that were inside
|
||||
a sack, box, or chest into a bag of holding without blowing it up
|
||||
|
||||
curses: 'msg_window' option wasn't functional for curses unless the binary
|
||||
also included tty support
|
||||
|
||||
@@ -2105,7 +2105,7 @@ extern int doloot(void);
|
||||
extern void observe_quantum_cat(struct obj *, boolean, boolean);
|
||||
extern boolean container_gone(int(*)(struct obj *));
|
||||
extern boolean u_handsy(void);
|
||||
extern int use_container(struct obj **, int, boolean);
|
||||
extern int use_container(struct obj **, boolean, boolean);
|
||||
extern int loot_mon(struct monst *, int *, boolean *);
|
||||
extern int dotip(void);
|
||||
extern struct autopickup_exception *check_autopickup_exceptions(struct obj *);
|
||||
|
||||
@@ -4006,7 +4006,7 @@ doapply(void)
|
||||
case SACK:
|
||||
case BAG_OF_HOLDING:
|
||||
case OILSKIN_SACK:
|
||||
res = use_container(&obj, 1, FALSE);
|
||||
res = use_container(&obj, TRUE, FALSE);
|
||||
break;
|
||||
case BAG_OF_TRICKS:
|
||||
(void) bagotricks(obj, FALSE, (int *) 0);
|
||||
|
||||
48
src/pickup.c
48
src/pickup.c
@@ -31,10 +31,10 @@ static int lift_object(struct obj *, struct obj *, long *, boolean);
|
||||
static boolean mbag_explodes(struct obj *, int);
|
||||
static boolean is_boh_item_gone(void);
|
||||
static void do_boh_explosion(struct obj *, boolean);
|
||||
static long boh_loss(struct obj *, int);
|
||||
static long boh_loss(struct obj *, boolean);
|
||||
static int in_container(struct obj *);
|
||||
static int out_container(struct obj *);
|
||||
static long mbag_item_gone(int, struct obj *, boolean);
|
||||
static long mbag_item_gone(boolean, struct obj *, boolean);
|
||||
static int stash_ok(struct obj *);
|
||||
static void explain_container_prompt(boolean);
|
||||
static int traditional_loot(boolean);
|
||||
@@ -1958,7 +1958,7 @@ do_loot_cont(
|
||||
g.abort_looting = TRUE;
|
||||
return ECMD_TIME;
|
||||
}
|
||||
return use_container(cobjp, 0, (boolean) (cindex < ccount));
|
||||
return use_container(cobjp, FALSE, (boolean) (cindex < ccount));
|
||||
}
|
||||
|
||||
/* #loot extended command */
|
||||
@@ -2308,9 +2308,8 @@ is_boh_item_gone(void)
|
||||
return (boolean) (!rn2(13));
|
||||
}
|
||||
|
||||
/* Scatter most of Bag of holding contents around.
|
||||
Some items will be destroyed with the same chance as looting a cursed bag.
|
||||
*/
|
||||
/* Scatter most of Bag of holding contents around. Some items will be
|
||||
destroyed with the same chance as looting a cursed bag. */
|
||||
static void
|
||||
do_boh_explosion(struct obj *boh, boolean on_floor)
|
||||
{
|
||||
@@ -2329,7 +2328,7 @@ do_boh_explosion(struct obj *boh, boolean on_floor)
|
||||
}
|
||||
|
||||
static long
|
||||
boh_loss(struct obj *container, int held)
|
||||
boh_loss(struct obj *container, boolean held)
|
||||
{
|
||||
/* sometimes toss objects if a cursed magic bag */
|
||||
if (Is_mbag(container) && container->cursed && Has_contents(container)) {
|
||||
@@ -2598,7 +2597,7 @@ removed_from_icebox(struct obj *obj)
|
||||
|
||||
/* an object inside a cursed bag of holding is being destroyed */
|
||||
static long
|
||||
mbag_item_gone(int held, struct obj *item, boolean silent)
|
||||
mbag_item_gone(boolean held, struct obj *item, boolean silent)
|
||||
{
|
||||
struct monst *shkp;
|
||||
long loss = 0L;
|
||||
@@ -2755,7 +2754,7 @@ stash_ok(struct obj *obj)
|
||||
int
|
||||
use_container(
|
||||
struct obj **objp,
|
||||
int held,
|
||||
boolean held,
|
||||
boolean more_containers) /* True iff #loot multiple and this isn't last */
|
||||
{
|
||||
struct obj *otmp, *obj = *objp;
|
||||
@@ -3436,7 +3435,7 @@ static void
|
||||
tipcontainer(struct obj *box) /* or bag */
|
||||
{
|
||||
coordxy ox = u.ux, oy = u.uy; /* #tip only works at hero's location */
|
||||
boolean empty_it = TRUE, maybeshopgoods;
|
||||
boolean held = FALSE, maybeshopgoods;
|
||||
struct obj *targetbox = (struct obj *) 0;
|
||||
boolean cancelled = FALSE;
|
||||
|
||||
@@ -3469,14 +3468,14 @@ tipcontainer(struct obj *box) /* or bag */
|
||||
if (targetbox && tipcontainer_checks(targetbox, TRUE) != TIPCHECK_OK)
|
||||
return;
|
||||
|
||||
if (empty_it) {
|
||||
{
|
||||
struct obj *otmp, *nobj;
|
||||
boolean terse, highdrop = !can_reach_floor(TRUE),
|
||||
altarizing = IS_ALTAR(levl[ox][oy].typ),
|
||||
cursed_mbag = (Is_mbag(box) && box->cursed);
|
||||
int held = carried(box) || (targetbox && carried(targetbox));
|
||||
long loss = 0L;
|
||||
|
||||
held = carried(box) || (targetbox && carried(targetbox));
|
||||
if (u.uswallow)
|
||||
highdrop = altarizing = FALSE;
|
||||
terse = !(highdrop || altarizing || costly_spot(box->ox, box->oy));
|
||||
@@ -3494,6 +3493,7 @@ tipcontainer(struct obj *box) /* or bag */
|
||||
pline("%s out%c",
|
||||
box->cobj->nobj ? "Objects spill" : "An object spills",
|
||||
terse ? ':' : '.');
|
||||
|
||||
for (otmp = box->cobj; otmp; otmp = nobj) {
|
||||
nobj = otmp->nobj;
|
||||
obj_extract_self(otmp);
|
||||
@@ -3507,14 +3507,27 @@ tipcontainer(struct obj *box) /* or bag */
|
||||
terse = FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (maybeshopgoods) {
|
||||
addtobill(otmp, FALSE, FALSE, TRUE);
|
||||
iflags.suppress_price++; /* doname formatting */
|
||||
}
|
||||
|
||||
if (targetbox) {
|
||||
(void) add_to_container(targetbox, otmp);
|
||||
if (Is_mbag(targetbox) && mbag_explodes(otmp, 0)) {
|
||||
/* explicitly mention what item is triggering explosion */
|
||||
urgent_pline(
|
||||
"As %s %s inside, you are blasted by a magical explosion!",
|
||||
doname(otmp), otense(otmp, "tumble"));
|
||||
do_boh_explosion(targetbox, held);
|
||||
nobj = 0; /* stop tipping; want loop to exit 'normally' */
|
||||
if (!held)
|
||||
useup(targetbox);
|
||||
else
|
||||
useupf(targetbox, targetbox->quan);
|
||||
targetbox = 0; /* it's gone */
|
||||
} else {
|
||||
(void) add_to_container(targetbox, otmp);
|
||||
}
|
||||
} else if (highdrop) {
|
||||
/* might break or fall down stairs; handles altars itself */
|
||||
hitfloor(otmp, TRUE);
|
||||
@@ -3544,7 +3557,8 @@ tipcontainer(struct obj *box) /* or bag */
|
||||
if (held)
|
||||
(void) encumber_msg();
|
||||
}
|
||||
if (carried(box) || (targetbox && carried(targetbox)))
|
||||
|
||||
if (held)
|
||||
update_inventory();
|
||||
}
|
||||
|
||||
@@ -3600,7 +3614,9 @@ tipcontainer_gettarget(struct obj *box, boolean *cancelled)
|
||||
clr, "", MENU_ITEMFLAGS_NONE);
|
||||
|
||||
for (otmp = g.invent; otmp; otmp = otmp->nobj)
|
||||
if (Is_container(otmp) && (otmp != box)) {
|
||||
if (Is_container(otmp) && otmp != box
|
||||
/* don't include any container that's known to be locked */
|
||||
&& (!otmp->olocked || !otmp->lknown)) {
|
||||
any = cg.zeroany;
|
||||
any.a_obj = otmp;
|
||||
add_menu(win, &nul_glyphinfo, &any, otmp->invlet, 0,
|
||||
|
||||
Reference in New Issue
Block a user