OSDN Git Service

update year to 2023
[jnethack/source.git] / src / steed.c
index 97c5071..e3c9709 100644 (file)
@@ -1,10 +1,10 @@
-/* NetHack 3.6 steed.c $NHDT-Date: 1445906867 2015/10/27 00:47:47 $  $NHDT-Branch: master $:$NHDT-Revision: 1.47 $ */
+/* NetHack 3.6 steed.c $NHDT-Date: 1575245090 2019/12/02 00:04:50 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.68 $ */
 /* Copyright (c) Kevin Hugo, 1998-1999. */
 /* 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-2023            */
 /* JNetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
@@ -150,14 +150,14 @@ struct obj *otmp;
 /*JP
              && !strncmp(s, "riding ", 7))
 */
-             && !strncmp(s, "\8fæ\94n\97p\82Ì", 8))
+             && !STRNCMP2(s, "\8fæ\94n\97p\82Ì"))
         /* Bonus for wearing "riding" (but not fumbling) gloves */
         chance += 10;
     else if (uarmf && (s = OBJ_DESCR(objects[uarmf->otyp])) != (char *) 0
 /*JP
              && !strncmp(s, "riding ", 7))
 */
-             && !strncmp(s, "\8fæ\94n\97p\82Ì", 8))
+             && !STRNCMP2(s, "\8fæ\94n\97p\82Ì"))
         /* ... or for "riding boots" */
         chance += 10;
     if (otmp->cursed)
@@ -252,7 +252,7 @@ boolean force;      /* Quietly force this animal */
 
     /* Is the player in the right form? */
     if (Hallucination && !force) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         pline("Maybe you should find a designated driver.");
 #else
         pline("\82¨\82»\82ç\82­\82 \82È\82½\82Í\8ew\92è\83h\83\89\83C\83o\81[\82ð\92T\82·\82×\82«\82¾\82ë\82¤\81D");
@@ -278,7 +278,7 @@ boolean force;      /* Quietly force this animal */
 */
         pline("%s\82ð\89ö\89ä\82µ\82Ä\82¢\82é\82Ì\82Å\8fæ\82ê\82È\82¢\81D", makeplural(body_part(LEG)));
         if (force && wizard && yn("Heal your legs?") == 'y')
-            HWounded_legs = EWounded_legs = 0;
+            HWounded_legs = EWounded_legs = 0L;
         else
             return (FALSE);
     }
@@ -301,14 +301,27 @@ boolean force;      /* Quietly force this animal */
 
     /* Can the player reach and see the monster? */
     if (!mtmp || (!force && ((Blind && !Blind_telepat) || mtmp->mundetected
-                             || mtmp->m_ap_type == M_AP_FURNITURE
-                             || mtmp->m_ap_type == M_AP_OBJECT))) {
+                             || M_AP_TYPE(mtmp) == M_AP_FURNITURE
+                             || M_AP_TYPE(mtmp) == M_AP_OBJECT))) {
 /*JP
         pline("I see nobody there.");
 */
         pline("\82»\82±\82É\82Í\89½\82à\8c©\82¦\82È\82¢\81D");
         return (FALSE);
     }
