Esempio n. 1
0
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);
    }
    /* KMH -- February 2 */
    flags.groundhogday = groundhog_day();
    if (flags.groundhogday)
	pline("Happy Groundhog Day!");

    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 */
    if (defer_see_monsters) {
	defer_see_monsters = FALSE;
	see_monsters();
    }

    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 = flags.move;
	if(didmove) {
	    /* actual time passed */
	    youmonst.movement -= NORMAL_SPEED;

	    do { /* hero can't move this turn loop */
		wtcap = encumber_msg();

		flags.mon_moving = TRUE;
		do {
		    monscanmove = movemon();
		    if (youmonst.movement > NORMAL_SPEED)
			break;	/* it's now your turn */
		} while (monscanmove);
		flags.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;
			}
			if (tech_inuse(T_BLINK)) { /* TECH: Blinking! */
			    /* Case    Average  Variance
			     * -------------------------
			     * Normal    12         0
			     * Fast      16        12
			     * V fast    20        12
			     * Blinking  24        12
			     * F & B     28        18
			     * V F & B   30        18
			     */
			    moveamt += NORMAL_SPEED * 2 / 3;
			    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 (flags.bypasses) clear_bypasses();
		    if(Glib) glibr();
		    nh_timeout();
		    run_regions();

#ifdef DUNGEON_GROWTH
		    dgn_growths(TRUE, TRUE);
#endif

		    if (u.ublesscnt)  u.ublesscnt--;
		    
		    if(flags.time && !flags.run)
			flags.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--;
			    flags.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))) {
			    flags.botl = 1;
			    u.mh++;
			}
		    } else if (u.uhp < u.uhpmax &&
			 (wtcap < MOD_ENCUMBER || !u.umoved || Regeneration)) {
/*
 * KMH, balance patch -- New regeneration code
 * Healthstones have been added, which alter your effective
 * experience level and constitution (-2 cursed, +1 uncursed,
 * +2 blessed) for the basis of regeneration calculations.
 */

 			int efflev = u.ulevel + u.uhealbonus;
 			int effcon = ACURR(A_CON) + u.uhealbonus;
			int heal = 1;


			if (efflev > 9 && !(moves % 3)) {
			    if (effcon <= 12) {
				heal = 1;
			    } else {
				heal = rnd(effcon);
  				if (heal > efflev-9) heal = efflev-9;
			    }
			    flags.botl = 1;
			    u.uhp += heal;
			    if(u.uhp > u.uhpmax)
				u.uhp = u.uhpmax;
			} else if (Regeneration ||
			     (efflev <= 9 &&
			      !(moves % ((MAXULEV+12) / (u.ulevel+2) + 1)))) {
			    flags.botl = 1;
			    u.uhp++;
			}
		    }

		    if (!u.uinvulnerable && u.uen > 0 && u.uhp < u.uhpmax &&
			    tech_inuse(T_CHI_HEALING)) {
			u.uen--;
			u.uhp++;
			flags.botl = 1;
		    }

		    /* 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);
			    }
			}
		    }

		    
		    /* KMH -- OK to regenerate if you don't move */
		    if ((u.uen < u.uenmax) && (Energy_regeneration ||
				((wtcap < MOD_ENCUMBER || !flags.mv) &&
				(!(moves%((MAXULEV + 15 - u.ulevel) *                                    
				(Role_if(PM_WIZARD) ? 3 : 4) / 6)))))) {
			u.uen += rn1((int)(ACURR(A_WIS) + ACURR(A_INT)) / 15 + 1,1);
#ifdef WIZ_PATCH_DEBUG
                pline("mana was = %d now = %d",temp,u.uen);
#endif

			if (u.uen > u.uenmax)  u.uen = u.uenmax;
			flags.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(&youmonst, old_ux, old_uy, TRUE);
				}
#ifdef REDO
				/* clear doagain keystrokes */
				pushch(0);
				savech(0);
