From: PatR Date: Fri, 29 Apr 2016 22:10:56 +0000 (-0700) Subject: implement #H4305 - Quivering wielded weapon X-Git-Tag: NetHack-3.6.1_RC01~802 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2fe9170ee41fdafb61f7175fdadca151c3eec162;p=nethack implement #H4305 - Quivering wielded weapon This ended up being more elaborate than I anticipated. 'Q' will now accept the wielded weapon as a choice of item to quiver. If that item is a stack of more than one, it will offer to split N-1 into the quiver and leave 1 wielded. If the offer is declined, or if there is already just 1, it will require confirmation to move the item from the weapon slot to the quiver slot. The alternate weapon is handled similarly, with different phrasing when in twoweapon combat mode. Just to be crystal clear: a single object cannot be in more than one weapon, alt-weapon, or quiver slot at the same time. 'Q's old behavior of rejecting the wielded weapon was to avoid accidentally becoming empty-handed, not anything to do with multiple worn/wield slots. 'Q' will also accept a count when picking an inventory item, then put 'count'-many into the quiver, leaving N-count in original stack. Except when the chosen item is already in the quiver; it that case, it undoes the stack split and leaves things as they were. (That restriction may not have been necessary but I'm not planning to revisit it....) --- diff --git a/doc/fixes36.1 b/doc/fixes36.1 index d11688f85..c623afba3 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -313,6 +313,11 @@ default value for vibrating square symbol changed from yellow '^' to purple '~' allow symbol set values to be specified via char within single quotes add symbols set "plain", same as default except it uses '+' for corner walls extend wizard-mode '#stats' command +'Q' to ready an item for quick-throwing ('f') now allows the wielded weapon to + be moved to quiver; if quantity is more than 1, it offers to split the + stack and leave 1 wielded, otherwise it requires confirmation +'Q' will accept an item count to manually split a stack as part of it being + quivered, provided the stack isn't already in the quiver slot Platform- and/or Interface-Specific New Features diff --git a/include/extern.h b/include/extern.h index 17d9dc5f4..4bee6fcd0 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1461437800 2016/04/23 18:56:40 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.557 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1461967848 2016/04/29 22:10:48 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.558 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -922,6 +922,7 @@ E boolean FDECL(obj_here, (struct obj *, int, int)); E boolean NDECL(wearing_armor); E boolean FDECL(is_worn, (struct obj *)); E struct obj *FDECL(g_at, (int, int)); +E boolean FDECL(splittable, (struct obj *)); E struct obj *FDECL(getobj, (const char *, const char *)); E int FDECL(ggetobj, (const char *, int (*)(OBJ_P), int, BOOLEAN_P, unsigned *)); diff --git a/src/invent.c b/src/invent.c index 9654c7f8c..8859bb7b3 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 invent.c $NHDT-Date: 1461629196 2016/04/26 00:06:36 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.207 $ */ +/* NetHack 3.6 invent.c $NHDT-Date: 1461967848 2016/04/29 22:10:48 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.208 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -16,7 +16,6 @@ STATIC_DCL void FDECL(invdisp_nothing, (const char *, const char *)); STATIC_DCL boolean FDECL(worn_wield_only, (struct obj *)); STATIC_DCL boolean FDECL(only_here, (struct obj *)); STATIC_DCL void FDECL(compactify, (char *)); -STATIC_DCL boolean FDECL(splittable, (struct obj *)); STATIC_DCL boolean FDECL(taking_off, (const char *)); STATIC_DCL boolean FDECL(putting_on, (const char *)); STATIC_PTR int FDECL(ckunpaid, (struct obj *)); @@ -1042,7 +1041,7 @@ register char *buf; } /* some objects shouldn't be split when count given to getobj or askchain */ -STATIC_OVL boolean +boolean splittable(obj) struct obj *obj; { @@ -1163,8 +1162,9 @@ register const char *let, *word; || (!strcmp(word, "wield") && (otmp->owornmask & W_WEP)) #endif - || (!strcmp(word, "ready") /* exclude if wielded */ - && (otmp == uwep || (otmp == uswapwep && u.twoweap))) + || (!strcmp(word, "ready") /* exclude when wielded... */ + && ((otmp == uwep || (otmp == uswapwep && u.twoweap)) + && otmp->quan == 1L)) /* ...unless more than one */ || ((!strcmp(word, "dip") || !strcmp(word, "grease")) && inaccessible_equipment(otmp, (const char *) 0, FALSE)) ) { diff --git a/src/wield.c b/src/wield.c index 0b17031ea..058eff434 100644 --- a/src/wield.c +++ b/src/wield.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wield.c $NHDT-Date: 1450577672 2015/12/20 02:14:32 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.48 $ */ +/* NetHack 3.6 wield.c $NHDT-Date: 1461967849 2016/04/29 22:10:49 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.49 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -174,6 +174,10 @@ struct obj *wep; * 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. + * [That comment is obsolete. It dates from the days (3.0) + * when unwielding Firebrand could cause hero to be burned + * to death in Hell due to loss of fire resistance. + * "Lifesaved re-wielding or re-wearing" is ancient history.] */ long dummy = wep->owornmask; @@ -232,12 +236,16 @@ register struct obj *obj; /*** 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[] = { 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 }; +static NEARDATA const char wield_objs[] = { + ALL_CLASSES, ALLOW_NONE, WEAPON_CLASS, TOOL_CLASS, 0 +}; +static NEARDATA const char ready_objs[] = { + ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, ALLOW_NONE, WEAPON_CLASS, 0 +}; +static NEARDATA const char bullets[] = { /* (note: different from dothrow.c) */ + ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, ALLOW_NONE, + GEM_CLASS, WEAPON_CLASS, 0 +}; int dowield() @@ -316,10 +324,10 @@ doswapweapon() result = ready_weapon(oldswap); /* Set your new secondary weapon */ - if (uwep == oldwep) + if (uwep == oldwep) { /* Wield failed for some reason */ setuswapwep(oldswap); - else { + } else { setuswapwep(oldwep); if (uswapwep) prinv((char *) 0, uswapwep, 0L); @@ -336,64 +344,165 @@ doswapweapon() int dowieldquiver() { - register struct obj *newquiver; - const char *quivee_types = - (uslinging() - || (uswapwep && objects[uswapwep->otyp].oc_skill == P_SLING)) - ? bullets - : ready_objs; + char qbuf[QBUFSZ]; + struct obj *newquiver; + const char *quivee_types; + int res; + boolean finish_splitting = FALSE, + was_uwep = FALSE, was_twoweap = u.twoweap; /* Since the quiver isn't in your hands, don't check cantwield(), */ /* will_weld(), touch_petrifies(), etc. */ multi = 0; - - /* Prompt for a new quiver */ - if (!(newquiver = getobj(quivee_types, "ready"))) + /* forget last splitobj() before calling getobj() with ALLOW_COUNT */ + context.objsplit.child_oid = context.objsplit.parent_oid = 0; + + /* Prompt for a new quiver: "What do you want to ready?" + (Include gems/stones as likely candidates if either primary + or secondary weapon is a sling.) */ + quivee_types = (uslinging() + || (uswapwep + && objects[uswapwep->otyp].oc_skill == P_SLING)) + ? bullets + : ready_objs; + newquiver = getobj(quivee_types, "ready"); + + if (!newquiver) { /* Cancelled */ return 0; - - /* Handle no object, or object in other slot */ - /* Any type is okay, since we give no intrinsics anyways */ - if (newquiver == &zeroobj) { + } else if (newquiver == &zeroobj) { /* no object */ /* Explicitly nothing */ if (uquiver) { You("now have no ammunition readied."); - setuqwep(newquiver = (struct obj *) 0); + /* skip 'quivering: prinv()' */ + setuqwep((struct obj *) 0); } else { You("already have no ammunition readied!"); - return 0; } + return 0; + } else if (newquiver->o_id == context.objsplit.child_oid) { + /* if newquiver is the result of supplying a count to getobj() + we don't want to split something already in the quiver; + for any other item, we need to give it its own inventory slot */ + if (uquiver && uquiver->o_id == context.objsplit.parent_oid) { + unsplitobj(newquiver); + goto already_quivered; + } + finish_splitting = TRUE; } else if (newquiver == uquiver) { + already_quivered: 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_ACCESSORY | W_SADDLE)) { You("cannot ready that!"); return 0; - } else { - long dummy; + } else if (newquiver == uwep) { + int weld_res = !uwep->bknown; - /* Check if it's the secondary weapon */ - if (newquiver == uswapwep) { - setuswapwep((struct obj *) 0); - untwoweapon(); + if (welded(uwep)) { + weldmsg(uwep); + reset_remarm(); /* same as dowield() */ + return weld_res; } - - /* Okay to put in quiver; print it */ - dummy = newquiver->owornmask; - newquiver->owornmask |= W_QUIVER; - prinv((char *) 0, newquiver, 0L); - newquiver->owornmask = dummy; + /* offer to split stack if wielding more than 1 */ + if (uwep->quan > 1L && inv_cnt(FALSE) < 52 && splittable(uwep)) { + Sprintf(qbuf, "You are wielding %ld %s. Ready %ld of them?", + uwep->quan, simpleonames(uwep), uwep->quan - 1L); + switch (ynq(qbuf)) { + case 'q': + return 0; + case 'y': + /* leave 1 wielded, split rest off and put into quiver */ + newquiver = splitobj(uwep, uwep->quan - 1L); + finish_splitting = TRUE; + goto quivering; + default: + break; + } + Strcpy(qbuf, "Ready all of them instead?"); + } else { + Sprintf(qbuf, "You are wielding %s. Ready %s instead?", + /* uwep->quan is 1, but name might be plural ('boots') */ + !is_plural(uwep) ? "that" : "those", + !is_plural(uwep) ? "it" : "them"); + } + /* require confirmation to ready the main weapon */ + if (ynq(qbuf) != 'y') { + (void) Shk_Your(qbuf, uwep); /* replace qbuf[] contents */ + pline("%s%s %s wielded.", qbuf, + simpleonames(uwep), otense(uwep, "remain")); + return 0; + } + /* quivering main weapon, so no longer wielding it */ + setuwep((struct obj *) 0); + untwoweapon(); + was_uwep = TRUE; + } else if (newquiver == uswapwep) { + if (uswapwep->quan > 1L && inv_cnt(FALSE) < 52 + && splittable(uswapwep)) { + Sprintf(qbuf, "%s %ld %s. Ready %ld of them?", + u.twoweap ? "You are dual wielding" + : "Your alternate weapon is", + uswapwep->quan, simpleonames(uswapwep), + uswapwep->quan - 1L); + switch (ynq(qbuf)) { + case 'q': + return 0; + case 'y': + /* leave 1 alt-wielded, split rest off and put into quiver */ + newquiver = splitobj(uswapwep, uswapwep->quan - 1L); + finish_splitting = TRUE; + goto quivering; + default: + break; + } + Strcpy(qbuf, "Ready all of them instead?"); + } else { + Sprintf(qbuf, "%s your %s weapon. Ready %s instead?", + !is_plural(uswapwep) ? "That is" : "Those are", + u.twoweap ? "second" : "alternate", + /* uswapwep->quan is 1, but name might be plural */ + !is_plural(uswapwep) ? "it" : "them"); + } + /* require confirmation to ready the alternate weapon */ + if (ynq(qbuf) != 'y') { + (void) Shk_Your(qbuf, uswapwep); /* replace qbuf[] contents */ + pline("%s%s %s %s.", qbuf, + simpleonames(uswapwep), otense(uswapwep, "remain"), + u.twoweap ? "wielded" : "as secondary weapon"); + return 0; + } + /* quivering alternate weapon, so no more uswapwep */ + setuswapwep((struct obj *) 0); + untwoweapon(); } - /* Finally, place it in the quiver */ + quivering: + if (finish_splitting) { + freeinv(newquiver); + newquiver->nomerge = 1; + addinv(newquiver); + newquiver->nomerge = 0; + } + /* place item in quiver before printing so that inventory feedback + includes "(at the ready)" */ setuqwep(newquiver); - /* Take no time since this is a convenience slot */ - return 0; + prinv((char *) 0, newquiver, 0L); + + /* quiver is a convenience slot and manipulating it ordinarily + consumes no time, but unwielding primary or secondary weapon + should take time (perhaps we're adjacent to a rust monster + or disenchanter and want to hit it immediately, but not with + something we're wielding that's vulnerable to its damage) */ + res = 0; + if (was_uwep) { + You("are now empty %s.", body_part(HANDED)); + res = 1; + } else if (was_twoweap && !u.twoweap) { + You("are no longer wielding two weapons at once."); + res = 1; + } + return res; } /* used for #rub and for applying pick-axe, whip, grappling hook or polearm */