]> granicus.if.org Git - nethack/commitdiff
extend #timeout to cover all properties
authorPatR <rankin@nethack.org>
Mon, 26 Jun 2017 08:04:58 +0000 (01:04 -0700)
committerPatR <rankin@nethack.org>
Mon, 26 Jun 2017 08:04:58 +0000 (01:04 -0700)
Extend the wizard mode #timeout command:  show timeouts for all 67
intrinsics rather than just a handful.  Most won't appear because
they don't have any way to receive a timed value.  Except for...

Extend the wizard mode #wizintrinsic command:  allow setting a
brief (30 turn) timeout for any/every intrinsic, not just for
deafness.  It ought to prompt for duration, but that's more effort
than I'm willing to expend.  This might turn up lots of quirks that
the code isn't prepared to handle (like setting life-saving to
non-zero will break the assumption that it comes from worn amulet).
Perhaps some will warrant fixing, others just a shrug.

There are still some timed events that aren't listed by #timeout:
remaining duration to stay polymorphed in current form, number of
turns until it's safe to pray, luck decay, number of turns until
next attribute exercise/abuse check, probably others that I'm
overlooking.

Bug fix:  while testing, I observed
  Your limbs have turned to stone.
  You have turned to stone.
  You can hear again.
  You are a statue.
when deafness and petrification were timing out at the same time.
This modifies the stoning and sliming countdowns to extend deafness
duration a little if it's about to time out at the tail end of the
stoning or sliming sequence, so that "you can hear again" won't
happen until after life-saving.  There are probably other variations
of simultaneous or near simultaneous timeout that interact oddly.

include/prop.h
src/cmd.c
src/timeout.c

index e4d3ec7aa0fc6289b72f0bca24ff6cd2e00e9732..cb4cd3fde97418df504ea5621ff5fd66e478cad8 100644 (file)
@@ -5,7 +5,11 @@
 #ifndef PROP_H
 #define PROP_H
 
-/*** What the properties are ***/
+/*** What the properties are ***
+ *
+ * note:  propertynames[] array in timeout.c must be kept in synch with these.
+ *        Property #0 is not used.
+ */
 /* Resistances to troubles */
 #define FIRE_RES 1
 #define COLD_RES 2
