diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 4ee9ace1f..e84f9f305 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1058,6 +1058,8 @@ when hero hears an unseen monster reading a scroll, only describe the monster acccurately if hero is not hallucinating and monster is same species as hero's current form don't allow monsters to disarm hero with bullwhip if hero is engulfed +teleporting an object out of a shop put it on the shop bill instead of dealing + with robbery Fixes to 3.7.0-x Problems that Were Exposed Via git Repository @@ -1883,6 +1885,8 @@ very large humanoids can wear mummy wrappings for ranger characters, shooting any type of arrow while wielding the Longbow of Diana gets an extra +1 to multi-shot change the #vanquished command from debug-only to general user command +have 'I u' mention whether there are any unpaid items on the floor (unusual + but not impossible); it doesn't itemize them or show shop price Platform- and/or Interface-Specific New Features diff --git a/include/obj.h b/include/obj.h index 08c645e59..866e4e767 100644 --- a/include/obj.h +++ b/include/obj.h @@ -87,8 +87,10 @@ struct obj { Bitfield(cursed, 1); /* uncursed when neither cursed nor blessed */ Bitfield(blessed, 1); Bitfield(unpaid, 1); /* owned by shop; valid for objects in hero's - * inventory or inside containers there; - * not used for items on the floor */ + * inventory or inside containers there; also, + * used for items on the floor only on the shop + * boundary (including "free spot") or if moved + * from there to inside by wall repairs */ Bitfield(no_charge, 1); /* if shk shouldn't charge for this; valid for * items on shop floor or in containers there; * implicit for items at any other location diff --git a/src/dokick.c b/src/dokick.c index 0e05f9606..980906e91 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -395,8 +395,10 @@ ghitm(register struct monst *mtmp, register struct obj *gold) /* container is kicked, dropped, thrown or otherwise impacted by player. * Assumes container is on floor. Checks contents for possible damage. */ void -container_impact_dmg(struct obj *obj, coordxy x, - coordxy y) /* coordinates where object was before the impact, not after */ +container_impact_dmg( + struct obj *obj, + coordxy x, /* coordinates where object was */ + coordxy y) /* before the impact, not after */ { struct monst *shkp; struct obj *otmp, *otmp2; @@ -504,7 +506,7 @@ really_kick_object(coordxy x, coordxy y) return 1; } if (trap->ttyp == STATUE_TRAP) { - activate_statue_trap(trap, x,y, FALSE); + activate_statue_trap(trap, x, y, FALSE); return 1; } } diff --git a/src/invent.c b/src/invent.c index 546c63d81..32cafadd0 100644 --- a/src/invent.c +++ b/src/invent.c @@ -30,7 +30,7 @@ static char display_pickinv(const char *, const char *, const char *, boolean, long *); static char display_used_invlets(char); static boolean this_type_only(struct obj *); -static void dounpaid(void); +static void dounpaid(int); static struct obj *find_unpaid(struct obj *, struct obj **); static void menu_identify(int); static boolean tool_being_used(struct obj *); @@ -3732,7 +3732,7 @@ count_contents( } static void -dounpaid(void) +dounpaid(int floorcount) { winid win; struct obj *otmp, *marker, *contnr; @@ -3744,7 +3744,7 @@ dounpaid(void) count = count_unpaid(g.invent); otmp = marker = contnr = (struct obj *) 0; - if (count == 1) { + if (count == 1 && !floorcount) { otmp = find_unpaid(g.invent, &marker); contnr = unknwn_contnr_contents(otmp); } @@ -3825,10 +3825,30 @@ dounpaid(void) } } - putstr(win, 0, ""); - putstr(win, 0, - xprname((struct obj *) 0, "Total:", '*', FALSE, totcost, 0L)); - display_nhwindow(win, FALSE); + if (count > 0) { + putstr(win, 0, ""); + putstr(win, 0, + xprname((struct obj *) 0, "Total:", '*', FALSE, totcost, 0L)); + } + + if (floorcount > 0) { + char buf[BUFSZ]; + const char *floorverb = (floorcount > 1) ? "are" : "is"; + + if (!count) { + You( + "aren't carrying any unpaid items but there %s %d on the floor.", + floorverb, floorcount); + } else { + putstr(win, 0, ""); + Sprintf(buf, "(There %s %d more unpaid object%s on the floor.)", + floorverb, floorcount, plur(floorcount)); + putstr(win, 0, buf); + } + } + + if (count > 0) + display_nhwindow(win, FALSE); destroy_nhwindow(win); } @@ -3989,8 +4009,10 @@ dotypeinv(void) goto doI_done; } if (c == 'u' || (c == 'U' && unpaid_count && !ucnt)) { - if (unpaid_count) - dounpaid(); + int floorcount = count_unpaid(fobj); + + if (unpaid_count || floorcount) + dounpaid(floorcount); else You("are not carrying any unpaid objects."); goto doI_done; diff --git a/src/mkobj.c b/src/mkobj.c index f7cb712bc..871a4082c 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -2784,7 +2784,7 @@ shop_obj_sanity(struct obj *obj, const char *mesg) struct obj *otop; struct monst *shkp; const char *why; - boolean costly; + boolean costly, costlytoo; coordxy x = 0, y = 0; /* if contained, get top-most container; we needs its location */ @@ -2800,17 +2800,22 @@ shop_obj_sanity(struct obj *obj, const char *mesg) /* these will always be needed for the normal case, so don't bother waiting until we find an insanity to fetch them */ shkp = find_objowner(obj, x, y); - costly = costly_spot(x, y) || costly_adjacent(shkp, x, y); + costly = costly_spot(x, y); + costlytoo = costly_adjacent(shkp, x, y); why = (const char *) 0; if (obj->no_charge && obj->unpaid) { why = "%s obj both unpaid and no_charge! %s %s: %s"; } else if (obj->unpaid) { - /* unpaid is only applicable for directly carried objects and - for objects inside carried containers */ - if (otop->where != OBJ_INVENT) + /* unpaid is only applicable for directly carried objects, for + objects inside carried containers, and for floor items outside + the shop proper but within the shop boundary (walls, door, "free + spot") and for objects moved from such spots into the shop proper + by repair of shop walls */ + if (otop->where != OBJ_INVENT + && (otop->where != OBJ_FLOOR || (!costly && !costlytoo))) why = "%s unpaid obj not carried! %s %s: %s"; - else if (!costly) + else if (!costly && !costlytoo) why = "%s unpaid obj not inside tended shop! %s %s: %s"; else if (!shkp) why = "%s unpaid obj inside untended shop! %s %s: %s"; diff --git a/src/teleport.c b/src/teleport.c index fe0bb75e6..833e2d163 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -1655,15 +1655,37 @@ rloco(register struct obj* obj) } else if (otx == 0 && oty == 0) { ; /* fell through a trap door; no update of old loc needed */ } else { - if (costly_spot(otx, oty) - && (!costly_spot(tx, ty) - || !strchr(in_rooms(tx, ty, 0), *in_rooms(otx, oty, 0)))) { - if (costly_spot(u.ux, u.uy) - && strchr(u.urooms, *in_rooms(otx, oty, 0))) - addtobill(obj, FALSE, FALSE, FALSE); - else + struct monst *shkp = find_objowner(obj, otx, oty); + boolean objinshop = shkp && costly_spot(otx, oty), + onboundary = shkp && costly_adjacent(shkp, otx, oty); + + /* + * If object starts inside shop or is unpaid and on shop boundary: + * if hero is outside the shop, treat this as theft; + * otherwise, if it arrives inside same shop, remove it from bill; + * otherwise, if it arrives on the boundary, add it to bill; + * if it arrives outside the shop, treat this as a theft. + * Billing routines deal with obj->no_charge. + */ + if (objinshop || (obj->unpaid && onboundary)) { + char h = *in_rooms(u.ux, u.uy, SHOPBASE), + oo = *in_rooms(otx, oty, 0); + boolean hinshop = h && strchr(in_rooms(shkp->mx, shkp->my, 0), h); + + if (hinshop && costly_spot(tx, ty) + /* verify that it's the same shop */ + && oo && strchr(in_rooms(tx, ty, 0), oo)) { + if (obj->unpaid) + subfrombill(obj, shkp); + } else if (hinshop && costly_adjacent(shkp, tx, ty) + && oo && strchr(in_rooms(tx, ty, 0), oo)) { + if (!obj->unpaid) + addtobill(obj, FALSE, FALSE, FALSE); + } else { (void) stolen_value(obj, otx, oty, FALSE, FALSE); + } } + newsym(otx, oty); /* update old location */ } place_object(obj, tx, ty);