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
|
when the Astral level got created, any fake player character monsters that
|
||||||
were seen or sensed when created were reported as "<mon> suddenly
|
were seen or sensed when created were reported as "<mon> suddenly
|
||||||
appears!" rather than be treated as part of level's initial population
|
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
|
curses: 'msg_window' option wasn't functional for curses unless the binary
|
||||||
also included tty support
|
also included tty support
|
||||||
|
|||||||
+1
-1
@@ -2105,7 +2105,7 @@ extern int doloot(void);
|
|||||||
extern void observe_quantum_cat(struct obj *, boolean, boolean);
|
extern void observe_quantum_cat(struct obj *, boolean, boolean);
|
||||||
extern boolean container_gone(int(*)(struct obj *));
|
extern boolean container_gone(int(*)(struct obj *));
|
||||||
extern boolean u_handsy(void);
|
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 loot_mon(struct monst *, int *, boolean *);
|
||||||
extern int dotip(void);
|
extern int dotip(void);
|
||||||
extern struct autopickup_exception *check_autopickup_exceptions(struct obj *);
|
extern struct autopickup_exception *check_autopickup_exceptions(struct obj *);
|
||||||
|
|||||||
+1
-1
@@ -4006,7 +4006,7 @@ doapply(void)
|
|||||||
case SACK:
|
case SACK:
|
||||||
case BAG_OF_HOLDING:
|
case BAG_OF_HOLDING:
|
||||||
case OILSKIN_SACK:
|
case OILSKIN_SACK:
|
||||||
res = use_container(&obj, 1, FALSE);
|
res = use_container(&obj, TRUE, FALSE);
|
||||||
break;
|
break;
|
||||||
case BAG_OF_TRICKS:
|
case BAG_OF_TRICKS:
|
||||||
(void) bagotricks(obj, FALSE, (int *) 0);
|
(void) bagotricks(obj, FALSE, (int *) 0);
|
||||||
|
|||||||
+32
-16
@@ -31,10 +31,10 @@ static int lift_object(struct obj *, struct obj *, long *, boolean);
|
|||||||
static boolean mbag_explodes(struct obj *, int);
|
static boolean mbag_explodes(struct obj *, int);
|
||||||
static boolean is_boh_item_gone(void);
|
static boolean is_boh_item_gone(void);
|
||||||
static void do_boh_explosion(struct obj *, boolean);
|
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 in_container(struct obj *);
|
||||||
static int out_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 int stash_ok(struct obj *);
|
||||||
static void explain_container_prompt(boolean);
|
static void explain_container_prompt(boolean);
|
||||||
static int traditional_loot(boolean);
|
static int traditional_loot(boolean);
|
||||||
@@ -1958,7 +1958,7 @@ do_loot_cont(
|
|||||||
g.abort_looting = TRUE;
|
g.abort_looting = TRUE;
|
||||||
return ECMD_TIME;
|
return ECMD_TIME;
|
||||||
}
|
}
|
||||||
return use_container(cobjp, 0, (boolean) (cindex < ccount));
|
return use_container(cobjp, FALSE, (boolean) (cindex < ccount));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #loot extended command */
|
/* #loot extended command */
|
||||||
@@ -2308,9 +2308,8 @@ is_boh_item_gone(void)
|
|||||||
return (boolean) (!rn2(13));
|
return (boolean) (!rn2(13));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scatter most of Bag of holding contents around.
|
/* Scatter most of Bag of holding contents around. Some items will be
|
||||||
Some items will be destroyed with the same chance as looting a cursed bag.
|
destroyed with the same chance as looting a cursed bag. */
|
||||||
*/
|
|
||||||
static void
|
static void
|
||||||
do_boh_explosion(struct obj *boh, boolean on_floor)
|
do_boh_explosion(struct obj *boh, boolean on_floor)
|
||||||
{
|
{
|
||||||
@@ -2329,7 +2328,7 @@ do_boh_explosion(struct obj *boh, boolean on_floor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static long
|
static long
|
||||||
boh_loss(struct obj *container, int held)
|
boh_loss(struct obj *container, boolean held)
|
||||||
{
|
{
|
||||||
/* sometimes toss objects if a cursed magic bag */
|
/* sometimes toss objects if a cursed magic bag */
|
||||||
if (Is_mbag(container) && container->cursed && Has_contents(container)) {
|
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 */
|
/* an object inside a cursed bag of holding is being destroyed */
|
||||||
static long
|
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;
|
struct monst *shkp;
|
||||||
long loss = 0L;
|
long loss = 0L;
|
||||||
@@ -2755,7 +2754,7 @@ stash_ok(struct obj *obj)
|
|||||||
int
|
int
|
||||||
use_container(
|
use_container(
|
||||||
struct obj **objp,
|
struct obj **objp,
|
||||||
int held,
|
boolean held,
|
||||||
boolean more_containers) /* True iff #loot multiple and this isn't last */
|
boolean more_containers) /* True iff #loot multiple and this isn't last */
|
||||||
{
|
{
|
||||||
struct obj *otmp, *obj = *objp;
|
struct obj *otmp, *obj = *objp;
|
||||||
@@ -3436,7 +3435,7 @@ static void
|
|||||||
tipcontainer(struct obj *box) /* or bag */
|
tipcontainer(struct obj *box) /* or bag */
|
||||||
{
|
{
|
||||||
coordxy ox = u.ux, oy = u.uy; /* #tip only works at hero's location */
|
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;
|
struct obj *targetbox = (struct obj *) 0;
|
||||||
boolean cancelled = FALSE;
|
boolean cancelled = FALSE;
|
||||||
|
|
||||||
@@ -3469,14 +3468,14 @@ tipcontainer(struct obj *box) /* or bag */
|
|||||||
if (targetbox && tipcontainer_checks(targetbox, TRUE) != TIPCHECK_OK)
|
if (targetbox && tipcontainer_checks(targetbox, TRUE) != TIPCHECK_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (empty_it) {
|
{
|
||||||
struct obj *otmp, *nobj;
|
struct obj *otmp, *nobj;
|
||||||
boolean terse, highdrop = !can_reach_floor(TRUE),
|
boolean terse, highdrop = !can_reach_floor(TRUE),
|
||||||
altarizing = IS_ALTAR(levl[ox][oy].typ),
|
altarizing = IS_ALTAR(levl[ox][oy].typ),
|
||||||
cursed_mbag = (Is_mbag(box) && box->cursed);
|
cursed_mbag = (Is_mbag(box) && box->cursed);
|
||||||
int held = carried(box) || (targetbox && carried(targetbox));
|
|
||||||
long loss = 0L;
|
long loss = 0L;
|
||||||
|
|
||||||
|
held = carried(box) || (targetbox && carried(targetbox));
|
||||||
if (u.uswallow)
|
if (u.uswallow)
|
||||||
highdrop = altarizing = FALSE;
|
highdrop = altarizing = FALSE;
|
||||||
terse = !(highdrop || altarizing || costly_spot(box->ox, box->oy));
|
terse = !(highdrop || altarizing || costly_spot(box->ox, box->oy));
|
||||||
@@ -3494,6 +3493,7 @@ tipcontainer(struct obj *box) /* or bag */
|
|||||||
pline("%s out%c",
|
pline("%s out%c",
|
||||||
box->cobj->nobj ? "Objects spill" : "An object spills",
|
box->cobj->nobj ? "Objects spill" : "An object spills",
|
||||||
terse ? ':' : '.');
|
terse ? ':' : '.');
|
||||||
|
|
||||||
for (otmp = box->cobj; otmp; otmp = nobj) {
|
for (otmp = box->cobj; otmp; otmp = nobj) {
|
||||||
nobj = otmp->nobj;
|
nobj = otmp->nobj;
|
||||||
obj_extract_self(otmp);
|
obj_extract_self(otmp);
|
||||||
@@ -3507,14 +3507,27 @@ tipcontainer(struct obj *box) /* or bag */
|
|||||||
terse = FALSE;
|
terse = FALSE;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maybeshopgoods) {
|
if (maybeshopgoods) {
|
||||||
addtobill(otmp, FALSE, FALSE, TRUE);
|
addtobill(otmp, FALSE, FALSE, TRUE);
|
||||||
iflags.suppress_price++; /* doname formatting */
|
iflags.suppress_price++; /* doname formatting */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetbox) {
|
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) {
|
} else if (highdrop) {
|
||||||
/* might break or fall down stairs; handles altars itself */
|
/* might break or fall down stairs; handles altars itself */
|
||||||
hitfloor(otmp, TRUE);
|
hitfloor(otmp, TRUE);
|
||||||
@@ -3544,7 +3557,8 @@ tipcontainer(struct obj *box) /* or bag */
|
|||||||
if (held)
|
if (held)
|
||||||
(void) encumber_msg();
|
(void) encumber_msg();
|
||||||
}
|
}
|
||||||
if (carried(box) || (targetbox && carried(targetbox)))
|
|
||||||
|
if (held)
|
||||||
update_inventory();
|
update_inventory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3600,7 +3614,9 @@ tipcontainer_gettarget(struct obj *box, boolean *cancelled)
|
|||||||
clr, "", MENU_ITEMFLAGS_NONE);
|
clr, "", MENU_ITEMFLAGS_NONE);
|
||||||
|
|
||||||
for (otmp = g.invent; otmp; otmp = otmp->nobj)
|
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 = cg.zeroany;
|
||||||
any.a_obj = otmp;
|
any.a_obj = otmp;
|
||||||
add_menu(win, &nul_glyphinfo, &any, otmp->invlet, 0,
|
add_menu(win, &nul_glyphinfo, &any, otmp->invlet, 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user