1 /* aNetHack 0.0.1 end.c $ANH-Date: 1489192539 2017/03/11 00:35:39 $ $ANH-Branch: master $:$ANH-Revision: 1.130 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* aNetHack may be freely redistributed. See license for details. */
5 #define NEED_VARARGS /* comment line for pre-compiled headers */
16 /* add b to long a, convert wraparound to max value */
17 #define nowrap_add(a, b) (a = ((a + b) < 0 ? LONG_MAX : (a + b)))
19 /* these probably ought to be generated by makedefs, like LAST_GEM */
20 #define FIRST_GEM DILITHIUM_CRYSTAL
21 #define FIRST_AMULET AMULET_OF_ESP
22 #define LAST_AMULET AMULET_OF_YENDOR
24 struct valuable_data
{
29 static struct valuable_data
30 gems
[LAST_GEM
+ 1 - FIRST_GEM
+ 1], /* 1 extra for glass */
31 amulets
[LAST_AMULET
+ 1 - FIRST_AMULET
];
33 static struct val_list
{
34 struct valuable_data
*list
;
36 } valuables
[] = { { gems
, sizeof gems
/ sizeof *gems
},
37 { amulets
, sizeof amulets
/ sizeof *amulets
},
41 STATIC_PTR
void FDECL(done_intr
, (int));
42 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
43 static void FDECL(done_hangup
, (int));
46 STATIC_DCL
void FDECL(disclose
, (int, BOOLEAN_P
));
47 STATIC_DCL
void FDECL(get_valuables
, (struct obj
*));
48 STATIC_DCL
void FDECL(sort_valuables
, (struct valuable_data
*, int));
49 STATIC_DCL
void FDECL(artifact_score
, (struct obj
*, BOOLEAN_P
, winid
));
50 STATIC_DCL
void FDECL(really_done
, (int)) NORETURN
;
51 STATIC_DCL boolean
FDECL(odds_and_ends
, (struct obj
*, int));
52 STATIC_DCL
void FDECL(savelife
, (int));
53 STATIC_PTR
int FDECL(CFDECLSPEC vanqsort_cmp
, (const genericptr
,
55 STATIC_DCL
int NDECL(set_vanq_order
);
56 STATIC_DCL
void FDECL(list_vanquished
, (CHAR_P
, BOOLEAN_P
));
57 STATIC_DCL
void FDECL(list_genocided
, (CHAR_P
, BOOLEAN_P
));
58 STATIC_DCL boolean
FDECL(should_query_disclose_option
, (int, char *));
60 STATIC_DCL
void NDECL(dump_plines
);
62 STATIC_DCL
void FDECL(dump_everything
, (int));
63 STATIC_DCL
int NDECL(num_extinct
);
65 #if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2)
66 extern void FDECL(anethack_exit
, (int));
68 #define anethack_exit exit
71 #define done_stopprint program_state.stopprint
74 #define NH_abort NH_abort_
78 #define NH_abort_() Abort(0)
81 #define NH_abort_() (void) abort()
84 #define NH_abort_() win32_abort()
86 #define NH_abort_() abort()
93 #ifdef PANICTRACE_LIBC
97 /* What do we try and in what order? Tradeoffs:
98 * libc: +no external programs required
99 * -requires newish libc/glibc
100 * -requires -rdynamic
101 * gdb: +gives more detailed information
102 * +works on more OS versions
103 * -requires -g, which may preclude -O on some compilers
106 #define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
107 #ifdef PANICTRACE_LIBC
108 #define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc
110 #define SYSOPT_PANICTRACE_LIBC 0
113 #define SYSOPT_PANICTRACE_GDB (nh_getenv("ANETHACK_USE_GDB") == 0 ? 0 : 2)
114 #ifdef PANICTRACE_LIBC
115 #define SYSOPT_PANICTRACE_LIBC 1
117 #define SYSOPT_PANICTRACE_LIBC 0
121 static void NDECL(NH_abort
);
123 static void FDECL(panictrace_handler
, (int));
125 static boolean
NDECL(NH_panictrace_libc
);
126 static boolean
NDECL(NH_panictrace_gdb
);
130 void panictrace_handler(
131 sig_unused
) /* called as signal() handler, so sent at least one arg */
132 int sig_unused UNUSED
;
134 #define SIG_MSG "\nSignal received.\n"
135 (void) write(2, SIG_MSG
, sizeof(SIG_MSG
) - 1);
140 panictrace_setsignals(set
)
143 #define SETSIGNAL(sig) \
144 (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL);
164 SETSIGNAL(SIGSTKFLT
);
174 #endif /* NO_SIGNAL */
179 int gdb_prio
= SYSOPT_PANICTRACE_GDB
;
180 int libc_prio
= SYSOPT_PANICTRACE_LIBC
;
181 static boolean aborting
= FALSE
;
188 if (gdb_prio
== libc_prio
&& gdb_prio
> 0)
191 if (gdb_prio
> libc_prio
) {
192 (void) (NH_panictrace_gdb() || (libc_prio
&& NH_panictrace_libc()));
194 (void) (NH_panictrace_libc() || (gdb_prio
&& NH_panictrace_gdb()));
198 /* overload otherwise unused priority for debug mode: 1 = show
199 traceback and exit; 2 = show traceback and stay in debugger */
200 /* if (wizard && gdb_prio == 1) gdb_prio = 2; */
201 vms_traceback(gdb_prio
);
202 (void) libc_prio
; /* half-hearted attempt at lint suppression */
207 panictrace_setsignals(FALSE
);
215 #ifdef PANICTRACE_LIBC
220 raw_print("Generating more information you may report:\n");
221 count
= backtrace(bt
, SIZE(bt
));
222 info
= backtrace_symbols(bt
, count
);
223 for (x
= 0; x
< count
; x
++) {
224 raw_printf("[%lu] %s", (unsigned long) x
, info
[x
]);
226 /* free(info); -- Don't risk it. */
230 #endif /* !PANICTRACE_LIBC */
234 * fooPATH file system path for foo
235 * fooVAR (possibly const) variable containing fooPATH
237 #ifdef PANICTRACE_GDB
239 #define GDBVAR sysopt.gdbpath
240 #define GREPVAR sysopt.greppath
242 #define GDBVAR GDBPATH
243 #define GREPVAR GREPPATH
245 #endif /* PANICTRACE_GDB */
250 #ifdef PANICTRACE_GDB
251 /* A (more) generic method to get a stack trace - invoke
253 char *gdbpath
= GDBVAR
;
254 char *greppath
= GREPVAR
;
258 if (gdbpath
== NULL
|| gdbpath
[0] == 0)
260 if (greppath
== NULL
|| greppath
[0] == 0)
263 sprintf(buf
, "%s -n -q %s %d 2>&1 | %s '^#'", gdbpath
, ARGV0
, getpid(),
265 gdb
= popen(buf
, "w");
267 raw_print("Generating more information you may report:\n");
268 fprintf(gdb
, "bt\nquit\ny");
278 #endif /* !PANICTRACE_GDB */
280 #endif /* PANICTRACE */
283 * The order of these needs to match the macros in hack.h.
285 static NEARDATA
const char *deaths
[] = {
286 /* the array of death */
287 "died", "choked", "poisoned", "starvation", "drowning", "burning",
288 "dissolving under the heat and pressure", "crushed", "turned to stone",
289 "turned into slime", "genocided", "panic", "trickery", "quit",
290 "escaped", "ascended"
293 static NEARDATA
const char *ends
[] = {
295 "died", "choked", "were poisoned",
296 "starved", "drowned", "burned",
297 "dissolved in the lava",
298 "were crushed", "turned to stone",
299 "turned into slime", "were genocided",
300 "panicked", "were tricked", "quit",
301 "escaped", "ascended"
304 static boolean Schroedingers_cat
= FALSE
;
308 done1(sig_unused
) /* called as signal() handler, so sent at least one arg */
309 int sig_unused UNUSED
;
312 (void) signal(SIGINT
, SIG_IGN
);
316 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
318 clear_nhwindow(WIN_MESSAGE
);
328 /* "#quit" command or keyboard interrupt */
332 if (!paranoid_query(ParanoidQuit
, "Really quit?")) {
334 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
336 clear_nhwindow(WIN_MESSAGE
);
342 u
.uinvulnerable
= FALSE
; /* avoid ctrl-C bug -dlc */
347 #if (defined(UNIX) || defined(VMS) || defined(LATTICE))
351 extern int debuggable
; /* sys/vms/vmsmisc.c, vmsunix.c */
353 c
= !debuggable
? 'n' : ynq("Enter debugger?");
356 c
= ynq("Create SnapShot?");
358 c
= ynq("Dump core?");
363 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
365 exit_nhwindows((char *) 0);
380 done_intr(sig_unused
) /* called as signal() handler, so sent at least 1 arg */
381 int sig_unused UNUSED
;
384 (void) signal(SIGINT
, SIG_IGN
);
385 #if defined(UNIX) || defined(VMS)
386 (void) signal(SIGQUIT
, SIG_IGN
);
391 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
392 /* signal() handler */
397 program_state
.done_hup
++;
398 sethanguphandler((void FDECL((*), (int) )) SIG_IGN
);
403 #endif /* NO_SIGNAL */
406 done_in_by(mtmp
, how
)
411 struct permonst
*mptr
= mtmp
->data
,
412 *champtr
= ((mtmp
->cham
>= LOW_PM
)
415 boolean distorted
= (boolean
) (Hallucination
&& canspotmon(mtmp
)),
416 mimicker
= (mtmp
->m_ap_type
== M_AP_MONSTER
),
417 imitator
= (mptr
!= champtr
|| mimicker
);
419 You((how
== STONING
) ? "turn to stone..." : "die...");
420 mark_synch(); /* flush buffered screen output */
422 killer
.format
= KILLED_BY_AN
;
423 /* "killed by the high priest of Crom" is okay,
424 "killed by the high priest" alone isn't */
425 if ((mptr
->geno
& G_UNIQ
) != 0 && !(imitator
&& !mimicker
)
426 && !(mptr
== &mons
[PM_HIGH_PRIEST
] && !mtmp
->ispriest
)) {
427 if (!type_is_pname(mptr
))
429 killer
.format
= KILLED_BY
;
431 /* _the_ <invisible> <distorted> ghost of Dudley */
432 if (mptr
== &mons
[PM_GHOST
] && has_mname(mtmp
)) {
434 killer
.format
= KILLED_BY
;
437 Strcat(buf
, "invisible ");
439 Strcat(buf
, "hallucinogen-distorted ");
443 const char *realnm
= champtr
->mname
, *fakenm
= mptr
->mname
;
444 boolean alt
= is_vampshifter(mtmp
);
447 /* realnm is already correct because champtr==mptr;
448 set up fake mptr for type_is_pname/the_unique_pm */
449 mptr
= &mons
[mtmp
->mappearance
];
450 fakenm
= mptr
->mname
;
451 } else if (alt
&& strstri(realnm
, "vampire")
452 && !strcmp(fakenm
, "vampire bat")) {
453 /* special case: use "vampire in bat form" in preference
454 to redundant looking "vampire in vampire bat form" */
457 /* for the alternate format, always suppress any article;
458 pname and the_unique should also have s_suffix() applied,
459 but vampires don't take on any shapes which warrant that */
460 if (alt
|| type_is_pname(mptr
)) /* no article */
461 Strcpy(shape
, fakenm
);
462 else if (the_unique_pm(mptr
)) /* "the"; don't use the() here */
463 Sprintf(shape
, "the %s", fakenm
);
465 Strcpy(shape
, an(fakenm
));
466 /* omit "called" to avoid excessive verbosity */
468 alt
? "%s in %s form"
469 : mimicker
? "%s disguised as %s"
472 mptr
= mtmp
->data
; /* reset for mimicker case */
473 } else if (mptr
== &mons
[PM_GHOST
]) {
474 Strcat(buf
, "ghost");
476 Sprintf(eos(buf
), " of %s", MNAME(mtmp
));
477 } else if (mtmp
->isshk
) {
478 const char *shknm
= shkname(mtmp
),
479 *honorific
= shkname_is_pname(mtmp
) ? ""
480 : mtmp
->female
? "Ms. " : "Mr. ";
482 Sprintf(eos(buf
), "%s%s, the shopkeeper", honorific
, shknm
);
483 killer
.format
= KILLED_BY
;
484 } else if (mtmp
->ispriest
|| mtmp
->isminion
) {
485 /* m_monnam() suppresses "the" prefix plus "invisible", and
486 it overrides the effect of Hallucination on priestname() */
487 Strcat(buf
, m_monnam(mtmp
));
489 Strcat(buf
, mptr
->mname
);
491 Sprintf(eos(buf
), " called %s", MNAME(mtmp
));
494 Strcpy(killer
.name
, buf
);
495 if (mptr
->mlet
== S_WRAITH
)
496 u
.ugrave_arise
= PM_WRAITH
;
497 else if (mptr
->mlet
== S_MUMMY
&& urace
.mummynum
!= NON_PM
)
498 u
.ugrave_arise
= urace
.mummynum
;
499 else if (mptr
->mlet
== S_VAMPIRE
&& Race_if(PM_HUMAN
))
500 u
.ugrave_arise
= PM_VAMPIRE
;
501 else if (mptr
== &mons
[PM_GHOUL
])
502 u
.ugrave_arise
= PM_GHOUL
;
503 /* this could happen if a high-end vampire kills the hero
504 when ordinary vampires are genocided; ditto for wraiths */
505 if (u
.ugrave_arise
>= LOW_PM
506 && (mvitals
[u
.ugrave_arise
].mvflags
& G_GENOD
))
507 u
.ugrave_arise
= NON_PM
;
513 /* some special cases for overriding while-helpless reason */
514 static const struct {
516 const char *exclude
, *include
;
518 /* "petrified by <foo>, while getting stoned" -- "while getting stoned"
519 prevented any last-second recovery, but it was not the cause of
520 "petrified by <foo>" */
521 { STONING
, 1, "getting stoned", (char *) 0 },
522 /* "died of starvation, while fainted from lack of food" is accurate
523 but sounds a fairly silly (and doesn't actually appear unless you
524 splice together death and while-helpless from xlogfile) */
525 { STARVING
, 0, "fainted from lack of food", "fainted" },
528 /* clear away while-helpless when the cause of death caused that
529 helplessness (ie, "petrified by <foo> while getting stoned") */
537 for (i
= 0; i
< SIZE(death_fixups
); ++i
)
538 if (death_fixups
[i
].why
== how
539 && !strcmp(death_fixups
[i
].exclude
, multi_reason
)) {
540 if (death_fixups
[i
].include
) /* substitute alternate reason */
541 multi_reason
= death_fixups
[i
].include
;
542 else /* remove the helplessness reason */
543 multi_reason
= (char *) 0;
544 if (death_fixups
[i
].unmulti
) /* possibly hide helplessness */
551 #if defined(WIN32) && !defined(SYSCF)
552 #define NOTIFY_ANETHACK_BUGS
557 VA_DECL(const char *, str
)
560 VA_INIT(str
, char *);
562 if (program_state
.panicking
++)
563 NH_abort(); /* avoid loops - this should never happen*/
565 if (iflags
.window_inited
) {
566 raw_print("\r\nOops...");
567 wait_synch(); /* make sure all pending output gets flushed */
568 exit_nhwindows((char *) 0);
569 iflags
.window_inited
= 0; /* they're gone; force raw_print()ing */
572 raw_print(program_state
.gameover
573 ? "Postgame wrapup disrupted."
574 : !program_state
.something_worth_saving
575 ? "Program initialization has failed."
576 : "Suddenly, the dungeon collapses.");
578 #if defined(NOTIFY_ANETHACK_BUGS)
580 raw_printf("Report the following error to \"%s\" or at \"%s\".",
581 DEVTEAM_EMAIL
, DEVTEAM_URL
);
582 else if (program_state
.something_worth_saving
)
583 raw_print("\nError save file being written.\n");
586 const char *maybe_rebuild
= !program_state
.something_worth_saving
588 : "\nand it may be possible to rebuild.";
591 raw_printf("To report this error, %s%s", sysopt
.support
,
593 else if (sysopt
.fmtd_wizard_list
) /* formatted SYSCF WIZARDS */
594 raw_printf("To report this error, contact %s%s",
595 sysopt
.fmtd_wizard_list
, maybe_rebuild
);
597 raw_printf("Report error to \"%s\"%s", WIZARD_NAME
,
601 /* XXX can we move this above the prints? Then we'd be able to
602 * suppress "it may be possible to rebuild" based on dosave0()
603 * or say it's NOT possible to rebuild. */
604 if (program_state
.something_worth_saving
) {
605 set_error_savefile();
607 /* os/win port specific recover instructions */
609 raw_printf("%s", sysopt
.recover
);
616 Vsprintf(buf
, str
, VA_ARGS
);
618 paniclog("panic", buf
);
621 interject(INTERJECT_PANIC
);
623 #if defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32)
625 NH_abort(); /* generate core dump */
628 really_done(PANICKED
);
632 should_query_disclose_option(category
, defquery
)
640 if ((dop
= index(disclosure_options
, category
)) != 0) {
641 idx
= (int) (dop
- disclosure_options
);
642 if (idx
< 0 || idx
>= NUM_DISCLOSURE_OPTIONS
) {
644 "should_query_disclose_option: bad disclosure index %d %c",
646 *defquery
= DISCLOSE_PROMPT_DEFAULT_YES
;
649 disclose
= flags
.end_disclose
[idx
];
650 if (disclose
== DISCLOSE_YES_WITHOUT_PROMPT
) {
653 } else if (disclose
== DISCLOSE_SPECIAL_WITHOUT_PROMPT
) {
656 } else if (disclose
== DISCLOSE_NO_WITHOUT_PROMPT
) {
659 } else if (disclose
== DISCLOSE_PROMPT_DEFAULT_YES
) {
662 } else if (disclose
== DISCLOSE_PROMPT_DEFAULT_SPECIAL
) {
670 impossible("should_query_disclose_option: bad category %c", category
);
679 char buf
[BUFSZ
], **strp
;
680 extern char *saved_plines
[];
681 extern unsigned saved_pline_index
;
685 putstr(0, 0, "Latest messages:");
686 for (i
= 0, j
= (int) saved_pline_index
; i
< DUMPLOG_MSG_COUNT
;
687 ++i
, j
= (j
+ 1) % DUMPLOG_MSG_COUNT
) {
688 strp
= &saved_plines
[j
];
690 copynchars(&buf
[1], *strp
, BUFSZ
- 1 - 1);
692 #ifdef FREE_ALL_MEMORY
693 free(*strp
), *strp
= 0;
709 if (!iflags
.in_dumplog
)
714 for (obj
= invent
; obj
; obj
= obj
->nobj
) {
715 makeknown(obj
->otyp
);
716 obj
->known
= obj
->bknown
= obj
->dknown
= obj
->rknown
= 1;
717 if (Is_container(obj
) || obj
->otyp
== STATUE
)
718 obj
->cknown
= obj
->lknown
= 1;
721 Sprintf(pbuf
, "%s, %s %s %s %s", plname
,
722 aligns
[1 - u
.ualign
.type
].adj
,
723 genders
[flags
.female
].adj
,
725 (flags
.female
&& urole
.name
.f
) ? urole
.name
.f
: urole
.name
.m
);
730 putstr(0, 0, do_statusline1());
731 putstr(0, 0, do_statusline2());
736 putstr(0, 0, "Inventory:");
737 display_inventory((char *) 0, TRUE
);
738 container_contents(invent
, TRUE
, TRUE
, FALSE
);
739 enlightenment((BASICENLIGHTENMENT
| MAGICENLIGHTENMENT
),
740 (how
>= PANICKED
) ? ENL_GAMEOVERALIVE
: ENL_GAMEOVERDEAD
);
742 list_vanquished('d', FALSE
); /* 'd' => 'y' */
744 list_genocided('d', FALSE
); /* 'd' => 'y' */
746 show_conduct((how
>= PANICKED
) ? 1 : 2);
748 show_overview((how
>= PANICKED
) ? 1 : 2, how
);
750 dump_redirect(FALSE
);
761 char c
= '\0', defquery
;
765 if (invent
&& !done_stopprint
) {
767 Sprintf(qbuf
, "Do you want to see what you had when you %s?",
768 (how
== QUIT
) ? "quit" : "died");
770 Strcpy(qbuf
, "Do you want your possessions identified?");
772 ask
= should_query_disclose_option('i', &defquery
);
773 c
= ask
? yn_function(qbuf
, ynqchars
, defquery
) : defquery
;
777 for (obj
= invent
; obj
; obj
= obj
->nobj
) {
778 makeknown(obj
->otyp
);
779 obj
->known
= obj
->bknown
= obj
->dknown
= obj
->rknown
= 1;
780 if (Is_container(obj
) || obj
->otyp
== STATUE
)
781 obj
->cknown
= obj
->lknown
= 1;
783 (void) display_inventory((char *) 0, TRUE
);
784 container_contents(invent
, TRUE
, TRUE
, FALSE
);
790 if (!done_stopprint
) {
791 ask
= should_query_disclose_option('a', &defquery
);
792 c
= ask
? yn_function("Do you want to see your attributes?", ynqchars
,
796 enlightenment((BASICENLIGHTENMENT
| MAGICENLIGHTENMENT
),
797 (how
>= PANICKED
) ? ENL_GAMEOVERALIVE
803 if (!done_stopprint
) {
804 ask
= should_query_disclose_option('v', &defquery
);
805 list_vanquished(defquery
, ask
);
808 if (!done_stopprint
) {
809 ask
= should_query_disclose_option('g', &defquery
);
810 list_genocided(defquery
, ask
);
813 if (!done_stopprint
) {
814 ask
= should_query_disclose_option('c', &defquery
);
815 c
= ask
? yn_function("Do you want to see your conduct?", ynqchars
,
819 show_conduct((how
>= PANICKED
) ? 1 : 2);
824 if (!done_stopprint
) {
825 ask
= should_query_disclose_option('o', &defquery
);
826 c
= ask
? yn_function("Do you want to see the dungeon overview?",
830 show_overview((how
>= PANICKED
) ? 1 : 2, how
);
836 /* try to get the player back in a viable state after being killed */
841 int uhpmin
= max(2 * u
.ulevel
, 10);
843 if (u
.uhpmax
< uhpmin
)
847 if (u
.uhunger
< 500) {
851 /* cure impending doom of sickness hero won't have time to fix */
852 if ((Sick
& TIMEOUT
) == 1L) {
854 set_itimeout(&Sick
, 0L);
858 nomovemsg
= "You survived that attempt on your life.";
864 if (u
.utrap
&& u
.utraptype
== TT_LAVA
)
867 u
.ugrave_arise
= NON_PM
;
870 if (!context
.mon_moving
)
875 * Get valuables from the given list. Revised code: the list always remains
880 struct obj
*list
; /* inventory or container contents */
882 register struct obj
*obj
;
885 /* find amulets and gems, ignoring all artifacts */
886 for (obj
= list
; obj
; obj
= obj
->nobj
)
887 if (Has_contents(obj
)) {
888 get_valuables(obj
->cobj
);
889 } else if (obj
->oartifact
) {
891 } else if (obj
->oclass
== AMULET_CLASS
) {
892 i
= obj
->otyp
- FIRST_AMULET
;
893 if (!amulets
[i
].count
) {
894 amulets
[i
].count
= obj
->quan
;
895 amulets
[i
].typ
= obj
->otyp
;
897 amulets
[i
].count
+= obj
->quan
; /* always adds one */
898 } else if (obj
->oclass
== GEM_CLASS
&& obj
->otyp
< LUCKSTONE
) {
899 i
= min(obj
->otyp
, LAST_GEM
+ 1) - FIRST_GEM
;
900 if (!gems
[i
].count
) {
901 gems
[i
].count
= obj
->quan
;
902 gems
[i
].typ
= obj
->otyp
;
904 gems
[i
].count
+= obj
->quan
;
910 * Sort collected valuables, most frequent to least. We could just
911 * as easily use qsort, but we don't care about efficiency here.
914 sort_valuables(list
, size
)
915 struct valuable_data list
[];
916 int size
; /* max value is less than 20 */
919 struct valuable_data ltmp
;
921 /* move greater quantities to the front of the list */
922 for (i
= 1; i
< size
; i
++) {
923 if (list
[i
].count
== 0)
924 continue; /* empty slot */
925 ltmp
= list
[i
]; /* structure copy */
926 for (j
= i
; j
> 0; --j
)
927 if (list
[j
- 1].count
>= ltmp
.count
)
930 list
[j
] = list
[j
- 1];
940 odds_and_ends(list
, what
)
945 for (otmp
= list
; otmp
; otmp
= otmp
->nobj
) {
947 case CAT_CHECK
: /* Schroedinger's Cat */
948 /* Ascending is deterministic */
949 if (SchroedingersBox(otmp
))
953 if (Has_contents(otmp
))
954 return odds_and_ends(otmp
->cobj
, what
);
959 /* called twice; first to calculate total, then to list relevant items */
961 artifact_score(list
, counting
, endwin
)
963 boolean counting
; /* true => add up points; false => display them */
969 short dummy
; /* object type returned by artifact_name() */
971 for (otmp
= list
; otmp
; otmp
= otmp
->nobj
) {
972 if (otmp
->oartifact
|| otmp
->otyp
== BELL_OF_OPENING
973 || otmp
->otyp
== SPE_BOOK_OF_THE_DEAD
974 || otmp
->otyp
== CANDELABRUM_OF_INVOCATION
) {
975 value
= arti_cost(otmp
); /* zorkmid value */
976 points
= value
* 5 / 2; /* score value */
978 nowrap_add(u
.urexp
, points
);
980 makeknown(otmp
->otyp
);
981 otmp
->known
= otmp
->dknown
= otmp
->bknown
= otmp
->rknown
= 1;
982 /* assumes artifacts don't have quan > 1 */
983 Sprintf(pbuf
, "%s%s (worth %ld %s and %ld points)",
984 the_unique_obj(otmp
) ? "The " : "",
985 otmp
->oartifact
? artifact_name(xname(otmp
), &dummy
)
986 : OBJ_NAME(objects
[otmp
->otyp
]),
987 value
, currency(value
), points
);
988 putstr(endwin
, 0, pbuf
);
991 if (Has_contents(otmp
))
992 artifact_score(otmp
->cobj
, counting
, endwin
);
996 /* Be careful not to call panic from here! */
1001 if (how
== TRICKED
) {
1002 if (killer
.name
[0]) {
1003 paniclog("trickery", killer
.name
);
1007 You("are a very tricky wizard, it seems.");
1011 if (program_state
.panicking
1012 #ifdef HANGUPHANDLING
1013 || program_state
.done_hup
1016 /* skip status update if panicking or disconnected */
1017 context
.botl
= context
.botlx
= FALSE
;
1019 /* otherwise force full status update */
1020 context
.botlx
= TRUE
;
1024 if (how
== ASCENDED
|| (!killer
.name
[0] && how
== GENOCIDED
))
1025 killer
.format
= NO_KILLER_PREFIX
;
1026 /* Avoid killed by "a" burning or "a" starvation */
1027 if (!killer
.name
[0] && (how
== STARVING
|| how
== BURNING
))
1028 killer
.format
= KILLED_BY
;
1029 if (!killer
.name
[0] || how
>= PANICKED
)
1030 Strcpy(killer
.name
, deaths
[how
]);
1034 if (Lifesaved
&& (how
<= GENOCIDED
)) {
1035 pline("But wait...");
1036 makeknown(AMULET_OF_LIFE_SAVING
);
1037 Your("medallion %s!", !Blind
? "begins to glow" : "feels warm");
1040 You_feel("much better!");
1041 pline_The("medallion crumbles to dust!");
1045 (void) adjattrib(A_CON
, -1, TRUE
);
1047 if (how
== GENOCIDED
) {
1048 pline("Unfortunately you are still genocided...");
1055 if ((wizard
|| discover
) && (how
<= GENOCIDED
)
1056 && !paranoid_query(ParanoidDie
, "Die?")) {
1057 pline("OK, so you don't %s.", (how
== CHOKING
) ? "choke" : "die");
1066 /* separated from done() in order to specify the __noreturn__ attribute */
1073 winid endwin
= WIN_ERR
;
1074 boolean bones_ok
, have_windows
= iflags
.window_inited
;
1075 struct obj
*corpse
= (struct obj
*) 0;
1081 * The game is now over...
1083 program_state
.gameover
= 1;
1084 /* in case of a subsequent panic(), there's no point trying to save */
1085 program_state
.something_worth_saving
= 0;
1086 /* render vision subsystem inoperative */
1087 iflags
.vision_inited
= 0;
1089 /* might have been killed while using a disposable item, so make sure
1090 it's gone prior to inventory disclosure and creation of bones data */
1092 /* maybe not on object lists; if an active light source, would cause
1093 big trouble (`obj_is_local' panic) for savebones() -> savelev() */
1094 if (thrownobj
&& thrownobj
->where
== OBJ_FREE
)
1095 dealloc_obj(thrownobj
);
1096 if (kickedobj
&& kickedobj
->where
== OBJ_FREE
)
1097 dealloc_obj(kickedobj
);
1099 /* remember time of death here instead of having bones, rip, and
1100 topten figure it out separately and possibly getting different
1101 time or even day if player is slow responding to --More-- */
1102 urealtime
.finish_time
= endtime
= getnow();
1103 urealtime
.realtime
+= (long) (endtime
- urealtime
.start_timing
);
1105 dump_open_log(endtime
);
1106 /* Sometimes you die on the first move. Life's not fair.
1107 * On those rare occasions you get hosed immediately, go out
1108 * smiling... :-) -3.
1110 if (moves
<= 1 && how
< PANICKED
) /* You die... --More-- */
1111 pline("Do not pass go. Do not collect 200 %s.", currency(200L));
1114 wait_synch(); /* flush screen output */
1116 (void) signal(SIGINT
, (SIG_RET_TYPE
) done_intr
);
1117 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
1118 (void) signal(SIGQUIT
, (SIG_RET_TYPE
) done_intr
);
1119 sethanguphandler(done_hangup
);
1121 #endif /* NO_SIGNAL */
1123 bones_ok
= (how
< GENOCIDED
) && can_make_bones();
1125 if (bones_ok
&& launch_in_progress())
1126 force_launch_placement();
1128 /* maintain ugrave_arise even for !bones_ok */
1129 if (how
== PANICKED
)
1130 u
.ugrave_arise
= (NON_PM
- 3); /* no corpse, no grave */
1131 else if (how
== BURNING
|| how
== DISSOLVED
) /* corpse burns up too */
1132 u
.ugrave_arise
= (NON_PM
- 2); /* leave no corpse */
1133 else if (how
== STONING
)
1134 u
.ugrave_arise
= (NON_PM
- 1); /* statue instead of corpse */
1135 else if (how
== TURNED_SLIME
)
1136 u
.ugrave_arise
= PM_GREEN_SLIME
;
1138 /* if pets will contribute to score, populate mydogs list now
1139 (bones creation isn't a factor, but pline() messaging is) */
1140 if (how
== ESCAPED
|| how
== ASCENDED
)
1144 killer
.format
= NO_KILLER_PREFIX
;
1147 u
.umortality
++; /* skipped above when how==QUIT */
1148 Strcpy(killer
.name
, "quit while already on Charon's boat");
1151 if (how
== ESCAPED
|| how
== PANICKED
)
1152 killer
.format
= NO_KILLER_PREFIX
;
1154 fixup_death(how
); /* actually, fixup multi_reason */
1156 if (how
!= PANICKED
) {
1157 /* these affect score and/or bones, but avoid them during panic */
1158 taken
= paybill((how
== ESCAPED
) ? -1 : (how
!= QUIT
));
1162 taken
= FALSE
; /* lint; assert( !bones_ok ); */
1167 display_nhwindow(WIN_MESSAGE
, FALSE
);
1169 if (strcmp(flags
.end_disclose
, "none") && how
!= PANICKED
)
1170 disclose(how
, taken
);
1172 dump_everything(how
);
1174 /* finish_paybill should be called after disclosure but before bones */
1175 if (bones_ok
&& taken
)
1178 /* grave creation should be after disclosure so it doesn't have
1179 this grave in the current level's features for #overview */
1180 if (bones_ok
&& u
.ugrave_arise
== NON_PM
1181 && !(mvitals
[u
.umonnum
].mvflags
& G_NOCORPSE
)) {
1182 int mnum
= u
.umonnum
;
1185 /* Base corpse on race when not poly'd since original
1186 * u.umonnum is based on role, and all role monsters
1189 mnum
= (flags
.female
&& urace
.femalenum
!= NON_PM
)
1193 corpse
= mk_named_object(CORPSE
, &mons
[mnum
], u
.ux
, u
.uy
, plname
);
1194 Sprintf(pbuf
, "%s, ", plname
);
1195 formatkiller(eos(pbuf
), sizeof pbuf
- strlen(pbuf
), how
, TRUE
);
1196 make_grave(u
.ux
, u
.uy
, pbuf
);
1198 pbuf
[0] = '\0'; /* clear grave text; also lint suppression */
1200 /* calculate score, before creating bones [container gold] */
1202 int deepest
= deepest_lev_reached(FALSE
);
1204 umoney
= money_cnt(invent
);
1206 umoney
+= hidden_gold(); /* accumulate gold from containers */
1207 tmp
= umoney
- tmp
; /* net gain */
1213 tmp
+= 50L * (long) (deepest
- 1);
1215 tmp
+= 1000L * (long) ((deepest
> 30) ? 10 : deepest
- 20);
1216 nowrap_add(u
.urexp
, tmp
);
1218 /* ascension gives a score bonus iff offering to original deity */
1219 if (how
== ASCENDED
&& u
.ualign
.type
== u
.ualignbase
[A_ORIGINAL
]) {
1220 /* retaining original alignment: score *= 2;
1221 converting, then using helm-of-OA to switch back: *= 1.5 */
1222 tmp
= (u
.ualignbase
[A_CURRENT
] == u
.ualignbase
[A_ORIGINAL
])
1225 nowrap_add(u
.urexp
, tmp
);
1229 if (u
.ugrave_arise
>= LOW_PM
&& u
.ugrave_arise
!= PM_GREEN_SLIME
) {
1230 /* give this feedback even if bones aren't going to be created,
1231 so that its presence or absence doesn't tip off the player to
1232 new bones or their lack; it might be a lie if makemon fails */
1233 Your("body rises from the dead as %s...",
1234 an(mons
[u
.ugrave_arise
].mname
));
1235 display_nhwindow(WIN_MESSAGE
, FALSE
);
1239 if (!wizard
|| paranoid_query(ParanoidBones
, "Save bones?"))
1240 savebones(how
, endtime
, corpse
);
1241 /* corpse may be invalid pointer now so
1242 ensure that it isn't used again */
1243 corpse
= (struct obj
*) 0;
1246 /* update gold for the rip output, which can't use hidden_gold()
1247 (containers will be gone by then if bones just got saved...) */
1248 done_money
= umoney
;
1250 /* clean up unneeded windows */
1253 free_pickinv_cache(); /* extra persistent window if perm_invent */
1254 if (WIN_INVEN
!= WIN_ERR
)
1255 destroy_nhwindow(WIN_INVEN
), WIN_INVEN
= WIN_ERR
;
1256 display_nhwindow(WIN_MESSAGE
, TRUE
);
1257 destroy_nhwindow(WIN_MAP
), WIN_MAP
= WIN_ERR
;
1258 #ifndef STATUS_VIA_WINDOWPORT
1259 destroy_nhwindow(WIN_STATUS
), WIN_STATUS
= WIN_ERR
;
1261 destroy_nhwindow(WIN_MESSAGE
), WIN_MESSAGE
= WIN_ERR
;
1263 if (!done_stopprint
|| flags
.tombstone
)
1264 endwin
= create_nhwindow(NHW_TEXT
);
1266 if (how
< GENOCIDED
&& flags
.tombstone
&& endwin
!= WIN_ERR
)
1267 outrip(endwin
, how
, endtime
);
1269 done_stopprint
= 1; /* just avoid any more output */
1272 /* 'how' reasons beyond genocide shouldn't show tombstone;
1273 for normal end of game, genocide doesn't either */
1274 if (how
<= GENOCIDED
) {
1275 dump_redirect(TRUE
);
1276 genl_outrip(0, how
, endtime
);
1277 dump_redirect(FALSE
);
1280 if (u
.uhave
.amulet
) {
1281 Strcat(killer
.name
, " (with the Amulet)");
1282 } else if (how
== ESCAPED
) {
1283 if (Is_astralevel(&u
.uz
)) /* offered Amulet to wrong deity */
1284 Strcat(killer
.name
, " (in celestial disgrace)");
1285 else if (carrying(FAKE_AMULET_OF_YENDOR
))
1286 Strcat(killer
.name
, " (with a fake Amulet)");
1287 /* don't bother counting to see whether it should be plural */
1290 Sprintf(pbuf
, "%s %s the %s...", Goodbye(), plname
,
1292 ? (const char *) ((flags
.female
&& urole
.name
.f
)
1295 : (const char *) (flags
.female
? "Demigoddess" : "Demigod"));
1296 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1297 dump_forward_putstr(endwin
, 0, "", done_stopprint
);
1299 if (how
== ESCAPED
|| how
== ASCENDED
) {
1302 register struct val_list
*val
;
1305 for (val
= valuables
; val
->list
; val
++)
1306 for (i
= 0; i
< val
->size
; i
++) {
1307 val
->list
[i
].count
= 0L;
1309 get_valuables(invent
);
1311 /* add points for collected valuables */
1312 for (val
= valuables
; val
->list
; val
++)
1313 for (i
= 0; i
< val
->size
; i
++)
1314 if (val
->list
[i
].count
!= 0L) {
1315 tmp
= val
->list
[i
].count
1316 * (long) objects
[val
->list
[i
].typ
].oc_cost
;
1317 nowrap_add(u
.urexp
, tmp
);
1320 /* count the points for artifacts */
1321 artifact_score(invent
, TRUE
, endwin
);
1323 dump_redirect(TRUE
);
1324 artifact_score(invent
, TRUE
, endwin
);
1325 dump_redirect(FALSE
);
1328 viz_array
[0][0] |= IN_SIGHT
; /* need visibility for naming */
1330 Strcpy(pbuf
, "You");
1331 if (!Schroedingers_cat
) /* check here in case disclosure was off */
1332 Schroedingers_cat
= odds_and_ends(invent
, CAT_CHECK
);
1333 if (Schroedingers_cat
) {
1334 int mhp
, m_lev
= adj_lev(&mons
[PM_HOUSECAT
]);
1336 nowrap_add(u
.urexp
, mhp
);
1337 Strcat(eos(pbuf
), " and Schroedinger's cat");
1341 Sprintf(eos(pbuf
), " and %s", mon_nam(mtmp
));
1343 nowrap_add(u
.urexp
, mtmp
->mhp
);
1346 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1351 Sprintf(eos(pbuf
), "%s with %ld point%s,",
1352 how
== ASCENDED
? "went to your reward"
1353 : "escaped from the dungeon",
1354 u
.urexp
, plur(u
.urexp
));
1355 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1357 if (!done_stopprint
)
1358 artifact_score(invent
, FALSE
, endwin
); /* list artifacts */
1360 dump_redirect(TRUE
);
1361 artifact_score(invent
, FALSE
, 0);
1362 dump_redirect(FALSE
);
1365 /* list valuables here */
1366 for (val
= valuables
; val
->list
; val
++) {
1367 sort_valuables(val
->list
, val
->size
);
1368 for (i
= 0; i
< val
->size
&& !done_stopprint
; i
++) {
1369 int typ
= val
->list
[i
].typ
;
1370 long count
= val
->list
[i
].count
;
1374 if (objects
[typ
].oc_class
!= GEM_CLASS
|| typ
<= LAST_GEM
) {
1375 otmp
= mksobj(typ
, FALSE
, FALSE
);
1376 makeknown(otmp
->otyp
);
1377 otmp
->known
= 1; /* for fake amulets */
1378 otmp
->dknown
= 1; /* seen it (blindness fix) */
1379 if (has_oname(otmp
))
1382 Sprintf(pbuf
, "%8ld %s (worth %ld %s),", count
,
1383 xname(otmp
), count
* (long) objects
[typ
].oc_cost
,
1385 obfree(otmp
, (struct obj
*) 0);
1387 Sprintf(pbuf
, "%8ld worthless piece%s of colored glass,",
1388 count
, plur(count
));
1390 dump_forward_putstr(endwin
, 0, pbuf
, 0);
1395 /* did not escape or ascend */
1396 if (u
.uz
.dnum
== 0 && u
.uz
.dlevel
<= 0) {
1397 /* level teleported out of the dungeon; `how' is DIED,
1398 due to falling or to "arriving at heaven prematurely" */
1399 Sprintf(pbuf
, "You %s beyond the confines of the dungeon",
1400 (u
.uz
.dlevel
< 0) ? "passed away" : ends
[how
]);
1402 /* more conventional demise */
1403 const char *where
= dungeons
[u
.uz
.dnum
].dname
;
1405 if (Is_astralevel(&u
.uz
))
1406 where
= "The Astral Plane";
1407 Sprintf(pbuf
, "You %s in %s", ends
[how
], where
);
1408 if (!In_endgame(&u
.uz
) && !Is_knox(&u
.uz
))
1409 Sprintf(eos(pbuf
), " on dungeon level %d",
1410 In_quest(&u
.uz
) ? dunlev(&u
.uz
) : depth(&u
.uz
));
1413 Sprintf(eos(pbuf
), " with %ld point%s,", u
.urexp
, plur(u
.urexp
));
1414 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1417 Sprintf(pbuf
, "and %ld piece%s of gold, after %ld move%s.", umoney
,
1418 plur(umoney
), moves
, plur(moves
));
1419 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1421 "You were level %d with a maximum of %d hit point%s when you %s.",
1422 u
.ulevel
, u
.uhpmax
, plur(u
.uhpmax
), ends
[how
]);
1423 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1424 dump_forward_putstr(endwin
, 0, "", done_stopprint
);
1425 if (!done_stopprint
)
1426 display_nhwindow(endwin
, TRUE
);
1427 if (endwin
!= WIN_ERR
)
1428 destroy_nhwindow(endwin
);
1431 /* "So when I die, the first thing I will see in Heaven is a
1433 if (have_windows
&& !iflags
.toptenwin
)
1434 exit_nhwindows((char *) 0), have_windows
= FALSE
;
1435 topten(how
, endtime
);
1437 exit_nhwindows((char *) 0);
1439 if (done_stopprint
) {
1443 terminate(EXIT_SUCCESS
);
1447 container_contents(list
, identified
, all_containers
, reportempty
)
1449 boolean identified
, all_containers
, reportempty
;
1451 register struct obj
*box
, *obj
;
1453 boolean cat
, deadcat
;
1455 for (box
= list
; box
; box
= box
->nobj
) {
1456 if (Is_container(box
) || box
->otyp
== STATUE
) {
1457 box
->cknown
= 1; /* we're looking at the contents now */
1460 cat
= deadcat
= FALSE
;
1461 if (SchroedingersBox(box
) && !Schroedingers_cat
) {
1462 /* Schroedinger's Cat? */
1463 cat
= odds_and_ends(box
, CAT_CHECK
);
1465 Schroedingers_cat
= TRUE
;
1470 if (box
->otyp
== BAG_OF_TRICKS
) {
1471 continue; /* wrong type of container */
1472 } else if (box
->cobj
) {
1473 winid tmpwin
= create_nhwindow(NHW_MENU
);
1475 sortloot(&box
->cobj
,
1476 (((flags
.sortloot
== 'l' || flags
.sortloot
== 'f')
1477 ? SORTLOOT_LOOT
: 0)
1478 | (flags
.sortpack
? SORTLOOT_PACK
: 0)),
1480 Sprintf(buf
, "Contents of %s:", the(xname(box
)));
1481 putstr(tmpwin
, 0, buf
);
1482 putstr(tmpwin
, 0, "");
1483 for (obj
= box
->cobj
; obj
; obj
= obj
->nobj
) {
1485 makeknown(obj
->otyp
);
1486 obj
->known
= obj
->bknown
= obj
->dknown
1488 if (Is_container(obj
) || obj
->otyp
== STATUE
)
1489 obj
->cknown
= obj
->lknown
= 1;
1491 putstr(tmpwin
, 0, doname(obj
));
1494 putstr(tmpwin
, 0, "Schroedinger's cat");
1496 putstr(tmpwin
, 0, "Schroedinger's dead cat");
1497 display_nhwindow(tmpwin
, TRUE
);
1498 destroy_nhwindow(tmpwin
);
1500 container_contents(box
->cobj
, identified
, TRUE
,
1502 } else if (cat
|| deadcat
) {
1503 pline("%s Schroedinger's %scat!", Tobjnam(box
, "contain"),
1504 deadcat
? "dead " : "");
1505 display_nhwindow(WIN_MESSAGE
, FALSE
);
1506 } else if (reportempty
) {
1507 pline("%s is empty.", upstart(thesimpleoname(box
)));
1508 display_nhwindow(WIN_MESSAGE
, FALSE
);
1511 if (!all_containers
)
1516 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
1521 program_state
.in_moveloop
= 0; /* won't be returning to normal play */
1523 getreturn("to exit");
1525 /* don't bother to try to release memory if we're in panic mode, to
1526 avoid trouble in case that happens to be due to memory problems */
1527 if (!program_state
.panicking
) {
1534 * This is liable to draw a warning if compiled with gcc, but it's
1535 * more important to flag panic() -> really_done() -> terminate()
1536 * as __noreturn__ then to avoid the warning.
1538 /* don't call exit() if already executing within an exit handler;
1539 that would cancel any other pending user-mode handlers */
1540 if (program_state
.exiting
)
1543 program_state
.exiting
= 1;
1544 anethack_exit(status
);
1547 extern const int monstr
[];
1549 static const char *vanqorders
[] = {
1550 "traditional: by monster level, by internal monster index",
1551 #define VANQ_MLVL_MNDX 0
1552 "by monster toughness, by internal monster index",
1553 #define VANQ_MSTR_MNDX 1
1554 "alphabetically, first unique monsters, then others",
1555 #define VANQ_ALPHA_SEP 2
1556 "alphabetically, unique monsters and others intermixed",
1557 #define VANQ_ALPHA_MIX 3
1558 "by monster class, high to low level within class",
1559 #define VANQ_MCLS_HTOL 4
1560 "by monster class, low to high level within class",
1561 #define VANQ_MCLS_LTOH 5
1562 "by count, high to low, by internal index within tied count",
1563 #define VANQ_COUNT_H_L 6
1564 "by count, low to high, by internal index within tied count",
1565 #define VANQ_COUNT_L_H 7
1567 static int vanq_sortmode
= VANQ_MLVL_MNDX
;
1569 STATIC_PTR
int CFDECLSPEC
1570 vanqsort_cmp(vptr1
, vptr2
)
1571 const genericptr vptr1
;
1572 const genericptr vptr2
;
1574 int indx1
= *(short *) vptr1
, indx2
= *(short *) vptr2
,
1575 mlev1
, mlev2
, mstr1
, mstr2
, uniq1
, uniq2
, died1
, died2
, res
;
1576 const char *name1
, *name2
, *punct
;
1579 switch (vanq_sortmode
) {
1581 case VANQ_MLVL_MNDX
:
1582 /* sort by monster level */
1583 mlev1
= mons
[indx1
].mlevel
, mlev2
= mons
[indx2
].mlevel
;
1584 res
= mlev2
- mlev1
; /* mlevel high to low */
1586 case VANQ_MSTR_MNDX
:
1587 /* sort by monster toughness */
1588 mstr1
= monstr
[indx1
], mstr2
= monstr
[indx2
];
1589 res
= mstr2
- mstr1
; /* monstr high to low */
1591 case VANQ_ALPHA_SEP
:
1592 uniq1
= ((mons
[indx1
].geno
& G_UNIQ
) && indx1
!= PM_HIGH_PRIEST
);
1593 uniq2
= ((mons
[indx2
].geno
& G_UNIQ
) && indx2
!= PM_HIGH_PRIEST
);
1594 if (uniq1
^ uniq2
) { /* one or other uniq, but not both */
1595 res
= uniq2
- uniq1
;
1597 } /* else both unique or neither unique */
1599 case VANQ_ALPHA_MIX
:
1600 name1
= mons
[indx1
].mname
, name2
= mons
[indx2
].mname
;
1601 res
= strcmpi(name1
, name2
); /* caseblind alhpa, low to high */
1603 case VANQ_MCLS_HTOL
:
1604 case VANQ_MCLS_LTOH
:
1605 /* mons[].mlet is a small integer, 1..N, of type plain char;
1606 if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
1607 an inappropriate result when mlet2 is greater than mlet1,
1608 so force our copies (mcls1, mcls2) to be signed */
1609 mcls1
= (schar
) mons
[indx1
].mlet
, mcls2
= (schar
) mons
[indx2
].mlet
;
1610 /* S_ANT through S_ZRUTY correspond to lowercase monster classes,
1611 S_ANGEL through S_ZOMBIE correspond to uppercase, and various
1612 punctuation characters are used for classes beyond those */
1613 if (mcls1
> S_ZOMBIE
&& mcls2
> S_ZOMBIE
) {
1614 /* force a specific order to the punctuation classes that's
1615 different from the internal order;
1616 internal order is ok if neither or just one is punctuation
1617 since letters have lower values so come out before punct */
1618 static const char punctclasses
[] = {
1619 S_LIZARD
, S_EEL
, S_GOLEM
, S_GHOST
, S_DEMON
, S_HUMAN
, '\0'
1622 if ((punct
= index(punctclasses
, mcls1
)) != 0)
1623 mcls1
= (schar
) (S_ZOMBIE
+ 1 + (int) (punct
- punctclasses
));
1624 if ((punct
= index(punctclasses
, mcls2
)) != 0)
1625 mcls2
= (schar
) (S_ZOMBIE
+ 1 + (int) (punct
- punctclasses
));
1627 res
= mcls1
- mcls2
; /* class */
1629 mlev1
= mons
[indx1
].mlevel
, mlev2
= mons
[indx2
].mlevel
;
1630 res
= mlev1
- mlev2
; /* mlevel low to high */
1631 if (vanq_sortmode
== VANQ_MCLS_HTOL
)
1632 res
= -res
; /* mlevel high to low */
1635 case VANQ_COUNT_H_L
:
1636 case VANQ_COUNT_L_H
:
1637 died1
= mvitals
[indx1
].died
, died2
= mvitals
[indx2
].died
;
1638 res
= died2
- died1
; /* dead count high to low */
1639 if (vanq_sortmode
== VANQ_COUNT_L_H
)
1640 res
= -res
; /* dead count low to high */
1643 /* tiebreaker: internal mons[] index */
1645 res
= indx1
- indx2
; /* mndx low to high */
1649 /* returns -1 if cancelled via ESC */
1654 menu_item
*selected
;
1658 tmpwin
= create_nhwindow(NHW_MENU
);
1660 any
= zeroany
; /* zero out all bits */
1661 for (i
= 0; i
< SIZE(vanqorders
); i
++) {
1662 if (i
== VANQ_ALPHA_MIX
|| i
== VANQ_MCLS_HTOL
) /* skip these */
1665 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, vanqorders
[i
],
1666 (i
== vanq_sortmode
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1668 end_menu(tmpwin
, "Sort order for vanquished monster counts");
1670 n
= select_menu(tmpwin
, PICK_ONE
, &selected
);
1671 destroy_nhwindow(tmpwin
);
1673 choice
= selected
[0].item
.a_int
- 1;
1674 /* skip preselected entry if we have more than one item chosen */
1675 if (n
> 1 && choice
== vanq_sortmode
)
1676 choice
= selected
[1].item
.a_int
- 1;
1677 free((genericptr_t
) selected
);
1678 vanq_sortmode
= choice
;
1680 return (n
< 0) ? -1 : vanq_sortmode
;
1683 /* #vanquished command */
1687 list_vanquished('a', FALSE
);
1691 /* high priests aren't unique but are flagged as such to simplify something */
1692 #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
1693 && mndx != PM_HIGH_PRIEST)
1696 list_vanquished(defquery
, ask
)
1702 unsigned ntypes
, ni
;
1703 long total_killed
= 0L;
1705 short mindx
[NUMMONS
];
1706 char c
, buf
[BUFSZ
], buftoo
[BUFSZ
];
1707 boolean dumping
; /* for DUMPLOG; doesn't need to be conditional */
1709 dumping
= (defquery
== 'd');
1713 /* get totals first */
1715 for (i
= LOW_PM
; i
< NUMMONS
; i
++) {
1716 if ((nkilled
= (int) mvitals
[i
].died
) == 0)
1718 mindx
[ntypes
++] = i
;
1719 total_killed
+= (long) nkilled
;
1722 /* vanquished creatures list;
1723 * includes all dead monsters, not just those killed by the player
1726 char mlet
, prev_mlet
= 0; /* used as small integer, not character */
1727 boolean class_header
, uniq_header
, was_uniq
= FALSE
;
1729 c
= ask
? yn_function(
1730 "Do you want an account of creatures vanquished?",
1731 ynaqchars
, defquery
)
1735 if (c
== 'y' || c
== 'a') {
1736 if (c
== 'a') { /* ask player to choose sort order */
1737 /* choose value for vanq_sortmode via menu; ESC cancels list
1738 of vanquished monsters but does not set 'done_stopprint' */
1739 if (set_vanq_order() < 0)
1742 uniq_header
= (vanq_sortmode
== VANQ_ALPHA_SEP
);
1743 class_header
= (vanq_sortmode
== VANQ_MCLS_LTOH
1744 || vanq_sortmode
== VANQ_MCLS_HTOL
);
1746 klwin
= create_nhwindow(NHW_MENU
);
1747 putstr(klwin
, 0, "Vanquished creatures:");
1749 putstr(klwin
, 0, "");
1751 qsort((genericptr_t
) mindx
, ntypes
, sizeof *mindx
, vanqsort_cmp
);
1752 for (ni
= 0; ni
< ntypes
; ni
++) {
1754 nkilled
= mvitals
[i
].died
;
1755 mlet
= mons
[i
].mlet
;
1756 if (class_header
&& mlet
!= prev_mlet
) {
1757 Strcpy(buf
, def_monsyms
[(int) mlet
].explain
);
1758 putstr(klwin
, ask
? 0 : iflags
.menu_headings
,
1762 if (UniqCritterIndx(i
)) {
1763 Sprintf(buf
, "%s%s",
1764 !type_is_pname(&mons
[i
]) ? "the " : "",
1769 Sprintf(eos(buf
), " (twice)");
1772 Sprintf(eos(buf
), " (thrice)");
1775 Sprintf(eos(buf
), " (%d times)", nkilled
);
1781 if (uniq_header
&& was_uniq
) {
1782 putstr(klwin
, 0, "");
1785 /* trolls or undead might have come back,
1786 but we don't keep track of that */
1788 Strcpy(buf
, an(mons
[i
].mname
));
1790 Sprintf(buf
, "%3d %s", nkilled
,
1791 makeplural(mons
[i
].mname
));
1793 /* number of leading spaces to match 3 digit prefix */
1794 pfx
= !strncmpi(buf
, "the ", 3) ? 0
1795 : !strncmpi(buf
, "an ", 3) ? 1
1796 : !strncmpi(buf
, "a ", 2) ? 2
1797 : !digit(buf
[2]) ? 4 : 0;
1800 Sprintf(buftoo
, "%*s%s", pfx
, "", buf
);
1801 putstr(klwin
, 0, buftoo
);
1804 * if (Hallucination)
1805 * putstr(klwin, 0, "and a partridge in a pear tree");
1809 putstr(klwin
, 0, "");
1810 Sprintf(buf
, "%ld creatures vanquished.", total_killed
);
1811 putstr(klwin
, 0, buf
);
1813 display_nhwindow(klwin
, TRUE
);
1814 destroy_nhwindow(klwin
);
1816 } else if (defquery
== 'a') {
1817 /* #dovanquished rather than final disclosure, so pline() is ok */
1818 pline("No creatures have been vanquished.");
1820 } else if (dumping
) {
1821 putstr(0, 0, "No creatures were vanquished."); /* not pline() */
1826 /* number of monster species which have been genocided */
1832 for (i
= LOW_PM
; i
< NUMMONS
; ++i
) {
1833 if (mvitals
[i
].mvflags
& G_GENOD
) {
1835 if (UniqCritterIndx(i
))
1836 impossible("unique creature '%d: %s' genocided?",
1848 for (i
= LOW_PM
; i
< NUMMONS
; ++i
) {
1849 if (UniqCritterIndx(i
))
1851 if ((mvitals
[i
].mvflags
& G_GONE
) == G_EXTINCT
)
1858 list_genocided(defquery
, ask
)
1863 int ngenocided
, nextinct
;
1867 boolean dumping
; /* for DUMPLOG; doesn't need to be conditional */
1869 dumping
= (defquery
== 'd');
1873 ngenocided
= num_genocides();
1874 nextinct
= num_extinct();
1876 /* genocided or extinct species list */
1877 if (ngenocided
!= 0 || nextinct
!= 0) {
1878 Sprintf(buf
, "Do you want a list of %sspecies%s%s?",
1879 (nextinct
&& !ngenocided
) ? "extinct " : "",
1880 (ngenocided
) ? " genocided" : "",
1881 (nextinct
&& ngenocided
) ? " and extinct" : "");
1882 c
= ask
? yn_function(buf
, ynqchars
, defquery
) : defquery
;
1886 klwin
= create_nhwindow(NHW_MENU
);
1887 Sprintf(buf
, "%s%s species:",
1888 (ngenocided
) ? "Genocided" : "Extinct",
1889 (nextinct
&& ngenocided
) ? " or extinct" : "");
1890 putstr(klwin
, 0, buf
);
1892 putstr(klwin
, 0, "");
1894 for (i
= LOW_PM
; i
< NUMMONS
; i
++) {
1895 /* uniques can't be genocided but can become extinct;
1896 however, they're never reported as extinct, so skip them */
1897 if (UniqCritterIndx(i
))
1899 if (mvitals
[i
].mvflags
& G_GONE
) {
1900 Sprintf(buf
, " %s", makeplural(mons
[i
].mname
));
1902 * "Extinct" is unfortunate terminology. A species
1903 * is marked extinct when its birth limit is reached,
1904 * but there might be members of the species still
1905 * alive, contradicting the meaning of the word.
1907 if ((mvitals
[i
].mvflags
& G_GONE
) == G_EXTINCT
)
1908 Strcat(buf
, " (extinct)");
1909 putstr(klwin
, 0, buf
);
1913 putstr(klwin
, 0, "");
1914 if (ngenocided
> 0) {
1915 Sprintf(buf
, "%d species genocided.", ngenocided
);
1916 putstr(klwin
, 0, buf
);
1919 Sprintf(buf
, "%d species extinct.", nextinct
);
1920 putstr(klwin
, 0, buf
);
1923 display_nhwindow(klwin
, TRUE
);
1924 destroy_nhwindow(klwin
);
1927 } else if (dumping
) {
1928 putstr(0, 0, "No species were genocided or became extinct.");
1933 /* set a delayed killer, ensure non-delayed killer is cleared out */
1935 delayed_killer(id
, format
, killername
)
1938 const char *killername
;
1940 struct kinfo
*k
= find_delayed_killer(id
);
1942 if (k
== (struct kinfo
*) 0) {
1943 /* no match, add a new delayed killer to the list */
1944 k
= (struct kinfo
*) alloc(sizeof(struct kinfo
));
1945 (void) memset((genericptr_t
)k
, 0, sizeof(struct kinfo
));
1947 k
->next
= killer
.next
;
1952 Strcpy(k
->name
, killername
? killername
: "");
1957 find_delayed_killer(id
)
1962 for (k
= killer
.next
; k
!= (struct kinfo
*) 0; k
= k
->next
) {
1970 dealloc_killer(kptr
)
1973 struct kinfo
*prev
= &killer
, *k
;
1975 if (kptr
== (struct kinfo
*) 0)
1977 for (k
= killer
.next
; k
!= (struct kinfo
*) 0; k
= k
->next
) {
1983 if (k
== (struct kinfo
*) 0) {
1984 impossible("dealloc_killer not on list");
1986 prev
->next
= k
->next
;
1987 free((genericptr_t
) k
);
1992 save_killers(fd
, mode
)
1998 if (perform_bwrite(mode
)) {
1999 for (kptr
= &killer
; kptr
!= (struct kinfo
*) 0; kptr
= kptr
->next
) {
2000 bwrite(fd
, (genericptr_t
) kptr
, sizeof(struct kinfo
));
2003 if (release_data(mode
)) {
2004 while (killer
.next
) {
2005 kptr
= killer
.next
->next
;
2006 free((genericptr_t
) killer
.next
);
2018 for (kptr
= &killer
; kptr
!= (struct kinfo
*) 0; kptr
= kptr
->next
) {
2019 mread(fd
, (genericptr_t
) kptr
, sizeof(struct kinfo
));
2021 kptr
->next
= (struct kinfo
*) alloc(sizeof(struct kinfo
));
2033 while (*p
&& isspace((uchar
) *p
))
2037 while (*p
&& !isspace((uchar
) *p
))
2049 out
+= strlen(out
); /* eos() */
2050 while (*in
&& isspace((uchar
) *in
))
2052 while (*in
&& !isspace((uchar
) *in
))
2059 build_english_list(in
)
2063 int len
= (int) strlen(p
), words
= wordcount(p
);
2065 /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
2067 len
+= 3 + (words
- 1);
2068 out
= (char *) alloc(len
+ 1);
2069 *out
= '\0'; /* bel_copy1() appends */
2073 impossible("no words in list");
2081 /* "first or second" */
2085 /* "first, second, or third */
2089 } while (--words
> 1);