OSDN Git Service

upgrade to 3.6.7
[jnethack/source.git] / src / weapon.c
index 7de49ac..bcf4de3 100644 (file)
@@ -1,11 +1,11 @@
-/* NetHack 3.6 weapon.c        $NHDT-Date: 1454660575 2016/02/05 08:22:55 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.57 $ */
+/* NetHack 3.6 weapon.c        $NHDT-Date: 1559998716 2019/06/08 12:58:36 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.70 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /*-Copyright (c) Robert Patrick Rankin, 2011. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 /* JNetHack Copyright */
 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
-/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016            */
+/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2022            */
 /* JNetHack may be freely redistributed.  See license for details. */
 
 /*
 
 #include "hack.h"
 
+STATIC_DCL void FDECL(give_may_advance_msg, (int));
+STATIC_DCL boolean FDECL(could_advance, (int));
+STATIC_DCL boolean FDECL(peaked_skill, (int));
+STATIC_DCL int FDECL(slots_required, (int));
+STATIC_DCL void FDECL(skill_advance, (int));
+
 /* Categories whose names don't come from OBJ_NAME(objects[type])
  */
 #define PN_BARE_HANDED (-1) /* includes martial arts */
@@ -33,8 +39,6 @@
 #define PN_ESCAPE_SPELL (-13)
 #define PN_MATTER_SPELL (-14)
 
