]> granicus.if.org Git - nethack/commitdiff
AC and obj->spe limits: +127/-128 -> +99/-99
authorPatR <rankin@nethack.org>
Mon, 21 Dec 2020 22:09:17 +0000 (14:09 -0800)
committerPatR <rankin@nethack.org>
Mon, 21 Dec 2020 22:09:17 +0000 (14:09 -0800)
Cap overall AC at -99 instead of -128.  Put the same limit of 99
on enchantment and charge count of individual objects.

^X now reports if/when AC has reached its limit since players
could see that reaching that limit and then enchanting worn items
will change the worn items but not the total.  (Same thing would
have happened with -128, just without any explanation and less
likely to accomplish.)

Won't affect normal play for any reasonable definition of normal.

doc/fixes37.0
include/obj.h
include/you.h
src/do_wear.c
src/insight.c
src/objnam.c
src/read.c
src/worn.c

index 23868980b7e067aec1d67ec08f0a9fa217ac97dc..9c4f6f44acd2755676863d9a41486b65bd4bb94b 100644 (file)
@@ -341,6 +341,9 @@ when protection from shape changers begins, force mimic out of concealment
        even if hero can't see its location; for locations that can be seen,
        don't make double-trouble Wizard concealed as another monster--or pet
        temporarily mimicking something while eating mimic corpse--fall asleep
+best possible armor class reduced from -127 to -99; worst from +127 to +99;
+       charged or enchanted individual items also capped at +/- 99 (affects
+       wizard mode wishing, negligible effect on normal play)
 
 
 Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
@@ -676,8 +679,8 @@ mild zombie apocalypse
 list lamps and lanterns in charging prompt
 let tourists read conical hats
 when "?i" (show key bindings) displays commands and their keys, also show
-       commands without any key (so useable via '#', or possibly menu, only;
-       the majority are debugging commands)
+       commands without any key (so ones useable via '#', or possibly menu,
+       only; the majority are debugging commands)
 assign default key binding for <del> or <delete> to execute #terrain
 assign M-X to #exploremode
 make #herecmdmenu and #therecmdmenu autocomplete
