diff --git a/doc/fixes35.0 b/doc/fixes35.0 index 4c1bbed2e..15d49459c 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -80,6 +80,9 @@ allow use of the < command to try to exit a pit clean up messages when you stop levitation while riding a flying steed account for all attacks when determining max_passive_dmg dipping in acid can erode the dipped object +various actions--such as enchanting--performed on an unpaid shop object + either force the hero to buy the item (when its value is lowered) or + increase the current bill (when its value is raised) Platform- and/or Interface-Specific Fixes diff --git a/include/extern.h b/include/extern.h index 8e456d04d..b41f0f2e5 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1084,6 +1084,7 @@ E int NDECL(rndmonnum); E struct obj *FDECL(splitobj, (struct obj *,long)); E void FDECL(replace_object, (struct obj *,struct obj *)); E void FDECL(bill_dummy_object, (struct obj *)); +E void FDECL(costly_alteration, (struct obj *,int)); E struct obj *FDECL(mksobj, (int,BOOLEAN_P,BOOLEAN_P)); E int FDECL(bcsign, (struct obj *)); E int FDECL(weight, (struct obj *)); @@ -1878,6 +1879,7 @@ E struct obj *FDECL(find_oid, (unsigned)); E long FDECL(contained_cost, (struct obj *,struct monst *,long,BOOLEAN_P, BOOLEAN_P)); E long FDECL(contained_gold, (struct obj *)); E void FDECL(picked_container, (struct obj *)); +E void FDECL(alter_cost, (struct obj *,long)); E long FDECL(unpaid_cost, (struct obj *)); E boolean FDECL(billable, (struct monst **,struct obj *,CHAR_P,BOOLEAN_P)); E void FDECL(addtobill, (struct obj *,BOOLEAN_P,BOOLEAN_P,BOOLEAN_P)); diff --git a/include/hack.h b/include/hack.h index 1846e92a1..c9ae85eb8 100644 --- a/include/hack.h +++ b/include/hack.h @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)hack.h 3.5 2005/03/07 */ +/* SCCS Id: @(#)hack.h 3.5 2005/03/28 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -59,6 +59,23 @@ #define SELL_DELIBERATE (1) #define SELL_DONTSELL (2) +/* alteration types--keep in synch with costly_alteration(mkobj.c) */ +#define COST_CANCEL 0 /* standard cancellation */ +#define COST_DRAIN 1 /* drain life upon an object */ +#define COST_UNCHRG 2 /* cursed charging */ +#define COST_UNBLSS 3 /* unbless (devalues holy water) */ +#define COST_UNHOLY 4 /* uncurse (devalues unholy water) */ +#define COST_DECHNT 5 /* disenchant weapons or armor */ +#define COST_DEGRD 6 /* removal of rustproofing, dulling via engraving */ +#define COST_DILUTE 7 /* potion dilution */ +#define COST_ERASE 8 /* scroll or spellbook blanking */ +#define COST_BURN 9 /* dipped into flaming oil */ +#define COST_NUTRLZ 10 /* neutralized via unicorn horn */ +#define COST_DSTROY 11 /* wand breaking (bill first, useup later) */ +#define COST_SPLAT 12 /* cream pie to own face (ditto) */ +#define COST_BITE 13 /* start eating food */ +#define COST_OPEN 14 /* open tin */ + /* * This is the way the game ends. If these are rearranged, the arrays * in end.c and topten.c will need to be changed. Some parts of the diff --git a/src/apply.c b/src/apply.c index 5beb35615..f93e487b0 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)apply.c 3.5 2005/01/05 */ +/* SCCS Id: @(#)apply.c 3.5 2005/03/28 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1132,7 +1132,7 @@ struct obj *obj; if (obj->where == OBJ_MINVENT ? cansee(x,y) : !Blind) pline("%s %s light!", Yname2(obj), otense(obj, "catch")); if (obj->otyp == POT_OIL) makeknown(obj->otyp); - if (obj->unpaid && costly_spot(u.ux, u.uy) && (obj->where == OBJ_INVENT)) { + if (carried(obj) && obj->unpaid && costly_spot(u.ux, u.uy)) { /* if it catches while you have it, then it's your tough luck */ check_unpaid(obj); verbalize("That's in addition to the cost of %s %s, of course.", @@ -1187,7 +1187,8 @@ struct obj *obj; Blind ? "." : " brightly!"); if (obj->unpaid && costly_spot(u.ux, u.uy) && obj->age == 20L * (long)objects[obj->otyp].oc_cost) { - const char *ithem = obj->quan > 1L ? "them" : "it"; + const char *ithem = (obj->quan > 1L) ? "them" : "it"; + verbalize("You burn %s, you bought %s!", ithem, ithem); bill_dummy_object(obj); } @@ -2582,10 +2583,8 @@ struct obj *obj; You_cant("see through all the sticky goop on your %s.", body_part(FACE)); } - if (obj->unpaid) { - verbalize("You used it, you bought it!"); - bill_dummy_object(obj); - } + /* useup() is appropriate, but we want costly_alteration()'s message */ + costly_alteration(obj, COST_SPLAT); obj_extract_self(obj); delobj(obj); return(0); @@ -2753,7 +2752,7 @@ do_break_wand(obj) */ if (obj->unpaid) { check_unpaid(obj); /* Extra charge for use */ - bill_dummy_object(obj); + costly_alteration(obj, COST_DSTROY); } current_wand = obj; /* destroy_item might reset this */ diff --git a/src/do.c b/src/do.c index b3c4693ae..d125bd455 100644 --- a/src/do.c +++ b/src/do.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)do.c 3.5 2004/09/10 */ +/* SCCS Id: @(#)do.c 3.5 2005/03/28 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -589,16 +589,20 @@ struct obj *obj; { if (!obj) { return; - } else if ((Is_container(obj) || obj->otyp == STATUE) && obj->cobj) { + } else if (Has_contents(obj)) { struct obj *contents; - for(contents=obj->cobj; contents; contents=contents->nobj) + + for (contents = obj->cobj; contents; contents = contents->nobj) obj_no_longer_held(contents); } - switch(obj->otyp) { + switch (obj->otyp) { case CRYSKNIFE: /* KMH -- Fixed crysknives have only 10% chance of reverting */ /* only changes when not held by player or monster */ if (!obj->oerodeproof || !rn2(10)) { + /* if monsters aren't moving, assume player is responsible */ + if (!context.mon_moving && !program_state.gameover) + costly_alteration(obj, COST_DEGRD); obj->otyp = WORM_TOOTH; obj->oerodeproof = 0; } diff --git a/src/eat.c b/src/eat.c index 5368ceb9a..495b4d4ea 100644 --- a/src/eat.c +++ b/src/eat.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)eat.c 3.5 2005/03/09 */ +/* SCCS Id: @(#)eat.c 3.5 2005/03/28 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -15,7 +15,7 @@ STATIC_PTR int NDECL(eatmdone); STATIC_PTR int NDECL(eatfood); -STATIC_PTR void FDECL(costly_tin, (const char*)); +STATIC_PTR void FDECL(costly_tin, (int)); STATIC_PTR int NDECL(opentin); STATIC_PTR int NDECL(unfaint); @@ -293,13 +293,7 @@ register struct obj *otmp; } if (!otmp->oeaten) { - if(((!carried(otmp) && costly_spot(otmp->ox, otmp->oy) && - !otmp->no_charge) - || otmp->unpaid)) { - /* create a dummy duplicate to put on bill */ - verbalize("You bit it, you bought it!"); - bill_dummy_object(otmp); - } + costly_alteration(otmp, COST_BITE); otmp->oeaten = (otmp->otyp == CORPSE ? mons[otmp->corpsenm].cnutrit : objects[otmp->otyp].oc_nutrition); @@ -992,21 +986,19 @@ violated_vegetarian() * will split() context.tin.tin if necessary */ STATIC_PTR void -costly_tin(verb) - const char* verb; /* if 0, the verb is "open" */ +costly_tin(alter_type) +int alter_type; /* COST_xxx */ { - if(((!carried(context.tin.tin) && - costly_spot(context.tin.tin->ox, context.tin.tin->oy) && - !context.tin.tin->no_charge) - || context.tin.tin->unpaid)) { - verbalize("You %s it, you bought it!", verb ? verb : "open"); - if(context.tin.tin->quan > 1L) { - context.tin.tin = splitobj(context.tin.tin, 1L); - if (context.tin.tin) - context.tin.o_id = context.tin.tin->o_id; - } - bill_dummy_object(context.tin.tin); + struct obj *tin = context.tin.tin; + + if (carried(tin) ? tin->unpaid : + (costly_spot(tin->ox, tin->oy) && !tin->no_charge)) { + if (tin->quan > 1L) { + tin = context.tin.tin = splitobj(tin, 1L); + context.tin.o_id = tin->o_id; } + costly_alteration(tin, alter_type); + } } int @@ -1106,7 +1098,7 @@ opentin() /* called during each move whilst opening a tin */ if(context.tin.tin->otrapped || (context.tin.tin->cursed && context.tin.tin->spe != -1 && !rn2(8))) { b_trapped("tin", 0); - costly_tin("destroyed"); + costly_tin(COST_DSTROY); goto use_me; } @@ -1116,7 +1108,7 @@ opentin() /* called during each move whilst opening a tin */ if (mnum == NON_PM) { pline("It turns out to be empty."); context.tin.tin->dknown = context.tin.tin->known = TRUE; - costly_tin((const char*)0); + costly_tin(COST_OPEN); goto use_me; } r = tin_variety(context.tin.tin); @@ -1139,7 +1131,7 @@ opentin() /* called during each move whilst opening a tin */ if (!Hallucination) context.tin.tin->dknown = context.tin.tin->known = TRUE; if (flags.verbose) You("discard the open tin."); - costly_tin((const char*)0); + costly_tin(COST_OPEN); goto use_me; } /* in case stop_occupation() was called on previous meal */ @@ -1162,7 +1154,7 @@ opentin() /* called during each move whilst opening a tin */ cpostfx(mnum); /* charge for one at pre-eating cost */ - costly_tin((const char*)0); + costly_tin(COST_OPEN); /* check for vomiting added by GAN 01/16/87 */ if(tintxts[r].nut < 0) make_vomiting((long)rn1(15,10), FALSE); @@ -1186,12 +1178,12 @@ opentin() /* called during each move whilst opening a tin */ context.tin.tin->dknown = context.tin.tin->known = TRUE; if (flags.verbose) You("discard the open tin."); - costly_tin((const char*)0); + costly_tin(COST_OPEN); goto use_me; } context.tin.tin->dknown = context.tin.tin->known = TRUE; - costly_tin((const char*)0); + costly_tin(COST_OPEN); if (!context.tin.tin->cursed) pline("This makes you feel like %s!", diff --git a/src/engrave.c b/src/engrave.c index 3d0a38ddc..30cfc23a7 100644 --- a/src/engrave.c +++ b/src/engrave.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)engrave.c 3.5 2004/01/03 */ +/* SCCS Id: @(#)engrave.c 3.5 2005/03/28 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1031,13 +1031,7 @@ doengrave() * "ere", then "th". */ pline("%s dull.", Yobjnam2(otmp, "get")); - if (otmp->unpaid) { - struct monst *shkp = shop_keeper(*u.ushops); - if (shkp) { - You("damage it, you pay for it!"); - bill_dummy_object(otmp); - } - } + costly_alteration(otmp, COST_DEGRD); if (len > maxelen) { multi = -maxelen; otmp->spe = -3; diff --git a/src/mkobj.c b/src/mkobj.c index 35fd7bccb..d84ff79da 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -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 diff --git a/src/potion.c b/src/potion.c index e269bdb78..1c5310497 100644 --- a/src/potion.c +++ b/src/potion.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)potion.c 3.5 2004/12/21 */ +/* SCCS Id: @(#)potion.c 3.5 2005/03/26 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1571,20 +1571,14 @@ register struct obj *obj; } pline("%s%s.", Yobjnam2(obj,"dilute"), obj->odiluted ? " further" : ""); - if(obj->unpaid && costly_spot(u.ux, u.uy)) { - You("dilute it, you pay for it."); - bill_dummy_object(obj); - } + costly_alteration(obj, COST_DILUTE); if (obj->odiluted) { obj->odiluted = 0; -#ifdef UNIXPC obj->blessed = FALSE; obj->cursed = FALSE; -#else - obj->blessed = obj->cursed = FALSE; -#endif obj->otyp = POT_WATER; - } else obj->odiluted++; + } else + obj->odiluted++; update_inventory(); return TRUE; case SCROLL_CLASS: @@ -1593,15 +1587,10 @@ register struct obj *obj; && obj->otyp != SCR_MAIL #endif ) { - if (!Blind) { - boolean oq1 = obj->quan == 1L; - pline_The("scroll%s %s.", - oq1 ? "" : "s", otense(obj, "fade")); - } - if(obj->unpaid && costly_spot(u.ux, u.uy)) { - You("erase it, you pay for it."); - bill_dummy_object(obj); - } + if (!Blind) + pline_The("scroll%s %s.", + plur(obj->quan), otense(obj, "fade")); + costly_alteration(obj, COST_ERASE); obj->otyp = SCR_BLANK_PAPER; obj->spe = 0; update_inventory(); @@ -1611,18 +1600,15 @@ register struct obj *obj; if (obj->otyp != SPE_BLANK_PAPER) { if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { - pline("%s suddenly heats up; steam rises and it remains dry.", - The(xname(obj))); + pline( + "%s suddenly heats up; steam rises and it remains dry.", + The(xname(obj))); } else { - if (!Blind) { - boolean oq1 = obj->quan == 1L; - pline_The("spellbook%s %s.", - oq1 ? "" : "s", otense(obj, "fade")); - } - if(obj->unpaid && costly_spot(u.ux, u.uy)) { - You("erase it, you pay for it."); - bill_dummy_object(obj); - } + if (!Blind) + pline_The("spellbook%s %s.", + plur(obj->quan), + otense(obj, "fade")); + costly_alteration(obj, COST_ERASE); obj->otyp = SPE_BLANK_PAPER; update_inventory(); } @@ -1698,14 +1684,17 @@ dodip() potion->in_use = TRUE; /* assume it will be used up */ if(potion->otyp == POT_WATER) { boolean useeit = !Blind || (obj == ublindf && Blindfolded_only); + if (potion->blessed) { if (obj->cursed) { if (useeit) pline("%s %s.", Yobjnam2(obj, "softly glow"), hcolor(NH_AMBER)); + obj->bknown = 1; + if (obj->otyp == POT_WATER && obj->unpaid) + costly_alteration(obj, COST_UNHOLY); uncurse(obj); - obj->bknown=1; poof: if(!(objects[potion->otyp].oc_name_known) && !(objects[potion->otyp].oc_uname)) @@ -1719,8 +1708,10 @@ dodip() Yobjnam2(obj, "softly glow"), index(vowels, *tmp) ? "n" : "", tmp); } + obj->bknown = 1; bless(obj); - obj->bknown=1; + if (obj->otyp == POT_WATER && obj->unpaid) + alter_cost(obj, 0L); goto poof; } } else if (potion->cursed) { @@ -1729,8 +1720,10 @@ dodip() pline("%s %s.", Yobjnam2(obj, "glow"), hcolor((const char *)"brown")); + obj->bknown = 1; + if (obj->otyp == POT_WATER && obj->unpaid) + costly_alteration(obj, COST_UNBLSS); unbless(obj); - obj->bknown=1; goto poof; } else if(!obj->cursed) { if (useeit) { @@ -1739,8 +1732,10 @@ dodip() Yobjnam2(obj, "glow"), index(vowels, *tmp) ? "n" : "", tmp); } + obj->bknown = 1; curse(obj); - obj->bknown=1; + if (obj->otyp == POT_WATER && obj->unpaid) + alter_cost(obj, 0L); goto poof; } } else @@ -1902,8 +1897,8 @@ dodip() /* catch_lit does all the work if true */ } else if (obj->oerodeproof || obj_resists(obj, 5, 95) || !is_flammable(obj) || obj->oclass == FOOD_CLASS) { - pline("%s %s to burn for a moment.", - Yname2(obj), otense(obj, "seem")); + pline("%s %s to burn for a moment but %s unharmed.", + Yname2(obj), otense(obj, "seem"), otense(obj, "are")); } else { if ((omat == PLASTIC || omat == PAPER) && !obj->oartifact) obj->oeroded = MAX_ERODE; @@ -1911,18 +1906,13 @@ dodip() obj->oeroded == MAX_ERODE ? "destroys" : "damages", yname(obj), obj->oeroded == MAX_ERODE ? '!' : '.'); + costly_alteration(obj, COST_BURN); if (obj->oeroded == MAX_ERODE) { if (obj->owornmask) remove_worn_item(obj, TRUE); obj_extract_self(obj); obfree(obj, (struct obj *)0); obj = (struct obj *) 0; } else { - /* we know it's carried */ - if (obj->unpaid) { - /* create a dummy duplicate to put on bill */ - verbalize("You burnt it, you bought it!"); - bill_dummy_object(obj); - } obj->oeroded++; } } @@ -2009,11 +1999,8 @@ dodip() if (potion->quan > 1L) { singlepotion = splitobj(potion, 1L); } else singlepotion = potion; - - if(singlepotion->unpaid && costly_spot(u.ux, u.uy)) { - You("use it, you pay for it."); - bill_dummy_object(singlepotion); - } + + costly_alteration(singlepotion, COST_NUTRLZ); singlepotion->otyp = mixture; singlepotion->blessed = 0; if (mixture == POT_WATER) diff --git a/src/read.c b/src/read.c index 25e73c129..4a572e1c3 100644 --- a/src/read.c +++ b/src/read.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)read.c 3.5 2004/05/07 */ +/* SCCS Id: @(#)read.c 3.5 2005/03/28 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -150,14 +150,15 @@ STATIC_OVL void stripspe(obj) register struct obj *obj; { - if (obj->blessed) pline(nothing_happens); - else { - if (obj->spe > 0) { - obj->spe = 0; - if (obj->otyp == OIL_LAMP || obj->otyp == BRASS_LANTERN) - obj->age = 0; - pline("%s briefly.", Yobjnam2(obj, "vibrate")); - } else pline(nothing_happens); + if (obj->blessed || obj->spe <= 0) { + pline(nothing_happens); + } else { + /* order matters: message, shop handling, actual transformation */ + pline("%s briefly.", Yobjnam2(obj, "vibrate")); + costly_alteration(obj, COST_UNCHRG); + obj->spe = 0; + if (obj->otyp == OIL_LAMP || obj->otyp == BRASS_LANTERN) + obj->age = 0; } } @@ -257,6 +258,10 @@ int curse_bless; } if (obj->spe >= lim) p_glow2(obj, NH_BLUE); else p_glow1(obj); +#if 0 /*[shop price doesn't vary by charge count]*/ + /* update shop bill to reflect new higher price */ + if (obj->unpaid) alter_cost(obj, 0L); +#endif } } else if (obj->oclass == RING_CLASS && @@ -278,12 +283,15 @@ int curse_bless; RIGHT_RING) : 0L; pline("%s spins %sclockwise for a moment.", Yname2(obj), s < 0 ? "counter" : ""); + if (s < 0) costly_alteration(obj, COST_DECHNT); /* cause attributes and/or properties to be updated */ if (is_on) Ring_off(obj); obj->spe += s; /* update the ring while it's off */ if (is_on) setworn(obj, mask), Ring_on(obj); /* oartifact: if a touch-sensitive artifact ring is ever created the above will need to be revised */ + /* update shop bill to reflect new higher price */ + if (s > 0 && obj->unpaid) alter_cost(obj, 0L); } } else if (obj->oclass == TOOL_CLASS) { @@ -637,11 +645,12 @@ struct obj *sobj; int seffects(sobj) -register struct obj *sobj; +struct obj *sobj; { - register int cval; - register boolean confused = (Confusion != 0); - register struct obj *otmp; + int cval; + boolean confused = (Confusion != 0), + old_erodeproof, new_erodeproof; + struct obj *otmp; if (objects[sobj->otyp].oc_magic) exercise(A_WIS, TRUE); /* just for trying */ @@ -650,7 +659,8 @@ register struct obj *sobj; case SCR_MAIL: known = TRUE; if (sobj->spe) - pline("This seems to be junk mail addressed to the finder of the Eye of Larn."); + pline( + "This seems to be junk mail addressed to the finder of the Eye of Larn."); /* note to the puzzled: the game Larn actually sends you junk * mail if you win! */ @@ -673,7 +683,9 @@ register struct obj *sobj; return(1); } if(confused) { - otmp->oerodeproof = !(sobj->cursed); + old_erodeproof = (otmp->oerodeproof != 0); + new_erodeproof = !sobj->cursed; + otmp->oerodeproof = 0; /* for messages */ if(Blind) { otmp->rknown = FALSE; pline("%s warm for a moment.", @@ -683,16 +695,22 @@ register struct obj *sobj; pline("%s covered by a %s %s %s!", Yobjnam2(otmp, "are"), sobj->cursed ? "mottled" : "shimmering", - hcolor(sobj->cursed ? NH_BLACK : NH_GOLDEN), + hcolor(sobj->cursed ? NH_BLACK : NH_GOLDEN), sobj->cursed ? "glow" : (is_shield(otmp) ? "layer" : "shield")); } - if (otmp->oerodeproof && + if (new_erodeproof && (otmp->oeroded || otmp->oeroded2)) { otmp->oeroded = otmp->oeroded2 = 0; pline("%s as good as new!", Yobjnam2(otmp, Blind ? "feel" : "look")); } + if (old_erodeproof && !new_erodeproof) { + /* restore old_erodeproof before shop charges */ + otmp->oerodeproof = 1; + costly_alteration(otmp, COST_DEGRD); + } + otmp->oerodeproof = new_erodeproof ? 1 : 0; break; } /* elven armor vibrates warningly when enchanted beyond a limit */ @@ -739,29 +757,35 @@ register struct obj *sobj; /* assumes same order */ otmp->otyp = GRAY_DRAGON_SCALE_MAIL + otmp->otyp - GRAY_DRAGON_SCALES; - otmp->cursed = 0; if (sobj->blessed) { - otmp->spe++; - otmp->blessed = 1; - } + otmp->spe++; + if (!otmp->blessed) bless(otmp); + } else if (otmp->cursed) + uncurse(otmp); otmp->known = 1; setworn(otmp, W_ARM); + if (otmp->unpaid) alter_cost(otmp, 0L); /* shop bill */ break; } pline("%s %s%s%s%s for a %s.", - Yname2(otmp), - s == 0 ? "violently " : nul, - otense(otmp, Blind ? "vibrate" : "glow"), - (!Blind && !same_color) ? " " : nul, - (Blind || same_color) ? nul : hcolor(sobj->cursed ? NH_BLACK : NH_SILVER), - (s*s>1) ? "while" : "moment"); - otmp->cursed = sobj->cursed; - if (!otmp->blessed || sobj->cursed) - otmp->blessed = sobj->blessed; + Yname2(otmp), + s == 0 ? "violently " : nul, + otense(otmp, Blind ? "vibrate" : "glow"), + (!Blind && !same_color) ? " " : nul, + (Blind || same_color) ? nul : + hcolor(sobj->cursed ? NH_BLACK : NH_SILVER), + (s * s > 1) ? "while" : "moment"); + /* [this cost handling will need updating if shop pricing is + ever changed to care about curse/bless status of armor] */ + if (s < 0) costly_alteration(otmp, COST_DECHNT); + if (sobj->cursed && !otmp->cursed) curse(otmp); + else if (sobj->blessed && !otmp->blessed) bless(otmp); if (s) { otmp->spe += s; adj_abon(otmp, s); known = otmp->known; + /* update shop bill to reflect new higher price */ + if (s > 0 && otmp->unpaid) alter_cost(otmp, 0L); } if ((otmp->spe > (special_armor ? 5 : 3)) && @@ -781,8 +805,16 @@ register struct obj *sobj; exercise(A_CON, FALSE); return(1); } - otmp->oerodeproof = sobj->cursed; + old_erodeproof = (otmp->oerodeproof != 0); + new_erodeproof = (sobj->cursed != 0); + otmp->oerodeproof = 0; /* for messages */ p_glow2(otmp, NH_PURPLE); + if (old_erodeproof && !new_erodeproof) { + /* restore old_erodeproof before shop charges */ + otmp->oerodeproof = 1; + costly_alteration(otmp, COST_DEGRD); + } + otmp->oerodeproof = new_erodeproof ? 1 : 0; break; } if(!sobj->cursed || !otmp || !otmp->cursed) { @@ -850,7 +882,8 @@ register struct obj *sobj; break; case SCR_SCARE_MONSTER: case SPE_CAUSE_FEAR: - { register int ct = 0; + { + register int ct = 0; register struct monst *mtmp; for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { @@ -884,17 +917,16 @@ register struct obj *sobj; break; case SCR_REMOVE_CURSE: case SPE_REMOVE_CURSE: - { register struct obj *obj; - if(confused) - if (Hallucination) - You_feel("the power of the Force against you!"); - else - You_feel("like you need some help."); - else - if (Hallucination) - You_feel("in touch with the Universal Oneness."); - else - You_feel("like someone is helping you."); + { + register struct obj *obj; + + You_feel(!Hallucination ? + (!confused ? + "like someone is helping you." : + "like you need some help.") : + (!confused ? + "in touch with the Universal Oneness." : + "the power of the Force against you!")); if (sobj->cursed) { pline_The("scroll disintegrates."); @@ -932,8 +964,39 @@ register struct obj *sobj; if (sobj->blessed || wornmask || obj->otyp == LOADSTONE || (obj->otyp == LEASH && obj->leashmon)) { + unsigned save_bknown, save_cursed, save_blessed; + boolean was_cursed = !!obj->cursed, + was_blessed = !!obj->blessed, + was_normal = !(was_cursed || was_blessed); + if(confused) blessorcurse(obj, 2); else uncurse(obj); + /* water price varies by curse/bless status */ + if (obj->unpaid && obj->otyp == POT_WATER) { + if ((was_cursed && !obj->cursed) || + (was_blessed && !obj->blessed)) { + /* make `Ix' more specific for this item */ + save_bknown = obj->bknown; + obj->bknown = 1; + /* temporarily restore curse/bless to + obtain the right shop price (if potion + went from cursed directly to blessed + or vice versa its price didn't change + but hero will have to buy it anyway) */ + save_cursed = obj->cursed; + obj->cursed = was_cursed ? 1 : 0; + save_blessed = obj->blessed; + obj->blessed = was_blessed ? 1 : 0; + costly_alteration(obj, was_cursed ? + COST_UNHOLY : COST_UNBLSS); + obj->bknown = save_bknown; + obj->cursed = save_cursed; + obj->blessed = save_blessed; + } else if (was_normal && + (obj->blessed || obj->cursed)) { + alter_cost(obj, 0L); + } + } /* unpaid water */ } } } @@ -957,10 +1020,11 @@ register struct obj *sobj; */ break; case SCR_ENCHANT_WEAPON: - if(uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep)) - && confused) { - /* oclass check added 10/25/86 GAN */ - uwep->oerodeproof = !(sobj->cursed); + if (confused && uwep && + (uwep->oclass == WEAPON_CLASS || is_weptool(uwep))) { + old_erodeproof = (uwep->oerodeproof != 0); + new_erodeproof = !sobj->cursed; + uwep->oerodeproof = 0; /* for messages */ if (Blind) { uwep->rknown = FALSE; Your("weapon feels warm for a moment."); @@ -977,12 +1041,19 @@ register struct obj *sobj; pline("%s as good as new!", Yobjnam2(uwep, Blind ? "feel" : "look")); } - } else return !chwepon(sobj, - sobj->cursed ? -1 : - !uwep ? 1 : - uwep->spe >= 9 ? (rn2(uwep->spe) == 0) : - sobj->blessed ? rnd(3-uwep->spe/3) : 1); - break; + if (old_erodeproof && !new_erodeproof) { + /* restore old_erodeproof before shop charges */ + uwep->oerodeproof = 1; + costly_alteration(uwep, COST_DEGRD); + } + uwep->oerodeproof = new_erodeproof ? 1 : 0; + break; + } + return !chwepon(sobj, + sobj->cursed ? -1 : + !uwep ? 1 : + uwep->spe >= 9 ? (rn2(uwep->spe) == 0) : + sobj->blessed ? rnd(3 - uwep->spe / 3) : 1); case SCR_TAMING: case SPE_CHARM_MONSTER: if (u.uswallow) { @@ -1280,8 +1351,9 @@ register struct obj *sobj; } punish(sobj); break; - case SCR_STINKING_CLOUD: { - coord cc; + case SCR_STINKING_CLOUD: + { + coord cc; You("have found a scroll of stinking cloud!"); known = TRUE; diff --git a/src/shk.c b/src/shk.c index ec04143fc..11cb465a7 100644 --- a/src/shk.c +++ b/src/shk.c @@ -2005,6 +2005,31 @@ register struct monst *shkp; return tmp; } +/* called when an item's value has been enhanced; if it happens to be + on any shop bill, update that bill to reflect the new higher price + [if the new price drops for some reason, keep the old one in place] */ +void +alter_cost(obj, amt) +struct obj *obj; +long amt; /* if 0, use regular shop pricing, otherwise force amount; + if negative, use abs(amt) even if it's less than old cost */ +{ + struct bill_x *bp = 0; + struct monst *shkp; + long new_price; + + for (shkp = next_shkp(fmon, TRUE); shkp; shkp = next_shkp(shkp, TRUE)) + if ((bp = onbill(obj, shkp, TRUE)) != 0) { + new_price = !amt ? get_cost(obj, shkp) : (amt < 0L) ? -amt : amt; + if (new_price > bp->price || amt < 0L) { + bp->price = new_price; + update_inventory(); + } + break; /* done */ + } + return; +} + /* called from doinv(invent.c) for inventory of unpaid objects */ long unpaid_cost(unp_obj) diff --git a/src/wield.c b/src/wield.c index 45fa37145..66a1b0d69 100644 --- a/src/wield.c +++ b/src/wield.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)wield.c 3.5 2003/01/29 */ +/* SCCS Id: @(#)wield.c 3.5 2005/03/28 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -694,21 +694,24 @@ register int amount; if (otmp && otmp->oclass == SCROLL_CLASS) otyp = otmp->otyp; - if(uwep->otyp == WORM_TOOTH && amount >= 0) { + if (uwep->otyp == WORM_TOOTH && amount >= 0) { + /* order: message, transformation, shop handling */ + Your("%s is much sharper now.", simple_typename(WORM_TOOTH)); uwep->otyp = CRYSKNIFE; uwep->oerodeproof = 0; - Your("weapon seems sharper now."); - uwep->cursed = 0; + if (uwep->cursed) uncurse(uwep); + /* update shop bill to reflect new higher value */ + if (uwep->unpaid) alter_cost(uwep, 0L); if (otyp != STRANGE_OBJECT) makeknown(otyp); - return(1); - } - - if(uwep->otyp == CRYSKNIFE && amount < 0) { + return 1; + } else if (uwep->otyp == CRYSKNIFE && amount < 0) { + /* order matters: message, shop handling, transformation */ + Your("%s is much duller now.", simple_typename(CRYSKNIFE)); + costly_alteration(uwep, COST_DEGRD); /* DECHNT? other? */ uwep->otyp = WORM_TOOTH; uwep->oerodeproof = 0; - Your("weapon seems duller now."); if (otyp != STRANGE_OBJECT && otmp->bknown) makeknown(otyp); - return(1); + return 1; } if (amount < 0 && uwep->oartifact && restrict_name(uwep, ONAME(uwep))) { @@ -738,8 +741,13 @@ register int amount; (amount > 0 || (amount < 0 && otmp->bknown))) makeknown(otyp); } + if (amount < 0) costly_alteration(uwep, COST_DECHNT); uwep->spe += amount; - if(amount > 0) uwep->cursed = 0; + if (amount > 0) { + if (uwep->cursed) uncurse(uwep); + /* update shop bill to reflect new higher price */ + if (uwep->unpaid) alter_cost(uwep, 0L); + } /* * Enchantment, which normally improves a weapon, has an diff --git a/src/zap.c b/src/zap.c index 9b64da537..61b9e49b6 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)zap.c 3.5 2005/03/18 */ +/* SCCS Id: @(#)zap.c 3.5 2005/03/28 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -19,7 +19,6 @@ extern boolean notonhead; /* for long worms */ /* kludge to use mondied instead of killed */ extern boolean m_using; -STATIC_DCL void FDECL(costly_cancel, (struct obj *)); STATIC_DCL void FDECL(polyuse, (struct obj*, int, int)); STATIC_DCL void FDECL(create_polymon, (struct obj *, int)); STATIC_DCL boolean FDECL(zap_updown, (struct obj *)); @@ -802,48 +801,15 @@ struct monst *mon; return res; } -static const char charged_objs[] = { WAND_CLASS, WEAPON_CLASS, ARMOR_CLASS, 0 }; - -STATIC_OVL void -costly_cancel(obj) -register struct obj *obj; -{ - char objroom; - struct monst *shkp = (struct monst *)0; - - if (obj->no_charge) return; - - switch (obj->where) { - case OBJ_INVENT: - if (obj->unpaid) { - shkp = shop_keeper(*u.ushops); - if (!shkp) return; - Norep("You cancel an unpaid object, you pay for it!"); - bill_dummy_object(obj); - } - break; - case OBJ_FLOOR: - objroom = *in_rooms(obj->ox, obj->oy, SHOPBASE); - shkp = shop_keeper(objroom); - if (!shkp || !inhishop(shkp)) return; - if (costly_spot(u.ux, u.uy) && objroom == *u.ushops) { - Norep("You cancel it, you pay for it!"); - bill_dummy_object(obj); - } else - (void) stolen_value(obj, obj->ox, obj->oy, FALSE, FALSE); - break; - } -} - /* cancel obj, possibly carried by you or a monster */ void cancel_item(obj) register struct obj *obj; { - boolean u_ring = (obj == uleft) || (obj == uright); - register boolean holy = (obj->otyp == POT_WATER && obj->blessed); + boolean u_ring = (obj == uleft || obj == uright); + int otyp = obj->otyp; - switch(obj->otyp) { + switch (otyp) { case RIN_GAIN_STRENGTH: if ((obj->owornmask & W_RING) && u_ring) { ABON(A_STR) -= obj->spe; @@ -885,51 +851,50 @@ register struct obj *obj; break; /* case RIN_PROTECTION: not needed */ } - if (objects[obj->otyp].oc_magic - || (obj->spe && (obj->oclass == ARMOR_CLASS || - obj->oclass == WEAPON_CLASS || is_weptool(obj))) - || obj->otyp == POT_ACID || obj->otyp == POT_SICKNESS) { + if (objects[otyp].oc_magic || + (obj->spe && (obj->oclass == ARMOR_CLASS || + obj->oclass == WEAPON_CLASS || is_weptool(obj))) || + otyp == POT_ACID || otyp == POT_SICKNESS || + (otyp == WATER && (obj->blessed || obj->cursed))) { if (obj->spe != ((obj->oclass == WAND_CLASS) ? -1 : 0) && - obj->otyp != WAN_CANCELLATION && - /* can't cancel cancellation */ - obj->otyp != MAGIC_LAMP && - obj->otyp != CANDELABRUM_OF_INVOCATION) { - costly_cancel(obj); + otyp != WAN_CANCELLATION && /* can't cancel cancellation */ + otyp != MAGIC_LAMP && /* cancelling doesn't remove djini */ + otyp != CANDELABRUM_OF_INVOCATION) { + costly_alteration(obj, COST_CANCEL); obj->spe = (obj->oclass == WAND_CLASS) ? -1 : 0; } switch (obj->oclass) { case SCROLL_CLASS: - costly_cancel(obj); + costly_alteration(obj, COST_CANCEL); obj->otyp = SCR_BLANK_PAPER; obj->spe = 0; break; case SPBOOK_CLASS: - if (obj->otyp != SPE_CANCELLATION && - obj->otyp != SPE_BOOK_OF_THE_DEAD) { - costly_cancel(obj); + if (otyp != SPE_CANCELLATION && otyp != SPE_BOOK_OF_THE_DEAD) { + costly_alteration(obj, COST_CANCEL); obj->otyp = SPE_BLANK_PAPER; } break; case POTION_CLASS: - costly_cancel(obj); - if (obj->otyp == POT_SICKNESS || - obj->otyp == POT_SEE_INVISIBLE) { - /* sickness is "biologically contaminated" fruit juice; cancel it - * and it just becomes fruit juice... whereas see invisible - * tastes like "enchanted" fruit juice, it similarly cancels. - */ + costly_alteration(obj, (otyp == WATER && obj->cursed) ? + COST_UNHOLY : COST_CANCEL); + if (otyp == POT_SICKNESS || otyp == POT_SEE_INVISIBLE) { + /* sickness is "biologically contaminated" fruit juice; + cancel it and it just becomes fruit juice... + whereas see invisible tastes like "enchanted" fruit + juice, it similarly cancels */ obj->otyp = POT_FRUIT_JUICE; } else { - obj->otyp = POT_WATER; + obj->otyp = POT_WATER; obj->odiluted = 0; /* same as any other water */ } break; } } - if (holy) costly_cancel(obj); unbless(obj); uncurse(obj); #ifdef INVISIBLE_OBJECTS + /*[this will be insufficient if it ever reduces obj's shop value]*/ if (obj->oinvis) obj->oinvis = 0; #endif return; @@ -954,7 +919,7 @@ register struct obj *obj; return (FALSE); /* Charge for the cost of the object */ - costly_cancel(obj); /* The term "cancel" is okay for now */ + costly_alteration(obj, COST_DRAIN); /* Drain the object and any implied effects */ obj->spe--; @@ -1213,6 +1178,11 @@ struct obj *obj; delobj(obj); } +/* classes of items whose current charge count carries over across polymorph */ +static const char charged_objs[] = { + WAND_CLASS, WEAPON_CLASS, ARMOR_CLASS, '\0' +}; + /* * Polymorph the object to the given object ID. If the ID is STRANGE_OBJECT * then pick random object from the source's class (this is the standard