]> granicus.if.org Git - nethack/commitdiff
fix github #172 - ^T inconsistencies; add m^T
authorPatR <rankin@nethack.org>
Fri, 4 Jan 2019 01:37:00 +0000 (17:37 -0800)
committerPatR <rankin@nethack.org>
Fri, 4 Jan 2019 01:37:00 +0000 (17:37 -0800)
Fixes #172

Casting teleport-away via ^T used different requirements for energy,
strength, and hunger than casting it via 'Z'.  The strength and hunger
requirements were more stringent, the energy one more lenient.  When
it rejected a cast attempt due to any of those, it used up the move,
but 'Z' didn't.

When testing my fix, I wanted an easier way than a debugger to control
how ^T interacts with wizard mode, so finally got around to a first
cut at being able to invoke it via wizard mode but not override those
energy/strength/hunger requirements.  It uses the 'm' prefix to ask
for a menu.  'm^T' gives four options about how to teleport.  (There
are other permutations which aren't handled.)

Also noticed while testing:  ^T wouldn't attempt to cast teleport-away
if you didn't know the corresponding spellbook.  'Z' will attempt that
because it is possible to forget a book and still know its spell.

doc/fixes36.2
include/extern.h
src/cmd.c
src/spell.c
src/teleport.c

index beeed33a73f82c38570371d46c64a74a0d47d25d..015ce2c20a59b363605b66b0bf4f1c9f2125ac5d 100644 (file)
@@ -1,4 +1,4 @@
-$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.221 $ $NHDT-Date: 1546467443 2019/01/02 22:17:23 $
+$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.222 $ $NHDT-Date: 1546565812 2019/01/04 01:36:52 $
 
 This fixes36.2 file is here to capture information about updates in the 3.6.x
 lineage following the release of 3.6.1 in April 2018. Please note, however,
@@ -322,6 +322,11 @@ since knives became stackable in 3.6.0, fake player monsters could be given
 using 'O' to attempt to set bouldersym to a monster letter or warning digit
        while it still had its default value would override the display value
        for it to be <NUL> ('\0') after 'badoption' feedback
+when ^T resorted to the teleport-away spell if hero didn't have intrinsic
+       telepotation, it used different hunger/strength/energy requirements
+       than casting with 'Z'; ^T also required that the corresponding book
+       be known even though knowing and casting a spell should be (and is
+       with 'Z') possible after forgetting the spellbook due to amnesia
 
 
 Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository
@@ -493,6 +498,8 @@ when sortloot is enabled, gems are grouped in subsets (1) unseen gems and
        (4) identified glass, (5) unseen stones (includes unseen rocks),
        (6) seen but unidentified gray stones, (7) identified gray stones,
        and (8) seen rocks (IDed/unIDed not applicable)
+in wizard mode, ^T can be preceded by 'm' prefix in order to test teleporting
+       without having wizard mode override various restrictions
 
 
 NetHack Community Patches (or Variation) Included
index dbef64c22795b6c5c8f15474ebf5e1bacef76001..8e3f542945d14395d2520dc9793f45dbde6e8356 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 extern.h        $NHDT-Date: 1545964581 2018/12/28 02:36:21 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.678 $ */
+/* NetHack 3.6 extern.h        $NHDT-Date: 1546565812 2019/01/04 01:36:52 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.680 $ */
 /* Copyright (c) Steve Creps, 1988.                              */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -2329,6 +2329,7 @@ E void NDECL(age_spells);
 E int NDECL(docast);
 E int FDECL(spell_skilltype, (int));
 E int FDECL(spelleffects, (int, BOOLEAN_P));
+E int FDECL(tport_spell, (int));
 E void NDECL(losespells);
 E int NDECL(dovspell);
 E void FDECL(initialspell, (struct obj *));
index 04df1045893afdda21fd32f9b9b74fb7d65db3f6..061b156909136aa0b40a82f7370cc725c9bd59be 100644 (file)
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -1,4 +1,4 @@
-/* NetHack 3.6 cmd.c   $NHDT-Date: 1546038393 2018/12/28 23:06:33 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.323 $ */
+/* NetHack 3.6 cmd.c   $NHDT-Date: 1546565813 2019/01/04 01:36:53 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.324 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /*-Copyright (c) Robert Patrick Rankin, 2013. */
 /* NetHack may be freely redistributed.  See license for details. */
@@ -4408,8 +4408,8 @@ int NDECL((*cmd_func));
         || cmd_func == doloot
         /* travel: pop up a menu of interesting targets in view */
         || cmd_func == dotravel
-        /* wizard mode ^V */
-        || cmd_func == wiz_level_tele
+        /* wizard mode ^V and ^T */
+        || cmd_func == wiz_level_tele || cmd_func == dotelecmd
         /* 'm' prefix allowed for some extended commands */
         || cmd_func == doextcmd || cmd_func == doextlist)
         return TRUE;