#endif
			    }
			}
			long ch = (80 - (40 * night())) / 2 * 
					 (Race_if(PM_HUMAN_WEREWOLF) ? 
					  u.ulevel * u.ulevel :
					  2);
			ch = (ch > LARGEST_INT) ? LARGEST_INT : ch;
			/* 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((int)ch))
			    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;
			    }
			}
		}	/* !u.uinvulnerable */

		    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(!flags.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(flags.botl || flags.botlx) bot();

	flags.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;
		    killer = "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 */
		flags.move = 0;
		if (flags.time) flags.botl = 1;
		continue;
	    }
	    if (flags.mv) {
		if(multi < COLNO && !--multi)
		    flags.travel = iflags.travel1 = flags.mv = flags.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() */
	/* !flags.move here: multiple movement command stopped */
	else if (flags.time && (!flags.move || !flags.mv))
	    flags.botl = 1;

	if (vision_full_recalc) vision_recalc(0);	/* vision! */
	/* when running in non-tport mode, this gets done through domove() */
	if ((!flags.run || iflags.runmode == RUN_TPORT) &&
		(multi && (!flags.travel ? !(multi % 7) : !(moves % 7L)))) {
	    if (flags.time && flags.run) flags.botl = 1;
	    display_nhwindow(WIN_MAP, FALSE);
	}
    }
}
Esempio n. 2
0
int main(int argc, char *argv[])
{
    int fd;
#ifdef CHDIR
    char *dir;
#endif

    // Check for dirty tricks with closed fds 0, 1, 2
    fd = open("/dev/null", O_RDONLY);
    if (fd < 3)
	exit(1);
    close(fd);

    hname = argv[0];
    hackpid = getpid();

#ifdef CHDIR		       // otherwise no chdir()
    // See if we must change directory to the playground.
    // (Perhaps hack runs suid and playground is inaccessible
    //  for the player.)
    // The environment variable HACKDIR is overridden by a
    //  -d command line option (must be the first option given)

    dir = getenv("HACKDIR");
    if (argc > 1 && !strncmp(argv[1], "-d", 2)) {
	argc--;
	argv++;
	dir = argv[0] + 2;
	if (*dir == '=' || *dir == ':')
	    dir++;
	if (!*dir && argc > 1) {
	    argc--;
	    argv++;
	    dir = argv[0];
	}
	if (!*dir)
	    error("Flag -d must be followed by a directory name.");
    }
#endif

    // Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS
    //                      2. Use $USER or $LOGNAME        (if 1. fails)
    //                      3. Use getlogin()               (if 2. fails)
    // The resulting name is overridden by command line options.
    // If everything fails, or if the resulting name is some generic
    // account like "games", "play", "player", "hack" then eventually
    // we'll ask him.
    // Note that we trust him here; it is possible to play under
    // somebody else's name.
    {
	char *s;

	initoptions();
	if (!*plname && (s = getenv("USER")))
	    (void) strncpy(plname, s, sizeof(plname) - 1);
	if (!*plname && (s = getenv("LOGNAME")))
	    (void) strncpy(plname, s, sizeof(plname) - 1);
	if (!*plname && (s = getlogin()))
	    (void) strncpy(plname, s, sizeof(plname) - 1);
    }

    // Now we know the directory containing 'record' and
    // may do a prscore().
    if (argc > 1 && !strncmp(argv[1], "-s", 2)) {
#ifdef CHDIR
	chdirx(dir, 0);
#endif
	prscore(argc, argv);
	exit(0);
    }
    // It seems he really wants to play.
    // Remember tty modes, to be restored on exit.
    gettty();
    setbuf(stdout, obuf);
    setrandom();
    startup();
    cls();
    u.uhp = 1;		       // prevent RIP on early quits
    u.ux = FAR;		       // prevent nscr()
    signal(SIGHUP, hangup);

    // Find the creation date of this game,
    // so as to avoid restoring outdated savefiles.
    gethdate(hname);

    // We cannot do chdir earlier, otherwise gethdate will fail.
#ifdef CHDIR
    chdirx(dir, 1);
#endif

    // Process options.
    while (argc > 1 && argv[1][0] == '-') {
	argv++;
	argc--;
	switch (argv[0][1]) {
#ifdef WIZARD
	    case 'D':
		// if(!strcmp(getlogin(), WIZARD))
		wizard = true;
		// else printf("Sorry.\n");
		break;
#endif
#ifdef NEWS
	    case 'n':
		flags.nonews = true;
		break;
#endif
	    case 'u':
		if (argv[0][2])
		    (void) strncpy(plname, argv[0] + 2, sizeof(plname) - 1);
		else if (argc > 1) {
		    argc--;
		    argv++;
		    (void) strncpy(plname, argv[0], sizeof(plname) - 1);
		} else
		    printf("Player name expected after -u\n");
		break;
	    default:
		// allow -T for Tourist, etc.
		(void) strncpy(pl_character, argv[0] + 1, sizeof(pl_character) - 1);

		// printf("Unknown option: %s\n", *argv);
	}
    }

    if (argc > 1)
	locknum = atoi(argv[1]);
#ifdef MAX_NR_OF_PLAYERS
    if (!locknum || locknum > MAX_NR_OF_PLAYERS)
	locknum = MAX_NR_OF_PLAYERS;
#endif
#ifdef DEF_PAGER
    if (((catmore = getenv("HACKPAGER")) == NULL && (catmore = getenv("PAGER")) == NULL) || catmore[0] == '\0')
	catmore = DEF_PAGER;
#endif
#ifdef MAIL
    getmailstatus();
#endif
#ifdef WIZARD
    if (wizard)
	(void) strcpy(plname, "wizard");
    else
#endif
	if (!*plname || !strncmp(plname, "player", 4)
	    || !strncmp(plname, "games", 4))
	askname();
    plnamesuffix();	       // strip suffix from name; calls askname()
    // again if suffix was whole name
    // accepts any suffix
#ifdef WIZARD
    if (!wizard) {
#endif
	// check for multiple games under the same name
	// (if !locknum) or check max nr of players (otherwise)
	(void) signal(SIGQUIT, SIG_IGN);
	(void) signal(SIGINT, SIG_IGN);
	if (!locknum)
	    (void) strcpy(lock, plname);
	getlock();	       // sets lock if locknum != 0
#ifdef WIZARD
    } else {
	char *sfoo;
	(void) strcpy(lock, plname);
	if ((sfoo = getenv("MAGIC")) != NULL)
	    while (*sfoo) {
		switch (*sfoo++) {
		    case 'n':
			srand (*sfoo++);
			break;
		}
	    }
	if ((sfoo = getenv("GENOCIDED")) != NULL) {
	    if (*sfoo == '!') {
		const struct permonst *pm = mons;
		char *gp = genocided;

		while (pm < mons + CMNUM + 2) {
		    if (!strchr(sfoo, pm->mlet))
			*gp++ = pm->mlet;
		    pm++;
		}
		*gp = 0;
	    } else
		(void) strcpy(genocided, sfoo);
	    (void) strcpy(fut_geno, genocided);
	}
    }
#endif
    setftty();
    (void) sprintf(SAVEF, "save/%d%s", getuid(), plname);
    regularize(SAVEF + 5);     // avoid . or / in name
    if ((fd = open(SAVEF, O_RDONLY)) >= 0 && (uptodate(fd) || unlink(SAVEF) == 666)) {
	(void) signal(SIGINT, done1);
	pline("Restoring old save file...");
	(void) fflush(stdout);
	if (!dorecover(fd))
	    goto not_recovered;
	pline("Hello %s, welcome to %s!", plname, gamename);
	flags.move = 0;
    } else {
      not_recovered:
	fobj = fcobj = invent = 0;
	fmon = fallen_down = 0;
	ftrap = 0;
	fgold = 0;
	flags.ident = 1;
	init_objects();
	u_init();

	(void) signal(SIGINT, done1);
	mklev();
	u.ux = xupstair;
	u.uy = yupstair;
	(void) inshop();
	setsee();
	flags.botlx = 1;
	makedog();
	{
	    struct monst *mtmp;
	    if ((mtmp = m_at(u.ux, u.uy)) != NULL)
		mnexto(mtmp);  // riv05!a3
	}
	seemons();
#ifdef NEWS
	if (flags.nonews || !readnews())
	    // after reading news we did docrt() already
#endif
	    docrt();

	// give welcome message before pickup messages
	pline("Hello %s, welcome to %s!", plname, gamename);

	pickup(1);
	read_engr_at(u.ux, u.uy);
	flags.move = 1;
    }

    flags.moonphase = phase_of_the_moon();
    if (flags.moonphase == FULL_MOON) {
	pline("You are lucky! Full moon tonight.");
	u.uluck++;
    } else if (flags.moonphase == NEW_MOON) {
	pline("Be careful! New moon tonight.");
    }
    initrack();

    for (;;) {
	if (flags.move) {      // actual time passed

	    settrack();

	    if (moves % 2 == 0 || (!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) {
		movemon();
		if (!rn2(70))
		    (void) makemon((struct permonst *) 0, 0, 0);
	    }
	    if (Glib)
		glibr();
	    timeout();
	    ++moves;
	    if (flags.time)
		flags.botl = 1;
	    if (u.uhp < 1) {
		pline("You die...");
		done("died");
	    }
	    if (u.uhp * 10 < u.uhpmax && moves - wailmsg > 50) {
		wailmsg = moves;
		if (u.uhp == 1)
		    pline("You hear the wailing of the Banshee...");
		else
		    pline("You hear the howling of the CwnAnnwn...");
	    }
	    if (u.uhp < u.uhpmax) {
		if (u.ulevel > 9) {
		    if (Regeneration || !(moves % 3)) {
			flags.botl = 1;
			u.uhp += rnd((int) u.ulevel - 9);
			if (u.uhp > u.uhpmax)
			    u.uhp = u.uhpmax;
		    }
		} else if (Regeneration || (!(moves % (22 - u.ulevel * 2)))) {
		    flags.botl = 1;
		    u.uhp++;
		}
	    }
	    if (Teleportation && !rn2(85))
		tele();
	    if (Searching && multi >= 0)
		(void) dosearch();
	    gethungry();
	    invault();
	    amulet();
	}
	if (multi < 0) {
	    if (!++multi) {
		pline(nomovemsg ? nomovemsg : "You can move again.");
		nomovemsg = 0;
		if (afternmv)
		    (*afternmv) ();
		afternmv = 0;
	    }
	}
	find_ac();
#ifndef QUEST
	if (!flags.mv || Blind)
#endif
	{
	    seeobjs();
	    seemons();
	    nscr();
	}
	if (flags.botl || flags.botlx)
	    bot();

	flags.move = 1;

	if (multi >= 0 && occupation) {
	    if (monster_nearby())
		stop_occupation();
	    else if ((*occupation) () == 0)
		occupation = 0;
	    continue;
	}
	if (multi > 0) {
#ifdef QUEST
	    if (flags.run >= 4)
		finddir();
#endif
	    lookaround();
	    if (!multi) {      // lookaround may clear multi
		flags.move = 0;
		continue;
	    }
	    if (flags.mv) {
		if (multi < COLNO && !--multi)
		    flags.mv = flags.run = 0;
		domove();
	    } else {
		--multi;
		rhack(save_cm);
	    }
	} else if (multi == 0) {
#ifdef MAIL
	    ckmailstatus();
#endif
	    rhack((char *) 0);
	}
	if (multi && multi % 7 == 0)
	    (void) fflush(stdout);
    }
}
Esempio n. 3
0
static void you_moved(void)
{
    int moveamt = 0, wtcap = 0, change = 0;
    boolean monscanmove = FALSE;

    /* Begin turn-tracking for delay_msg. */
    if (delay_start == 0)
	delay_start = moves;

    /* actual time passed */
    youmonst.movement -= NORMAL_SPEED;

    do { /* hero can't move this turn loop */
	wtcap = encumber_msg();
	calc_attr_bonus();

	flags.mon_moving = TRUE;
	do {
	    monscanmove = movemon();
	    if (youmonst.movement > NORMAL_SPEED)
		break;	/* it's now your turn */
	} while (monscanmove);
	flags.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 = level->monlist; mtmp; mtmp = mtmp->nmon)
		mtmp->movement += mcalcmove(mtmp);

	    if (!rn2(u.uevent.udemigod ? 25 :
		    (depth(&u.uz) > depth(&stronghold_level)) ? 50 : 70))
		makemon(NULL, level, 0, 0, NO_MM_FLAGS);

	    /* calculate how much time passed. */
	    if (u.usteed && u.umoved) {
		/* your speed doesn't augment steed's speed */
		moveamt = mcalcmove(u.usteed);
	    } else {
		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();

	    moves++;
	    level->lastmoves = moves;

	    /********************************/
	    /* once-per-turn things go here */
	    /********************************/

	    if (flags.bypasses) clear_bypasses();
	    if (Glib) glibr();
	    nh_timeout();
	    run_regions(level);
	    dgn_growths(level, TRUE, TRUE);

	    if (u.ublesscnt)  u.ublesscnt--;
	    iflags.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(level, u.ux,u.uy) && !Is_waterlevel(&u.uz)) {
		if (u.mh > 1) {
		    u.mh--;
		    iflags.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))) {
		    iflags.botl = 1;
		    u.mh++;
		    interrupt_multi("Hit points", u.mh, u.mhmax);
		}
	    } 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;
		    }
		    iflags.botl = 1;
		    u.uhp += heal;
		    if (u.uhp > u.uhpmax)
			u.uhp = u.uhpmax;
		    interrupt_multi("Hit points", u.uhp, u.uhpmax);
		} else if (Regeneration ||
			(u.ulevel <= 9 &&
			!(moves % ((MAXULEV+12) / (u.ulevel+2) + 1)))) {
		    iflags.botl = 1;
		    u.uhp++;
		    interrupt_multi("Hit points", u.uhp, u.uhpmax);
		}
	    }

	    /* 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 {
			pline("你用力过度昏倒了!");
			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;
		iflags.botl = 1;
		interrupt_multi("Magic energy", u.uen, u.uenmax);
	    }

	    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);
			}
		    }
		}
		/* 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, NULL);
			if (change == 1) polyself(FALSE);
			else you_were();
			change = 0;
		    }
		}
	    }

	    if (Searching && multi >= 0) 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(NULL);
		    /* 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 */
    /******************************************/

    if (u.utrap && u.utraptype == TT_LAVA)
	handle_lava_trap(TRUE);

    if (iflags.hp_notify && prev_hp_notify != uhp()) {
	pline("%s", hp_notify_format_str(iflags.hp_notify_fmt ?
					 iflags.hp_notify_fmt : "[HP%c%a=%h]"));
	prev_hp_notify = uhp();
    }
}