]> granicus.if.org Git - nethack/commitdiff
implement #H4305 - Quivering wielded weapon
authorPatR <rankin@nethack.org>
Fri, 29 Apr 2016 22:10:56 +0000 (15:10 -0700)
committerPatR <rankin@nethack.org>
Fri, 29 Apr 2016 22:10:56 +0000 (15:10 -0700)
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....)

doc/fixes36.1
include/extern.h
src/invent.c
src/wield.c

index d11688f85264e564539f31ecc9f250f68173acde..c623afba372fb704835fff3e4aa0cee7f3dbfb3b 100644 (file)
@@ -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
index 17d9dc5f4725f034c73585b3e7426c61c314d9d0..4bee6fcd0b43bb0e1b3409babfc6f0dc1d9f8406 100644 (file)
@@ -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 *));
index 9654c7f8cf97a4039b6625fd94792017d54734ee..8859bb7b3dbdc5923961ebd9b24ec6254f6294fd 100644 (file)
@@ -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))
              ) {
index 0b17031ea8bc45f8b58fb6677b3c905cc5ea3a20..058eff434099797ffd09cc3d060c760eb2a322c6 100644 (file)
@@ -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 */