OSDN Git Service

fix indent
[jnethack/source.git] / src / teleport.c
index beff951..41cca5a 100644 (file)
@@ -1,11 +1,11 @@
-/* NetHack 3.6 teleport.c      $NHDT-Date: 1523306912 2018/04/09 20:48:32 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.73 $ */
+/* NetHack 3.6 teleport.c      $NHDT-Date: 1553885439 2019/03/29 18:50:39 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.86 $ */
 /* 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-2019            */
 /* JNetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
@@ -135,7 +135,7 @@ unsigned entflags;
         mdat = &mons[u.umonster];
     }
     fakemon = zeromonst;
-    set_mon_data(&fakemon, mdat, -1); /* set up for goodpos */
+    set_mon_data(&fakemon, mdat); /* set up for goodpos */
 
     good_ptr = good;
     range = 1;
@@ -257,6 +257,7 @@ register int nux, nuy;
 boolean allow_drag;
 {
     boolean ball_active, ball_still_in_range;
+    struct monst *vault_guard = vault_occupied(u.urooms) ? findgd() : 0;
 
     if (u.utraptype == TT_BURIEDBALL) {
         /* unearth it */
@@ -294,7 +295,7 @@ boolean allow_drag;
             }
         }
     }
-    u.utrap = 0;
+    reset_utrap(FALSE);
     u.ustuck = 0;
     u.ux0 = u.ux;
     u.uy0 = u.uy;
@@ -345,6 +346,11 @@ boolean allow_drag;
     vision_full_recalc = 1;
     nomul(0);
     vision_recalc(0); /* vision before effects */
+    /* if terrain type changes, levitation or flying might become blocked
+       or unblocked; might issue message, so do this after map+vision has
+       been updated for new location instead of right after u_on_newpos() */
+    if (levl[u.ux][u.uy].typ != levl[u.ux0][u.uy0].typ)
+        switch_terrain();
     if (telescroll) {
         /* when teleporting by scroll, we need to handle discovery
            now before getting feedback about any objects at our
@@ -354,6 +360,21 @@ boolean allow_drag;
         else
             telescroll = 0; /* no discovery by scrolltele()'s caller */
     }
+    /* sequencing issue:  we want guard's alarm, if any, to occur before
+       room entry message, if any, so need to check for vault exit prior
+       to spoteffects; but spoteffects() sets up new value for u.urooms
+       and vault code depends upon that value, so we need to fake it */
+    if (vault_guard) {
+        char save_urooms[5]; /* [sizeof u.urooms] */
+
+        Strcpy(save_urooms, u.urooms);
+        Strcpy(u.urooms, in_rooms(u.ux, u.uy, VAULT));
+        /* if hero has left vault, make guard notice */
+        if (!vault_occupied(u.urooms))
+            uleftvault(vault_guard);
+        Strcpy(u.urooms, save_urooms); /* reset prior to spoteffects() */
+    }
+    /* possible shop entry message comes after guard's shrill whistle */
     spoteffects(TRUE);
     invocation_message();
 }
@@ -475,7 +496,7 @@ struct obj *scroll;
             Strcpy(whobuf, "you");
             if (u.usteed)
                 Sprintf(eos(whobuf), " and %s", mon_nam(u.usteed));
-            pline("To what position do %s want to be teleported?", whobuf);
+            pline("Where do %s want to be teleported?", whobuf);
 #else
             pline("\82Ç\82Ì\88Ê\92u\82É\8fu\8aÔ\88Ú\93®\82µ\82Ü\82·\82©\81H");
 #endif
@@ -521,10 +542,123 @@ struct obj *scroll;
     return result;
 }
 
