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 \{\
.PS "Mines'\~End\~"
.\}
.fi
+.PL "<Rank>"
+Attained rank title <Rank>.
.PL Shop
Entered a shop.
.PL Temple
.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.
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]
\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}]
\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
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);
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));
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,
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)
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)
/* 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 {
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;
}
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
}
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", "");
/* 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 */
/* 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;
}
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;
/* 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;
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.
*/
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;
case ACH_SOKO_PRIZE:
achievement = "obtained_the_sokoban_prize";
break;
-
case ACH_ORCL:
achievement = "consulted_the_oracle";
break;
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;
}