3271 lines
107 KiB
C
3271 lines
107 KiB
C
/* NetHack 3.7 do_wear.c $NHDT-Date: 1702017586 2023/12/08 06:39:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.175 $ */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/*-Copyright (c) Robert Patrick Rankin, 2012. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
|
|
static NEARDATA const char see_yourself[] = "see yourself";
|
|
static NEARDATA const char unknown_type[] = "Unknown type of %s (%d)";
|
|
static NEARDATA const char c_armor[] = "armor", c_suit[] = "suit",
|
|
c_shirt[] = "shirt", c_cloak[] = "cloak",
|
|
c_gloves[] = "gloves", c_boots[] = "boots",
|
|
c_helmet[] = "helmet", c_shield[] = "shield",
|
|
c_weapon[] = "weapon", c_sword[] = "sword",
|
|
c_axe[] = "axe", c_that_[] = "that";
|
|
|
|
static NEARDATA const long takeoff_order[] = {
|
|
WORN_BLINDF, W_WEP, WORN_SHIELD, WORN_GLOVES, LEFT_RING,
|
|
RIGHT_RING, WORN_CLOAK, WORN_HELMET, WORN_AMUL, WORN_ARMOR,
|
|
WORN_SHIRT, WORN_BOOTS, W_SWAPWEP, W_QUIVER, 0L
|
|
};
|
|
|
|
static void on_msg(struct obj *);
|
|
static void toggle_stealth(struct obj *, long, boolean);
|
|
static int Armor_on(void);
|
|
/* int Boots_on(void); -- moved to extern.h */
|
|
static int Cloak_on(void);
|
|
static int Helmet_on(void);
|
|
static int Gloves_on(void);
|
|
static int Shield_on(void);
|
|
static int Shirt_on(void);
|
|
static void dragon_armor_handling(struct obj *, boolean, boolean);
|
|
static void Amulet_on(void);
|
|
static void learnring(struct obj *, boolean);
|
|
static void adjust_attrib(struct obj *, int, int);
|
|
static void Ring_off_or_gone(struct obj *, boolean);
|
|
static int select_off(struct obj *);
|
|
static struct obj *do_takeoff(void);
|
|
static int take_off(void);
|
|
static int menu_remarm(int);
|
|
static void wornarm_destroyed(struct obj *);
|
|
static void count_worn_stuff(struct obj **, boolean);
|
|
static int armor_or_accessory_off(struct obj *);
|
|
static int accessory_or_armor_on(struct obj *);
|
|
static void already_wearing(const char *);
|
|
static void already_wearing2(const char *, const char *);
|
|
static int equip_ok(struct obj *, boolean, boolean);
|
|
static int puton_ok(struct obj *);
|
|
static int remove_ok(struct obj *);
|
|
static int wear_ok(struct obj *);
|
|
static int takeoff_ok(struct obj *);
|
|
/* maybe_destroy_armor() may return NULL */
|
|
static struct obj *maybe_destroy_armor(struct obj *, struct obj *,
|
|
boolean *) NONNULLARG3;
|
|
|
|
/* plural "fingers" or optionally "gloves" */
|
|
const char *
|
|
fingers_or_gloves(boolean check_gloves)
|
|
{
|
|
return ((check_gloves && uarmg)
|
|
? gloves_simple_name(uarmg) /* "gloves" or "gauntlets" */
|
|
: makeplural(body_part(FINGER))); /* "fingers" */
|
|
}
|
|
|
|
void
|
|
off_msg(struct obj *otmp)
|
|
{
|
|
if (flags.verbose)
|
|
You("were wearing %s.", doname(otmp));
|
|
}
|
|
|
|
/* for items that involve no delay */
|
|
static void
|
|
on_msg(struct obj *otmp)
|
|
{
|
|
if (flags.verbose) {
|
|
char how[BUFSZ];
|
|
/* call xname() before obj_is_pname(); formatting obj's name
|
|
might set obj->dknown and that affects the pname test */
|
|
const char *otmp_name = xname(otmp);
|
|
|
|
how[0] = '\0';
|
|
if (otmp->otyp == TOWEL)
|
|
Sprintf(how, " around your %s", body_part(HEAD));
|
|
You("are now wearing %s%s.",
|
|
obj_is_pname(otmp) ? the(otmp_name) : an(otmp_name), how);
|
|
}
|
|
}
|
|
|
|
/* putting on or taking off an item which confers stealth;
|
|
give feedback and discover it iff stealth state is changing;
|
|
stealth is blocked by riding unless hero+steed fly (handled with
|
|
BStealth by mount and dismount routines) */
|
|
static
|
|
void
|
|
toggle_stealth(
|
|
struct obj *obj,
|
|
long oldprop, /* prop[].extrinsic, with obj->owornmask pre-stripped */
|
|
boolean on)
|
|
{
|
|
if (on ? gi.initial_don : gc.context.takeoff.cancelled_don)
|
|
return;
|
|
|
|
if (!oldprop /* extrinsic stealth from something else */
|
|
&& !HStealth /* intrinsic stealth */
|
|
&& !BStealth) { /* stealth blocked by something */
|
|
if (obj->otyp == RIN_STEALTH)
|
|
learnring(obj, TRUE);
|
|
else /* discover elven cloak or elven boots */
|
|
makeknown(obj->otyp);
|
|
|
|
if (on) {
|
|
if (!is_boots(obj))
|
|
You("move very quietly.");
|
|
else if (Levitation || Flying)
|
|
You("float imperceptibly.");
|
|
else
|
|
You("walk very quietly.");
|
|
} else {
|
|
boolean riding = (u.usteed != NULL);
|
|
|
|
You("%s%s are noisy.", riding ? "and " : "sure",
|
|
riding ? x_monnam(u.usteed, ARTICLE_YOUR, (char *) NULL,
|
|
(SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION),
|
|
FALSE)
|
|
: "");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* putting on or taking off an item which confers displacement, or gaining
|
|
or losing timed displacement after eating a displacer beast corpse or tin;
|
|
give feedback and discover it iff displacement state is changing *and*
|
|
hero is able to see self (or sense monsters); for timed, 'obj' is Null
|
|
and this is only called for the message */
|
|
void
|
|
toggle_displacement(
|
|
struct obj *obj,
|
|
long oldprop, /* prop[].extrinsic, with obj->owornmask
|
|
stripped by caller */
|
|
boolean on)
|
|
{
|
|
if (on ? gi.initial_don : gc.context.takeoff.cancelled_don)
|
|
return;
|
|
|
|
if (!oldprop /* extrinsic displacement from something else */
|
|
&& !(u.uprops[DISPLACED].intrinsic) /* timed, from eating */
|
|
&& !(u.uprops[DISPLACED].blocked) /* (theoretical) */
|
|
/* we don't use canseeself() here because it augments vision
|
|
with touch, which isn't appropriate for deciding whether
|
|
we'll notice that monsters have trouble spotting the hero */
|
|
&& ((!Blind /* see anything */
|
|
&& !u.uswallow /* see surroundings */
|
|
&& !Invisible) /* see self */
|
|
/* actively sensing nearby monsters via telepathy or extended
|
|
monster detection overrides vision considerations because
|
|
hero also senses self in this situation */
|
|
|| (Unblind_telepat
|
|
|| (Blind_telepat && Blind)
|
|
|| Detect_monsters))) {
|
|
if (obj)
|
|
makeknown(obj->otyp);
|
|
|
|
You_feel("that monsters%s have difficulty pinpointing your location.",
|
|
on ? "" : " no longer");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The Type_on() functions should be called *after* setworn().
|
|
* The Type_off() functions call setworn() themselves.
|
|
* [Blindf_on() is an exception and calls setworn() itself.]
|
|
*/
|
|
|
|
int
|
|
Boots_on(void)
|
|
{
|
|
long oldprop =
|
|
u.uprops[objects[uarmf->otyp].oc_oprop].extrinsic & ~WORN_BOOTS;
|
|
|
|
switch (uarmf->otyp) {
|
|
case LOW_BOOTS:
|
|
case IRON_SHOES:
|
|
case HIGH_BOOTS:
|
|
case JUMPING_BOOTS:
|
|
case KICKING_BOOTS:
|
|
break;
|
|
case WATER_WALKING_BOOTS:
|
|
if (u.uinwater)
|
|
spoteffects(TRUE);
|
|
/* (we don't need a lava check here since boots can't be
|
|
put on while feet are stuck) */
|
|
break;
|
|
case SPEED_BOOTS:
|
|
/* Speed boots are still better than intrinsic speed, */
|
|
/* though not better than potion speed */
|
|
if (!oldprop && !(HFast & TIMEOUT)) {
|
|
makeknown(uarmf->otyp);
|
|
You_feel("yourself speed up%s.",
|
|
(oldprop || HFast) ? " a bit more" : "");
|
|
}
|
|
break;
|
|
case ELVEN_BOOTS:
|
|
toggle_stealth(uarmf, oldprop, TRUE);
|
|
break;
|
|
case FUMBLE_BOOTS:
|
|
if (!oldprop && !(HFumbling & ~TIMEOUT))
|
|
incr_itimeout(&HFumbling, rnd(20));
|
|
break;
|
|
case LEVITATION_BOOTS:
|
|
if (!oldprop && !HLevitation && !(BLevitation & FROMOUTSIDE)) {
|
|
uarmf->known = 1; /* might come off if putting on over a sink,
|
|
* so uarmf could be Null below; status line
|
|
* gets updated during brief interval they're
|
|
* worn so hero and player learn enchantment */
|
|
disp.botl = TRUE; /* status hilites might mark AC changed */
|
|
makeknown(uarmf->otyp);
|
|
float_up();
|
|
if (Levitation)
|
|
spoteffects(FALSE); /* for sink effect */
|
|
} else {
|
|
float_vs_flight(); /* maybe toggle BFlying's I_SPECIAL */
|
|
}
|
|
break;
|
|
default:
|
|
impossible(unknown_type, c_boots, uarmf->otyp);
|
|
}
|
|
/* uarmf could be Null here (levitation boots put on over a sink) */
|
|
if (uarmf && !uarmf->known) {
|
|
uarmf->known = 1; /* boots' +/- evident because of status line AC */
|
|
update_inventory();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Boots_off(void)
|
|
{
|
|
struct obj *otmp = uarmf;
|
|
int otyp = otmp->otyp;
|
|
long oldprop = u.uprops[objects[otyp].oc_oprop].extrinsic & ~WORN_BOOTS;
|
|
|
|
gc.context.takeoff.mask &= ~W_ARMF;
|
|
/* For levitation, float_down() returns if Levitation, so we
|
|
* must do a setworn() _before_ the levitation case.
|
|
*/
|
|
setworn((struct obj *) 0, W_ARMF);
|
|
switch (otyp) {
|
|
case SPEED_BOOTS:
|
|
if (!Very_fast && !gc.context.takeoff.cancelled_don) {
|
|
makeknown(otyp);
|
|
You_feel("yourself slow down%s.", Fast ? " a bit" : "");
|
|
}
|
|
break;
|
|
case WATER_WALKING_BOOTS:
|
|
/* check for lava since fireproofed boots make it viable */
|
|
if ((is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy))
|
|
&& !Levitation && !Flying
|
|
&& !(is_clinger(gy.youmonst.data) && has_ceiling(&u.uz))
|
|
&& !gc.context.takeoff.cancelled_don
|
|
/* avoid recursive call to lava_effects() */
|
|
&& !iflags.in_lava_effects) {
|
|
/* make boots known in case you survive the drowning */
|
|
makeknown(otyp);
|
|
spoteffects(TRUE);
|
|
}
|
|
break;
|
|
case ELVEN_BOOTS:
|
|
toggle_stealth(otmp, oldprop, FALSE);
|
|
break;
|
|
case FUMBLE_BOOTS:
|
|
if (!oldprop && !(HFumbling & ~TIMEOUT))
|
|
HFumbling = EFumbling = 0;
|
|
break;
|
|
case LEVITATION_BOOTS:
|
|
if (!oldprop && !HLevitation && !(BLevitation & FROMOUTSIDE)
|
|
&& !gc.context.takeoff.cancelled_don) {
|
|
/* lava_effects() sets in_lava_effects and calls Boots_off()
|
|
so hero is already in midst of floating down */
|
|
if (!iflags.in_lava_effects)
|
|
(void) float_down(0L, 0L);
|
|
makeknown(otyp);
|
|
} else {
|
|
float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
|
|
}
|
|
break;
|
|
case LOW_BOOTS:
|
|
case IRON_SHOES:
|
|
case HIGH_BOOTS:
|
|
case JUMPING_BOOTS:
|
|
case KICKING_BOOTS:
|
|
break;
|
|
default:
|
|
impossible(unknown_type, c_boots, otyp);
|
|
}
|
|
gc.context.takeoff.cancelled_don = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
Cloak_on(void)
|
|
{
|
|
long oldprop =
|
|
u.uprops[objects[uarmc->otyp].oc_oprop].extrinsic & ~WORN_CLOAK;
|
|
|
|
switch (uarmc->otyp) {
|
|
case ORCISH_CLOAK:
|
|
case DWARVISH_CLOAK:
|
|
case CLOAK_OF_MAGIC_RESISTANCE:
|
|
case ROBE:
|
|
case LEATHER_CLOAK:
|
|
break;
|
|
case CLOAK_OF_PROTECTION:
|
|
makeknown(uarmc->otyp);
|
|
break;
|
|
case ELVEN_CLOAK:
|
|
toggle_stealth(uarmc, oldprop, TRUE);
|
|
break;
|
|
case CLOAK_OF_DISPLACEMENT:
|
|
toggle_displacement(uarmc, oldprop, TRUE);
|
|
break;
|
|
case MUMMY_WRAPPING:
|
|
/* Note: it's already being worn, so we have to cheat here. */
|
|
if ((HInvis || EInvis) && !Blind) {
|
|
newsym(u.ux, u.uy);
|
|
You("can %s!", See_invisible ? "no longer see through yourself"
|
|
: see_yourself);
|
|
}
|
|
break;
|
|
case CLOAK_OF_INVISIBILITY:
|
|
/* since cloak of invisibility was worn, we know mummy wrapping
|
|
wasn't, so no need to check `oldprop' against blocked */
|
|
if (!oldprop && !HInvis && !Blind) {
|
|
makeknown(uarmc->otyp);
|
|
newsym(u.ux, u.uy);
|
|
pline("Suddenly you can%s yourself.",
|
|
See_invisible ? " see through" : "not see");
|
|
}
|
|
break;
|
|
case OILSKIN_CLOAK:
|
|
pline("%s very tightly.", Tobjnam(uarmc, "fit"));
|
|
break;
|
|
/* Alchemy smock gives poison _and_ acid resistance */
|
|
case ALCHEMY_SMOCK:
|
|
EAcid_resistance |= WORN_CLOAK;
|
|
break;
|
|
default:
|
|
impossible(unknown_type, c_cloak, uarmc->otyp);
|
|
}
|
|
if (uarmc && !uarmc->known) { /* no known instance of !uarmc here */
|
|
uarmc->known = 1; /* cloak's +/- evident because of status line AC */
|
|
update_inventory();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Cloak_off(void)
|
|
{
|
|
struct obj *otmp = uarmc;
|
|
int otyp = otmp->otyp;
|
|
long oldprop = u.uprops[objects[otyp].oc_oprop].extrinsic & ~WORN_CLOAK;
|
|
|
|
gc.context.takeoff.mask &= ~W_ARMC;
|
|
/* For mummy wrapping, taking it off first resets `Invisible'. */
|
|
setworn((struct obj *) 0, W_ARMC);
|
|
switch (otyp) {
|
|
case ORCISH_CLOAK:
|
|
case DWARVISH_CLOAK:
|
|
case CLOAK_OF_PROTECTION:
|
|
case CLOAK_OF_MAGIC_RESISTANCE:
|
|
case OILSKIN_CLOAK:
|
|
case ROBE:
|
|
case LEATHER_CLOAK:
|
|
break;
|
|
case ELVEN_CLOAK:
|
|
toggle_stealth(otmp, oldprop, FALSE);
|
|
break;
|
|
case CLOAK_OF_DISPLACEMENT:
|
|
toggle_displacement(otmp, oldprop, FALSE);
|
|
break;
|
|
case MUMMY_WRAPPING:
|
|
if (Invis && !Blind) {
|
|
newsym(u.ux, u.uy);
|
|
You("can %s.", See_invisible ? "see through yourself"
|
|
: "no longer see yourself");
|
|
}
|
|
break;
|
|
case CLOAK_OF_INVISIBILITY:
|
|
if (!oldprop && !HInvis && !Blind) {
|
|
makeknown(CLOAK_OF_INVISIBILITY);
|
|
newsym(u.ux, u.uy);
|
|
pline("Suddenly you can %s.",
|
|
See_invisible ? "no longer see through yourself"
|
|
: see_yourself);
|
|
}
|
|
break;
|
|
/* Alchemy smock gives poison _and_ acid resistance */
|
|
case ALCHEMY_SMOCK:
|
|
EAcid_resistance &= ~WORN_CLOAK;
|
|
break;
|
|
default:
|
|
impossible(unknown_type, c_cloak, otyp);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
Helmet_on(void)
|
|
{
|
|
switch (uarmh->otyp) {
|
|
case FEDORA:
|
|
case HELMET:
|
|
case DENTED_POT:
|
|
case ELVEN_LEATHER_HELM:
|
|
case DWARVISH_IRON_HELM:
|
|
case ORCISH_HELM:
|
|
case HELM_OF_TELEPATHY:
|
|
break;
|
|
case HELM_OF_CAUTION:
|
|
see_monsters();
|
|
break;
|
|
case HELM_OF_BRILLIANCE:
|
|
adj_abon(uarmh, uarmh->spe);
|
|
break;
|
|
case CORNUTHAUM:
|
|
/* people think marked wizards know what they're talking about,
|
|
but it takes trained arrogance to pull it off, and the actual
|
|
enchantment of the hat is irrelevant */
|
|
ABON(A_CHA) += (Role_if(PM_WIZARD) ? 1 : -1);
|
|
disp.botl = TRUE;
|
|
makeknown(uarmh->otyp);
|
|
break;
|
|
case HELM_OF_OPPOSITE_ALIGNMENT:
|
|
uarmh->known = 1; /* do this here because uarmh could get cleared */
|
|
/* changing alignment can toggle off active artifact properties,
|
|
including levitation; uarmh could get dropped or destroyed here
|
|
by hero falling onto a polymorph trap or into water (emergency
|
|
disrobe) or maybe lava (probably not, helm isn't 'organic') */
|
|
uchangealign((u.ualign.type != A_NEUTRAL)
|
|
? -u.ualign.type
|
|
: (uarmh->o_id % 2) ? A_CHAOTIC : A_LAWFUL,
|
|
A_CG_HELM_ON);
|
|
/* makeknown(HELM_OF_OPPOSITE_ALIGNMENT); -- below, after Tobjnam() */
|
|
/*FALLTHRU*/
|
|
case DUNCE_CAP:
|
|
if (uarmh && !uarmh->cursed) {
|
|
if (Blind)
|
|
pline("%s for a moment.", Tobjnam(uarmh, "vibrate"));
|
|
else
|
|
pline("%s %s for a moment.", Tobjnam(uarmh, "glow"),
|
|
hcolor(NH_BLACK));
|
|
curse(uarmh);
|
|
/* curse() doesn't touch bknown so doesn't update persistent
|
|
inventory; do so now [set_bknown() calls update_inventory()] */
|
|
if (Blind)
|
|
set_bknown(uarmh, 0); /* lose bknown if previously set */
|
|
else if (Role_if(PM_CLERIC))
|
|
set_bknown(uarmh, 1); /* (bknown should already be set) */
|
|
else if (uarmh->bknown)
|
|
update_inventory(); /* keep bknown as-is; display the curse */
|
|
}
|
|
disp.botl = TRUE; /* reveal new alignment or INT & WIS */
|
|
if (Hallucination) {
|
|
pline("My brain hurts!"); /* Monty Python's Flying Circus */
|
|
} else if (uarmh && uarmh->otyp == DUNCE_CAP) {
|
|
You_feel("%s.", /* track INT change; ignore WIS */
|
|
ACURR(A_INT)
|
|
<= (ABASE(A_INT) + ABON(A_INT) + ATEMP(A_INT))
|
|
? "like sitting in a corner"
|
|
: "giddy");
|
|
} else {
|
|
/* [message formerly given here moved to uchangealign()] */
|
|
makeknown(HELM_OF_OPPOSITE_ALIGNMENT);
|
|
}
|
|
break;
|
|
default:
|
|
impossible(unknown_type, c_helmet, uarmh->otyp);
|
|
}
|
|
/* uarmh could be Null due to uchangealign() */
|
|
if (uarmh && !uarmh->known) {
|
|
uarmh->known = 1; /* helmet's +/- evident because of status line AC */
|
|
update_inventory();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Helmet_off(void)
|
|
{
|
|
gc.context.takeoff.mask &= ~W_ARMH;
|
|
|
|
switch (uarmh->otyp) {
|
|
case FEDORA:
|
|
case HELMET:
|
|
case DENTED_POT:
|
|
case ELVEN_LEATHER_HELM:
|
|
case DWARVISH_IRON_HELM:
|
|
case ORCISH_HELM:
|
|
break;
|
|
case DUNCE_CAP:
|
|
disp.botl = TRUE;
|
|
break;
|
|
case CORNUTHAUM:
|
|
if (!gc.context.takeoff.cancelled_don) {
|
|
ABON(A_CHA) += (Role_if(PM_WIZARD) ? -1 : 1);
|
|
disp.botl = TRUE;
|
|
}
|
|
break;
|
|
case HELM_OF_TELEPATHY:
|
|
case HELM_OF_CAUTION:
|
|
/* need to update ability before calling see_monsters() */
|
|
setworn((struct obj *) 0, W_ARMH);
|
|
see_monsters();
|
|
return 0;
|
|
case HELM_OF_BRILLIANCE:
|
|
if (!gc.context.takeoff.cancelled_don)
|
|
adj_abon(uarmh, -uarmh->spe);
|
|
break;
|
|
case HELM_OF_OPPOSITE_ALIGNMENT:
|
|
/* changing alignment can toggle off active artifact
|
|
properties, including levitation; uarmh could get
|
|
dropped or destroyed here */
|
|
uchangealign(u.ualignbase[A_CURRENT], A_CG_HELM_OFF);
|
|
break;
|
|
default:
|
|
impossible(unknown_type, c_helmet, uarmh->otyp);
|
|
}
|
|
setworn((struct obj *) 0, W_ARMH);
|
|
gc.context.takeoff.cancelled_don = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
/* hard helms provide better protection against falling rocks */
|
|
boolean
|
|
hard_helmet(struct obj *obj)
|
|
{
|
|
if (!obj || !is_helmet(obj))
|
|
return FALSE;
|
|
return (is_metallic(obj) || is_crackable(obj)) ? TRUE : FALSE;
|
|
}
|
|
|
|
static int
|
|
Gloves_on(void)
|
|
{
|
|
long oldprop =
|
|
u.uprops[objects[uarmg->otyp].oc_oprop].extrinsic & ~WORN_GLOVES;
|
|
|
|
switch (uarmg->otyp) {
|
|
case LEATHER_GLOVES:
|
|
break;
|
|
case GAUNTLETS_OF_FUMBLING:
|
|
if (!oldprop && !(HFumbling & ~TIMEOUT))
|
|
incr_itimeout(&HFumbling, rnd(20));
|
|
break;
|
|
case GAUNTLETS_OF_POWER:
|
|
makeknown(uarmg->otyp);
|
|
disp.botl = TRUE; /* taken care of in attrib.c */
|
|
break;
|
|
case GAUNTLETS_OF_DEXTERITY:
|
|
adj_abon(uarmg, uarmg->spe);
|
|
break;
|
|
default:
|
|
impossible(unknown_type, c_gloves, uarmg->otyp);
|
|
}
|
|
if (!uarmg->known) {
|
|
uarmg->known = 1; /* gloves' +/- evident because of status line AC */
|
|
update_inventory();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* check for wielding cockatrice corpse after taking off gloves or yellow
|
|
dragon scales/mail or having temporary stoning resistance time out */
|
|
void
|
|
wielding_corpse(
|
|
struct obj *obj, /* uwep, potentially a wielded cockatrice corpse */
|
|
struct obj *how, /* gloves or dragon armor or Null (resist timeout) */
|
|
boolean voluntary) /* True: taking protective armor off on purpose */
|
|
{
|
|
if (!obj || obj->otyp != CORPSE || uarmg)
|
|
return;
|
|
/* note: can't dual-wield with non-weapons/weapon-tools so u.twoweap
|
|
will always be false if uswapwep happens to be a corpse */
|
|
if (obj != uwep && (obj != uswapwep || !u.twoweap))
|
|
return;
|
|
|
|
if (touch_petrifies(&mons[obj->corpsenm]) && !Stone_resistance) {
|
|
char kbuf[BUFSZ], hbuf[BUFSZ];
|
|
|
|
You("%s %s in your bare %s.",
|
|
(how && is_gloves(how)) ? "now wield" : "are wielding",
|
|
corpse_xname(obj, (const char *) 0, CXN_ARTICLE),
|
|
makeplural(body_part(HAND)));
|
|
/* "removing" ought to be "taking off" but that makes the
|
|
tombstone text more likely to be truncated */
|
|
if (how)
|
|
Sprintf(hbuf, "%s %s", voluntary ? "removing" : "losing",
|
|
is_gloves(how) ? gloves_simple_name(how)
|
|
: strsubst(simpleonames(how), "set of ", ""));
|
|
else
|
|
Strcpy(hbuf, "resistance timing out");
|
|
Snprintf(kbuf, sizeof kbuf, "%s while wielding %s",
|
|
hbuf, killer_xname(obj));
|
|
instapetrify(kbuf);
|
|
/* life-saved or got poly'd into a stone golem; can't continue
|
|
wielding cockatrice corpse unless have now become resistant */
|
|
if (!Stone_resistance)
|
|
remove_worn_item(obj, FALSE);
|
|
}
|
|
}
|
|
|
|
int
|
|
Gloves_off(void)
|
|
{
|
|
struct obj *gloves = uarmg; /* needed after uarmg has been set to Null */
|
|
long oldprop =
|
|
u.uprops[objects[uarmg->otyp].oc_oprop].extrinsic & ~WORN_GLOVES;
|
|
boolean on_purpose = !gc.context.mon_moving && !uarmg->in_use;
|
|
|
|
gc.context.takeoff.mask &= ~W_ARMG;
|
|
|
|
switch (uarmg->otyp) {
|
|
case LEATHER_GLOVES:
|
|
break;
|
|
case GAUNTLETS_OF_FUMBLING:
|
|
if (!oldprop && !(HFumbling & ~TIMEOUT))
|
|
HFumbling = EFumbling = 0;
|
|
break;
|
|
case GAUNTLETS_OF_POWER:
|
|
makeknown(uarmg->otyp);
|
|
disp.botl = TRUE; /* taken care of in attrib.c */
|
|
break;
|
|
case GAUNTLETS_OF_DEXTERITY:
|
|
if (!gc.context.takeoff.cancelled_don)
|
|
adj_abon(uarmg, -uarmg->spe);
|
|
break;
|
|
default:
|
|
impossible(unknown_type, c_gloves, uarmg->otyp);
|
|
}
|
|
setworn((struct obj *) 0, W_ARMG);
|
|
gc.context.takeoff.cancelled_don = FALSE;
|
|
(void) encumber_msg(); /* immediate feedback for GoP */
|
|
|
|
/* usually can't remove gloves when they're slippery but it can
|
|
be done by having them fall off (polymorph), stolen, or
|
|
destroyed (scroll, overenchantment, monster spell); if that
|
|
happens, 'cure' slippery fingers so that it doesn't transfer
|
|
from gloves to bare hands */
|
|
if (Glib)
|
|
make_glib(0); /* for update_inventory() */
|
|
|
|
/* prevent wielding cockatrice when not wearing gloves */
|
|
if (uwep && uwep->otyp == CORPSE)
|
|
wielding_corpse(uwep, gloves, on_purpose);
|
|
/* KMH -- ...or your secondary weapon when you're wielding it
|
|
[This case can't actually happen; twoweapon mode won't engage
|
|
if a corpse has been set up as either the primary or alternate
|
|
weapon. If it could happen and /both/ uwep and uswapwep could
|
|
be cockatrice corpses, life-saving for the first would need to
|
|
prevent the second from being fatal since conceptually they'd
|
|
be being touched simultaneously.] */
|
|
if (u.twoweap && uswapwep && uswapwep->otyp == CORPSE)
|
|
wielding_corpse(uswapwep, gloves, on_purpose);
|
|
|
|
if (condtests[bl_bareh].enabled)
|
|
disp.botl = TRUE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
Shield_on(void)
|
|
{
|
|
/* no shield currently requires special handling when put on, but we
|
|
keep this uncommented in case somebody adds a new one which does
|
|
[reflection is handled by setting u.uprops[REFLECTION].extrinsic
|
|
in setworn() called by armor_or_accessory_on() before Shield_on()] */
|
|
switch (uarms->otyp) {
|
|
case SMALL_SHIELD:
|
|
case ELVEN_SHIELD:
|
|
case URUK_HAI_SHIELD:
|
|
case ORCISH_SHIELD:
|
|
case DWARVISH_ROUNDSHIELD:
|
|
case LARGE_SHIELD:
|
|
case SHIELD_OF_REFLECTION:
|
|
break;
|
|
default:
|
|
impossible(unknown_type, c_shield, uarms->otyp);
|
|
}
|
|
if (!uarms->known) {
|
|
uarms->known = 1; /* shield's +/- evident because of status line AC */
|
|
update_inventory();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Shield_off(void)
|
|
{
|
|
gc.context.takeoff.mask &= ~W_ARMS;
|
|
|
|
/* no shield currently requires special handling when taken off, but we
|
|
keep this uncommented in case somebody adds a new one which does */
|
|
switch (uarms->otyp) {
|
|
case SMALL_SHIELD:
|
|
case ELVEN_SHIELD:
|
|
case URUK_HAI_SHIELD:
|
|
case ORCISH_SHIELD:
|
|
case DWARVISH_ROUNDSHIELD:
|
|
case LARGE_SHIELD:
|
|
case SHIELD_OF_REFLECTION:
|
|
break;
|
|
default:
|
|
impossible(unknown_type, c_shield, uarms->otyp);
|
|
}
|
|
|
|
setworn((struct obj *) 0, W_ARMS);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
Shirt_on(void)
|
|
{
|
|
/* no shirt currently requires special handling when put on, but we
|
|
keep this uncommented in case somebody adds a new one which does */
|
|
switch (uarmu->otyp) {
|
|
case HAWAIIAN_SHIRT:
|
|
case T_SHIRT:
|
|
break;
|
|
default:
|
|
impossible(unknown_type, c_shirt, uarmu->otyp);
|
|
}
|
|
if (!uarmu->known) {
|
|
uarmu->known = 1; /* shirt's +/- evident because of status line AC */
|
|
update_inventory();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Shirt_off(void)
|
|
{
|
|
gc.context.takeoff.mask &= ~W_ARMU;
|
|
|
|
/* no shirt currently requires special handling when taken off, but we
|
|
keep this uncommented in case somebody adds a new one which does */
|
|
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;
|
|
}
|
|
|
|
/* handle extra abilities for hero wearing dragon scale armor */
|
|
static void
|
|
dragon_armor_handling(
|
|
struct obj *otmp, /* armor being put on or taken off */
|
|
boolean puton, /* True: on, False: off */
|
|
boolean on_purpose) /* voluntary removal; not applicable for putting on */
|
|
{
|
|
if (!otmp)
|
|
return;
|
|
|
|
switch (otmp->otyp) {
|
|
/* grey: no extra effect */
|
|
/* silver: no extra effect */
|
|
case BLACK_DRAGON_SCALES:
|
|
case BLACK_DRAGON_SCALE_MAIL:
|
|
if (puton) {
|
|
EDrain_resistance |= W_ARM;
|
|
} else {
|
|
EDrain_resistance &= ~W_ARM;
|
|
}
|
|
break;
|
|
case BLUE_DRAGON_SCALES:
|
|
case BLUE_DRAGON_SCALE_MAIL:
|
|
if (puton) {
|
|
if (!Very_fast)
|
|
You("speed up%s.", Fast ? " a bit more" : "");
|
|
EFast |= W_ARM;
|
|
} else {
|
|
EFast &= ~W_ARM;
|
|
if (!Very_fast && !gc.context.takeoff.cancelled_don)
|
|
You("slow down.");
|
|
}
|
|
break;
|
|
case GREEN_DRAGON_SCALES:
|
|
case GREEN_DRAGON_SCALE_MAIL:
|
|
if (puton) {
|
|
ESick_resistance |= W_ARM;
|
|
} else {
|
|
ESick_resistance &= ~W_ARM;
|
|
}
|
|
break;
|
|
case RED_DRAGON_SCALES:
|
|
case RED_DRAGON_SCALE_MAIL:
|
|
if (puton) {
|
|
EInfravision |= W_ARM;
|
|
} else {
|
|
EInfravision &= ~W_ARM;
|
|
}
|
|
see_monsters();
|
|
break;
|
|
case GOLD_DRAGON_SCALES:
|
|
case GOLD_DRAGON_SCALE_MAIL:
|
|
(void) make_hallucinated((long) !puton,
|
|
gp.program_state.restoring ? FALSE : TRUE,
|
|
W_ARM);
|
|
break;
|
|
case ORANGE_DRAGON_SCALES:
|
|
case ORANGE_DRAGON_SCALE_MAIL:
|
|
if (puton) {
|
|
Free_action |= W_ARM;
|
|
} else {
|
|
Free_action &= ~W_ARM;
|
|
}
|
|
break;
|
|
case YELLOW_DRAGON_SCALES:
|
|
case YELLOW_DRAGON_SCALE_MAIL:
|
|
if (puton) {
|
|
EStone_resistance |= W_ARM;
|
|
} else {
|
|
EStone_resistance &= ~W_ARM;
|
|
|
|
/* prevent wielding cockatrice after losing stoning resistance
|
|
when not wearing gloves; the uswapwep case is always a no-op */
|
|
wielding_corpse(uwep, otmp, on_purpose);
|
|
wielding_corpse(uswapwep, otmp, on_purpose);
|
|
}
|
|
break;
|
|
case WHITE_DRAGON_SCALES:
|
|
case WHITE_DRAGON_SCALE_MAIL:
|
|
if (puton) {
|
|
ESlow_digestion |= W_ARM;
|
|
} else {
|
|
ESlow_digestion &= ~W_ARM;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int
|
|
Armor_on(void)
|
|
{
|
|
if (!uarm) /* no known instances of !uarm here but play it safe */
|
|
return 0;
|
|
if (!uarm->known) {
|
|
uarm->known = 1; /* suit's +/- evident because of status line AC */
|
|
update_inventory();
|
|
}
|
|
dragon_armor_handling(uarm, TRUE, TRUE);
|
|
/* gold DSM requires extra handling since it emits light when worn;
|
|
do that after the special armor handling */
|
|
if (artifact_light(uarm) && !uarm->lamplit) {
|
|
begin_burn(uarm, FALSE);
|
|
if (!Blind)
|
|
pline("%s %s to shine %s!",
|
|
Yname2(uarm), otense(uarm, "begin"),
|
|
arti_light_description(uarm));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Armor_off(void)
|
|
{
|
|
struct obj *otmp = uarm;
|
|
boolean was_arti_light = otmp && otmp->lamplit && artifact_light(otmp);
|
|
|
|
gc.context.takeoff.mask &= ~W_ARM;
|
|
setworn((struct obj *) 0, W_ARM);
|
|
gc.context.takeoff.cancelled_don = FALSE;
|
|
|
|
/* taking off yellow dragon scales/mail might be fatal; arti_light
|
|
comes from gold dragon scales/mail so they don't overlap, but
|
|
conceptually the non-fatal change should be done before the
|
|
potentially fatal change in case the latter results in bones */
|
|
if (was_arti_light && !artifact_light(otmp)) {
|
|
end_burn(otmp, FALSE);
|
|
if (!Blind)
|
|
pline("%s shining.", Tobjnam(otmp, "stop"));
|
|
}
|
|
dragon_armor_handling(otmp, FALSE, TRUE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* The gone functions differ from the off functions in that if you die from
|
|
* taking it off and have life saving, you still die. [Obsolete reference
|
|
* to lack of fire resistance being fatal in hell (nethack 3.0) and life
|
|
* saving putting a removed item back on to prevent that from immediately
|
|
* repeating.]
|
|
*/
|
|
int
|
|
Armor_gone(void)
|
|
{
|
|
struct obj *otmp = uarm;
|
|
boolean was_arti_light = otmp && otmp->lamplit && artifact_light(otmp);
|
|
|
|
gc.context.takeoff.mask &= ~W_ARM;
|
|
setnotworn(uarm);
|
|
gc.context.takeoff.cancelled_don = FALSE;
|
|
|
|
/* losing yellow dragon scales/mail might be fatal; arti_light
|
|
comes from gold dragon scales/mail so they don't overlap, but
|
|
conceptually the non-fatal change should be done before the
|
|
potentially fatal change in case the latter results in bones */
|
|
if (was_arti_light && !artifact_light(otmp)) {
|
|
end_burn(otmp, FALSE);
|
|
if (!Blind)
|
|
pline("%s shining.", Tobjnam(otmp, "stop"));
|
|
}
|
|
dragon_armor_handling(otmp, FALSE, FALSE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
Amulet_on(void)
|
|
{
|
|
/* 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);
|
|
|
|
switch (uamul->otyp) {
|
|
case AMULET_OF_ESP:
|
|
case AMULET_OF_LIFE_SAVING:
|
|
case AMULET_VERSUS_POISON:
|
|
case AMULET_OF_REFLECTION:
|
|
case AMULET_OF_MAGICAL_BREATHING:
|
|
case FAKE_AMULET_OF_YENDOR:
|
|
break;
|
|
case AMULET_OF_UNCHANGING:
|
|
if (Slimed)
|
|
make_slimed(0L, (char *) 0);
|
|
break;
|
|
case AMULET_OF_CHANGE: {
|
|
int new_sex, orig_sex = poly_gender();
|
|
|
|
if (Unchanging)
|
|
break;
|
|
change_sex();
|
|
new_sex = poly_gender();
|
|
/* Don't use same message as polymorph */
|
|
if (new_sex != orig_sex) {
|
|
makeknown(AMULET_OF_CHANGE);
|
|
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.");
|
|
}
|
|
livelog_newform(FALSE, orig_sex, new_sex);
|
|
pline_The("amulet disintegrates!");
|
|
if (orig_sex == poly_gender() && uamul->dknown)
|
|
trycall(uamul);
|
|
useup(uamul);
|
|
break;
|
|
}
|
|
case AMULET_OF_STRANGULATION:
|
|
if (can_be_strangled(&gy.youmonst)) {
|
|
makeknown(AMULET_OF_STRANGULATION);
|
|
Strangled = 6L;
|
|
disp.botl = TRUE;
|
|
pline("It constricts your throat!");
|
|
}
|
|
break;
|
|
case AMULET_OF_RESTFUL_SLEEP: {
|
|
long newnap = (long) rnd(100), oldnap = (HSleepy & TIMEOUT);
|
|
|
|
/* avoid clobbering FROMOUTSIDE bit, which might have
|
|
gotten set by previously eating one of these amulets */
|
|
if (newnap < oldnap || oldnap == 0L)
|
|
HSleepy = (HSleepy & ~TIMEOUT) | newnap;
|
|
break;
|
|
}
|
|
case AMULET_OF_FLYING:
|
|
/* setworn() has already set extrinsic flying */
|
|
float_vs_flight(); /* block flying if levitating */
|
|
if (Flying) {
|
|
boolean already_flying;
|
|
|
|
/* to determine whether this flight is new we have to muck
|
|
about in the Flying intrinsic (actually extrinsic) */
|
|
EFlying &= ~W_AMUL;
|
|
already_flying = !!Flying;
|
|
EFlying |= W_AMUL;
|
|
|
|
if (!already_flying) {
|
|
makeknown(AMULET_OF_FLYING);
|
|
disp.botl = TRUE; /* status: 'Fly' On */
|
|
You("are now in flight.");
|
|
}
|
|
}
|
|
break;
|
|
case AMULET_OF_GUARDING:
|
|
makeknown(AMULET_OF_GUARDING);
|
|
find_ac();
|
|
break;
|
|
case AMULET_OF_YENDOR:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
Amulet_off(void)
|
|
{
|
|
gc.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);
|
|
see_monsters();
|
|
return;
|
|
case AMULET_OF_LIFE_SAVING:
|
|
case AMULET_VERSUS_POISON:
|
|
case AMULET_OF_REFLECTION:
|
|
case AMULET_OF_CHANGE:
|
|
case AMULET_OF_UNCHANGING:
|
|
case FAKE_AMULET_OF_YENDOR:
|
|
break;
|
|
case AMULET_OF_MAGICAL_BREATHING:
|
|
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"));
|
|
(void) drown();
|
|
}
|
|
return;
|
|
}
|
|
break;
|
|
case AMULET_OF_STRANGULATION:
|
|
if (Strangled) {
|
|
Strangled = 0L;
|
|
disp.botl = TRUE;
|
|
if (Breathless)
|
|
Your("%s is no longer constricted!", body_part(NECK));
|
|
else
|
|
You("can breathe more easily!");
|
|
}
|
|
break;
|
|
case AMULET_OF_RESTFUL_SLEEP:
|
|
setworn((struct obj *) 0, W_AMUL);
|
|
/* HSleepy = 0L; -- avoid clobbering FROMOUTSIDE bit */
|
|
if (!ESleepy && !(HSleepy & ~TIMEOUT))
|
|
HSleepy &= ~TIMEOUT; /* clear timeout bits */
|
|
return;
|
|
case AMULET_OF_FLYING: {
|
|
boolean was_flying = !!Flying;
|
|
|
|
/* remove amulet 'early' to determine whether Flying changes */
|
|
setworn((struct obj *) 0, W_AMUL);
|
|
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");
|
|
spoteffects(TRUE);
|
|
}
|
|
break;
|
|
}
|
|
case AMULET_OF_GUARDING:
|
|
find_ac();
|
|
break;
|
|
case AMULET_OF_YENDOR:
|
|
break;
|
|
}
|
|
setworn((struct obj *) 0, W_AMUL);
|
|
return;
|
|
}
|
|
|
|
/* handle ring discovery; comparable to learnwand() */
|
|
static void
|
|
learnring(struct obj *ring, boolean observed)
|
|
{
|
|
int ringtype = ring->otyp;
|
|
|
|
/* if effect was observable then we usually discover the type */
|
|
if (observed) {
|
|
/* if we already know the ring type which accomplishes this
|
|
effect (assumes there is at most one type for each effect),
|
|
mark this ring as having been seen (no need for makeknown);
|
|
otherwise if we have seen this ring, discover its type */
|
|
if (objects[ringtype].oc_name_known)
|
|
ring->dknown = 1;
|
|
else if (ring->dknown)
|
|
makeknown(ringtype);
|
|
#if 0 /* see learnwand() */
|
|
else
|
|
ring->eknown = 1;
|
|
#endif
|
|
}
|
|
|
|
/* make enchantment of charged ring known (might be +0) and update
|
|
perm invent window if we've seen this ring and know its type */
|
|
if (ring->dknown && objects[ringtype].oc_name_known) {
|
|
if (objects[ringtype].oc_charged)
|
|
ring->known = 1;
|
|
update_inventory();
|
|
}
|
|
}
|
|
|
|
static void
|
|
adjust_attrib(struct obj *obj, int which, int val)
|
|
{
|
|
int old_attrib;
|
|
boolean observable;
|
|
|
|
old_attrib = ACURR(which);
|
|
ABON(which) += val;
|
|
observable = (old_attrib != ACURR(which));
|
|
/* if didn't change, usually means ring is +0 but might
|
|
be because nonzero couldn't go below min or above max;
|
|
learn +0 enchantment if attribute value is not stuck
|
|
at a limit [and ring has been seen and its type is
|
|
already discovered, both handled by learnring()] */
|
|
if (observable || !extremeattr(which))
|
|
learnring(obj, observable);
|
|
disp.botl = TRUE;
|
|
}
|
|
|
|
void
|
|
Ring_on(struct obj *obj)
|
|
{
|
|
long oldprop = u.uprops[objects[obj->otyp].oc_oprop].extrinsic;
|
|
boolean observable;
|
|
|
|
/* make sure ring isn't wielded; can't use remove_worn_item()
|
|
here because it has already been set worn in a ring slot */
|
|
if (obj == uwep)
|
|
setuwep((struct obj *) 0);
|
|
else if (obj == uswapwep)
|
|
setuswapwep((struct obj *) 0);
|
|
else if (obj == uquiver)
|
|
setuqwep((struct obj *) 0);
|
|
|
|
/* only mask out W_RING when we don't have both
|
|
left and right rings of the same type */
|
|
if ((oldprop & W_RING) != W_RING)
|
|
oldprop &= ~W_RING;
|
|
|
|
switch (obj->otyp) {
|
|
case RIN_TELEPORTATION:
|
|
case RIN_REGENERATION:
|
|
case RIN_SEARCHING:
|
|
case RIN_HUNGER:
|
|
case RIN_AGGRAVATE_MONSTER:
|
|
case RIN_POISON_RESISTANCE:
|
|
case RIN_FIRE_RESISTANCE:
|
|
case RIN_COLD_RESISTANCE:
|
|
case RIN_SHOCK_RESISTANCE:
|
|
case RIN_CONFLICT:
|
|
case RIN_TELEPORT_CONTROL:
|
|
case RIN_POLYMORPH:
|
|
case RIN_POLYMORPH_CONTROL:
|
|
case RIN_FREE_ACTION:
|
|
case RIN_SLOW_DIGESTION:
|
|
case RIN_SUSTAIN_ABILITY:
|
|
case MEAT_RING:
|
|
break;
|
|
case RIN_STEALTH:
|
|
toggle_stealth(obj, oldprop, TRUE);
|
|
break;
|
|
case RIN_WARNING:
|
|
see_monsters();
|
|
break;
|
|
case RIN_SEE_INVISIBLE:
|
|
/* can now see invisible monsters */
|
|
set_mimic_blocking(); /* do special mimic handling */
|
|
see_monsters();
|
|
|
|
if (Invis && !oldprop && !HSee_invisible && !Blind) {
|
|
newsym(u.ux, u.uy);
|
|
pline("Suddenly you are transparent, but there!");
|
|
learnring(obj, TRUE);
|
|
}
|
|
break;
|
|
case RIN_INVISIBILITY:
|
|
if (!oldprop && !HInvis && !BInvis && !Blind) {
|
|
learnring(obj, TRUE);
|
|
newsym(u.ux, u.uy);
|
|
self_invis_message();
|
|
}
|
|
break;
|
|
case RIN_LEVITATION:
|
|
if (!oldprop && !HLevitation && !(BLevitation & FROMOUTSIDE)) {
|
|
float_up();
|
|
learnring(obj, TRUE);
|
|
if (Levitation)
|
|
spoteffects(FALSE); /* for sinks */
|
|
} else {
|
|
float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
|
|
}
|
|
break;
|
|
case RIN_GAIN_STRENGTH:
|
|
adjust_attrib(obj, A_STR, obj->spe);
|
|
break;
|
|
case RIN_GAIN_CONSTITUTION:
|
|
adjust_attrib(obj, A_CON, obj->spe);
|
|
break;
|
|
case RIN_ADORNMENT:
|
|
adjust_attrib(obj, A_CHA, obj->spe);
|
|
break;
|
|
case RIN_INCREASE_ACCURACY: /* KMH */
|
|
u.uhitinc += obj->spe;
|
|
break;
|
|
case RIN_INCREASE_DAMAGE:
|
|
u.udaminc += obj->spe;
|
|
break;
|
|
case RIN_PROTECTION_FROM_SHAPE_CHAN:
|
|
rescham();
|
|
break;
|
|
case RIN_PROTECTION:
|
|
/* usually learn enchantment and discover type;
|
|
won't happen if ring is unseen or if it's +0
|
|
and the type hasn't been discovered yet */
|
|
observable = (obj->spe != 0);
|
|
learnring(obj, observable);
|
|
if (obj->spe)
|
|
find_ac(); /* updates botl */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
Ring_off_or_gone(struct obj *obj, boolean gone)
|
|
{
|
|
long mask = (obj->owornmask & W_RING);
|
|
boolean observable;
|
|
|
|
gc.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);
|
|
else
|
|
setworn((struct obj *) 0, obj->owornmask);
|
|
|
|
switch (obj->otyp) {
|
|
case RIN_TELEPORTATION:
|
|
case RIN_REGENERATION:
|
|
case RIN_SEARCHING:
|
|
case RIN_HUNGER:
|
|
case RIN_AGGRAVATE_MONSTER:
|
|
case RIN_POISON_RESISTANCE:
|
|
case RIN_FIRE_RESISTANCE:
|
|
case RIN_COLD_RESISTANCE:
|
|
case RIN_SHOCK_RESISTANCE:
|
|
case RIN_CONFLICT:
|
|
case RIN_TELEPORT_CONTROL:
|
|
case RIN_POLYMORPH:
|
|
case RIN_POLYMORPH_CONTROL:
|
|
case RIN_FREE_ACTION:
|
|
case RIN_SLOW_DIGESTION:
|
|
case RIN_SUSTAIN_ABILITY:
|
|
case MEAT_RING:
|
|
break;
|
|
case RIN_STEALTH:
|
|
toggle_stealth(obj, (EStealth & ~mask), FALSE);
|
|
break;
|
|
case RIN_WARNING:
|
|
see_monsters();
|
|
break;
|
|
case RIN_SEE_INVISIBLE:
|
|
/* Make invisible monsters go away */
|
|
if (!See_invisible) {
|
|
set_mimic_blocking(); /* do special mimic handling */
|
|
see_monsters();
|
|
}
|
|
|
|
if (Invisible && !Blind) {
|
|
newsym(u.ux, u.uy);
|
|
pline("Suddenly you cannot see yourself.");
|
|
learnring(obj, TRUE);
|
|
}
|
|
break;
|
|
case RIN_INVISIBILITY:
|
|
if (!Invis && !BInvis && !Blind) {
|
|
newsym(u.ux, u.uy);
|
|
Your("body seems to unfade%s.",
|
|
See_invisible ? " completely" : "..");
|
|
learnring(obj, TRUE);
|
|
}
|
|
break;
|
|
case RIN_LEVITATION:
|
|
if (!(BLevitation & FROMOUTSIDE)) {
|
|
(void) float_down(0L, 0L);
|
|
if (!Levitation)
|
|
learnring(obj, TRUE);
|
|
} else {
|
|
float_vs_flight(); /* maybe toggle (BFlying & I_SPECIAL) */
|
|
}
|
|
break;
|
|
case RIN_GAIN_STRENGTH:
|
|
adjust_attrib(obj, A_STR, -obj->spe);
|
|
break;
|
|
case RIN_GAIN_CONSTITUTION:
|
|
adjust_attrib(obj, A_CON, -obj->spe);
|
|
break;
|
|
case RIN_ADORNMENT:
|
|
adjust_attrib(obj, A_CHA, -obj->spe);
|
|
break;
|
|
case RIN_INCREASE_ACCURACY: /* KMH */
|
|
u.uhitinc -= obj->spe;
|
|
break;
|
|
case RIN_INCREASE_DAMAGE:
|
|
u.udaminc -= obj->spe;
|
|
break;
|
|
case RIN_PROTECTION:
|
|
/* might have been put on while blind and we can now see
|
|
or perhaps been forgotten due to amnesia */
|
|
observable = (obj->spe != 0);
|
|
learnring(obj, observable);
|
|
if (obj->spe)
|
|
find_ac(); /* updates botl */
|
|
break;
|
|
case RIN_PROTECTION_FROM_SHAPE_CHAN:
|
|
/* If you're no longer protected, let the chameleons
|
|
* change shape again -dgk
|
|
*/
|
|
restartcham();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
Ring_off(struct obj *obj)
|
|
{
|
|
Ring_off_or_gone(obj, FALSE);
|
|
}
|
|
|
|
void
|
|
Ring_gone(struct obj *obj)
|
|
{
|
|
Ring_off_or_gone(obj, TRUE);
|
|
}
|
|
|
|
void
|
|
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);
|
|
setworn(otmp, W_TOOL);
|
|
on_msg(otmp);
|
|
|
|
if (Blind && !already_blind) {
|
|
changed = TRUE;
|
|
if (flags.verbose)
|
|
You_cant("see any more.");
|
|
/* set ball&chain variables before the hero goes blind */
|
|
if (Punished)
|
|
set_bc(0);
|
|
} else if (already_blind && !Blind) {
|
|
changed = TRUE;
|
|
/* "You are now wearing the Eyes of the Overworld." */
|
|
if (u.uroleplay.blind) {
|
|
/* this can only happen by putting on the Eyes of the Overworld;
|
|
that shouldn't actually produce a permanent cure, but we
|
|
can't let the "blind from birth" conduct remain intact */
|
|
pline("For the first time in your life, you can see!");
|
|
u.uroleplay.blind = FALSE;
|
|
} else
|
|
You("can see!");
|
|
}
|
|
if (changed) {
|
|
toggle_blindness(); /* potion.c */
|
|
}
|
|
}
|
|
|
|
void
|
|
Blindf_off(struct obj *otmp)
|
|
{
|
|
boolean was_blind = Blind, changed = FALSE,
|
|
nooffmsg = !otmp;
|
|
|
|
if (!otmp)
|
|
otmp = ublindf;
|
|
if (!otmp) {
|
|
impossible("Blindf_off without eyewear?");
|
|
return;
|
|
}
|
|
gc.context.takeoff.mask &= ~W_TOOL;
|
|
setworn((struct obj *) 0, otmp->owornmask);
|
|
if (!nooffmsg)
|
|
off_msg(otmp);
|
|
|
|
if (Blind) {
|
|
if (was_blind) {
|
|
/* "still cannot see" makes no sense when removing lenses
|
|
since they can't have been the cause of your blindness */
|
|
if (otmp->otyp != LENSES)
|
|
You("still cannot see.");
|
|
} else {
|
|
changed = TRUE; /* !was_blind */
|
|
/* "You were wearing the Eyes of the Overworld." */
|
|
You_cant("see anything now!");
|
|
/* set ball&chain variables before the hero goes blind */
|
|
if (Punished)
|
|
set_bc(0);
|
|
}
|
|
} else if (was_blind) {
|
|
if (!gulp_blnd_check()) {
|
|
changed = TRUE; /* !Blind */
|
|
You("can see again.");
|
|
}
|
|
}
|
|
if (changed) {
|
|
toggle_blindness(); /* potion.c */
|
|
}
|
|
}
|
|
|
|
/* called in moveloop()'s prologue to set side-effects of worn start-up items;
|
|
also used by poly_obj() when a worn item gets transformed */
|
|
void
|
|
set_wear(
|
|
struct obj *obj) /* if Null, do all worn items; otherwise just obj */
|
|
{
|
|
gi.initial_don = !obj;
|
|
|
|
if (!obj ? ublindf != 0 : (obj == ublindf))
|
|
(void) Blindf_on(ublindf);
|
|
if (!obj ? uright != 0 : (obj == uright))
|
|
(void) Ring_on(uright);
|
|
if (!obj ? uleft != 0 : (obj == uleft))
|
|
(void) Ring_on(uleft);
|
|
if (!obj ? uamul != 0 : (obj == uamul))
|
|
(void) Amulet_on();
|
|
|
|
if (!obj ? uarmu != 0 : (obj == uarmu))
|
|
(void) Shirt_on();
|
|
if (!obj ? uarm != 0 : (obj == uarm))
|
|
(void) Armor_on();
|
|
if (!obj ? uarmc != 0 : (obj == uarmc))
|
|
(void) Cloak_on();
|
|
if (!obj ? uarmf != 0 : (obj == uarmf))
|
|
(void) Boots_on();
|
|
if (!obj ? uarmg != 0 : (obj == uarmg))
|
|
(void) Gloves_on();
|
|
if (!obj ? uarmh != 0 : (obj == uarmh))
|
|
(void) Helmet_on();
|
|
if (!obj ? uarms != 0 : (obj == uarms))
|
|
(void) Shield_on();
|
|
|
|
gi.initial_don = FALSE;
|
|
}
|
|
|
|
/* check whether the target object is currently being put on (or taken off--
|
|
also checks for doffing--[why?]) */
|
|
boolean
|
|
donning(struct obj *otmp)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
/* 'W' (or 'P' used for armor) sets ga.afternmv */
|
|
if (doffing(otmp))
|
|
result = TRUE;
|
|
else if (otmp == uarm)
|
|
result = (ga.afternmv == Armor_on);
|
|
else if (otmp == uarmu)
|
|
result = (ga.afternmv == Shirt_on);
|
|
else if (otmp == uarmc)
|
|
result = (ga.afternmv == Cloak_on);
|
|
else if (otmp == uarmf)
|
|
result = (ga.afternmv == Boots_on);
|
|
else if (otmp == uarmh)
|
|
result = (ga.afternmv == Helmet_on);
|
|
else if (otmp == uarmg)
|
|
result = (ga.afternmv == Gloves_on);
|
|
else if (otmp == uarms)
|
|
result = (ga.afternmv == Shield_on);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* check whether the target object is currently being taken off,
|
|
so that stop_donning() and steal() can vary messages and doname()
|
|
can vary "(being worn)" suffix */
|
|
boolean
|
|
doffing(struct obj *otmp)
|
|
{
|
|
long what = gc.context.takeoff.what;
|
|
boolean result = FALSE;
|
|
|
|
/* 'T' (or 'R' used for armor) sets ga.afternmv, 'A' sets takeoff.what */
|
|
if (otmp == uarm)
|
|
result = (ga.afternmv == Armor_off || what == WORN_ARMOR);
|
|
else if (otmp == uarmu)
|
|
result = (ga.afternmv == Shirt_off || what == WORN_SHIRT);
|
|
else if (otmp == uarmc)
|
|
result = (ga.afternmv == Cloak_off || what == WORN_CLOAK);
|
|
else if (otmp == uarmf)
|
|
result = (ga.afternmv == Boots_off || what == WORN_BOOTS);
|
|
else if (otmp == uarmh)
|
|
result = (ga.afternmv == Helmet_off || what == WORN_HELMET);
|
|
else if (otmp == uarmg)
|
|
result = (ga.afternmv == Gloves_off || what == WORN_GLOVES);
|
|
else if (otmp == uarms)
|
|
result = (ga.afternmv == Shield_off || what == WORN_SHIELD);
|
|
/* these 1-turn items don't need 'ga.afternmv' checks */
|
|
else if (otmp == uamul)
|
|
result = (what == WORN_AMUL);
|
|
else if (otmp == uleft)
|
|
result = (what == LEFT_RING);
|
|
else if (otmp == uright)
|
|
result = (what == RIGHT_RING);
|
|
else if (otmp == ublindf)
|
|
result = (what == WORN_BLINDF);
|
|
else if (otmp == uwep)
|
|
result = (what == W_WEP);
|
|
else if (otmp == uswapwep)
|
|
result = (what == W_SWAPWEP);
|
|
else if (otmp == uquiver)
|
|
result = (what == W_QUIVER);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* despite their names, cancel_don() and cancel_doff() both apply to both
|
|
donning and doffing... */
|
|
void
|
|
cancel_doff(struct obj *obj, long slotmask)
|
|
{
|
|
/* Called by setworn() for old item in specified slot or by setnotworn()
|
|
* for specified item. We don't want to call cancel_don() if we got
|
|
* here via <X>_off() -> setworn((struct obj *) 0) -> cancel_doff()
|
|
* because that would stop the 'A' command from continuing with next
|
|
* selected item. So do_takeoff() sets a flag in takeoff.mask for us.
|
|
* [For taking off an individual item with 'T'/'R'/'w-', it doesn't
|
|
* matter whether cancel_don() gets called here--the item has already
|
|
* been removed by now.]
|
|
*/
|
|
if (!(gc.context.takeoff.mask & I_SPECIAL) && donning(obj))
|
|
cancel_don(); /* applies to doffing too */
|
|
gc.context.takeoff.mask &= ~slotmask;
|
|
}
|
|
|
|
/* despite their names, cancel_don() and cancel_doff() both apply to both
|
|
donning and doffing... */
|
|
void
|
|
cancel_don(void)
|
|
{
|
|
/* the piece of armor we were donning/doffing has vanished, so stop
|
|
* wasting time on it (and don't dereference it when donning would
|
|
* otherwise finish); afternmv never has some of these values because
|
|
* every item of the corresponding armor category takes 1 turn to wear,
|
|
* but check all of them anyway
|
|
*/
|
|
gc.context.takeoff.cancelled_don = (ga.afternmv == Cloak_on
|
|
|| ga.afternmv == Armor_on
|
|
|| ga.afternmv == Shirt_on
|
|
|| ga.afternmv == Helmet_on
|
|
|| ga.afternmv == Gloves_on
|
|
|| ga.afternmv == Boots_on
|
|
|| ga.afternmv == Shield_on);
|
|
ga.afternmv = (int (*)(void)) 0;
|
|
gn.nomovemsg = (char *) 0;
|
|
gm.multi = 0;
|
|
gc.context.takeoff.delay = 0;
|
|
gc.context.takeoff.what = 0L;
|
|
}
|
|
|
|
/* called by steal() during theft from hero; interrupt donning/doffing */
|
|
int
|
|
stop_donning(struct obj *stolenobj) /* no message if stolenobj is already
|
|
being doffing */
|
|
{
|
|
char buf[BUFSZ];
|
|
struct obj *otmp;
|
|
boolean putting_on;
|
|
int result = 0;
|
|
|
|
for (otmp = gi.invent; otmp; otmp = otmp->nobj)
|
|
if ((otmp->owornmask & W_ARMOR) && donning(otmp))
|
|
break;
|
|
/* at most one item will pass donning() test at any given time */
|
|
if (!otmp)
|
|
return 0;
|
|
|
|
/* donning() returns True when doffing too; doffing() is more specific */
|
|
putting_on = !doffing(otmp);
|
|
/* cancel_don() looks at afternmv; it can also cancel doffing */
|
|
cancel_don();
|
|
/* don't want <armor>_on() or <armor>_off() being called
|
|
by unmul() since the on or off action isn't completing */
|
|
ga.afternmv = (int (*)(void)) 0;
|
|
if (putting_on || otmp != stolenobj) {
|
|
Sprintf(buf, "You stop %s %s.",
|
|
putting_on ? "putting on" : "taking off",
|
|
thesimpleoname(otmp));
|
|
} else {
|
|
buf[0] = '\0'; /* silently stop doffing stolenobj */
|
|
result = (int) -gm.multi; /* remember this before calling unmul() */
|
|
}
|
|
unmul(buf);
|
|
/* while putting on, item becomes worn immediately but side-effects are
|
|
deferred until the delay expires; when interrupted, make it unworn
|
|
(while taking off, item stays worn until the delay expires; when
|
|
interrupted, leave it worn) */
|
|
if (putting_on)
|
|
remove_worn_item(otmp, FALSE);
|
|
|
|
return result;
|
|
}
|
|
|
|
static NEARDATA int Narmorpieces, Naccessories;
|
|
|
|
/* assign values to Narmorpieces and Naccessories */
|
|
static void
|
|
count_worn_stuff(struct obj **which, /* caller wants this when count is 1 */
|
|
boolean accessorizing)
|
|
{
|
|
struct obj *otmp;
|
|
|
|
Narmorpieces = Naccessories = 0;
|
|
|
|
#define MOREWORN(x,wtyp) do { if (x) { wtyp++; otmp = x; } } while (0)
|
|
otmp = 0;
|
|
MOREWORN(uarmh, Narmorpieces);
|
|
MOREWORN(uarms, Narmorpieces);
|
|
MOREWORN(uarmg, Narmorpieces);
|
|
MOREWORN(uarmf, Narmorpieces);
|
|
/* for cloak/suit/shirt, we only count the outermost item so that it
|
|
can be taken off without confirmation if final count ends up as 1 */
|
|
if (uarmc)
|
|
MOREWORN(uarmc, Narmorpieces);
|
|
else if (uarm)
|
|
MOREWORN(uarm, Narmorpieces);
|
|
else if (uarmu)
|
|
MOREWORN(uarmu, Narmorpieces);
|
|
if (!accessorizing)
|
|
*which = otmp; /* default item iff Narmorpieces is 1 */
|
|
|
|
otmp = 0;
|
|
MOREWORN(uleft, Naccessories);
|
|
MOREWORN(uright, Naccessories);
|
|
MOREWORN(uamul, Naccessories);
|
|
MOREWORN(ublindf, Naccessories);
|
|
if (accessorizing)
|
|
*which = otmp; /* default item iff Naccessories is 1 */
|
|
#undef MOREWORN
|
|
}
|
|
|
|
/* take off one piece or armor or one accessory;
|
|
shared by dotakeoff('T') and doremring('R') */
|
|
static int
|
|
armor_or_accessory_off(struct obj *obj)
|
|
{
|
|
if (!(obj->owornmask & (W_ARMOR | W_ACCESSORY))) {
|
|
You("are not wearing that.");
|
|
return ECMD_OK;
|
|
}
|
|
if (obj == uskin
|
|
|| ((obj == uarm) && uarmc)
|
|
|| ((obj == uarmu) && (uarmc || uarm))) {
|
|
char why[QBUFSZ], what[QBUFSZ];
|
|
|
|
why[0] = what[0] = '\0';
|
|
if (obj != uskin) {
|
|
if (uarmc)
|
|
Strcat(what, cloak_simple_name(uarmc));
|
|
if ((obj == uarmu) && uarm) {
|
|
if (uarmc)
|
|
Strcat(what, " and ");
|
|
Strcat(what, suit_simple_name(uarm));
|
|
}
|
|
Snprintf(why, sizeof(why), " without taking off your %s first",
|
|
what);
|
|
} else {
|
|
Strcpy(why, "; it's embedded");
|
|
}
|
|
You_cant("take that off%s.", why);
|
|
return ECMD_OK;
|
|
}
|
|
|
|
reset_remarm(); /* clear context.takeoff.mask and context.takeoff.what */
|
|
(void) select_off(obj);
|
|
if (!gc.context.takeoff.mask)
|
|
return ECMD_OK;
|
|
/* none of armoroff()/Ring_/Amulet/Blindf_off() use context.takeoff.mask */
|
|
reset_remarm();
|
|
|
|
if (obj->owornmask & W_ARMOR) {
|
|
(void) armoroff(obj);
|
|
} else if (obj == uright || obj == uleft) {
|
|
/* Sometimes we want to give the off_msg before removing and
|
|
* sometimes after; for instance, "you were wearing a moonstone
|
|
* ring (on right hand)" is desired but "you were wearing a
|
|
* square amulet (being worn)" is not because of the redundant
|
|
* "being worn".
|
|
*/
|
|
off_msg(obj);
|
|
Ring_off(obj);
|
|
} else if (obj == uamul) {
|
|
Amulet_off();
|
|
off_msg(obj);
|
|
} else if (obj == ublindf) {
|
|
Blindf_off(obj); /* does its own off_msg */
|
|
} else {
|
|
impossible("removing strange accessory?");
|
|
if (obj->owornmask)
|
|
remove_worn_item(obj, FALSE);
|
|
}
|
|
return ECMD_TIME;
|
|
}
|
|
|
|
/* the #takeoff command - remove worn armor */
|
|
int
|
|
dotakeoff(void)
|
|
{
|
|
struct obj *otmp = (struct obj *) 0;
|
|
|
|
count_worn_stuff(&otmp, FALSE);
|
|
if (!Narmorpieces && !Naccessories) {
|
|
/* assert( GRAY_DRAGON_SCALES > YELLOW_DRAGON_SCALE_MAIL ); */
|
|
if (uskin)
|
|
pline_The("%s merged with your skin!",
|
|
uskin->otyp >= GRAY_DRAGON_SCALES
|
|
? "dragon scales are"
|
|
: "dragon scale mail is");
|
|
else
|
|
pline("Not wearing any armor or accessories.");
|
|
return ECMD_OK;
|
|
}
|
|
if (Narmorpieces != 1 || ParanoidRemove || cmdq_peek(CQ_CANNED))
|
|
otmp = getobj("take off", takeoff_ok, GETOBJ_NOFLAGS);
|
|
if (!otmp)
|
|
return ECMD_CANCEL;
|
|
|
|
return armor_or_accessory_off(otmp);
|
|
}
|
|
|
|
/* the #remove command - take off ring or other accessory */
|
|
int
|
|
doremring(void)
|
|
{
|
|
struct obj *otmp = 0;
|
|
|
|
count_worn_stuff(&otmp, TRUE);
|
|
if (!Naccessories && !Narmorpieces) {
|
|
pline("Not wearing any accessories or armor.");
|
|
return ECMD_OK;
|
|
}
|
|
if (Naccessories != 1 || ParanoidRemove || cmdq_peek(CQ_CANNED))
|
|
otmp = getobj("remove", remove_ok, GETOBJ_NOFLAGS);
|
|
if (!otmp)
|
|
return ECMD_CANCEL;
|
|
|
|
return armor_or_accessory_off(otmp);
|
|
}
|
|
|
|
/* Check if something worn is cursed _and_ unremovable. */
|
|
int
|
|
cursed(struct obj *otmp)
|
|
{
|
|
if (!otmp) {
|
|
impossible("cursed without otmp");
|
|
return 0;
|
|
}
|
|
/* Curses, like chickens, come home to roost. */
|
|
if ((otmp == uwep) ? welded(otmp) : (int) otmp->cursed) {
|
|
boolean use_plural = (is_boots(otmp) || is_gloves(otmp)
|
|
|| otmp->otyp == LENSES || otmp->quan > 1L);
|
|
|
|
/* might be trying again after applying grease to hands */
|
|
if (Glib && otmp->bknown
|
|
/* for weapon, we'll only get here via 'A )' */
|
|
&& (uarmg ? (otmp == uwep)
|
|
: ((otmp->owornmask & (W_WEP | W_RING)) != 0)))
|
|
pline("Despite your slippery %s, you can't.",
|
|
fingers_or_gloves(TRUE));
|
|
else
|
|
You("can't. %s cursed.", use_plural ? "They are" : "It is");
|
|
set_bknown(otmp, 1);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
armoroff(struct obj *otmp)
|
|
{
|
|
static char offdelaybuf[60];
|
|
int delay = -objects[otmp->otyp].oc_delay;
|
|
const char *what = 0;
|
|
|
|
if (cursed(otmp))
|
|
return 0;
|
|
/* this used to make assumptions about which types of armor had
|
|
delays and which didn't; now both are handled for all types */
|
|
if (delay) {
|
|
nomul(delay);
|
|
gm.multi_reason = "disrobing";
|
|
switch (objects[otmp->otyp].oc_armcat) {
|
|
case ARM_SUIT:
|
|
what = suit_simple_name(otmp);
|
|
ga.afternmv = Armor_off;
|
|
break;
|
|
case ARM_SHIELD:
|
|
what = shield_simple_name(otmp);
|
|
ga.afternmv = Shield_off;
|
|
break;
|
|
case ARM_HELM:
|
|
what = helm_simple_name(otmp);
|
|
ga.afternmv = Helmet_off;
|
|
break;
|
|
case ARM_GLOVES:
|
|
what = gloves_simple_name(otmp);
|
|
ga.afternmv = Gloves_off;
|
|
break;
|
|
case ARM_BOOTS:
|
|
what = boots_simple_name(otmp);
|
|
ga.afternmv = Boots_off;
|
|
break;
|
|
case ARM_CLOAK:
|
|
what = cloak_simple_name(otmp);
|
|
ga.afternmv = Cloak_off;
|
|
break;
|
|
case ARM_SHIRT:
|
|
what = shirt_simple_name(otmp);
|
|
ga.afternmv = Shirt_off;
|
|
break;
|
|
default:
|
|
impossible("Taking off unknown armor (%d: %d), delay %d",
|
|
otmp->otyp, objects[otmp->otyp].oc_armcat, delay);
|
|
break;
|
|
}
|
|
if (what) {
|
|
/* sizeof offdelaybuf == 60; increase it if this becomes longer */
|
|
Sprintf(offdelaybuf, "You finish taking off your %s.", what);
|
|
gn.nomovemsg = offdelaybuf;
|
|
}
|
|
} else {
|
|
/* no delay so no '(*afternmv)()' or 'nomovemsg' */
|
|
switch (objects[otmp->otyp].oc_armcat) {
|
|
case ARM_SUIT:
|
|
(void) Armor_off();
|
|
break;
|
|
case ARM_SHIELD:
|
|
(void) Shield_off();
|
|
break;
|
|
case ARM_HELM:
|
|
(void) Helmet_off();
|
|
break;
|
|
case ARM_GLOVES:
|
|
(void) Gloves_off();
|
|
break;
|
|
case ARM_BOOTS:
|
|
(void) Boots_off();
|
|
break;
|
|
case ARM_CLOAK:
|
|
(void) Cloak_off();
|
|
break;
|
|
case ARM_SHIRT:
|
|
(void) Shirt_off();
|
|
break;
|
|
default:
|
|
impossible("Taking off unknown armor (%d: %d), no delay",
|
|
otmp->otyp, objects[otmp->otyp].oc_armcat);
|
|
break;
|
|
}
|
|
/* We want off_msg() after removing the item to
|
|
avoid "You were wearing ____ (being worn)." */
|
|
off_msg(otmp);
|
|
}
|
|
gc.context.takeoff.mask = gc.context.takeoff.what = 0L;
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
already_wearing(const char *cc)
|
|
{
|
|
You("are already wearing %s%c", cc, (cc == c_that_) ? '!' : '.');
|
|
}
|
|
|
|
static void
|
|
already_wearing2(const char *cc1, const char *cc2)
|
|
{
|
|
You_cant("wear %s because you're wearing %s there already.", cc1, cc2);
|
|
}
|
|
|
|
/*
|
|
* canwearobj checks to see whether the player can wear a piece of armor
|
|
*
|
|
* inputs: otmp (the piece of armor)
|
|
* noisy (if TRUE give error messages, otherwise be quiet about it)
|
|
* output: mask (otmp's armor type)
|
|
*/
|
|
int
|
|
canwearobj(struct obj *otmp, long *mask, boolean noisy)
|
|
{
|
|
int err = 0;
|
|
const char *which;
|
|
|
|
/* this is the same check as for 'W' (dowear), but different message,
|
|
in case we get here via 'P' (doputon) */
|
|
if (verysmall(gy.youmonst.data) || nohands(gy.youmonst.data)) {
|
|
if (noisy)
|
|
You("can't wear any armor in your current form.");
|
|
return 0;
|
|
}
|
|
|
|
which = is_cloak(otmp) ? c_cloak
|
|
: is_shirt(otmp) ? c_shirt
|
|
: is_suit(otmp) ? c_suit
|
|
: 0;
|
|
if (which && cantweararm(gy.youmonst.data)
|
|
/* same exception for cloaks as used in m_dowear() */
|
|
&& (which != c_cloak
|
|
|| ((otmp->otyp != MUMMY_WRAPPING)
|
|
? gy.youmonst.data->msize != MZ_SMALL
|
|
: !WrappingAllowed(gy.youmonst.data)))
|
|
&& (racial_exception(&gy.youmonst, otmp) < 1)) {
|
|
if (noisy)
|
|
pline_The("%s will not fit on your body.", which);
|
|
return 0;
|
|
} else if (otmp->owornmask & W_ARMOR) {
|
|
if (noisy)
|
|
already_wearing(c_that_);
|
|
return 0;
|
|
}
|
|
|
|
if (welded(uwep) && bimanual(uwep) && (is_suit(otmp) || is_shirt(otmp))) {
|
|
if (noisy)
|
|
You("cannot do that while holding your %s.",
|
|
is_sword(uwep) ? c_sword : c_weapon);
|
|
return 0;
|
|
}
|
|
|
|
if (is_helmet(otmp)) {
|
|
if (uarmh) {
|
|
if (noisy)
|
|
already_wearing(an(helm_simple_name(uarmh)));
|
|
err++;
|
|
} else if (Upolyd && has_horns(gy.youmonst.data) && !is_flimsy(otmp)) {
|
|
/* (flimsy exception matches polyself handling) */
|
|
if (noisy)
|
|
pline_The("%s won't fit over your horn%s.",
|
|
helm_simple_name(otmp),
|
|
plur(num_horns(gy.youmonst.data)));
|
|
err++;
|
|
} else
|
|
*mask = W_ARMH;
|
|
} else if (is_shield(otmp)) {
|
|
if (uarms) {
|
|
if (noisy)
|
|
already_wearing(an(c_shield));
|
|
err++;
|
|
} else if (uwep && bimanual(uwep)) {
|
|
if (noisy)
|
|
You("cannot wear a shield while wielding a two-handed %s.",
|
|
is_sword(uwep) ? c_sword : (uwep->otyp == BATTLE_AXE)
|
|
? c_axe
|
|
: c_weapon);
|
|
err++;
|
|
} else if (u.twoweap) {
|
|
if (noisy)
|
|
You("cannot wear a shield while wielding two weapons.");
|
|
err++;
|
|
} else
|
|
*mask = W_ARMS;
|
|
} else if (is_boots(otmp)) {
|
|
if (uarmf) {
|
|
if (noisy)
|
|
already_wearing(c_boots);
|
|
err++;
|
|
} else if (Upolyd && slithy(gy.youmonst.data)) {
|
|
if (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... */
|
|
if (noisy)
|
|
You("have too many hooves to wear %s.",
|
|
c_boots); /* makeplural(body_part(FOOT)) yields
|
|
"rear hooves" which sounds odd */
|
|
err++;
|
|
} else if (u.utrap
|
|
&& (u.utraptype == TT_BEARTRAP || u.utraptype == TT_INFLOOR
|
|
|| u.utraptype == TT_LAVA
|
|
|| u.utraptype == TT_BURIEDBALL)) {
|
|
if (u.utraptype == TT_BEARTRAP) {
|
|
if (noisy)
|
|
Your("%s is trapped!", body_part(FOOT));
|
|
} else if (u.utraptype == TT_INFLOOR || u.utraptype == TT_LAVA) {
|
|
if (noisy)
|
|
Your("%s are stuck in the %s!",
|
|
makeplural(body_part(FOOT)), surface(u.ux, u.uy));
|
|
} else { /*TT_BURIEDBALL*/
|
|
if (noisy)
|
|
Your("%s is attached to the buried ball!",
|
|
body_part(LEG));
|
|
}
|
|
err++;
|
|
} else
|
|
*mask = W_ARMF;
|
|
} else if (is_gloves(otmp)) {
|
|
if (uarmg) {
|
|
if (noisy)
|
|
already_wearing(c_gloves);
|
|
err++;
|
|
} else if (welded(uwep)) {
|
|
if (noisy)
|
|
You("cannot wear gloves over your %s.",
|
|
is_sword(uwep) ? c_sword : c_weapon);
|
|
err++;
|
|
} else if (Glib) {
|
|
/* prevent slippery bare fingers from transferring to
|
|
gloved fingers */
|
|
if (noisy)
|
|
Your("%s are too slippery to pull on %s.",
|
|
fingers_or_gloves(FALSE), gloves_simple_name(otmp));
|
|
err++;
|
|
} else
|
|
*mask = W_ARMG;
|
|
} else if (is_shirt(otmp)) {
|
|
if (uarm || uarmc || uarmu) {
|
|
if (uarmu) {
|
|
if (noisy)
|
|
already_wearing(an(c_shirt));
|
|
} else {
|
|
if (noisy)
|
|
You_cant("wear that over your %s.",
|
|
(uarm && !uarmc) ? c_armor
|
|
: cloak_simple_name(uarmc));
|
|
}
|
|
err++;
|
|
} else
|
|
*mask = W_ARMU;
|
|
} else if (is_cloak(otmp)) {
|
|
if (uarmc) {
|
|
if (noisy)
|
|
already_wearing(an(cloak_simple_name(uarmc)));
|
|
err++;
|
|
} else
|
|
*mask = W_ARMC;
|
|
} else if (is_suit(otmp)) {
|
|
if (uarmc) {
|
|
if (noisy)
|
|
You("cannot wear armor over a %s.", cloak_simple_name(uarmc));
|
|
err++;
|
|
} else if (uarm) {
|
|
if (noisy)
|
|
already_wearing("some armor");
|
|
err++;
|
|
} else
|
|
*mask = W_ARM;
|
|
} else {
|
|
/* getobj can't do this after setting its allow_all flag; that
|
|
happens if you have armor for slots that are covered up or
|
|
extra armor for slots that are filled */
|
|
if (noisy)
|
|
silly_thing("wear", otmp);
|
|
err++;
|
|
}
|
|
/* Unnecessary since now only weapons and special items like pick-axes get
|
|
* welded to your hand, not armor
|
|
if (welded(otmp)) {
|
|
if (!err++) {
|
|
if (noisy) weldmsg(otmp);
|
|
}
|
|
}
|
|
*/
|
|
return !err;
|
|
}
|
|
|
|
static int
|
|
accessory_or_armor_on(struct obj *obj)
|
|
{
|
|
long mask = 0L;
|
|
boolean armor, ring, eyewear;
|
|
|
|
if (obj->owornmask & (W_ACCESSORY | W_ARMOR)) {
|
|
already_wearing(c_that_);
|
|
return ECMD_OK;
|
|
}
|
|
armor = (obj->oclass == ARMOR_CLASS);
|
|
ring = (obj->oclass == RING_CLASS || obj->otyp == MEAT_RING);
|
|
eyewear = (obj->otyp == BLINDFOLD || obj->otyp == TOWEL
|
|
|| obj->otyp == LENSES);
|
|
/* checks which are performed prior to actually touching the item */
|
|
if (armor) {
|
|
if (!canwearobj(obj, &mask, TRUE))
|
|
return ECMD_OK;
|
|
|
|
if (obj->otyp == HELM_OF_OPPOSITE_ALIGNMENT
|
|
&& qstart_level.dnum == u.uz.dnum) { /* in quest */
|
|
if (u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL])
|
|
You("narrowly avoid losing all chance at your goal.");
|
|
else /* converted */
|
|
You("are suddenly overcome with shame and change your mind.");
|
|
u.ublessed = 0; /* lose your god's protection */
|
|
makeknown(obj->otyp);
|
|
disp.botl = TRUE; /* for AC after zeroing u.ublessed */
|
|
return ECMD_TIME;
|
|
}
|
|
} else {
|
|
/*
|
|
* FIXME:
|
|
* except for the rings/nolimbs case, this allows you to put on
|
|
* accessories without having any hands to manipulate them, and
|
|
* to put them on when poly'd into a tiny or huge form where
|
|
* they shouldn't fit. [If the latter situation changes, make
|
|
* comparable change to break_armor(polyself.c).]
|
|
*/
|
|
|
|
/* accessory */
|
|
if (ring) {
|
|
char answer, qbuf[QBUFSZ];
|
|
int res = 0;
|
|
|
|
if (nolimbs(gy.youmonst.data)) {
|
|
You("cannot make the ring stick to your body.");
|
|
return ECMD_OK;
|
|
}
|
|
if (uleft && uright) {
|
|
There("are no more %s%s to fill.",
|
|
humanoid(gy.youmonst.data) ? "ring-" : "",
|
|
fingers_or_gloves(FALSE));
|
|
return ECMD_OK;
|
|
}
|
|
if (uleft) {
|
|
mask = RIGHT_RING;
|
|
} else if (uright) {
|
|
mask = LEFT_RING;
|
|
} else {
|
|
do {
|
|
Sprintf(qbuf, "Which %s%s, Right or Left?",
|
|
humanoid(gy.youmonst.data) ? "ring-" : "",
|
|
body_part(FINGER));
|
|
answer = yn_function(qbuf, "rl", '\0', TRUE);
|
|
switch (answer) {
|
|
case '\0':
|
|
case '\033':
|
|
return ECMD_OK;
|
|
case 'l':
|
|
case 'L':
|
|
mask = LEFT_RING;
|
|
break;
|
|
case 'r':
|
|
case 'R':
|
|
mask = RIGHT_RING;
|
|
break;
|
|
}
|
|
} while (!mask);
|
|
}
|
|
if (uarmg && Glib) {
|
|
Your(
|
|
"%s are too slippery to remove, so you cannot put on the ring.",
|
|
gloves_simple_name(uarmg));
|
|
return ECMD_TIME; /* always uses move */
|
|
}
|
|
if (uarmg && uarmg->cursed) {
|
|
res = !uarmg->bknown;
|
|
set_bknown(uarmg, 1);
|
|
You("cannot remove your %s to put on the ring.", c_gloves);
|
|
/* uses move iff we learned gloves are cursed */
|
|
return res ? ECMD_TIME : ECMD_OK;
|
|
}
|
|
if (uwep) {
|
|
res = !uwep->bknown; /* check this before calling welded() */
|
|
if (((mask == RIGHT_RING && URIGHTY)
|
|
|| (mask == LEFT_RING && ULEFTY)
|
|
|| bimanual(uwep)) && welded(uwep)) {
|
|
const char *hand = body_part(HAND);
|
|
|
|
/* welded will set bknown */
|
|
if (bimanual(uwep))
|
|
hand = makeplural(hand);
|
|
You("cannot free your weapon %s to put on the ring.",
|
|
hand);
|
|
/* uses move iff we learned weapon is cursed */
|
|
return res ? ECMD_TIME : ECMD_OK;
|
|
}
|
|
}
|
|
} else if (obj->oclass == AMULET_CLASS) {
|
|
if (uamul) {
|
|
already_wearing("an amulet");
|
|
return ECMD_OK;
|
|
}
|
|
} else if (eyewear) {
|
|
if (!has_head(gy.youmonst.data)) {
|
|
You("have no head to wear %s on.", ansimpleoname(obj));
|
|
return ECMD_OK;
|
|
}
|
|
|
|
if (ublindf) {
|
|
if (ublindf->otyp == TOWEL)
|
|
Your("%s is already covered by a towel.",
|
|
body_part(FACE));
|
|
else if (ublindf->otyp == BLINDFOLD) {
|
|
if (obj->otyp == LENSES)
|
|
already_wearing2("lenses", "a blindfold");
|
|
else
|
|
already_wearing("a blindfold");
|
|
} else if (ublindf->otyp == LENSES) {
|
|
if (obj->otyp == BLINDFOLD)
|
|
already_wearing2("a blindfold", "some lenses");
|
|
else
|
|
already_wearing("some lenses");
|
|
} else {
|
|
already_wearing(something); /* ??? */
|
|
}
|
|
return ECMD_OK;
|
|
}
|
|
} else {
|
|
/* neither armor nor accessory */
|
|
You_cant("wear that!");
|
|
return ECMD_OK;
|
|
}
|
|
}
|
|
|
|
if (!retouch_object(&obj, FALSE))
|
|
return ECMD_TIME; /* costs a turn even though it didn't get worn */
|
|
|
|
if (armor) {
|
|
int delay;
|
|
|
|
/* if the armor is wielded, release it for wearing (won't be
|
|
welded even if cursed; that only happens for weapons/weptools) */
|
|
if (obj->owornmask & W_WEAPONS)
|
|
remove_worn_item(obj, FALSE);
|
|
/*
|
|
* Setting obj->known=1 is done because setworn() causes hero's AC
|
|
* to change so armor's +/- value is evident via the status line.
|
|
* We used to set it here because of that, but then it would stick
|
|
* if a nymph stole the armor before it was fully worn. Delay it
|
|
* until the afternmv action. The player may still know this armor's
|
|
* +/- amount if donning gets interrupted, but the hero won't.
|
|
*
|
|
obj->known = 1;
|
|
*/
|
|
setworn(obj, mask);
|
|
/* if there's no delay, we'll execute 'afternmv' immediately */
|
|
if (obj == uarm)
|
|
ga.afternmv = Armor_on;
|
|
else if (obj == uarmh)
|
|
ga.afternmv = Helmet_on;
|
|
else if (obj == uarmg)
|
|
ga.afternmv = Gloves_on;
|
|
else if (obj == uarmf)
|
|
ga.afternmv = Boots_on;
|
|
else if (obj == uarms)
|
|
ga.afternmv = Shield_on;
|
|
else if (obj == uarmc)
|
|
ga.afternmv = Cloak_on;
|
|
else if (obj == uarmu)
|
|
ga.afternmv = Shirt_on;
|
|
else
|
|
panic("wearing armor not worn as armor? [%08lx]", obj->owornmask);
|
|
|
|
delay = -objects[obj->otyp].oc_delay;
|
|
if (delay) {
|
|
nomul(delay);
|
|
gm.multi_reason = "dressing up";
|
|
gn.nomovemsg = "You finish your dressing maneuver.";
|
|
} else {
|
|
unmul(""); /* call afternmv, clear it+nomovemsg+multi_reason */
|
|
on_msg(obj);
|
|
}
|
|
gc.context.takeoff.mask = gc.context.takeoff.what = 0L;
|
|
} else { /* not armor */
|
|
boolean give_feedback = FALSE;
|
|
|
|
/* [releasing wielded accessory handled in Xxx_on()] */
|
|
if (ring) {
|
|
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);
|
|
} else if (eyewear) {
|
|
/* setworn() handled by Blindf_on() */
|
|
Blindf_on(obj);
|
|
/* message handled by Blindf_on(); leave give_feedback False */
|
|
}
|
|
/* feedback for ring or for amulet other than 'change' */
|
|
if (give_feedback && is_worn(obj))
|
|
prinv((char *) 0, obj, 0L);
|
|
}
|
|
return ECMD_TIME;
|
|
}
|
|
|
|
/* the #wear command */
|
|
int
|
|
dowear(void)
|
|
{
|
|
struct obj *otmp;
|
|
|
|
/* cantweararm() checks for suits of armor, not what we want here;
|
|
verysmall() or nohands() checks for shields, gloves, etc... */
|
|
if (verysmall(gy.youmonst.data) || nohands(gy.youmonst.data)) {
|
|
pline("Don't even bother.");
|
|
return ECMD_OK;
|
|
}
|
|
if (uarm && uarmu && uarmc && uarmh && uarms && uarmg && uarmf
|
|
&& uleft && uright && uamul && ublindf) {
|
|
/* 'W' message doesn't mention accessories */
|
|
You("are already wearing a full complement of armor.");
|
|
return ECMD_OK;
|
|
}
|
|
otmp = getobj("wear", wear_ok, GETOBJ_NOFLAGS);
|
|
return otmp ? accessory_or_armor_on(otmp) : ECMD_CANCEL;
|
|
}
|
|
|
|
/* the #puton command */
|
|
int
|
|
doputon(void)
|
|
{
|
|
struct obj *otmp;
|
|
|
|
if (uleft && uright && uamul && ublindf
|
|
&& uarm && uarmu && uarmc && uarmh && uarms && uarmg && uarmf) {
|
|
/* 'P' message doesn't mention armor */
|
|
Your("%s%s are full, and you're already wearing an amulet and %s.",
|
|
humanoid(gy.youmonst.data) ? "ring-" : "",
|
|
fingers_or_gloves(FALSE),
|
|
(ublindf->otyp == LENSES) ? "some lenses" : "a blindfold");
|
|
return ECMD_OK;
|
|
}
|
|
otmp = getobj("put on", puton_ok, GETOBJ_NOFLAGS);
|
|
return otmp ? accessory_or_armor_on(otmp) : ECMD_CANCEL;
|
|
}
|
|
|
|
/* calculate current armor class */
|
|
void
|
|
find_ac(void)
|
|
{
|
|
int uac = mons[u.umonnum].ac; /* base armor class for current form */
|
|
|
|
/* armor class from worn gear */
|
|
if (uarm)
|
|
uac -= ARM_BONUS(uarm);
|
|
if (uarmc)
|
|
uac -= ARM_BONUS(uarmc);
|
|
if (uarmh)
|
|
uac -= ARM_BONUS(uarmh);
|
|
if (uarmf)
|
|
uac -= ARM_BONUS(uarmf);
|
|
if (uarms)
|
|
uac -= ARM_BONUS(uarms);
|
|
if (uarmg)
|
|
uac -= ARM_BONUS(uarmg);
|
|
if (uarmu)
|
|
uac -= ARM_BONUS(uarmu);
|
|
if (uleft && uleft->otyp == RIN_PROTECTION)
|
|
uac -= uleft->spe;
|
|
if (uright && uright->otyp == RIN_PROTECTION)
|
|
uac -= uright->spe;
|
|
if (uamul && uamul->otyp == AMULET_OF_GUARDING)
|
|
uac -= 2; /* fixed amount; main benefit is to MC */
|
|
|
|
/* armor class from other sources */
|
|
if (HProtection & INTRINSIC)
|
|
uac -= u.ublessed;
|
|
uac -= u.uspellprot;
|
|
|
|
/* put a cap on armor class [3.7: was +127,-128, now reduced to +/- 99 */
|
|
if (abs(uac) > AC_MAX)
|
|
uac = sgn(uac) * AC_MAX;
|
|
|
|
if (uac != u.uac) {
|
|
u.uac = uac;
|
|
disp.botl = TRUE;
|
|
#if 0
|
|
/* these could conceivably be achieved out of order (by being near
|
|
threshold and putting on +N dragon scale mail from bones, for
|
|
instance), but if that happens, that's the order it happened;
|
|
also, testing for these in the usual order would result in more
|
|
record_achievement() attempts and rejects for duplication */
|
|
if (u.uac <= -20)
|
|
record_achievement(ACH_AC_20);
|
|
else if (u.uac <= -10)
|
|
record_achievement(ACH_AC_10);
|
|
else if (u.uac <= 0)
|
|
record_achievement(ACH_AC_00);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void
|
|
glibr(void)
|
|
{
|
|
struct obj *otmp;
|
|
int xfl = 0;
|
|
boolean leftfall, rightfall, wastwoweap = FALSE;
|
|
const char *otherwep = 0, *thiswep, *which, *hand;
|
|
|
|
leftfall = (uleft && !uleft->cursed
|
|
&& (!uwep || !(welded(uwep) && ULEFTY)
|
|
|| !bimanual(uwep)));
|
|
rightfall = (uright && !uright->cursed
|
|
&& (!uwep || !(welded(uwep) && URIGHTY)
|
|
|| !bimanual(uwep)));
|
|
/*
|
|
leftfall = (uleft && !uleft->cursed
|
|
&& (!uwep || !welded(uwep) || !bimanual(uwep)));
|
|
rightfall = (uright && !uright->cursed && (!welded(uwep)));
|
|
*/
|
|
|
|
if (!uarmg && (leftfall || rightfall) && !nolimbs(gy.youmonst.data)) {
|
|
/* changed so cursed rings don't fall off, GAN 10/30/86 */
|
|
Your("%s off your %s.",
|
|
(leftfall && rightfall) ? "rings slip" : "ring slips",
|
|
(leftfall && rightfall) ? fingers_or_gloves(FALSE)
|
|
: body_part(FINGER));
|
|
xfl++;
|
|
if (leftfall) {
|
|
otmp = uleft;
|
|
Ring_off(uleft);
|
|
dropx(otmp);
|
|
cmdq_clear(CQ_CANNED);
|
|
}
|
|
if (rightfall) {
|
|
otmp = uright;
|
|
Ring_off(uright);
|
|
dropx(otmp);
|
|
cmdq_clear(CQ_CANNED);
|
|
}
|
|
}
|
|
|
|
otmp = uswapwep;
|
|
if (u.twoweap && otmp) {
|
|
/* secondary weapon doesn't need nearly as much handling as
|
|
primary; when in two-weapon mode, we know it's one-handed
|
|
with something else in the other hand and also that it's
|
|
a weapon or weptool rather than something unusual, plus
|
|
we don't need to compare its type with the primary */
|
|
otherwep = is_sword(otmp) ? c_sword : weapon_descr(otmp);
|
|
if (otmp->quan > 1L)
|
|
otherwep = makeplural(otherwep);
|
|
hand = body_part(HAND);
|
|
which = URIGHTY ? "left " : "right "; /* text for the off hand */
|
|
Your("%s %s%s from your %s%s.", otherwep, xfl ? "also " : "",
|
|
otense(otmp, "slip"), which, hand);
|
|
xfl++;
|
|
wastwoweap = TRUE;
|
|
setuswapwep((struct obj *) 0); /* clears u.twoweap */
|
|
cmdq_clear(CQ_CANNED);
|
|
if (canletgo(otmp, ""))
|
|
dropx(otmp);
|
|
}
|
|
otmp = uwep;
|
|
if (otmp && otmp->otyp != AKLYS && !welded(otmp)) {
|
|
long savequan = otmp->quan;
|
|
|
|
/* nice wording if both weapons are the same type */
|
|
thiswep = is_sword(otmp) ? c_sword : weapon_descr(otmp);
|
|
if (otherwep && strcmp(thiswep, makesingular(otherwep)))
|
|
otherwep = 0;
|
|
if (otmp->quan > 1L) {
|
|
/* most class names for unconventional wielded items
|
|
are ok, but if wielding multiple apples or rations
|
|
we don't want "your foods slip", so force non-corpse
|
|
food to be singular; skipping makeplural() isn't
|
|
enough--we need to fool otense() too */
|
|
if (!strcmp(thiswep, "food"))
|
|
otmp->quan = 1L;
|
|
else
|
|
thiswep = makeplural(thiswep);
|
|
}
|
|
hand = body_part(HAND);
|
|
which = "";
|
|
if (bimanual(otmp)) {
|
|
hand = makeplural(hand);
|
|
} else if (wastwoweap) {
|
|
/* preceding msg was about non-dominant hand */
|
|
which = URIGHTY ? "right " : "left ";
|
|
}
|
|
pline("%s %s%s %s%s from your %s%s.",
|
|
!strncmp(thiswep, "corpse", 6) ? "The" : "Your",
|
|
otherwep ? "other " : "", thiswep, xfl ? "also " : "",
|
|
otense(otmp, "slip"), which, hand);
|
|
/* xfl++; */
|
|
otmp->quan = savequan;
|
|
setuwep((struct obj *) 0);
|
|
cmdq_clear(CQ_CANNED);
|
|
if (canletgo(otmp, ""))
|
|
dropx(otmp);
|
|
}
|
|
}
|
|
|
|
struct obj *
|
|
some_armor(struct monst *victim)
|
|
{
|
|
struct obj *otmph, *otmp;
|
|
|
|
otmph = (victim == &gy.youmonst) ? uarmc : which_armor(victim, W_ARMC);
|
|
if (!otmph)
|
|
otmph = (victim == &gy.youmonst) ? uarm : which_armor(victim, W_ARM);
|
|
if (!otmph)
|
|
otmph = (victim == &gy.youmonst) ? uarmu : which_armor(victim, W_ARMU);
|
|
|
|
otmp = (victim == &gy.youmonst) ? uarmh : which_armor(victim, W_ARMH);
|
|
if (otmp && (!otmph || !rn2(4)))
|
|
otmph = otmp;
|
|
otmp = (victim == &gy.youmonst) ? uarmg : which_armor(victim, W_ARMG);
|
|
if (otmp && (!otmph || !rn2(4)))
|
|
otmph = otmp;
|
|
otmp = (victim == &gy.youmonst) ? uarmf : which_armor(victim, W_ARMF);
|
|
if (otmp && (!otmph || !rn2(4)))
|
|
otmph = otmp;
|
|
otmp = (victim == &gy.youmonst) ? uarms : which_armor(victim, W_ARMS);
|
|
if (otmp && (!otmph || !rn2(4)))
|
|
otmph = otmp;
|
|
return otmph;
|
|
}
|
|
|
|
/* used for praying to check and fix levitation trouble */
|
|
struct obj *
|
|
stuck_ring(struct obj *ring, int otyp)
|
|
{
|
|
if (ring != uleft && ring != uright) {
|
|
impossible("stuck_ring: neither left nor right?");
|
|
return (struct obj *) 0;
|
|
}
|
|
|
|
if (ring && ring->otyp == otyp) {
|
|
/* reasons ring can't be removed match those checked by select_off();
|
|
limbless case has extra checks because ordinarily it's temporary */
|
|
if (nolimbs(gy.youmonst.data) && uamul
|
|
&& uamul->otyp == AMULET_OF_UNCHANGING && uamul->cursed)
|
|
return uamul;
|
|
if (welded(uwep) && ((ring == RING_ON_PRIMARY) || bimanual(uwep)))
|
|
return uwep;
|
|
if (uarmg && uarmg->cursed)
|
|
return uarmg;
|
|
if (ring->cursed)
|
|
return ring;
|
|
/* normally outermost layer is processed first, but slippery gloves
|
|
wears off quickly so uncurse ring itself before handling those */
|
|
if (uarmg && Glib)
|
|
return uarmg;
|
|
}
|
|
/* either no ring or not right type or nothing prevents its removal */
|
|
return (struct obj *) 0;
|
|
}
|
|
|
|
/* also for praying; find worn item that confers "Unchanging" attribute */
|
|
struct obj *
|
|
unchanger(void)
|
|
{
|
|
if (uamul && uamul->otyp == AMULET_OF_UNCHANGING)
|
|
return uamul;
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
int
|
|
select_off(struct obj *otmp)
|
|
{
|
|
struct obj *why;
|
|
char buf[BUFSZ];
|
|
|
|
if (!otmp)
|
|
return 0;
|
|
*buf = '\0'; /* lint suppression */
|
|
|
|
/* special ring checks */
|
|
if (otmp == uright || otmp == uleft) {
|
|
struct obj glibdummy;
|
|
|
|
if (nolimbs(gy.youmonst.data)) {
|
|
pline_The("ring is stuck.");
|
|
return 0;
|
|
}
|
|
glibdummy = cg.zeroobj;
|
|
why = 0; /* the item which prevents ring removal */
|
|
if (welded(uwep) && ((otmp == RING_ON_PRIMARY) || bimanual(uwep))) {
|
|
Sprintf(buf, "free a weapon %s", body_part(HAND));
|
|
why = uwep;
|
|
} else if (uarmg && (uarmg->cursed || Glib)) {
|
|
Sprintf(buf, "take off your %s%s",
|
|
Glib ? "slippery " : "", gloves_simple_name(uarmg));
|
|
why = !Glib ? uarmg : &glibdummy;
|
|
}
|
|
if (why) {
|
|
You("cannot %s to remove the ring.", buf);
|
|
set_bknown(why, 1);
|
|
return 0;
|
|
}
|
|
}
|
|
/* special glove checks */
|
|
if (otmp == uarmg) {
|
|
if (welded(uwep)) {
|
|
You("are unable to take off your %s while wielding that %s.",
|
|
c_gloves, is_sword(uwep) ? c_sword : c_weapon);
|
|
set_bknown(uwep, 1);
|
|
return 0;
|
|
} else if (Glib) {
|
|
pline("%s %s are too slippery to take off.",
|
|
uarmg->unpaid ? "The" : "Your", /* simplified Shk_Your() */
|
|
gloves_simple_name(uarmg));
|
|
return 0;
|
|
}
|
|
}
|
|
/* special boot checks */
|
|
if (otmp == uarmf) {
|
|
if (u.utrap && u.utraptype == TT_BEARTRAP) {
|
|
pline_The("bear trap prevents you from pulling your %s out.",
|
|
body_part(FOOT));
|
|
return 0;
|
|
} else if (u.utrap && u.utraptype == TT_INFLOOR) {
|
|
You("are stuck in the %s, and cannot pull your %s out.",
|
|
surface(u.ux, u.uy), makeplural(body_part(FOOT)));
|
|
return 0;
|
|
}
|
|
}
|
|
/* special suit and shirt checks */
|
|
if (otmp == uarm || otmp == uarmu) {
|
|
why = 0; /* the item which prevents disrobing */
|
|
if (uarmc && uarmc->cursed) {
|
|
Sprintf(buf, "remove your %s", cloak_simple_name(uarmc));
|
|
why = uarmc;
|
|
} else if (otmp == uarmu && uarm && uarm->cursed) {
|
|
Sprintf(buf, "remove your %s", c_suit);
|
|
why = uarm;
|
|
} else if (welded(uwep) && bimanual(uwep)) {
|
|
Sprintf(buf, "release your %s",
|
|
is_sword(uwep) ? c_sword : (uwep->otyp == BATTLE_AXE)
|
|
? c_axe
|
|
: c_weapon);
|
|
why = uwep;
|
|
}
|
|
if (why) {
|
|
You("cannot %s to take off %s.", buf, the(xname(otmp)));
|
|
set_bknown(why, 1);
|
|
return 0;
|
|
}
|
|
}
|
|
/* basic curse check */
|
|
if (otmp == uquiver || (otmp == uswapwep && !u.twoweap)) {
|
|
; /* some items can be removed even when cursed */
|
|
} else {
|
|
/* otherwise, this is fundamental */
|
|
if (cursed(otmp))
|
|
return 0;
|
|
}
|
|
|
|
if (otmp == uarm)
|
|
gc.context.takeoff.mask |= WORN_ARMOR;
|
|
else if (otmp == uarmc)
|
|
gc.context.takeoff.mask |= WORN_CLOAK;
|
|
else if (otmp == uarmf)
|
|
gc.context.takeoff.mask |= WORN_BOOTS;
|
|
else if (otmp == uarmg)
|
|
gc.context.takeoff.mask |= WORN_GLOVES;
|
|
else if (otmp == uarmh)
|
|
gc.context.takeoff.mask |= WORN_HELMET;
|
|
else if (otmp == uarms)
|
|
gc.context.takeoff.mask |= WORN_SHIELD;
|
|
else if (otmp == uarmu)
|
|
gc.context.takeoff.mask |= WORN_SHIRT;
|
|
else if (otmp == uleft)
|
|
gc.context.takeoff.mask |= LEFT_RING;
|
|
else if (otmp == uright)
|
|
gc.context.takeoff.mask |= RIGHT_RING;
|
|
else if (otmp == uamul)
|
|
gc.context.takeoff.mask |= WORN_AMUL;
|
|
else if (otmp == ublindf)
|
|
gc.context.takeoff.mask |= WORN_BLINDF;
|
|
else if (otmp == uwep)
|
|
gc.context.takeoff.mask |= W_WEP;
|
|
else if (otmp == uswapwep)
|
|
gc.context.takeoff.mask |= W_SWAPWEP;
|
|
else if (otmp == uquiver)
|
|
gc.context.takeoff.mask |= W_QUIVER;
|
|
|
|
else
|
|
impossible("select_off: %s???", doname(otmp));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct obj *
|
|
do_takeoff(void)
|
|
{
|
|
struct obj *otmp = (struct obj *) 0;
|
|
boolean was_twoweap = u.twoweap;
|
|
struct takeoff_info *doff = &gc.context.takeoff;
|
|
|
|
gc.context.takeoff.mask |= I_SPECIAL; /* set flag for cancel_doff() */
|
|
if (doff->what == W_WEP) {
|
|
if (!cursed(uwep)) {
|
|
setuwep((struct obj *) 0);
|
|
if (was_twoweap)
|
|
You("are no longer wielding either weapon.");
|
|
else
|
|
You("are %s.", empty_handed());
|
|
}
|
|
} else if (doff->what == W_SWAPWEP) {
|
|
setuswapwep((struct obj *) 0);
|
|
You("%sno longer %s.", was_twoweap ? "are " : "",
|
|
was_twoweap ? "wielding two weapons at once"
|
|
: "have a second weapon readied");
|
|
} 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();
|
|
} else if (doff->what == WORN_SHIRT) {
|
|
otmp = uarmu;
|
|
if (!cursed(otmp))
|
|
(void) Shirt_off();
|
|
} 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);
|
|
}
|
|
gc.context.takeoff.mask &= ~I_SPECIAL; /* clear cancel_doff() flag */
|
|
|
|
return otmp;
|
|
}
|
|
|
|
/* occupation callback for 'A' */
|
|
static int
|
|
take_off(void)
|
|
{
|
|
int i;
|
|
struct obj *otmp;
|
|
struct takeoff_info *doff = &gc.context.takeoff;
|
|
|
|
if (doff->what) {
|
|
if (doff->delay > 0) {
|
|
doff->delay--;
|
|
return 1; /* still busy */
|
|
}
|
|
if ((otmp = do_takeoff()) != 0)
|
|
off_msg(otmp);
|
|
doff->mask &= ~doff->what;
|
|
doff->what = 0L;
|
|
}
|
|
|
|
for (i = 0; takeoff_order[i]; i++)
|
|
if (doff->mask & takeoff_order[i]) {
|
|
doff->what = takeoff_order[i];
|
|
break;
|
|
}
|
|
|
|
otmp = (struct obj *) 0;
|
|
doff->delay = 0;
|
|
|
|
if (doff->what == 0L) {
|
|
You("finish %s.", doff->disrobing);
|
|
return 0;
|
|
} 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)
|
|
doff->delay += 2 * objects[uarmc->otyp].oc_delay + 1;
|
|
} else if (doff->what == WORN_CLOAK) {
|
|
otmp = uarmc;
|
|
} else if (doff->what == WORN_BOOTS) {
|
|
otmp = uarmf;
|
|
} else if (doff->what == WORN_GLOVES) {
|
|
otmp = uarmg;
|
|
} else if (doff->what == WORN_HELMET) {
|
|
otmp = uarmh;
|
|
} else if (doff->what == WORN_SHIELD) {
|
|
otmp = uarms;
|
|
} else if (doff->what == WORN_SHIRT) {
|
|
otmp = uarmu;
|
|
/* add the time to take off and put back on armor and/or cloak */
|
|
if (uarm)
|
|
doff->delay += 2 * objects[uarm->otyp].oc_delay;
|
|
if (uarmc)
|
|
doff->delay += 2 * objects[uarmc->otyp].oc_delay + 1;
|
|
} 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) {
|
|
/* [this used to be 2, but 'R' (and 'T') only require 1 turn to
|
|
remove a blindfold, so 'A' shouldn't have been requiring 2] */
|
|
doff->delay = 1;
|
|
} else {
|
|
impossible("take_off: taking off %lx", doff->what);
|
|
return 0; /* force done */
|
|
}
|
|
|
|
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
|
|
* 1 here to account for it.
|
|
*/
|
|
if (doff->delay > 0)
|
|
doff->delay--;
|
|
|
|
set_occupation(take_off, doff->disrobing, 0);
|
|
return 1; /* get busy */
|
|
}
|
|
|
|
/* clear saved context to avoid inappropriate resumption of interrupted 'A' */
|
|
void
|
|
reset_remarm(void)
|
|
{
|
|
gc.context.takeoff.what = gc.context.takeoff.mask = 0L;
|
|
gc.context.takeoff.disrobing[0] = '\0';
|
|
}
|
|
|
|
/* the #takeoffall command -- remove multiple worn items */
|
|
int
|
|
doddoremarm(void)
|
|
{
|
|
int result = 0;
|
|
|
|
if (gc.context.takeoff.what || gc.context.takeoff.mask) {
|
|
You("continue %s.", gc.context.takeoff.disrobing);
|
|
set_occupation(take_off, gc.context.takeoff.disrobing, 0);
|
|
return ECMD_OK;
|
|
} else if (!uwep && !uswapwep && !uquiver && !uamul && !ublindf
|
|
&& !uleft && !uright && !wearing_armor()) {
|
|
You("are not wearing anything.");
|
|
return ECMD_OK;
|
|
}
|
|
|
|
add_valid_menu_class(0); /* reset */
|
|
if (flags.menu_style != MENU_TRADITIONAL
|
|
|| (result = ggetobj("take off", select_off, 0, FALSE,
|
|
(unsigned *) 0)) < -1)
|
|
result = menu_remarm(result);
|
|
|
|
if (gc.context.takeoff.mask) {
|
|
(void) strncpy(gc.context.takeoff.disrobing,
|
|
(((gc.context.takeoff.mask & ~W_WEAPONS) != 0)
|
|
/* default activity for armor and/or accessories,
|
|
possibly combined with weapons */
|
|
? "disrobing"
|
|
/* specific activity when handling weapons only */
|
|
: "disarming"), CONTEXTVERBSZ);
|
|
(void) take_off();
|
|
}
|
|
/* The time to perform the command is already completely accounted for
|
|
* in take_off(); if we return 1, that would add an extra turn to each
|
|
* disrobe.
|
|
*/
|
|
return ECMD_OK;
|
|
}
|
|
|
|
/* #altunwield - just unwield alternate weapon, item-action '-' when picking
|
|
uswapwep from context-sensitive inventory */
|
|
int
|
|
remarm_swapwep(void)
|
|
{
|
|
struct _cmd_queue cq, *cmdq;
|
|
unsigned oldbknown;
|
|
|
|
if ((cmdq = cmdq_pop()) != 0) {
|
|
/* '-' uswapwep item-action picked from context-sensitive invent */
|
|
cq = *cmdq;
|
|
free(cmdq);
|
|
} else {
|
|
cq.typ = CMDQ_KEY;
|
|
cq.key = '\0'; /* something other than '-' */
|
|
}
|
|
if (cq.typ != CMDQ_KEY || cq.key != '-' || !uswapwep)
|
|
return ECMD_FAIL;
|
|
|
|
oldbknown = uswapwep->bknown; /* when deciding whether this command
|
|
* has done something that takes time,
|
|
* behave as if a cursed secondary weapon
|
|
* can't be unwielded even though things
|
|
* don't work that way... */
|
|
reset_remarm();
|
|
gc.context.takeoff.what = gc.context.takeoff.mask = W_SWAPWEP;
|
|
(void) do_takeoff();
|
|
return (!uswapwep || uswapwep->bknown != oldbknown) ? ECMD_TIME : ECMD_OK;
|
|
}
|
|
|
|
static int
|
|
menu_remarm(int retry)
|
|
{
|
|
int n, i = 0;
|
|
menu_item *pick_list;
|
|
boolean all_worn_categories = TRUE;
|
|
|
|
if (retry) {
|
|
all_worn_categories = (retry == -2);
|
|
} else if (flags.menu_style == MENU_FULL) {
|
|
all_worn_categories = FALSE;
|
|
n = query_category("What type of things do you want to take off?",
|
|
gi.invent, (WORN_TYPES | ALL_TYPES
|
|
| UNPAID_TYPES | BUCX_TYPES),
|
|
&pick_list, PICK_ANY);
|
|
if (!n)
|
|
return 0;
|
|
for (i = 0; i < n; i++) {
|
|
if (pick_list[i].item.a_int == ALL_TYPES_SELECTED)
|
|
all_worn_categories = TRUE;
|
|
else
|
|
add_valid_menu_class(pick_list[i].item.a_int);
|
|
}
|
|
free((genericptr_t) pick_list);
|
|
} else if (flags.menu_style == MENU_COMBINATION) {
|
|
unsigned ggofeedback = 0;
|
|
|
|
i = ggetobj("take off", select_off, 0, TRUE, &ggofeedback);
|
|
if (ggofeedback & ALL_FINISHED)
|
|
return 0;
|
|
all_worn_categories = (i == -2);
|
|
}
|
|
if (menu_class_present('u')
|
|
|| menu_class_present('B') || menu_class_present('U')
|
|
|| menu_class_present('C') || menu_class_present('X'))
|
|
all_worn_categories = FALSE;
|
|
|
|
n = query_objlist("What do you want to take off?", &gi.invent,
|
|
(SIGNAL_NOMENU | USE_INVLET | INVORDER_SORT),
|
|
&pick_list, PICK_ANY,
|
|
all_worn_categories ? is_worn : is_worn_by_type);
|
|
if (n > 0) {
|
|
for (i = 0; i < n; i++)
|
|
(void) select_off(pick_list[i].item.a_obj);
|
|
free((genericptr_t) pick_list);
|
|
} else if (n < 0 && flags.menu_style != MENU_COMBINATION) {
|
|
There("is nothing else you can remove or unwield.");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* take off the specific worn object and if it still exists after that,
|
|
destroy it (taking off the item might already destroy it by dunking
|
|
hero into lava) */
|
|
static void
|
|
wornarm_destroyed(struct obj *wornarm)
|
|
{
|
|
struct obj *invobj;
|
|
unsigned wornoid = wornarm->o_id;
|
|
|
|
/* cancel_don() resets 'afternmv' when appropriate but doesn't reset
|
|
uarmc/uarm/&c so doing this now won't interfere with the tests in
|
|
'if (wornarm==uarmc) ... else if (wornarm==uarm) ... else ...' */
|
|
if (donning(wornarm))
|
|
cancel_don();
|
|
|
|
if (wornarm == uarmc)
|
|
(void) Cloak_off();
|
|
else if (wornarm == uarm)
|
|
(void) Armor_off();
|
|
else if (wornarm == uarmu)
|
|
(void) Shirt_off();
|
|
else if (wornarm == uarmh)
|
|
(void) Helmet_off();
|
|
else if (wornarm == uarmg)
|
|
(void) Gloves_off();
|
|
else if (wornarm == uarmf)
|
|
(void) Boots_off();
|
|
else if (wornarm == uarms)
|
|
(void) Shield_off();
|
|
|
|
/* 'wornarm' might be destroyed as a side-effect of xxx_off() so
|
|
using carried() to check wornarm->where==OBJ_INVENT is not viable;
|
|
scan invent instead; if already freed it shouldn't be possible to
|
|
have re-used the stale memory for a new item yet but verify o_id
|
|
just in case */
|
|
for (invobj = gi.invent; invobj; invobj = invobj->nobj)
|
|
if (invobj == wornarm && invobj->o_id == wornoid) {
|
|
useup(wornarm);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* returns impacted armor with its in_use bit set,
|
|
* or Null. *resisted is updated to reflect whether
|
|
* it resisted or not */
|
|
static struct obj *
|
|
maybe_destroy_armor(struct obj *armor, struct obj *atmp, boolean *resisted)
|
|
{
|
|
if ((armor != 0) && (!atmp || atmp == armor)
|
|
&& ((*resisted = obj_resists(armor, 0, 90)) == FALSE)) {
|
|
armor->in_use = 1;
|
|
return armor;
|
|
}
|
|
return (struct obj *) 0;
|
|
}
|
|
|
|
/* hit by destroy armor scroll/black dragon breath/monster spell */
|
|
int
|
|
destroy_arm(struct obj *atmp)
|
|
{
|
|
struct obj *otmp = (struct obj *) 0;
|
|
boolean losing_gloves = FALSE, resisted = FALSE,
|
|
resistedc = FALSE, resistedsuit = FALSE;
|
|
/*
|
|
* Note: if the cloak resisted, then the suit or shirt underneath
|
|
* wouldn't be impacted either. Likewise, if the suit resisted, the
|
|
* shirt underneath wouldn't be impacted. Since there are no artifact
|
|
* cloaks or suits right now, this is unlikely to come into effect,
|
|
* but it should behave appropriately if/when the situation changes.
|
|
*/
|
|
|
|
if ((otmp = maybe_destroy_armor(uarmc, atmp, &resistedc)) != 0) {
|
|
urgent_pline("Your %s crumbles and turns to dust!",
|
|
/* cloak/robe/apron/smock (ID'd apron)/wrapping */
|
|
cloak_simple_name(otmp));
|
|
} else if (!resistedc
|
|
&& (otmp = maybe_destroy_armor(uarm, atmp, &resistedsuit)) != 0) {
|
|
const char *suit = suit_simple_name(otmp);
|
|
|
|
/* for gold DSM, we don't want Armor_gone() to report that it
|
|
stops shining _after_ we've been told that it is destroyed */
|
|
if (otmp->lamplit)
|
|
end_burn(otmp, FALSE);
|
|
urgent_pline("Your %s %s to dust and %s to the %s!",
|
|
/* suit might be "dragon scales" so vtense() is needed */
|
|
suit, vtense(suit, "turn"), vtense(suit, "fall"),
|
|
surface(u.ux, u.uy));
|
|
} else if (!resistedc && !resistedsuit
|
|
&& (otmp = maybe_destroy_armor(uarmu, atmp, &resisted)) != 0) {
|
|
urgent_pline("Your %s crumbles into tiny threads and falls apart!",
|
|
shirt_simple_name(otmp)); /* always "shirt" */
|
|
} else if ((otmp = maybe_destroy_armor(uarmh, atmp, &resisted)) != 0) {
|
|
urgent_pline("Your %s turns to dust and is blown away!",
|
|
helm_simple_name(otmp)); /* "helm" or "hat" */
|
|
} else if ((otmp = maybe_destroy_armor(uarmg, atmp, &resisted)) != 0) {
|
|
urgent_pline("Your %s vanish!", gloves_simple_name(otmp));
|
|
losing_gloves = TRUE;
|
|
} else if ((otmp = maybe_destroy_armor(uarmf, atmp, &resisted)) != 0) {
|
|
urgent_pline("Your %s disintegrate!", boots_simple_name(otmp));
|
|
} else if ((otmp = maybe_destroy_armor(uarms, atmp, &resisted)) != 0) {
|
|
urgent_pline("Your %s crumbles away!", shield_simple_name(otmp));
|
|
} else {
|
|
return 0; /* could not destroy anything */
|
|
}
|
|
|
|
/* cancel_don() if applicable, Cloak_off()/Armor_off()/&c, and useup() */
|
|
wornarm_destroyed(otmp);
|
|
/* glove loss means wielded weapon will be touched */
|
|
if (losing_gloves)
|
|
selftouch("You");
|
|
|
|
stop_occupation();
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
adj_abon(struct obj *otmp, schar delta)
|
|
{
|
|
if (uarmg && uarmg == otmp && otmp->otyp == GAUNTLETS_OF_DEXTERITY) {
|
|
if (delta) {
|
|
makeknown(uarmg->otyp);
|
|
ABON(A_DEX) += (delta);
|
|
}
|
|
disp.botl = TRUE;
|
|
}
|
|
if (uarmh && uarmh == otmp && otmp->otyp == HELM_OF_BRILLIANCE) {
|
|
if (delta) {
|
|
makeknown(uarmh->otyp);
|
|
ABON(A_INT) += (delta);
|
|
ABON(A_WIS) += (delta);
|
|
}
|
|
disp.botl = TRUE;
|
|
}
|
|
}
|
|
|
|
/* decide whether a worn item is covered up by some other worn item,
|
|
used for dipping into liquid and applying grease;
|
|
some criteria are different than select_off()'s */
|
|
boolean
|
|
inaccessible_equipment(struct obj *obj,
|
|
const char *verb, /* "dip" or "grease", or null to
|
|
avoid messages */
|
|
boolean only_if_known_cursed) /* ignore covering unless
|
|
known to be cursed */
|
|
{
|
|
static NEARDATA const char need_to_take_off_outer_armor[] =
|
|
"need to take off %s to %s %s.";
|
|
char buf[BUFSZ];
|
|
boolean anycovering = !only_if_known_cursed; /* more comprehensible... */
|
|
#define BLOCKSACCESS(x) (anycovering || ((x)->cursed && (x)->bknown))
|
|
|
|
if (!obj || !obj->owornmask)
|
|
return FALSE; /* not inaccessible */
|
|
|
|
/* check for suit covered by cloak */
|
|
if (obj == uarm && uarmc && BLOCKSACCESS(uarmc)) {
|
|
if (verb) {
|
|
Strcpy(buf, yname(uarmc));
|
|
You(need_to_take_off_outer_armor, buf, verb, yname(obj));
|
|
}
|
|
return TRUE;
|
|
}
|
|
/* check for shirt covered by suit and/or cloak */
|
|
if (obj == uarmu
|
|
&& ((uarm && BLOCKSACCESS(uarm)) || (uarmc && BLOCKSACCESS(uarmc)))) {
|
|
if (verb) {
|
|
char cloaktmp[QBUFSZ], suittmp[QBUFSZ];
|
|
/* if sameprefix, use yname and xname to get "your cloak and suit"
|
|
or "Manlobbi's cloak and suit"; otherwise, use yname and yname
|
|
to get "your cloak and Manlobbi's suit" or vice versa */
|
|
boolean sameprefix = (uarm && uarmc
|
|
&& !strcmp(shk_your(cloaktmp, uarmc),
|
|
shk_your(suittmp, uarm)));
|
|
|
|
*buf = '\0';
|
|
if (uarmc)
|
|
Strcat(buf, yname(uarmc));
|
|
if (uarm && uarmc)
|
|
Strcat(buf, " and ");
|
|
if (uarm)
|
|
Strcat(buf, sameprefix ? xname(uarm) : yname(uarm));
|
|
You(need_to_take_off_outer_armor, buf, verb, yname(obj));
|
|
}
|
|
return TRUE;
|
|
}
|
|
/* check for ring covered by gloves */
|
|
if ((obj == uleft || obj == uright) && uarmg && BLOCKSACCESS(uarmg)) {
|
|
if (verb) {
|
|
Strcpy(buf, yname(uarmg));
|
|
You(need_to_take_off_outer_armor, buf, verb, yname(obj));
|
|
}
|
|
return TRUE;
|
|
}
|
|
/* item is not inaccessible */
|
|
return FALSE;
|
|
}
|
|
|
|
/* not a getobj callback - unifies code among the other 4 getobj callbacks */
|
|
static int
|
|
equip_ok(struct obj *obj, boolean removing, boolean accessory)
|
|
{
|
|
boolean is_worn;
|
|
long dummymask = 0;
|
|
|
|
if (!obj)
|
|
return GETOBJ_EXCLUDE;
|
|
|
|
/* ignore for putting on if already worn, or removing if not worn */
|
|
is_worn = ((obj->owornmask & (W_ARMOR | W_ACCESSORY)) != 0);
|
|
if (removing ^ is_worn)
|
|
return GETOBJ_EXCLUDE_INACCESS;
|
|
|
|
/* exclude most object classes outright */
|
|
if (obj->oclass != ARMOR_CLASS && obj->oclass != RING_CLASS
|
|
&& obj->oclass != AMULET_CLASS) {
|
|
/* ... except for a few wearable exceptions outside these classes */
|
|
if (obj->otyp != MEAT_RING && obj->otyp != BLINDFOLD
|
|
&& obj->otyp != TOWEL && obj->otyp != LENSES)
|
|
return GETOBJ_EXCLUDE;
|
|
}
|
|
|
|
/* armor with 'P' or 'R' or accessory with 'W' or 'T' */
|
|
if (accessory ^ (obj->oclass != ARMOR_CLASS))
|
|
return GETOBJ_DOWNPLAY;
|
|
|
|
/* armor we can't wear, e.g. from polyform */
|
|
if (obj->oclass == ARMOR_CLASS && !removing
|
|
&& !canwearobj(obj, &dummymask, FALSE))
|
|
return GETOBJ_DOWNPLAY;
|
|
|
|
/* Possible extension: downplay items (both accessories and armor) which
|
|
* can't be worn because the slot is filled with something else. */
|
|
|
|
/* removing inaccessible equipment */
|
|
if (removing && inaccessible_equipment(obj, (const char *) 0,
|
|
(obj->oclass == RING_CLASS)))
|
|
return GETOBJ_EXCLUDE_INACCESS;
|
|
|
|
/* all good to go */
|
|
return GETOBJ_SUGGEST;
|
|
}
|
|
|
|
/* getobj callback for P command */
|
|
static int
|
|
puton_ok(struct obj *obj)
|
|
{
|
|
return equip_ok(obj, FALSE, TRUE);
|
|
}
|
|
|
|
/* getobj callback for R command */
|
|
static int
|
|
remove_ok(struct obj *obj)
|
|
{
|
|
return equip_ok(obj, TRUE, TRUE);
|
|
}
|
|
|
|
/* getobj callback for W command */
|
|
static int
|
|
wear_ok(struct obj *obj)
|
|
{
|
|
return equip_ok(obj, FALSE, FALSE);
|
|
}
|
|
|
|
/* getobj callback for T command */
|
|
static int
|
|
takeoff_ok(struct obj *obj)
|
|
{
|
|
return equip_ok(obj, TRUE, FALSE);
|
|
}
|
|
|
|
/*do_wear.c*/
|