Introduce a new set of functions to manage delayed killers in the trunk, used in addressing the various reports of delayed killer confusion. Since existing delayed killers are related to player properties, the delayed killers are keyed by uprop indexes. I did this to avoid adding yet another set of similar identifiers. - the new delayed_killer() is used for stoning, sliming, sickness, and delayed self-genocide while polymorphed. Some other timed events don't use it (and didn't use the old delayed_killer variable) because they use a fixed message when the timeout occurs. - A new data structure, struct kinfo, is used to track both delayed and immediate killers. This encapsulates all the info involved with identifying a killer. The structure contains a buffer, which subsumes the old killer_buf and several other buffers that didn't/couldn't use killer_buf. - the killer list is saved and restored as part of the game state. - the special case of usick_cause was removed and a delayed killer list entry is now used in its place - common code dealing with (un)sliming is moved to a new make_slimed function - attempted to update all make dependencies for new end.c -> lev.h dependency, sorry if I messed any up
626 lines
16 KiB
C
626 lines
16 KiB
C
/* SCCS Id: @(#)allmain.c 3.4 2003/04/02 */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/* various code that was replicated in *main.c */
|
|
|
|
#include "hack.h"
|
|
|
|
#ifndef NO_SIGNAL
|
|
#include <signal.h>
|
|
#endif
|
|
|
|
#ifdef POSITIONBAR
|
|
STATIC_DCL void NDECL(do_positionbar);
|
|
#endif
|
|
|
|
void
|
|
moveloop()
|
|
{
|
|
#if defined(MICRO) || defined(WIN32)
|
|
char ch;
|
|
int abort_lev;
|
|
#endif
|
|
int moveamt = 0, wtcap = 0, change = 0;
|
|
boolean didmove = FALSE, monscanmove = FALSE;
|
|
|
|
flags.moonphase = phase_of_the_moon();
|
|
if(flags.moonphase == FULL_MOON) {
|
|
You("are lucky! Full moon tonight.");
|
|
change_luck(1);
|
|
} else if(flags.moonphase == NEW_MOON) {
|
|
pline("Be careful! New moon tonight.");
|
|
}
|
|
flags.friday13 = friday_13th();
|
|
if (flags.friday13) {
|
|
pline("Watch out! Bad things can happen on Friday the 13th.");
|
|
change_luck(-1);
|
|
}
|
|
|
|
initrack();
|
|
|
|
|
|
/* Note: these initializers don't do anything except guarantee that
|
|
we're linked properly.
|
|
*/
|
|
decl_init();
|
|
monst_init();
|
|
monstr_init(); /* monster strengths */
|
|
objects_init();
|
|
|
|
#ifdef WIZARD
|
|
if (wizard) add_debug_extended_commands();
|
|
#endif
|
|
|
|
(void) encumber_msg(); /* in case they auto-picked up something */
|
|
|
|
u.uz0.dlevel = u.uz.dlevel;
|
|
youmonst.movement = NORMAL_SPEED; /* give the hero some movement points */
|
|
|
|
for(;;) {
|
|
get_nh_event();
|
|
#ifdef POSITIONBAR
|
|
do_positionbar();
|
|
#endif
|
|
|
|
didmove = context.move;
|
|
if(didmove) {
|
|
/* actual time passed */
|
|
youmonst.movement -= NORMAL_SPEED;
|
|
|
|
do { /* hero can't move this turn loop */
|
|
wtcap = encumber_msg();
|
|
|
|
context.mon_moving = TRUE;
|
|
do {
|
|
monscanmove = movemon();
|
|
if (youmonst.movement > NORMAL_SPEED)
|
|
break; /* it's now your turn */
|
|
} while (monscanmove);
|
|
context.mon_moving = FALSE;
|
|
|
|
if (!monscanmove && youmonst.movement < NORMAL_SPEED) {
|
|
/* both you and the monsters are out of steam this round */
|
|
/* set up for a new turn */
|
|
struct monst *mtmp;
|
|
mcalcdistress(); /* adjust monsters' trap, blind, etc */
|
|
|
|
/* reallocate movement rations to monsters */
|
|
for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
|
|
mtmp->movement += mcalcmove(mtmp);
|
|
|
|
if(!rn2(u.uevent.udemigod ? 25 :
|
|
(depth(&u.uz) > depth(&stronghold_level)) ? 50 : 70))
|
|
(void) makemon((struct permonst *)0, 0, 0, NO_MM_FLAGS);
|
|
|
|
/* calculate how much time passed. */
|
|
#ifdef STEED
|
|
if (u.usteed && u.umoved) {
|
|
/* your speed doesn't augment steed's speed */
|
|
moveamt = mcalcmove(u.usteed);
|
|
} else
|
|
#endif
|
|
{
|
|
moveamt = youmonst.data->mmove;
|
|
|
|
if (Very_fast) { /* speed boots or potion */
|
|
/* average movement is 1.67 times normal */
|
|
moveamt += NORMAL_SPEED / 2;
|
|
if (rn2(3) == 0) moveamt += NORMAL_SPEED / 2;
|
|
} else if (Fast) {
|
|
/* average movement is 1.33 times normal */
|
|
if (rn2(3) != 0) moveamt += NORMAL_SPEED / 2;
|
|
}
|
|
}
|
|
|
|
switch (wtcap) {
|
|
case UNENCUMBERED: break;
|
|
case SLT_ENCUMBER: moveamt -= (moveamt / 4); break;
|
|
case MOD_ENCUMBER: moveamt -= (moveamt / 2); break;
|
|
case HVY_ENCUMBER: moveamt -= ((moveamt * 3) / 4); break;
|
|
case EXT_ENCUMBER: moveamt -= ((moveamt * 7) / 8); break;
|
|
default: break;
|
|
}
|
|
|
|
youmonst.movement += moveamt;
|
|
if (youmonst.movement < 0) youmonst.movement = 0;
|
|
settrack();
|
|
|
|
monstermoves++;
|
|
moves++;
|
|
|
|
/********************************/
|
|
/* once-per-turn things go here */
|
|
/********************************/
|
|
|
|
if (context.bypasses) clear_bypasses();
|
|
if(Glib) glibr();
|
|
nh_timeout();
|
|
run_regions();
|
|
|
|
if (u.ublesscnt) u.ublesscnt--;
|
|
if(flags.time && !context.run)
|
|
context.botl = 1;
|
|
|
|
/* One possible result of prayer is healing. Whether or
|
|
* not you get healed depends on your current hit points.
|
|
* If you are allowed to regenerate during the prayer, the
|
|
* end-of-prayer calculation messes up on this.
|
|
* Another possible result is rehumanization, which requires
|
|
* that encumbrance and movement rate be recalculated.
|
|
*/
|
|
if (u.uinvulnerable) {
|
|
/* for the moment at least, you're in tiptop shape */
|
|
wtcap = UNENCUMBERED;
|
|
} else if (Upolyd && youmonst.data->mlet == S_EEL && !is_pool(u.ux,u.uy) && !Is_waterlevel(&u.uz)) {
|
|
if (u.mh > 1) {
|
|
u.mh--;
|
|
context.botl = 1;
|
|
} else if (u.mh < 1)
|
|
rehumanize();
|
|
} else if (Upolyd && u.mh < u.mhmax) {
|
|
if (u.mh < 1)
|
|
rehumanize();
|
|
else if (Regeneration ||
|
|
(wtcap < MOD_ENCUMBER && !(moves%20))) {
|
|
context.botl = 1;
|
|
u.mh++;
|
|
}
|
|
} else if (u.uhp < u.uhpmax &&
|
|
(wtcap < MOD_ENCUMBER || !u.umoved || Regeneration)) {
|
|
if (u.ulevel > 9 && !(moves % 3)) {
|
|
int heal, Con = (int) ACURR(A_CON);
|
|
|
|
if (Con <= 12) {
|
|
heal = 1;
|
|
} else {
|
|
heal = rnd(Con);
|
|
if (heal > u.ulevel-9) heal = u.ulevel-9;
|
|
}
|
|
context.botl = 1;
|
|
u.uhp += heal;
|
|
if(u.uhp > u.uhpmax)
|
|
u.uhp = u.uhpmax;
|
|
} else if (Regeneration ||
|
|
(u.ulevel <= 9 &&
|
|
!(moves % ((MAXULEV+12) / (u.ulevel+2) + 1)))) {
|
|
context.botl = 1;
|
|
u.uhp++;
|
|
}
|
|
}
|
|
|
|
/* moving around while encumbered is hard work */
|
|
if (wtcap > MOD_ENCUMBER && u.umoved) {
|
|
if(!(wtcap < EXT_ENCUMBER ? moves%30 : moves%10)) {
|
|
if (Upolyd && u.mh > 1) {
|
|
u.mh--;
|
|
} else if (!Upolyd && u.uhp > 1) {
|
|
u.uhp--;
|
|
} else {
|
|
You("pass out from exertion!");
|
|
exercise(A_CON, FALSE);
|
|
fall_asleep(-10, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((u.uen < u.uenmax) &&
|
|
((wtcap < MOD_ENCUMBER &&
|
|
(!(moves%((MAXULEV + 8 - u.ulevel) *
|
|
(Role_if(PM_WIZARD) ? 3 : 4) / 6))))
|
|
|| Energy_regeneration)) {
|
|
u.uen += rn1((int)(ACURR(A_WIS) + ACURR(A_INT)) / 15 + 1,1);
|
|
if (u.uen > u.uenmax) u.uen = u.uenmax;
|
|
context.botl = 1;
|
|
}
|
|
|
|
if(!u.uinvulnerable) {
|
|
if(Teleportation && !rn2(85)) {
|
|
xchar old_ux = u.ux, old_uy = u.uy;
|
|
tele();
|
|
if (u.ux != old_ux || u.uy != old_uy) {
|
|
if (!next_to_u()) {
|
|
check_leash(old_ux, old_uy);
|
|
}
|
|
#ifdef REDO
|
|
/* clear doagain keystrokes */
|
|
pushch(0);
|
|
savech(0);
|
|
#endif
|
|
}
|
|
}
|
|
/* delayed change may not be valid anymore */
|
|
if ((change == 1 && !Polymorph) ||
|
|
(change == 2 && u.ulycn == NON_PM))
|
|
change = 0;
|
|
if(Polymorph && !rn2(100))
|
|
change = 1;
|
|
else if (u.ulycn >= LOW_PM && !Upolyd &&
|
|
!rn2(80 - (20 * night())))
|
|
change = 2;
|
|
if (change && !Unchanging) {
|
|
if (multi >= 0) {
|
|
if (occupation)
|
|
stop_occupation();
|
|
else
|
|
nomul(0);
|
|
if (change == 1) polyself(FALSE);
|
|
else you_were();
|
|
change = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(Searching && multi >= 0) (void) dosearch0(1);
|
|
dosounds();
|
|
do_storms();
|
|
gethungry();
|
|
age_spells();
|
|
exerchk();
|
|
invault();
|
|
if (u.uhave.amulet) amulet();
|
|
if (!rn2(40+(int)(ACURR(A_DEX)*3)))
|
|
u_wipe_engr(rnd(3));
|
|
if (u.uevent.udemigod && !u.uinvulnerable) {
|
|
if (u.udg_cnt) u.udg_cnt--;
|
|
if (!u.udg_cnt) {
|
|
intervene();
|
|
u.udg_cnt = rn1(200, 50);
|
|
}
|
|
}
|
|
restore_attrib();
|
|
/* underwater and waterlevel vision are done here */
|
|
if (Is_waterlevel(&u.uz))
|
|
movebubbles();
|
|
else if (Underwater)
|
|
under_water(0);
|
|
/* vision while buried done here */
|
|
else if (u.uburied) under_ground(0);
|
|
|
|
/* when immobile, count is in turns */
|
|
if(multi < 0) {
|
|
if (++multi == 0) { /* finished yet? */
|
|
unmul((char *)0);
|
|
/* if unmul caused a level change, take it now */
|
|
if (u.utotype) deferred_goto();
|
|
}
|
|
}
|
|
}
|
|
} while (youmonst.movement<NORMAL_SPEED); /* hero can't move loop */
|
|
|
|
/******************************************/
|
|
/* once-per-hero-took-time things go here */
|
|
/******************************************/
|
|
|
|
|
|
} /* actual time passed */
|
|
|
|
/****************************************/
|
|
/* once-per-player-input things go here */
|
|
/****************************************/
|
|
|
|
find_ac();
|
|
if(!context.mv || Blind) {
|
|
/* redo monsters if hallu or wearing a helm of telepathy */
|
|
if (Hallucination) { /* update screen randomly */
|
|
see_monsters();
|
|
see_objects();
|
|
see_traps();
|
|
if (u.uswallow) swallowed(0);
|
|
} else if (Unblind_telepat) {
|
|
see_monsters();
|
|
} else if (Warning || Warn_of_mon)
|
|
see_monsters();
|
|
|
|
if (vision_full_recalc) vision_recalc(0); /* vision! */
|
|
}
|
|
if(context.botl || context.botlx) bot();
|
|
|
|
context.move = 1;
|
|
|
|
if(multi >= 0 && occupation) {
|
|
#if defined(MICRO) || defined(WIN32)
|
|
abort_lev = 0;
|
|
if (kbhit()) {
|
|
if ((ch = Getchar()) == ABORT)
|
|
abort_lev++;
|
|
# ifdef REDO
|
|
else
|
|
pushch(ch);
|
|
# endif /* REDO */
|
|
}
|
|
if (!abort_lev && (*occupation)() == 0)
|
|
#else
|
|
if ((*occupation)() == 0)
|
|
#endif
|
|
occupation = 0;
|
|
if(
|
|
#if defined(MICRO) || defined(WIN32)
|
|
abort_lev ||
|
|
#endif
|
|
monster_nearby()) {
|
|
stop_occupation();
|
|
reset_eat();
|
|
}
|
|
#if defined(MICRO) || defined(WIN32)
|
|
if (!(++occtime % 7))
|
|
display_nhwindow(WIN_MAP, FALSE);
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
if ((u.uhave.amulet || Clairvoyant) &&
|
|
!In_endgame(&u.uz) && !BClairvoyant &&
|
|
!(moves % 15) && !rn2(2))
|
|
do_vicinity_map();
|
|
|
|
if(u.utrap && u.utraptype == TT_LAVA) {
|
|
if(!is_lava(u.ux,u.uy))
|
|
u.utrap = 0;
|
|
else if (!u.uinvulnerable) {
|
|
u.utrap -= 1<<8;
|
|
if(u.utrap < 1<<8) {
|
|
killer.format = KILLED_BY;
|
|
Strcpy(killer.name, "molten lava");
|
|
You("sink below the surface and die.");
|
|
done(DISSOLVED);
|
|
} else if(didmove && !u.umoved) {
|
|
Norep("You sink deeper into the lava.");
|
|
u.utrap += rnd(4);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef WIZARD
|
|
if (iflags.sanity_check)
|
|
sanity_check();
|
|
#endif
|
|
|
|
#ifdef CLIPPING
|
|
/* just before rhack */
|
|
cliparound(u.ux, u.uy);
|
|
#endif
|
|
|
|
u.umoved = FALSE;
|
|
|
|
if (multi > 0) {
|
|
lookaround();
|
|
if (!multi) {
|
|
/* lookaround may clear multi */
|
|
context.move = 0;
|
|
if (flags.time) context.botl = 1;
|
|
continue;
|
|
}
|
|
if (context.mv) {
|
|
if(multi < COLNO && !--multi)
|
|
context.travel = context.mv = context.run = 0;
|
|
domove();
|
|
} else {
|
|
--multi;
|
|
rhack(save_cm);
|
|
}
|
|
} else if (multi == 0) {
|
|
#ifdef MAIL
|
|
ckmailstatus();
|
|
#endif
|
|
rhack((char *)0);
|
|
}
|
|
if (u.utotype) /* change dungeon level */
|
|
deferred_goto(); /* after rhack() */
|
|
/* !context.move here: multiple movement command stopped */
|
|
else if (flags.time && (!context.move || !context.mv))
|
|
context.botl = 1;
|
|
|
|
if (vision_full_recalc) vision_recalc(0); /* vision! */
|
|
/* when running in non-tport mode, this gets done through domove() */
|
|
if ((!context.run || flags.runmode == RUN_TPORT) &&
|
|
(multi && (!context.travel ? !(multi % 7) : !(moves % 7L)))) {
|
|
if (flags.time && context.run) context.botl = 1;
|
|
display_nhwindow(WIN_MAP, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
stop_occupation()
|
|
{
|
|
if(occupation) {
|
|
if (!maybe_finished_meal(TRUE))
|
|
You("stop %s.", occtxt);
|
|
occupation = 0;
|
|
context.botl = 1; /* in case u.uhs changed */
|
|
/* fainting stops your occupation, there's no reason to sync.
|
|
sync_hunger();
|
|
*/
|
|
#ifdef REDO
|
|
nomul(0);
|
|
pushch(0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void
|
|
display_gamewindows()
|
|
{
|
|
WIN_MESSAGE = create_nhwindow(NHW_MESSAGE);
|
|
WIN_STATUS = create_nhwindow(NHW_STATUS);
|
|
WIN_MAP = create_nhwindow(NHW_MAP);
|
|
WIN_INVEN = create_nhwindow(NHW_MENU);
|
|
|
|
#ifdef MAC
|
|
/*
|
|
* This _is_ the right place for this - maybe we will
|
|
* have to split display_gamewindows into create_gamewindows
|
|
* and show_gamewindows to get rid of this ifdef...
|
|
*/
|
|
if ( ! strcmp ( windowprocs . name , "mac" ) ) {
|
|
SanePositions ( ) ;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* The mac port is not DEPENDENT on the order of these
|
|
* displays, but it looks a lot better this way...
|
|
*/
|
|
display_nhwindow(WIN_STATUS, FALSE);
|
|
display_nhwindow(WIN_MESSAGE, FALSE);
|
|
clear_glyph_buffer();
|
|
display_nhwindow(WIN_MAP, FALSE);
|
|
}
|
|
|
|
void
|
|
newgame()
|
|
{
|
|
int i;
|
|
|
|
#ifdef MFLOPPY
|
|
gameDiskPrompt();
|
|
#endif
|
|
|
|
context.botlx = 1;
|
|
context.ident = 1;
|
|
context.stethoscope_move = -1L;
|
|
context.warnlevel = 1;
|
|
|
|
for (i = 0; i < NUMMONS; i++)
|
|
mvitals[i].mvflags = mons[i].geno & G_NOCORPSE;
|
|
|
|
init_objects(); /* must be before u_init() */
|
|
|
|
flags.pantheon = -1; /* role_init() will reset this */
|
|
role_init(); /* must be before init_dungeons(), u_init(),
|
|
* and init_artifacts() */
|
|
|
|
init_dungeons(); /* must be before u_init() to avoid rndmonst()
|
|
* creating odd monsters for any tins and eggs
|
|
* in hero's initial inventory */
|
|
init_artifacts(); /* before u_init() in case $WIZKIT specifies
|
|
* any artifacts */
|
|
u_init();
|
|
|
|
#ifndef NO_SIGNAL
|
|
(void) signal(SIGINT, (SIG_RET_TYPE) done1);
|
|
#endif
|
|
#ifdef NEWS
|
|
if(iflags.news) display_file(NEWS, FALSE);
|
|
#endif
|
|
load_qtlist(); /* load up the quest text info */
|
|
/* quest_init();*/ /* Now part of role_init() */
|
|
|
|
mklev();
|
|
u_on_upstairs();
|
|
vision_reset(); /* set up internals for level (after mklev) */
|
|
check_special_room(FALSE);
|
|
|
|
|
|
/* Move the monster from under you or else
|
|
* makedog() will fail when it calls makemon().
|
|
* - ucsfcgl!kneller
|
|
*/
|
|
if(MON_AT(u.ux, u.uy)) mnexto(m_at(u.ux, u.uy));
|
|
(void) makedog();
|
|
docrt();
|
|
|
|
if (flags.legacy) {
|
|
flush_screen(1);
|
|
com_pager(1);
|
|
}
|
|
|
|
#ifdef INSURANCE
|
|
save_currentstate();
|
|
#endif
|
|
program_state.something_worth_saving++; /* useful data now exists */
|
|
|
|
/* Success! */
|
|
welcome(TRUE);
|
|
return;
|
|
}
|
|
|
|
/* show "welcome [back] to nethack" message at program startup */
|
|
void
|
|
welcome(new_game)
|
|
boolean new_game; /* false => restoring an old game */
|
|
{
|
|
char buf[BUFSZ];
|
|
boolean currentgend = Upolyd ? u.mfemale : flags.female;
|
|
|
|
/*
|
|
* The "welcome back" message always describes your innate form
|
|
* even when polymorphed or wearing a helm of opposite alignment.
|
|
* Alignment is shown unconditionally for new games; for restores
|
|
* it's only shown if it has changed from its original value.
|
|
* Sex is shown for new games except when it is redundant; for
|
|
* restores it's only shown if different from its original value.
|
|
*/
|
|
*buf = '\0';
|
|
if (new_game || u.ualignbase[A_ORIGINAL] != u.ualignbase[A_CURRENT])
|
|
Sprintf(eos(buf), " %s", align_str(u.ualignbase[A_ORIGINAL]));
|
|
if (!urole.name.f &&
|
|
(new_game ? (urole.allow & ROLE_GENDMASK) == (ROLE_MALE|ROLE_FEMALE) :
|
|
currentgend != flags.initgend))
|
|
Sprintf(eos(buf), " %s", genders[currentgend].adj);
|
|
|
|
pline(new_game ? "%s %s, welcome to NetHack! You are a%s %s %s."
|
|
: "%s %s, the%s %s %s, welcome back to NetHack!",
|
|
Hello((struct monst *) 0), plname, buf, urace.adj,
|
|
(currentgend && urole.name.f) ? urole.name.f : urole.name.m);
|
|
}
|
|
|
|
#ifdef POSITIONBAR
|
|
STATIC_DCL void
|
|
do_positionbar()
|
|
{
|
|
static char pbar[COLNO];
|
|
char *p;
|
|
|
|
p = pbar;
|
|
/* up stairway */
|
|
if (upstair.sx &&
|
|
(glyph_to_cmap(level.locations[upstair.sx][upstair.sy].glyph) ==
|
|
S_upstair ||
|
|
glyph_to_cmap(level.locations[upstair.sx][upstair.sy].glyph) ==
|
|
S_upladder)) {
|
|
*p++ = '<';
|
|
*p++ = upstair.sx;
|
|
}
|
|
if (sstairs.sx &&
|
|
(glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph) ==
|
|
S_upstair ||
|
|
glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph) ==
|
|
S_upladder)) {
|
|
*p++ = '<';
|
|
*p++ = sstairs.sx;
|
|
}
|
|
|
|
/* down stairway */
|
|
if (dnstair.sx &&
|
|
(glyph_to_cmap(level.locations[dnstair.sx][dnstair.sy].glyph) ==
|
|
S_dnstair ||
|
|
glyph_to_cmap(level.locations[dnstair.sx][dnstair.sy].glyph) ==
|
|
S_dnladder)) {
|
|
*p++ = '>';
|
|
*p++ = dnstair.sx;
|
|
}
|
|
if (sstairs.sx &&
|
|
(glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph) ==
|
|
S_dnstair ||
|
|
glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph) ==
|
|
S_dnladder)) {
|
|
*p++ = '>';
|
|
*p++ = sstairs.sx;
|
|
}
|
|
|
|
/* hero location */
|
|
if (u.ux) {
|
|
*p++ = '@';
|
|
*p++ = u.ux;
|
|
}
|
|
/* fence post */
|
|
*p = 0;
|
|
|
|
update_positionbar(pbar);
|
|
}
|
|
#endif
|
|
|
|
/*allmain.c*/
|