populate insight.c

Move enlightenment and conduct from cmd.c to insight.c.  Also move
vanquished monsters plus genocided and/or extinct monsters from end.c
to there.  And move the one-line stethoscope/probing feedback for
self and for monsters from priest.c to there.

Achievement feedback has been overhauled a bit.  When no achievements
have been recorded, the header for them (after conducts) won't be
shown, and when at least one has been recorded, make the prompt for
asking whether to disclose conduct be about disclosing conduct and
achievements.  Also, describe achievements in the Guidebook.

I ran out of gas before updating Guidebook.tex; it will catch up to
Guidebook.mn eventually.

Some of the MS-DOS Makefiles haven't been updated yet so linking
without insight.{o,obj} will break there.
This commit is contained in:
PatR
2020-02-06 17:42:15 -08:00
parent 690e072a49
commit b4d8475b95
6 changed files with 2559 additions and 2420 deletions

1807
src/cmd.c

File diff suppressed because it is too large Load Diff

396
src/end.c
View File

@@ -32,17 +32,11 @@ static void NDECL(done_object_cleanup);
static void FDECL(artifact_score, (struct obj *, BOOLEAN_P, winid));
static void FDECL(really_done, (int)) NORETURN;
static void FDECL(savelife, (int));
static int FDECL(CFDECLSPEC vanqsort_cmp, (const genericptr,
const genericptr));
static int NDECL(set_vanq_order);
static void FDECL(list_vanquished, (CHAR_P, BOOLEAN_P));
static void FDECL(list_genocided, (CHAR_P, BOOLEAN_P));
static boolean FDECL(should_query_disclose_option, (int, char *));
#ifdef DUMPLOG
static void NDECL(dump_plines);
#endif
static void FDECL(dump_everything, (int, time_t));
static int NDECL(num_extinct);
#if defined(__BEOS__) || defined(MICRO) || defined(OS2) || defined(WIN32)
extern void FDECL(nethack_exit, (int));
@@ -817,10 +811,16 @@ boolean taken;
}
if (!done_stopprint) {
ask = should_query_disclose_option('c', &defquery);
c = ask ? yn_function("Do you want to see your conduct?", ynqchars,
defquery)
: defquery;
if (should_query_disclose_option('c', &defquery)) {
int acnt = count_uachieve();
Sprintf(qbuf, "Do you want to see your conduct%s%s?",
(acnt > 0) ? " and achievement" : "",
(acnt > 1) ? "s" : "");
c = yn_function(qbuf, ynqchars, defquery);
} else {
c = defquery;
}
if (c == 'y')
show_conduct((how >= PANICKED) ? 1 : 2);
if (c == 'q')
@@ -1711,382 +1711,6 @@ int status;
nethack_exit(status);
}
static const char *vanqorders[NUM_VANQ_ORDER_MODES] = {
"traditional: by monster level, by internal monster index",
"by monster toughness, by internal monster index",
"alphabetically, first unique monsters, then others",
"alphabetically, unique monsters and others intermixed",
"by monster class, high to low level within class",
"by monster class, low to high level within class",
"by count, high to low, by internal index within tied count",
"by count, low to high, by internal index within tied count",
};
static int CFDECLSPEC
vanqsort_cmp(vptr1, vptr2)
const genericptr vptr1;
const genericptr vptr2;
{
int indx1 = *(short *) vptr1, indx2 = *(short *) vptr2,
mlev1, mlev2, mstr1, mstr2, uniq1, uniq2, died1, died2, res;
const char *name1, *name2, *punct;
schar mcls1, mcls2;
switch (g.vanq_sortmode) {
default:
case VANQ_MLVL_MNDX:
/* sort by monster level */
mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
res = mlev2 - mlev1; /* mlevel high to low */
break;
case VANQ_MSTR_MNDX:
/* sort by monster toughness */
mstr1 = mons[indx1].difficulty, mstr2 = mons[indx2].difficulty;
res = mstr2 - mstr1; /* monstr high to low */
break;
case VANQ_ALPHA_SEP:
uniq1 = ((mons[indx1].geno & G_UNIQ) && indx1 != PM_HIGH_PRIEST);
uniq2 = ((mons[indx2].geno & G_UNIQ) && indx2 != PM_HIGH_PRIEST);
if (uniq1 ^ uniq2) { /* one or other uniq, but not both */
res = uniq2 - uniq1;
break;
} /* else both unique or neither unique */
/*FALLTHRU*/
case VANQ_ALPHA_MIX:
name1 = mons[indx1].mname, name2 = mons[indx2].mname;
res = strcmpi(name1, name2); /* caseblind alhpa, low to high */
break;
case VANQ_MCLS_HTOL:
case VANQ_MCLS_LTOH:
/* mons[].mlet is a small integer, 1..N, of type plain char;
if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
an inappropriate result when mlet2 is greater than mlet1,
so force our copies (mcls1, mcls2) to be signed */
mcls1 = (schar) mons[indx1].mlet, mcls2 = (schar) mons[indx2].mlet;
/* S_ANT through S_ZRUTY correspond to lowercase monster classes,
S_ANGEL through S_ZOMBIE correspond to uppercase, and various
punctuation characters are used for classes beyond those */
if (mcls1 > S_ZOMBIE && mcls2 > S_ZOMBIE) {
/* force a specific order to the punctuation classes that's
different from the internal order;
internal order is ok if neither or just one is punctuation
since letters have lower values so come out before punct */
static const char punctclasses[] = {
S_LIZARD, S_EEL, S_GOLEM, S_GHOST, S_DEMON, S_HUMAN, '\0'
};
if ((punct = index(punctclasses, mcls1)) != 0)
mcls1 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
if ((punct = index(punctclasses, mcls2)) != 0)
mcls2 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
}
res = mcls1 - mcls2; /* class */
if (res == 0) {
mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
res = mlev1 - mlev2; /* mlevel low to high */
if (g.vanq_sortmode == VANQ_MCLS_HTOL)
res = -res; /* mlevel high to low */
}
break;
case VANQ_COUNT_H_L:
case VANQ_COUNT_L_H:
died1 = g.mvitals[indx1].died, died2 = g.mvitals[indx2].died;
res = died2 - died1; /* dead count high to low */
if (g.vanq_sortmode == VANQ_COUNT_L_H)
res = -res; /* dead count low to high */
break;
}
/* tiebreaker: internal mons[] index */
if (res == 0)
res = indx1 - indx2; /* mndx low to high */
return res;
}
/* returns -1 if cancelled via ESC */
static int
set_vanq_order()
{
winid tmpwin;
menu_item *selected;
anything any;
int i, n, choice;
tmpwin = create_nhwindow(NHW_MENU);
start_menu(tmpwin);
any = cg.zeroany; /* zero out all bits */
for (i = 0; i < SIZE(vanqorders); i++) {
if (i == VANQ_ALPHA_MIX || i == VANQ_MCLS_HTOL) /* skip these */
continue;
any.a_int = i + 1;
add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, vanqorders[i],
(i == g.vanq_sortmode)
? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
}
end_menu(tmpwin, "Sort order for vanquished monster counts");
n = select_menu(tmpwin, PICK_ONE, &selected);
destroy_nhwindow(tmpwin);
if (n > 0) {
choice = selected[0].item.a_int - 1;
/* skip preselected entry if we have more than one item chosen */
if (n > 1 && choice == g.vanq_sortmode)
choice = selected[1].item.a_int - 1;
free((genericptr_t) selected);
g.vanq_sortmode = choice;
}
return (n < 0) ? -1 : g.vanq_sortmode;
}
/* #vanquished command */
int
dovanquished()
{
list_vanquished('a', FALSE);
return 0;
}
/* high priests aren't unique but are flagged as such to simplify something */
#define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
&& mndx != PM_HIGH_PRIEST)
static void
list_vanquished(defquery, ask)
char defquery;
boolean ask;
{
register int i;
int pfx, nkilled;
unsigned ntypes, ni;
long total_killed = 0L;
winid klwin;
short mindx[NUMMONS];
char c, buf[BUFSZ], buftoo[BUFSZ];
boolean dumping; /* for DUMPLOG; doesn't need to be conditional */
dumping = (defquery == 'd');
if (dumping)
defquery = 'y';
/* get totals first */
ntypes = 0;
for (i = LOW_PM; i < NUMMONS; i++) {
if ((nkilled = (int) g.mvitals[i].died) == 0)
continue;
mindx[ntypes++] = i;
total_killed += (long) nkilled;
}
/* vanquished creatures list;
* includes all dead monsters, not just those killed by the player
*/
if (ntypes != 0) {
char mlet, prev_mlet = 0; /* used as small integer, not character */
boolean class_header, uniq_header, was_uniq = FALSE;
c = ask ? yn_function(
"Do you want an account of creatures vanquished?",
ynaqchars, defquery)
: defquery;
if (c == 'q')
done_stopprint++;
if (c == 'y' || c == 'a') {
if (c == 'a') { /* ask player to choose sort order */
/* choose value for vanq_sortmode via menu; ESC cancels list
of vanquished monsters but does not set 'done_stopprint' */
if (set_vanq_order() < 0)
return;
}
uniq_header = (g.vanq_sortmode == VANQ_ALPHA_SEP);
class_header = (g.vanq_sortmode == VANQ_MCLS_LTOH
|| g.vanq_sortmode == VANQ_MCLS_HTOL);
klwin = create_nhwindow(NHW_MENU);
putstr(klwin, 0, "Vanquished creatures:");
if (!dumping)
putstr(klwin, 0, "");
qsort((genericptr_t) mindx, ntypes, sizeof *mindx, vanqsort_cmp);
for (ni = 0; ni < ntypes; ni++) {
i = mindx[ni];
nkilled = g.mvitals[i].died;
mlet = mons[i].mlet;
if (class_header && mlet != prev_mlet) {
Strcpy(buf, def_monsyms[(int) mlet].explain);
putstr(klwin, ask ? 0 : iflags.menu_headings,
upstart(buf));
prev_mlet = mlet;
}
if (UniqCritterIndx(i)) {
Sprintf(buf, "%s%s",
!type_is_pname(&mons[i]) ? "the " : "",
mons[i].mname);
if (nkilled > 1) {
switch (nkilled) {
case 2:
Sprintf(eos(buf), " (twice)");
break;
case 3:
Sprintf(eos(buf), " (thrice)");
break;
default:
Sprintf(eos(buf), " (%d times)", nkilled);
break;
}
}
was_uniq = TRUE;
} else {
if (uniq_header && was_uniq) {
putstr(klwin, 0, "");
was_uniq = FALSE;
}
/* trolls or undead might have come back,
but we don't keep track of that */
if (nkilled == 1)
Strcpy(buf, an(mons[i].mname));
else
Sprintf(buf, "%3d %s", nkilled,
makeplural(mons[i].mname));
}
/* number of leading spaces to match 3 digit prefix */
pfx = !strncmpi(buf, "the ", 3) ? 0
: !strncmpi(buf, "an ", 3) ? 1
: !strncmpi(buf, "a ", 2) ? 2
: !digit(buf[2]) ? 4 : 0;
if (class_header)
++pfx;
Sprintf(buftoo, "%*s%s", pfx, "", buf);
putstr(klwin, 0, buftoo);
}
/*
* if (Hallucination)
* putstr(klwin, 0, "and a partridge in a pear tree");
*/
if (ntypes > 1) {
if (!dumping)
putstr(klwin, 0, "");
Sprintf(buf, "%ld creatures vanquished.", total_killed);
putstr(klwin, 0, buf);
}
display_nhwindow(klwin, TRUE);
destroy_nhwindow(klwin);
}
} else if (defquery == 'a') {
/* #dovanquished rather than final disclosure, so pline() is ok */
pline("No creatures have been vanquished.");
#ifdef DUMPLOG
} else if (dumping) {
putstr(0, 0, "No creatures were vanquished."); /* not pline() */
#endif
}
}
/* number of monster species which have been genocided */
int
num_genocides()
{
int i, n = 0;
for (i = LOW_PM; i < NUMMONS; ++i) {
if (g.mvitals[i].mvflags & G_GENOD) {
++n;
if (UniqCritterIndx(i))
impossible("unique creature '%d: %s' genocided?",
i, mons[i].mname);
}
}
return n;
}
static int
num_extinct()
{
int i, n = 0;
for (i = LOW_PM; i < NUMMONS; ++i) {
if (UniqCritterIndx(i))
continue;
if ((g.mvitals[i].mvflags & G_GONE) == G_EXTINCT)
++n;
}
return n;
}
static void
list_genocided(defquery, ask)
char defquery;
boolean ask;
{
register int i;
int ngenocided, nextinct;
char c;
winid klwin;
char buf[BUFSZ];
boolean dumping; /* for DUMPLOG; doesn't need to be conditional */
dumping = (defquery == 'd');
if (dumping)
defquery = 'y';
ngenocided = num_genocides();
nextinct = num_extinct();
/* genocided or extinct species list */
if (ngenocided != 0 || nextinct != 0) {
Sprintf(buf, "Do you want a list of %sspecies%s%s?",
(nextinct && !ngenocided) ? "extinct " : "",
(ngenocided) ? " genocided" : "",
(nextinct && ngenocided) ? " and extinct" : "");
c = ask ? yn_function(buf, ynqchars, defquery) : defquery;
if (c == 'q')
done_stopprint++;
if (c == 'y') {
klwin = create_nhwindow(NHW_MENU);
Sprintf(buf, "%s%s species:",
(ngenocided) ? "Genocided" : "Extinct",
(nextinct && ngenocided) ? " or extinct" : "");
putstr(klwin, 0, buf);
if (!dumping)
putstr(klwin, 0, "");
for (i = LOW_PM; i < NUMMONS; i++) {
/* uniques can't be genocided but can become extinct;
however, they're never reported as extinct, so skip them */
if (UniqCritterIndx(i))
continue;
if (g.mvitals[i].mvflags & G_GONE) {
Sprintf(buf, " %s", makeplural(mons[i].mname));
/*
* "Extinct" is unfortunate terminology. A species
* is marked extinct when its birth limit is reached,
* but there might be members of the species still
* alive, contradicting the meaning of the word.
*/
if ((g.mvitals[i].mvflags & G_GONE) == G_EXTINCT)
Strcat(buf, " (extinct)");
putstr(klwin, 0, buf);
}
}
if (!dumping)
putstr(klwin, 0, "");
if (ngenocided > 0) {
Sprintf(buf, "%d species genocided.", ngenocided);
putstr(klwin, 0, buf);
}
if (nextinct > 0) {
Sprintf(buf, "%d species extinct.", nextinct);
putstr(klwin, 0, buf);
}
display_nhwindow(klwin, TRUE);
destroy_nhwindow(klwin);
}
#ifdef DUMPLOG
} else if (dumping) {
putstr(0, 0, "No species were genocided or became extinct.");
#endif
}
}
/* set a delayed killer, ensure non-delayed killer is cleared out */
void
delayed_killer(id, format, killername)

