diff --git a/doc/fixes36.3 b/doc/fixes36.3 index c17abf893..77aac6895 100644 --- a/doc/fixes36.3 +++ b/doc/fixes36.3 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.1 $ $NHDT-Date: 1557569075 2019/05/11 10:04:35 $ +$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.3 $ $NHDT-Date: 1558045586 2019/05/16 22:26:26 $ This fixes36.3 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.2 in May 2019. Please note, however, @@ -11,6 +11,10 @@ when place_object() puts a non-boulder on the map at a spot which contains other objects, put it under all boulders in the pile rather than just under the top one; previously, map wasn't showing a boulder there if the top one got moved by means other than pushing +when examining the map with '/' or ';', picking a symbol which is used for + more than 4 things yielded a sentence lacking its terminating period +billing and payment issue as a result of glob coalescing +glob pricing did not consider weight properly Fixes to Post-3.6.2 Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index e4e289dbb..9366817a8 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2265,6 +2265,8 @@ E boolean FDECL(block_door, (XCHAR_P, XCHAR_P)); E boolean FDECL(block_entry, (XCHAR_P, XCHAR_P)); E char *FDECL(shk_your, (char *, struct obj *)); E char *FDECL(Shk_Your, (char *, struct obj *)); +E void FDECL(globby_bill_fixup, (struct obj *, struct obj *)); +E void FDECL(globby_donation, (struct obj *, struct obj *)); /* ### shknam.c ### */ diff --git a/src/invent.c b/src/invent.c index 61cfd5d0f..4a65624ac 100644 --- a/src/invent.c +++ b/src/invent.c @@ -715,7 +715,8 @@ struct obj **potmp, **pobj; otmp->age = ((otmp->age * otmp->quan) + (obj->age * obj->quan)) / (otmp->quan + obj->quan); - otmp->quan += obj->quan; + if (!otmp->globby) + otmp->quan += obj->quan; /* temporary special case for gold objects!!!! */ if (otmp->oclass == COIN_CLASS) otmp->owt = weight(otmp), otmp->bknown = 0; diff --git a/src/mkobj.c b/src/mkobj.c index 1593781ce..9d45b9e42 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -2755,6 +2755,10 @@ struct obj **obj1, **obj2; otmp1 = *obj1; otmp2 = *obj2; if (otmp1 && otmp2 && otmp1 != otmp2) { + if (otmp1->unpaid || otmp2->unpaid) + globby_bill_fixup(otmp1, otmp2); + else if (costly_spot(otmp1->ox, otmp1->oy)) + globby_donation(otmp1, otmp2); if (otmp1->bknown != otmp2->bknown) otmp1->bknown = otmp2->bknown = 0; if (otmp1->rknown != otmp2->rknown) @@ -2837,7 +2841,12 @@ struct obj *otmp2; * they'll be out of our view (minvent or container) * so don't actually show anything */ } else if (onfloor || inpack) { - pline("The %s coalesce%s.", makeplural(obj_typename(otmp->otyp)), + boolean adj = ((otmp->ox != u.ux || otmp->oy != u.uy) && + (otmp2->ox != u.ux || otmp2->oy != u.uy)); + + pline("The %s%s coalesce%s.", + (onfloor && adj) ? "adjacent " : "", + makeplural(obj_typename(otmp->otyp)), inpack ? " inside your pack" : ""); } } else { diff --git a/src/objnam.c b/src/objnam.c index ea28a3289..4a5c4d28d 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -25,6 +25,7 @@ STATIC_DCL boolean FDECL(singplur_lookup, (char *, char *, BOOLEAN_P, STATIC_DCL char *FDECL(singplur_compound, (char *)); STATIC_DCL char *FDECL(xname_flags, (struct obj *, unsigned)); STATIC_DCL boolean FDECL(badman, (const char *, BOOLEAN_P)); +STATIC_DCL char *FDECL(globwt, (struct obj *, char *)); struct Jitem { int item; @@ -1214,11 +1215,12 @@ unsigned doname_flags; } else if (with_price) { /* on floor or in container on floor */ int nochrg = 0; long price = get_cost_of_shop_item(obj, &nochrg); + char globbuf[BUFSZ]; if (price > 0L) - Sprintf(eos(bp), " (%s, %ld %s)", + Sprintf(eos(bp), " (%s, %s%ld %s)", nochrg ? "contents" : "for sale", - price, currency(price)); + globwt(obj, globbuf), price, currency(price)); else if (nochrg > 0) Strcat(bp, " (no charge)"); } @@ -4195,4 +4197,16 @@ const char *lastR; return qbuf; } +STATIC_OVL char * +globwt(otmp, buf) +struct obj *otmp; +char *buf; +{ + if (otmp && buf && otmp->globby && otmp->quan == 1L) { + Sprintf(buf, "%ld aum, ", otmp->owt); + return buf; + } + return ""; +} + /*objnam.c*/ diff --git a/src/pager.c b/src/pager.c index e0058371b..08a839620 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pager.c $NHDT-Date: 1555627307 2019/04/18 22:41:47 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.151 $ */ +/* NetHack 3.6 pager.c $NHDT-Date: 1558045586 2019/05/16 22:26:26 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.153 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1042,7 +1042,10 @@ struct permonst **for_supplement; */ if (found > 4) - Sprintf(out_str, "%s", "That can be many things"); + /* 3.6.3: this used to be "That can be many things" (without prefix) + which turned it into a sentence that lacked its terminating period; + we could add one below but reinstating the prefix here is better */ + Sprintf(out_str, "%scan be many things", prefix); didlook: if (looked) { diff --git a/src/shk.c b/src/shk.c index 40c4367fc..dfbbaacbf 100644 --- a/src/shk.c +++ b/src/shk.c @@ -74,6 +74,7 @@ STATIC_DCL void FDECL(deserted_shop, (char *)); STATIC_DCL boolean FDECL(special_stock, (struct obj *, struct monst *, BOOLEAN_P)); STATIC_DCL const char *FDECL(cad, (BOOLEAN_P)); +STATIC_DCL long FDECL(get_pricing_units, (struct obj *obj)); /* invariants: obj->unpaid iff onbill(obj) [unless bp->useup] @@ -2009,14 +2010,34 @@ int *nochrg; /* alternate return value: 1: no charge, 0: shop owned, */ items on freespot are implicitly 'no charge' */ *nochrg = (top->where == OBJ_FLOOR && (obj->no_charge || freespot)); - if (carried(top) ? (int) obj->unpaid : !*nochrg) - cost = obj->quan * get_cost(obj, shkp); + if (carried(top) ? (int) obj->unpaid : !*nochrg) { + long per_unit_cost = get_cost(obj, shkp); + + cost = get_pricing_units(obj) * per_unit_cost; + } if (Has_contents(obj) && !freespot) cost += contained_cost(obj, shkp, 0L, FALSE, TRUE); } return cost; } +STATIC_OVL long +get_pricing_units(obj) +struct obj *obj; +{ + long units = obj->quan; + + if (obj->globby) { + /* globs must be sold by weight not by volume */ + int unit_weight = (int) objects[obj->otyp].oc_weight, + wt = (obj->owt > 0) ? obj->owt : weight(obj); + + if (unit_weight) + units = wt / unit_weight; + } + return units; +} + /* decide whether to apply a surcharge (or hypothetically, a discount) to obj if it had ID number 'oid'; returns 1: increase, 0: normal, -1: decrease */ int @@ -2185,7 +2206,7 @@ boolean unpaid_only; items on freespot are implicitly 'no charge' */ if (on_floor ? (!otmp->no_charge && !freespot) : (otmp->unpaid || !unpaid_only)) - price += get_cost(otmp, shkp) * otmp->quan; + price += get_cost(otmp, shkp) * get_pricing_units(otmp); } if (Has_contents(otmp)) @@ -2298,7 +2319,9 @@ set_cost(obj, shkp) register struct obj *obj; register struct monst *shkp; { - long tmp = getprice(obj, TRUE) * obj->quan, multiplier = 1L, divisor = 1L; + long tmp, unit_price = getprice(obj, TRUE), multiplier = 1L, divisor = 1L; + + tmp = get_pricing_units(obj) * unit_price; if (uarmh && uarmh->otyp == DUNCE_CAP) divisor *= 3L; @@ -2474,6 +2497,9 @@ struct monst *shkp; } else bp->useup = 0; bp->price = get_cost(obj, shkp); + if (obj->globby) + /* for globs, the amt charged for quan 1 depends on owt */ + bp->price *= get_pricing_units(obj); eshkp->billct++; obj->unpaid = 1; } @@ -2615,9 +2641,11 @@ boolean ininv, dummy, silent; ltmp = cltmp = gltmp = 0L; container = Has_contents(obj); - if (!obj->no_charge) + if (!obj->no_charge) { ltmp = get_cost(obj, shkp); - + if (obj->globby) + ltmp *= get_pricing_units(obj); + } if (obj->no_charge && !container) { obj->no_charge = 0; return; @@ -2845,7 +2873,7 @@ boolean ininv; if (billamt) price += billamt; else if (ininv ? otmp->unpaid : !otmp->no_charge) - price += otmp->quan * get_cost(otmp, shkp); + price += get_pricing_units(otmp) * get_cost(otmp, shkp); if (Has_contents(otmp)) price = stolen_container(otmp, shkp, price, ininv); @@ -2892,7 +2920,7 @@ boolean peaceful, silent; if (billamt) value += billamt; else if (!obj->no_charge) - value += obj->quan * get_cost(obj, shkp); + value += get_pricing_units(obj) * get_cost(obj, shkp); if (Has_contents(obj)) { boolean ininv = @@ -4264,6 +4292,8 @@ register struct obj *first_obj; contentsonly = !cost; if (Has_contents(otmp)) cost += contained_cost(otmp, shkp, 0L, FALSE, FALSE); + if (otmp->globby) + cost *= get_pricing_units(otmp); /* always quan 1, vary by wt */ if (!cost) { Strcpy(price, "no charge"); contentsonly = FALSE; @@ -4757,4 +4787,121 @@ sasc_bug(struct obj *op, unsigned x) } #endif +/* + * When one glob is absorbed by another glob, the two become + * become indistinguishable and the remaining glob grows in + * mass, the product of both. + * + * The original billed item is lost to the absorption and the + * original billed amount for the object being absorbed gets + * added to the cost owing for the absorber, and the separate + * cost for the absorbed object goes away. + */ +void +globby_bill_fixup(obj_absorber, obj_absorbed) +struct obj *obj_absorber, *obj_absorbed; +{ + struct bill_x *bp, *bp_absorber = (struct bill_x *) 0; + struct monst *shkp = 0; + + if (!obj_absorber->globby) + impossible("globby_bill_fixup called for non-globby object"); + + if (obj_absorber->unpaid) { + /* look for a shopkeeper who owns this object */ + for (shkp = next_shkp(fmon, TRUE); shkp; + shkp = next_shkp(shkp->nmon, TRUE)) + if (onbill(obj_absorber, shkp, TRUE)) + break; + } + /* sanity check, in case obj is on bill but not marked 'unpaid' */ + if (!shkp) + shkp = shop_keeper(*u.ushops); + + if ((bp_absorber = onbill(obj_absorber, shkp, FALSE)) != 0) { + bp = onbill(obj_absorbed, shkp, FALSE); + if (bp) { + bp_absorber->price += bp->price; + ESHK(shkp)->billct--; +#ifdef DUMB + { + /* DRS/NS 2.2.6 messes up -- Peter Kendell */ + int indx = ESHK(shkp)->billct; + + *bp = ESHK(shkp)->bill_p[indx]; + } +#else + *bp = ESHK(shkp)->bill_p[ESHK(shkp)->billct]; +#endif + clear_unpaid_obj(shkp, obj_absorbed); + } else { + /* should never happen */ + bp_absorber->price += get_cost(obj_absorbed, shkp) + * get_pricing_units(obj_absorbed); + } + } +} + +/* + * The caller is about to make obj_absorbed go away. + * + * There's no way for you (or a shopkeeper) to prevent globs + * from merging with each other on the floor due to the + * inherent nature of globs so it irretrievably becomes part + * of the floor glob mass. + * + * globby_donation() needs to handle whether/how to + * compensate you for that. + * + * unpaid globs don't end up here. + */ +void +globby_donation(obj_absorber, obj_absorbed) +struct obj *obj_absorber, *obj_absorbed; +{ + if (obj_absorber->where == OBJ_FLOOR + && costly_spot(obj_absorber->ox, obj_absorber->oy)) { + int x = obj_absorber->ox, y = obj_absorber->oy; + char *o_shop = in_rooms(x, y, SHOPBASE); + struct monst *shkp = shop_keeper(*in_rooms(x, y, SHOPBASE)); + struct eshk *eshkp = ESHK(shkp); + long amount, per_unit_cost = get_cost(obj_absorbed, shkp); + + if (saleable(shkp, obj_absorbed)) { + amount = get_pricing_units(obj_absorbed) * per_unit_cost; + if (eshkp->debit >= amount) { + if (eshkp->loan) { /* you carry shop's gold */ + if (eshkp->loan >= amount) + eshkp->loan -= amount; + else + eshkp->loan = 0L; + } + eshkp->debit -= amount; + pline_The("donated %s %spays off your debt.", + obj_typename(obj_absorbed->otyp), + eshkp->debit ? "partially " : ""); + } else { + long delta = amount - eshkp->debit; + + eshkp->credit += delta; + if (eshkp->debit) { + eshkp->debit = 0L; + eshkp->loan = 0L; + Your("debt is paid off."); + } + if (eshkp->credit == delta) + pline_The("%s established %ld %s credit.", + obj_typename(obj_absorbed->otyp), + delta, currency(delta)); + else + pline_The("%s added %ld %s %s %ld %s.", + obj_typename(obj_absorbed->otyp), + delta, currency(delta), + "to your credit; total is now", + eshkp->credit, currency(eshkp->credit)); + } + } + } +} + /*shk.c*/