-STATIC_DCL void FDECL(give_may_advance_msg, (int));
-
 STATIC_VAR NEARDATA const short skill_names_indices[P_NUM_SKILLS] = {
     0, DAGGER, KNIFE, AXE, PICK_AXE, SHORT_SWORD, BROADSWORD, LONG_SWORD,
     TWO_HANDED_SWORD, SCIMITAR, PN_SABER, CLUB, MACE, MORNING_STAR, FLAIL,
@@ -47,7 +51,7 @@ STATIC_VAR NEARDATA const short skill_names_indices[P_NUM_SKILLS] = {
 
 /* note: entry [0] isn't used */
 STATIC_VAR NEARDATA const char *const odd_skill_names[] = {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
     "no skill", "bare hands", /* use barehands_or_martial[] instead */
     "two weapon combat", "riding", "polearms", "saber", "hammer", "whip",
     "attack spells", "healing spells", "divination spells",
@@ -61,41 +65,13 @@ STATIC_VAR NEARDATA const char *const odd_skill_names[] = {
 };
 /* indexed vis `is_martial() */
 STATIC_VAR NEARDATA const char *const barehands_or_martial[] = {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
     "bare handed combat", "martial arts"
 #else
     "\91f\8eè", "\91Ì\8fp"
 #endif
 };
 
-STATIC_OVL void
-give_may_advance_msg(skill)
-int skill;
-{
-#if 0 /*JP*/
-    You_feel("more confident in your %sskills.",
-             skill == P_NONE ? "" : skill <= P_LAST_WEAPON
-                                        ? "weapon "
-                                        : skill <= P_LAST_SPELL
-                                              ? "spell casting "
-                                              : "fighting ");
-#else
-    You("%s\83X\83L\83\8b\82ð\8d\82\82ß\82é\8e©\90M\82ª\97N\82¢\82Ä\82«\82½\81D",
-        skill == P_NONE ? "" : skill <= P_LAST_WEAPON
-            ? "\95\90\8aí\82Ì"
-            : skill <= P_LAST_SPELL
-                  ? "\96\82\96@\82Ì"
-                  : "\90í\82¢\82Ì");
-#endif
-}
-
-STATIC_DCL boolean FDECL(can_advance, (int, BOOLEAN_P));
-STATIC_DCL boolean FDECL(could_advance, (int));
-STATIC_DCL boolean FDECL(peaked_skill, (int));
-STATIC_DCL int FDECL(slots_required, (int));
-STATIC_DCL char *FDECL(skill_level_name, (int, char *));
-STATIC_DCL void FDECL(skill_advance, (int));
-
 #define P_NAME(type)                                    \
     ((skill_names_indices[type] > 0)                    \
          ? OBJ_NAME(objects[skill_names_indices[type]]) \
@@ -106,6 +82,25 @@ STATIC_DCL void FDECL(skill_advance, (int));
 static NEARDATA const char kebabable[] = { S_XORN, S_DRAGON, S_JABBERWOCK,
                                            S_NAGA, S_GIANT,  '\0' };
 
+STATIC_OVL void
+give_may_advance_msg(skill)
+int skill;
+{
+#if 0 /*JP:T*/
+    You_feel("more confident in your %sskills.",
+             (skill == P_NONE) ? ""
+                 : (skill <= P_LAST_WEAPON) ? "weapon "
+                     : (skill <= P_LAST_SPELL) ? "spell casting "
+                         : "fighting ");
+#else
+    You("%s\83X\83L\83\8b\82ð\8d\82\82ß\82é\8e©\90M\82ª\97N\82¢\82Ä\82«\82½\81D",
+        (skill == P_NONE) ? ""
+            : (skill <= P_LAST_WEAPON) ? "\95\90\8aí\82Ì"
+                : (skill <= P_LAST_SPELL) ? "\96\82\96@\82Ì"
+                    : "\90í\82¢\82Ì");
+#endif
+}
+
 /* weapon's skill category name for use as generalized description of weapon;
    mostly used to shorten "you drop your <weapon>" messages when slippery
    fingers or polymorph causes hero to involuntarily drop wielded weapon(s) */
@@ -373,6 +368,8 @@ struct monst *mon;
             bonus += rnd(4);
         if (objects[otyp].oc_material == SILVER && mon_hates_silver(mon))
             bonus += rnd(20);
+        if (artifact_light(otmp) && otmp->lamplit && hates_light(ptr))
+            bonus += rnd(8);
 
         /* if the weapon is going to get a double damage bonus, adjust
            this bonus so that effectively it's added after the doubling */
@@ -396,6 +393,128 @@ struct monst *mon;
     return  tmp;
 }
 
+/* check whether blessed and/or silver damage applies for *non-weapon* hit;
+   return value is the amount of the extra damage */
+int
+special_dmgval(magr, mdef, armask, silverhit_p)
+struct monst *magr, *mdef;
+long armask; /* armor mask, multiple bits accepted for W_ARMC|W_ARM|W_ARMU
+              * or W_ARMG|W_RINGL|W_RINGR only */
+long *silverhit_p; /* output flag mask for silver bonus */
+{
+    struct obj *obj;
+    struct permonst *ptr = mdef->data;
+    boolean left_ring = (armask & W_RINGL) ? TRUE : FALSE,
+            right_ring = (armask & W_RINGR) ? TRUE : FALSE;
+    long silverhit = 0L;
+    int bonus = 0;
+
+    obj = 0;
+    if (armask & (W_ARMC | W_ARM | W_ARMU)) {
+        if ((armask & W_ARMC) != 0L
+            && (obj = which_armor(magr, W_ARMC)) != 0)
+            armask = W_ARMC;
+        else if ((armask & W_ARM) != 0L
+                 && (obj = which_armor(magr, W_ARM)) != 0)
+            armask = W_ARM;
+        else if ((armask & W_ARMU) != 0L
+                 && (obj = which_armor(magr, W_ARMU)) != 0)
+            armask = W_ARMU;
+        else
+            armask = 0L;
+    } else if (armask & (W_ARMG | W_RINGL | W_RINGR)) {
+        armask = ((obj = which_armor(magr, W_ARMG)) != 0) ?  W_ARMG : 0L;
+    } else {
+        obj = which_armor(magr, armask);
+    }
+
+    if (obj) {
+        if (obj->blessed
+            && (is_undead(ptr) || is_demon(ptr) || is_vampshifter(mdef)))
+            bonus += rnd(4);
+        /* the only silver armor is shield of reflection (silver dragon
+           scales refer to color, not material) and the only way to hit
+           with one--aside from throwing--is to wield it and perform a
+           weapon hit, but we include a general check here */
+        if (objects[obj->otyp].oc_material == SILVER
+            && mon_hates_silver(mdef)) {
+            bonus += rnd(20);
+            silverhit |= armask;
+        }
+
+    /* when no gloves we check for silver rings (blessed rings ignored) */
+    } else if ((left_ring || right_ring) && magr == &youmonst) {
+        if (left_ring && uleft) {
+            if (objects[uleft->otyp].oc_material == SILVER
+                && mon_hates_silver(mdef)) {
+                bonus += rnd(20);
+                silverhit |= W_RINGL;
+            }
+        }
+        if (right_ring && uright) {
+            if (objects[uright->otyp].oc_material == SILVER
+                && mon_hates_silver(mdef)) {
+                /* two silver rings don't give double silver damage
+                   but 'silverhit' messages might be adjusted for them */
+                if (!(silverhit & W_RINGL))
+                    bonus += rnd(20);
+                silverhit |= W_RINGR;
+            }
+        }
+    }
+
+    if (silverhit_p)
+        *silverhit_p = silverhit;
+    return bonus;
+}
+
+/* give a "silver <item> sears <target>" message;
+   not used for weapon hit, so we only handle rings */
+void
+silver_sears(magr, mdef, silverhit)
+struct monst *magr UNUSED;
+struct monst *mdef;
+long silverhit;
+{
+#if 0 /*JP*/
+    char rings[20]; /* plenty of room for "rings" */
+#endif
+    int ltyp = ((uleft && (silverhit & W_RINGL) != 0L)
+                ? uleft->otyp : STRANGE_OBJECT),
+        rtyp = ((uright && (silverhit & W_RINGR) != 0L)
+                ? uright->otyp : STRANGE_OBJECT);
+    boolean both,
+        l_ag = (objects[ltyp].oc_material == SILVER && uleft->dknown),
+        r_ag = (objects[rtyp].oc_material == SILVER && uright->dknown);
+
+    if ((silverhit & (W_RINGL | W_RINGR)) != 0L) {
+        /* plural if both the same type (so not multi_claw and both rings
+           are non-Null) and either both known or neither known, or both
+           silver (in case there is ever more than one type of silver ring)
+           and both known; singular if multi_claw (where one of ltyp or
+           rtyp will always be STRANGE_OBJECT) even if both rings are known
+           silver [see hmonas(uhitm.c) for explanation of 'multi_claw'] */
+        both = ((ltyp == rtyp && uleft->dknown == uright->dknown)
+                || (l_ag && r_ag));
+#if 0 /*JP*/
+        Sprintf(rings, "ring%s", both ? "s" : "");
+        Your("%s%s %s %s!",
+             (l_ag || r_ag) ? "silver "
+             : both ? ""
+               : ((silverhit & W_RINGL) != 0L) ? "left "
+                 : "right ",
+             rings, vtense(rings, "sear"), mon_nam(mdef));
+#else
+        Your("%s\8ew\97Ö\82ª%s\82ð\8fÄ\82¢\82½\81I",
+             (l_ag || r_ag) ? "\8bâ\82Ì"
+             : both ? ""
+               : ((silverhit & W_RINGL) != 0L) ? "\8d\82Ì"
+                 : "\89E\82Ì",
+             mon_nam(mdef));
+#endif
+    }
+}
+
 STATIC_DCL struct obj *FDECL(oselect, (struct monst *, int));
 #define Oselect(x)                      \
     if ((otmp = oselect(mtmp, x)) != 0) \
@@ -419,6 +538,7 @@ int x;
     return (struct obj *) 0;
 }
 
+/* TODO: have monsters use aklys' throw-and-return */
 static NEARDATA const int rwep[] = {
     DWARVISH_SPEAR, SILVER_SPEAR, ELVEN_SPEAR, SPEAR, ORCISH_SPEAR, JAVELIN,
     SHURIKEN, YA, SILVER_ARROW, ELVEN_ARROW, ARROW, ORCISH_ARROW,
@@ -447,7 +567,7 @@ register struct monst *mtmp;
 
     char mlet = mtmp->data->mlet;
 
-    propellor = &zeroobj;
+    propellor = (struct obj *) &zeroobj;
     Oselect(EGG);      /* cockatrice egg */
     if (mlet == S_KOP) /* pies are first choice for Kops */
         Oselect(CREAM_PIE);
@@ -506,25 +626,25 @@ register struct monst *mtmp;
         }
 
         /* KMH -- This belongs here so darts will work */
-        propellor = &zeroobj;
+        propellor = (struct obj *) &zeroobj;
 
-        prop = (objects[rwep[i]]).oc_skill;
+        prop = objects[rwep[i]].oc_skill;
         if (prop < 0) {
             switch (-prop) {
             case P_BOW:
-                propellor = (oselect(mtmp, YUMI));
+                propellor = oselect(mtmp, YUMI);
                 if (!propellor)
-                    propellor = (oselect(mtmp, ELVEN_BOW));
+                    propellor = oselect(mtmp, ELVEN_BOW);
                 if (!propellor)
-                    propellor = (oselect(mtmp, BOW));
+                    propellor = oselect(mtmp, BOW);
                 if (!propellor)
-                    propellor = (oselect(mtmp, ORCISH_BOW));
+                    propellor = oselect(mtmp, ORCISH_BOW);
                 break;
             case P_SLING:
-                propellor = (oselect(mtmp, SLING));
+                propellor = oselect(mtmp, SLING);
                 break;
             case P_CROSSBOW:
-                propellor = (oselect(mtmp, CROSSBOW));
+                propellor = oselect(mtmp, CROSSBOW);
             }
             if ((otmp = MON_WEP(mtmp)) && mwelded(otmp) && otmp != propellor
                 && mtmp->weapon_check == NO_WEAPON_WANTED)
@@ -556,6 +676,19 @@ register struct monst *mtmp;
     return (struct obj *) 0;
 }
 
+/* is 'obj' a type of weapon that any monster knows how to throw? */
+boolean
+monmightthrowwep(obj)
+struct obj *obj;
+{
+    short idx;
+
+    for (idx = 0; idx < SIZE(rwep); ++idx)
+        if (obj->otyp == rwep[idx])
+            return TRUE;
+    return FALSE;
+}
+
 /* Weapons in order of preference */
 static const NEARDATA short hwep[] = {
     CORPSE, /* cockatrice corpse */
@@ -719,6 +852,7 @@ register struct monst *mon;
     }
     if (obj && obj != &zeroobj) {
         struct obj *mw_tmp = MON_WEP(mon);
+
         if (mw_tmp && mw_tmp->otyp == obj->otyp) {
             /* already wielding it */
             mon->weapon_check = NEED_WEAPON;
@@ -750,7 +884,7 @@ register struct monst *mon;
 #else
                     pline("%s\82Í\95\90\8aí\82ð\8eè\82É\82µ\82æ\82¤\82Æ\82µ\82½\82ª\81C", mon_nam(mon));
 #endif
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     pline("%s cannot wield that %s.", mon_nam(mon),
                           xname(obj));
 #else
@@ -776,19 +910,33 @@ register struct monst *mon;
         setmnotwielded(mon, mw_tmp);
         mon->weapon_check = NEED_WEAPON;
         if (canseemon(mon)) {
+            boolean newly_welded;
+
 /*JP
             pline("%s wields %s!", Monnam(mon), doname(obj));
 */
             pline("%s\82Í%s\82ð\91\95\94õ\82µ\82½\81I", Monnam(mon), doname(obj));
-            if (mwelded(mw_tmp)) {
-#if 0 /*JP*/
+            /* 3.6.3: mwelded() predicate expects the object to have its
+               W_WEP bit set in owormmask, but the pline here and for
+               artifact_light don't want that because they'd have '(weapon
+               in hand/claw)' appended; so we set it for the mwelded test
+               and then clear it, until finally setting it for good below */
+            obj->owornmask |= W_WEP;
+            newly_welded = mwelded(obj);
+            obj->owornmask &= ~W_WEP;
+            if (newly_welded) {
+                const char *mon_hand = mbodypart(mon, HAND);
+
+                if (bimanual(obj))
+                    mon_hand = makeplural(mon_hand);
+#if 0 /*JP:T*/
                 pline("%s %s to %s %s!", Tobjnam(obj, "weld"),
                       is_plural(obj) ? "themselves" : "itself",
-                      s_suffix(mon_nam(mon)), mbodypart(mon, HAND));
+                      s_suffix(mon_nam(mon)), mon_hand);
 #else
                 pline("%s\82Í\8f\9f\8eè\82É%s\82Ì%s\82É\91\95\94õ\82³\82ê\82½\81I",
                       xname(obj),
-                      mon_nam(mon), mbodypart(mon, HAND));
+                      mon_nam(mon), mon_hand);
 #endif
                 obj->bknown = 1;
             }
@@ -796,7 +944,7 @@ register struct monst *mon;
         if (artifact_light(obj) && !obj->lamplit) {
             begin_burn(obj, FALSE);
             if (canseemon(mon))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s %s in %s %s!", Tobjnam(obj, "shine"),
                       arti_light_description(obj), s_suffix(mon_nam(mon)),
                       mbodypart(mon, HAND));
@@ -805,6 +953,19 @@ register struct monst *mon;
                       xname(obj), mon_nam(mon),
                       mbodypart(mon, HAND), arti_light_description(obj));
 #endif
+            /* 3.6.3: artifact might be getting wielded by invisible monst */
+            else if (cansee(mon->mx, mon->my))
+#if 0 /*JP*/
+                pline("Light begins shining %s.",
+                      (distu(mon->mx, mon->my) <= 5 * 5)
+                          ? "nearby"
+                          : "in the distance");
+#else
+                pline("\96¾\82©\82è\82ª%s\82Å\8bP\82«\82Í\82\82ß\82½\81D",
+                      (distu(mon->mx, mon->my) <= 5 * 5)
+                          ? "\8bß\82­"
+                          : "\89\93\82­");
+#endif
         }
         obj->owornmask = W_WEP;
         return 1;
@@ -903,7 +1064,7 @@ boolean verbose;
     /* new state is only reported if it's an increase */
     if (newspe > obj->spe) {
         if (verbose) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             const char *wetness = (newspe < 3)
                                      ? (!obj->spe ? "damp" : "damper")
                                      : (!obj->spe ? "wet" : "wetter");
@@ -914,7 +1075,7 @@ boolean verbose;
 #endif
 
             if (carried(obj))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s gets %s.", Yobjnam2(obj, (const char *) 0),
                       wetness);
 #else
@@ -922,7 +1083,7 @@ boolean verbose;
                       wetness);
 #endif
             else if (mcarried(obj) && canseemon(obj->ocarry))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s %s gets %s.", s_suffix(Monnam(obj->ocarry)),
                       xname(obj), wetness);
 #else