+/* ^T command; 'm ^T' == choose among several teleport modes */
+int
+dotelecmd()
+{
+    long save_HTele, save_ETele;
+    int res, added, hidden;
+    boolean ignore_restrictions = FALSE;
+/* also defined in spell.c */
+#define NOOP_SPELL  0
+#define HIDE_SPELL  1
+#define ADD_SPELL   2
+#define UNHIDESPELL 3
+#define REMOVESPELL 4
+
+    /* normal mode; ignore 'm' prefix if it was given */
+    if (!wizard)
+        return dotele(FALSE);
+
+    added = hidden = NOOP_SPELL;
+    save_HTele = HTeleportation, save_ETele = ETeleportation;
+    if (!iflags.menu_requested) {
+        ignore_restrictions = TRUE;
+    } else {
+        static const struct tporttypes {
+            char menulet;
+            const char *menudesc;
+        } tports[] = {
+            /*
+             * Potential combinations:
+             *  1) attempt ^T without intrinsic, not know spell;
+             *  2) via intrinsic, not know spell, obey restrictions;
+             *  3) via intrinsic, not know spell, ignore restrictions;
+             *  4) via intrinsic, know spell, obey restrictions;
+             *  5) via intrinsic, know spell, ignore restrictions;
+             *  6) via spell, not have intrinsic, obey restrictions;
+             *  7) via spell, not have intrinsic, ignore restrictions;
+             *  8) force, obey other restrictions;
+             *  9) force, ignore restrictions.
+             * We only support the 1st (t), 2nd (n), 6th (s), and 9th (w).
+             *
+             * This ignores the fact that there is an experience level
+             * (or poly-form) requirement which might make normal ^T fail.
+             */
+            { 'n', "normal ^T on demand; no spell, obey restrictions" },
+            { 's', "via spellcast; no intrinsic teleport" },
+            { 't', "try ^T without having it; no spell" },
+            { 'w', "debug mode; ignore restrictions" }, /* trad wizard mode */
+        };
+        menu_item *picks = (menu_item *) 0;
+        anything any;
+        winid win;
+        int i, tmode;
+
+        win = create_nhwindow(NHW_MENU);
+        start_menu(win);
+        any = zeroany;
+        for (i = 0; i < SIZE(tports); ++i) {
+            any.a_int = (int) tports[i].menulet;
+            add_menu(win, NO_GLYPH, &any, (char) any.a_int, 0, ATR_NONE,
+                     tports[i].menudesc,
+                     (tports[i].menulet == 'w') ? MENU_SELECTED
+                                                : MENU_UNSELECTED);
+        }
+        end_menu(win, "Which way do you want to teleport?");
+        i = select_menu(win, PICK_ONE, &picks);
+        destroy_nhwindow(win);
+        if (i > 0) {
+            tmode = picks[0].item.a_int;
+            /* if we got 2, use the one which wasn't preselected */
+            if (i > 1 && tmode == 'w')
+                tmode = picks[1].item.a_int;
+            free((genericptr_t) picks);
+        } else if (i == 0) {
+            /* preselected one was explicitly chosen and got toggled off */
+            tmode = 'w';
+        } else { /* ESC */
+            return 0;
+        }
+        switch (tmode) {
+        case 'n':
+            HTeleportation |= I_SPECIAL; /* confer intrinsic teleportation */
+            hidden = tport_spell(HIDE_SPELL); /* hide teleport-away */
+            break;
+        case 's':
+            HTeleportation = ETeleportation = 0L; /* suppress intrinsic */
+            added = tport_spell(ADD_SPELL); /* add teleport-away */
+            break;
+        case 't':
+            HTeleportation = ETeleportation = 0L; /* suppress intrinsic */
+            hidden = tport_spell(HIDE_SPELL); /* hide teleport-away */
+            break;
+        case 'w':
+            ignore_restrictions = TRUE;
+            break;
+        }
+    }
+
+    /* if dotele() can be fatal, final disclosure might lie about
+       intrinsic teleportation; we should be able to live with that
+       since the menu finagling is only applicable in wizard mode */
+    res = dotele(ignore_restrictions);
+
+    HTeleportation = save_HTele;
+    ETeleportation = save_ETele;
+    if (added != NOOP_SPELL || hidden != NOOP_SPELL)
+        /* can't both be non-NOOP so addition will yield the non-NOOP one */
+        (void) tport_spell(added + hidden - NOOP_SPELL);
+
+    return res;
+}
+
 int
