/* SCCS Id: @(#)wield.c 3.5 2006/07/14 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" /* KMH -- Differences between the three weapon slots. * * The main weapon (uwep): * 1. Is filled by the (w)ield command. * 2. Can be filled with any type of item. * 3. May be carried in one or both hands. * 4. Is used as the melee weapon and as the launcher for * ammunition. * 5. Only conveys intrinsics when it is a weapon, weapon-tool, * or artifact. * 6. Certain cursed items will weld to the hand and cannot be * unwielded or dropped. See erodeable_wep() and will_weld() * below for the list of which items apply. * * The secondary weapon (uswapwep): * 1. Is filled by the e(x)change command, which swaps this slot * with the main weapon. If the "pushweapon" option is set, * the (w)ield command will also store the old weapon in the * secondary slot. * 2. Can be field with anything that will fit in the main weapon * slot; that is, any type of item. * 3. Is usually NOT considered to be carried in the hands. * That would force too many checks among the main weapon, * second weapon, shield, gloves, and rings; and it would * further be complicated by bimanual weapons. A special * exception is made for two-weapon combat. * 4. Is used as the second weapon for two-weapon combat, and as * a convenience to swap with the main weapon. * 5. Never conveys intrinsics. * 6. Cursed items never weld (see #3 for reasons), but they also * prevent two-weapon combat. * * The quiver (uquiver): * 1. Is filled by the (Q)uiver command. * 2. Can be filled with any type of item. * 3. Is considered to be carried in a special part of the pack. * 4. Is used as the item to throw with the (f)ire command. * This is a convenience over the normal (t)hrow command. * 5. Never conveys intrinsics. * 6. Cursed items never weld; their effect is handled by the normal * throwing code. * * No item may be in more than one of these slots. */ STATIC_DCL boolean FDECL(cant_wield_corpse, (struct obj *)); STATIC_DCL int FDECL(ready_weapon, (struct obj *)); /* used by will_weld() */ /* probably should be renamed */ #define erodeable_wep(optr) ((optr)->oclass == WEAPON_CLASS \ || is_weptool(optr) \ || (optr)->otyp == HEAVY_IRON_BALL \ || (optr)->otyp == IRON_CHAIN) /* used by welded(), and also while wielding */ #define will_weld(optr) ((optr)->cursed \ && (erodeable_wep(optr) \ || (optr)->otyp == TIN_OPENER)) /*** Functions that place a given item in a slot ***/ /* Proper usage includes: * 1. Initializing the slot during character generation or a * restore. * 2. Setting the slot due to a player's actions. * 3. If one of the objects in the slot are split off, these * functions can be used to put the remainder back in the slot. * 4. Putting an item that was thrown and returned back into the slot. * 5. Emptying the slot, by passing a null object. NEVER pass * zeroobj! * * If the item is being moved from another slot, it is the caller's * responsibility to handle that. It's also the caller's responsibility * to print the appropriate messages. */ void setuwep(obj) register struct obj *obj; { struct obj *olduwep = uwep; if (obj == uwep) return; /* necessary to not set unweapon */ /* This message isn't printed in the caller because it happens * *whenever* Sunsword is unwielded, from whatever cause. */ setworn(obj, W_WEP); if (uwep == obj && artifact_light(olduwep) && olduwep->lamplit) { end_burn(olduwep, FALSE); if (!Blind) pline("%s glowing.", Tobjnam(olduwep, "stop")); } /* Note: Explicitly wielding a pick-axe will not give a "bashing" * message. Wielding one via 'a'pplying it will. * 3.2.2: Wielding arbitrary objects will give bashing message too. */ if (obj) { unweapon = (obj->oclass == WEAPON_CLASS) ? is_launcher(obj) || is_ammo(obj) || is_missile(obj) || (is_pole(obj) #ifdef STEED && !u.usteed #endif ) : !is_weptool(obj); } else unweapon = TRUE; /* for "bare hands" message */ update_inventory(); } STATIC_OVL boolean cant_wield_corpse(obj) struct obj *obj; { char kbuf[BUFSZ]; if (uarmg || obj->otyp != CORPSE || !touch_petrifies(&mons[obj->corpsenm]) || Stone_resistance) return FALSE; /* Prevent wielding cockatrice when not wearing gloves --KAA */ You("wield %s in your bare %s.", corpse_xname(obj, (const char *)0, CXN_PFX_THE), makeplural(body_part(HAND))); Sprintf(kbuf, "wielding %s bare-handed", killer_xname(obj)); instapetrify(kbuf); return TRUE; } STATIC_OVL int ready_weapon(wep) struct obj *wep; { /* Separated function so swapping works easily */ int res = 0; if (!wep) { /* No weapon */ if (uwep) { You("are empty %s.", body_part(HANDED)); setuwep((struct obj *) 0); res++; } else You("are already empty %s.", body_part(HANDED)); } else if (wep->otyp == CORPSE && cant_wield_corpse(wep)) { /* hero must have been life-saved to get here; use a turn */ res++; /* corpse won't be wielded */ } else if (uarms && bimanual(wep)) You("cannot wield a two-handed %s while wearing a shield.", is_sword(wep) ? "sword" : wep->otyp == BATTLE_AXE ? "axe" : "weapon"); else if (wep->oartifact && !touch_artifact(wep, &youmonst)) { res++; /* takes a turn even though it doesn't get wielded */ } else { /* Weapon WILL be wielded after this point */ res++; if (will_weld(wep)) { const char *tmp = xname(wep), *thestr = "The "; if (strncmp(tmp, thestr, 4) && !strncmp(The(tmp),thestr,4)) tmp = thestr; else tmp = ""; pline("%s%s %s to your %s!", tmp, aobjnam(wep, "weld"), (wep->quan == 1L) ? "itself" : "themselves", /* a3 */ bimanual(wep) ? (const char *)makeplural(body_part(HAND)) : body_part(HAND)); wep->bknown = TRUE; } else { /* The message must be printed before setuwep (since * you might die and be revived from changing weapons), * and the message must be before the death message and * Lifesaved rewielding. Yet we want the message to * say "weapon in hand", thus this kludge. */ long dummy = wep->owornmask; wep->owornmask |= W_WEP; prinv((char *)0, wep, 0L); wep->owornmask = dummy; } setuwep(wep); /* KMH -- Talking artifacts are finally implemented */ arti_speak(wep); if (artifact_light(wep) && !wep->lamplit) { begin_burn(wep, FALSE); if (!Blind) pline("%s to glow brilliantly!", Tobjnam(wep, "begin")); } #if 0 /* we'll get back to this someday, but it's not balanced yet */ if (Race_if(PM_ELF) && !wep->oartifact && objects[wep->otyp].oc_material == IRON) { /* Elves are averse to wielding cold iron */ You("have an uneasy feeling about wielding cold iron."); change_luck(-1); } #endif if (wep->unpaid) { struct monst *this_shkp; if ((this_shkp = shop_keeper(inside_shop(u.ux, u.uy))) != (struct monst *)0) { pline("%s says \"You be careful with my %s!\"", shkname(this_shkp), xname(wep)); } } } return(res); } void setuqwep(obj) register struct obj *obj; { setworn(obj, W_QUIVER); update_inventory(); } void setuswapwep(obj) register struct obj *obj; { setworn(obj, W_SWAPWEP); update_inventory(); } /*** Commands to change particular slot(s) ***/ static NEARDATA const char wield_objs[] = { ALL_CLASSES, ALLOW_NONE, WEAPON_CLASS, TOOL_CLASS, 0 }; #ifdef GOLDOBJ static NEARDATA const char ready_objs[] = { COIN_CLASS, ALL_CLASSES, ALLOW_NONE, WEAPON_CLASS, 0 }; static NEARDATA const char bullets[] = /* (note: different from dothrow.c) */ { COIN_CLASS, ALL_CLASSES, ALLOW_NONE, GEM_CLASS, WEAPON_CLASS, 0 }; #else static NEARDATA const char ready_objs[] = { ALL_CLASSES, ALLOW_NONE, WEAPON_CLASS, 0 }; static NEARDATA const char bullets[] = /* (note: different from dothrow.c) */ { ALL_CLASSES, ALLOW_NONE, GEM_CLASS, WEAPON_CLASS, 0 }; #endif int dowield() { register struct obj *wep, *oldwep; int result; /* May we attempt this? */ multi = 0; if (cantwield(youmonst.data)) { pline("Don't be ridiculous!"); return(0); } /* Prompt for a new weapon */ if (!(wep = getobj(wield_objs, "wield"))) /* Cancelled */ return (0); else if (wep == uwep) { You("are already wielding that!"); if (is_weptool(wep)) unweapon = FALSE; /* [see setuwep()] */ return (0); } else if (welded(uwep)) { weldmsg(uwep); /* previously interrupted armor removal mustn't be resumed */ reset_remarm(); return (0); } /* Handle no object, or object in other slot */ if (wep == &zeroobj) wep = (struct obj *) 0; else if (wep == uswapwep) return (doswapweapon()); else if (wep == uquiver) setuqwep((struct obj *) 0); else if (wep->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL #ifdef STEED | W_SADDLE #endif )) { You("cannot wield that!"); return (0); } /* Set your new primary weapon */ oldwep = uwep; result = ready_weapon(wep); if (flags.pushweapon && oldwep && uwep != oldwep) setuswapwep(oldwep); untwoweapon(); return (result); } int doswapweapon() { register struct obj *oldwep, *oldswap; int result = 0; /* May we attempt this? */ multi = 0; if (cantwield(youmonst.data)) { pline("Don't be ridiculous!"); return(0); } if (welded(uwep)) { weldmsg(uwep); return (0); } /* Unwield your current secondary weapon */ oldwep = uwep; oldswap = uswapwep; setuswapwep((struct obj *) 0); /* Set your new primary weapon */ result = ready_weapon(oldswap); /* Set your new secondary weapon */ if (uwep == oldwep) /* Wield failed for some reason */ setuswapwep(oldswap); else { setuswapwep(oldwep); if (uswapwep) prinv((char *)0, uswapwep, 0L); else You("have no secondary weapon readied."); } if (u.twoweap && !can_twoweapon()) untwoweapon(); return (result); } int dowieldquiver() { register struct obj *newquiver; const char *quivee_types = (uslinging() || (uswapwep && objects[uswapwep->otyp].oc_skill == P_SLING)) ? bullets : ready_objs; /* Since the quiver isn't in your hands, don't check cantwield(), */ /* will_weld(), touch_petrifies(), etc. */ multi = 0; /* Because 'Q' used to be quit... */ if (flags.suppress_alert < FEATURE_NOTICE_VER(3,3,0)) pline("Note: Please use #quit if you wish to exit the game."); /* Prompt for a new quiver */ if (!(newquiver = getobj(quivee_types, "ready"))) /* Cancelled */ return (0); /* Handle no object, or object in other slot */ /* Any type is okay, since we give no intrinsics anyways */ if (newquiver == &zeroobj) { /* Explicitly nothing */ if (uquiver) { You("now have no ammunition readied."); setuqwep(newquiver = (struct obj *) 0); } else { You("already have no ammunition readied!"); return(0); } } else if (newquiver == uquiver) { pline("That ammunition is already readied!"); return(0); } else if (newquiver == uwep) { /* Prevent accidentally readying the main weapon */ pline("%s already being used as a weapon!", !is_plural(uwep) ? "That is" : "They are"); return(0); } else if (newquiver->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL #ifdef STEED | W_SADDLE #endif )) { You("cannot ready that!"); return (0); } else { long dummy; /* Check if it's the secondary weapon */ if (newquiver == uswapwep) { setuswapwep((struct obj *) 0); untwoweapon(); } /* Okay to put in quiver; print it */ dummy = newquiver->owornmask; newquiver->owornmask |= W_QUIVER; prinv((char *)0, newquiver, 0L); newquiver->owornmask = dummy; } /* Finally, place it in the quiver */ setuqwep(newquiver); /* Take no time since this is a convenience slot */ return (0); } /* used for #rub and for applying pick-axe, whip, grappling hook, or polearm */ /* (moved from apply.c) */ boolean wield_tool(obj, verb) struct obj *obj; const char *verb; /* "rub",&c */ { const char *what; boolean more_than_1; if (obj == uwep) return TRUE; /* nothing to do if already wielding it */ if (!verb) verb = "wield"; what = xname(obj); more_than_1 = (obj->quan > 1L || strstri(what, "pair of ") != 0 || strstri(what, "s of ") != 0); if (obj->owornmask & (W_ARMOR|W_RING|W_AMUL|W_TOOL)) { You_cant("%s %s while wearing %s.", verb, yname(obj), more_than_1 ? "them" : "it"); return FALSE; } if (welded(uwep)) { if (flags.verbose) { const char *hand = body_part(HAND); if (bimanual(uwep)) hand = makeplural(hand); if (strstri(what, "pair of ") != 0) more_than_1 = FALSE; pline( "Since your weapon is welded to your %s, you cannot %s %s %s.", hand, verb, more_than_1 ? "those" : "that", xname(obj)); } else { You_cant("do that."); } return FALSE; } if (cantwield(youmonst.data)) { You_cant("hold %s strongly enough.", more_than_1 ? "them" : "it"); return FALSE; } /* check shield */ if (uarms && bimanual(obj)) { You("cannot %s a two-handed %s while wearing a shield.", verb, (obj->oclass == WEAPON_CLASS) ? "weapon" : "tool"); return FALSE; } if (uquiver == obj) setuqwep((struct obj *)0); if (uswapwep == obj) { (void) doswapweapon(); /* doswapweapon might fail */ if (uswapwep == obj) return FALSE; } else { struct obj *oldwep = uwep; You("now wield %s.", doname(obj)); setuwep(obj); if (flags.pushweapon && oldwep && uwep != oldwep) setuswapwep(oldwep); } if (uwep != obj) return FALSE; /* rewielded old object after dying */ /* applying weapon or tool that gets wielded ends two-weapon combat */ if (u.twoweap) untwoweapon(); if (obj->oclass != WEAPON_CLASS) unweapon = TRUE; return TRUE; } int can_twoweapon() { struct obj *otmp; #define NOT_WEAPON(obj) (!is_weptool(obj) && obj->oclass != WEAPON_CLASS) if (!could_twoweap(youmonst.data)) { if (Upolyd) You_cant("use two weapons in your current form."); else pline("%s aren't able to use two weapons at once.", makeplural((flags.female && urole.name.f) ? urole.name.f : urole.name.m)); } else if (!uwep || !uswapwep) Your("%s%s%s empty.", uwep ? "left " : uswapwep ? "right " : "", body_part(HAND), (!uwep && !uswapwep) ? "s are" : " is"); else if (NOT_WEAPON(uwep) || NOT_WEAPON(uswapwep)) { otmp = NOT_WEAPON(uwep) ? uwep : uswapwep; pline("%s %s.", Yname2(otmp), is_plural(otmp) ? "aren't weapons" : "isn't a weapon"); } else if (bimanual(uwep) || bimanual(uswapwep)) { otmp = bimanual(uwep) ? uwep : uswapwep; pline("%s isn't one-handed.", Yname2(otmp)); } else if (uarms) You_cant("use two weapons while wearing a shield."); else if (uswapwep->oartifact) pline("%s being held second to another weapon!", Yobjnam2(uswapwep, "resist")); else if (uswapwep->otyp == CORPSE && cant_wield_corpse(uswapwep)) { /* [Note: NOT_WEAPON() check prevents ever getting here...] */ ; /* must be life-saved to reach here; return FALSE */ } else if (Glib || uswapwep->cursed) { if (!Glib) uswapwep->bknown = TRUE; drop_uswapwep(); } else return (TRUE); return (FALSE); } void drop_uswapwep() { char str[BUFSZ]; struct obj *obj = uswapwep; /* Avoid trashing makeplural's static buffer */ Strcpy(str, makeplural(body_part(HAND))); pline("%s from your %s!", Yobjnam2(obj, "slip"), str); dropx(obj); } int dotwoweapon() { /* You can always toggle it off */ if (u.twoweap) { You("switch to your primary weapon."); u.twoweap = 0; update_inventory(); return (0); } /* May we use two weapons? */ if (can_twoweapon()) { /* Success! */ You("begin two-weapon combat."); u.twoweap = 1; update_inventory(); return (rnd(20) > ACURR(A_DEX)); } return (0); } /*** Functions to empty a given slot ***/ /* These should be used only when the item can't be put back in * the slot by life saving. Proper usage includes: * 1. The item has been eaten, stolen, burned away, or rotted away. * 2. Making an item disappear for a bones pile. */ void uwepgone() { if (uwep) { if (artifact_light(uwep) && uwep->lamplit) { end_burn(uwep, FALSE); if (!Blind) pline("%s glowing.", Tobjnam(uwep, "stop")); } setworn((struct obj *)0, W_WEP); unweapon = TRUE; update_inventory(); } } void uswapwepgone() { if (uswapwep) { setworn((struct obj *)0, W_SWAPWEP); update_inventory(); } } void uqwepgone() { if (uquiver) { setworn((struct obj *)0, W_QUIVER); update_inventory(); } } void untwoweapon() { if (u.twoweap) { You("can no longer use two weapons at once."); u.twoweap = FALSE; update_inventory(); } return; } /* Maybe rust object, or corrode it if acid damage is called for. * Returns TRUE if something happened. */ boolean erode_obj(target, acid_dmg, fade_scrolls, for_dip) struct obj *target; /* object (e.g. weapon or armor) to erode */ boolean acid_dmg; boolean fade_scrolls; boolean for_dip; { int erosion; struct monst *victim; boolean vismon, visobj, chill; boolean ret = FALSE; boolean already_affected = FALSE; if (!target) return FALSE; victim = carried(target) ? &youmonst : mcarried(target) ? target->ocarry : (struct monst *)0; vismon = victim && (victim != &youmonst) && canseemon(victim); visobj = !victim && cansee(bhitpos.x, bhitpos.y); /* assume thrown */ if (!acid_dmg && target->lamplit) { already_affected = snuff_lit(target); if (already_affected) ret = TRUE; } erosion = acid_dmg ? target->oeroded2 : target->oeroded; if (target->greased) { grease_protect(target,(char *)0,victim); ret = TRUE; } else if (target->oclass == SCROLL_CLASS) { if(fade_scrolls && target->otyp != SCR_BLANK_PAPER #ifdef MAIL && target->otyp != SCR_MAIL #endif ) { if (!Blind) { if ((victim == &youmonst) || vismon || visobj) pline("%s.", Yobjnam2(target, "fade")); } target->otyp = SCR_BLANK_PAPER; target->spe = 0; ret = TRUE; } } else if (target->oartifact && /* (no artifacts currently meet either of these criteria) */ arti_immune(target, acid_dmg ? AD_ACID : AD_RUST)) { if (flags.verbose) { if (victim == &youmonst) pline("%s.", Yobjnam2(target, "sizzle")); } /* no damage to object */ } else if (target->oartifact && !acid_dmg && /* cold and fire provide partial protection against rust */ ((chill = arti_immune(target, AD_COLD)) != 0 || arti_immune(target, AD_FIRE)) && /* once rusted, the chance for further rusting goes up */ rn2(2 * (MAX_ERODE + 1 - erosion))) { if (flags.verbose && target->oclass == WEAPON_CLASS) { if (victim == &youmonst || vismon || visobj) pline("%s some water.", Yobjnam2(target, chill ? "freeze" : "boil")); } /* no damage to object */ } else if (target->oerodeproof || (acid_dmg ? !is_corrodeable(target) : !is_rustprone(target))) { if (flags.verbose || !(target->oerodeproof && target->rknown)) { if (((victim == &youmonst) || vismon) && !for_dip) pline("%s not %s.", Yobjnam2(target, "are"), already_affected ? "harmed" : "affected"); /* no message if not carried or dipping */ } if (target->oerodeproof) target->rknown = !for_dip; } else if (erosion < MAX_ERODE) { if ((victim == &youmonst) || vismon || visobj) pline("%s%s!", Yobjnam2(target, acid_dmg ? "corrode" : "rust"), erosion+1 == MAX_ERODE ? " completely" : erosion ? " further" : ""); if (acid_dmg) target->oeroded2++; else target->oeroded++; ret = TRUE; } else { if (flags.verbose && !for_dip) { if (victim == &youmonst) pline("%s completely %s.", Yobjnam2(target, Blind ? "feel" : "look"), acid_dmg ? "corroded" : "rusty"); else if (vismon || visobj) pline("%s completely %s.", Yobjnam2(target, "look"), acid_dmg ? "corroded" : "rusty"); } } return ret; } int chwepon(otmp, amount) register struct obj *otmp; register int amount; { const char *color = hcolor((amount < 0) ? NH_BLACK : NH_BLUE); const char *xtime, *wepname = ""; boolean multiple; int otyp = STRANGE_OBJECT; if(!uwep || (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep))) { char buf[BUFSZ]; Sprintf(buf, "Your %s %s.", makeplural(body_part(HAND)), (amount >= 0) ? "twitch" : "itch"); strange_feeling(otmp, buf); exercise(A_DEX, (boolean) (amount >= 0)); return(0); } if (otmp && otmp->oclass == SCROLL_CLASS) otyp = otmp->otyp; if (uwep->otyp == WORM_TOOTH && amount >= 0) { multiple = (uwep->quan > 1L); /* order: message, transformation, shop handling */ Your("%s %s much sharper now.", simpleonames(uwep), multiple ? "fuse, and become" : "is"); uwep->otyp = CRYSKNIFE; uwep->oerodeproof = 0; if (multiple) { uwep->quan = 1L; uwep->owt = weight(uwep); } if (uwep->cursed) uncurse(uwep); /* update shop bill to reflect new higher value */ if (uwep->unpaid) alter_cost(uwep, 0L); if (otyp != STRANGE_OBJECT) makeknown(otyp); if (multiple) encumber_msg(); return 1; } else if (uwep->otyp == CRYSKNIFE && amount < 0) { multiple = (uwep->quan > 1L); /* order matters: message, shop handling, transformation */ Your("%s %s much duller now.", simpleonames(uwep), multiple ? "fuse, and become" : "is"); costly_alteration(uwep, COST_DEGRD); /* DECHNT? other? */ uwep->otyp = WORM_TOOTH; uwep->oerodeproof = 0; if (multiple) { uwep->quan = 1L; uwep->owt = weight(uwep); } if (otyp != STRANGE_OBJECT && otmp->bknown) makeknown(otyp); if (multiple) encumber_msg(); return 1; } if (has_oname(uwep)) wepname = ONAME(uwep); if (amount < 0 && uwep->oartifact && restrict_name(uwep, wepname)) { if (!Blind) pline("%s %s.", Yobjnam2(uwep, "faintly glow"), color); return(1); } /* there is a (soft) upper and lower limit to uwep->spe */ if(((uwep->spe > 5 && amount >= 0) || (uwep->spe < -5 && amount < 0)) && rn2(3)) { if (!Blind) pline("%s %s for a while and then %s.", Yobjnam2(uwep, "violently glow"), color, otense(uwep, "evaporate")); else pline("%s.", Yobjnam2(uwep, "evaporate")); useupall(uwep); /* let all of them disappear */ return(1); } if (!Blind) { xtime = (amount*amount == 1) ? "moment" : "while"; pline("%s %s for a %s.", Yobjnam2(uwep, amount == 0 ? "violently glow" : "glow"), color, xtime); if (otyp != STRANGE_OBJECT && uwep->known && (amount > 0 || (amount < 0 && otmp->bknown))) makeknown(otyp); } if (amount < 0) costly_alteration(uwep, COST_DECHNT); uwep->spe += amount; if (amount > 0) { if (uwep->cursed) uncurse(uwep); /* update shop bill to reflect new higher price */ if (uwep->unpaid) alter_cost(uwep, 0L); } /* * Enchantment, which normally improves a weapon, has an * addition adverse reaction on Magicbane whose effects are * spe dependent. Give an obscure clue here. */ if (uwep->oartifact == ART_MAGICBANE && uwep->spe >= 0) { Your("right %s %sches!", body_part(HAND), (((amount > 1) && (uwep->spe > 1)) ? "flin" : "it")); } /* an elven magic clue, cookie@keebler */ /* elven weapons vibrate warningly when enchanted beyond a limit */ if ((uwep->spe > 5) && (is_elven_weapon(uwep) || uwep->oartifact || !rn2(7))) pline("%s unexpectedly.", Yobjnam2(uwep, "suddenly vibrate")); return(1); } int welded(obj) register struct obj *obj; { if (obj && obj == uwep && will_weld(obj)) { obj->bknown = TRUE; return 1; } return 0; } void weldmsg(obj) register struct obj *obj; { long savewornmask; savewornmask = obj->owornmask; pline("%s welded to your %s!", Yobjnam2(obj, "are"), bimanual(obj) ? (const char *)makeplural(body_part(HAND)) : body_part(HAND)); obj->owornmask = savewornmask; } /*wield.c*/