From ea6cdd3f399443deaaacf1743352530050298719 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 26 Nov 2024 21:20:30 -0800 Subject: [PATCH] Amulet_on() & Amulet_off() Fix a FIXME in Amulet_off() for removing an amulet of magical breathing when within a poison gas cloud. Redo message sequencing for both Amulet_on() and Amulet_off(). Use up an amulet of change if put on while the Unchanging attribute is active (via #wizintrinsic) instead of wearing it with no effect. Don't discover amulet of strangulation if put on while already Strangled (via #wizintrinsic). --- src/do_wear.c | 180 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 116 insertions(+), 64 deletions(-) diff --git a/src/do_wear.c b/src/do_wear.c index 652c13868..72ecfd75d 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -30,7 +30,7 @@ staticfn int Gloves_on(void); staticfn int Shield_on(void); staticfn int Shirt_on(void); staticfn void dragon_armor_handling(struct obj *, boolean, boolean); -staticfn void Amulet_on(void); +staticfn void Amulet_on(struct obj *) NONNULLARG1; staticfn void learnring(struct obj *, boolean); staticfn void adjust_attrib(struct obj *, int, int); staticfn void Ring_off_or_gone(struct obj *, boolean); @@ -74,6 +74,15 @@ off_msg(struct obj *otmp) staticfn void on_msg(struct obj *otmp) { + /* on_msg() for rings and amulets just shows add-to-invent feedback + [after caller calls setworn(), for suffix: "(on {left|right} hand)" + or "(being worn)"]; eyewear too unless giving verbose message below */ + if ((otmp->owornmask & (W_RING | W_AMUL)) != 0L + || ((otmp->owornmask & W_TOOL) != 0L && !flags.verbose)) { + prinv((char *) NULL, otmp, 0L); + return; + } + if (flags.verbose) { char how[BUFSZ]; /* call xname() before obj_is_pname(); formatting obj's name @@ -925,16 +934,13 @@ Armor_gone(void) } staticfn void -Amulet_on(void) +Amulet_on(struct obj *amul) { - /* make sure amulet isn't wielded; can't use remove_worn_item() - here because it has already been set worn in amulet slot */ - if (uamul == uwep) - setuwep((struct obj *) 0); - else if (uamul == uswapwep) - setuswapwep((struct obj *) 0); - else if (uamul == uquiver) - setuqwep((struct obj *) 0); + boolean on_msg_done = FALSE; + + /* make sure amulet isn't wielded/alt-wielded/quivered, before wearing */ + remove_worn_item(amul, FALSE); + setworn(amul, W_AMUL); switch (uamul->otyp) { case AMULET_OF_ESP: @@ -952,8 +958,10 @@ Amulet_on(void) was_in_poison_gas = region_danger(); EMagical_breathing |= W_AMUL; if (was_in_poison_gas) { - You("are no longer bothered by the poison gas."); makeknown(AMULET_OF_MAGICAL_BREATHING); + on_msg(uamul); + on_msg_done = TRUE; + You("are no longer bothered by the poison gas."); } /* no need to check for becoming able to breathe underwater; if we are underwater, we already can or we would have drowned */ @@ -964,46 +972,58 @@ Amulet_on(void) make_slimed(0L, (char *) 0); break; case AMULET_OF_CHANGE: { + boolean call_it = FALSE; int new_sex, orig_sex = poly_gender(); - if (Unchanging) - break; - change_sex(); + /* in normal play it's not possible to put on an amulet of change + while already wearing an amulet of unchanging, but in wizard + mode the Unchanging attribute can be set via #wizintrinsic */ + if (!Unchanging) + change_sex(); + new_sex = poly_gender(); + if (new_sex != orig_sex) + makeknown(AMULET_OF_CHANGE); + on_msg(uamul); /* show 'z - amulet of change (being worn)' */ + on_msg_done = TRUE; + /* Don't use same message as polymorph */ if (new_sex != orig_sex) { - makeknown(AMULET_OF_CHANGE); + newsym(u.ux, u.uy); /* glyphmon flag and tile have changed */ + disp.botl = TRUE; /* role name or rank title might have changed */ You("are suddenly very %s!", flags.female ? "feminine" : "masculine"); - disp.botl = TRUE; - newsym(u.ux, u.uy); /* glyphmon flag and tile may have gone - * from male to female or vice versa */ } else { /* already polymorphed into single-gender monster; only changed the character's base sex */ You("don't feel like yourself."); + /* checking dknown is redundant--amulets always have dknown set */ + call_it = (uamul->dknown != 0); } livelog_newform(FALSE, orig_sex, new_sex); pline_The("amulet disintegrates!"); - if (orig_sex == poly_gender() && uamul->dknown) + if (call_it) trycall(uamul); useup(uamul); break; } case AMULET_OF_STRANGULATION: - if (can_be_strangled(&gy.youmonst)) { + /* note: might already be Strangled (via #wizintrinsic) */ + if (can_be_strangled(&gy.youmonst) && !Strangled) { makeknown(AMULET_OF_STRANGULATION); Strangled = 6L; disp.botl = TRUE; + on_msg(uamul); + on_msg_done = TRUE; pline("It constricts your throat!"); } break; case AMULET_OF_RESTFUL_SLEEP: { - long newnap = (long) rnd(100), oldnap = (HSleepy & TIMEOUT); + long newnap = (long) rnd(98) + 2L, oldnap = (HSleepy & TIMEOUT); - /* avoid clobbering FROMOUTSIDE bit, which might have - gotten set by previously eating one of these amulets */ if (newnap < oldnap || oldnap == 0L) + /* avoid clobbering FROMOUTSIDE bit, which might have + gotten set by previously eating one of these amulets */ HSleepy = (HSleepy & ~TIMEOUT) | newnap; break; } @@ -1021,6 +1041,8 @@ Amulet_on(void) if (!already_flying) { makeknown(AMULET_OF_FLYING); + on_msg(uamul); + on_msg_done = TRUE; disp.botl = TRUE; /* status: 'Fly' On */ You("are now in flight."); } @@ -1033,19 +1055,28 @@ Amulet_on(void) case AMULET_OF_YENDOR: break; } + + if (!on_msg_done) + on_msg(uamul); } void Amulet_off(void) { + struct obj *amul = uamul; /* for off_msg() after setworn(NULL,W_AMUL) */ + boolean mkn = FALSE, early_off_msg = FALSE; + svc.context.takeoff.mask &= ~W_AMUL; switch (uamul->otyp) { case AMULET_OF_ESP: /* need to update ability before calling see_monsters() */ setworn((struct obj *) 0, W_AMUL); + off_msg(amul); + early_off_msg = TRUE; + see_monsters(); - return; + break; case AMULET_OF_LIFE_SAVING: case AMULET_VERSUS_POISON: case AMULET_OF_REFLECTION: @@ -1054,21 +1085,31 @@ Amulet_off(void) case FAKE_AMULET_OF_YENDOR: break; case AMULET_OF_MAGICAL_BREATHING: + /* amulet is currently still on; take it off before calling drown() + and region_danger(); call off_msg() before specific messages */ + setworn((struct obj *) 0, W_AMUL); + off_msg(amul); /* 'uamul' has been set to Null */ + early_off_msg = TRUE; + if (Underwater) { - /* HMagical_breathing must be set off before calling drown() */ - setworn((struct obj *) 0, W_AMUL); if (!cant_drown(gy.youmonst.data) && !Swimming) { You("suddenly inhale an unhealthy amount of %s!", hliquid("water")); + mkn = TRUE; /* in case of life-saving */ (void) drown(); } - return; } - /* - * FIXME: we need a poison gas region check here - */ + if (region_danger()) { + /* "breathing": wouldn't get here otherwise */ + You("are breathing poison gas!"); + mkn = TRUE; + } break; case AMULET_OF_STRANGULATION: + setworn((struct obj *) 0, W_AMUL); + off_msg(amul); + early_off_msg = TRUE; + if (Strangled) { Strangled = 0L; disp.botl = TRUE; @@ -1076,6 +1117,7 @@ Amulet_off(void) Your("%s is no longer constricted!", body_part(NECK)); else You("can breathe more easily!"); + mkn = TRUE; } break; case AMULET_OF_RESTFUL_SLEEP: @@ -1083,20 +1125,24 @@ Amulet_off(void) /* HSleepy = 0L; -- avoid clobbering FROMOUTSIDE bit */ if (!ESleepy && !(HSleepy & ~TIMEOUT)) HSleepy &= ~TIMEOUT; /* clear timeout bits */ - return; + break; case AMULET_OF_FLYING: { boolean was_flying = !!Flying; - /* remove amulet 'early' to determine whether Flying changes */ + /* remove amulet 'early' to determine whether Flying changes; + also in case spoteffects() does something with the amulet */ setworn((struct obj *) 0, W_AMUL); + off_msg(amul); + early_off_msg = TRUE; + float_vs_flight(); /* probably not needed here */ if (was_flying && !Flying) { - makeknown(AMULET_OF_FLYING); disp.botl = TRUE; /* status: 'Fly' Off */ You("%s.", (is_pool_or_lava(u.ux, u.uy) || Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) ? "stop flying" : "land"); + mkn = TRUE; /* makeknown(AMULET_OF_FLYING) */ spoteffects(TRUE); } break; @@ -1107,7 +1153,12 @@ Amulet_off(void) case AMULET_OF_YENDOR: break; } + setworn((struct obj *) 0, W_AMUL); + if (!early_off_msg) + off_msg(amul); /* (not 'uamul'; it's Null now) */ + if (mkn) + makeknown(amul->otyp); return; } @@ -1198,7 +1249,9 @@ Ring_on(struct obj *obj) case RIN_FREE_ACTION: case RIN_SLOW_DIGESTION: case RIN_SUSTAIN_ABILITY: + break; case MEAT_RING: + /* wearing a meat ring does not affect vegan conduct */ break; case RIN_STEALTH: toggle_stealth(obj, oldprop, TRUE); @@ -1383,8 +1436,7 @@ Blindf_on(struct obj *otmp) boolean already_blind = Blind, changed = FALSE; /* blindfold might be wielded; release it for wearing */ - if (otmp->owornmask & W_WEAPONS) - remove_worn_item(otmp, FALSE); + remove_worn_item(otmp, FALSE); setworn(otmp, W_TOOL); on_msg(otmp); @@ -1469,7 +1521,7 @@ set_wear( if (!obj ? uleft != 0 : (obj == uleft)) (void) Ring_on(uleft); if (!obj ? uamul != 0 : (obj == uamul)) - (void) Amulet_on(); + (void) Amulet_on(uamul); if (!obj ? uarmu != 0 : (obj == uarmu)) (void) Shirt_on(); @@ -1606,8 +1658,8 @@ cancel_don(void) /* called by steal() during theft from hero; interrupt donning/doffing */ int -stop_donning(struct obj *stolenobj) /* no message if stolenobj is already - being doffing */ +stop_donning( + struct obj *stolenobj) /* no mesg if stolenobj is already being doffed */ { char buf[BUFSZ]; struct obj *otmp; @@ -1651,8 +1703,9 @@ static NEARDATA int Narmorpieces, Naccessories; /* assign values to Narmorpieces and Naccessories */ staticfn void -count_worn_stuff(struct obj **which, /* caller wants this when count is 1 */ - boolean accessorizing) +count_worn_stuff( + struct obj **which, /* caller wants this when count is 1 */ + boolean accessorizing) { struct obj *otmp; @@ -1736,12 +1789,12 @@ armor_or_accessory_off(struct obj *obj) off_msg(obj); Ring_off(obj); } else if (obj == uamul) { - Amulet_off(); - off_msg(obj); + Amulet_off(); /* does its own off_msg */ } else if (obj == ublindf) { Blindf_off(obj); /* does its own off_msg */ } else { - impossible("removing strange accessory?"); + impossible("removing strange accessory: %s", + safe_typename(obj->otyp)); if (obj->owornmask) remove_worn_item(obj, FALSE); } @@ -2014,12 +2067,12 @@ canwearobj(struct obj *otmp, long *mask, boolean noisy) You("have no feet..."); /* not body_part(FOOT) */ err++; } else if (Upolyd && gy.youmonst.data->mlet == S_CENTAUR) { - /* break_armor() pushes boots off for centaurs, - so don't let dowear() put them back on... */ + /* break_armor() pushes boots off for centaurs, so don't let + dowear() put them back on; + makeplural(body_part(FOOT)) would yield "rear hooves" here, + which sounds odd, so use hard-coded "hooves" */ if (noisy) - You("have too many hooves to wear %s.", - c_boots); /* makeplural(body_part(FOOT)) yields - "rear hooves" which sounds odd */ + You("have too many hooves to wear %s.", c_boots); err++; } else if (u.utrap && (u.utraptype == TT_BEARTRAP || u.utraptype == TT_INFLOOR @@ -2114,7 +2167,7 @@ staticfn int accessory_or_armor_on(struct obj *obj) { long mask = 0L; - boolean armor, ring, eyewear; + boolean armor, ring, amulet, eyewear; if (obj->owornmask & (W_ACCESSORY | W_ARMOR)) { already_wearing(c_that_); @@ -2122,6 +2175,7 @@ accessory_or_armor_on(struct obj *obj) } armor = (obj->oclass == ARMOR_CLASS); ring = (obj->oclass == RING_CLASS || obj->otyp == MEAT_RING); + amulet = (obj->oclass == AMULET_CLASS); eyewear = (obj->otyp == BLINDFOLD || obj->otyp == TOWEL || obj->otyp == LENSES); /* checks which are performed prior to actually touching the item */ @@ -2219,7 +2273,7 @@ accessory_or_armor_on(struct obj *obj) return res ? ECMD_TIME : ECMD_OK; } } - } else if (obj->oclass == AMULET_CLASS) { + } else if (amulet) { if (uamul) { already_wearing("an amulet"); return ECMD_OK; @@ -2306,26 +2360,24 @@ accessory_or_armor_on(struct obj *obj) } svc.context.takeoff.mask = svc.context.takeoff.what = 0L; } else { /* not armor */ - boolean give_feedback = FALSE; - - /* [releasing wielded accessory handled in Xxx_on()] */ if (ring) { + /* Ring_on() expects ring to already be worn as uleft or uright */ setworn(obj, mask); Ring_on(obj); - give_feedback = TRUE; - } else if (obj->oclass == AMULET_CLASS) { - setworn(obj, W_AMUL); - Amulet_on(); - /* no feedback here if amulet of change got used up */ - give_feedback = (uamul != 0); + /* is_worn(): 'obj' will always be worn here except when putting + on a ring of levitation while at a sink location */ + if (is_worn(obj)) + on_msg(obj); + } else if (amulet) { + /* setworn() and on_msg() handled by Amulet_on() */ + Amulet_on(obj); } else if (eyewear) { - /* setworn() handled by Blindf_on() */ + /* setworn() and on_msg() handled by Blindf_on() */ Blindf_on(obj); - /* message handled by Blindf_on(); leave give_feedback False */ + } else { + impossible("putting on unexpected type of accessory: %s", + safe_typename(obj->otyp)); } - /* feedback for ring or for amulet other than 'change' */ - if (give_feedback && is_worn(obj)) - prinv((char *) 0, obj, 0L); } return ECMD_TIME; }