-dotele()
+dotele(break_the_rules)
+boolean break_the_rules; /* True: wizard mode ^T */
 {
     struct trap *trap;
+    const char *cantdoit;
     boolean trap_once = FALSE;
 
     trap = t_at(u.ux, u.uy);
@@ -539,11 +673,11 @@ dotele()
 */
             pline("\88ê\93x\82©\82¬\82è\82Ì\91q\8cÉ\82Ö\82Ì\8fu\8aÔ\88Ú\93®\82Ìã©\82¾\81D");
 /*JP
-            if (yn("Jump in?") == 'n')
+            if (yn("Jump in?") == 'n') {
 */
-            if (yn("\94ò\82Ñ\8d\9e\82Þ\81H") == 'n')
+            if (yn("\94ò\82Ñ\8d\9e\82Þ\81H") == 'n') {
                 trap = 0;
-            else {
+            else {
                 deltrap(trap);
                 newsym(u.ux, u.uy);
             }
@@ -562,73 +696,95 @@ dotele()
 
         if (!Teleportation || (u.ulevel < (Role_if(PM_WIZARD) ? 8 : 12)
                                && !can_teleport(youmonst.data))) {
-            /* Try to use teleport away spell. */
-            if (objects[SPE_TELEPORT_AWAY].oc_name_known && !Confusion)
-                for (sp_no = 0; sp_no < MAXSPELL; sp_no++)
-                    if (spl_book[sp_no].sp_id == SPE_TELEPORT_AWAY) {
-                        castit = TRUE;
-                        break;
-                    }
-            if (!wizard) {
-                if (!castit) {
-                    if (!Teleportation)
-/*JP
-                        You("don't know that spell.");
-*/
-                        You("\82»\82ñ\82È\96\82\96@\82Í\92m\82ç\82È\82¢\81D");
-                    else
-/*JP
-                        You("are not able to teleport at will.");
-*/
-                        You("\8e©\95ª\82Ì\88Ó\8ev\82Å\8fu\8aÔ\88Ú\93®\82Å\82«\82È\82¢\81D");
-                    return 0;
-                }
-            }
-        }
-
-        if (u.uhunger <= 100 || ACURR(A_STR) < 6) {
-            if (!wizard) {
+            /* Try to use teleport away spell.
+               3.6.2: this used to require that you know the spellbook
+               (probably just intended as an optimization to skip the
+               lookup loop) but it is possible to know and cast a spell
+               after forgetting its book due to amnesia. */
+            for (sp_no = 0; sp_no < MAXSPELL; sp_no++)
+                if (spl_book[sp_no].sp_id == SPE_TELEPORT_AWAY)
+                    break;
+            /* casting isn't inhibited by being Stunned (...it ought to be) */
+            castit = (sp_no < MAXSPELL && !Confusion);
+            if (!castit && !break_the_rules) {
 #if 0 /*JP*/
-                You("lack the strength %s.",
-                    castit ? "for a teleport spell" : "to teleport");
+                You("%s.",
+                    !Teleportation ? ((sp_no < MAXSPELL)
+                                        ? "can't cast that spell"
+                                        : "don't know that spell")
+                                   : "are not able to teleport at will");
 #else
-                You("%s\82¾\82¯\82Ì\97Í\82ª\82È\82¢\81D",
-                    castit ? "\8fu\8aÔ\88Ú\93®\82Ì\96\82\96@\82ð\8f¥\82¦\82é" : "\8fu\8aÔ\88Ú\93®\82·\82é");
+                You("%s\81D",
+                    !Teleportation ? ((sp_no < MAXSPELL)
+                                        ? "\82»\82Ì\96\82\96@\82Í\8f¥\82¦\82ç\82ê\82È\82¢"
+                                        : "\82»\82ñ\82È\96\82\96@\82Í\92m\82ç\82È\82¢")
+                                   : "\8e©\95ª\82Ì\88Ó\8ev\82Å\8fu\8aÔ\88Ú\93®\82Å\82«\82È\82¢");
 #endif
-                return 1;
+                return 0;
             }
         }
 
-        energy = objects[SPE_TELEPORT_AWAY].oc_level * 7 / 2 - 2;
-        if (u.uen <= energy) {
-            if (wizard)
-                energy = u.uen;
-            else {
+        cantdoit = 0;
+        /* 3.6.2: the magic numbers for hunger, strength, and energy
+           have been changed to match the ones used in spelleffects().
+           Also, failing these tests used to return 1 and use a move
+           even though casting failure due to these reasons doesn't.
+           [Note: this spellev() is different from the one in spell.c
+           but they both yield the same result.] */
+#define spellev(spell_otyp) ((int) objects[spell_otyp].oc_level)
+        energy = 5 * spellev(SPE_TELEPORT_AWAY);
+        if (break_the_rules) {
+            if (!castit)
+                energy = 0;
+            /* spell will cost more if carrying the Amulet, but the
+               amount is rnd(2 * energy) so we can't know by how much;
+               average is twice the normal cost, but could be triple;
+               the extra energy is spent even if that results in not
+               having enough to cast (which also uses the move) */
+            else if (u.uen < energy)
+                u.uen = energy;
+        } else if (u.uhunger <= 10) {
+/*JP
+            cantdoit = "are too weak from hunger";
+*/
+            cantdoit = "\82É\82Í\82¨\82È\82©\82ª\8bó\82«\82·\82¬\82Ä\82¢\82é";
+        } else if (ACURR(A_STR) < 4) {
+/*JP
+            cantdoit = "lack the strength";
+*/
+            cantdoit = "\82¾\82¯\82Ì\97Í\82ª\82È\82¢";
+        } else if (energy > u.uen) {
+/*JP
+            cantdoit = "lack the energy";
+*/
+            cantdoit = "\82¾\82¯\82Ì\83G\83l\83\8b\83M\81[\82ª\82È\82¢";
+        }
+        if (cantdoit) {
 #if 0 /*JP*/
-                You("lack the energy %s.",
-                    castit ? "for a teleport spell" : "to teleport");
+            You("%s %s.", cantdoit,
+                castit ? "for a teleport spell" : "to teleport");
 #else
-                You("%s\82¾\82¯\82Ì\83G\83l\83\8b\83M\81[\82ª\82È\82¢\81D",
-                    castit ? "\8fu\8aÔ\88Ú\93®\82Ì\96\82\96@\82ð\8f¥\82¦\82é" : "\8fu\8aÔ\88Ú\93®\82·\82é");
+            You("%s%s\81D", cantdoit,
+                castit ? "\8fu\8aÔ\88Ú\93®\82Ì\96\82\96@\82ð\8f¥\82¦\82é" : "\8fu\8aÔ\88Ú\93®\82·\82é");
 #endif
-                return 1;
-            }
-        }
-
-        if (check_capacity(
+            return 0;
+        } else if (check_capacity(
 /*JP
-                "Your concentration falters from carrying so much."))
+                       "Your concentration falters from carrying so much.")) {
 */
-                "\91ò\8eR\82à\82Ì\82ð\8e\9d\82¿\82·\82¬\82Ä\8fW\92\86\82Å\82«\82È\82¢\81D"))
-            return 1;
+                       "\91ò\8eR\82à\82Ì\82ð\8e\9d\82¿\82·\82¬\82Ä\8fW\92\86\82Å\82«\82È\82¢\81D")) {
+            return 1; /* this failure in spelleffects() also uses the move */
+        }
 
         if (castit) {
+            /* energy cost is deducted in spelleffects() */
             exercise(A_WIS, TRUE);
             if (spelleffects(sp_no, TRUE))
                 return 1;
-            else if (!wizard)
+            else if (!break_the_rules)
                 return 0;
         } else {
+            /* bypassing spelleffects(); apply energy cost directly */
             u.uen -= energy;
             context.botl = 1;
         }
@@ -641,7 +797,7 @@ dotele()
             tele();
         (void) next_to_u();
     } else {
-        You1(shudder_for_moment);
+        You("%s", shudder_for_moment);
         return 0;
     }
     if (!trap)
@@ -658,6 +814,8 @@ level_tele()
     char buf[BUFSZ];
     boolean force_dest = FALSE;
 
+    if (iflags.debug_fuzzer)
+        goto random_levtport;
     if ((u.uhave.amulet || In_endgame(&u.uz) || In_sokoban(&u.uz))
         && !wizard) {
 /*JP
@@ -846,6 +1004,8 @@ level_tele()
 
     killer.name[0] = 0; /* still alive, so far... */
 
+    if (iflags.debug_fuzzer && newlev < 0)
+        goto random_levtport;
     if (newlev < 0 && !force_dest) {
         if (*u.ushops0) {
             /* take unpaid inventory items off of shop bills */
@@ -871,11 +1031,11 @@ level_tele()
             Strcpy(killer.name, "\8eá\82­\82µ\82Ä\93V\8d\91\82É\8ds\82Á\82½");
         } else if (newlev == -9) {
 /*JP
-            You_feel("deliriously happy. ");
+            You_feel("deliriously happy.");
 */
             You("\8b\82Á\82½\82æ\82¤\82È\8dK\82¹\82ð\8a´\82\82½\81D");
 /*JP
-            pline("(In fact, you're on Cloud 9!) ");
+            pline("(In fact, you're on Cloud 9!)");
 */
             pline("(\96{\93\96\82É\8bê\82ð\8fæ\82è\89z\82¦\82½\8fê\8f\8a\82É\82¢\82é\81I) ");
             display_nhwindow(WIN_MESSAGE, FALSE);
@@ -1053,9 +1213,9 @@ level_tele_trap(trap, trflags)
 struct trap *trap;
 unsigned trflags;
 {
+#if 0 /*JP*/
     char verbbuf[BUFSZ];
 
-#if 0 /*JP*/
     if ((trflags & VIASITTING) != 0)
         Strcpy(verbbuf, "trigger"); /* follows "You sit down." */
     else
@@ -1172,8 +1332,8 @@ register int x, y;
     register int oldx = mtmp->mx, oldy = mtmp->my;
     boolean resident_shk = mtmp->isshk && inhishop(mtmp);
 
-    if (x == mtmp->mx && y == mtmp->my) /* that was easy */
-        return;
+    if (x == mtmp->mx && y == mtmp->my && m_at(x,y) == mtmp)
+        return; /* that was easy */
 
     if (oldx) { /* "pick up" monster */
         if (mtmp->wormno) {
@@ -1350,7 +1510,7 @@ int in_sight;
         d_level tolevel;
         int migrate_typ = MIGR_RANDOM;
 
-        if ((tt == HOLE || tt == TRAPDOOR)) {
+        if (is_hole(tt)) {
             if (Is_stronghold(&u.uz)) {
                 assign_level(&tolevel, &valley_level);
             } else if (Is_botlevel(&u.uz)) {
@@ -1409,9 +1569,9 @@ int in_sight;
                 if (nlev == depth(&u.uz)) {
                     if (in_sight)
 /*JP
-                    pline("%s shudders for a moment.", Monnam(mtmp));
+                        pline("%s shudders for a moment.", Monnam(mtmp));
 */
-                    pline("%s\82Í\88ê\8fu\90k\82¦\82½\81D", Monnam(mtmp));
+                        pline("%s\82Í\88ê\8fu\90k\82¦\82½\81D", Monnam(mtmp));
                     return 0;
                 }
                 get_level(&tolevel, nlev);