index 89d932251302e56bf26de71ed307a5f07a9f6de0..9205c81518615d61c489e12a3bb27d1cd9e81629 100644 (file)
@@ -40,16 +40,19 @@ struct obj {
     unsigned owt;
     long quan; /* number of items */
 
+#define SPE_LIM 99 /* abs(obj->spe) <= 99, cap for enchanted and charged
+                    * objects (and others; named fruit index excepted) */
     schar spe; /* quality of weapon, weptool, armor or ring (+ or -);
-                  number of charges for wand or charged tool ( >= -1 );
-                  number of candles attached to candelabrum;
-                  marks your eggs, tin variety and spinach tins;
-                  candy bar wrapper index;
-                  Schroedinger's Box (1) or royal coffers for a court (2);
-                  tells which fruit a fruit is;
-                  special for uball and amulet;
-                  scroll of mail (normal==0, bones or wishing==1, written==2);
-                  historic and gender for statues */
+                * number of charges for wand or charged tool ( >= -1 );
+                * number of candles attached to candelabrum;
+                * marks your eggs, tin variety and spinach tins;
+                * candy bar wrapper index;
+                * Schroedinger's Box (1) or royal coffers for a court (2);
+                * tells which fruit a fruit is;
+                * special for uball and amulet;
+                * scroll of mail (normal==0, bones or wishing==1, written==2);
+                * splash of venom (normal==0, wishing==1);
+                * historic and gender for statues */
 #define STATUE_HISTORIC 0x01
 #define STATUE_MALE 0x02
 #define STATUE_FEMALE 0x04
index c8d4c4fe69869163c08d15978a430dad321ce431..0d9686f88be328d186266a2fe86e1f87dbe5e825 100644 (file)
@@ -93,7 +93,9 @@ enum achivements {
     ACH_NOVL = 20, /* read at least one passage from a Discworld novel */
     ACH_SOKO = 21, /* entered Sokoban */
     ACH_BGRM = 22, /* entered Bigroom (not guaranteed to be in every dgn) */
-    /* 23..30 are negated if hero is female at the time new rank is gained */
+    /* role's rank titles, beyond first (#0 at level one, not an achievement);
+       23..30 are negated if hero is female at the time new rank is gained
+       so that disclosing them can use the gender which applied at the time */
     ACH_RNK1 = 23, ACH_RNK2 = 24, ACH_RNK3 = 25, ACH_RNK4 = 26,
     ACH_RNK5 = 27, ACH_RNK6 = 28, ACH_RNK7 = 29, ACH_RNK8 = 30,
     /* foo=31, 1 available potential achievement; #32 currently off-limits */
@@ -102,6 +104,8 @@ enum achivements {
     /*
      * Other potential achievements to track (this comment briefly resided
      * in encodeachieve(topten.c) and has been revised since moving here:
+     *  AC <= 0, AC <= -10, AC <= -20 (stop there; lower is better but
+     *    not something to encourage with achievements),
      *  got quest summons,
      *  entered quest branch,
      *  chatted with leader,
@@ -431,14 +435,18 @@ struct you {
 #define A_CURRENT  0
     aligntyp ualignbase[CONVERT]; /* for ualign conversion record */
     schar uluck, moreluck;        /* luck and luck bonus */
+    /* default u.uluck is 0 except on special days (full moon: +1, Fri 13: -1,
+       both: 0); equilibrium for luck timeout is changed to those values,
+       but Luck max and min stay at 10+3 and -10-3 even on those days */
 #define Luck (u.uluck + u.moreluck)
 #define LUCKADD    3  /* value of u.moreluck when carrying luck stone;
-                         + when blessed or uncursed, - when cursed */
-#define LUCKMAX   10  /* maximum value of u.ulUck */
+                       * +3 when blessed or uncursed, -3 when cursed */
+#define LUCKMAX   10  /* maximum value of u.uluck */
 #define LUCKMIN (-10) /* minimum value of u.uluck */
     schar uhitinc;
     schar udaminc;
     schar uac;
+#define AC_MAX    99  /* abs(u.uac) <= 99; likewise for monster AC */
     uchar uspellprot;        /* protection by SPE_PROTECTION */
     uchar usptime;           /* #moves until uspellprot-- */
     uchar uspmtime;          /* #moves between uspellprot-- */
index 77e7251ae2a8d2d16a7147b599121767ddede004..670fc6aa84a53740c4ad861c4a5ddc94fd18956d 100644 (file)
@@ -2213,19 +2213,26 @@ find_ac()
         uac -= u.ublessed;
     uac -= u.uspellprot;
 
-    /* [The magic binary numbers 127 and -128 should be replaced with the
-     * mystic decimal numbers 99 and -99 which require no explanation to
-     * the uninitiated and would cap the width of a status line value at
-     * one less character.]
-     */
-    if (uac < -128)
-        uac = -128; /* u.uac is an schar */
-    else if (uac > 127)
-        uac = 127; /* for completeness */
+    /* put a cap on armor class [3.7: was +127,-128, now reduced to +/- 99 */
+    if (abs(uac) > AC_MAX)
+        uac = sgn(uac) * AC_MAX;
 
     if (uac != u.uac) {
         u.uac = uac;
         g.context.botl = 1;
+#if 0
+        /* these could conceivably be achieved out of order (by being near
+           threshold and putting on +N dragon scale mail from bones, for
+           instance), but if that happens, that's the order it happened;
+           also, testing for these in the usual order would result in more
+           record_achievement() attempts and rejects for duplication */
+        if (u.uac <= -20)
+            record_achievement(ACH_AC_20);
+        else if (u.uac <= -10)
+            record_achievement(ACH_AC_10);
+        else if (u.uac <= 0)
+            record_achievement(ACH_AC_00);
+#endif
     }
 }
 
index b957919686cd16127c8c213b205c5271c387fb72..57cd5596e3dde198041c500c144cafa491d8f5b0 100644 (file)
@@ -590,7 +590,11 @@ int final;
         you_have(buf, "");
     }
 
+    find_ac(); /* enforces AC_MAX cap */
     Sprintf(buf, "%d", u.uac);
+    if (abs(u.uac) == AC_MAX)
+        Sprintf(eos(buf), ", the %s possible",
+                (u.uac < 0) ? "best" : "worst");
     enl_msg("Your armor class ", "is ", "was ", buf, "");
 
     /* gold; similar to doprgold(#seegold) but without shop billing info;
index 9efcd4fc69bb8bb7d9ec05178f9e18dc2387b92a..a99523ef5aea40dacc435b360fd2003207d7bf2a 100644 (file)
@@ -3431,8 +3431,11 @@ struct _readobjnam_data *d;
 {
     if (strlen(d->bp) > 1 && (d->p = rindex(d->bp, '(')) != 0) {
         boolean keeptrailingchars = TRUE;
+        int idx = 0;
 
-        d->p[(d->p > d->bp && d->p[-1] == ' ') ? -1 : 0] = '\0'; /*terminate bp */
+        if (d->p > d->bp && d->p[-1] == ' ')
+            idx = -1;
+        d->p[idx] = '\0'; /* terminate bp */
         ++d->p; /* advance past '(' */
         if (!strncmpi(d->p, "lit)", 4)) {
             d->islit = 1;
@@ -3478,8 +3481,9 @@ struct _readobjnam_data *d;
         d->spesgn = -1; /* cheaters get what they deserve */
         d->spe = abs(d->spe);
     }
-    if (d->spe > SCHAR_LIM)
-        d->spe = SCHAR_LIM;
+    /* cap on obj->spe is independent of (and less than) SCHAR_LIM */
+    if (d->spe > SPE_LIM)
+        d->spe = SPE_LIM; /* slime mold uses d.ftype, so not affected */
     if (d->rechrg < 0 || d->rechrg > 7)
         d->rechrg = 7; /* recharge_limit */
 }
