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).
This commit is contained in:
PatR
2024-11-26 21:20:30 -08:00
parent d32ab55b84
commit ea6cdd3f39
+116 -64
View File
@@ -30,7 +30,7 @@ staticfn int Gloves_on(void);
staticfn int Shield_on(void); staticfn int Shield_on(void);
staticfn int Shirt_on(void); staticfn int Shirt_on(void);
staticfn void dragon_armor_handling(struct obj *, boolean, boolean); 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 learnring(struct obj *, boolean);
staticfn void adjust_attrib(struct obj *, int, int); staticfn void adjust_attrib(struct obj *, int, int);
staticfn void Ring_off_or_gone(struct obj *, boolean); staticfn void Ring_off_or_gone(struct obj *, boolean);
@@ -74,6 +74,15 @@ off_msg(struct obj *otmp)
staticfn void staticfn void
on_msg(struct obj *otmp) 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) { if (flags.verbose) {
char how[BUFSZ]; char how[BUFSZ];
/* call xname() before obj_is_pname(); formatting obj's name /* call xname() before obj_is_pname(); formatting obj's name
@@ -925,16 +934,13 @@ Armor_gone(void)
} }
staticfn void staticfn void
Amulet_on(void) Amulet_on(struct obj *amul)
{ {
/* make sure amulet isn't wielded; can't use remove_worn_item() boolean on_msg_done = FALSE;
here because it has already been set worn in amulet slot */
if (uamul == uwep) /* make sure amulet isn't wielded/alt-wielded/quivered, before wearing */
setuwep((struct obj *) 0); remove_worn_item(amul, FALSE);
else if (uamul == uswapwep) setworn(amul, W_AMUL);
setuswapwep((struct obj *) 0);
else if (uamul == uquiver)
setuqwep((struct obj *) 0);
switch (uamul->otyp) { switch (uamul->otyp) {
case AMULET_OF_ESP: case AMULET_OF_ESP:
@@ -952,8 +958,10 @@ Amulet_on(void)
was_in_poison_gas = region_danger(); was_in_poison_gas = region_danger();
EMagical_breathing |= W_AMUL; EMagical_breathing |= W_AMUL;
if (was_in_poison_gas) { if (was_in_poison_gas) {
You("are no longer bothered by the poison gas.");
makeknown(AMULET_OF_MAGICAL_BREATHING); 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; /* no need to check for becoming able to breathe underwater;
if we are underwater, we already can or we would have drowned */ if we are underwater, we already can or we would have drowned */
@@ -964,46 +972,58 @@ Amulet_on(void)
make_slimed(0L, (char *) 0); make_slimed(0L, (char *) 0);
break; break;
case AMULET_OF_CHANGE: { case AMULET_OF_CHANGE: {
boolean call_it = FALSE;
int new_sex, orig_sex = poly_gender(); int new_sex, orig_sex = poly_gender();
if (Unchanging) /* in normal play it's not possible to put on an amulet of change
break; while already wearing an amulet of unchanging, but in wizard
change_sex(); mode the Unchanging attribute can be set via #wizintrinsic */
if (!Unchanging)
change_sex();
new_sex = poly_gender(); 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 */ /* Don't use same message as polymorph */
if (new_sex != orig_sex) { 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!", You("are suddenly very %s!",
flags.female ? "feminine" : "masculine"); 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 { } else {
/* already polymorphed into single-gender monster; only /* already polymorphed into single-gender monster; only
changed the character's base sex */ changed the character's base sex */
You("don't feel like yourself."); 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); livelog_newform(FALSE, orig_sex, new_sex);
pline_The("amulet disintegrates!"); pline_The("amulet disintegrates!");
if (orig_sex == poly_gender() && uamul->dknown) if (call_it)
trycall(uamul); trycall(uamul);
useup(uamul); useup(uamul);
break; break;
} }
case AMULET_OF_STRANGULATION: 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); makeknown(AMULET_OF_STRANGULATION);
Strangled = 6L; Strangled = 6L;
disp.botl = TRUE; disp.botl = TRUE;
on_msg(uamul);
on_msg_done = TRUE;
pline("It constricts your throat!"); pline("It constricts your throat!");
} }
break; break;
case AMULET_OF_RESTFUL_SLEEP: { 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) 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; HSleepy = (HSleepy & ~TIMEOUT) | newnap;
break; break;
} }
@@ -1021,6 +1041,8 @@ Amulet_on(void)
if (!already_flying) { if (!already_flying) {
makeknown(AMULET_OF_FLYING); makeknown(AMULET_OF_FLYING);
on_msg(uamul);
on_msg_done = TRUE;
disp.botl = TRUE; /* status: 'Fly' On */ disp.botl = TRUE; /* status: 'Fly' On */
You("are now in flight."); You("are now in flight.");
} }
@@ -1033,19 +1055,28 @@ Amulet_on(void)
case AMULET_OF_YENDOR: case AMULET_OF_YENDOR:
break; break;
} }
if (!on_msg_done)
on_msg(uamul);
} }
void void
Amulet_off(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; svc.context.takeoff.mask &= ~W_AMUL;
switch (uamul->otyp) { switch (uamul->otyp) {
case AMULET_OF_ESP: case AMULET_OF_ESP:
/* need to update ability before calling see_monsters() */ /* need to update ability before calling see_monsters() */
setworn((struct obj *) 0, W_AMUL); setworn((struct obj *) 0, W_AMUL);
off_msg(amul);
early_off_msg = TRUE;
see_monsters(); see_monsters();
return; break;
case AMULET_OF_LIFE_SAVING: case AMULET_OF_LIFE_SAVING:
case AMULET_VERSUS_POISON: case AMULET_VERSUS_POISON:
case AMULET_OF_REFLECTION: case AMULET_OF_REFLECTION:
@@ -1054,21 +1085,31 @@ Amulet_off(void)
case FAKE_AMULET_OF_YENDOR: case FAKE_AMULET_OF_YENDOR:
break; break;
case AMULET_OF_MAGICAL_BREATHING: 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) { if (Underwater) {
/* HMagical_breathing must be set off before calling drown() */
setworn((struct obj *) 0, W_AMUL);
if (!cant_drown(gy.youmonst.data) && !Swimming) { if (!cant_drown(gy.youmonst.data) && !Swimming) {
You("suddenly inhale an unhealthy amount of %s!", You("suddenly inhale an unhealthy amount of %s!",
hliquid("water")); hliquid("water"));
mkn = TRUE; /* in case of life-saving */
(void) drown(); (void) drown();
} }
return;
} }
/* if (region_danger()) {
* FIXME: we need a poison gas region check here /* "breathing": wouldn't get here otherwise */
*/ You("are breathing poison gas!");
mkn = TRUE;
}
break; break;
case AMULET_OF_STRANGULATION: case AMULET_OF_STRANGULATION:
setworn((struct obj *) 0, W_AMUL);
off_msg(amul);
early_off_msg = TRUE;
if (Strangled) { if (Strangled) {
Strangled = 0L; Strangled = 0L;
disp.botl = TRUE; disp.botl = TRUE;
@@ -1076,6 +1117,7 @@ Amulet_off(void)
Your("%s is no longer constricted!", body_part(NECK)); Your("%s is no longer constricted!", body_part(NECK));
else else
You("can breathe more easily!"); You("can breathe more easily!");
mkn = TRUE;
} }
break; break;
case AMULET_OF_RESTFUL_SLEEP: case AMULET_OF_RESTFUL_SLEEP:
@@ -1083,20 +1125,24 @@ Amulet_off(void)
/* HSleepy = 0L; -- avoid clobbering FROMOUTSIDE bit */ /* HSleepy = 0L; -- avoid clobbering FROMOUTSIDE bit */
if (!ESleepy && !(HSleepy & ~TIMEOUT)) if (!ESleepy && !(HSleepy & ~TIMEOUT))
HSleepy &= ~TIMEOUT; /* clear timeout bits */ HSleepy &= ~TIMEOUT; /* clear timeout bits */
return; break;
case AMULET_OF_FLYING: { case AMULET_OF_FLYING: {
boolean was_flying = !!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); setworn((struct obj *) 0, W_AMUL);
off_msg(amul);
early_off_msg = TRUE;
float_vs_flight(); /* probably not needed here */ float_vs_flight(); /* probably not needed here */
if (was_flying && !Flying) { if (was_flying && !Flying) {
makeknown(AMULET_OF_FLYING);
disp.botl = TRUE; /* status: 'Fly' Off */ disp.botl = TRUE; /* status: 'Fly' Off */
You("%s.", (is_pool_or_lava(u.ux, u.uy) You("%s.", (is_pool_or_lava(u.ux, u.uy)
|| Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) || Is_waterlevel(&u.uz) || Is_airlevel(&u.uz))
? "stop flying" ? "stop flying"
: "land"); : "land");
mkn = TRUE; /* makeknown(AMULET_OF_FLYING) */
spoteffects(TRUE); spoteffects(TRUE);
} }
break; break;
@@ -1107,7 +1153,12 @@ Amulet_off(void)
case AMULET_OF_YENDOR: case AMULET_OF_YENDOR:
break; break;
} }
setworn((struct obj *) 0, W_AMUL); 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; return;
} }
@@ -1198,7 +1249,9 @@ Ring_on(struct obj *obj)
case RIN_FREE_ACTION: case RIN_FREE_ACTION:
case RIN_SLOW_DIGESTION: case RIN_SLOW_DIGESTION:
case RIN_SUSTAIN_ABILITY: case RIN_SUSTAIN_ABILITY:
break;
case MEAT_RING: case MEAT_RING:
/* wearing a meat ring does not affect vegan conduct */
break; break;
case RIN_STEALTH: case RIN_STEALTH:
toggle_stealth(obj, oldprop, TRUE); toggle_stealth(obj, oldprop, TRUE);
@@ -1383,8 +1436,7 @@ Blindf_on(struct obj *otmp)
boolean already_blind = Blind, changed = FALSE; boolean already_blind = Blind, changed = FALSE;
/* blindfold might be wielded; release it for wearing */ /* 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); setworn(otmp, W_TOOL);
on_msg(otmp); on_msg(otmp);
@@ -1469,7 +1521,7 @@ set_wear(
if (!obj ? uleft != 0 : (obj == uleft)) if (!obj ? uleft != 0 : (obj == uleft))
(void) Ring_on(uleft); (void) Ring_on(uleft);
if (!obj ? uamul != 0 : (obj == uamul)) if (!obj ? uamul != 0 : (obj == uamul))
(void) Amulet_on(); (void) Amulet_on(uamul);
if (!obj ? uarmu != 0 : (obj == uarmu)) if (!obj ? uarmu != 0 : (obj == uarmu))
(void) Shirt_on(); (void) Shirt_on();
@@ -1606,8 +1658,8 @@ cancel_don(void)
/* called by steal() during theft from hero; interrupt donning/doffing */ /* called by steal() during theft from hero; interrupt donning/doffing */
int int
stop_donning(struct obj *stolenobj) /* no message if stolenobj is already stop_donning(
being doffing */ struct obj *stolenobj) /* no mesg if stolenobj is already being doffed */
{ {
char buf[BUFSZ]; char buf[BUFSZ];
struct obj *otmp; struct obj *otmp;
@@ -1651,8 +1703,9 @@ static NEARDATA int Narmorpieces, Naccessories;
/* assign values to Narmorpieces and Naccessories */ /* assign values to Narmorpieces and Naccessories */
staticfn void staticfn void
count_worn_stuff(struct obj **which, /* caller wants this when count is 1 */ count_worn_stuff(
boolean accessorizing) struct obj **which, /* caller wants this when count is 1 */
boolean accessorizing)
{ {
struct obj *otmp; struct obj *otmp;
@@ -1736,12 +1789,12 @@ armor_or_accessory_off(struct obj *obj)
off_msg(obj); off_msg(obj);
Ring_off(obj); Ring_off(obj);
} else if (obj == uamul) { } else if (obj == uamul) {
Amulet_off(); Amulet_off(); /* does its own off_msg */
off_msg(obj);
} else if (obj == ublindf) { } else if (obj == ublindf) {
Blindf_off(obj); /* does its own off_msg */ Blindf_off(obj); /* does its own off_msg */
} else { } else {
impossible("removing strange accessory?"); impossible("removing strange accessory: %s",
safe_typename(obj->otyp));
if (obj->owornmask) if (obj->owornmask)
remove_worn_item(obj, FALSE); 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) */ You("have no feet..."); /* not body_part(FOOT) */
err++; err++;
} else if (Upolyd && gy.youmonst.data->mlet == S_CENTAUR) { } else if (Upolyd && gy.youmonst.data->mlet == S_CENTAUR) {
/* break_armor() pushes boots off for centaurs, /* break_armor() pushes boots off for centaurs, so don't let
so don't let dowear() put them back on... */ dowear() put them back on;
makeplural(body_part(FOOT)) would yield "rear hooves" here,
which sounds odd, so use hard-coded "hooves" */
if (noisy) if (noisy)
You("have too many hooves to wear %s.", You("have too many hooves to wear %s.", c_boots);
c_boots); /* makeplural(body_part(FOOT)) yields
"rear hooves" which sounds odd */
err++; err++;
} else if (u.utrap } else if (u.utrap
&& (u.utraptype == TT_BEARTRAP || u.utraptype == TT_INFLOOR && (u.utraptype == TT_BEARTRAP || u.utraptype == TT_INFLOOR
@@ -2114,7 +2167,7 @@ staticfn int
accessory_or_armor_on(struct obj *obj) accessory_or_armor_on(struct obj *obj)
{ {
long mask = 0L; long mask = 0L;
boolean armor, ring, eyewear; boolean armor, ring, amulet, eyewear;
if (obj->owornmask & (W_ACCESSORY | W_ARMOR)) { if (obj->owornmask & (W_ACCESSORY | W_ARMOR)) {
already_wearing(c_that_); already_wearing(c_that_);
@@ -2122,6 +2175,7 @@ accessory_or_armor_on(struct obj *obj)
} }
armor = (obj->oclass == ARMOR_CLASS); armor = (obj->oclass == ARMOR_CLASS);
ring = (obj->oclass == RING_CLASS || obj->otyp == MEAT_RING); ring = (obj->oclass == RING_CLASS || obj->otyp == MEAT_RING);
amulet = (obj->oclass == AMULET_CLASS);
eyewear = (obj->otyp == BLINDFOLD || obj->otyp == TOWEL eyewear = (obj->otyp == BLINDFOLD || obj->otyp == TOWEL
|| obj->otyp == LENSES); || obj->otyp == LENSES);
/* checks which are performed prior to actually touching the item */ /* 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; return res ? ECMD_TIME : ECMD_OK;
} }
} }
} else if (obj->oclass == AMULET_CLASS) { } else if (amulet) {
if (uamul) { if (uamul) {
already_wearing("an amulet"); already_wearing("an amulet");
return ECMD_OK; return ECMD_OK;
@@ -2306,26 +2360,24 @@ accessory_or_armor_on(struct obj *obj)
} }
svc.context.takeoff.mask = svc.context.takeoff.what = 0L; svc.context.takeoff.mask = svc.context.takeoff.what = 0L;
} else { /* not armor */ } else { /* not armor */
boolean give_feedback = FALSE;
/* [releasing wielded accessory handled in Xxx_on()] */
if (ring) { if (ring) {
/* Ring_on() expects ring to already be worn as uleft or uright */
setworn(obj, mask); setworn(obj, mask);
Ring_on(obj); Ring_on(obj);
give_feedback = TRUE; /* is_worn(): 'obj' will always be worn here except when putting
} else if (obj->oclass == AMULET_CLASS) { on a ring of levitation while at a sink location */
setworn(obj, W_AMUL); if (is_worn(obj))
Amulet_on(); on_msg(obj);
/* no feedback here if amulet of change got used up */ } else if (amulet) {
give_feedback = (uamul != 0); /* setworn() and on_msg() handled by Amulet_on() */
Amulet_on(obj);
} else if (eyewear) { } else if (eyewear) {
/* setworn() handled by Blindf_on() */ /* setworn() and on_msg() handled by Blindf_on() */
Blindf_on(obj); 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; return ECMD_TIME;
} }