#tipping shop containers (trunk only)
Using #tip (post-3.4.3 code) on a container that's on a shop floor
didn't handle ownership correctly. Bag of tricks could be emptied for
free, and contents of other containers were being sold to the shop even
when the shop already owned them. This fixes bag of tricks and makes a
first cut at doing so for regular containers.
Message handling when #tipping any bag of tricks was also suboptimal
since the decision about message delivery was made again as each charge
released something instead of waiting until the whole bag was emptied.
So you could get inappropriate "nothing seems to happen" before or after
a monster visibily popped up if something unseen was also produced.
This commit is contained in:
@@ -1038,7 +1038,7 @@ E void FDECL(mimic_hit_msg, (struct monst *, SHORT_P));
|
||||
#ifdef GOLDOBJ
|
||||
E void FDECL(mkmonmoney, (struct monst *, long));
|
||||
#endif
|
||||
E int FDECL(bagotricks, (struct obj *,BOOLEAN_P));
|
||||
E int FDECL(bagotricks, (struct obj *,BOOLEAN_P,int *));
|
||||
E boolean FDECL(propagate, (int, BOOLEAN_P,BOOLEAN_P));
|
||||
E boolean FDECL(usmellmon, (struct permonst *));
|
||||
|
||||
|
||||
@@ -2996,7 +2996,7 @@ doapply()
|
||||
res = use_container(&obj, 1);
|
||||
break;
|
||||
case BAG_OF_TRICKS:
|
||||
(void) bagotricks(obj, FALSE);
|
||||
(void) bagotricks(obj, FALSE, (int *)0);
|
||||
break;
|
||||
case CAN_OF_GREASE:
|
||||
use_grease(obj);
|
||||
|
||||
@@ -1818,9 +1818,10 @@ assign_sym:
|
||||
|
||||
/* release a monster from a bag of tricks; return number of monsters created */
|
||||
int
|
||||
bagotricks(bag, tipping)
|
||||
bagotricks(bag, tipping, seencount)
|
||||
struct obj *bag;
|
||||
boolean tipping; /* caller emptying entire contents; affects shop handling */
|
||||
int *seencount; /* secondary output */
|
||||
{
|
||||
int moncount = 0;
|
||||
|
||||
@@ -1832,8 +1833,7 @@ boolean tipping; /* caller emptying entire contents; affects shop handling */
|
||||
if (bag->dknown && objects[bag->otyp].oc_name_known) bag->cknown = 1;
|
||||
} else {
|
||||
struct monst *mtmp;
|
||||
boolean sawone = FALSE;
|
||||
int creatcnt = 1;
|
||||
int creatcnt = 1, seecount = 0;
|
||||
|
||||
consume_obj_charge(bag, !tipping);
|
||||
|
||||
@@ -1842,18 +1842,18 @@ boolean tipping; /* caller emptying entire contents; affects shop handling */
|
||||
mtmp = makemon((struct permonst *)0, u.ux, u.uy, NO_MM_FLAGS);
|
||||
if (mtmp) {
|
||||
++moncount;
|
||||
if (canspotmon(mtmp)) sawone = TRUE;
|
||||
if (canspotmon(mtmp)) ++seecount;
|
||||
}
|
||||
} while (--creatcnt > 0);
|
||||
if (sawone) {
|
||||
if (seecount) {
|
||||
if (seencount) *seencount += seecount;
|
||||
/* don't set contents-known flag if we just used last charge
|
||||
(such suppression doesn't actually gain us much since
|
||||
player can now deduce that the bag has become empty) */
|
||||
if (bag->spe > 0) bag->cknown = 1;
|
||||
if (bag->dknown) makeknown(BAG_OF_TRICKS);
|
||||
} else {
|
||||
/* #tip while blind can trigger this successive times */
|
||||
Norep("Nothing seems to happen.");
|
||||
} else if (!tipping) {
|
||||
pline(!moncount ? nothing_happens : "Nothing seems to happen.");
|
||||
}
|
||||
}
|
||||
return moncount;
|
||||
|
||||
@@ -1828,8 +1828,7 @@ boolean tipping; /* caller emptying entire contents; affects shop handling */
|
||||
obj->owt = weight(obj);
|
||||
/* using a shop's horn of plenty entails a usage fee and also
|
||||
confers ownership of the created item to the shopkeeper */
|
||||
if (carried(horn) ? horn->unpaid :
|
||||
(costly_spot(u.ux, u.uy) && !horn->no_charge))
|
||||
if (horn->unpaid)
|
||||
addtobill(obj, FALSE, FALSE, tipping);
|
||||
/* if it ended up on bill, we don't want "(unpaid, N zorkids)"
|
||||
being included in its formatted name during next message */
|
||||
|
||||
42
src/pickup.c
42
src/pickup.c
@@ -2529,7 +2529,21 @@ STATIC_OVL void
|
||||
tipcontainer(box)
|
||||
struct obj *box; /* or bag */
|
||||
{
|
||||
boolean empty_it = FALSE;
|
||||
xchar ox = u.ux, oy = u.uy; /* #tip only works at hero's location */
|
||||
boolean empty_it = FALSE,
|
||||
/* Shop handling: can't rely on the container's own unpaid
|
||||
or no_charge status because contents might differ with it.
|
||||
A carried container's contents will be flagged as unpaid
|
||||
or not, as appropriate, and need no special handling here.
|
||||
Items owned by the hero get sold to the shop without
|
||||
confirmation as with other uncontrolled drops. A floor
|
||||
container's contents will be marked no_charge if owned by
|
||||
hero, otherwise they're owned by the shop. By passing
|
||||
the contents through shop billing, they end up getting
|
||||
treated the same as in the carried case. We do so one
|
||||
item at a time instead of doing whole container at once
|
||||
to reduce the chance of exhausting shk's billing capacity. */
|
||||
maybeshopgoods = !carried(box) && costly_spot(ox, oy);
|
||||
|
||||
/* caveat: this assumes that cknown, lknown, olocked, and otrapped
|
||||
fields haven't been overloaded to mean something special for the
|
||||
@@ -2547,22 +2561,29 @@ struct obj *box; /* or bag */
|
||||
}
|
||||
} else if (box->otyp == BAG_OF_TRICKS || box->otyp == HORN_OF_PLENTY) {
|
||||
boolean bag = box->otyp == BAG_OF_TRICKS;
|
||||
int old_spe = box->spe;
|
||||
int old_spe = box->spe, seen = 0;
|
||||
|
||||
if (maybeshopgoods && !box->no_charge)
|
||||
addtobill(box, FALSE, FALSE, TRUE);
|
||||
/* apply this bag/horn until empty or monster/object creation fails
|
||||
(if the latter occurs, force the former...) */
|
||||
do {
|
||||
if (!(bag ? bagotricks(box, TRUE) : hornoplenty(box, TRUE)))
|
||||
if (!(bag ? bagotricks(box, TRUE, &seen) : hornoplenty(box, TRUE)))
|
||||
break;
|
||||
} while (box->spe > 0);
|
||||
|
||||
if (box->spe < old_spe) {
|
||||
if (bag) pline((seen == 0) ? "Nothing seems to happen." :
|
||||
(seen == 1) ? "A monster appears." :
|
||||
"Monsters appear!");
|
||||
/* check_unpaid wants to see a non-zero charge count */
|
||||
box->spe = old_spe;
|
||||
check_unpaid_usage(box, TRUE);
|
||||
box->spe = 0; /* empty */
|
||||
box->cknown = 1;
|
||||
}
|
||||
if (maybeshopgoods && !box->no_charge)
|
||||
subfrombill(box, shop_keeper(*in_rooms(ox, oy, SHOPBASE)));
|
||||
} else if (box->spe) {
|
||||
char yourbuf[BUFSZ];
|
||||
|
||||
@@ -2584,7 +2605,7 @@ struct obj *box; /* or bag */
|
||||
struct obj *otmp, *nobj;
|
||||
boolean verbose = FALSE,
|
||||
highdrop = !can_reach_floor(TRUE),
|
||||
altarizing = IS_ALTAR(levl[u.ux][u.uy].typ),
|
||||
altarizing = IS_ALTAR(levl[ox][oy].typ),
|
||||
cursed_mbag = (Is_mbag(box) && box->cursed);
|
||||
int held = carried(box);
|
||||
long loss = 0L;
|
||||
@@ -2600,7 +2621,15 @@ struct obj *box; /* or bag */
|
||||
loss += mbag_item_gone(held, otmp);
|
||||
/* abbreviated drop format is no longer appropriate */
|
||||
verbose = TRUE;
|
||||
} else if (highdrop) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (maybeshopgoods) {
|
||||
addtobill(otmp, FALSE, FALSE, TRUE);
|
||||
iflags.suppress_price++; /* doname formatting */
|
||||
}
|
||||
|
||||
if (highdrop) {
|
||||
/* might break or fall down stairs; handles altars itself */
|
||||
hitfloor(otmp);
|
||||
} else {
|
||||
@@ -2608,11 +2637,12 @@ struct obj *box; /* or bag */
|
||||
doaltarobj(otmp);
|
||||
else if (verbose)
|
||||
pline("%s %s to the %s.", Doname2(otmp),
|
||||
otense(otmp, "drop"), surface(u.ux, u.uy));
|
||||
otense(otmp, "drop"), surface(ox, oy));
|
||||
else
|
||||
pline("%s%c", doname(otmp), nobj ? ',' : '.');
|
||||
dropy(otmp);
|
||||
}
|
||||
if (maybeshopgoods) iflags.suppress_price--; /* reset */
|
||||
}
|
||||
if (loss) /* magic bag lost some shop goods */
|
||||
You("owe %ld %s for lost merchandise.", loss, currency(loss));
|
||||
|
||||
Reference in New Issue
Block a user