index fc29aaf6ba19c0f8980a36e5e3a238a1281473fe..de5645d2d9ae8f344dcdcedaccede7808075f236 100644 (file)
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -1141,41 +1141,94 @@ STATIC_PTR int
 wiz_intrinsic(VOID_ARGS)
 {
     if (wizard) {
+        extern const char *const propertynames[]; /* timeout.c */
+        static const char wizintrinsic[] = "#wizintrinsic";
+        static const char fmt[] = "You are%s %s.";
         winid win;
         anything any;
-        int i, n, accelerator;
+        char buf[BUFSZ];
+        int i, n, p, amt, typ;
+        long oldtimeout, newtimeout;
+        const char *propname;
         menu_item *pick_list = (menu_item *) 0;
 
-        static const char *const intrinsics[] = {
-            "deafness",
-        };
-
         win = create_nhwindow(NHW_MENU);
         start_menu(win);
-        accelerator = 0;
 
-        for (i = 0; i < SIZE(intrinsics); ++i) {
-            accelerator = intrinsics[i][0];
-            any.a_int = i + 1;
-            add_menu(win, NO_GLYPH, &any, accelerator, 0,
-                     ATR_NONE, intrinsics[i], FALSE);
+        for (i = 1; (propname = propertynames[i]) != 0; ++i) {
+            if (i == HALLUC_RES) {
+                /* Grayswandir vs hallucination; ought to be redone to
+                   use u.uprops[HALLUC].blocked instead of being treated
+                   as a separate property; letting in be manually toggled
+                   even only in wizard mode would be asking for trouble... */
+                continue;
+            }
+            any.a_int = i;
+            add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, propname, FALSE);
         }
-        end_menu(win, "Which intrinsic?");
-        n = select_menu(win, PICK_ONE, &pick_list);
+        end_menu(win, "Which intrinsics?");
+        n = select_menu(win, PICK_ANY, &pick_list);
         destroy_nhwindow(win);
 
-        if (n >= 1) {
-            i = pick_list[0].item.a_int-1;
-            free((genericptr_t) pick_list);
-        } else {
-            return 0;
-        }
+        amt = 30; /* TODO: prompt for duration */
+        for (i = 0; i < n; ++i) {
+            p = pick_list[i].item.a_int;
+            oldtimeout = u.uprops[p].intrinsic & TIMEOUT;
+            newtimeout = oldtimeout + (long) amt;
+            switch (p) {
+            case SICK:
+            case SLIMED:
+            case STONED:
+                if (oldtimeout > 0L && newtimeout > oldtimeout)
+                    newtimeout = oldtimeout;
+                break;
+            }
 
-        if (!strcmp(intrinsics[i], "deafness")) {
-            You("go deaf.");
-            incr_itimeout(&HDeaf, 30);
-            context.botl = TRUE;
+            switch (p) {
+            case BLINDED:
+                make_blinded(newtimeout, TRUE);
+                break;
+            case CONFUSION:
+                make_confused(newtimeout, TRUE);
+                break;
+            case DEAF:
+                make_deaf(newtimeout, TRUE);
+                break;
+            case HALLUC:
+                make_hallucinated(newtimeout, TRUE, 0L);
+                break;
+            case SICK:
+                typ = !rn2(2) ? SICK_VOMITABLE : SICK_NONVOMITABLE;
+                make_sick(newtimeout, wizintrinsic, TRUE, typ);
+                break;
+            case SLIMED:
+                Sprintf(buf, fmt,
+                        !Slimed ? "" : " still", "turning into slime");
+                make_slimed(newtimeout, buf);
+                break;
+            case STONED:
+                Sprintf(buf, fmt,
+                        !Stoned ? "" : " still", "turning into stone");
+                make_stoned(newtimeout, buf, KILLED_BY, wizintrinsic);
+                break;
+            case STUNNED:
+                make_stunned(newtimeout, TRUE);
+                break;
+            case VOMITING:
+                Sprintf(buf, fmt, !Vomiting ? "" : " still", "vomiting");
+                make_vomiting(newtimeout, FALSE);
+                pline1(buf);
+                break;
+            default:
+                pline("Timeout for %s %s %d.", propertynames[p],
+                      oldtimeout ? "increased by" : "set to", amt);
+                incr_itimeout(&u.uprops[p].intrinsic, amt);
+                break;
+            }
+            context.botl = 1; /* probably not necessary... */
         }
+        if (n >= 1)
+            free((genericptr_t) pick_list);
     } else
         pline("Unavailable command '%s'.",
               visctrl((int) cmd_from_func(wiz_intrinsic)));
@@ -2814,7 +2867,7 @@ struct ext_func_tab extcmdlist[] = {
     { '\\', "known", "show what object types have been discovered",
             dodiscovered, IFBURIED | GENERALCMD },
     { '`', "knownclass", "show discovered types for one class of objects",
-            doclassdisco, IFBURIED|GENERALCMD },
+            doclassdisco, IFBURIED | GENERALCMD },
     { '\0', "levelchange", "change experience level",
             wiz_level_change, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
     { '\0', "lightsources", "show mobile light sources",
@@ -2835,11 +2888,11 @@ struct ext_func_tab extcmdlist[] = {
             dosacrifice, AUTOCOMPLETE },
     { 'o', "open", "open a door", doopen },
     { 'O', "options", "show option settings, possibly change them",
-            doset, IFBURIED|GENERALCMD },
+            doset, IFBURIED | GENERALCMD },
     { C('o'), "overview", "show a summary of the explored dungeon",
-            dooverview, IFBURIED|AUTOCOMPLETE },
+            dooverview, IFBURIED | AUTOCOMPLETE },
     { '\0', "panic", "test panic routine (fatal to game)",
-            wiz_panic, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
+            wiz_panic, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
     { 'p', "pay", "pay your shopping bill", dopay },
     { ',', "pickup", "pick up things at the current location", dopickup },
     { '\0', "polyself", "polymorph self",
@@ -2851,7 +2904,7 @@ struct ext_func_tab extcmdlist[] = {
     { M('p'), "pray", "pray to the gods for help",
             dopray, IFBURIED | AUTOCOMPLETE },
     { C('p'), "prevmsg", "view recent game messages",
-            doprev_message, IFBURIED|GENERALCMD },
+            doprev_message, IFBURIED | GENERALCMD },
     { 'P', "puton", "put on an accessory (ring, amulet, etc)", doputon },
     { 'q', "quaff", "quaff (drink) something", dodrink },
     { M('q'), "quit", "exit without saving current game",
@@ -2900,7 +2953,7 @@ struct ext_func_tab extcmdlist[] = {
     { '\0', "terrain", "show map without obstructions",
             doterrain, IFBURIED | AUTOCOMPLETE },
     { 't', "throw", "throw something", dothrow },
-    { '\0', "timeout", "look at timeout queue",
+    { '\0', "timeout", "look at timeout queue and hero's timed intrinsics",
             wiz_timeout_queue, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
     { M('T'), "tip", "empty a container", dotip, AUTOCOMPLETE },
     { '_', "travel", "travel to a specific location on the map", dotravel },
index f6f92283f60fc7915fd99fddc4abf26d23dcc087..66add4ec8d3d5c73a981e55aa6fabec7c5025663 100644 (file)
@@ -15,6 +15,45 @@ STATIC_DCL void FDECL(see_lamp_flicker, (struct obj *, const char *));
 STATIC_DCL void FDECL(lantern_message, (struct obj *));
 STATIC_DCL void FDECL(cleanup_burn, (ANY_P *, long));
 
+/* the order of these must match their numerical sequence in prop.h
+   because the property number is inferred from the array index;
+   used by wizard mode #timeout and #wizintrinsic */
+const char *const propertynames[] = {
+               "0: not used",
+/* Resistances */
+  /*  1..2  */ "fire resistance", "cold resistance",
+  /*  3..4  */ "sleep resistance", "disintegration resistance",
+  /*  5..6  */ "shock resistance", "poison resistance",
+  /*  7..8  */ "acid resistance", "stoning resistance",
+  /*  9..10 */ "drain resistance", "sickness resistance",
+  /* 11..12 */ "invulnerable", "magic resistance",
+/* Troubles */
+  /* 13..16 */ "stunned", "confused", "blinded", "deafness",
+  /* 17..20 */ "fatally sick", "petrifying", "strangling", "vomiting",
+  /* 21..22 */ "slippery fingers", "becoming slime",
+  /* 23..24 */ "hallucinating", "halluicination resistance",
+  /* 25..28 */ "fumbling", "wounded legs", "sleepy", "voracious hunger",
+/* Vision and senses */
+  /* 29..30 */ "see invisible", "telepathic",
+  /* 31..33 */ "warning", "warn:monster", "warn:undead",
+  /* 34..37 */ "searching", "clairvoyant", "infravision", "monster detection",
+/* Appearance and behavior */
+  /* 38..41 */ "adorned (+/-Cha)", "invisible", "displaced", "stealthy",
+  /* 42..43 */ "monster aggrevation", "conflict",
+/* Transportation */
+  /* 44..46 */ "jumping", "teleporting", "teleport control",
+  /* 47..49 */ "levitating", "flying", "water walking",
+  /* 50..52 */ "swimming", "magical breathing", "pass thru walls",
+/* Physical attributes */
+  /* 53..55 */ "slow digestion", "half spell damage", "half physical damage",
+  /* 56..58 */ "HP regeneration", "energy regeneration", "extra protection",
+  /* 59     */ "protection from shape changers",
+  /* 60..62 */ "polymorphing", "polymorph control", "unchanging",
+  /* 63..66 */ "fast", "reflecting", "free action", "fixed abilites",
+  /* 67     */ "life will be saved",
+               0 /* sentinel */
+};
+
 /* He is being petrified - dialogue by inmet!tower */
 static NEARDATA const char *const stoned_texts[] = {
     "You are slowing down.",            /* 5 */
@@ -57,6 +96,9 @@ stoned_dialogue()
         multi_reason = "getting stoned";
         nomovemsg = You_can_move_again; /* not unconscious */
         break;
+    case 2:
+        if ((HDeaf & TIMEOUT) > 0L && (HDeaf & TIMEOUT) < 5L)
+            set_itimeout(&HDeaf, 5L); /* avoid Hear_again at tail end */
     default:
         break;
     }
@@ -179,8 +221,9 @@ levitation_dialogue()
     if (((HLevitation & TIMEOUT) % 2L) && i > 0L && i <= SIZE(levi_texts)) {
         const char *s = levi_texts[SIZE(levi_texts) - i];
         if (index(s, '%')) {
-            boolean danger = is_pool_or_lava(u.ux, u.uy)
-                && !Is_waterlevel(&u.uz);
+            boolean danger = (is_pool_or_lava(u.ux, u.uy)
+                              && !Is_waterlevel(&u.uz));
+
             pline(s, danger ? "over" : "in",
                   danger ? surface(u.ux, u.uy) : "air");
         } else
@@ -225,6 +268,8 @@ slime_dialogue()
         if (multi > 0)
             nomul(0);
     }
+    if (i == 2L && (HDeaf & TIMEOUT) > 0L && (HDeaf & TIMEOUT) < 5L)
+        set_itimeout(&HDeaf, 5L); /* avoid Hear_again at tail end */
     exercise(A_DEX, FALSE);
 }
 
@@ -1473,7 +1518,7 @@ timer_element *base;
     char buf[BUFSZ];
 
     if (!base) {
-        putstr(win, 0, "<empty>");
+        putstr(win, 0, " <empty>");
     } else {
         putstr(win, 0, "timeout  id   kind   call");
         for (curr = base; curr; curr = curr->next) {
@@ -1492,31 +1537,14 @@ timer_element *base;
     }
 }
 
-static boolean print_prop_header = TRUE;
-void
-print_prop(win, text, prop)
-winid win;
-const char *text;
-long prop;
-{
-    char buf[BUFSZ];
-    if (prop & TIMEOUT) {
-        if (print_prop_header) {
-            putstr(win, 0, "");
-            putstr(win, 0, "Properties:");
-            putstr(win, 0, "");
-            print_prop_header = FALSE;
-        }
-        Sprintf(buf, " %10s: %ld", text, (prop & TIMEOUT));
-        putstr(win, 0, buf);
-    }
-}
-
 int
 wiz_timeout_queue()
 {
     winid win;
     char buf[BUFSZ];
+    const char *propname;
+    long intrinsic;
+    int i, count, longestlen, ln;
 
     win = create_nhwindow(NHW_MENU); /* corner text window */
     if (win == WIN_ERR)
@@ -1529,19 +1557,38 @@ wiz_timeout_queue()
     putstr(win, 0, "");
     print_queue(win, timer_base);
 
-    print_prop_header = TRUE;
-    print_prop(win, "Confused", Confusion);
-    print_prop(win, "Deaf", HDeaf);
-    print_prop(win, "Levitation", HLevitation);
-    print_prop(win, "Monster detection", HDetect_monsters);
-    print_prop(win, "Slimed", Slimed);
-    print_prop(win, "Slippery hands", Glib);
-    print_prop(win, "Stoned", Stoned);
-    print_prop(win, "Strangled", Strangled);
-    print_prop(win, "Stunned", Stunned);
-    print_prop(win, "Vomiting", Vomiting);
-    print_prop(win, "Wounded legs", HWounded_legs);
-
+    /* Timed properies:
+     * check every one; the majority can't obtain temporary timeouts in
+     * normal play but those can be forced via the #wizintrinsic command.
+     */
+    count = longestlen = 0;
+    for (i = 1; (propname = propertynames[i]) != 0; ++i) { /* [0] not used */
+        intrinsic = u.uprops[i].intrinsic;
+        if (intrinsic & TIMEOUT) {
+            ++count;
+            if ((ln = (int) strlen(propname)) > longestlen)
+                longestlen = ln;
+        }
+    }
+    putstr(win, 0, "");
+    if (!count) {
+        putstr(win, 0, "No timed properties.");
+    } else {
+        putstr(win, 0, "Timed properties:");
+        putstr(win, 0, "");
+        for (i = 1; (propname = propertynames[i]) != 0; ++i) {
+            intrinsic = u.uprops[i].intrinsic;
+            if (intrinsic & TIMEOUT) {
+                /* timeout value can be up to 16777215 (0x00ffffff) but
+                   width of 4 digits should result in values lining up
+                   almost all the time (if/when they don't, it won't
+                   look nice but the information will still be accurate) */
+                Sprintf(buf, " %*s %4ld", -longestlen, propname,
+                        (intrinsic & TIMEOUT));
+                putstr(win, 0, buf);
+            }
+        }
+    }
     display_nhwindow(win, FALSE);
     destroy_nhwindow(win);
 
@@ -1557,6 +1604,7 @@ timer_sanity_check()
     for (curr = timer_base; curr; curr = curr->next)
         if (curr->kind == TIMER_OBJECT) {
             struct obj *obj = curr->arg.a_obj;
+
             if (obj->timed == 0) {
                 pline("timer sanity: untimed obj %s, timer %ld",
                       fmt_ptr((genericptr_t) obj), curr->tid);