@@ -4225,13 +4229,11 @@ struct obj *no_wish;
                               || d.typ == ROCK || is_missile(d.otmp)))))
         d.otmp->quan = (long) d.cnt;
 
-    if (d.oclass == VENOM_CLASS)
-        d.otmp->spe = 1;
-
     if (d.spesgn == 0) {
+        /* spe not specifed; retain the randomly assigned value */
         d.spe = d.otmp->spe;
     } else if (wizard) {
-        ; /* no alteration to spe */
+        ; /* no restrictions except SPE_LIM */
     } else if (d.oclass == ARMOR_CLASS || d.oclass == WEAPON_CLASS
                || is_weptool(d.otmp)
                || (d.oclass == RING_CLASS && objects[d.typ].oc_charged)) {
@@ -4240,7 +4242,8 @@ struct obj *no_wish;
         if (d.spe > 2 && Luck < 0)
             d.spesgn = -1;
     } else {
-        if (d.oclass == WAND_CLASS) {
+        /* crystal ball cancels like a wand, to (n:-1) */
+        if (d.oclass == WAND_CLASS || d.typ == CRYSTAL_BALL) {
             if (d.spe > 1 && d.spesgn == -1)
                 d.spe = 1;
         } else {
@@ -4281,12 +4284,16 @@ struct obj *no_wish;
         /* otmp->cobj already done in mksobj() */
         break;
 #ifdef MAIL_STRUCTURES
+    /* scroll of mail:  0: delivered in-game via external event (or randomly
+       for fake mail); 1: from bones or wishing; 2: written with marker */
     case SCR_MAIL:
-        /* 0: delivered in-game via external event (or randomly for fake mail);
-           1: from bones or wishing; 2: written with marker */
+        /*FALLTHRU*/
+#endif
+    /* splash of venom:  0: normal, and transitory; 1: wishing */
+    case ACID_VENOM:
+    case BLINDING_VENOM:
         d.otmp->spe = 1;
         break;
-#endif
     case WAN_WISHING:
         if (!wizard) {
             d.otmp->spe = (rn2(10) ? -1 : 0);
index 0c41973aa2047ed67b75b8ad7dc3d32fc7b0db88..6f8505dba5c0803f7b80038fd143f7b7f8ab4268 100644 (file)
@@ -17,6 +17,7 @@ static NEARDATA const char readable[] = { ALL_CLASSES, SCROLL_CLASS,
 static const char all_count[] = { ALLOW_COUNT, ALL_CLASSES, 0 };
 
 static boolean FDECL(learnscrolltyp, (SHORT_P));
+static void FDECL(cap_spe, (struct obj *));
 static char *FDECL(erode_obj_text, (struct obj *, char *));
 static void FDECL(stripspe, (struct obj *));
 static void FDECL(p_glow1, (struct obj *));
@@ -54,6 +55,17 @@ struct obj *sobj;
         (void) learnscrolltyp(sobj->otyp);
 }
 
+/* max spe is +99, min is -99 */
+static void
+cap_spe(obj)
+struct obj *obj;
+{
+    if (obj) {
+        if (abs(obj->spe) > SPE_LIM)
+            obj->spe = sgn(obj->spe) * SPE_LIM;
+    }
+}
+
 static char *
 erode_obj_text(otmp, buf)
 struct obj *otmp;
@@ -679,11 +691,10 @@ int curse_bless;
         case MAGIC_MARKER:
         case TINNING_KIT:
         case EXPENSIVE_CAMERA:
-            if (is_cursed)
+            if (is_cursed) {
                 stripspe(obj);
-            else if (rechrg
-                     && obj->otyp
-                            == MAGIC_MARKER) { /* previously recharged */
+            } else if (rechrg && obj->otyp == MAGIC_MARKER) {
+                /* previously recharged */
                 obj->recharged = 1; /* override increment done above */
                 if (obj->spe < 3)
                     Your("marker seems permanently dried out.");
@@ -709,8 +720,9 @@ int curse_bless;
                     obj->spe = 50;
                 else {
                     int chrg = (int) obj->spe;
-                    if ((chrg + n) > 127)
-                        obj->spe = 127;
+
+                    if (chrg + n > SPE_LIM)
+                        obj->spe = SPE_LIM;
                     else
                         obj->spe += n;
                 }
@@ -824,9 +836,12 @@ int curse_bless;
         } /* switch */
 
     } else {
   not_chargable:
+ not_chargable:
         You("have a feeling of loss.");
     }
+
+    /* prevent enchantment from getting out of range */
+    cap_spe(obj);
 }
 
 /*
@@ -1039,6 +1054,7 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */
             otmp->otyp += GRAY_DRAGON_SCALE_MAIL - GRAY_DRAGON_SCALES;
             if (sblessed) {
                 otmp->spe++;
+                cap_spe(otmp);
                 if (!otmp->blessed)
                     bless(otmp);
             } else if (otmp->cursed)
@@ -1050,7 +1066,7 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */
             break;
         }
         pline("%s %s%s%s%s for a %s.", Yname2(otmp),
-              s == 0 ? "violently " : "",
+              (s == 0) ? "violently " : "",
               otense(otmp, Blind ? "vibrate" : "glow"),
               (!Blind && !same_color) ? " " : "",
               (Blind || same_color)
@@ -1067,8 +1083,15 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */
         else if (!scursed && otmp->cursed)
             uncurse(otmp);
         if (s) {
+            int oldspe = otmp->spe;
+            /* despite being schar, it shouldn't be possible for spe to wrap
+               here because it has been capped at 99 and s is quite small;
+               however, might need to change s if it takes spe past 99 */
             otmp->spe += s;
-            adj_abon(otmp, s);
+            cap_spe(otmp); /* make sure that it doesn't exceed SPE_LIM */
+            s = otmp->spe - oldspe; /* cap_spe() might have throttled 's' */
+            if (s) /* skip if it got changed to 0 */
+                adj_abon(otmp, s); /* adjust armor bonus for Dex or Int+Wis */
             g.known = otmp->known;
             /* update shop bill to reflect new higher price */
             if (s > 0 && otmp->unpaid)
@@ -1331,6 +1354,8 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */
                                  : sblessed ? rnd(3 - uwep->spe / 3)
                                    : 1))
             sobj = 0; /* nothing enchanted: strange_feeling -> useup */
+        if (uwep)
+            cap_spe(uwep);
         break;
     case SCR_TAMING:
     case SPE_CHARM_MONSTER: {
index f5c4c3a1d8818b1f45d7d7309649848e23c261dd..82abcf26c1ea355b048bff32ef2409fba9c79807 100644 (file)
@@ -472,6 +472,9 @@ register struct monst *mon;
             /* since ARM_BONUS is positive, subtracting it increases AC */
         }
     }
+    /* same cap as for hero [find_ac(do_wear.c)] */
+    if (abs(base) > AC_MAX)
+        base = sgn(base) * AC_MAX;
     return base;
 }