alteration of shop-owned objects (trunk only)

[This ought to be suitable for the branch version too but I'm not going to
spend the effort to migrate it there.]

     Recently From a bug report, reducing
the value of a shop object via cursed enchantment was ignored by shopkeeper.
This replaces the existing costly_cancel() routine with costly_alteration()
which performs a similar task:  bill for any item whose value has been made
less.  The hero owns the resulting object but must pay for the original one
before being allowed to leave the shop.

     This covers the majority of cases where bill_dummy_object() was already
being used:  cancelling a charged or enchanted item, casting drain life at
same, diluting potions or blanking scrolls or books by dipping them into a
potion of water, dulling a weapon by engraving with it, eating unpaid food
or opening unpaid tins, applying a cream pie to hit yourself with it in the
face, applying a wand to break it, burning something by dipping it into lit
potion of oil, and clearing potions by dipping a unicorn horn into them.
The shop billing behavior for those actions hasn't been changed, just
consolidated into one place which delivers a common message for them.

     This also covers many cases which weren't being handled:  stripping
wand or magic tool charges via cursed scroll of charging, reducing a charged
ring's enchantment via same, reducing weapon or armor enchantment via cursed
scroll of enchant weapon or armor, stripping an item's rustproofing via
confused enchantment, making a crysknife revert to a worm tooth, unblessing
potions of holy water or uncursing potions of unholy water.  (That last one
won't be billed if it's the result of prayer rather scroll, spell, or #dip.)

     And this tries to handle the reverse situation more thoroughly too:
many actions which improve the value of an unpaid item now also cause the
shop bill to be updated to reflect its new higher price.  Aside from the
basic enchanting and charging magic, it covers converting dragon scales into
dragon scale mail and worm tooth into crysknife.  Some things which might be
expected to inflate shop prices, like rustproofing or increasing the number
of charges in a wand, don't actually affect the price.  And there are bound
to be cases where the price is affected but I've overlooked.
This commit is contained in:
nethack.rankin
2005-04-05 05:24:04 +00:00
parent 19a6dd6f3f
commit c7099cd772
13 changed files with 369 additions and 223 deletions

View File

@@ -315,6 +315,11 @@ struct obj *otmp;
* an object which is different from what it started out as; the "I x"
* command needs to display the original object.
*
* [BUG: The cost might end up being different if item originally had a
* surcharge but doesn't get one now or vice versa. Having the dummy keep
* the same o_id value as the original would avoid this; is that viable?
* (Mustn't give the original itself a new o_id, so can't just swap them.)]
*
* The caller is responsible for checking otmp->unpaid and
* costly_spot(u.ux, u.uy). This function will make otmp no charge.
*
@@ -349,6 +354,74 @@ register struct obj *otmp;
return;
}
/* alteration types; must match COST_xxx macros in hack.h */
static const char * const alteration_verbs[] = {
"cancel", "drain", "uncharge", "unbless", "uncurse",
"disenchant", "degrade", "dilute", "erase", "burn",
"neutralize", "destroy", "splatter", "bite", "open",
};
/* possibly bill for an object which the player has just modified */
void
costly_alteration(obj, alter_type)
struct obj *obj;
int alter_type;
{
xchar ox, oy;
char objroom;
const char *those, *them, *what;
struct monst *shkp = 0;
if (alter_type < 0 || alter_type >= SIZE(alteration_verbs)) {
impossible("invalid alteration type (%d)", alter_type);
alter_type = 0;
}
ox = oy = 0; /* lint suppression */
objroom = '\0'; /* ditto */
if (carried(obj) || obj->where == OBJ_FREE) {
/* OBJ_FREE catches obj_no_longer_held()'s transformation
of crysknife back into worm tooth; the object has been
removed from inventory but not necessarily placed at
its new location yet--the unpaid flag will still be set
if this item is owned by a shop */
if (!obj->unpaid) return;
} else {
/* this get_obj_location shouldn't fail, but if it does,
use hero's location */
if (!get_obj_location(obj, &ox, &oy, CONTAINED_TOO))
ox = u.ux, oy = u.uy;
objroom = *in_rooms(ox, oy, SHOPBASE);
/* if no shop cares about it, we're done */
if (!billable(&shkp, obj, objroom, FALSE)) return;
}
if (obj->quan == 1L)
those = "that", them = "it";
else
those = "those", them = "them";
switch (obj->where) {
case OBJ_FREE: /* obj_no_longer_held() */
case OBJ_INVENT:
what = simple_typename(obj->otyp);
if (obj->quan != 1L) what = makeplural(what);
verbalize("You %s %s %s, you pay for %s!",
alteration_verbs[alter_type], those, what, them);
bill_dummy_object(obj);
break;
case OBJ_FLOOR:
if (costly_spot(u.ux, u.uy) && objroom == *u.ushops) {
verbalize("You %s %s, you pay for %s!",
alteration_verbs[alter_type], those, them);
bill_dummy_object(obj);
} else {
(void) stolen_value(obj, ox, oy, FALSE, FALSE);
}
break;
}
}
static const char dknowns[] = {
WAND_CLASS, RING_CLASS, POTION_CLASS, SCROLL_CLASS,
GEM_CLASS, SPBOOK_CLASS, WEAPON_CLASS, TOOL_CLASS, 0