index a7fd388cadd3ace78aba29607dd929c2e5c1d3b9..9cbb9a480063e70a63489e9accc01feb63c30abd 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 spell.c $NHDT-Date: 1542765363 2018/11/21 01:56:03 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.87 $ */
+/* NetHack 3.6 spell.c $NHDT-Date: 1546565814 2019/01/04 01:36:54 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.88 $ */
 /*      Copyright (c) M. Stephenson 1988                          */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -923,6 +923,10 @@ boolean atme;
     } else if (spellknow(spell) <= KEEN / 10) { /* 2000 turns left */
         Your("recall of this spell is gradually fading.");
     }
+    /*
+     *  Note: dotele() also calculates energy use and checks nutrition
+     *  and strength requirements; it any of these change, update it too.
+     */
     energy = (spellev(spell) * 5); /* 5 <= energy <= 35 */
 
     if (u.uhunger <= 10 && spellid(spell) != SPE_DETECT_FOOD) {
@@ -1269,6 +1273,62 @@ throwspell()
     return 1;
 }
 
+/* add/hide/remove/unhide teleport-away on behalf of dotelecmd() to give
+   more control to behavior of ^T when used in wizard mode */
+int
+tport_spell(what)
+int what;
+{
+    static struct tport_hideaway {
+        struct spell savespell;
+        int tport_indx;
+    } save_tport;
+    int i;
+/* also defined in teleport.c */
+#define NOOP_SPELL  0
+#define HIDE_SPELL  1
+#define ADD_SPELL   2
+#define UNHIDESPELL 3
+#define REMOVESPELL 4
+
+    for (i = 0; i < MAXSPELL; i++)
+        if (spellid(i) == SPE_TELEPORT_AWAY || spellid(i) == NO_SPELL)
+            break;
+    if (i == MAXSPELL) {
+        impossible("tport_spell: spellbook full");
+        /* wizard mode ^T is not able to honor player's menu choice */
+    } else if (spellid(i) == NO_SPELL) {
+        if (what == HIDE_SPELL || what == REMOVESPELL) {
+            save_tport.tport_indx = MAXSPELL;
+        } else if (what == UNHIDESPELL) {
+            /*assert( save_tport.savespell.sp_id == SPE_TELEPORT_AWAY );*/
+            spl_book[save_tport.tport_indx] = save_tport.savespell;
+            save_tport.tport_indx = MAXSPELL; /* burn bridge... */
+        } else if (what == ADD_SPELL) {
+            save_tport.savespell = spl_book[i];
+            save_tport.tport_indx = i;
+            spl_book[i].sp_id = SPE_TELEPORT_AWAY;
+            spl_book[i].sp_lev = objects[SPE_TELEPORT_AWAY].oc_level;
+            spl_book[i].sp_know = KEEN;
+            return REMOVESPELL; /* operation needed to reverse */
+        }
+    } else { /* spellid(i) == SPE_TELEPORT_AWAY */
+        if (what == ADD_SPELL || what == UNHIDESPELL) {
+            save_tport.tport_indx = MAXSPELL;
+        } else if (what == REMOVESPELL) {
+            /*assert( i == save_tport.tport_indx );*/
+            spl_book[i] = save_tport.savespell;
+            save_tport.tport_indx = MAXSPELL;
+        } else if (what == HIDE_SPELL) {
+            save_tport.savespell = spl_book[i];
+            save_tport.tport_indx = i;
+            spl_book[i].sp_id = NO_SPELL;
+            return UNHIDESPELL; /* operation needed to reverse */
+        }
+    }
+    return NOOP_SPELL;
+}
+
 /* forget a random selection of known spells due to amnesia;
    they used to be lost entirely, as if never learned, but now we
    just set the memory retention to zero so that they can't be cast */