File diff suppressed because it is too large Load Diff

View File

@@ -861,242 +861,4 @@ boolean ghostly;
}
}
/*
* align_str(), piousness(), mstatusline() and ustatusline() used to be
* in pline.c, presumeably because the latter two generate one line of
* output. The USE_OLDARGS config gets warnings from 2016ish-vintage
* gcc (for -Wint-to-pointer-cast, activated by -Wall or -W) when they
* follow pline() itself. Fixing up the variadic calls like is done for
* lev_comp would be needlessly messy there.
*
* They don't belong here. If/when enlightenment ever gets split off
* from cmd.c (which definitely doesn't belong there), they should go
* with it.
*/
const char *
align_str(alignment)
aligntyp alignment;
{
switch ((int) alignment) {
case A_CHAOTIC:
return "chaotic";
case A_NEUTRAL:
return "neutral";
case A_LAWFUL:
return "lawful";
case A_NONE:
return "unaligned";
}
return "unknown";
}
/* used for self-probing */
char *
piousness(showneg, suffix)
boolean showneg;
const char *suffix;
{
static char buf[32]; /* bigger than "insufficiently neutral" */
const char *pio;
/* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */
if (u.ualign.record >= 20)
pio = "piously";
else if (u.ualign.record > 13)
pio = "devoutly";
else if (u.ualign.record > 8)
pio = "fervently";
else if (u.ualign.record > 3)
pio = "stridently";
else if (u.ualign.record == 3)
pio = "";
else if (u.ualign.record > 0)
pio = "haltingly";
else if (u.ualign.record == 0)
pio = "nominally";
else if (!showneg)
pio = "insufficiently";
else if (u.ualign.record >= -3)
pio = "strayed";
else if (u.ualign.record >= -8)
pio = "sinned";
else
pio = "transgressed";
Sprintf(buf, "%s", pio);
if (suffix && (!showneg || u.ualign.record >= 0)) {
if (u.ualign.record != 3)
Strcat(buf, " ");
Strcat(buf, suffix);
}
return buf;
}
/* stethoscope or probing applied to monster -- one-line feedback */
void
mstatusline(mtmp)
struct monst *mtmp;
{
aligntyp alignment = mon_aligntyp(mtmp);
char info[BUFSZ], monnambuf[BUFSZ];
info[0] = 0;
if (mtmp->mtame) {
Strcat(info, ", tame");
if (wizard) {
Sprintf(eos(info), " (%d", mtmp->mtame);
if (!mtmp->isminion)
Sprintf(eos(info), "; hungry %ld; apport %d",
EDOG(mtmp)->hungrytime, EDOG(mtmp)->apport);
Strcat(info, ")");
}
} else if (mtmp->mpeaceful)
Strcat(info, ", peaceful");
if (mtmp->data == &mons[PM_LONG_WORM]) {
int segndx, nsegs = count_wsegs(mtmp);
/* the worm code internals don't consider the head of be one of
the worm's segments, but we count it as such when presenting
worm feedback to the player */
if (!nsegs) {
Strcat(info, ", single segment");
} else {
++nsegs; /* include head in the segment count */
segndx = wseg_at(mtmp, g.bhitpos.x, g.bhitpos.y);
Sprintf(eos(info), ", %d%s of %d segments",
segndx, ordin(segndx), nsegs);
}
}
if (mtmp->cham >= LOW_PM && mtmp->data != &mons[mtmp->cham])
/* don't reveal the innate form (chameleon, vampire, &c),
just expose the fact that this current form isn't it */
Strcat(info, ", shapechanger");
/* pets eating mimic corpses mimic while eating, so this comes first */
if (mtmp->meating)
Strcat(info, ", eating");
/* a stethoscope exposes mimic before getting here so this
won't be relevant for it, but wand of probing doesn't */
if (mtmp->mundetected || mtmp->m_ap_type)
mhidden_description(mtmp, TRUE, eos(info));
if (mtmp->mcan)
Strcat(info, ", cancelled");
if (mtmp->mconf)
Strcat(info, ", confused");
if (mtmp->mblinded || !mtmp->mcansee)
Strcat(info, ", blind");
if (mtmp->mstun)
Strcat(info, ", stunned");
if (mtmp->msleeping)
Strcat(info, ", asleep");
#if 0 /* unfortunately mfrozen covers temporary sleep and being busy \
(donning armor, for instance) as well as paralysis */
else if (mtmp->mfrozen)
Strcat(info, ", paralyzed");
#else
else if (mtmp->mfrozen || !mtmp->mcanmove)
Strcat(info, ", can't move");
#endif
/* [arbitrary reason why it isn't moving] */
else if (mtmp->mstrategy & STRAT_WAITMASK)
Strcat(info, ", meditating");
if (mtmp->mflee)
Strcat(info, ", scared");
if (mtmp->mtrapped)
Strcat(info, ", trapped");
if (mtmp->mspeed)
Strcat(info, (mtmp->mspeed == MFAST) ? ", fast"
: (mtmp->mspeed == MSLOW) ? ", slow"
: ", [? speed]");
if (mtmp->minvis)
Strcat(info, ", invisible");
if (mtmp == u.ustuck)
Strcat(info, sticks(g.youmonst.data) ? ", held by you"
: !u.uswallow ? ", holding you"
: attacktype_fordmg(u.ustuck->data, AT_ENGL, AD_DGST)
? ", digesting you"
: is_animal(u.ustuck->data) ? ", swallowing you"
: ", engulfing you");
if (mtmp == u.usteed)
Strcat(info, ", carrying you");
/* avoid "Status of the invisible newt ..., invisible" */
/* and unlike a normal mon_nam, use "saddled" even if it has a name */
Strcpy(monnambuf, x_monnam(mtmp, ARTICLE_THE, (char *) 0,
(SUPPRESS_IT | SUPPRESS_INVISIBLE), FALSE));
pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", monnambuf,
align_str(alignment), mtmp->m_lev, mtmp->mhp, mtmp->mhpmax,
find_mac(mtmp), info);
}
/* stethoscope or probing applied to hero -- one-line feedback */
void
ustatusline()
{
char info[BUFSZ];
info[0] = '\0';
if (Sick) {
Strcat(info, ", dying from");
if (u.usick_type & SICK_VOMITABLE)
Strcat(info, " food poisoning");
if (u.usick_type & SICK_NONVOMITABLE) {
if (u.usick_type & SICK_VOMITABLE)
Strcat(info, " and");
Strcat(info, " illness");
}
}
if (Stoned)
Strcat(info, ", solidifying");
if (Slimed)
Strcat(info, ", becoming slimy");
if (Strangled)
Strcat(info, ", being strangled");
if (Vomiting)
Strcat(info, ", nauseated"); /* !"nauseous" */
if (Confusion)
Strcat(info, ", confused");
if (Blind) {
Strcat(info, ", blind");
if (u.ucreamed) {
if ((long) u.ucreamed < Blinded || Blindfolded
|| !haseyes(g.youmonst.data))
Strcat(info, ", cover");
Strcat(info, "ed by sticky goop");
} /* note: "goop" == "glop"; variation is intentional */
}
if (Stunned)
Strcat(info, ", stunned");
if (!u.usteed && Wounded_legs) {
const char *what = body_part(LEG);
if ((Wounded_legs & BOTH_SIDES) == BOTH_SIDES)
what = makeplural(what);
Sprintf(eos(info), ", injured %s", what);
}
if (Glib)
Sprintf(eos(info), ", slippery %s", makeplural(body_part(HAND)));
if (u.utrap)
Strcat(info, ", trapped");
if (Fast)
Strcat(info, Very_fast ? ", very fast" : ", fast");
if (u.uundetected)
Strcat(info, ", concealed");
if (Invis)
Strcat(info, ", invisible");
if (u.ustuck) {
if (sticks(g.youmonst.data))
Strcat(info, ", holding ");
else
Strcat(info, ", held by ");
Strcat(info, mon_nam(u.ustuck));
}
pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", g.plname,
piousness(FALSE, align_str(u.ualign.type)),
Upolyd ? mons[u.umonnum].mlevel : u.ulevel, Upolyd ? u.mh : u.uhp,
Upolyd ? u.mhmax : u.uhpmax, u.uac, info);
}
/*priest.c*/