+    if (mtmp->data == &mons[PM_LONG_WORM]
+        && (u.ux + u.dx != mtmp->mx || u.uy + u.dy != mtmp->my)) {
+        /* As of 3.6.2:  test_move(below) is used to check for trying to mount
+           diagonally into or out of a doorway or through a tight squeeze;
+           attempting to mount a tail segment when hero was not adjacent
+           to worm's head could trigger an impossible() in worm_cross()
+           called from test_move(), so handle not-on-head before that */
+/*JP
+        You("couldn't ride %s, let alone its tail.", a_monnam(mtmp));
+*/
+        You("%s\82É\82Í\8fæ\82ê\82È\82¢\81C\82à\82¿\82ë\82ñ\90K\94ö\82É\82à\8fæ\82ê\82È\82¢\81D", a_monnam(mtmp));
+        return FALSE;
+    }
     if (u.uswallow || u.ustuck || u.utrap || Punished
         || !test_move(u.ux, u.uy, mtmp->mx - u.ux, mtmp->my - u.uy,
                       TEST_MOVE)) {
@@ -358,7 +371,7 @@ boolean force;      /* Quietly force this animal */
     if (mtmp->mtrapped) {
         struct trap *t = t_at(mtmp->mx, mtmp->my);
 
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You_cant("mount %s while %s's trapped in %s.", mon_nam(mtmp),
                  mhe(mtmp), an(defsyms[trap_to_defsym(t->ttyp)].explanation));
 #else
@@ -371,7 +384,7 @@ boolean force;      /* Quietly force this animal */
     if (!force && !Role_if(PM_KNIGHT) && !(--mtmp->mtame)) {
         /* no longer tame */
         newsym(mtmp->mx, mtmp->my);
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         pline("%s resists%s!", Monnam(mtmp),
               mtmp->mleashed ? " and its leash comes off" : "");
 #else
@@ -409,7 +422,7 @@ boolean force;      /* Quietly force this animal */
         return (FALSE);
     }
     if (!force && uarm && is_metallic(uarm) && greatest_erosion(uarm)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         Your("%s armor is too stiff to be able to mount %s.",
              uarm->oeroded ? "rusty" : "corroded", mon_nam(mtmp));
 #else
@@ -463,6 +476,11 @@ boolean force;      /* Quietly force this animal */
         You("mount %s.", mon_nam(mtmp));
 */
         You("%s\82É\8fæ\82Á\82½\81D", mon_nam(mtmp));
+        if (Flying)
+/*JP
+            You("and %s take flight together.", mon_nam(mtmp));
+*/
+            You("\82Æ%s\82Í\88ê\8f\8f\82É\8bó\82ð\94ò\82ñ\82¾\81D", mon_nam(mtmp));
     }
     /* setuwep handles polearms differently when you're mounted */
     if (uwep && is_pole(uwep))
@@ -482,7 +500,7 @@ exercise_steed()
         return;
 
     /* It takes many turns of riding to exercise skill */
-    if (u.urideturns++ >= 100) {
+    if (++u.urideturns >= 100) {
         u.urideturns = 0;
         use_skill(P_RIDING, 1);
     }
@@ -519,19 +537,19 @@ kick_steed()
                 u.usteed->mcanmove = 1;
             }
             if (u.usteed->msleeping || !u.usteed->mcanmove)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s stirs.", He);
 #else
                 pline("%s\82Í\90g\82\82ë\82¬\82µ\82½\81D", He);
 #endif
             else
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s rouses %sself!", He, mhim(u.usteed));
 #else
                 pline("%s\82Í\95±\8bN\82µ\82½\81I", He);
 #endif
         } else
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline("%s does not respond.", He);
 #else
             pline("%s\82Í\94½\89\9e\82µ\82È\82¢\81D", He);
@@ -617,7 +635,7 @@ int reason; /* Player was thrown off etc. */
 {
     struct monst *mtmp;
     struct obj *otmp;
-    coord cc;
+    coord cc, steedcc;
 /*JP
     const char *verb = "fall";
 */
@@ -674,19 +692,19 @@ int reason; /* Player was thrown off etc. */
     case DISMOUNT_BYCHOICE:
     default:
         if (otmp && otmp->cursed) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             You("can't.  The saddle %s cursed.",
                 otmp->bknown ? "is" : "seems to be");
 #else
             You("\8d~\82è\82ç\82ê\82È\82¢\81D\88Æ\82Í\8eô\82í\82ê\82Ä\82¢\82é%s\81D",
                 otmp->bknown ? "" : "\82æ\82¤\82¾");
 #endif
-            otmp->bknown = TRUE;
+            otmp->bknown = 1; /* ok to skip set_bknown() here */
             return;
         }
         if (!have_spot) {
 /*JP
-            You("can't. There isn't anywhere for you to stand.");
+            You("can't.  There isn't anywhere for you to stand.");
 */
             pline("\82 \82È\82½\82Ì\97§\82Â\8fê\8f\8a\82ª\82È\82¢\82Ì\82Å\8d~\82è\82ç\82ê\82È\82¢\81D");
             return;
@@ -710,29 +728,61 @@ int reason; /* Player was thrown off etc. */
     }
     /* While riding, Wounded_legs refers to the steed's legs;
        after dismounting, it reverts to the hero's legs. */
-    if (repair_leg_damage) {
-        /* [TODO: make heal_legs() take a parameter to handle this] */
-        in_steed_dismounting = TRUE;
-        heal_legs();
-        in_steed_dismounting = FALSE;
-    }
+    if (repair_leg_damage)
+        heal_legs(1);
 
     /* Release the steed and saddle */
     u.usteed = 0;
     u.ugallop = 0L;
+    /*
+     * rloc(), rloc_to(), and monkilled()->mondead()->m_detach() all
+     * expect mtmp to be on the map or else have mtmp->mx be 0, but
+     * setting the latter to 0 here would interfere with dropping
+     * the saddle.  Prior to 3.6.2, being off the map didn't matter.
+     *
+     * place_monster() expects mtmp to be alive and not be u.usteed.
+     *
+     * Unfortunately, <u.ux,u.uy> (former steed's implicit location)
+     * might now be occupied by an engulfer, so we can't just put mtmp
+     * at that spot.  An engulfer's previous spot will be unoccupied
+     * but we don't know where that was and even if we did, it might
+     * be hostile terrain.
+     */
+    steedcc.x = u.ux, steedcc.y = u.uy;
+    if (m_at(u.ux, u.uy)) {
+        /* hero's spot has a monster in it; hero must have been plucked
+           from saddle as engulfer moved into his spot--other dismounts
+           shouldn't run into this situation; find nearest viable spot */
+        if (!enexto(&steedcc, u.ux, u.uy, mtmp->data)
+            /* no spot? must have been engulfed by a lurker-above over
+               water or lava; try requesting a location for a flyer */
+            && !enexto(&steedcc, u.ux, u.uy, &mons[PM_BAT]))
+            /* still no spot; last resort is any spot within bounds */
+            (void) enexto(&steedcc, u.ux, u.uy, &mons[PM_GHOST]);
+    }
+    if (!m_at(steedcc.x, steedcc.y)) {
+        if (mtmp->mhp < 1)
+            mtmp->mhp = 0; /* make sure it isn't negative */
+        mtmp->mhp++; /* force at least one hit point, possibly resurrecting */
+        place_monster(mtmp, steedcc.x, steedcc.y);
+        mtmp->mhp--; /* take the extra hit point away: cancel resurrection */
+    } else {
+        impossible("Dismounting: can't place former steed on map.");
+    }
+
+    if (!DEADMONSTER(mtmp)) {
+        /* if for bones, there's no reason to place the hero;
+           we want to make room for potential ghost, so move steed */
+        if (reason == DISMOUNT_BONES) {
+            /* move the steed to an adjacent square */
+            if (enexto(&cc, u.ux, u.uy, mtmp->data))
+                rloc_to(mtmp, cc.x, cc.y);
+            else /* evidently no room nearby; move steed elsewhere */
+                (void) rloc(mtmp, FALSE);
+            return;
+        }
 
-    /* Set player and steed's position.  Try moving the player first
-       unless we're in the midst of creating a bones file. */
-    if (reason == DISMOUNT_BONES) {
-        /* move the steed to an adjacent square */
-        if (enexto(&cc, u.ux, u.uy, mtmp->data))
-            rloc_to(mtmp, cc.x, cc.y);
-        else /* evidently no room nearby; move steed elsewhere */
-            (void) rloc(mtmp, FALSE);
-        return;
-    }
-    if (mtmp->mhp > 0) {
-        place_monster(mtmp, u.ux, u.uy);
+        /* Set hero's and/or steed's positions.  Try moving the hero first. */
         if (!u.uswallow && !u.ustuck && have_spot) {
             struct permonst *mdat = mtmp->data;
 
@@ -750,7 +800,7 @@ int reason; /* Player was thrown off etc. */
                         adjalign(-1);
                     }
                 } else if (is_lava(u.ux, u.uy)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     pline("%s is pulled into the %s!", Monnam(mtmp),
                           hliquid("lava"));
 #else
@@ -782,7 +832,7 @@ int reason; /* Player was thrown off etc. */
              * falling into the hole).
              */
             /* [ALI] No need to move the player if the steed died. */
-            if (mtmp->mhp > 0) {
+            if (!DEADMONSTER(mtmp)) {
                 /* Keep steed here, move the player to cc;
                  * teleds() clears u.utrap
                  */
@@ -794,20 +844,29 @@ int reason; /* Player was thrown off etc. */
                 if (save_utrap)
                     (void) mintrap(mtmp);
             }
-            /* Couldn't... try placing the steed */
+
+        /* Couldn't move hero... try moving the steed. */
         } else if (enexto(&cc, u.ux, u.uy, mtmp->data)) {
             /* Keep player here, move the steed to cc */
             rloc_to(mtmp, cc.x, cc.y);
             /* Player stays put */
-            /* Otherwise, kill the steed */
+
+        /* Otherwise, kill the steed. */
         } else {
-            killed(mtmp);
-            adjalign(-1);
+            if (reason == DISMOUNT_BYCHOICE) {
+                /* [un]#ride: hero gets credit/blame for killing steed */
+                killed(mtmp);
+                adjalign(-1);
+            } else {
+                /* other dismount: kill former steed with no penalty;
+                   damage type is just "neither AD_DGST nor -AD_RBRE" */
+                monkilled(mtmp, "", -AD_PHYS);
+            }
         }
-    }
+    } /* !DEADMONST(mtmp) */
 
