diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index e189cd8a9..fd906d9c8 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1084,6 +1084,9 @@ Close a door. Default key is \(oqc\(cq. .lp "#conduct " List voluntary challenges you have maintained. +.\" [only in wizard mode:] +.\" Also lists various achievements representing progress toward ultimate +.\" ascension if any have been attained. Autocompletes. Default key is \(oqM-C\(cq. .lp "" @@ -2726,6 +2729,71 @@ wish for any items is a challenge, as is a game without wishing for an artifact (even if the artifact immediately disappears). When the game offers you an opportunity to make a wish for an item, you may choose \(lqnothing\(rq if you want to decline. +.hn 2 +Achievements +.pg +End of game disclosure will also display various achievements +representing progress toward ultimate ascension, if any have been +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. +.pg +The first two are also conducts, the rest are listed roughly in order +of difficulty and not necessarily in the order in which you might +accomplish them. +.lp "Blind " +Blind from birth. +.lp "Nudist " +Never wore any armor. +.lp "Mines' End" +Explored to the bottom of the Gnomish Mines and found a special item there. +.lp "Sokoban " +Explored to the top of Sokoban and found a special item there. +.lp "Medusa " +Defeated Medusa. +.lp "Bell " +Acquired the Bell of Opening. +.lp "Gehennom" +Entered Gehennom proper (beyond the Valley). +.lp "Candle " +Acquired the Candelabrum of Invocation. +.lp "Book " +Acquired the Book of the Dead. +.lp Invocation +Gained access to the bottommost level of Gehennom. +.lp "Amulet " +Acquired the fabled Amulet of Yendor. +.lp "Endgame " +Reached the Elemental Planes. +.lp "Astral " +Reached the Astral Plane level. +.lp "Ascended" +Delivered the Amulet to its final destination. +.lp "" +.lp "Notes: " +.pg +\fIBlind\fP and \fINudist\fP are also conducts, and they can only be +enabled by setting the correspondingly named option in NETHACKOPTIONS +or run-time configuration file prior to game start. +In the case of \fIBlind\fP, the option also enforces the conduct. +They aren't really significant accomplishments unless/until you make +substantial progress into the dungeon but they are treated as +Achievements as long as the conduct is enabled and hasn't been broken. +.pg +The \(lqspecial items\(rq hidden in \fIMines\~End\fP and \fISokoban\fP +are not unique but are considered to be prizes or rewards +for exploring those levels since doing so is not necessary to complete +the game. +Finding other instances of the same objects doesn't record the +corresponding achievement. +.pg +The \fIMedusa\fP achievement is recorded if she dies for any reason, +even if you are not directly responsible. +.pg +The \fIValley of the Dead\fP is part of \fIGehennom\fP but the +\fIGehennom\fP achievement doesn't get recorded until you make it past +that level. . .hn 1 Options @@ -3106,7 +3174,7 @@ The possibilities are: .CC a "disclose your attributes;" .CC v "summarize monsters that have been vanquished;" .CC g "list monster species that have been genocided;" -.CC c "display your conduct;" +.CC c "display your conduct; also achievements, if any;" .CC o "display dungeon overview." .ei .ed diff --git a/include/extern.h b/include/extern.h index f08f922df..c19f8df64 100644 --- a/include/extern.h +++ b/include/extern.h @@ -198,7 +198,6 @@ E boolean NDECL(status_hilite_menu); E char NDECL(randomkey); E void FDECL(random_response, (char *, int)); E int NDECL(rnd_extcmd_idx); -E int NDECL(doconduct); E int NDECL(domonability); E char FDECL(cmd_from_func, (int NDECL((*)))); E boolean FDECL(redraw_cmd, (CHAR_P)); @@ -231,10 +230,6 @@ E void FDECL(rhack, (char *)); E int NDECL(doextlist); E int NDECL(extcmd_via_menu); E int NDECL(enter_explore_mode); -E char *FDECL(trap_predicament, (char *, int, BOOLEAN_P)); -E void FDECL(enlightenment, (int, int)); -E void FDECL(youhiding, (BOOLEAN_P, int)); -E void FDECL(show_conduct, (int)); E boolean FDECL(bind_key, (UCHAR_P, const char *)); E void NDECL(dokeylist); E int FDECL(xytod, (SCHAR_P, SCHAR_P)); @@ -724,8 +719,6 @@ E void FDECL(done, (int)); E void FDECL(container_contents, (struct obj *, BOOLEAN_P, BOOLEAN_P, BOOLEAN_P)); E void FDECL(nh_terminate, (int)) NORETURN; -E int NDECL(dovanquished); -E int NDECL(num_genocides); E void FDECL(delayed_killer, (int, int, const char *)); E struct kinfo *FDECL(find_delayed_killer, (int)); E void FDECL(dealloc_killer, (struct kinfo *)); @@ -986,6 +979,24 @@ E void FDECL(strbuf_empty, (strbuf_t *)); E void FDECL(strbuf_nl_to_crlf, (strbuf_t *)); E char *FDECL(nonconst, (const char *, char *)); +/* ### insight.c ### */ + +E int NDECL(doattributes); +E void FDECL(enlightenment, (int, int)); +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 int NDECL(count_uachieve); +E int NDECL(dovanquished); +E void FDECL(list_vanquished, (CHAR_P, BOOLEAN_P)); +E int NDECL(num_genocides); +E void FDECL(list_genocided, (CHAR_P, BOOLEAN_P)); +E const char *FDECL(align_str, (ALIGNTYP_P)); +E char *FDECL(piousness, (BOOLEAN_P, const char *)); +E void FDECL(mstatusline, (struct monst *)); +E void NDECL(ustatusline); + /* ### invent.c ### */ E Loot *FDECL(sortloot, (struct obj **, unsigned, BOOLEAN_P, @@ -2095,10 +2106,6 @@ E void NDECL(clearpriests); E void FDECL(restpriest, (struct monst *, BOOLEAN_P)); E void FDECL(newepri, (struct monst *)); E void FDECL(free_epri, (struct monst *)); -E const char *FDECL(align_str, (ALIGNTYP_P)); -E char *FDECL(piousness, (BOOLEAN_P, const char *)); -E void FDECL(mstatusline, (struct monst *)); -E void NDECL(ustatusline); /* ### quest.c ### */ diff --git a/src/cmd.c b/src/cmd.c index df02c6e4f..a619b42a4 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -29,9 +29,6 @@ static boolean alt_esc = FALSE; #endif -extern const char *hu_stat[]; /* hunger status from eat.c */ -extern const char *enc_stat[]; /* encumbrance status from botl.c */ - #ifdef UNIX /* * Some systems may have getchar() return EOF for various reasons, and @@ -178,23 +175,6 @@ static void FDECL(misc_stats, (winid, long *, long *)); static int NDECL(wiz_show_stats); static boolean FDECL(accept_menu_prefix, (int NDECL((*)))); static int NDECL(wiz_rumor_check); -static int NDECL(doattributes); - -static void FDECL(enlght_out, (const char *)); -static void FDECL(enlght_line, (const char *, const char *, const char *, - const char *)); -static char *FDECL(enlght_combatinc, (const char *, int, int, char *)); -static void FDECL(enlght_halfdmg, (int, int)); -static boolean NDECL(walking_on_water); -static boolean FDECL(cause_known, (int)); -static char *FDECL(attrval, (int, int, char *)); -static void FDECL(background_enlightenment, (int, int)); -static void FDECL(basics_enlightenment, (int, int)); -static void FDECL(characteristics_enlightenment, (int, int)); -static void FDECL(one_characteristic, (int, int, int)); -static void FDECL(status_enlightenment, (int, int)); -static void FDECL(attributes_enlightenment, (int, int)); -static void FDECL(show_achievements, (int)); static void FDECL(add_herecmd_menuitem, (winid, int NDECL((*)), const char *)); @@ -419,7 +399,7 @@ doextlist(VOID_ARGS) any.a_int = 4; add_menu(menuwin, NO_GLYPH, &any, 'z', 0, ATR_NONE, onelist ? "Show debugging commands in separate section" - : "Show all alphabetically, including debugging commands", + : "Show all alphabetically, including debugging commands", MENU_ITEMFLAGS_NONE); } any = cg.zeroany; @@ -997,7 +977,8 @@ wiz_load_splua(VOID_ARGS) strcat(buf, ".lua"); makemap_prepost(TRUE, was_in_W_tower); - /* TODO: need to split some of this out of mklev(), makelevel(), makemaz() */ + /* TODO: need to split some of this out of mklev(), makelevel(), + makemaz() */ g.in_mklev = TRUE; oinit(); /* assign level dependent obj probabilities */ clear_level_structures(); @@ -1019,8 +1000,8 @@ wiz_load_splua(VOID_ARGS) #endif } set_wall_state(); - /* for many room types, g.rooms[].rtype is zeroed once the room has been - entered; g.rooms[].orig_rtype always retains original rtype value */ + /* for many room types, rooms[].rtype is zeroed once the room has been + entered; rooms[].orig_rtype always retains original rtype value */ for (ridx = 0; ridx < SIZE(g.rooms); ridx++) g.rooms[ridx].orig_rtype = g.rooms[ridx].rtype; @@ -1584,7 +1565,7 @@ wiz_intrinsic(VOID_ARGS) if (!Warn_of_mon) { g.context.warntype.speciesidx = PM_GRID_BUG; g.context.warntype.species - = &mons[g.context.warntype.speciesidx]; + = &mons[g.context.warntype.speciesidx]; } goto def_feedback; case GLIB: @@ -1714,1746 +1695,6 @@ doterrain(VOID_ARGS) return 0; /* no time elapses */ } -/* -enlightenment and conduct- */ -static const char You_[] = "You ", are[] = "are ", were[] = "were ", - have[] = "have ", had[] = "had ", can[] = "can ", - could[] = "could "; -static const char have_been[] = "have been ", have_never[] = "have never ", - never[] = "never "; - -#define enl_msg(prefix, present, past, suffix, ps) \ - enlght_line(prefix, final ? past : present, suffix, ps) -#define you_are(attr, ps) enl_msg(You_, are, were, attr, ps) -#define you_have(attr, ps) enl_msg(You_, have, had, attr, ps) -#define you_can(attr, ps) enl_msg(You_, can, could, attr, ps) -#define you_have_been(goodthing) enl_msg(You_, have_been, were, goodthing, "") -#define you_have_never(badthing) \ - enl_msg(You_, have_never, never, badthing, "") -#define you_have_X(something) \ - enl_msg(You_, have, (const char *) "", something, "") - -static void -enlght_out(buf) -const char *buf; -{ - if (g.en_via_menu) { - anything any; - - any = cg.zeroany; - add_menu(g.en_win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, - MENU_ITEMFLAGS_NONE); - } else - putstr(g.en_win, 0, buf); -} - -static void -enlght_line(start, middle, end, ps) -const char *start, *middle, *end, *ps; -{ - char buf[BUFSZ]; - - Sprintf(buf, " %s%s%s%s.", start, middle, end, ps); - enlght_out(buf); -} - -/* format increased chance to hit or damage or defense (Protection) */ -static char * -enlght_combatinc(inctyp, incamt, final, outbuf) -const char *inctyp; -int incamt, final; -char *outbuf; -{ - const char *modif, *bonus; - boolean invrt; - int absamt; - - absamt = abs(incamt); - /* Protection amount is typically larger than damage or to-hit; - reduce magnitude by a third in order to stretch modifier ranges - (small:1..5, moderate:6..10, large:11..19, huge:20+) */ - if (!strcmp(inctyp, "defense")) - absamt = (absamt * 2) / 3; - - if (absamt <= 3) - modif = "small"; - else if (absamt <= 6) - modif = "moderate"; - else if (absamt <= 12) - modif = "large"; - else - modif = "huge"; - - modif = !incamt ? "no" : an(modif); /* ("no" case shouldn't happen) */ - bonus = (incamt >= 0) ? "bonus" : "penalty"; - /* "bonus " (to hit) vs " bonus" (damage, defense) */ - invrt = strcmp(inctyp, "to hit") ? TRUE : FALSE; - - Sprintf(outbuf, "%s %s %s", modif, invrt ? inctyp : bonus, - invrt ? bonus : inctyp); - if (final || wizard) - Sprintf(eos(outbuf), " (%s%d)", (incamt > 0) ? "+" : "", incamt); - - return outbuf; -} - -/* report half physical or half spell damage */ -static void -enlght_halfdmg(category, final) -int category; -int final; -{ - const char *category_name; - char buf[BUFSZ]; - - switch (category) { - case HALF_PHDAM: - category_name = "physical"; - break; - case HALF_SPDAM: - category_name = "spell"; - break; - default: - category_name = "unknown"; - break; - } - Sprintf(buf, " %s %s damage", (final || wizard) ? "half" : "reduced", - category_name); - enl_msg(You_, "take", "took", buf, from_what(category)); -} - -/* is hero actively using water walking capability on water (or lava)? */ -static boolean -walking_on_water() -{ - if (u.uinwater || Levitation || Flying) - return FALSE; - return (boolean) (Wwalking - && (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy))); -} - -/* describe u.utraptype; used by status_enlightenment() and self_lookat() */ -char * -trap_predicament(outbuf, final, wizxtra) -char *outbuf; -int final; -boolean wizxtra; -{ - struct trap *t; - - /* caller has verified u.utrap */ - *outbuf = '\0'; - switch (u.utraptype) { - case TT_BURIEDBALL: - Strcpy(outbuf, "tethered to something buried"); - break; - case TT_LAVA: - Sprintf(outbuf, "sinking into %s", final ? "lava" : hliquid("lava")); - break; - case TT_INFLOOR: - Sprintf(outbuf, "stuck in %s", the(surface(u.ux, u.uy))); - break; - default: /* TT_BEARTRAP, TT_PIT, or TT_WEB */ - Strcpy(outbuf, "trapped"); - if ((t = t_at(u.ux, u.uy)) != 0) /* should never be null */ - Sprintf(eos(outbuf), " in %s", an(trapname(t->ttyp, FALSE))); - break; - } - if (wizxtra) { /* give extra information for wizard mode enlightenment */ - /* curly braces: u.utrap is an escape attempt counter rather than a - turn timer so use different ornamentation than usual parentheses */ - Sprintf(eos(outbuf), " {%u}", u.utrap); - } - return outbuf; -} - -/* check whether hero is wearing something that player definitely knows - confers the target property; item must have been seen and its type - discovered but it doesn't necessarily have to be fully identified */ -static boolean -cause_known(propindx) -int propindx; /* index of a property which can be conveyed by worn item */ -{ - register struct obj *o; - long mask = W_ARMOR | W_AMUL | W_RING | W_TOOL; - - /* simpler than from_what()/what_gives(); we don't attempt to - handle artifacts and we deliberately ignore wielded items */ - for (o = g.invent; o; o = o->nobj) { - if (!(o->owornmask & mask)) - continue; - if ((int) objects[o->otyp].oc_oprop == propindx - && objects[o->otyp].oc_name_known && o->dknown) - return TRUE; - } - return FALSE; -} - -/* format a characteristic value, accommodating Strength's strangeness */ -static char * -attrval(attrindx, attrvalue, resultbuf) -int attrindx, attrvalue; -char resultbuf[]; /* should be at least [7] to hold "18/100\0" */ -{ - if (attrindx != A_STR || attrvalue <= 18) - Sprintf(resultbuf, "%d", attrvalue); - else if (attrvalue > STR18(100)) /* 19 to 25 */ - Sprintf(resultbuf, "%d", attrvalue - 100); - else /* simplify "18/ **" to be "18/100" */ - Sprintf(resultbuf, "18/%02d", attrvalue - 18); - return resultbuf; -} - -void -enlightenment(mode, final) -int mode; /* BASICENLIGHTENMENT | MAGICENLIGHTENMENT (| both) */ -int final; /* ENL_GAMEINPROGRESS:0, ENL_GAMEOVERALIVE, ENL_GAMEOVERDEAD */ -{ - char buf[BUFSZ], tmpbuf[BUFSZ]; - - g.en_win = create_nhwindow(NHW_MENU); - g.en_via_menu = !final; - if (g.en_via_menu) - start_menu(g.en_win); - - Strcpy(tmpbuf, g.plname); - *tmpbuf = highc(*tmpbuf); /* same adjustment as bottom line */ - /* as in background_enlightenment, when poly'd we need to use the saved - gender in u.mfemale rather than the current you-as-monster gender */ - Sprintf(buf, "%s the %s's attributes:", tmpbuf, - ((Upolyd ? u.mfemale : flags.female) && g.urole.name.f) - ? g.urole.name.f - : g.urole.name.m); - - /* title */ - enlght_out(buf); /* "Conan the Archeologist's attributes:" */ - /* background and characteristics; ^X or end-of-game disclosure */ - if (mode & BASICENLIGHTENMENT) { - /* role, race, alignment, deities, dungeon level, time, experience */ - background_enlightenment(mode, final); - /* hit points, energy points, armor class, gold */ - basics_enlightenment(mode, final); - /* strength, dexterity, &c */ - characteristics_enlightenment(mode, final); - } - /* expanded status line information, including things which aren't - included there due to space considerations--such as obvious - alternative movement indicators (riding, levitation, &c), and - various troubles (turning to stone, trapped, confusion, &c); - shown for both basic and magic enlightenment */ - status_enlightenment(mode, final); - /* remaining attributes; shown for potion,&c or wizard mode and - explore mode ^X or end of game disclosure */ - if (mode & MAGICENLIGHTENMENT) { - /* intrinsics and other traditional enlightenment feedback */ - attributes_enlightenment(mode, final); - } - - if (!g.en_via_menu) { - display_nhwindow(g.en_win, TRUE); - } else { - menu_item *selected = 0; - - end_menu(g.en_win, (char *) 0); - if (select_menu(g.en_win, PICK_NONE, &selected) > 0) - free((genericptr_t) selected); - g.en_via_menu = FALSE; - } - destroy_nhwindow(g.en_win); - g.en_win = WIN_ERR; -} - -/*ARGSUSED*/ -/* display role, race, alignment and such to en_win */ -static void -background_enlightenment(unused_mode, final) -int unused_mode UNUSED; -int final; -{ - const char *role_titl, *rank_titl; - int innategend, difgend, difalgn; - char buf[BUFSZ], tmpbuf[BUFSZ]; - - /* note that if poly'd, we need to use u.mfemale instead of flags.female - to access hero's saved gender-as-human/elf/&c rather than current one */ - innategend = (Upolyd ? u.mfemale : flags.female) ? 1 : 0; - role_titl = (innategend && g.urole.name.f) ? g.urole.name.f - : g.urole.name.m; - rank_titl = rank_of(u.ulevel, Role_switch, innategend); - - enlght_out(""); /* separator after title */ - enlght_out("Background:"); - - /* if polymorphed, report current shape before underlying role; - will be repeated as first status: "you are transformed" and also - among various attributes: "you are in beast form" (after being - told about lycanthropy) or "you are polymorphed into " - (with countdown timer appended for wizard mode); we really want - the player to know he's not a samurai at the moment... */ - if (Upolyd) { - char anbuf[20]; /* includes trailing space; [4] suffices */ - struct permonst *uasmon = g.youmonst.data; - boolean altphrasing = vampshifted(&g.youmonst); - - tmpbuf[0] = '\0'; - /* here we always use current gender, not saved role gender */ - if (!is_male(uasmon) && !is_female(uasmon) && !is_neuter(uasmon)) - Sprintf(tmpbuf, "%s ", genders[flags.female ? 1 : 0].adj); - if (altphrasing) - Sprintf(eos(tmpbuf), "%s in ", mons[g.youmonst.cham].mname); - Sprintf(buf, "%s%s%s%s form", !final ? "currently " : "", - altphrasing ? just_an(anbuf, tmpbuf) : "in ", - tmpbuf, uasmon->mname); - you_are(buf, ""); - } - - /* report role; omit gender if it's redundant (eg, "female priestess") */ - tmpbuf[0] = '\0'; - if (!g.urole.name.f - && ((g.urole.allow & ROLE_GENDMASK) == (ROLE_MALE | ROLE_FEMALE) - || innategend != flags.initgend)) - Sprintf(tmpbuf, "%s ", genders[innategend].adj); - buf[0] = '\0'; - if (Upolyd) - Strcpy(buf, "actually "); /* "You are actually a ..." */ - if (!strcmpi(rank_titl, role_titl)) { - /* omit role when rank title matches it */ - Sprintf(eos(buf), "%s, level %d %s%s", an(rank_titl), u.ulevel, - tmpbuf, g.urace.noun); - } else { - Sprintf(eos(buf), "%s, a level %d %s%s %s", an(rank_titl), u.ulevel, - tmpbuf, g.urace.adj, role_titl); - } - you_are(buf, ""); - - /* report alignment (bypass you_are() in order to omit ending period); - adverb is used to distinguish between temporary change (helm of opp. - alignment), permanent change (one-time conversion), and original */ - Sprintf(buf, " %s%s%s, %son a mission for %s", - You_, !final ? are : were, - align_str(u.ualign.type), - /* helm of opposite alignment (might hide conversion) */ - (u.ualign.type != u.ualignbase[A_CURRENT]) - /* what's the past tense of "currently"? if we used "formerly" - it would sound like a reference to the original alignment */ - ? (!final ? "currently " : "temporarily ") - /* permanent conversion */ - : (u.ualign.type != u.ualignbase[A_ORIGINAL]) - /* and what's the past tense of "now"? certainly not "then" - in a context like this...; "belatedly" == weren't that - way sooner (in other words, didn't start that way) */ - ? (!final ? "now " : "belatedly ") - /* atheist (ignored in very early game) */ - : (!u.uconduct.gnostic && g.moves > 1000L) - ? "nominally " - /* lastly, normal case */ - : "", - u_gname()); - enlght_out(buf); - /* show the rest of this game's pantheon (finishes previous sentence) - [appending "also Moloch" at the end would allow for straightforward - trailing "and" on all three aligned entries but looks too verbose] */ - Sprintf(buf, " who %s opposed by", !final ? "is" : "was"); - if (u.ualign.type != A_LAWFUL) - Sprintf(eos(buf), " %s (%s) and", align_gname(A_LAWFUL), - align_str(A_LAWFUL)); - if (u.ualign.type != A_NEUTRAL) - Sprintf(eos(buf), " %s (%s)%s", align_gname(A_NEUTRAL), - align_str(A_NEUTRAL), - (u.ualign.type != A_CHAOTIC) ? " and" : ""); - if (u.ualign.type != A_CHAOTIC) - Sprintf(eos(buf), " %s (%s)", align_gname(A_CHAOTIC), - align_str(A_CHAOTIC)); - Strcat(buf, "."); /* terminate sentence */ - enlght_out(buf); - - /* show original alignment,gender,race,role if any have been changed; - giving separate message for temporary alignment change bypasses need - for tricky phrasing otherwise necessitated by possibility of having - helm of opposite alignment mask a permanent alignment conversion */ - difgend = (innategend != flags.initgend); - difalgn = (((u.ualign.type != u.ualignbase[A_CURRENT]) ? 1 : 0) - + ((u.ualignbase[A_CURRENT] != u.ualignbase[A_ORIGINAL]) - ? 2 : 0)); - if (difalgn & 1) { /* have temporary alignment so report permanent one */ - Sprintf(buf, "actually %s", align_str(u.ualignbase[A_CURRENT])); - you_are(buf, ""); - difalgn &= ~1; /* suppress helm from "started out " message */ - } - if (difgend || difalgn) { /* sex change or perm align change or both */ - Sprintf(buf, " You started out %s%s%s.", - difgend ? genders[flags.initgend].adj : "", - (difgend && difalgn) ? " and " : "", - difalgn ? align_str(u.ualignbase[A_ORIGINAL]) : ""); - enlght_out(buf); - } - - /* As of 3.6.2: dungeon level, so that ^X really has all status info as - claimed by the comment below; this reveals more information than - the basic status display, but that's one of the purposes of ^X; - similar information is revealed by #overview; the "You died in - " given by really_done() is more rudimentary than this */ - *buf = *tmpbuf = '\0'; - if (In_endgame(&u.uz)) { - int egdepth = observable_depth(&u.uz); - - (void) endgamelevelname(tmpbuf, egdepth); - Sprintf(buf, "in the endgame, on the %s%s", - !strncmp(tmpbuf, "Plane", 5) ? "Elemental " : "", tmpbuf); - } else if (Is_knox(&u.uz)) { - /* this gives away the fact that the knox branch is only 1 level */ - Sprintf(buf, "on the %s level", g.dungeons[u.uz.dnum].dname); - /* TODO? maybe phrase it differently when actually inside the fort, - if we're able to determine that (not trivial) */ - } else { - char dgnbuf[QBUFSZ]; - - Strcpy(dgnbuf, g.dungeons[u.uz.dnum].dname); - if (!strncmpi(dgnbuf, "The ", 4)) - *dgnbuf = lowc(*dgnbuf); - Sprintf(tmpbuf, "level %d", - In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz)); - /* TODO? maybe extend this bit to include various other automatic - annotations from the dungeon overview code */ - if (Is_rogue_level(&u.uz)) - Strcat(tmpbuf, ", a primitive area"); - else if (Is_bigroom(&u.uz) && !Blind) - Strcat(tmpbuf, ", a very big room"); - Sprintf(buf, "in %s, on %s", dgnbuf, tmpbuf); - } - you_are(buf, ""); - - /* this is shown even if the 'time' option is off */ - if (g.moves == 1L) { - you_have("just started your adventure", ""); - } else { - /* 'turns' grates on the nerves in this context... */ - Sprintf(buf, "the dungeon %ld turn%s ago", g.moves, plur(g.moves)); - /* same phrasing for current and final: "entered" is unconditional */ - enlght_line(You_, "entered ", buf, ""); - } - - /* for gameover, these have been obtained in really_done() so that they - won't vary if user leaves a disclosure prompt or --More-- unanswered - long enough for the dynamic value to change between then and now */ - if (final ? iflags.at_midnight : midnight()) { - enl_msg("It ", "is ", "was ", "the midnight hour", ""); - } else if (final ? iflags.at_night : night()) { - enl_msg("It ", "is ", "was ", "nighttime", ""); - } - /* other environmental factors */ - if (flags.moonphase == FULL_MOON || flags.moonphase == NEW_MOON) { - /* [This had "tonight" but has been changed to "in effect". - There is a similar issue to Friday the 13th--it's the value - at the start of the current session but that session might - have dragged on for an arbitrary amount of time. We want to - report the values that currently affect play--or affected - play when game ended--rather than actual outside situation.] */ - Sprintf(buf, "a %s moon in effect%s", - (flags.moonphase == FULL_MOON) ? "full" - : (flags.moonphase == NEW_MOON) ? "new" - /* showing these would probably just lead to confusion - since they have no effect on game play... */ - : (flags.moonphase < FULL_MOON) ? "first quarter" - : "last quarter", - /* we don't have access to 'how' here--aside from survived - vs died--so settle for general platitude */ - final ? " when your adventure ended" : ""); - enl_msg("There ", "is ", "was ", buf, ""); - } - if (flags.friday13) { - /* let player know that friday13 penalty is/was in effect; - we don't say "it is/was Friday the 13th" because that was at - the start of the session and it might be past midnight (or - days later if the game has been paused without save/restore), - so phrase this similar to the start up message */ - Sprintf(buf, " Bad things %s on Friday the 13th.", - !final ? "can happen" - : (final == ENL_GAMEOVERALIVE) ? "could have happened" - /* there's no may to tell whether -1 Luck made a - difference but hero has died... */ - : "happened"); - enlght_out(buf); - } - - if (!Upolyd) { - int ulvl = (int) u.ulevel; - /* [flags.showexp currently does not matter; should it?] */ - - /* experience level is already shown above */ - Sprintf(buf, "%-1ld experience point%s", u.uexp, plur(u.uexp)); - /* TODO? - * Remove wizard-mode restriction since patient players can - * determine the numbers needed without resorting to spoilers - * (even before this started being disclosed for 'final'; - * just enable 'showexp' and look at normal status lines - * after drinking gain level potions or eating wraith corpses - * or being level-drained by vampires). - */ - if (ulvl < 30 && (final || wizard)) { - long nxtlvl = newuexp(ulvl), delta = nxtlvl - u.uexp; - - Sprintf(eos(buf), ", %ld %s%sneeded %s level %d", - delta, (u.uexp > 0) ? "more " : "", - /* present tense=="needed", past tense=="were needed" */ - !final ? "" : (delta == 1L) ? "was " : "were ", - /* "for": grammatically iffy but less likely to wrap */ - (ulvl < 18) ? "to attain" : "for", (ulvl + 1)); - } - you_have(buf, ""); - } -#ifdef SCORE_ON_BOTL - if (flags.showscore) { - /* describes what's shown on status line, which is an approximation; - only show it here if player has the 'showscore' option enabled */ - Sprintf(buf, "%ld%s", botl_score(), - !final ? "" : " before end-of-game adjustments"); - enl_msg("Your score ", "is ", "was ", buf, ""); - } -#endif -} - -/* hit points, energy points, armor class -- essential information which - doesn't fit very well in other categories */ -/*ARGSUSED*/ -static void -basics_enlightenment(mode, final) -int mode UNUSED; -int final; -{ - static char Power[] = "energy points (spell power)"; - char buf[BUFSZ]; - int pw = u.uen, hp = (Upolyd ? u.mh : u.uhp), - pwmax = u.uenmax, hpmax = (Upolyd ? u.mhmax : u.uhpmax); - - enlght_out(""); /* separator after background */ - enlght_out("Basics:"); - - if (hp < 0) - hp = 0; - /* "1 out of 1" rather than "all" if max is only 1; should never happen */ - if (hp == hpmax && hpmax > 1) - Sprintf(buf, "all %d hit points", hpmax); - else - Sprintf(buf, "%d out of %d hit point%s", hp, hpmax, plur(hpmax)); - you_have(buf, ""); - - /* low max energy is feasible, so handle couple of extra special cases */ - if (pwmax == 0 || (pw == pwmax && pwmax == 2)) /* both: "all 2" is silly */ - Sprintf(buf, "%s %s", !pwmax ? "no" : "both", Power); - else if (pw == pwmax && pwmax > 2) - Sprintf(buf, "all %d %s", pwmax, Power); - else - Sprintf(buf, "%d out of %d %s", pw, pwmax, Power); - you_have(buf, ""); - - if (Upolyd) { - switch (mons[u.umonnum].mlevel) { - case 0: - /* status line currently being explained shows "HD:0" */ - Strcpy(buf, "0 hit dice (actually 1/2)"); - break; - case 1: - Strcpy(buf, "1 hit die"); - break; - default: - Sprintf(buf, "%d hit dice", mons[u.umonnum].mlevel); - break; - } - you_have(buf, ""); - } - - Sprintf(buf, "%d", u.uac); - enl_msg("Your armor class ", "is ", "was ", buf, ""); - - /* gold; similar to doprgold(#seegold) but without shop billing info; - same amount as shown on status line which ignores container contents */ - { - static const char Your_wallet[] = "Your wallet "; - long umoney = money_cnt(g.invent); - - if (!umoney) { - enl_msg(Your_wallet, "is ", "was ", "empty", ""); - } else { - Sprintf(buf, "%ld %s", umoney, currency(umoney)); - enl_msg(Your_wallet, "contains ", "contained ", buf, ""); - } - } - - if (flags.pickup) { - char ocl[MAXOCLASSES + 1]; - - Strcpy(buf, "on"); - oc_to_str(flags.pickup_types, ocl); - Sprintf(eos(buf), " for %s%s%s", - *ocl ? "'" : "", *ocl ? ocl : "all types", *ocl ? "'" : ""); - if (flags.pickup_thrown && *ocl) /* *ocl: don't show if 'all types' */ - Strcat(buf, " plus thrown"); - if (g.apelist) - Strcat(buf, ", with exceptions"); - } else - Strcpy(buf, "off"); - enl_msg("Autopickup ", "is ", "was ", buf, ""); -} - -/* characteristics: expanded version of bottom line strength, dexterity, &c */ -static void -characteristics_enlightenment(mode, final) -int mode; -int final; -{ - char buf[BUFSZ]; - - enlght_out(""); - Sprintf(buf, "%s Characteristics:", !final ? "Current" : "Final"); - enlght_out(buf); - - /* bottom line order */ - one_characteristic(mode, final, A_STR); /* strength */ - one_characteristic(mode, final, A_DEX); /* dexterity */ - one_characteristic(mode, final, A_CON); /* constitution */ - one_characteristic(mode, final, A_INT); /* intelligence */ - one_characteristic(mode, final, A_WIS); /* wisdom */ - one_characteristic(mode, final, A_CHA); /* charisma */ -} - -/* display one attribute value for characteristics_enlightenment() */ -static void -one_characteristic(mode, final, attrindx) -int mode, final, attrindx; -{ - extern const char *const attrname[]; /* attrib.c */ - boolean hide_innate_value = FALSE, interesting_alimit; - int acurrent, abase, apeak, alimit; - const char *paren_pfx; - char subjbuf[BUFSZ], valubuf[BUFSZ], valstring[32]; - - /* being polymorphed or wearing certain cursed items prevents - hero from reliably tracking changes to characteristics so - we don't show base & peak values then; when the items aren't - cursed, hero could take them off to check underlying values - and we show those in such case so that player doesn't need - to actually resort to doing that */ - if (Upolyd) { - hide_innate_value = TRUE; - } else if (Fixed_abil) { - if (stuck_ring(uleft, RIN_SUSTAIN_ABILITY) - || stuck_ring(uright, RIN_SUSTAIN_ABILITY)) - hide_innate_value = TRUE; - } - switch (attrindx) { - case A_STR: - if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER && uarmg->cursed) - hide_innate_value = TRUE; - break; - case A_DEX: - break; - case A_CON: - if (uwep && uwep->oartifact == ART_OGRESMASHER && uwep->cursed) - hide_innate_value = TRUE; - break; - case A_INT: - if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed) - hide_innate_value = TRUE; - break; - case A_WIS: - if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed) - hide_innate_value = TRUE; - break; - case A_CHA: - break; - default: - return; /* impossible */ - }; - /* note: final disclosure includes MAGICENLIGHTENTMENT */ - if ((mode & MAGICENLIGHTENMENT) && !Upolyd) - hide_innate_value = FALSE; - - acurrent = ACURR(attrindx); - (void) attrval(attrindx, acurrent, valubuf); /* Sprintf(valubuf,"%d",) */ - Sprintf(subjbuf, "Your %s ", attrname[attrindx]); - - if (!hide_innate_value) { - /* show abase, amax, and/or attrmax if acurr doesn't match abase - (a magic bonus or penalty is in effect) or abase doesn't match - amax (some points have been lost to poison or exercise abuse - and are restorable) or attrmax is different from normal human - (while game is in progress; trying to reduce dependency on - spoilers to keep track of such stuff) or attrmax was different - from abase (at end of game; this attribute wasn't maxed out) */ - abase = ABASE(attrindx); - apeak = AMAX(attrindx); - alimit = ATTRMAX(attrindx); - /* criterium for whether the limit is interesting varies */ - interesting_alimit = - final ? TRUE /* was originally `(abase != alimit)' */ - : (alimit != (attrindx != A_STR ? 18 : STR18(100))); - paren_pfx = final ? " (" : " (current; "; - if (acurrent != abase) { - Sprintf(eos(valubuf), "%sbase:%s", paren_pfx, - attrval(attrindx, abase, valstring)); - paren_pfx = ", "; - } - if (abase != apeak) { - Sprintf(eos(valubuf), "%speak:%s", paren_pfx, - attrval(attrindx, apeak, valstring)); - paren_pfx = ", "; - } - if (interesting_alimit) { - Sprintf(eos(valubuf), "%s%slimit:%s", paren_pfx, - /* more verbose if exceeding 'limit' due to magic bonus */ - (acurrent > alimit) ? "innate " : "", - attrval(attrindx, alimit, valstring)); - /* paren_pfx = ", "; */ - } - if (acurrent != abase || abase != apeak || interesting_alimit) - Strcat(valubuf, ")"); - } - enl_msg(subjbuf, "is ", "was ", valubuf, ""); -} - -/* status: selected obvious capabilities, assorted troubles */ -static void -status_enlightenment(mode, final) -int mode; -int final; -{ - boolean magic = (mode & MAGICENLIGHTENMENT) ? TRUE : FALSE; - int cap, wtype; - char buf[BUFSZ], youtoo[BUFSZ]; - boolean Riding = (u.usteed - /* if hero dies while dismounting, u.usteed will still - be set; we want to ignore steed in that situation */ - && !(final == ENL_GAMEOVERDEAD - && !strcmp(g.killer.name, "riding accident"))); - const char *steedname = (!Riding ? (char *) 0 - : x_monnam(u.usteed, - u.usteed->mtame ? ARTICLE_YOUR : ARTICLE_THE, - (char *) 0, - (SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION), - FALSE)); - - /*\ - * Status (many are abbreviated on bottom line; others are or - * should be discernible to the hero hence to the player) - \*/ - enlght_out(""); /* separator after title or characteristics */ - enlght_out(final ? "Final Status:" : "Current Status:"); - - Strcpy(youtoo, You_); - /* not a traditional status but inherently obvious to player; more - detail given below (attributes section) for magic enlightenment */ - if (Upolyd) { - Strcpy(buf, "transformed"); - if (ugenocided()) - Sprintf(eos(buf), " and %s %s inside", - final ? "felt" : "feel", udeadinside()); - you_are(buf, ""); - } - /* not a trouble, but we want to display riding status before maybe - reporting steed as trapped or hero stuck to cursed saddle */ - if (Riding) { - Sprintf(buf, "riding %s", steedname); - you_are(buf, ""); - Sprintf(eos(youtoo), "and %s ", steedname); - } - /* other movement situations that hero should always know */ - if (Levitation) { - if (Lev_at_will && magic) - you_are("levitating, at will", ""); - else - enl_msg(youtoo, are, were, "levitating", from_what(LEVITATION)); - } else if (Flying) { /* can only fly when not levitating */ - enl_msg(youtoo, are, were, "flying", from_what(FLYING)); - } - if (Underwater) { - you_are("underwater", ""); - } else if (u.uinwater) { - you_are(Swimming ? "swimming" : "in water", from_what(SWIMMING)); - } else if (walking_on_water()) { - /* show active Wwalking here, potential Wwalking elsewhere */ - Sprintf(buf, "walking on %s", - is_pool(u.ux, u.uy) ? "water" - : is_lava(u.ux, u.uy) ? "lava" - : surface(u.ux, u.uy)); /* catchall; shouldn't happen */ - you_are(buf, from_what(WWALKING)); - } - if (Upolyd && (u.uundetected || U_AP_TYPE != M_AP_NOTHING)) - youhiding(TRUE, final); - - /* internal troubles, mostly in the order that prayer ranks them */ - if (Stoned) { - if (final && (Stoned & I_SPECIAL)) - enlght_out(" You turned into stone."); - else - you_are("turning to stone", ""); - } - if (Slimed) { - if (final && (Slimed & I_SPECIAL)) - enlght_out(" You turned into slime."); - else - you_are("turning into slime", ""); - } - if (Strangled) { - if (u.uburied) { - you_are("buried", ""); - } else { - if (final && (Strangled & I_SPECIAL)) { - enlght_out(" You died from strangulation."); - } else { - Strcpy(buf, "being strangled"); - if (wizard) - Sprintf(eos(buf), " (%ld)", (Strangled & TIMEOUT)); - you_are(buf, from_what(STRANGLED)); - } - } - } - if (Sick) { - /* the two types of sickness are lumped together; hero can be - afflicted by both but there is only one timeout; botl status - puts TermIll before FoodPois and death due to timeout reports - terminal illness if both are in effect, so do the same here */ - if (final && (Sick & I_SPECIAL)) { - Sprintf(buf, " %sdied from %s.", You_, /* has trailing space */ - (u.usick_type & SICK_NONVOMITABLE) - ? "terminal illness" : "food poisoning"); - enlght_out(buf); - } else { - /* unlike death due to sickness, report the two cases separately - because it is possible to cure one without curing the other */ - if (u.usick_type & SICK_NONVOMITABLE) - you_are("terminally sick from illness", ""); - if (u.usick_type & SICK_VOMITABLE) - you_are("terminally sick from food poisoning", ""); - } - } - if (Vomiting) - you_are("nauseated", ""); - if (Stunned) - you_are("stunned", ""); - if (Confusion) - you_are("confused", ""); - if (Hallucination) - you_are("hallucinating", ""); - if (Blind) { - /* from_what() (currently wizard-mode only) checks !haseyes() - before u.uroleplay.blind, so we should too */ - Sprintf(buf, "%s blind", - !haseyes(g.youmonst.data) ? "innately" - : u.uroleplay.blind ? "permanently" - /* better phrasing desperately wanted... */ - : Blindfolded_only ? "deliberately" - : "temporarily"); - if (wizard && (Blinded & TIMEOUT) != 0L - && !u.uroleplay.blind && haseyes(g.youmonst.data)) - Sprintf(eos(buf), " (%ld)", (Blinded & TIMEOUT)); - /* !haseyes: avoid "you are innately blind innately" */ - you_are(buf, !haseyes(g.youmonst.data) ? "" : from_what(BLINDED)); - } - if (Deaf) - you_are("deaf", from_what(DEAF)); - - /* external troubles, more or less */ - if (Punished) { - if (uball) { - Sprintf(buf, "chained to %s", ansimpleoname(uball)); - } else { - impossible("Punished without uball?"); - Strcpy(buf, "punished"); - } - you_are(buf, ""); - } - if (u.utrap) { - char predicament[BUFSZ]; - boolean anchored = (u.utraptype == TT_BURIEDBALL); - - (void) trap_predicament(predicament, final, wizard); - if (u.usteed) { /* not `Riding' here */ - Sprintf(buf, "%s%s ", anchored ? "you and " : "", steedname); - *buf = highc(*buf); - enl_msg(buf, (anchored ? "are " : "is "), - (anchored ? "were " : "was "), predicament, ""); - } else - you_are(predicament, ""); - } /* (u.utrap) */ - if (u.uswallow) { - Sprintf(buf, "swallowed by %s", a_monnam(u.ustuck)); - if (wizard) - Sprintf(eos(buf), " (%u)", u.uswldtim); - you_are(buf, ""); - } else if (u.ustuck) { - Sprintf(buf, "%s %s", - (Upolyd && sticks(g.youmonst.data)) ? "holding" : "held by", - a_monnam(u.ustuck)); - you_are(buf, ""); - } - if (Riding) { - struct obj *saddle = which_armor(u.usteed, W_SADDLE); - - if (saddle && saddle->cursed) { - Sprintf(buf, "stuck to %s %s", s_suffix(steedname), - simpleonames(saddle)); - you_are(buf, ""); - } - } - if (Wounded_legs) { - /* when mounted, Wounded_legs applies to steed rather than to - hero; we only report steed's wounded legs in wizard mode */ - if (u.usteed) { /* not `Riding' here */ - if (wizard && steedname) { - Strcpy(buf, steedname); - *buf = highc(*buf); - enl_msg(buf, " has", " had", " wounded legs", ""); - } - } else { - Sprintf(buf, "wounded %s", makeplural(body_part(LEG))); - you_have(buf, ""); - } - } - if (Glib) { - Sprintf(buf, "slippery %s", fingers_or_gloves(TRUE)); - if (wizard) - Sprintf(eos(buf), " (%ld)", (Glib & TIMEOUT)); - you_have(buf, ""); - } - if (Fumbling) { - if (magic || cause_known(FUMBLING)) - enl_msg(You_, "fumble", "fumbled", "", from_what(FUMBLING)); - } - if (Sleepy) { - if (magic || cause_known(SLEEPY)) { - Strcpy(buf, from_what(SLEEPY)); - if (wizard) - Sprintf(eos(buf), " (%ld)", (HSleepy & TIMEOUT)); - enl_msg("You ", "fall", "fell", " asleep uncontrollably", buf); - } - } - /* hunger/nutrition */ - if (Hunger) { - if (magic || cause_known(HUNGER)) - enl_msg(You_, "hunger", "hungered", " rapidly", - from_what(HUNGER)); - } - Strcpy(buf, hu_stat[u.uhs]); /* hunger status; omitted if "normal" */ - mungspaces(buf); /* strip trailing spaces */ - if (*buf) { - *buf = lowc(*buf); /* override capitalization */ - if (!strcmp(buf, "weak")) - Strcat(buf, " from severe hunger"); - else if (!strncmp(buf, "faint", 5)) /* fainting, fainted */ - Strcat(buf, " due to starvation"); - you_are(buf, ""); - } - /* encumbrance */ - if ((cap = near_capacity()) > UNENCUMBERED) { - const char *adj = "?_?"; /* (should always get overridden) */ - - Strcpy(buf, enc_stat[cap]); - *buf = lowc(*buf); - switch (cap) { - case SLT_ENCUMBER: - adj = "slightly"; - break; /* burdened */ - case MOD_ENCUMBER: - adj = "moderately"; - break; /* stressed */ - case HVY_ENCUMBER: - adj = "very"; - break; /* strained */ - case EXT_ENCUMBER: - adj = "extremely"; - break; /* overtaxed */ - case OVERLOADED: - adj = "not possible"; - break; - } - Sprintf(eos(buf), "; movement %s %s%s", !final ? "is" : "was", adj, - (cap < OVERLOADED) ? " slowed" : ""); - you_are(buf, ""); - } else { - /* last resort entry, guarantees Status section is non-empty - (no longer needed for that purpose since weapon status added; - still useful though) */ - you_are("unencumbered", ""); - } - - /* report being weaponless; distinguish whether gloves are worn */ - if (!uwep) { - you_are(uarmg ? "empty handed" /* gloves imply hands */ - : humanoid(g.youmonst.data) - /* hands but no weapon and no gloves */ - ? "bare handed" - /* alternate phrasing for paws or lack of hands */ - : "not wielding anything", - ""); - /* two-weaponing implies hands (can't be polymorphed) and - a weapon or wep-tool (not other odd stuff) in each hand */ - } else if (u.twoweap) { - you_are("wielding two weapons at once", ""); - /* report most weapons by their skill class (so a katana will be - described as a long sword, for instance; mattock and hook are - exceptions), or wielded non-weapon item by its object class */ - } else { - const char *what = weapon_descr(uwep); - - if (!strcmpi(what, "armor") || !strcmpi(what, "food") - || !strcmpi(what, "venom")) - Sprintf(buf, "wielding some %s", what); - else - Sprintf(buf, "wielding %s", - (uwep->quan == 1L) ? an(what) : makeplural(what)); - you_are(buf, ""); - } - /* - * Skill with current weapon. Might help players who've never - * noticed #enhance or decided that it was pointless. - * - * TODO? Maybe merge wielding line and skill line into one sentence. - */ - if ((wtype = uwep_skill_type()) != P_NONE) { - char sklvlbuf[20]; - int sklvl = P_SKILL(wtype); - boolean hav = (sklvl != P_UNSKILLED && sklvl != P_SKILLED); - - if (sklvl == P_ISRESTRICTED) - Strcpy(sklvlbuf, "no"); - else - (void) lcase(skill_level_name(wtype, sklvlbuf)); - /* "you have no/basic/expert/master/grand-master skill with " - or "you are unskilled/skilled in " */ - Sprintf(buf, "%s %s %s", sklvlbuf, - hav ? "skill with" : "in", skill_name(wtype)); - if (can_advance(wtype, FALSE)) - Sprintf(eos(buf), " and %s that", - !final ? "can enhance" : "could have enhanced"); - if (hav) - you_have(buf, ""); - else - you_are(buf, ""); - } - /* report 'nudity' */ - if (!uarm && !uarmu && !uarmc && !uarms && !uarmg && !uarmf && !uarmh) { - if (u.uroleplay.nudist) - enl_msg(You_, "do", "did", " not wear any armor", ""); - else - you_are("not wearing any armor", ""); - } -} - -/* attributes: intrinsics and the like, other non-obvious capabilities */ -static void -attributes_enlightenment(unused_mode, final) -int unused_mode UNUSED; -int final; -{ - static NEARDATA const char if_surroundings_permitted[] = - " if surroundings permitted"; - int ltmp, armpro; - char buf[BUFSZ]; - - /*\ - * Attributes - \*/ - enlght_out(""); - enlght_out(final ? "Final Attributes:" : "Current Attributes:"); - - if (u.uevent.uhand_of_elbereth) { - static const char *const hofe_titles[3] = { "the Hand of Elbereth", - "the Envoy of Balance", - "the Glory of Arioch" }; - you_are(hofe_titles[u.uevent.uhand_of_elbereth - 1], ""); - } - - Sprintf(buf, "%s", piousness(TRUE, "aligned")); - if (u.ualign.record >= 0) - you_are(buf, ""); - else - you_have(buf, ""); - - if (wizard) { - Sprintf(buf, " %d", u.ualign.record); - enl_msg("Your alignment ", "is", "was", buf, ""); - } - - /*** Resistances to troubles ***/ - if (Invulnerable) - you_are("invulnerable", from_what(INVULNERABLE)); - if (Antimagic) - you_are("magic-protected", from_what(ANTIMAGIC)); - if (Fire_resistance) - you_are("fire resistant", from_what(FIRE_RES)); - if (Cold_resistance) - you_are("cold resistant", from_what(COLD_RES)); - if (Sleep_resistance) - you_are("sleep resistant", from_what(SLEEP_RES)); - if (Disint_resistance) - you_are("disintegration-resistant", from_what(DISINT_RES)); - if (Shock_resistance) - you_are("shock resistant", from_what(SHOCK_RES)); - if (Poison_resistance) - you_are("poison resistant", from_what(POISON_RES)); - if (Acid_resistance) - you_are("acid resistant", from_what(ACID_RES)); - if (Drain_resistance) - you_are("level-drain resistant", from_what(DRAIN_RES)); - if (Sick_resistance) - you_are("immune to sickness", from_what(SICK_RES)); - if (Stone_resistance) - you_are("petrification resistant", from_what(STONE_RES)); - if (Halluc_resistance) - enl_msg(You_, "resist", "resisted", " hallucinations", - from_what(HALLUC_RES)); - if (u.uedibility) - you_can("recognize detrimental food", ""); - - /*** Vision and senses ***/ - if (!Blind && (Blinded || !haseyes(g.youmonst.data))) - you_can("see", from_what(-BLINDED)); /* Eyes of the Overworld */ - if (See_invisible) { - if (!Blind) - enl_msg(You_, "see", "saw", " invisible", from_what(SEE_INVIS)); - else - enl_msg(You_, "will see", "would have seen", - " invisible when not blind", from_what(SEE_INVIS)); - } - if (Blind_telepat) - you_are("telepathic", from_what(TELEPAT)); - if (Warning) - you_are("warned", from_what(WARNING)); - if (Warn_of_mon && g.context.warntype.obj) { - Sprintf(buf, "aware of the presence of %s", - (g.context.warntype.obj & M2_ORC) ? "orcs" - : (g.context.warntype.obj & M2_ELF) ? "elves" - : (g.context.warntype.obj & M2_DEMON) ? "demons" : something); - you_are(buf, from_what(WARN_OF_MON)); - } - if (Warn_of_mon && g.context.warntype.polyd) { - Sprintf(buf, "aware of the presence of %s", - ((g.context.warntype.polyd & (M2_HUMAN | M2_ELF)) - == (M2_HUMAN | M2_ELF)) - ? "humans and elves" - : (g.context.warntype.polyd & M2_HUMAN) - ? "humans" - : (g.context.warntype.polyd & M2_ELF) - ? "elves" - : (g.context.warntype.polyd & M2_ORC) - ? "orcs" - : (g.context.warntype.polyd & M2_DEMON) - ? "demons" - : "certain monsters"); - you_are(buf, ""); - } - if (Warn_of_mon && g.context.warntype.speciesidx >= LOW_PM) { - Sprintf(buf, "aware of the presence of %s", - makeplural(mons[g.context.warntype.speciesidx].mname)); - you_are(buf, from_what(WARN_OF_MON)); - } - if (Undead_warning) - you_are("warned of undead", from_what(WARN_UNDEAD)); - if (Searching) - you_have("automatic searching", from_what(SEARCHING)); - if (Clairvoyant) - you_are("clairvoyant", from_what(CLAIRVOYANT)); - else if ((HClairvoyant || EClairvoyant) && BClairvoyant) { - Strcpy(buf, from_what(-CLAIRVOYANT)); - if (!strncmp(buf, " because of ", 12)) - /* overwrite substring; strncpy doesn't add terminator */ - (void) strncpy(buf, " if not for ", 12); - enl_msg(You_, "could be", "could have been", " clairvoyant", buf); - } - if (Infravision) - you_have("infravision", from_what(INFRAVISION)); - if (Detect_monsters) - you_are("sensing the presence of monsters", ""); - if (u.umconf) - you_are("going to confuse monsters", ""); - - /*** Appearance and behavior ***/ - if (Adornment) { - int adorn = 0; - - if (uleft && uleft->otyp == RIN_ADORNMENT) - adorn += uleft->spe; - if (uright && uright->otyp == RIN_ADORNMENT) - adorn += uright->spe; - /* the sum might be 0 (+0 ring or two which negate each other); - that yields "you are charismatic" (which isn't pointless - because it potentially impacts seduction attacks) */ - Sprintf(buf, "%scharismatic", - (adorn > 0) ? "more " : (adorn < 0) ? "less " : ""); - you_are(buf, from_what(ADORNED)); - } - if (Invisible) - you_are("invisible", from_what(INVIS)); - else if (Invis) - you_are("invisible to others", from_what(INVIS)); - /* ordinarily "visible" is redundant; this is a special case for - the situation when invisibility would be an expected attribute */ - else if ((HInvis || EInvis) && BInvis) - you_are("visible", from_what(-INVIS)); - if (Displaced) - you_are("displaced", from_what(DISPLACED)); - if (Stealth) - you_are("stealthy", from_what(STEALTH)); - if (Aggravate_monster) - enl_msg("You aggravate", "", "d", " monsters", - from_what(AGGRAVATE_MONSTER)); - if (Conflict) - enl_msg("You cause", "", "d", " conflict", from_what(CONFLICT)); - - /*** Transportation ***/ - if (Jumping) - you_can("jump", from_what(JUMPING)); - if (Teleportation) - you_can("teleport", from_what(TELEPORT)); - if (Teleport_control) - you_have("teleport control", from_what(TELEPORT_CONTROL)); - /* actively levitating handled earlier as a status condition */ - if (BLevitation) { /* levitation is blocked */ - long save_BLev = BLevitation; - - BLevitation = 0L; - if (Levitation) { - /* either trapped in the floor or inside solid rock - (or both if chained to buried iron ball and have - moved one step into solid rock somehow) */ - boolean trapped = (save_BLev & I_SPECIAL) != 0L, - terrain = (save_BLev & FROMOUTSIDE) != 0L; - - Sprintf(buf, "%s%s%s", - trapped ? " if not trapped" : "", - (trapped && terrain) ? " and" : "", - terrain ? if_surroundings_permitted : ""); - enl_msg(You_, "would levitate", "would have levitated", buf, ""); - } - BLevitation = save_BLev; - } - /* actively flying handled earlier as a status condition */ - if (BFlying) { /* flight is blocked */ - long save_BFly = BFlying; - - BFlying = 0L; - if (Flying) { - enl_msg(You_, "would fly", "would have flown", - /* wording quibble: for past tense, "hadn't been" - would sound better than "weren't" (and - "had permitted" better than "permitted"), but - "weren't" and "permitted" are adequate so the - extra complexity to handle that isn't worth it */ - Levitation - ? " if you weren't levitating" - : (save_BFly == I_SPECIAL) - /* this is an oversimpliction; being trapped - might also be blocking levitation so flight - would still be blocked after escaping trap */ - ? " if you weren't trapped" - : (save_BFly == FROMOUTSIDE) - ? if_surroundings_permitted - /* two or more of levitation, surroundings, - and being trapped in the floor */ - : " if circumstances permitted", - ""); - } - BFlying = save_BFly; - } - /* actively walking on water handled earlier as a status condition */ - if (Wwalking && !walking_on_water()) - you_can("walk on water", from_what(WWALKING)); - /* actively swimming (in water but not under it) handled earlier */ - if (Swimming && (Underwater || !u.uinwater)) - you_can("swim", from_what(SWIMMING)); - if (Breathless) - you_can("survive without air", from_what(MAGICAL_BREATHING)); - else if (Amphibious) - you_can("breathe water", from_what(MAGICAL_BREATHING)); - if (Passes_walls) - you_can("walk through walls", from_what(PASSES_WALLS)); - - /*** Physical attributes ***/ - if (Regeneration) - enl_msg("You regenerate", "", "d", "", from_what(REGENERATION)); - if (Slow_digestion) - you_have("slower digestion", from_what(SLOW_DIGESTION)); - if (u.uhitinc) - you_have(enlght_combatinc("to hit", u.uhitinc, final, buf), ""); - if (u.udaminc) - you_have(enlght_combatinc("damage", u.udaminc, final, buf), ""); - if (u.uspellprot || Protection) { - int prot = 0; - - if (uleft && uleft->otyp == RIN_PROTECTION) - prot += uleft->spe; - if (uright && uright->otyp == RIN_PROTECTION) - prot += uright->spe; - if (HProtection & INTRINSIC) - prot += u.ublessed; - prot += u.uspellprot; - if (prot) - you_have(enlght_combatinc("defense", prot, final, buf), ""); - } - if ((armpro = magic_negation(&g.youmonst)) > 0) { - /* magic cancellation factor, conferred by worn armor */ - static const char *const mc_types[] = { - "" /*ordinary*/, "warded", "guarded", "protected", - }; - /* sanity check */ - if (armpro >= SIZE(mc_types)) - armpro = SIZE(mc_types) - 1; - you_are(mc_types[armpro], ""); - } - if (Half_physical_damage) - enlght_halfdmg(HALF_PHDAM, final); - if (Half_spell_damage) - enlght_halfdmg(HALF_SPDAM, final); - if (Half_gas_damage) - enl_msg(You_, "take", "took", " reduced poison gas damage", ""); - /* polymorph and other shape change */ - if (Protection_from_shape_changers) - you_are("protected from shape changers", - from_what(PROT_FROM_SHAPE_CHANGERS)); - if (Unchanging) { - const char *what = 0; - - if (!Upolyd) /* Upolyd handled below after current form */ - you_can("not change from your current form", - from_what(UNCHANGING)); - /* blocked shape changes */ - if (Polymorph) - what = !final ? "polymorph" : "have polymorphed"; - else if (u.ulycn >= LOW_PM) - what = !final ? "change shape" : "have changed shape"; - if (what) { - Sprintf(buf, "would %s periodically", what); - /* omit from_what(UNCHANGING); too verbose */ - enl_msg(You_, buf, buf, " if not locked into your current form", - ""); - } - } else if (Polymorph) { - you_are("polymorphing periodically", from_what(POLYMORPH)); - } - if (Polymorph_control) - you_have("polymorph control", from_what(POLYMORPH_CONTROL)); - if (Upolyd && u.umonnum != u.ulycn - /* if we've died from turning into slime, we're polymorphed - right now but don't want to list it as a temporary attribute - [we need a more reliable way to detect this situation] */ - && !(final == ENL_GAMEOVERDEAD - && u.umonnum == PM_GREEN_SLIME && !Unchanging)) { - /* foreign shape (except were-form which is handled below) */ - if (!vampshifted(&g.youmonst)) - Sprintf(buf, "polymorphed into %s", an(g.youmonst.data->mname)); - else - Sprintf(buf, "polymorphed into %s in %s form", - an(mons[g.youmonst.cham].mname), g.youmonst.data->mname); - if (wizard) - Sprintf(eos(buf), " (%d)", u.mtimedone); - you_are(buf, ""); - } - if (lays_eggs(g.youmonst.data) && flags.female) /* Upolyd */ - you_can("lay eggs", ""); - if (u.ulycn >= LOW_PM) { - /* "you are a werecreature [in beast form]" */ - Strcpy(buf, an(mons[u.ulycn].mname)); - if (u.umonnum == u.ulycn) { - Strcat(buf, " in beast form"); - if (wizard) - Sprintf(eos(buf), " (%d)", u.mtimedone); - } - you_are(buf, ""); - } - if (Unchanging && Upolyd) /* !Upolyd handled above */ - you_can("not change from your current form", from_what(UNCHANGING)); - if (Hate_silver) - you_are("harmed by silver", ""); - /* movement and non-armor-based protection */ - if (Fast) - you_are(Very_fast ? "very fast" : "fast", from_what(FAST)); - if (Reflecting) - you_have("reflection", from_what(REFLECTING)); - if (Free_action) - you_have("free action", from_what(FREE_ACTION)); - if (Fixed_abil) - you_have("fixed abilities", from_what(FIXED_ABIL)); - if (Lifesaved) - enl_msg("Your life ", "will be", "would have been", " saved", ""); - - /*** Miscellany ***/ - if (Luck) { - ltmp = abs((int) Luck); - Sprintf(buf, "%s%slucky", - ltmp >= 10 ? "extremely " : ltmp >= 5 ? "very " : "", - Luck < 0 ? "un" : ""); - if (wizard) - Sprintf(eos(buf), " (%d)", Luck); - you_are(buf, ""); - } else if (wizard) - enl_msg("Your luck ", "is", "was", " zero", ""); - if (u.moreluck > 0) - you_have("extra luck", ""); - else if (u.moreluck < 0) - you_have("reduced luck", ""); - if (carrying(LUCKSTONE) || stone_luck(TRUE)) { - ltmp = stone_luck(FALSE); - if (ltmp <= 0) - enl_msg("Bad luck ", "does", "did", " not time out for you", ""); - if (ltmp >= 0) - enl_msg("Good luck ", "does", "did", " not time out for you", ""); - } - - if (u.ugangr) { - Sprintf(buf, " %sangry with you", - u.ugangr > 6 ? "extremely " : u.ugangr > 3 ? "very " : ""); - if (wizard) - Sprintf(eos(buf), " (%d)", u.ugangr); - enl_msg(u_gname(), " is", " was", buf, ""); - } else { - /* - * We need to suppress this when the game is over, because death - * can change the value calculated by can_pray(), potentially - * resulting in a false claim that you could have prayed safely. - */ - if (!final) { -#if 0 - /* "can [not] safely pray" vs "could [not] have safely prayed" */ - Sprintf(buf, "%s%ssafely pray%s", can_pray(FALSE) ? "" : "not ", - final ? "have " : "", final ? "ed" : ""); -#else - Sprintf(buf, "%ssafely pray", can_pray(FALSE) ? "" : "not "); -#endif - if (wizard) - Sprintf(eos(buf), " (%d)", u.ublesscnt); - you_can(buf, ""); - } - } - -#ifdef DEBUG - /* named fruit debugging (doesn't really belong here...); to enable, - include 'fruit' in DEBUGFILES list (even though it isn't a file...) */ - if (wizard && explicitdebug("fruit")) { - struct fruit *f; - - reorder_fruit(TRUE); /* sort by fruit index, from low to high; - * this modifies the g.ffruit chain, so could - * possibly mask or even introduce a problem, - * but it does useful sanity checking */ - for (f = g.ffruit; f; f = f->nextf) { - Sprintf(buf, "Fruit #%d ", f->fid); - enl_msg(buf, "is ", "was ", f->fname, ""); - } - enl_msg("The current fruit ", "is ", "was ", g.pl_fruit, ""); - Sprintf(buf, "%d", flags.made_fruit); - enl_msg("The made fruit flag ", "is ", "was ", buf, ""); - } -#endif - - { - const char *p; - - buf[0] = '\0'; - if (final < 2) { /* still in progress, or quit/escaped/ascended */ - p = "survived after being killed "; - switch (u.umortality) { - case 0: - p = !final ? (char *) 0 : "survived"; - break; - case 1: - Strcpy(buf, "once"); - break; - case 2: - Strcpy(buf, "twice"); - break; - case 3: - Strcpy(buf, "thrice"); - break; - default: - Sprintf(buf, "%d times", u.umortality); - break; - } - } else { /* game ended in character's death */ - p = "are dead"; - switch (u.umortality) { - case 0: - impossible("dead without dying?"); - case 1: - break; /* just "are dead" */ - default: - Sprintf(buf, " (%d%s time!)", u.umortality, - ordin(u.umortality)); - break; - } - } - if (p) - enl_msg(You_, "have been killed ", p, buf, ""); - } -} - -/* ^X command */ -static int -doattributes(VOID_ARGS) -{ - int mode = BASICENLIGHTENMENT; - - /* show more--as if final disclosure--for wizard and explore modes */ - if (wizard || discover) - mode |= MAGICENLIGHTENMENT; - - enlightenment(mode, ENL_GAMEINPROGRESS); - return 0; -} - -void -youhiding(via_enlghtmt, msgflag) -boolean via_enlghtmt; /* englightment line vs topl message */ -int msgflag; /* for variant message phrasing */ -{ - char *bp, buf[BUFSZ]; - - Strcpy(buf, "hiding"); - if (U_AP_TYPE != M_AP_NOTHING) { - /* mimic; hero is only able to mimic a strange object or gold - or hallucinatory alternative to gold, so we skip the details - for the hypothetical furniture and monster cases */ - bp = eos(strcpy(buf, "mimicking")); - if (U_AP_TYPE == M_AP_OBJECT) { - Sprintf(bp, " %s", an(simple_typename(g.youmonst.mappearance))); - } else if (U_AP_TYPE == M_AP_FURNITURE) { - Strcpy(bp, " something"); - } else if (U_AP_TYPE == M_AP_MONSTER) { - Strcpy(bp, " someone"); - } else { - ; /* something unexpected; leave 'buf' as-is */ - } - } else if (u.uundetected) { - bp = eos(buf); /* points past "hiding" */ - if (g.youmonst.data->mlet == S_EEL) { - if (is_pool(u.ux, u.uy)) - Sprintf(bp, " in the %s", waterbody_name(u.ux, u.uy)); - } else if (hides_under(g.youmonst.data)) { - struct obj *o = g.level.objects[u.ux][u.uy]; - - if (o) - Sprintf(bp, " underneath %s", ansimpleoname(o)); - } else if (is_clinger(g.youmonst.data) || Flying) { - /* Flying: 'lurker above' hides on ceiling but doesn't cling */ - Sprintf(bp, " on the %s", ceiling(u.ux, u.uy)); - } else { - /* on floor; is_hider() but otherwise not special: 'trapper' */ - if (u.utrap && u.utraptype == TT_PIT) { - struct trap *t = t_at(u.ux, u.uy); - - Sprintf(bp, " in a %spit", - (t && t->ttyp == SPIKED_PIT) ? "spiked " : ""); - } else - Sprintf(bp, " on the %s", surface(u.ux, u.uy)); - } - } else { - ; /* shouldn't happen; will result in generic "you are hiding" */ - } - - if (via_enlghtmt) { - int final = msgflag; /* 'final' is used by you_are() macro */ - - you_are(buf, ""); - } else { - /* for dohide(), when player uses '#monster' command */ - You("are %s %s.", msgflag ? "already" : "now", buf); - } -} - -/* KMH, #conduct - * (shares enlightenment's tense handling) - */ -int -doconduct(VOID_ARGS) -{ - show_conduct(0); - return 0; -} - -void -show_conduct(final) -int final; -{ - char buf[BUFSZ]; - int ngenocided; - - /* Create the conduct window */ - g.en_win = create_nhwindow(NHW_MENU); - putstr(g.en_win, 0, "Voluntary challenges:"); - - if (u.uroleplay.blind) - you_have_been("blind from birth"); - if (u.uroleplay.nudist) - you_have_been("faithfully nudist"); - - if (!u.uconduct.food) - enl_msg(You_, "have gone", "went", " without food", ""); - /* but beverages are okay */ - else if (!u.uconduct.unvegan) - you_have_X("followed a strict vegan diet"); - else if (!u.uconduct.unvegetarian) - you_have_been("vegetarian"); - - if (!u.uconduct.gnostic) - you_have_been("an atheist"); - - if (!u.uconduct.weaphit) { - you_have_never("hit with a wielded weapon"); - } else if (wizard) { - Sprintf(buf, "used a wielded weapon %ld time%s", u.uconduct.weaphit, - plur(u.uconduct.weaphit)); - you_have_X(buf); - } - if (!u.uconduct.killer) - you_have_been("a pacifist"); - - if (!u.uconduct.literate) { - you_have_been("illiterate"); - } else if (wizard) { - Sprintf(buf, "read items or engraved %ld time%s", u.uconduct.literate, - plur(u.uconduct.literate)); - you_have_X(buf); - } - - ngenocided = num_genocides(); - if (ngenocided == 0) { - you_have_never("genocided any monsters"); - } else { - Sprintf(buf, "genocided %d type%s of monster%s", ngenocided, - plur(ngenocided), plur(ngenocided)); - you_have_X(buf); - } - - if (!u.uconduct.polypiles) { - you_have_never("polymorphed an object"); - } else if (wizard) { - Sprintf(buf, "polymorphed %ld item%s", u.uconduct.polypiles, - plur(u.uconduct.polypiles)); - you_have_X(buf); - } - - if (!u.uconduct.polyselfs) { - you_have_never("changed form"); - } else if (wizard) { - Sprintf(buf, "changed form %ld time%s", u.uconduct.polyselfs, - plur(u.uconduct.polyselfs)); - you_have_X(buf); - } - - if (!u.uconduct.wishes) { - you_have_X("used no wishes"); - } else { - Sprintf(buf, "used %ld wish%s", u.uconduct.wishes, - (u.uconduct.wishes > 1L) ? "es" : ""); - if (u.uconduct.wisharti) { - /* if wisharti == wishes - * 1 wish (for an artifact) - * 2 wishes (both for artifacts) - * N wishes (all for artifacts) - * else (N is at least 2 in order to get here; M < N) - * N wishes (1 for an artifact) - * N wishes (M for artifacts) - */ - if (u.uconduct.wisharti == u.uconduct.wishes) - Sprintf(eos(buf), " (%s", - (u.uconduct.wisharti > 2L) ? "all " - : (u.uconduct.wisharti == 2L) ? "both " : ""); - else - Sprintf(eos(buf), " (%ld ", u.uconduct.wisharti); - - Sprintf(eos(buf), "for %s)", - (u.uconduct.wisharti == 1L) ? "an artifact" - : "artifacts"); - } - you_have_X(buf); - - if (!u.uconduct.wisharti) - enl_msg(You_, "have not wished", "did not wish", - " for any artifacts", ""); - } - - show_achievements(final); - - /* Pop up the window and wait for a key */ - display_nhwindow(g.en_win, TRUE); - destroy_nhwindow(g.en_win); - g.en_win = WIN_ERR; -} - -static void -show_achievements(final) -int final; -{ - winid awin = WIN_ERR; - int acnt = 0; - - /* unfortunately we can't show the achievements (at least not all of - them) while the game is in progress because it would give away the - ID of luckstone (at Mine's End) and of real Amulet of Yendor */ - if (!final && !wizard) - return; - - if (g.en_win != WIN_ERR) { - awin = g.en_win; - putstr(awin, 0, ""); - } else { - awin = create_nhwindow(NHW_MENU); - } - putstr(awin, 0, "Achievements:"); - /* arranged in approximate order of difficulty */ - if (u.uachieve.mines_luckstone) - enl_msg(You_, "have ", "", - "completed the Gnomish Mines", ""), ++acnt; - if (u.uachieve.finish_sokoban) - enl_msg(You_, "have ", "", - "completed Sokoban", ""), ++acnt; - if (u.uachieve.killed_medusa) - enl_msg(You_, "have ", "", - "defeated Medusa", ""), ++acnt; - if (u.uachieve.bell) - enl_msg(You_, "have ", "", - "handled the Bell of Opening", ""), ++acnt; - if (u.uachieve.enter_gehennom) - enl_msg(You_, "have ", "", - "passed the Valley of the Dead", ""), ++acnt; - if (u.uachieve.menorah) - enl_msg(You_, "have ", "", - "handled the Candelabrum of Invocation", ""), ++acnt; - if (u.uachieve.book) - enl_msg(You_, "have ", "", - "handled the Book of the Dead", ""), ++acnt; - if (u.uevent.invoked) - enl_msg(You_, "have ", "", - "gained access to Moloch's Sanctum", ""), ++acnt; - if (u.uachieve.amulet) - enl_msg(You_, "have ", "", - "obtained the Amulet of Yendor", ""), ++acnt; - if (In_endgame(&u.uz)) - enl_msg(You_, "have ", "", - "reached the Elemental Planes", ""), ++acnt; - if (Is_astralevel(&u.uz)) - enl_msg(You_, "have ", "", - "reached the Astral Plane", ""), ++acnt; - if (u.uachieve.ascended) - enlght_out(" You ascended!"), ++acnt; - if (u.uroleplay.blind || u.uroleplay.nudist) { - if (acnt) - putstr(awin, 0, ""); - if (u.uroleplay.blind) - enl_msg(You_, "are exploring", "explored", - " without being able to see", ""), ++acnt; - if (u.uroleplay.nudist) - enl_msg(You_, "have gone", "went", - " without any armor", ""), ++acnt; - } - if (!acnt) - enlght_out(" []"); - - if (awin != g.en_win) { - display_nhwindow(awin, TRUE); - destroy_nhwindow(awin); - } -} - /* ordered by command name */ struct ext_func_tab extcmdlist[] = { { '#', "#", "perform an extended command", @@ -3868,7 +2109,7 @@ dokeylist(VOID_ARGS) /* directional keys */ putstr(datawin, 0, ""); putstr(datawin, 0, "Directional keys:"); - show_direction_keys(datawin, '.', FALSE); /* '.'==self in direction grid */ + show_direction_keys(datawin, '.', FALSE); /* '.'==self in direct'n grid */ keys_used[(uchar) g.Cmd.move_NW] = keys_used[(uchar) g.Cmd.move_N] = keys_used[(uchar) g.Cmd.move_NE] = keys_used[(uchar) g.Cmd.move_W] @@ -4126,7 +2367,7 @@ long *total_size; char buf[BUFSZ]; long count, size; struct monst *mon; - /* mon->wormno means something different for g.migrating_mons and g.mydogs */ + /* mon->wormno means something different for migrating_mons and mydogs */ boolean incl_wsegs = !strcmpi(src, "fmon"); count = size = 0L; @@ -4281,7 +2522,8 @@ wiz_show_stats() putstr(win, 0, stats_hdr); Sprintf(buf, " Objects, base size %ld", (long) sizeof (struct obj)); putstr(win, 0, buf); - obj_chain(win, "invent", g.invent, TRUE, &total_obj_count, &total_obj_size); + obj_chain(win, "invent", g.invent, TRUE, + &total_obj_count, &total_obj_size); obj_chain(win, "fobj", fobj, TRUE, &total_obj_count, &total_obj_size); obj_chain(win, "buried", g.level.buriedobjlist, FALSE, &total_obj_count, &total_obj_size); @@ -4595,10 +2837,10 @@ boolean initial; 'y', 'Y', C('y'), M('y'), M('Y'), M(C('y')) }; static struct ext_func_tab *back_dir_cmd[8]; + static boolean backed_dir_cmd = FALSE; const struct ext_func_tab *cmdtmp; boolean flagtemp; int c, i, updated = 0; - static boolean backed_dir_cmd = FALSE; if (initial) { updated = 1; @@ -4608,7 +2850,6 @@ boolean initial; g.Cmd.spkeys[spkeys_binds[i].nhkf] = spkeys_binds[i].key; commands_init(); } else { - if (backed_dir_cmd) { for (i = 0; i < 8; i++) { g.Cmd.commands[(uchar) g.Cmd.dirchars[i]] = back_dir_cmd[i]; @@ -4686,9 +2927,10 @@ boolean initial; if (!initial) { for (i = 0; i < 8; i++) { - back_dir_cmd[i] = - (struct ext_func_tab *) g.Cmd.commands[(uchar) g.Cmd.dirchars[i]]; - g.Cmd.commands[(uchar) g.Cmd.dirchars[i]] = (struct ext_func_tab *) 0; + uchar di = (uchar) g.Cmd.dirchars[i]; + + back_dir_cmd[i] = (struct ext_func_tab *) g.Cmd.commands[di]; + g.Cmd.commands[di] = (struct ext_func_tab *) 0; } backed_dir_cmd = TRUE; for (i = 0; i < 8; i++) @@ -5096,7 +3338,7 @@ dxdy_moveok() return u.dx || u.dy; } -/* decide whether a character (user input keystroke) requests screen repaint */ +/* decide whether character (user input keystroke) requests screen repaint */ boolean redraw_cmd(c) char c; @@ -5515,7 +3757,8 @@ int x, y; if ((ttmp = t_at(x, y)) != 0 && ttmp->tseen) { add_herecmd_menuitem(win, doidtrap, "Examine trap"), ++K; if (ttmp->ttyp != VIBRATING_SQUARE) - add_herecmd_menuitem(win, dountrap, "Attempt to disarm trap"), ++K; + add_herecmd_menuitem(win, dountrap, + "Attempt to disarm trap"), ++K; } mtmp = m_at(x, y); @@ -5675,9 +3918,11 @@ const char * click_to_cmd(x, y, mod) int x, y, mod; { - int dir; static char cmd[4]; - cmd[1] = 0; + struct obj *o; + int dir; + + cmd[1] = '\0'; if (iflags.clicklook && mod == CLICK_2) { g.clicklook_cc.x = x; @@ -5725,9 +3970,8 @@ int x, y, mod; || (u.ux == xdnladder && u.uy == ydnladder)) { cmd[0] = cmd_from_func(dodown); return cmd; - } else if (OBJ_AT(u.ux, u.uy)) { - cmd[0] = cmd_from_func(Is_container(g.level.objects[u.ux][u.uy]) - ? doloot : dopickup); + } else if ((o = vobj_at(u.ux, u.uy)) != 0) { + cmd[0] = cmd_from_func(Is_container(o) ? doloot : dopickup); return cmd; } else { cmd[0] = cmd_from_func(donull); /* just rest */ @@ -5751,8 +3995,7 @@ int x, y, mod; } if (IS_DOOR(levl[u.ux + x][u.uy + y].typ)) { - /* slight assistance to the player: choose kick/open for them - */ + /* slight assistance to player: choose kick/open for them */ if (levl[u.ux + x][u.uy + y].doormask & D_LOCKED) { cmd[0] = cmd_from_func(dokick); return cmd; diff --git a/src/end.c b/src/end.c index 0f601cc99..f10e0f424 100644 --- a/src/end.c +++ b/src/end.c @@ -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) diff --git a/src/insight.c b/src/insight.c index 2d0b43cdd..62ee93607 100644 --- a/src/insight.c +++ b/src/insight.c @@ -2,9 +2,2444 @@ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ +/* + * Enlightenment and Conduct+Achievements and Vanquished+Extinct+Geno'd + * and stethoscope/probing feedback. + * + * Most code used to reside in cmd.c, presumeably because ^X was originally + * a wizard mode command and the majority of those are in that file. + * Some came from end.c where it is used during end of game disclosure. + * And some came from priest.c that had once been in pline.c. + */ + #include "hack.h" -/* temporary, until Makefiles and IDE project files are all updated */ -int insight_dummy = 0; +static void FDECL(enlght_out, (const char *)); +static void FDECL(enlght_line, (const char *, const char *, const char *, + const char *)); +static char *FDECL(enlght_combatinc, (const char *, int, int, char *)); +static void FDECL(enlght_halfdmg, (int, int)); +static boolean NDECL(walking_on_water); +static boolean FDECL(cause_known, (int)); +static char *FDECL(attrval, (int, int, char *)); +static void FDECL(background_enlightenment, (int, int)); +static void FDECL(basics_enlightenment, (int, int)); +static void FDECL(characteristics_enlightenment, (int, int)); +static void FDECL(one_characteristic, (int, int, int)); +static void FDECL(status_enlightenment, (int, int)); +static void FDECL(attributes_enlightenment, (int, int)); +static void FDECL(show_achievements, (int)); +static int FDECL(CFDECLSPEC vanqsort_cmp, (const genericptr, + const genericptr)); +static int NDECL(set_vanq_order); +static int NDECL(num_extinct); + +extern const char *hu_stat[]; /* hunger status from eat.c */ +extern const char *enc_stat[]; /* encumbrance status from botl.c */ + +static const char You_[] = "You ", are[] = "are ", were[] = "were ", + have[] = "have ", had[] = "had ", can[] = "can ", + could[] = "could "; +static const char have_been[] = "have been ", have_never[] = "have never ", + never[] = "never "; + +#define enl_msg(prefix, present, past, suffix, ps) \ + enlght_line(prefix, final ? past : present, suffix, ps) +#define you_are(attr, ps) enl_msg(You_, are, were, attr, ps) +#define you_have(attr, ps) enl_msg(You_, have, had, attr, ps) +#define you_can(attr, ps) enl_msg(You_, can, could, attr, ps) +#define you_have_been(goodthing) enl_msg(You_, have_been, were, goodthing, "") +#define you_have_never(badthing) \ + enl_msg(You_, have_never, never, badthing, "") +#define you_have_X(something) \ + enl_msg(You_, have, (const char *) "", something, "") + +static void +enlght_out(buf) +const char *buf; +{ + if (g.en_via_menu) { + anything any; + + any = cg.zeroany; + add_menu(g.en_win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, + MENU_ITEMFLAGS_NONE); + } else + putstr(g.en_win, 0, buf); +} + +static void +enlght_line(start, middle, end, ps) +const char *start, *middle, *end, *ps; +{ + char buf[BUFSZ]; + + Sprintf(buf, " %s%s%s%s.", start, middle, end, ps); + enlght_out(buf); +} + +/* format increased chance to hit or damage or defense (Protection) */ +static char * +enlght_combatinc(inctyp, incamt, final, outbuf) +const char *inctyp; +int incamt, final; +char *outbuf; +{ + const char *modif, *bonus; + boolean invrt; + int absamt; + + absamt = abs(incamt); + /* Protection amount is typically larger than damage or to-hit; + reduce magnitude by a third in order to stretch modifier ranges + (small:1..5, moderate:6..10, large:11..19, huge:20+) */ + if (!strcmp(inctyp, "defense")) + absamt = (absamt * 2) / 3; + + if (absamt <= 3) + modif = "small"; + else if (absamt <= 6) + modif = "moderate"; + else if (absamt <= 12) + modif = "large"; + else + modif = "huge"; + + modif = !incamt ? "no" : an(modif); /* ("no" case shouldn't happen) */ + bonus = (incamt >= 0) ? "bonus" : "penalty"; + /* "bonus " (to hit) vs " bonus" (damage, defense) */ + invrt = strcmp(inctyp, "to hit") ? TRUE : FALSE; + + Sprintf(outbuf, "%s %s %s", modif, invrt ? inctyp : bonus, + invrt ? bonus : inctyp); + if (final || wizard) + Sprintf(eos(outbuf), " (%s%d)", (incamt > 0) ? "+" : "", incamt); + + return outbuf; +} + +/* report half physical or half spell damage */ +static void +enlght_halfdmg(category, final) +int category; +int final; +{ + const char *category_name; + char buf[BUFSZ]; + + switch (category) { + case HALF_PHDAM: + category_name = "physical"; + break; + case HALF_SPDAM: + category_name = "spell"; + break; + default: + category_name = "unknown"; + break; + } + Sprintf(buf, " %s %s damage", (final || wizard) ? "half" : "reduced", + category_name); + enl_msg(You_, "take", "took", buf, from_what(category)); +} + +/* is hero actively using water walking capability on water (or lava)? */ +static boolean +walking_on_water() +{ + if (u.uinwater || Levitation || Flying) + return FALSE; + return (boolean) (Wwalking + && (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy))); +} + +/* describe u.utraptype; used by status_enlightenment() and self_lookat() */ +char * +trap_predicament(outbuf, final, wizxtra) +char *outbuf; +int final; +boolean wizxtra; +{ + struct trap *t; + + /* caller has verified u.utrap */ + *outbuf = '\0'; + switch (u.utraptype) { + case TT_BURIEDBALL: + Strcpy(outbuf, "tethered to something buried"); + break; + case TT_LAVA: + Sprintf(outbuf, "sinking into %s", final ? "lava" : hliquid("lava")); + break; + case TT_INFLOOR: + Sprintf(outbuf, "stuck in %s", the(surface(u.ux, u.uy))); + break; + default: /* TT_BEARTRAP, TT_PIT, or TT_WEB */ + Strcpy(outbuf, "trapped"); + if ((t = t_at(u.ux, u.uy)) != 0) /* should never be null */ + Sprintf(eos(outbuf), " in %s", an(trapname(t->ttyp, FALSE))); + break; + } + if (wizxtra) { /* give extra information for wizard mode enlightenment */ + /* curly braces: u.utrap is an escape attempt counter rather than a + turn timer so use different ornamentation than usual parentheses */ + Sprintf(eos(outbuf), " {%u}", u.utrap); + } + return outbuf; +} + +/* check whether hero is wearing something that player definitely knows + confers the target property; item must have been seen and its type + discovered but it doesn't necessarily have to be fully identified */ +static boolean +cause_known(propindx) +int propindx; /* index of a property which can be conveyed by worn item */ +{ + register struct obj *o; + long mask = W_ARMOR | W_AMUL | W_RING | W_TOOL; + + /* simpler than from_what()/what_gives(); we don't attempt to + handle artifacts and we deliberately ignore wielded items */ + for (o = g.invent; o; o = o->nobj) { + if (!(o->owornmask & mask)) + continue; + if ((int) objects[o->otyp].oc_oprop == propindx + && objects[o->otyp].oc_name_known && o->dknown) + return TRUE; + } + return FALSE; +} + +/* format a characteristic value, accommodating Strength's strangeness */ +static char * +attrval(attrindx, attrvalue, resultbuf) +int attrindx, attrvalue; +char resultbuf[]; /* should be at least [7] to hold "18/100\0" */ +{ + if (attrindx != A_STR || attrvalue <= 18) + Sprintf(resultbuf, "%d", attrvalue); + else if (attrvalue > STR18(100)) /* 19 to 25 */ + Sprintf(resultbuf, "%d", attrvalue - 100); + else /* simplify "18/ **" to be "18/100" */ + Sprintf(resultbuf, "18/%02d", attrvalue - 18); + return resultbuf; +} + +void +enlightenment(mode, final) +int mode; /* BASICENLIGHTENMENT | MAGICENLIGHTENMENT (| both) */ +int final; /* ENL_GAMEINPROGRESS:0, ENL_GAMEOVERALIVE, ENL_GAMEOVERDEAD */ +{ + char buf[BUFSZ], tmpbuf[BUFSZ]; + + g.en_win = create_nhwindow(NHW_MENU); + g.en_via_menu = !final; + if (g.en_via_menu) + start_menu(g.en_win); + + Strcpy(tmpbuf, g.plname); + *tmpbuf = highc(*tmpbuf); /* same adjustment as bottom line */ + /* as in background_enlightenment, when poly'd we need to use the saved + gender in u.mfemale rather than the current you-as-monster gender */ + Sprintf(buf, "%s the %s's attributes:", tmpbuf, + ((Upolyd ? u.mfemale : flags.female) && g.urole.name.f) + ? g.urole.name.f + : g.urole.name.m); + + /* title */ + enlght_out(buf); /* "Conan the Archeologist's attributes:" */ + /* background and characteristics; ^X or end-of-game disclosure */ + if (mode & BASICENLIGHTENMENT) { + /* role, race, alignment, deities, dungeon level, time, experience */ + background_enlightenment(mode, final); + /* hit points, energy points, armor class, gold */ + basics_enlightenment(mode, final); + /* strength, dexterity, &c */ + characteristics_enlightenment(mode, final); + } + /* expanded status line information, including things which aren't + included there due to space considerations--such as obvious + alternative movement indicators (riding, levitation, &c), and + various troubles (turning to stone, trapped, confusion, &c); + shown for both basic and magic enlightenment */ + status_enlightenment(mode, final); + /* remaining attributes; shown for potion,&c or wizard mode and + explore mode ^X or end of game disclosure */ + if (mode & MAGICENLIGHTENMENT) { + /* intrinsics and other traditional enlightenment feedback */ + attributes_enlightenment(mode, final); + } + + if (!g.en_via_menu) { + display_nhwindow(g.en_win, TRUE); + } else { + menu_item *selected = 0; + + end_menu(g.en_win, (char *) 0); + if (select_menu(g.en_win, PICK_NONE, &selected) > 0) + free((genericptr_t) selected); + g.en_via_menu = FALSE; + } + destroy_nhwindow(g.en_win); + g.en_win = WIN_ERR; +} + +/*ARGSUSED*/ +/* display role, race, alignment and such to en_win */ +static void +background_enlightenment(unused_mode, final) +int unused_mode UNUSED; +int final; +{ + const char *role_titl, *rank_titl; + int innategend, difgend, difalgn; + char buf[BUFSZ], tmpbuf[BUFSZ]; + + /* note that if poly'd, we need to use u.mfemale instead of flags.female + to access hero's saved gender-as-human/elf/&c rather than current */ + innategend = (Upolyd ? u.mfemale : flags.female) ? 1 : 0; + role_titl = (innategend && g.urole.name.f) ? g.urole.name.f + : g.urole.name.m; + rank_titl = rank_of(u.ulevel, Role_switch, innategend); + + enlght_out(""); /* separator after title */ + enlght_out("Background:"); + + /* if polymorphed, report current shape before underlying role; + will be repeated as first status: "you are transformed" and also + among various attributes: "you are in beast form" (after being + told about lycanthropy) or "you are polymorphed into " + (with countdown timer appended for wizard mode); we really want + the player to know he's not a samurai at the moment... */ + if (Upolyd) { + char anbuf[20]; /* includes trailing space; [4] suffices */ + struct permonst *uasmon = g.youmonst.data; + boolean altphrasing = vampshifted(&g.youmonst); + + tmpbuf[0] = '\0'; + /* here we always use current gender, not saved role gender */ + if (!is_male(uasmon) && !is_female(uasmon) && !is_neuter(uasmon)) + Sprintf(tmpbuf, "%s ", genders[flags.female ? 1 : 0].adj); + if (altphrasing) + Sprintf(eos(tmpbuf), "%s in ", mons[g.youmonst.cham].mname); + Sprintf(buf, "%s%s%s%s form", !final ? "currently " : "", + altphrasing ? just_an(anbuf, tmpbuf) : "in ", + tmpbuf, uasmon->mname); + you_are(buf, ""); + } + + /* report role; omit gender if it's redundant (eg, "female priestess") */ + tmpbuf[0] = '\0'; + if (!g.urole.name.f + && ((g.urole.allow & ROLE_GENDMASK) == (ROLE_MALE | ROLE_FEMALE) + || innategend != flags.initgend)) + Sprintf(tmpbuf, "%s ", genders[innategend].adj); + buf[0] = '\0'; + if (Upolyd) + Strcpy(buf, "actually "); /* "You are actually a ..." */ + if (!strcmpi(rank_titl, role_titl)) { + /* omit role when rank title matches it */ + Sprintf(eos(buf), "%s, level %d %s%s", an(rank_titl), u.ulevel, + tmpbuf, g.urace.noun); + } else { + Sprintf(eos(buf), "%s, a level %d %s%s %s", an(rank_titl), u.ulevel, + tmpbuf, g.urace.adj, role_titl); + } + you_are(buf, ""); + + /* report alignment (bypass you_are() in order to omit ending period); + adverb is used to distinguish between temporary change (helm of opp. + alignment), permanent change (one-time conversion), and original */ + Sprintf(buf, " %s%s%s, %son a mission for %s", + You_, !final ? are : were, + align_str(u.ualign.type), + /* helm of opposite alignment (might hide conversion) */ + (u.ualign.type != u.ualignbase[A_CURRENT]) + /* what's the past tense of "currently"? if we used "formerly" + it would sound like a reference to the original alignment */ + ? (!final ? "currently " : "temporarily ") + /* permanent conversion */ + : (u.ualign.type != u.ualignbase[A_ORIGINAL]) + /* and what's the past tense of "now"? certainly not "then" + in a context like this...; "belatedly" == weren't that + way sooner (in other words, didn't start that way) */ + ? (!final ? "now " : "belatedly ") + /* atheist (ignored in very early game) */ + : (!u.uconduct.gnostic && g.moves > 1000L) + ? "nominally " + /* lastly, normal case */ + : "", + u_gname()); + enlght_out(buf); + /* show the rest of this game's pantheon (finishes previous sentence) + [appending "also Moloch" at the end would allow for straightforward + trailing "and" on all three aligned entries but looks too verbose] */ + Sprintf(buf, " who %s opposed by", !final ? "is" : "was"); + if (u.ualign.type != A_LAWFUL) + Sprintf(eos(buf), " %s (%s) and", align_gname(A_LAWFUL), + align_str(A_LAWFUL)); + if (u.ualign.type != A_NEUTRAL) + Sprintf(eos(buf), " %s (%s)%s", align_gname(A_NEUTRAL), + align_str(A_NEUTRAL), + (u.ualign.type != A_CHAOTIC) ? " and" : ""); + if (u.ualign.type != A_CHAOTIC) + Sprintf(eos(buf), " %s (%s)", align_gname(A_CHAOTIC), + align_str(A_CHAOTIC)); + Strcat(buf, "."); /* terminate sentence */ + enlght_out(buf); + + /* show original alignment,gender,race,role if any have been changed; + giving separate message for temporary alignment change bypasses need + for tricky phrasing otherwise necessitated by possibility of having + helm of opposite alignment mask a permanent alignment conversion */ + difgend = (innategend != flags.initgend); + difalgn = (((u.ualign.type != u.ualignbase[A_CURRENT]) ? 1 : 0) + + ((u.ualignbase[A_CURRENT] != u.ualignbase[A_ORIGINAL]) + ? 2 : 0)); + if (difalgn & 1) { /* have temporary alignment so report permanent one */ + Sprintf(buf, "actually %s", align_str(u.ualignbase[A_CURRENT])); + you_are(buf, ""); + difalgn &= ~1; /* suppress helm from "started out " message */ + } + if (difgend || difalgn) { /* sex change or perm align change or both */ + Sprintf(buf, " You started out %s%s%s.", + difgend ? genders[flags.initgend].adj : "", + (difgend && difalgn) ? " and " : "", + difalgn ? align_str(u.ualignbase[A_ORIGINAL]) : ""); + enlght_out(buf); + } + + /* As of 3.6.2: dungeon level, so that ^X really has all status info as + claimed by the comment below; this reveals more information than + the basic status display, but that's one of the purposes of ^X; + similar information is revealed by #overview; the "You died in + " given by really_done() is more rudimentary than this */ + *buf = *tmpbuf = '\0'; + if (In_endgame(&u.uz)) { + int egdepth = observable_depth(&u.uz); + + (void) endgamelevelname(tmpbuf, egdepth); + Sprintf(buf, "in the endgame, on the %s%s", + !strncmp(tmpbuf, "Plane", 5) ? "Elemental " : "", tmpbuf); + } else if (Is_knox(&u.uz)) { + /* this gives away the fact that the knox branch is only 1 level */ + Sprintf(buf, "on the %s level", g.dungeons[u.uz.dnum].dname); + /* TODO? maybe phrase it differently when actually inside the fort, + if we're able to determine that (not trivial) */ + } else { + char dgnbuf[QBUFSZ]; + + Strcpy(dgnbuf, g.dungeons[u.uz.dnum].dname); + if (!strncmpi(dgnbuf, "The ", 4)) + *dgnbuf = lowc(*dgnbuf); + Sprintf(tmpbuf, "level %d", + In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz)); + /* TODO? maybe extend this bit to include various other automatic + annotations from the dungeon overview code */ + if (Is_rogue_level(&u.uz)) + Strcat(tmpbuf, ", a primitive area"); + else if (Is_bigroom(&u.uz) && !Blind) + Strcat(tmpbuf, ", a very big room"); + Sprintf(buf, "in %s, on %s", dgnbuf, tmpbuf); + } + you_are(buf, ""); + + /* this is shown even if the 'time' option is off */ + if (g.moves == 1L) { + you_have("just started your adventure", ""); + } else { + /* 'turns' grates on the nerves in this context... */ + Sprintf(buf, "the dungeon %ld turn%s ago", g.moves, plur(g.moves)); + /* same phrasing for current and final: "entered" is unconditional */ + enlght_line(You_, "entered ", buf, ""); + } + + /* for gameover, these have been obtained in really_done() so that they + won't vary if user leaves a disclosure prompt or --More-- unanswered + long enough for the dynamic value to change between then and now */ + if (final ? iflags.at_midnight : midnight()) { + enl_msg("It ", "is ", "was ", "the midnight hour", ""); + } else if (final ? iflags.at_night : night()) { + enl_msg("It ", "is ", "was ", "nighttime", ""); + } + /* other environmental factors */ + if (flags.moonphase == FULL_MOON || flags.moonphase == NEW_MOON) { + /* [This had "tonight" but has been changed to "in effect". + There is a similar issue to Friday the 13th--it's the value + at the start of the current session but that session might + have dragged on for an arbitrary amount of time. We want to + report the values that currently affect play--or affected + play when game ended--rather than actual outside situation.] */ + Sprintf(buf, "a %s moon in effect%s", + (flags.moonphase == FULL_MOON) ? "full" + : (flags.moonphase == NEW_MOON) ? "new" + /* showing these would probably just lead to confusion + since they have no effect on game play... */ + : (flags.moonphase < FULL_MOON) ? "first quarter" + : "last quarter", + /* we don't have access to 'how' here--aside from survived + vs died--so settle for general platitude */ + final ? " when your adventure ended" : ""); + enl_msg("There ", "is ", "was ", buf, ""); + } + if (flags.friday13) { + /* let player know that friday13 penalty is/was in effect; + we don't say "it is/was Friday the 13th" because that was at + the start of the session and it might be past midnight (or + days later if the game has been paused without save/restore), + so phrase this similar to the start up message */ + Sprintf(buf, " Bad things %s on Friday the 13th.", + !final ? "can happen" + : (final == ENL_GAMEOVERALIVE) ? "could have happened" + /* there's no may to tell whether -1 Luck made a + difference but hero has died... */ + : "happened"); + enlght_out(buf); + } + + if (!Upolyd) { + int ulvl = (int) u.ulevel; + /* [flags.showexp currently does not matter; should it?] */ + + /* experience level is already shown above */ + Sprintf(buf, "%-1ld experience point%s", u.uexp, plur(u.uexp)); + /* TODO? + * Remove wizard-mode restriction since patient players can + * determine the numbers needed without resorting to spoilers + * (even before this started being disclosed for 'final'; + * just enable 'showexp' and look at normal status lines + * after drinking gain level potions or eating wraith corpses + * or being level-drained by vampires). + */ + if (ulvl < 30 && (final || wizard)) { + long nxtlvl = newuexp(ulvl), delta = nxtlvl - u.uexp; + + Sprintf(eos(buf), ", %ld %s%sneeded %s level %d", + delta, (u.uexp > 0) ? "more " : "", + /* present tense=="needed", past tense=="were needed" */ + !final ? "" : (delta == 1L) ? "was " : "were ", + /* "for": grammatically iffy but less likely to wrap */ + (ulvl < 18) ? "to attain" : "for", (ulvl + 1)); + } + you_have(buf, ""); + } +#ifdef SCORE_ON_BOTL + if (flags.showscore) { + /* describes what's shown on status line, which is an approximation; + only show it here if player has the 'showscore' option enabled */ + Sprintf(buf, "%ld%s", botl_score(), + !final ? "" : " before end-of-game adjustments"); + enl_msg("Your score ", "is ", "was ", buf, ""); + } +#endif +} + +/* hit points, energy points, armor class -- essential information which + doesn't fit very well in other categories */ +/*ARGSUSED*/ +static void +basics_enlightenment(mode, final) +int mode UNUSED; +int final; +{ + static char Power[] = "energy points (spell power)"; + char buf[BUFSZ]; + int pw = u.uen, hp = (Upolyd ? u.mh : u.uhp), + pwmax = u.uenmax, hpmax = (Upolyd ? u.mhmax : u.uhpmax); + + enlght_out(""); /* separator after background */ + enlght_out("Basics:"); + + if (hp < 0) + hp = 0; + /* "1 out of 1" rather than "all" if max is only 1; should never happen */ + if (hp == hpmax && hpmax > 1) + Sprintf(buf, "all %d hit points", hpmax); + else + Sprintf(buf, "%d out of %d hit point%s", hp, hpmax, plur(hpmax)); + you_have(buf, ""); + + /* low max energy is feasible, so handle couple of extra special cases */ + if (pwmax == 0 || (pw == pwmax && pwmax == 2)) /* both: not "all 2" */ + Sprintf(buf, "%s %s", !pwmax ? "no" : "both", Power); + else if (pw == pwmax && pwmax > 2) + Sprintf(buf, "all %d %s", pwmax, Power); + else + Sprintf(buf, "%d out of %d %s", pw, pwmax, Power); + you_have(buf, ""); + + if (Upolyd) { + switch (mons[u.umonnum].mlevel) { + case 0: + /* status line currently being explained shows "HD:0" */ + Strcpy(buf, "0 hit dice (actually 1/2)"); + break; + case 1: + Strcpy(buf, "1 hit die"); + break; + default: + Sprintf(buf, "%d hit dice", mons[u.umonnum].mlevel); + break; + } + you_have(buf, ""); + } + + Sprintf(buf, "%d", u.uac); + enl_msg("Your armor class ", "is ", "was ", buf, ""); + + /* gold; similar to doprgold(#seegold) but without shop billing info; + same amount as shown on status line which ignores container contents */ + { + static const char Your_wallet[] = "Your wallet "; + long umoney = money_cnt(g.invent); + + if (!umoney) { + enl_msg(Your_wallet, "is ", "was ", "empty", ""); + } else { + Sprintf(buf, "%ld %s", umoney, currency(umoney)); + enl_msg(Your_wallet, "contains ", "contained ", buf, ""); + } + } + + if (flags.pickup) { + char ocl[MAXOCLASSES + 1]; + + Strcpy(buf, "on"); + oc_to_str(flags.pickup_types, ocl); + Sprintf(eos(buf), " for %s%s%s", + *ocl ? "'" : "", *ocl ? ocl : "all types", *ocl ? "'" : ""); + if (flags.pickup_thrown && *ocl) /* *ocl: don't show if 'all types' */ + Strcat(buf, " plus thrown"); + if (g.apelist) + Strcat(buf, ", with exceptions"); + } else + Strcpy(buf, "off"); + enl_msg("Autopickup ", "is ", "was ", buf, ""); +} + +/* characteristics: expanded version of bottom line strength, dexterity, &c */ +static void +characteristics_enlightenment(mode, final) +int mode; +int final; +{ + char buf[BUFSZ]; + + enlght_out(""); + Sprintf(buf, "%s Characteristics:", !final ? "Current" : "Final"); + enlght_out(buf); + + /* bottom line order */ + one_characteristic(mode, final, A_STR); /* strength */ + one_characteristic(mode, final, A_DEX); /* dexterity */ + one_characteristic(mode, final, A_CON); /* constitution */ + one_characteristic(mode, final, A_INT); /* intelligence */ + one_characteristic(mode, final, A_WIS); /* wisdom */ + one_characteristic(mode, final, A_CHA); /* charisma */ +} + +/* display one attribute value for characteristics_enlightenment() */ +static void +one_characteristic(mode, final, attrindx) +int mode, final, attrindx; +{ + extern const char *const attrname[]; /* attrib.c */ + boolean hide_innate_value = FALSE, interesting_alimit; + int acurrent, abase, apeak, alimit; + const char *paren_pfx; + char subjbuf[BUFSZ], valubuf[BUFSZ], valstring[32]; + + /* being polymorphed or wearing certain cursed items prevents + hero from reliably tracking changes to characteristics so + we don't show base & peak values then; when the items aren't + cursed, hero could take them off to check underlying values + and we show those in such case so that player doesn't need + to actually resort to doing that */ + if (Upolyd) { + hide_innate_value = TRUE; + } else if (Fixed_abil) { + if (stuck_ring(uleft, RIN_SUSTAIN_ABILITY) + || stuck_ring(uright, RIN_SUSTAIN_ABILITY)) + hide_innate_value = TRUE; + } + switch (attrindx) { + case A_STR: + if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER && uarmg->cursed) + hide_innate_value = TRUE; + break; + case A_DEX: + break; + case A_CON: + if (uwep && uwep->oartifact == ART_OGRESMASHER && uwep->cursed) + hide_innate_value = TRUE; + break; + case A_INT: + if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed) + hide_innate_value = TRUE; + break; + case A_WIS: + if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed) + hide_innate_value = TRUE; + break; + case A_CHA: + break; + default: + return; /* impossible */ + }; + /* note: final disclosure includes MAGICENLIGHTENTMENT */ + if ((mode & MAGICENLIGHTENMENT) && !Upolyd) + hide_innate_value = FALSE; + + acurrent = ACURR(attrindx); + (void) attrval(attrindx, acurrent, valubuf); /* Sprintf(valubuf,"%d",) */ + Sprintf(subjbuf, "Your %s ", attrname[attrindx]); + + if (!hide_innate_value) { + /* show abase, amax, and/or attrmax if acurr doesn't match abase + (a magic bonus or penalty is in effect) or abase doesn't match + amax (some points have been lost to poison or exercise abuse + and are restorable) or attrmax is different from normal human + (while game is in progress; trying to reduce dependency on + spoilers to keep track of such stuff) or attrmax was different + from abase (at end of game; this attribute wasn't maxed out) */ + abase = ABASE(attrindx); + apeak = AMAX(attrindx); + alimit = ATTRMAX(attrindx); + /* criterium for whether the limit is interesting varies */ + interesting_alimit = + final ? TRUE /* was originally `(abase != alimit)' */ + : (alimit != (attrindx != A_STR ? 18 : STR18(100))); + paren_pfx = final ? " (" : " (current; "; + if (acurrent != abase) { + Sprintf(eos(valubuf), "%sbase:%s", paren_pfx, + attrval(attrindx, abase, valstring)); + paren_pfx = ", "; + } + if (abase != apeak) { + Sprintf(eos(valubuf), "%speak:%s", paren_pfx, + attrval(attrindx, apeak, valstring)); + paren_pfx = ", "; + } + if (interesting_alimit) { + Sprintf(eos(valubuf), "%s%slimit:%s", paren_pfx, + /* more verbose if exceeding 'limit' due to magic bonus */ + (acurrent > alimit) ? "innate " : "", + attrval(attrindx, alimit, valstring)); + /* paren_pfx = ", "; */ + } + if (acurrent != abase || abase != apeak || interesting_alimit) + Strcat(valubuf, ")"); + } + enl_msg(subjbuf, "is ", "was ", valubuf, ""); +} + +/* status: selected obvious capabilities, assorted troubles */ +static void +status_enlightenment(mode, final) +int mode; +int final; +{ + boolean magic = (mode & MAGICENLIGHTENMENT) ? TRUE : FALSE; + int cap, wtype; + char buf[BUFSZ], youtoo[BUFSZ]; + boolean Riding = (u.usteed + /* if hero dies while dismounting, u.usteed will still + be set; we want to ignore steed in that situation */ + && !(final == ENL_GAMEOVERDEAD + && !strcmp(g.killer.name, "riding accident"))); + const char *steedname = (!Riding ? (char *) 0 + : x_monnam(u.usteed, + u.usteed->mtame ? ARTICLE_YOUR : ARTICLE_THE, + (char *) 0, + (SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION), + FALSE)); + + /*\ + * Status (many are abbreviated on bottom line; others are or + * should be discernible to the hero hence to the player) + \*/ + enlght_out(""); /* separator after title or characteristics */ + enlght_out(final ? "Final Status:" : "Current Status:"); + + Strcpy(youtoo, You_); + /* not a traditional status but inherently obvious to player; more + detail given below (attributes section) for magic enlightenment */ + if (Upolyd) { + Strcpy(buf, "transformed"); + if (ugenocided()) + Sprintf(eos(buf), " and %s %s inside", + final ? "felt" : "feel", udeadinside()); + you_are(buf, ""); + } + /* not a trouble, but we want to display riding status before maybe + reporting steed as trapped or hero stuck to cursed saddle */ + if (Riding) { + Sprintf(buf, "riding %s", steedname); + you_are(buf, ""); + Sprintf(eos(youtoo), "and %s ", steedname); + } + /* other movement situations that hero should always know */ + if (Levitation) { + if (Lev_at_will && magic) + you_are("levitating, at will", ""); + else + enl_msg(youtoo, are, were, "levitating", from_what(LEVITATION)); + } else if (Flying) { /* can only fly when not levitating */ + enl_msg(youtoo, are, were, "flying", from_what(FLYING)); + } + if (Underwater) { + you_are("underwater", ""); + } else if (u.uinwater) { + you_are(Swimming ? "swimming" : "in water", from_what(SWIMMING)); + } else if (walking_on_water()) { + /* show active Wwalking here, potential Wwalking elsewhere */ + Sprintf(buf, "walking on %s", + is_pool(u.ux, u.uy) ? "water" + : is_lava(u.ux, u.uy) ? "lava" + : surface(u.ux, u.uy)); /* catchall; shouldn't happen */ + you_are(buf, from_what(WWALKING)); + } + if (Upolyd && (u.uundetected || U_AP_TYPE != M_AP_NOTHING)) + youhiding(TRUE, final); + + /* internal troubles, mostly in the order that prayer ranks them */ + if (Stoned) { + if (final && (Stoned & I_SPECIAL)) + enlght_out(" You turned into stone."); + else + you_are("turning to stone", ""); + } + if (Slimed) { + if (final && (Slimed & I_SPECIAL)) + enlght_out(" You turned into slime."); + else + you_are("turning into slime", ""); + } + if (Strangled) { + if (u.uburied) { + you_are("buried", ""); + } else { + if (final && (Strangled & I_SPECIAL)) { + enlght_out(" You died from strangulation."); + } else { + Strcpy(buf, "being strangled"); + if (wizard) + Sprintf(eos(buf), " (%ld)", (Strangled & TIMEOUT)); + you_are(buf, from_what(STRANGLED)); + } + } + } + if (Sick) { + /* the two types of sickness are lumped together; hero can be + afflicted by both but there is only one timeout; botl status + puts TermIll before FoodPois and death due to timeout reports + terminal illness if both are in effect, so do the same here */ + if (final && (Sick & I_SPECIAL)) { + Sprintf(buf, " %sdied from %s.", You_, /* has trailing space */ + (u.usick_type & SICK_NONVOMITABLE) + ? "terminal illness" : "food poisoning"); + enlght_out(buf); + } else { + /* unlike death due to sickness, report the two cases separately + because it is possible to cure one without curing the other */ + if (u.usick_type & SICK_NONVOMITABLE) + you_are("terminally sick from illness", ""); + if (u.usick_type & SICK_VOMITABLE) + you_are("terminally sick from food poisoning", ""); + } + } + if (Vomiting) + you_are("nauseated", ""); + if (Stunned) + you_are("stunned", ""); + if (Confusion) + you_are("confused", ""); + if (Hallucination) + you_are("hallucinating", ""); + if (Blind) { + /* from_what() (currently wizard-mode only) checks !haseyes() + before u.uroleplay.blind, so we should too */ + Sprintf(buf, "%s blind", + !haseyes(g.youmonst.data) ? "innately" + : u.uroleplay.blind ? "permanently" + /* better phrasing desperately wanted... */ + : Blindfolded_only ? "deliberately" + : "temporarily"); + if (wizard && (Blinded & TIMEOUT) != 0L + && !u.uroleplay.blind && haseyes(g.youmonst.data)) + Sprintf(eos(buf), " (%ld)", (Blinded & TIMEOUT)); + /* !haseyes: avoid "you are innately blind innately" */ + you_are(buf, !haseyes(g.youmonst.data) ? "" : from_what(BLINDED)); + } + if (Deaf) + you_are("deaf", from_what(DEAF)); + + /* external troubles, more or less */ + if (Punished) { + if (uball) { + Sprintf(buf, "chained to %s", ansimpleoname(uball)); + } else { + impossible("Punished without uball?"); + Strcpy(buf, "punished"); + } + you_are(buf, ""); + } + if (u.utrap) { + char predicament[BUFSZ]; + boolean anchored = (u.utraptype == TT_BURIEDBALL); + + (void) trap_predicament(predicament, final, wizard); + if (u.usteed) { /* not `Riding' here */ + Sprintf(buf, "%s%s ", anchored ? "you and " : "", steedname); + *buf = highc(*buf); + enl_msg(buf, (anchored ? "are " : "is "), + (anchored ? "were " : "was "), predicament, ""); + } else + you_are(predicament, ""); + } /* (u.utrap) */ + if (u.uswallow) { + Sprintf(buf, "swallowed by %s", a_monnam(u.ustuck)); + if (wizard) + Sprintf(eos(buf), " (%u)", u.uswldtim); + you_are(buf, ""); + } else if (u.ustuck) { + Sprintf(buf, "%s %s", + (Upolyd && sticks(g.youmonst.data)) ? "holding" : "held by", + a_monnam(u.ustuck)); + you_are(buf, ""); + } + if (Riding) { + struct obj *saddle = which_armor(u.usteed, W_SADDLE); + + if (saddle && saddle->cursed) { + Sprintf(buf, "stuck to %s %s", s_suffix(steedname), + simpleonames(saddle)); + you_are(buf, ""); + } + } + if (Wounded_legs) { + /* when mounted, Wounded_legs applies to steed rather than to + hero; we only report steed's wounded legs in wizard mode */ + if (u.usteed) { /* not `Riding' here */ + if (wizard && steedname) { + Strcpy(buf, steedname); + *buf = highc(*buf); + enl_msg(buf, " has", " had", " wounded legs", ""); + } + } else { + Sprintf(buf, "wounded %s", makeplural(body_part(LEG))); + you_have(buf, ""); + } + } + if (Glib) { + Sprintf(buf, "slippery %s", fingers_or_gloves(TRUE)); + if (wizard) + Sprintf(eos(buf), " (%ld)", (Glib & TIMEOUT)); + you_have(buf, ""); + } + if (Fumbling) { + if (magic || cause_known(FUMBLING)) + enl_msg(You_, "fumble", "fumbled", "", from_what(FUMBLING)); + } + if (Sleepy) { + if (magic || cause_known(SLEEPY)) { + Strcpy(buf, from_what(SLEEPY)); + if (wizard) + Sprintf(eos(buf), " (%ld)", (HSleepy & TIMEOUT)); + enl_msg("You ", "fall", "fell", " asleep uncontrollably", buf); + } + } + /* hunger/nutrition */ + if (Hunger) { + if (magic || cause_known(HUNGER)) + enl_msg(You_, "hunger", "hungered", " rapidly", + from_what(HUNGER)); + } + Strcpy(buf, hu_stat[u.uhs]); /* hunger status; omitted if "normal" */ + mungspaces(buf); /* strip trailing spaces */ + if (*buf) { + *buf = lowc(*buf); /* override capitalization */ + if (!strcmp(buf, "weak")) + Strcat(buf, " from severe hunger"); + else if (!strncmp(buf, "faint", 5)) /* fainting, fainted */ + Strcat(buf, " due to starvation"); + you_are(buf, ""); + } + /* encumbrance */ + if ((cap = near_capacity()) > UNENCUMBERED) { + const char *adj = "?_?"; /* (should always get overridden) */ + + Strcpy(buf, enc_stat[cap]); + *buf = lowc(*buf); + switch (cap) { + case SLT_ENCUMBER: + adj = "slightly"; + break; /* burdened */ + case MOD_ENCUMBER: + adj = "moderately"; + break; /* stressed */ + case HVY_ENCUMBER: + adj = "very"; + break; /* strained */ + case EXT_ENCUMBER: + adj = "extremely"; + break; /* overtaxed */ + case OVERLOADED: + adj = "not possible"; + break; + } + Sprintf(eos(buf), "; movement %s %s%s", !final ? "is" : "was", adj, + (cap < OVERLOADED) ? " slowed" : ""); + you_are(buf, ""); + } else { + /* last resort entry, guarantees Status section is non-empty + (no longer needed for that purpose since weapon status added; + still useful though) */ + you_are("unencumbered", ""); + } + + /* report being weaponless; distinguish whether gloves are worn */ + if (!uwep) { + you_are(uarmg ? "empty handed" /* gloves imply hands */ + : humanoid(g.youmonst.data) + /* hands but no weapon and no gloves */ + ? "bare handed" + /* alternate phrasing for paws or lack of hands */ + : "not wielding anything", + ""); + /* two-weaponing implies hands (can't be polymorphed) and + a weapon or wep-tool (not other odd stuff) in each hand */ + } else if (u.twoweap) { + you_are("wielding two weapons at once", ""); + /* report most weapons by their skill class (so a katana will be + described as a long sword, for instance; mattock and hook are + exceptions), or wielded non-weapon item by its object class */ + } else { + const char *what = weapon_descr(uwep); + + if (!strcmpi(what, "armor") || !strcmpi(what, "food") + || !strcmpi(what, "venom")) + Sprintf(buf, "wielding some %s", what); + else + Sprintf(buf, "wielding %s", + (uwep->quan == 1L) ? an(what) : makeplural(what)); + you_are(buf, ""); + } + /* + * Skill with current weapon. Might help players who've never + * noticed #enhance or decided that it was pointless. + */ + if ((wtype = uwep_skill_type()) != P_NONE) { + char sklvlbuf[20]; + int sklvl = P_SKILL(wtype); + boolean hav = (sklvl != P_UNSKILLED && sklvl != P_SKILLED); + + if (sklvl == P_ISRESTRICTED) + Strcpy(sklvlbuf, "no"); + else + (void) lcase(skill_level_name(wtype, sklvlbuf)); + /* "you have no/basic/expert/master/grand-master skill with " + or "you are unskilled/skilled in " */ + Sprintf(buf, "%s %s %s", sklvlbuf, + hav ? "skill with" : "in", skill_name(wtype)); + if (can_advance(wtype, FALSE)) + Sprintf(eos(buf), " and %s that", + !final ? "can enhance" : "could have enhanced"); + if (hav) + you_have(buf, ""); + else + you_are(buf, ""); + } + /* report 'nudity' */ + if (!uarm && !uarmu && !uarmc && !uarms && !uarmg && !uarmf && !uarmh) { + if (u.uroleplay.nudist) + enl_msg(You_, "do", "did", " not wear any armor", ""); + else + you_are("not wearing any armor", ""); + } +} + +/* attributes: intrinsics and the like, other non-obvious capabilities */ +static void +attributes_enlightenment(unused_mode, final) +int unused_mode UNUSED; +int final; +{ + static NEARDATA const char if_surroundings_permitted[] = + " if surroundings permitted"; + int ltmp, armpro; + char buf[BUFSZ]; + + /*\ + * Attributes + \*/ + enlght_out(""); + enlght_out(final ? "Final Attributes:" : "Current Attributes:"); + + if (u.uevent.uhand_of_elbereth) { + static const char *const hofe_titles[3] = { "the Hand of Elbereth", + "the Envoy of Balance", + "the Glory of Arioch" }; + you_are(hofe_titles[u.uevent.uhand_of_elbereth - 1], ""); + } + + Sprintf(buf, "%s", piousness(TRUE, "aligned")); + if (u.ualign.record >= 0) + you_are(buf, ""); + else + you_have(buf, ""); + + if (wizard) { + Sprintf(buf, " %d", u.ualign.record); + enl_msg("Your alignment ", "is", "was", buf, ""); + } + + /*** Resistances to troubles ***/ + if (Invulnerable) + you_are("invulnerable", from_what(INVULNERABLE)); + if (Antimagic) + you_are("magic-protected", from_what(ANTIMAGIC)); + if (Fire_resistance) + you_are("fire resistant", from_what(FIRE_RES)); + if (Cold_resistance) + you_are("cold resistant", from_what(COLD_RES)); + if (Sleep_resistance) + you_are("sleep resistant", from_what(SLEEP_RES)); + if (Disint_resistance) + you_are("disintegration-resistant", from_what(DISINT_RES)); + if (Shock_resistance) + you_are("shock resistant", from_what(SHOCK_RES)); + if (Poison_resistance) + you_are("poison resistant", from_what(POISON_RES)); + if (Acid_resistance) + you_are("acid resistant", from_what(ACID_RES)); + if (Drain_resistance) + you_are("level-drain resistant", from_what(DRAIN_RES)); + if (Sick_resistance) + you_are("immune to sickness", from_what(SICK_RES)); + if (Stone_resistance) + you_are("petrification resistant", from_what(STONE_RES)); + if (Halluc_resistance) + enl_msg(You_, "resist", "resisted", " hallucinations", + from_what(HALLUC_RES)); + if (u.uedibility) + you_can("recognize detrimental food", ""); + + /*** Vision and senses ***/ + if (!Blind && (Blinded || !haseyes(g.youmonst.data))) + you_can("see", from_what(-BLINDED)); /* Eyes of the Overworld */ + if (See_invisible) { + if (!Blind) + enl_msg(You_, "see", "saw", " invisible", from_what(SEE_INVIS)); + else + enl_msg(You_, "will see", "would have seen", + " invisible when not blind", from_what(SEE_INVIS)); + } + if (Blind_telepat) + you_are("telepathic", from_what(TELEPAT)); + if (Warning) + you_are("warned", from_what(WARNING)); + if (Warn_of_mon && g.context.warntype.obj) { + Sprintf(buf, "aware of the presence of %s", + (g.context.warntype.obj & M2_ORC) ? "orcs" + : (g.context.warntype.obj & M2_ELF) ? "elves" + : (g.context.warntype.obj & M2_DEMON) ? "demons" : something); + you_are(buf, from_what(WARN_OF_MON)); + } + if (Warn_of_mon && g.context.warntype.polyd) { + Sprintf(buf, "aware of the presence of %s", + ((g.context.warntype.polyd & (M2_HUMAN | M2_ELF)) + == (M2_HUMAN | M2_ELF)) + ? "humans and elves" + : (g.context.warntype.polyd & M2_HUMAN) + ? "humans" + : (g.context.warntype.polyd & M2_ELF) + ? "elves" + : (g.context.warntype.polyd & M2_ORC) + ? "orcs" + : (g.context.warntype.polyd & M2_DEMON) + ? "demons" + : "certain monsters"); + you_are(buf, ""); + } + if (Warn_of_mon && g.context.warntype.speciesidx >= LOW_PM) { + Sprintf(buf, "aware of the presence of %s", + makeplural(mons[g.context.warntype.speciesidx].mname)); + you_are(buf, from_what(WARN_OF_MON)); + } + if (Undead_warning) + you_are("warned of undead", from_what(WARN_UNDEAD)); + if (Searching) + you_have("automatic searching", from_what(SEARCHING)); + if (Clairvoyant) + you_are("clairvoyant", from_what(CLAIRVOYANT)); + else if ((HClairvoyant || EClairvoyant) && BClairvoyant) { + Strcpy(buf, from_what(-CLAIRVOYANT)); + if (!strncmp(buf, " because of ", 12)) + /* overwrite substring; strncpy doesn't add terminator */ + (void) strncpy(buf, " if not for ", 12); + enl_msg(You_, "could be", "could have been", " clairvoyant", buf); + } + if (Infravision) + you_have("infravision", from_what(INFRAVISION)); + if (Detect_monsters) + you_are("sensing the presence of monsters", ""); + if (u.umconf) + you_are("going to confuse monsters", ""); + + /*** Appearance and behavior ***/ + if (Adornment) { + int adorn = 0; + + if (uleft && uleft->otyp == RIN_ADORNMENT) + adorn += uleft->spe; + if (uright && uright->otyp == RIN_ADORNMENT) + adorn += uright->spe; + /* the sum might be 0 (+0 ring or two which negate each other); + that yields "you are charismatic" (which isn't pointless + because it potentially impacts seduction attacks) */ + Sprintf(buf, "%scharismatic", + (adorn > 0) ? "more " : (adorn < 0) ? "less " : ""); + you_are(buf, from_what(ADORNED)); + } + if (Invisible) + you_are("invisible", from_what(INVIS)); + else if (Invis) + you_are("invisible to others", from_what(INVIS)); + /* ordinarily "visible" is redundant; this is a special case for + the situation when invisibility would be an expected attribute */ + else if ((HInvis || EInvis) && BInvis) + you_are("visible", from_what(-INVIS)); + if (Displaced) + you_are("displaced", from_what(DISPLACED)); + if (Stealth) + you_are("stealthy", from_what(STEALTH)); + if (Aggravate_monster) + enl_msg("You aggravate", "", "d", " monsters", + from_what(AGGRAVATE_MONSTER)); + if (Conflict) + enl_msg("You cause", "", "d", " conflict", from_what(CONFLICT)); + + /*** Transportation ***/ + if (Jumping) + you_can("jump", from_what(JUMPING)); + if (Teleportation) + you_can("teleport", from_what(TELEPORT)); + if (Teleport_control) + you_have("teleport control", from_what(TELEPORT_CONTROL)); + /* actively levitating handled earlier as a status condition */ + if (BLevitation) { /* levitation is blocked */ + long save_BLev = BLevitation; + + BLevitation = 0L; + if (Levitation) { + /* either trapped in the floor or inside solid rock + (or both if chained to buried iron ball and have + moved one step into solid rock somehow) */ + boolean trapped = (save_BLev & I_SPECIAL) != 0L, + terrain = (save_BLev & FROMOUTSIDE) != 0L; + + Sprintf(buf, "%s%s%s", + trapped ? " if not trapped" : "", + (trapped && terrain) ? " and" : "", + terrain ? if_surroundings_permitted : ""); + enl_msg(You_, "would levitate", "would have levitated", buf, ""); + } + BLevitation = save_BLev; + } + /* actively flying handled earlier as a status condition */ + if (BFlying) { /* flight is blocked */ + long save_BFly = BFlying; + + BFlying = 0L; + if (Flying) { + enl_msg(You_, "would fly", "would have flown", + /* wording quibble: for past tense, "hadn't been" + would sound better than "weren't" (and + "had permitted" better than "permitted"), but + "weren't" and "permitted" are adequate so the + extra complexity to handle that isn't worth it */ + Levitation + ? " if you weren't levitating" + : (save_BFly == I_SPECIAL) + /* this is an oversimpliction; being trapped + might also be blocking levitation so flight + would still be blocked after escaping trap */ + ? " if you weren't trapped" + : (save_BFly == FROMOUTSIDE) + ? if_surroundings_permitted + /* two or more of levitation, surroundings, + and being trapped in the floor */ + : " if circumstances permitted", + ""); + } + BFlying = save_BFly; + } + /* actively walking on water handled earlier as a status condition */ + if (Wwalking && !walking_on_water()) + you_can("walk on water", from_what(WWALKING)); + /* actively swimming (in water but not under it) handled earlier */ + if (Swimming && (Underwater || !u.uinwater)) + you_can("swim", from_what(SWIMMING)); + if (Breathless) + you_can("survive without air", from_what(MAGICAL_BREATHING)); + else if (Amphibious) + you_can("breathe water", from_what(MAGICAL_BREATHING)); + if (Passes_walls) + you_can("walk through walls", from_what(PASSES_WALLS)); + + /*** Physical attributes ***/ + if (Regeneration) + enl_msg("You regenerate", "", "d", "", from_what(REGENERATION)); + if (Slow_digestion) + you_have("slower digestion", from_what(SLOW_DIGESTION)); + if (u.uhitinc) + you_have(enlght_combatinc("to hit", u.uhitinc, final, buf), ""); + if (u.udaminc) + you_have(enlght_combatinc("damage", u.udaminc, final, buf), ""); + if (u.uspellprot || Protection) { + int prot = 0; + + if (uleft && uleft->otyp == RIN_PROTECTION) + prot += uleft->spe; + if (uright && uright->otyp == RIN_PROTECTION) + prot += uright->spe; + if (HProtection & INTRINSIC) + prot += u.ublessed; + prot += u.uspellprot; + if (prot) + you_have(enlght_combatinc("defense", prot, final, buf), ""); + } + if ((armpro = magic_negation(&g.youmonst)) > 0) { + /* magic cancellation factor, conferred by worn armor */ + static const char *const mc_types[] = { + "" /*ordinary*/, "warded", "guarded", "protected", + }; + /* sanity check */ + if (armpro >= SIZE(mc_types)) + armpro = SIZE(mc_types) - 1; + you_are(mc_types[armpro], ""); + } + if (Half_physical_damage) + enlght_halfdmg(HALF_PHDAM, final); + if (Half_spell_damage) + enlght_halfdmg(HALF_SPDAM, final); + if (Half_gas_damage) + enl_msg(You_, "take", "took", " reduced poison gas damage", ""); + /* polymorph and other shape change */ + if (Protection_from_shape_changers) + you_are("protected from shape changers", + from_what(PROT_FROM_SHAPE_CHANGERS)); + if (Unchanging) { + const char *what = 0; + + if (!Upolyd) /* Upolyd handled below after current form */ + you_can("not change from your current form", + from_what(UNCHANGING)); + /* blocked shape changes */ + if (Polymorph) + what = !final ? "polymorph" : "have polymorphed"; + else if (u.ulycn >= LOW_PM) + what = !final ? "change shape" : "have changed shape"; + if (what) { + Sprintf(buf, "would %s periodically", what); + /* omit from_what(UNCHANGING); too verbose */ + enl_msg(You_, buf, buf, " if not locked into your current form", + ""); + } + } else if (Polymorph) { + you_are("polymorphing periodically", from_what(POLYMORPH)); + } + if (Polymorph_control) + you_have("polymorph control", from_what(POLYMORPH_CONTROL)); + if (Upolyd && u.umonnum != u.ulycn + /* if we've died from turning into slime, we're polymorphed + right now but don't want to list it as a temporary attribute + [we need a more reliable way to detect this situation] */ + && !(final == ENL_GAMEOVERDEAD + && u.umonnum == PM_GREEN_SLIME && !Unchanging)) { + /* foreign shape (except were-form which is handled below) */ + if (!vampshifted(&g.youmonst)) + Sprintf(buf, "polymorphed into %s", an(g.youmonst.data->mname)); + else + Sprintf(buf, "polymorphed into %s in %s form", + an(mons[g.youmonst.cham].mname), g.youmonst.data->mname); + if (wizard) + Sprintf(eos(buf), " (%d)", u.mtimedone); + you_are(buf, ""); + } + if (lays_eggs(g.youmonst.data) && flags.female) /* Upolyd */ + you_can("lay eggs", ""); + if (u.ulycn >= LOW_PM) { + /* "you are a werecreature [in beast form]" */ + Strcpy(buf, an(mons[u.ulycn].mname)); + if (u.umonnum == u.ulycn) { + Strcat(buf, " in beast form"); + if (wizard) + Sprintf(eos(buf), " (%d)", u.mtimedone); + } + you_are(buf, ""); + } + if (Unchanging && Upolyd) /* !Upolyd handled above */ + you_can("not change from your current form", from_what(UNCHANGING)); + if (Hate_silver) + you_are("harmed by silver", ""); + /* movement and non-armor-based protection */ + if (Fast) + you_are(Very_fast ? "very fast" : "fast", from_what(FAST)); + if (Reflecting) + you_have("reflection", from_what(REFLECTING)); + if (Free_action) + you_have("free action", from_what(FREE_ACTION)); + if (Fixed_abil) + you_have("fixed abilities", from_what(FIXED_ABIL)); + if (Lifesaved) + enl_msg("Your life ", "will be", "would have been", " saved", ""); + + /*** Miscellany ***/ + if (Luck) { + ltmp = abs((int) Luck); + Sprintf(buf, "%s%slucky", + ltmp >= 10 ? "extremely " : ltmp >= 5 ? "very " : "", + Luck < 0 ? "un" : ""); + if (wizard) + Sprintf(eos(buf), " (%d)", Luck); + you_are(buf, ""); + } else if (wizard) + enl_msg("Your luck ", "is", "was", " zero", ""); + if (u.moreluck > 0) + you_have("extra luck", ""); + else if (u.moreluck < 0) + you_have("reduced luck", ""); + if (carrying(LUCKSTONE) || stone_luck(TRUE)) { + ltmp = stone_luck(FALSE); + if (ltmp <= 0) + enl_msg("Bad luck ", "does", "did", " not time out for you", ""); + if (ltmp >= 0) + enl_msg("Good luck ", "does", "did", " not time out for you", ""); + } + + if (u.ugangr) { + Sprintf(buf, " %sangry with you", + u.ugangr > 6 ? "extremely " : u.ugangr > 3 ? "very " : ""); + if (wizard) + Sprintf(eos(buf), " (%d)", u.ugangr); + enl_msg(u_gname(), " is", " was", buf, ""); + } else { + /* + * We need to suppress this when the game is over, because death + * can change the value calculated by can_pray(), potentially + * resulting in a false claim that you could have prayed safely. + */ + if (!final) { +#if 0 + /* "can [not] safely pray" vs "could [not] have safely prayed" */ + Sprintf(buf, "%s%ssafely pray%s", can_pray(FALSE) ? "" : "not ", + final ? "have " : "", final ? "ed" : ""); +#else + Sprintf(buf, "%ssafely pray", can_pray(FALSE) ? "" : "not "); +#endif + if (wizard) + Sprintf(eos(buf), " (%d)", u.ublesscnt); + you_can(buf, ""); + } + } + +#ifdef DEBUG + /* named fruit debugging (doesn't really belong here...); to enable, + include 'fruit' in DEBUGFILES list (even though it isn't a file...) */ + if (wizard && explicitdebug("fruit")) { + struct fruit *f; + + reorder_fruit(TRUE); /* sort by fruit index, from low to high; + * this modifies the g.ffruit chain, so could + * possibly mask or even introduce a problem, + * but it does useful sanity checking */ + for (f = g.ffruit; f; f = f->nextf) { + Sprintf(buf, "Fruit #%d ", f->fid); + enl_msg(buf, "is ", "was ", f->fname, ""); + } + enl_msg("The current fruit ", "is ", "was ", g.pl_fruit, ""); + Sprintf(buf, "%d", flags.made_fruit); + enl_msg("The made fruit flag ", "is ", "was ", buf, ""); + } +#endif + + { + const char *p; + + buf[0] = '\0'; + if (final < 2) { /* still in progress, or quit/escaped/ascended */ + p = "survived after being killed "; + switch (u.umortality) { + case 0: + p = !final ? (char *) 0 : "survived"; + break; + case 1: + Strcpy(buf, "once"); + break; + case 2: + Strcpy(buf, "twice"); + break; + case 3: + Strcpy(buf, "thrice"); + break; + default: + Sprintf(buf, "%d times", u.umortality); + break; + } + } else { /* game ended in character's death */ + p = "are dead"; + switch (u.umortality) { + case 0: + impossible("dead without dying?"); + case 1: + break; /* just "are dead" */ + default: + Sprintf(buf, " (%d%s time!)", u.umortality, + ordin(u.umortality)); + break; + } + } + if (p) + enl_msg(You_, "have been killed ", p, buf, ""); + } +} + +/* ^X command */ +int +doattributes(VOID_ARGS) +{ + int mode = BASICENLIGHTENMENT; + + /* show more--as if final disclosure--for wizard and explore modes */ + if (wizard || discover) + mode |= MAGICENLIGHTENMENT; + + enlightenment(mode, ENL_GAMEINPROGRESS); + return 0; +} + +void +youhiding(via_enlghtmt, msgflag) +boolean via_enlghtmt; /* englightment line vs topl message */ +int msgflag; /* for variant message phrasing */ +{ + char *bp, buf[BUFSZ]; + + Strcpy(buf, "hiding"); + if (U_AP_TYPE != M_AP_NOTHING) { + /* mimic; hero is only able to mimic a strange object or gold + or hallucinatory alternative to gold, so we skip the details + for the hypothetical furniture and monster cases */ + bp = eos(strcpy(buf, "mimicking")); + if (U_AP_TYPE == M_AP_OBJECT) { + Sprintf(bp, " %s", an(simple_typename(g.youmonst.mappearance))); + } else if (U_AP_TYPE == M_AP_FURNITURE) { + Strcpy(bp, " something"); + } else if (U_AP_TYPE == M_AP_MONSTER) { + Strcpy(bp, " someone"); + } else { + ; /* something unexpected; leave 'buf' as-is */ + } + } else if (u.uundetected) { + bp = eos(buf); /* points past "hiding" */ + if (g.youmonst.data->mlet == S_EEL) { + if (is_pool(u.ux, u.uy)) + Sprintf(bp, " in the %s", waterbody_name(u.ux, u.uy)); + } else if (hides_under(g.youmonst.data)) { + struct obj *o = g.level.objects[u.ux][u.uy]; + + if (o) + Sprintf(bp, " underneath %s", ansimpleoname(o)); + } else if (is_clinger(g.youmonst.data) || Flying) { + /* Flying: 'lurker above' hides on ceiling but doesn't cling */ + Sprintf(bp, " on the %s", ceiling(u.ux, u.uy)); + } else { + /* on floor; is_hider() but otherwise not special: 'trapper' */ + if (u.utrap && u.utraptype == TT_PIT) { + struct trap *t = t_at(u.ux, u.uy); + + Sprintf(bp, " in a %spit", + (t && t->ttyp == SPIKED_PIT) ? "spiked " : ""); + } else + Sprintf(bp, " on the %s", surface(u.ux, u.uy)); + } + } else { + ; /* shouldn't happen; will result in generic "you are hiding" */ + } + + if (via_enlghtmt) { + int final = msgflag; /* 'final' is used by you_are() macro */ + + you_are(buf, ""); + } else { + /* for dohide(), when player uses '#monster' command */ + You("are %s %s.", msgflag ? "already" : "now", buf); + } +} + +/* #conduct command [KMH]; shares enlightenment's tense handling */ +int +doconduct(VOID_ARGS) +{ + show_conduct(0); + return 0; +} + +/* display conducts; for doconduct(), also disclose() and dump_everything() */ +void +show_conduct(final) +int final; +{ + char buf[BUFSZ]; + int ngenocided; + + /* Create the conduct window */ + g.en_win = create_nhwindow(NHW_MENU); + putstr(g.en_win, 0, "Voluntary challenges:"); + + if (u.uroleplay.blind) + you_have_been("blind from birth"); + if (u.uroleplay.nudist) + you_have_been("faithfully nudist"); + + if (!u.uconduct.food) + enl_msg(You_, "have gone", "went", " without food", ""); + /* but beverages are okay */ + else if (!u.uconduct.unvegan) + you_have_X("followed a strict vegan diet"); + else if (!u.uconduct.unvegetarian) + you_have_been("vegetarian"); + + if (!u.uconduct.gnostic) + you_have_been("an atheist"); + + if (!u.uconduct.weaphit) { + you_have_never("hit with a wielded weapon"); + } else if (wizard) { + Sprintf(buf, "used a wielded weapon %ld time%s", u.uconduct.weaphit, + plur(u.uconduct.weaphit)); + you_have_X(buf); + } + if (!u.uconduct.killer) + you_have_been("a pacifist"); + + if (!u.uconduct.literate) { + you_have_been("illiterate"); + } else if (wizard) { + Sprintf(buf, "read items or engraved %ld time%s", u.uconduct.literate, + plur(u.uconduct.literate)); + you_have_X(buf); + } + + ngenocided = num_genocides(); + if (ngenocided == 0) { + you_have_never("genocided any monsters"); + } else { + Sprintf(buf, "genocided %d type%s of monster%s", ngenocided, + plur(ngenocided), plur(ngenocided)); + you_have_X(buf); + } + + if (!u.uconduct.polypiles) { + you_have_never("polymorphed an object"); + } else if (wizard) { + Sprintf(buf, "polymorphed %ld item%s", u.uconduct.polypiles, + plur(u.uconduct.polypiles)); + you_have_X(buf); + } + + if (!u.uconduct.polyselfs) { + you_have_never("changed form"); + } else if (wizard) { + Sprintf(buf, "changed form %ld time%s", u.uconduct.polyselfs, + plur(u.uconduct.polyselfs)); + you_have_X(buf); + } + + if (!u.uconduct.wishes) { + you_have_X("used no wishes"); + } else { + Sprintf(buf, "used %ld wish%s", u.uconduct.wishes, + (u.uconduct.wishes > 1L) ? "es" : ""); + if (u.uconduct.wisharti) { + /* if wisharti == wishes + * 1 wish (for an artifact) + * 2 wishes (both for artifacts) + * N wishes (all for artifacts) + * else (N is at least 2 in order to get here; M < N) + * N wishes (1 for an artifact) + * N wishes (M for artifacts) + */ + if (u.uconduct.wisharti == u.uconduct.wishes) + Sprintf(eos(buf), " (%s", + (u.uconduct.wisharti > 2L) ? "all " + : (u.uconduct.wisharti == 2L) ? "both " : ""); + else + Sprintf(eos(buf), " (%ld ", u.uconduct.wisharti); + + Sprintf(eos(buf), "for %s)", + (u.uconduct.wisharti == 1L) ? "an artifact" + : "artifacts"); + } + you_have_X(buf); + + if (!u.uconduct.wisharti) + enl_msg(You_, "have not wished", "did not wish", + " for any artifacts", ""); + } + + show_achievements(final); + + /* Pop up the window and wait for a key */ + display_nhwindow(g.en_win, TRUE); + destroy_nhwindow(g.en_win); + g.en_win = WIN_ERR; +} + +/* uses to decide whether there are any achievements to display */ +int +count_uachieve() +{ + int acnt = 0; + + /* these tests must be kept in sync with show_achievements() */ + if (u.uroleplay.blind) + ++acnt; + if (u.uroleplay.nudist) + ++acnt; + if (u.uachieve.mines_luckstone) + ++acnt; + if (u.uachieve.finish_sokoban) + ++acnt; + if (u.uachieve.killed_medusa) + ++acnt; + if (u.uachieve.bell) + ++acnt; + if (u.uachieve.enter_gehennom) + ++acnt; + if (u.uachieve.menorah) + ++acnt; + if (u.uachieve.book) + ++acnt; + if (u.uevent.invoked) + ++acnt; + if (u.uachieve.amulet) + ++acnt; + if (In_endgame(&u.uz)) + ++acnt; + if (Is_astralevel(&u.uz)) + ++acnt; + if (u.uachieve.ascended) + ++acnt; + + return acnt; +} + +static void +show_achievements(final) +int final; +{ + int acnt; + char title[BUFSZ]; + winid awin = WIN_ERR; + + /* unfortunately we can't show the achievements (at least not all of + them) while the game is in progress because it would give away the + ID of luckstone (at Mine's End) and of real Amulet of Yendor */ + if (!final && !wizard) + return; + + /* first, figure whether any achievements have been accomplished + so that we don't show the header for them if the resulting list + below it would be empty */ + if ((acnt = count_uachieve()) == 0) + return; + + if (g.en_win != WIN_ERR) { + awin = g.en_win; /* end of game disclosure window */ + putstr(awin, 0, ""); + } else { + awin = create_nhwindow(NHW_MENU); + } + Sprintf(title, "Achievement%s:", plur(acnt)); + putstr(awin, 0, title); + /* after 'blind' and 'nudist', which are the easiest if you die but + the hardest if you ascend, they're arranged in approximate order + of difficulty */ + if (u.uroleplay.blind) + enl_msg(You_, "are exploring", "explored", + " without being able to see", ""); + if (u.uroleplay.nudist) + enl_msg(You_, "have gone", "went", " without any armor", ""); + + if (u.uachieve.mines_luckstone) + enl_msg(You_, "have ", "", "completed the Gnomish Mines", ""); + if (u.uachieve.finish_sokoban) + enl_msg(You_, "have ", "", "completed Sokoban", ""); + if (u.uachieve.killed_medusa) + enl_msg(You_, "have ", "", "defeated Medusa", ""); + if (u.uachieve.bell) { + /* alternate phrasing for present vs past and also for possessing + the item vs once held it */ + enl_msg(You_, + u.uhave.bell ? "have" : "have handled", + u.uhave.bell ? "had" : "handled", + " the Bell of Opening", ""); + } + /* wording is clumsy but the game is inconsistent about "entering + Gehennom"; the Valley is part of Gehennom but the message about + entering Gehennom is given when descending from the Valley to the + level below and that's also when the flag about entering gets set */ + if (u.uachieve.enter_gehennom) + enl_msg(You_, "have ", "", "passed the Valley of the Dead", ""); + if (u.uachieve.menorah) { + enl_msg(You_, + u.uhave.menorah ? "have" : "have handled", + u.uhave.menorah ? "had" : "handled", + " the Candelabrum of Invocation", ""); + } + if (u.uachieve.book) { + enl_msg(You_, + u.uhave.book ? "have" : "have handled", + u.uhave.book ? "had" : "handled", + " the Book of the Dead", ""); + } + if (u.uevent.invoked) + enl_msg(You_, "have ", "", "gained access to Moloch's Sanctum", ""); + if (u.uachieve.amulet) { + /* extra alternate wording for past tense because ascension + requires giving up the Amulet */ + enl_msg(You_, + u.uhave.amulet ? "have" : "have obtained", + u.uachieve.ascended ? "delivered" + : u.uhave.amulet ? "had" : "had obtained", + " the Amulet of Yendor", ""); + } + + /* reaching Astral makes feedback about reaching the Planes be redundant + and asceding makes both be redundant, but we display all that apply */ + if (In_endgame(&u.uz)) + enl_msg(You_, "have ", "", "reached the Elemental Planes", ""); + if (Is_astralevel(&u.uz)) + enl_msg(You_, "have ", "", "reached the Astral Plane", ""); + if (u.uachieve.ascended) + enlght_out(" You ascended!"); + + if (awin != g.en_win) { + display_nhwindow(awin, TRUE); + destroy_nhwindow(awin); + } +} + +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) + +#define done_stopprint g.program_state.stopprint + +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; +} + +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 + } +} + +/* + * align_str(), piousness(), mstatusline() and ustatusline() once resided + * in pline.c, then got moved to priest.c just to be out of there. They + * fit better here. + */ + +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) != 0) + 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); +} /*insight.c*/ diff --git a/src/priest.c b/src/priest.c index ad17c0b8b..259597e7d 100644 --- a/src/priest.c +++ b/src/priest.c @@ -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*/