/* SCCS Id: @(#)wield.c 3.3 2000/06/04 */ /* 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 int FDECL(ready_weapon, (struct obj *)); /* elven weapons vibrate warningly when enchanted beyond a limit */ #define is_elven_weapon(optr) ((optr)->otyp == ELVEN_ARROW\ || (optr)->otyp == ELVEN_SPEAR\ || (optr)->otyp == ELVEN_DAGGER\ || (optr)->otyp == ELVEN_SHORT_SWORD\ || (optr)->otyp == ELVEN_BROADSWORD\ || (optr)->otyp == ELVEN_BOW) /* 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! * * Note: setuwep() with a null obj, and uwepgone(), are NOT the same! * Sometimes unwielding a weapon can kill you, and lifesaving will then * put it back into your hand. If lifesaving is permitted to do this, * use setwuep((struct obj *)0); otherwise use uwepgone(). * * 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; { if (obj == uwep) return; /* necessary to not set unweapon */ setworn(obj, W_WEP); /* 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) : !is_weptool(obj); } else unweapon = TRUE; /* for "bare hands" message */ update_inventory(); } static 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 (!uarmg && !Stone_resistance && wep->otyp == CORPSE && touch_petrifies(&mons[wep->corpsenm])) { /* Prevent wielding cockatrice when not wearing gloves --KAA */ char kbuf[BUFSZ]; You("wield the %s corpse in your bare %s.", mons[wep->corpsenm].mname, makeplural(body_part(HAND))); Sprintf(kbuf, "%s corpse", an(mons[wep->corpsenm].mname)); instapetrify(kbuf); } 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 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 }; 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 }; int dowield() { register struct obj *wep; 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); 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 */ if (flags.pushweapon && uwep) setuswapwep(uwep); result = ready_weapon(wep); 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; /* 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 || 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(uslinging() ? bullets : ready_objs, "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!", (uwep->quan == 1L) ? "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); } int can_twoweapon() { struct obj *otmp; #define NOT_WEAPON(obj) (!is_weptool(obj) && obj->oclass != WEAPON_CLASS) if (Upolyd) You("can only use two weapons in your normal form."); 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), (otmp->quan) > 1L ? "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 resists being held second to another weapon!", Yname2(uswapwep)); else if (!uarmg && !Stone_resistance && (uswapwep->otyp == CORPSE && touch_petrifies(&mons[uswapwep->corpsenm]))) { char kbuf[BUFSZ]; You("wield the %s corpse with your bare %s.", mons[uswapwep->corpsenm].mname, body_part(HAND)); Sprintf(kbuf, "%s corpse", an(mons[uswapwep->corpsenm].mname)); instapetrify(kbuf); } else if (Glib || uswapwep->cursed) { struct obj *obj = uswapwep; Your("%s from your %s!", aobjnam(obj, "slip"), makeplural(body_part(HAND))); if (!Glib) obj->bknown = TRUE; setuswapwep((struct obj *) 0); dropx(obj); } else return (TRUE); return (FALSE); } 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) { 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 weapon, or corrode it if acid damage is called for */ void erode_weapon(target, acid_dmg) struct obj *target; boolean acid_dmg; { int erosion; struct monst *victim; boolean vismon; if (!target) return; if (!carried(target) && !mcarried(target)) panic("erode whose weapon? (%d)", (int)target->where); victim = carried(target) ? &youmonst : target->ocarry; vismon = (victim != &youmonst) && canseemon(victim); erosion = acid_dmg ? target->oeroded2 : target->oeroded; if (target->greased) { grease_protect(target,(char *)0,FALSE,victim); } else if (target->oclass == SCROLL_CLASS) { #ifdef MAIL if(target->otyp != SCR_MAIL) #endif { if (!Blind) { if (victim == &youmonst) Your("%s.", aobjnam(target, "fade")); else if (vismon) pline("%s's %s.", Monnam(victim), aobjnam(target, "fade")); } target->otyp = SCR_BLANK_PAPER; target->spe = 0; } } else if (target->oerodeproof || (acid_dmg ? !is_corrodeable(target) : !is_rustprone(target))) { if (flags.verbose || !(target->oerodeproof && target->rknown)) { if (victim == &youmonst) Your("%s not affected.", aobjnam(target, "are")); else if (vismon) pline("%s's %s not affected.", Monnam(victim), aobjnam(target, "are")); } if (target->oerodeproof) target->rknown = TRUE; } else if (erosion < MAX_ERODE) { if (victim == &youmonst) Your("%s%s!", aobjnam(target, acid_dmg ? "corrode" : "rust"), erosion+1 == MAX_ERODE ? " completely" : erosion ? " further" : ""); else if (vismon) pline("%s's %s%s!", Monnam(victim), aobjnam(target, acid_dmg ? "corrode" : "rust"), erosion+1 == MAX_ERODE ? " completely" : erosion ? " further" : ""); if (acid_dmg) target->oeroded2++; else target->oeroded++; } else { if (flags.verbose) { if (victim == &youmonst) Your("%s completely %s.", aobjnam(target, Blind ? "feel" : "look"), acid_dmg ? "corroded" : "rusty"); else if (vismon) pline("%s's %s completely %s.", Monnam(victim), aobjnam(target, "look"), acid_dmg ? "corroded" : "rusty"); } } } int chwepon(otmp, amount) register struct obj *otmp; register int amount; { register const char *color = hcolor((amount < 0) ? Black : blue); register const char *xtime; 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(uwep->otyp == WORM_TOOTH && amount >= 0) { uwep->otyp = CRYSKNIFE; uwep->oerodeproof = 0; Your("weapon seems sharper now."); uwep->cursed = 0; return(1); } if(uwep->otyp == CRYSKNIFE && amount < 0) { uwep->otyp = WORM_TOOTH; uwep->oerodeproof = 0; Your("weapon seems duller now."); return(1); } if (amount < 0 && uwep->oartifact && restrict_name(uwep, ONAME(uwep))) { if (!Blind) Your("%s %s.", aobjnam(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) Your("%s %s for a while and then evaporate%s.", aobjnam(uwep, "violently glow"), color, uwep->quan == 1L ? "s" : ""); else Your("%s.", aobjnam(uwep, "evaporate")); while(uwep) /* let all of them disappear */ /* note: uwep->quan = 1 is nogood if unpaid */ useup(uwep); return(1); } if (!Blind) { xtime = (amount*amount == 1) ? "moment" : "while"; Your("%s %s for a %s.", aobjnam(uwep, amount == 0 ? "violently glow" : "glow"), color, xtime); } uwep->spe += amount; if(amount > 0) uwep->cursed = 0; /* * 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 */ if ((uwep->spe > 5) && (is_elven_weapon(uwep) || uwep->oartifact || !rn2(7))) Your("%s unexpectedly.", aobjnam(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; Your("%s %s welded to your %s!", xname(obj), (obj->quan == 1L) ? "is" : "are", bimanual(obj) ? (const char *)makeplural(body_part(HAND)) : body_part(HAND)); obj->owornmask = savewornmask; } /*wield.c*/