]> granicus.if.org Git - nethack/commitdiff
track eight more achievements
authorPatR <rankin@nethack.org>
Mon, 4 May 2020 23:35:40 +0000 (16:35 -0700)
committerPatR <rankin@nethack.org>
Mon, 4 May 2020 23:35:40 +0000 (16:35 -0700)
Record reaching experience level 3, 6, 10, 14, 18, 22, 26, and 30,
the levels where the character gets a new rank title, and report
those as achievements at end of game.  These achievements persist
even if enough levels to lose a rank are lost, and if lost ranks
are regained the original achievement is the one that gets tracked
and disclosed.

doc/Guidebook.mn
doc/Guidebook.tex
include/extern.h
include/you.h
src/botl.c
src/exper.c
src/insight.c
src/topten.c

index bc0d4803117685bff4c88ab70a73872e1649b43c..b10e8ca251afcdc646f0a8bd69d3e7b56a83958f 100644 (file)
@@ -2876,7 +2876,7 @@ attained.
 They aren't directly related to \fIconduct\fP but are grouped with
 it because they fall into the same category of \(lqbragging rights\(rq
 and to limit the number of questions during disclosure.
-Listed roughly in order of difficulty and not necessarily in the order
+Listed here roughly in order of difficulty and not necessarily in the order
 in which you might accomplish them.
 .\" Vary the output between Guidebook.txt and Guidebook.{ps,pdf}
 .ie \n(fF \{\
@@ -2891,6 +2891,8 @@ in which you might accomplish them.
 .PS "Mines'\~End\~"
 .\}
 .fi
+.PL "<Rank>"
+Attained rank title <Rank>.
 .PL Shop
 Entered a shop.
 .PL Temple
@@ -2953,6 +2955,15 @@ Delivered the Amulet to its final destination.
 .sp
 .lp "Notes:  "
 .pg
+Achievements are recorded and subsequently reported in the order in which
+they happen during your current game rather than the order listed here.
+.pg
+There are nine \fI<Rank>\fP titles for each role, bestowed at experience
+levels 1, 3, 6, 10, 14, 18, 22, 26, and 30.
+The one for experience level 1 is not recorded as an achievement.
+Losing enough levels to revert to lower rank(s) does not discard the
+corresponding achievement(s).
+.pg
 There's no guaranteed \fINovel\fP so the achievement to read one might
 not always be attainable (except perhaps by \fIwishing\fP).
 Similarly, the \fIBig Room\fP level is not always present.
index da0f1f3c386349415e57e38aef0d1fa7a2996b70..937000f8f62c0a676e64c823929667688277698b 100644 (file)
@@ -3107,7 +3107,7 @@ attained.
 They aren't directly related to {\it conduct\/} but are grouped with
 it because they fall into the same category of ``bragging rights''
 and to limit the number of questions during disclosure.
-Listed roughly in order of difficulty and not necessarily in the order
+Listed here roughly in order of difficulty and not necessarily in the order
 in which you might accomplish them.
 
 % [length stuff copied from paranoid_confirmation]
@@ -3117,6 +3117,8 @@ in which you might accomplish them.
 \addtolength{\achwidth}{\labelsep}
 \blist{\leftmargin \achwidth \topsep 1mm \itemsep 0mm}
 %.PL Shop
+\item[{\tt <Rank>}]
+Attained rank title <Rank>.
 \item[{\tt Shop}]
 Entered a shop.
 \item[{\tt Temple}]
@@ -3169,6 +3171,17 @@ Delivered the Amulet to its final destination.
 \noindent
 Notes:
 
+%.pg
+Achievements are recorded and subsequently reported in the order in which
+they happen during your current game rather than the order listed here.
+
+%.pg
+There are nine {\it <Rank>\/} titles for each role, bestowed at experience
+levels 1, 3, 6, 10, 14, 18, 22, 26, and 30.
+The one for experience level 1 is not recorded as an achievement.
+Losing enough levels to revert to lower rank(s) does not discard the
+corresponding achievement(s).
+
 %.pg
 The ``special items'' hidden in {\it Mines'~End\/} and (\it Sokoban\/}
 are not unique but are considered to be prizes or rewards
index f41f9531a65bcfe49e1be74c767563211b4094c1..4b5bac1699c5d6d2722de64c9fe4cf2779887751 100644 (file)
@@ -170,6 +170,7 @@ E char *NDECL(do_statusline2);
 E void NDECL(bot);
 E void NDECL(timebot);
 E int FDECL(xlev_to_rank, (int));
+E int FDECL(rank_to_xlev, (int));
 E const char *FDECL(rank_of, (int, SHORT_P, BOOLEAN_P));
 E int FDECL(title_to_mon, (const char *, int *, int *));
 E void NDECL(max_rank_sz);
@@ -1007,9 +1008,10 @@ E void FDECL(youhiding, (BOOLEAN_P, int));
 E char *FDECL(trap_predicament, (char *, int, BOOLEAN_P));
 E int NDECL(doconduct);
 E void FDECL(show_conduct, (int));
-E void FDECL(record_achievement, (XCHAR_P));
-E boolean FDECL(remove_achievement, (XCHAR_P));
+E void FDECL(record_achievement, (SCHAR_P));
+E boolean FDECL(remove_achievement, (SCHAR_P));
 E int NDECL(count_achievements);
+E schar FDECL(achieve_rank, (int));
 E int NDECL(dovanquished);
 E int NDECL(doborn);
 E void FDECL(list_vanquished, (CHAR_P, BOOLEAN_P));
index d17d65ba2555538468bf0408015c5b1d624dfb6f..437500d9c4fd70906da6704de7c28d8774b9e430 100644 (file)
@@ -93,14 +93,15 @@ 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..31, 9 available potential achievements; #32 currently off-limits */
+    /* 23..30 are negated if hero is female at the time new rank is gained */
+    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 */
     N_ACH = 32     /* allocate room for 31 plus a slot for 0 terminator */
 };
     /*
      * Other potential achievements to track (this comment briefly resided
      * in encodeachieve(topten.c) and has been revised since moving here:
-     *  [reached experience level N for a few interesting values of N
-     *   or perhaps "became a <rank title>" for each new rank reached]
      *  got quest summons,
      *  entered quest branch,
      *  chatted with leader,
@@ -456,7 +457,7 @@ struct you {
     struct skills weapon_skills[P_NUM_SKILLS];
     boolean twoweap;         /* KMH -- Using two-weapon combat */
     short mcham;             /* vampire mndx if shapeshifted to bat/cloud */
-    xchar uachieved[N_ACH];  /* list of achievements in the order attained */
+    schar uachieved[N_ACH];  /* list of achievements in the order attained */
 }; /* end of `struct you' */
 
 #define Upolyd (u.umonnum != u.umonster)
index 06f8a35558158153b08649729f9d31bad2615a98..2dfaa11b998a7a30090baa79edd2e1687f697bbc 100644 (file)
@@ -270,18 +270,37 @@ int
 xlev_to_rank(xlev)
 int xlev;
 {
+    /*
+     *   1..2  => 0
+     *   3..5  => 1
+     *   6..9  => 2
+     *  10..13 => 3
+     *      ...
+     *  26..29 => 7
+     *    30   => 8
+     * Conversion is precise but only partially reversible.
+     */
     return (xlev <= 2) ? 0 : (xlev <= 30) ? ((xlev + 2) / 4) : 8;
 }
 
-#if 0 /* not currently needed */
 /* convert rank index (0..8) to experience level (1..30) */
 int
 rank_to_xlev(rank)
 int rank;
 {
-    return (rank <= 0) ? 1 : (rank <= 8) ? ((rank * 4) - 2) : 30;
+    /*
+     *  0 =>  1..2
+     *  1 =>  3..5
+     *  2 =>  6..9
+     *  3 => 10..13
+     *      ...
+     *  7 => 26..29
+     *  8 =>   30
+     * We return the low end of each range.
+     */
+    return (rank < 1) ? 1 : (rank < 2) ? 3
+           : (rank < 8) ? ((rank * 4) - 2) : 30;
 }
-#endif
 
 const char *
 rank_of(lev, monnum, female)
index 8c44645113dce948fc29dd7fb0a372efeb240fc8..f7e426db498aabf09fc96f9349058ecc390dcbc6 100644 (file)
@@ -299,9 +299,12 @@ boolean incr; /* true iff via incremental experience growth */
 
     /* increase level (unless already maxxed) */
     if (u.ulevel < MAXULEV) {
+        int newrank, oldrank = xlev_to_rank(u.ulevel);
+
         /* increase experience points to reflect new level */
         if (incr) {
             long tmp = newuexp(u.ulevel + 1);
+
             if (u.uexp >= tmp)
                 u.uexp = tmp - 1;
         } else {
@@ -314,6 +317,9 @@ boolean incr; /* true iff via incremental experience growth */
         if (u.ulevelmax < u.ulevel)
             u.ulevelmax = u.ulevel;
         adjabil(u.ulevel - 1, u.ulevel); /* give new intrinsics */
+        newrank = xlev_to_rank(u.ulevel);
+        if (newrank > oldrank)
+            record_achievement(achieve_rank(newrank));
     }
     g.context.botl = TRUE;
 }
index a44e04bc63b8186003f1cf1b0e5c6c6d3af9e1bc..6e300241b6f102005923cbc26d01ca6ccfe963ab 100644 (file)
@@ -1882,8 +1882,8 @@ static void
 show_achievements(final)
 int final; /* used "behind the curtain" by enl_foo() macros */
 {
-    int i, achidx, acnt;
-    char title[BUFSZ];
+    int i, achidx, absidx, acnt;
+    char title[QBUFSZ], buf[QBUFSZ];
     winid awin = WIN_ERR;
 
     /* unfortunately we can't show the achievements (at least not all of
@@ -1922,8 +1922,9 @@ int final; /* used "behind the curtain" by enl_foo() macros */
     }
     for (i = 0; i < acnt; ++i) {
         achidx = u.uachieved[i];
+        absidx = abs(achidx);
 
-        switch (achidx) {
+        switch (absidx) {
         case ACH_BLND:
             enl_msg(You_, "are exploring", "explored",
                     " without being able to see", "");
@@ -2013,10 +2014,19 @@ int final; /* used "behind the curtain" by enl_foo() macros */
             /* the ultimate achievement... */
             enlght_out(" You ascended!");
             break;
+
+        /* rank 0 is the starting condition, not an achievement; 8 is Xp 30 */
+        case ACH_RNK1: case ACH_RNK2: case ACH_RNK3: case ACH_RNK4:
+        case ACH_RNK5: case ACH_RNK6: case ACH_RNK7: case ACH_RNK8:
+            Sprintf(buf, "attained the rank of %s",
+                    rank_of(rank_to_xlev(absidx - (ACH_RNK1 - 1)),
+                            Role_switch, (achidx < 0) ? TRUE : FALSE));
+            you_have_X(buf);
+            break;
+
         default:
-            /* title[] has served its purpose, reuse it as a scratch buffer */
-            Sprintf(title, " [Unexpected achievement #%d.]", achidx);
-            enlght_out(title);
+            Sprintf(buf, " [Unexpected achievement #%d.]", achidx);
+            enlght_out(buf);
             break;
         } /* switch */
     } /* for */
@@ -2030,12 +2040,15 @@ int final; /* used "behind the curtain" by enl_foo() macros */
 /* record an achievement (add at end of list unless already present) */
 void
 record_achievement(achidx)
-xchar achidx;
+schar achidx;
 {
-    int i;
+    int i, absidx;
 
-    /* valid achievements range from 1 to N_ACH-1 */
-    if (achidx < 1 || achidx >= N_ACH) {
+    absidx = abs(achidx);
+    /* valid achievements range from 1 to N_ACH-1; however, ranks can be
+       stored as the complement (ie, negative) to track gender */
+    if ((achidx < 1 && (absidx < ACH_RNK1 || absidx > ACH_RNK8))
+        || achidx >= N_ACH) {
         impossible("Achievement #%d is out of range.", achidx);
         return;
     }
@@ -2046,7 +2059,7 @@ xchar achidx;
        an attempt to duplicate an achievement can happen if any of Bell,
        Candelabrum, Book, or Amulet is dropped then picked up again */
     for (i = 0; u.uachieved[i]; ++i)
-        if (u.uachieved[i] == achidx)
+        if (abs(u.uachieved[i]) == abs(achidx))
             return; /* already recorded, don't duplicate it */
     u.uachieved[i] = achidx;
     return;
@@ -2055,12 +2068,12 @@ xchar achidx;
 /* discard a recorded achievement; return True if removed, False otherwise */
 boolean
 remove_achievement(achidx)
-xchar achidx;
+schar achidx;
 {
     int i;
 
     for (i = 0; u.uachieved[i]; ++i)
-        if (u.uachieved[i] == achidx)
+        if (abs(u.uachieved[i]) == abs(achidx))
             break; /* stop when found */
     if (!u.uachieved[i]) /* not found */
         return FALSE;
@@ -2082,6 +2095,19 @@ count_achievements()
     return acnt;
 }
 
+/* convert a rank index to an achievement number; encode it when female
+   in order to subsequently report gender-specific ranks accurately */
+schar
+achieve_rank(rank)
+int rank; /* 1..8 */
+{
+    schar achidx = (schar) ((rank - 1) + ACH_RNK1);
+
+    if (flags.female)
+        achidx = -achidx;
+    return achidx;
+}
+
 /*
  *      Vanquished monsters.
  */
index 2e1cc73616400f22ece196e8a5346e3f62f7bdf3..c86f38ab8c1e661d3d4219e850e2d31501de674c 100644 (file)
@@ -474,13 +474,16 @@ boolean condition;
 static char *
 encode_extended_achievements()
 {
-    static char buf[N_ACH*40];
+    static char buf[N_ACH * 40];
+    char rnkbuf[40];
     const char *achievement = NULL;
-    int i;
+    int i, achidx, absidx;
 
     buf[0] = '\0';
     for (i = 0; u.uachieved[i]; i++) {
-        switch (u.uachieved[i]) {
+        achidx = u.uachieved[i];
+        absidx = abs(achidx);
+        switch (absidx) {
         case ACH_UWIN:
             achievement = "ascended";
             break;
@@ -517,7 +520,6 @@ encode_extended_achievements()
         case ACH_SOKO_PRIZE:
             achievement = "obtained_the_sokoban_prize";
             break;
-
         case ACH_ORCL:
             achievement = "consulted_the_oracle";
             break;
@@ -542,6 +544,15 @@ encode_extended_achievements()
         case ACH_BGRM:
             achievement = "entered_bigroom";
             break;
+        /* rank 0 is the starting condition, not an achievement; 8 is Xp 30 */
+        case ACH_RNK1: case ACH_RNK2: case ACH_RNK3: case ACH_RNK4:
+        case ACH_RNK5: case ACH_RNK6: case ACH_RNK7: case ACH_RNK8:
+            Sprintf(rnkbuf, "attained_the_rank_of_%s",
+                    rank_of(rank_to_xlev(absidx - (ACH_RNK1 - 1)),
+                            Role_switch, (achidx < 0) ? TRUE : FALSE));
+            strNsubst(rnkbuf, " ", "_", 0); /* replace every ' ' with '_' */
+            achievement = lcase(rnkbuf);
+            break;
         default:
             continue;
         }