track eight more achievements

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.
This commit is contained in:
PatR
2020-05-04 16:35:40 -07:00
parent 16562b2892
commit 116642ce1e
8 changed files with 117 additions and 28 deletions

View 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.

View 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

View 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));

View 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)

View 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)

View 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;
}

View 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.
*/

View 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;
}