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

View File

@@ -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;
}