@@ -952,7 +1113,7 @@ boolean verbose;
     if (newspe < obj->spe) {
         if (verbose) {
             if (carried(obj))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s dries%s.", Yobjnam2(obj, (const char *) 0),
                       !newspe ? " out" : "");
 #else
@@ -960,7 +1121,7 @@ boolean verbose;
                       !newspe ? "\8a£\82«\82«\82Á\82½" : "\8a£\82¢\82½");
 #endif
             else if (mcarried(obj) && canseemon(obj->ocarry))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s %s drie%s.", s_suffix(Monnam(obj->ocarry)),
                       xname(obj), !newspe ? " out" : "");
 #else
@@ -979,7 +1140,7 @@ boolean verbose;
 }
 
 /* copy the skill level name into the given buffer */
-STATIC_OVL char *
+char *
 skill_level_name(skill, buf)
 int skill;
 char *buf;
@@ -1035,6 +1196,13 @@ char *buf;
     return buf;
 }
 
+const char *
+skill_name(skill)
+int skill;
+{
+    return P_NAME(skill);
+}
+
 /* return the # of slots required to advance the skill */
 STATIC_OVL int
 slots_required(skill)
@@ -1061,8 +1229,7 @@ int skill;
 }
 
 /* return true if this skill can be advanced */
-/*ARGSUSED*/
-STATIC_OVL boolean
+boolean
 can_advance(skill, speedy)
 int skill;
 boolean speedy;
