From f8f5642d3ab41daa6dd6db90143c64545ca1f04c Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Thu, 9 Nov 2006 06:36:36 +0000 Subject: [PATCH] #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. --- include/extern.h | 2 +- src/apply.c | 2 +- src/makemon.c | 16 ++++++++-------- src/mkobj.c | 3 +-- src/pickup.c | 42 ++++++++++++++++++++++++++++++++++++------ 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/include/extern.h b/include/extern.h index 3f5eb9f48..96a043505 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 *)); diff --git a/src/apply.c b/src/apply.c index 4010540c4..0b4103fd2 100644 --- a/src/apply.c +++ b/src/apply.c @@ -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); diff --git a/src/makemon.c b/src/makemon.c index 3536dce42..047463261 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -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; diff --git a/src/mkobj.c b/src/mkobj.c index 5eea5b698..f0985d19b 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -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 */ diff --git a/src/pickup.c b/src/pickup.c index 320efa849..61315c3c6 100644 --- a/src/pickup.c +++ b/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));