index 9aedc0c3ef426820740a9a9e43fc9654027768d2..e2a6c32dc3f819f67ae0cb9c49536ce7927388c9 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 teleport.c      $NHDT-Date: 1544401270 2018/12/10 00:21:10 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.81 $ */
+/* NetHack 3.6 teleport.c      $NHDT-Date: 1546565815 2019/01/04 01:36:55 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.82 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /*-Copyright (c) Robert Patrick Rankin, 2011. */
 /* NetHack may be freely redistributed.  See license for details. */
@@ -496,17 +496,114 @@ struct obj *scroll;
     return result;
 }
 
+/* ^T command; 'm ^T' == choose among several teleport modes */
 int
 dotelecmd()
 {
-    return dotele((wizard) ? TRUE : FALSE);
+    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, MENU_UNSELECTED);
+        }
+        end_menu(win, "Which way do you want to teleport?");
+        if (select_menu(win, PICK_ONE, &picks) > 0) {
+            tmode = picks[0].item.a_int;
+            free((genericptr_t) picks);
+        } else {
+            return 0;
+        }
+        destroy_nhwindow(win);
+        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(break_the_rules)
-boolean 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);
@@ -517,9 +614,9 @@ boolean break_the_rules;
         trap_once = trap->once; /* trap may get deleted, save this */
         if (trap->once) {
             pline("This is a vault teleport, usable once only.");
-            if (yn("Jump in?") == 'n')
+            if (yn("Jump in?") == 'n') {
                 trap = 0;
-            else {
+            else {
                 deltrap(trap);
                 newsym(u.ux, u.uy);
             }
@@ -534,58 +631,72 @@ boolean break_the_rules;
 
         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 (!break_the_rules) {
-                if (!castit) {
-                    if (!Teleportation)
-                        You("don't know that spell.");
-                    else
-                        You("are not able to teleport at will.");
-                    return 0;
-                }
+            /* 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) {
+                You("%s.",
+                    !Teleportation ? ((sp_no < MAXSPELL)
+                                        ? "can't cast that spell"
+                                        : "don't know that spell")
+                                   : "are not able to teleport at will");
+                return 0;
             }
         }
 
-        if (u.uhunger <= 100 || ACURR(A_STR) < 6) {
-            if (!break_the_rules) {
-                You("lack the strength %s.",
-                    castit ? "for a teleport spell" : "to teleport");
-                return 1;
-            }
+        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) {
+            cantdoit = "are too weak from hunger";
+        } else if (ACURR(A_STR) < 4) {
+            cantdoit = "lack the strength";
+        } else if (energy > u.uen) {
+            cantdoit = "lack the energy";
         }
-
-        energy = objects[SPE_TELEPORT_AWAY].oc_level * 7 / 2 - 2;
-        if (u.uen <= energy) {
-            if (break_the_rules)
-                energy = u.uen;
-            else {
-                You("lack the energy %s.",
-                    castit ? "for a teleport spell" : "to teleport");
-                return 1;
-            }
+        if (cantdoit) {
+            You("%s %s.", cantdoit,
+                castit ? "for a teleport spell" : "to teleport");
+            return 0;
+        } else if (check_capacity(
+                       "Your concentration falters from carrying so much.")) {
+            return 1; /* this failure in spelleffects() also uses the move */
         }
 
-        if (check_capacity(
-                "Your concentration falters from carrying so much."))
-            return 1;
-
         if (castit) {
+            /* energy cost is deducted in spelleffects() */
             exercise(A_WIS, TRUE);
             if (spelleffects(sp_no, TRUE))
                 return 1;
             else if (!break_the_rules)
                 return 0;
         } else {
-            if (!break_the_rules) {
-                u.uen -= energy;
-                context.botl = 1;
-            }
+            /* bypassing spelleffects(); apply energy cost directly */
+            u.uen -= energy;
+            context.botl = 1;
         }
     }
 
@@ -596,7 +707,7 @@ boolean break_the_rules;
             tele();
         (void) next_to_u();
     } else {
-        You1(shudder_for_moment);
+        You("%s", shudder_for_moment);
         return 0;
     }
     if (!trap)