@@ -1116,7 +1283,7 @@ int skill;
     P_SKILL(skill)++;
     u.skill_record[u.skills_advanced++] = skill;
     /* subtly change the advance message to indicate no more advancement */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
     You("are now %s skilled in %s.",
         P_SKILL(skill) >= P_MAX_SKILL(skill) ? "most" : "more",
         P_NAME(skill));
@@ -1191,7 +1358,7 @@ enhance_weapon_skill()
         if (eventually_advance > 0 || maxxed_cnt > 0) {
             any = zeroany;
             if (eventually_advance > 0) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 Sprintf(buf, "(Skill%s flagged by \"*\" may be enhanced %s.)",
                         plur(eventually_advance),
                         (u.ulevel < MAXULEV)
@@ -1207,7 +1374,7 @@ enhance_weapon_skill()
                          MENU_UNSELECTED);
             }
             if (maxxed_cnt > 0) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 Sprintf(buf,
                  "(Skill%s flagged by \"#\" cannot be enhanced any further.)",
                         plur(maxxed_cnt));
@@ -1278,7 +1445,7 @@ enhance_weapon_skill()
                          MENU_UNSELECTED);
             }
 
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         Strcpy(buf, (to_advance > 0) ? "Pick a skill to advance:"
                                      : "Current skills:");
 #else
@@ -1684,7 +1851,7 @@ register struct obj *obj;
     if (artifact_light(obj) && obj->lamplit) {
         end_burn(obj, FALSE);
         if (canseemon(mon))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline("%s in %s %s %s shining.", The(xname(obj)),
                   s_suffix(mon_nam(mon)), mbodypart(mon, HAND),
                   otense(obj, "stop"));