-    /* Return the player to the floor */
-    if (reason != DISMOUNT_ENGULFED) {
+    /* usually return the hero to the surface */
+    if (reason != DISMOUNT_ENGULFED && reason != DISMOUNT_BONES) {
         in_steed_dismounting = TRUE;
         (void) float_down(0L, W_SADDLE);
         in_steed_dismounting = FALSE;
@@ -886,15 +945,39 @@ place_monster(mon, x, y)
 struct monst *mon;
 int x, y;
 {
+    struct monst *othermon;
+    const char *monnm, *othnm;
+    char buf[QBUFSZ];
+
+    buf[0] = '\0';
+    /* normal map bounds are <1..COLNO-1,0..ROWNO-1> but sometimes
+       vault guards (either living or dead) are parked at <0,0> */
+    if (!isok(x, y) && (x != 0 || y != 0 || !mon->isgd)) {
+        describe_level(buf);
+        impossible("trying to place %s at <%d,%d> mstate:%lx on %s",
+                   minimal_monnam(mon, TRUE), x, y, mon->mstate, buf);
+        x = y = 0;
+    }
     if (mon == u.usteed
         /* special case is for convoluted vault guard handling */
         || (DEADMONSTER(mon) && !(mon->isgd && x == 0 && y == 0))) {
-        impossible("placing %s onto map?",
-                   (mon == u.usteed) ? "steed" : "defunct monster");
+        describe_level(buf);
+        impossible("placing %s onto map, mstate:%lx, on %s?",
+                   (mon == u.usteed) ? "steed" : "defunct monster",
+                   mon->mstate, buf);
         return;
     }
+    if ((othermon = level.monsters[x][y]) != 0) {
+        describe_level(buf);
+        monnm = minimal_monnam(mon, FALSE);
+        othnm = (mon != othermon) ? minimal_monnam(othermon, TRUE) : "itself";
+        impossible("placing %s over %s at <%d,%d>, mstates:%lx %lx on %s?",
+                   monnm, othnm, x, y, othermon->mstate, mon->mstate, buf);
+    }
     mon->mx = x, mon->my = y;
     level.monsters[x][y] = mon;
+    mon->mstate &= ~(MON_OFFMAP | MON_MIGRATING | MON_LIMBO | MON_BUBBLEMOVE
+                     | MON_ENDGAME_FREE | MON_ENDGAME_MIGR);
 }
 
 /*steed.c*/