From e989ae0b96c12fc81939e0db35debff108e458bb Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Sat, 15 Nov 2003 10:25:09 +0000 Subject: [PATCH] resuming interrupted 'A' command [ Caveat: compiles ok on branch code but only play tested on trunk code; the do_wear.c diff is a lot different between the two variants and the trunk one includes some whitespace cleanup. ] reported that having a spellcasting monster destroy some armor while you're in the midst of using 'A' to take that armor off would result in a crash. The problem was actually more widespread than that: having a nymph steal worn items (accessories as well as armor), or a succubus remove them, or being interrupted by monster activity and then reading a scroll of destroy armor prior to resuming 'A' could all produce a similar crash. 'A' relied on stale context and could attempt to manipulate an equipment slot which had become empty, ultimately leading to an attempt to dereference a null pointer. The 'R' command didn't have this problem since any accessory gets removed immediately. The 'T' command already had handling for this: there's only one item to deal with and multi-turn take off only applies to some of the slots; the donning() check followed by cancel_don() took care of those. Only 'A' was vulnerable to the problem and it wouldn't necessarily need to be interrupted and resumed; loss of the current multi-turn item or any pending item would be enough--but I'm not sure whether such item loss could occur without also interrupting the current activity, so resumption of previous 'A' was probably a requirement for triggering the crash. This makes shield and shirt handling be similar to other types of armor instead of relying on the fact that none of them need to have any attribute adjustments when put on or taken off. However, there are still assumptions (the `cancelled_don' stuff) that some slots don't have any eligible items requiring more than a single turn to use; that should probably be changed. --- doc/fixes34.3 | 2 + include/extern.h | 3 + src/do_wear.c | 307 ++++++++++++++++++++++++++++++----------------- src/steal.c | 6 +- 4 files changed, 210 insertions(+), 108 deletions(-) diff --git a/doc/fixes34.3 b/doc/fixes34.3 index 6ada5a3c0..a67fbc691 100644 --- a/doc/fixes34.3 +++ b/doc/fixes34.3 @@ -89,6 +89,8 @@ rotting corpses grammar fix allow successful teleport to more locations on debug mode level teleport menu trapped monster repeatedly switched between ranged and hand-to-hand weapon silver items such as wands avoided all the silver checks in hmon_hitmon() +resuming an interrupted 'A' command could cause crash if pending worn item(s) + were stolen or destroyed Platform- and/or Interface-Specific Fixes diff --git a/include/extern.h b/include/extern.h index 5c8e3d055..6df504e5d 100644 --- a/include/extern.h +++ b/include/extern.h @@ -374,6 +374,9 @@ E int NDECL(Gloves_off); E int NDECL(Boots_off); E int NDECL(Cloak_off); E int NDECL(Shield_off); +#ifdef TOURIST +E int NDECL(Shirt_off); +#endif E void NDECL(Amulet_off); E void FDECL(Ring_on, (struct obj *)); E void FDECL(Ring_off, (struct obj *)); diff --git a/src/do_wear.c b/src/do_wear.c index b412dc7e6..628f8b84e 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)do_wear.c 3.4 2003/05/25 */ +/* SCCS Id: @(#)do_wear.c 3.4 2003/11/14 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -35,6 +35,10 @@ STATIC_PTR int NDECL(Boots_on); STATIC_DCL int NDECL(Cloak_on); STATIC_PTR int NDECL(Helmet_on); STATIC_PTR int NDECL(Gloves_on); +STATIC_PTR int NDECL(Shield_on); +#ifdef TOURIST +STATIC_PTR int NDECL(Shirt_on); +#endif STATIC_DCL void NDECL(Amulet_on); STATIC_DCL void FDECL(Ring_off_or_gone, (struct obj *, BOOLEAN_P)); STATIC_PTR int FDECL(select_off, (struct obj *)); @@ -128,7 +132,7 @@ Boots_off() int otyp = uarmf->otyp; long oldprop = u.uprops[objects[otyp].oc_oprop].extrinsic & ~WORN_BOOTS; - + context.takeoff.mask &= ~W_ARMF; /* For levitation, float_down() returns if Levitation, so we * must do a setworn() _before_ the levitation case. */ @@ -150,7 +154,8 @@ Boots_off() } break; case ELVEN_BOOTS: - if (!oldprop && !HStealth && !BStealth && !context.takeoff.cancelled_don) { + if (!oldprop && !HStealth && !BStealth && + !context.takeoff.cancelled_don) { makeknown(otyp); You("sure are noisy."); } @@ -232,7 +237,7 @@ Cloak_off() int otyp = uarmc->otyp; long oldprop = u.uprops[objects[otyp].oc_oprop].extrinsic & ~WORN_CLOAK; - + context.takeoff.mask &= ~W_ARMC; /* For mummy wrapping, taking it off first resets `Invisible'. */ setworn((struct obj *)0, W_ARMC); switch (otyp) { @@ -333,6 +338,8 @@ Helmet_on() int Helmet_off() { + context.takeoff.mask &= ~W_ARMH; + switch(uarmh->otyp) { case FEDORA: case HELMET: @@ -402,6 +409,8 @@ Gloves_off() long oldprop = u.uprops[objects[uarmg->otyp].oc_oprop].extrinsic & ~WORN_GLOVES; + context.takeoff.mask &= ~W_ARMG; + switch(uarmg->otyp) { case LEATHER_GLOVES: break; @@ -450,11 +459,11 @@ Gloves_off() return 0; } -/* STATIC_OVL int Shield_on() { - switch(uarms->otyp) { +/* + switch (uarms->otyp) { case SMALL_SHIELD: case ELVEN_SHIELD: case URUK_HAI_SHIELD: @@ -465,15 +474,16 @@ Shield_on() break; default: impossible(unknown_type, c_shield, uarms->otyp); } +*/ return 0; } -*/ int Shield_off() { + context.takeoff.mask &= ~W_ARMS; /* - switch(uarms->otyp) { + switch (uarms->otyp) { case SMALL_SHIELD: case ELVEN_SHIELD: case URUK_HAI_SHIELD: @@ -489,6 +499,38 @@ Shield_off() return 0; } +#ifdef TOURIST +STATIC_OVL int +Shirt_on() +{ +/* + switch (uarmu->otyp) { + case HAWAIIAN_SHIRT: + case T_SHIRT: + break; + default: impossible(unknown_type, c_shirt, uarmu->otyp); + } +*/ + return 0; +} + +int +Shirt_off() +{ + context.takeoff.mask &= ~W_ARMU; +/* + switch (uarmu->otyp) { + case HAWAIIAN_SHIRT: + case T_SHIRT: + break; + default: impossible(unknown_type, c_shirt, uarmu->otyp); + } +*/ + setworn((struct obj *)0, W_ARMU); + return 0; +} +#endif /*TOURIST*/ + /* This must be done in worn.c, because one of the possible intrinsics conferred * is fire resistance, and we have to immediately set HFire_resistance in worn.c * since worn.c will check it before returning. @@ -503,6 +545,7 @@ Armor_on() int Armor_off() { + context.takeoff.mask &= ~W_ARM; setworn((struct obj *)0, W_ARM); context.takeoff.cancelled_don = FALSE; return 0; @@ -514,6 +557,7 @@ Armor_off() int Armor_gone() { + context.takeoff.mask &= ~W_ARM; setnotworn(uarm); context.takeoff.cancelled_don = FALSE; return 0; @@ -573,6 +617,8 @@ Amulet_on() void Amulet_off() { + context.takeoff.mask &= ~W_AMUL; + switch(uamul->otyp) { case AMULET_OF_ESP: /* need to update ability before calling see_monsters() */ @@ -729,9 +775,10 @@ Ring_off_or_gone(obj,gone) register struct obj *obj; boolean gone; { - register long mask = obj->owornmask & W_RING; + long mask = (obj->owornmask & W_RING); int old_attrib, which; + context.takeoff.mask &= ~mask; if(!(u.uprops[objects[obj->otyp].oc_oprop].extrinsic & mask)) impossible("Strange... I didn't know you had that ring."); if(gone) setnotworn(obj); @@ -882,6 +929,7 @@ register struct obj *otmp; { boolean was_blind = Blind, changed = FALSE; + context.takeoff.mask &= ~W_TOOL; setworn((struct obj *)0, otmp->owornmask); off_msg(otmp); @@ -916,22 +964,51 @@ register struct obj *otmp; void set_wear() { +#ifdef TOURIST + if (uarmu) (void) Shirt_on(); +#endif if (uarm) (void) Armor_on(); if (uarmc) (void) Cloak_on(); if (uarmf) (void) Boots_on(); if (uarmg) (void) Gloves_on(); if (uarmh) (void) Helmet_on(); -/* if (uarms) (void) Shield_on(); */ + if (uarms) (void) Shield_on(); } +/* check whether the target object is currently being put on (or taken off) */ boolean -donning(otmp) +donning(otmp) /* also checks for doffing */ register struct obj *otmp; { - return((boolean)((otmp == uarmf && (afternmv == Boots_on || afternmv == Boots_off)) - || (otmp == uarmh && (afternmv == Helmet_on || afternmv == Helmet_off)) - || (otmp == uarmg && (afternmv == Gloves_on || afternmv == Gloves_off)) - || (otmp == uarm && (afternmv == Armor_on || afternmv == Armor_off)))); + /* long what = (occupation == take_off) ? context.takeoff.what : 0L; */ + long what = context.takeoff.what; /* if nonzero, occupation is implied */ + boolean result = FALSE; + + if (otmp == uarm) + result = (afternmv == Armor_on || afternmv == Armor_off || + what == WORN_ARMOR); +#ifdef TOURIST + else if (otmp == uarmu) + result = (afternmv == Shirt_on || afternmv == Shirt_off || + what == WORN_SHIRT); +#endif + else if (otmp == uarmc) + result = (afternmv == Cloak_on || afternmv == Cloak_off || + what == WORN_CLOAK); + else if (otmp == uarmf) + result = (afternmv == Boots_on || afternmv == Boots_off || + what == WORN_BOOTS); + else if (otmp == uarmh) + result = (afternmv == Helmet_on || afternmv == Helmet_off || + what == WORN_HELMET); + else if (otmp == uarmg) + result = (afternmv == Gloves_on || afternmv == Gloves_off || + what == WORN_GLOVES); + else if (otmp == uarms) + result = (afternmv == Shield_on || afternmv == Shield_off || + what == WORN_SHIELD); + + return result; } void @@ -941,11 +1018,15 @@ cancel_don() * wasting time on it (and don't dereference it when donning would * otherwise finish) */ - context.takeoff.cancelled_don = (afternmv == Boots_on || afternmv == Helmet_on || - afternmv == Gloves_on || afternmv == Armor_on); + context.takeoff.cancelled_don = (afternmv == Boots_on || + afternmv == Helmet_on || + afternmv == Gloves_on || + afternmv == Armor_on); afternmv = 0; nomovemsg = (char *)0; multi = 0; + context.takeoff.delay = 0; + context.takeoff.what = 0L; } static NEARDATA const char clothes[] = {ARMOR_CLASS, 0}; @@ -1362,7 +1443,10 @@ dowear() nomovemsg = "You finish your dressing maneuver."; } else { if(is_cloak(otmp)) (void) Cloak_on(); -/* if(is_shield(otmp)) (void) Shield_on(); */ + if (is_shield(otmp)) (void) Shield_on(); +#ifdef TOURIST + if (is_shirt(otmp)) (void) Shirt_on(); +#endif on_msg(otmp); } context.takeoff.mask = context.takeoff.what = 0L; @@ -1773,57 +1857,59 @@ register struct obj *otmp; STATIC_OVL struct obj * do_takeoff() { - register struct obj *otmp = (struct obj *)0; + struct obj *otmp = (struct obj *)0; + struct takeoff_info *doff = &context.takeoff; - if (context.takeoff.what == W_WEP) { - if(!cursed(uwep)) { + if (doff->what == W_WEP) { + if (!cursed(uwep)) { setuwep((struct obj *) 0); You("are empty %s.", body_part(HANDED)); u.twoweap = FALSE; - } - } else if (context.takeoff.what == W_SWAPWEP) { - setuswapwep((struct obj *) 0); - You("no longer have a second weapon readied."); - u.twoweap = FALSE; - } else if (context.takeoff.what == W_QUIVER) { - setuqwep((struct obj *) 0); - You("no longer have ammunition readied."); - } else if (context.takeoff.what == WORN_ARMOR) { - otmp = uarm; - if(!cursed(otmp)) (void) Armor_off(); - } else if (context.takeoff.what == WORN_CLOAK) { - otmp = uarmc; - if(!cursed(otmp)) (void) Cloak_off(); - } else if (context.takeoff.what == WORN_BOOTS) { - otmp = uarmf; - if(!cursed(otmp)) (void) Boots_off(); - } else if (context.takeoff.what == WORN_GLOVES) { - otmp = uarmg; - if(!cursed(otmp)) (void) Gloves_off(); - } else if (context.takeoff.what == WORN_HELMET) { - otmp = uarmh; - if(!cursed(otmp)) (void) Helmet_off(); - } else if (context.takeoff.what == WORN_SHIELD) { - otmp = uarms; - if(!cursed(otmp)) (void) Shield_off(); + } + } else if (doff->what == W_SWAPWEP) { + setuswapwep((struct obj *) 0); + You("no longer have a second weapon readied."); + u.twoweap = FALSE; + } else if (doff->what == W_QUIVER) { + setuqwep((struct obj *) 0); + You("no longer have ammunition readied."); + } else if (doff->what == WORN_ARMOR) { + otmp = uarm; + if (!cursed(otmp)) (void) Armor_off(); + } else if (doff->what == WORN_CLOAK) { + otmp = uarmc; + if (!cursed(otmp)) (void) Cloak_off(); + } else if (doff->what == WORN_BOOTS) { + otmp = uarmf; + if (!cursed(otmp)) (void) Boots_off(); + } else if (doff->what == WORN_GLOVES) { + otmp = uarmg; + if (!cursed(otmp)) (void) Gloves_off(); + } else if (doff->what == WORN_HELMET) { + otmp = uarmh; + if (!cursed(otmp)) (void) Helmet_off(); + } else if (doff->what == WORN_SHIELD) { + otmp = uarms; + if (!cursed(otmp)) (void) Shield_off(); #ifdef TOURIST - } else if (context.takeoff.what == WORN_SHIRT) { - otmp = uarmu; - if(!cursed(otmp)) - setworn((struct obj *)0, uarmu->owornmask & W_ARMOR); + } else if (doff->what == WORN_SHIRT) { + otmp = uarmu; + if (!cursed(otmp)) (void) Shirt_off(); #endif - } else if (context.takeoff.what == WORN_AMUL) { - otmp = uamul; - if(!cursed(otmp)) Amulet_off(); - } else if (context.takeoff.what == LEFT_RING) { - otmp = uleft; - if(!cursed(otmp)) Ring_off(uleft); - } else if (context.takeoff.what == RIGHT_RING) { - otmp = uright; - if(!cursed(otmp)) Ring_off(uright); - } else if (context.takeoff.what == WORN_BLINDF) { - if (!cursed(ublindf)) Blindf_off(ublindf); - } else impossible("do_takeoff: taking off %lx", context.takeoff.what); + } else if (doff->what == WORN_AMUL) { + otmp = uamul; + if (!cursed(otmp)) Amulet_off(); + } else if (doff->what == LEFT_RING) { + otmp = uleft; + if (!cursed(otmp)) Ring_off(uleft); + } else if (doff->what == RIGHT_RING) { + otmp = uright; + if (!cursed(otmp)) Ring_off(uright); + } else if (doff->what == WORN_BLINDF) { + if (!cursed(ublindf)) Blindf_off(ublindf); + } else { + impossible("do_takeoff: taking off %lx", doff->what); + } return(otmp); } @@ -1834,84 +1920,83 @@ take_off() { register int i; register struct obj *otmp; + struct takeoff_info *doff = &context.takeoff; - if (context.takeoff.what) { - if (context.takeoff.delay > 0) { - context.takeoff.delay--; + if (doff->what) { + if (doff->delay > 0) { + doff->delay--; return(1); /* still busy */ } else { if ((otmp = do_takeoff())) off_msg(otmp); } - context.takeoff.mask &= ~context.takeoff.what; - context.takeoff.what = 0L; + doff->mask &= ~doff->what; + doff->what = 0L; } for(i = 0; takeoff_order[i]; i++) - if(context.takeoff.mask & takeoff_order[i]) { - context.takeoff.what = takeoff_order[i]; + if(doff->mask & takeoff_order[i]) { + doff->what = takeoff_order[i]; break; } otmp = (struct obj *) 0; - context.takeoff.delay = 0; + doff->delay = 0; - if (context.takeoff.what == 0L) { - You("finish %s.", context.takeoff.disrobing); + if (doff->what == 0L) { + You("finish %s.", doff->disrobing); return 0; - } else if (context.takeoff.what == W_WEP) { - context.takeoff.delay = 1; - } else if (context.takeoff.what == W_SWAPWEP) { - context.takeoff.delay = 1; - } else if (context.takeoff.what == W_QUIVER) { - context.takeoff.delay = 1; - } else if (context.takeoff.what == WORN_ARMOR) { + } else if (doff->what == W_WEP) { + doff->delay = 1; + } else if (doff->what == W_SWAPWEP) { + doff->delay = 1; + } else if (doff->what == W_QUIVER) { + doff->delay = 1; + } else if (doff->what == WORN_ARMOR) { otmp = uarm; /* If a cloak is being worn, add the time to take it off and put * it back on again. Kludge alert! since that time is 0 for all * known cloaks, add 1 so that it actually matters... */ - if (uarmc) context.takeoff.delay += 2 * objects[uarmc->otyp].oc_delay + 1; - } else if (context.takeoff.what == WORN_CLOAK) { + if (uarmc) doff->delay += 2 * objects[uarmc->otyp].oc_delay + 1; + } else if (doff->what == WORN_CLOAK) { otmp = uarmc; - } else if (context.takeoff.what == WORN_BOOTS) { + } else if (doff->what == WORN_BOOTS) { otmp = uarmf; - } else if (context.takeoff.what == WORN_GLOVES) { + } else if (doff->what == WORN_GLOVES) { otmp = uarmg; - } else if (context.takeoff.what == WORN_HELMET) { + } else if (doff->what == WORN_HELMET) { otmp = uarmh; - } else if (context.takeoff.what == WORN_SHIELD) { + } else if (doff->what == WORN_SHIELD) { otmp = uarms; #ifdef TOURIST - } else if (context.takeoff.what == WORN_SHIRT) { + } else if (doff->what == WORN_SHIRT) { otmp = uarmu; /* add the time to take off and put back on armor and/or cloak */ - if (uarm) context.takeoff.delay += 2 * objects[uarm->otyp].oc_delay; - if (uarmc) context.takeoff.delay += 2 * objects[uarmc->otyp].oc_delay + 1; + if (uarm) doff->delay += 2 * objects[uarm->otyp].oc_delay; + if (uarmc) doff->delay += 2 * objects[uarmc->otyp].oc_delay + 1; #endif - } else if (context.takeoff.what == WORN_AMUL) { - context.takeoff.delay = 1; - } else if (context.takeoff.what == LEFT_RING) { - context.takeoff.delay = 1; - } else if (context.takeoff.what == RIGHT_RING) { - context.takeoff.delay = 1; - } else if (context.takeoff.what == WORN_BLINDF) { - context.takeoff.delay = 2; + } else if (doff->what == WORN_AMUL) { + doff->delay = 1; + } else if (doff->what == LEFT_RING) { + doff->delay = 1; + } else if (doff->what == RIGHT_RING) { + doff->delay = 1; + } else if (doff->what == WORN_BLINDF) { + doff->delay = 2; } else { - impossible("take_off: taking off %lx", context.takeoff.what); + impossible("take_off: taking off %lx", doff->what); return 0; /* force done */ } - if (otmp) context.takeoff.delay += objects[otmp->otyp].oc_delay; + if (otmp) doff->delay += objects[otmp->otyp].oc_delay; /* Since setting the occupation now starts the counter next move, that - * would always produce a delay 1 too big per item unless we subtract + * would always produce a delay 1 too big per item unless we subtract * 1 here to account for it. */ - if (context.takeoff.delay>0) context.takeoff.delay--; + if (doff->delay > 0) doff->delay--; - set_occupation(take_off, - (context.takeoff.disrobing[0]) ? - context.takeoff.disrobing : (char *)0, 0); + set_occupation(take_off, doff->disrobing, 0); return(1); /* get busy */ } @@ -2002,6 +2087,7 @@ int retry; return 0; } +/* hit by destroy armor scroll/black dragon breath/monster spell */ int destroy_arm(atmp) register struct obj *atmp; @@ -2012,11 +2098,12 @@ register struct obj *atmp; (!obj_resists(otmp, 0, 90))) if (DESTROY_ARM(uarmc)) { - Your("%s crumbles and turns to dust!", cloak_simple_name(uarmc)); + if (donning(otmp)) cancel_don(); + Your("%s crumbles and turns to dust!", + cloak_simple_name(uarmc)); (void) Cloak_off(); useup(otmp); } else if (DESTROY_ARM(uarm)) { - /* may be disintegrated by spell or dragon breath... */ if (donning(otmp)) cancel_don(); Your("armor turns to dust and falls to the %s!", surface(u.ux,u.uy)); @@ -2024,7 +2111,9 @@ register struct obj *atmp; useup(otmp); #ifdef TOURIST } else if (DESTROY_ARM(uarmu)) { + if (donning(otmp)) cancel_don(); Your("shirt crumbles into tiny threads and falls apart!"); + (void) Shirt_off(); useup(otmp); #endif } else if (DESTROY_ARM(uarmh)) { @@ -2044,12 +2133,16 @@ register struct obj *atmp; (void) Boots_off(); useup(otmp); } else if (DESTROY_ARM(uarms)) { + if (donning(otmp)) cancel_don(); Your("shield crumbles away!"); (void) Shield_off(); useup(otmp); - } else return(0); /* could not destroy anything */ + } else { + return 0; /* could not destroy anything */ + } #undef DESTROY_ARM + stop_occupation(); return(1); } diff --git a/src/steal.c b/src/steal.c index a58424bad..61f0b93ca 100644 --- a/src/steal.c +++ b/src/steal.c @@ -1,4 +1,4 @@ -/* SCCS Id: @(#)steal.c 3.4 2002/09/07 */ +/* SCCS Id: @(#)steal.c 3.4 2003/11/14 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -195,6 +195,10 @@ boolean unchain_ball; /* whether to unpunish or just unwield */ else if (obj == uarmg) (void) Gloves_off(); else if (obj == uarmh) (void) Helmet_off(); else if (obj == uarms) (void) Shield_off(); +#ifdef TOURIST + else if (obj == uarmu) (void) Shirt_off(); +#endif + /* catchall -- should never happen */ else setworn((struct obj *)0, obj->owornmask & W_ARMOR); } else if (obj->owornmask & W_AMUL) { Amulet_off(); -- 2.40.0