diff --git a/doc/fixes34.2 b/doc/fixes34.2 index ff50705f3..f70f0830e 100644 --- a/doc/fixes34.2 +++ b/doc/fixes34.2 @@ -31,6 +31,7 @@ allow all tame monsters that eat to consider food thrown to them the screen display wasn't always up to date after map topology changes jumping over a sokobon pit would result in the player next to, not in, the pit don't let arrow, rock or dart traps provide an infinite number of objects +dropping from height or throwing a normal container may damage contents Platform- and/or Interface-Specific Fixes diff --git a/include/extern.h b/include/extern.h index 8fb0b749f..8fef9bfa5 100644 --- a/include/extern.h +++ b/include/extern.h @@ -423,6 +423,7 @@ E void FDECL(wantdoor, (int,int,genericptr_t)); /* ### dokick.c ### */ E boolean FDECL(ghitm, (struct monst *,struct obj *)); +E void FDECL(container_impact_dmg, (struct obj *)); E int NDECL(dokick); E boolean FDECL(ship_object, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P)); E void NDECL(obj_delivery); diff --git a/src/dokick.c b/src/dokick.c index 3bc3fc1c1..6ca89c08a 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -330,16 +330,75 @@ register struct obj *gold; return(0); } +/* container is kicked, dropped, thrown or otherwise impacted by player. + * Assumes container is on floor. Checks contents for possible damage. */ +void +container_impact_dmg(obj) +struct obj *obj; +{ + struct monst *shkp; + struct obj *otmp, *otmp2; + long loss = 0L; + boolean costly, insider; + xchar x = obj->ox, y = obj->oy; + + /* only consider normal containers */ + if (!Is_container(obj) || Is_mbag(obj)) return; + + costly = ((shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) && + costly_spot(x, y)); + insider = (*u.ushops && inside_shop(u.ux, u.uy) && + *in_rooms(x, y, SHOPBASE) == *u.ushops); + + for (otmp = obj->cobj; otmp; otmp = otmp2) { + const char *result = (char *)0; + + otmp2 = otmp->nobj; + if (objects[otmp->otyp].oc_material == GLASS && + otmp->oclass != GEM_CLASS && !obj_resists(otmp, 33, 100)) { + result = "shatter"; + } else if (otmp->otyp == EGG && !rn2(3)) { + result = "cracking"; + } + if (result) { + if (otmp->otyp == MIRROR) change_luck(-2); + + /* eggs laid by you. penalty is -1 per egg, max 5, + * but it's always exactly 1 that breaks */ + if (otmp->otyp == EGG && otmp->spe && otmp->corpsenm >= LOW_PM) + change_luck(-1); + You_hear("a muffled %s.", result); + if (costly) + loss += stolen_value(otmp, x, y, + (boolean)shkp->mpeaceful, TRUE); + if (otmp->quan > 1L) + useup(otmp); + else { + obj_extract_self(otmp); + obfree(otmp, (struct obj *) 0); + } + } + } + if (costly && loss) { + if (!insider) { + You("caused %ld %s worth of damage!", loss, currency(loss)); + make_angry_shk(shkp, x, y); + } else { + You("owe %s %ld %s for objects destroyed.", + mon_nam(shkp), loss, currency(loss)); + } + } +} + STATIC_OVL int kick_object(x, y) xchar x, y; { int range; register struct monst *mon, *shkp; - register struct obj *otmp; struct trap *trap; char bhitroom; - boolean costly, insider, isgold, slide = FALSE; + boolean costly, isgold, slide = FALSE; /* if a pile, the "top" object gets kicked */ kickobj = level.objects[x][y]; @@ -405,8 +464,6 @@ xchar x, y; costly = ((shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) && costly_spot(x, y)); - insider = (*u.ushops && inside_shop(u.ux, u.uy) && - *in_rooms(x, y, SHOPBASE) == *u.ushops); isgold = (kickobj->oclass == COIN_CLASS); if (IS_ROCK(levl[x][y].typ) || closed_door(x, y)) { @@ -442,51 +499,10 @@ xchar x, y; /* a box gets a chance of breaking open here */ if(Is_box(kickobj)) { boolean otrp = kickobj->otrapped; - struct obj *otmp2; - long loss = 0L; if(range < 2) pline("THUD!"); - for(otmp = kickobj->cobj; otmp; otmp = otmp2) { - const char *result = (char *)0; - - otmp2 = otmp->nobj; - if (objects[otmp->otyp].oc_material == GLASS - && otmp->oclass != GEM_CLASS - && !obj_resists(otmp, 33, 100)) { - result = "shatter"; - } else if (otmp->otyp == EGG && !rn2(3)) { - result = "cracking"; - } - if (result) { - if (otmp->otyp == MIRROR) - change_luck(-2); - /* eggs laid by you */ - /* penalty is -1 per egg, max 5, but it's always - exactly 1 that breaks */ - if (otmp->otyp == EGG && otmp->spe && otmp->corpsenm >= LOW_PM) - change_luck(-1); - You_hear("a muffled %s.",result); - if(costly) loss += stolen_value(otmp, x, y, - (boolean)shkp->mpeaceful, TRUE); - if (otmp->quan > 1L) - useup(otmp); - else { - obj_extract_self(otmp); - obfree(otmp, (struct obj *) 0); - } - } - } - if(costly && loss) { - if(!insider) { - You("caused %ld %s worth of damage!", - loss, currency(loss)); - make_angry_shk(shkp, x, y); - } else { - You("owe %s %ld %s for objects destroyed.", - mon_nam(shkp), loss, currency(loss)); - } - } + container_impact_dmg(kickobj); if (kickobj->olocked) { if (!rn2(5) || (martial() && !rn2(2))) { diff --git a/src/dothrow.c b/src/dothrow.c index a9193990f..24bdae312 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -340,6 +340,7 @@ register struct obj *obj; if (hero_breaks(obj, u.ux, u.uy, TRUE)) return; if (ship_object(obj, u.ux, u.uy, FALSE)) return; dropy(obj); + if (!u.uswallow) container_impact_dmg(obj); } /* @@ -1088,6 +1089,8 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */ newsym(bhitpos.x,bhitpos.y); if (obj_sheds_light(obj)) vision_full_recalc = 1; + if (!IS_SOFT(levl[bhitpos.x][bhitpos.y].typ)) + container_impact_dmg(obj); } }