view arogue7/main.c @ 296:000b1c5b8d63

UltraRogue: fix inventory collision after save and restore. Inventory letters are based on "identifiers" stored in objects' o_ident field. Identifiers are allocated by get_ident(), which keeps a list of objects that have them, to avoid giving the same identifier to multiple objects. The list is not stored in the savefile, so after restore, get_ident() was not aware of existing identifiers. This resulted in picked-up objects having the same inventory letters as objects restored from the file. The restore code now adds all objects with identifiers to the list.
author John "Elwin" Edwards
date Mon, 15 Jan 2018 20:20:35 -0500
parents d3968e9cb98d
children 827441d05b3e
line wrap: on
line source

/*
 * main.c  -  setup code
 *
 * Advanced Rogue
 * Copyright (C) 1984, 1985, 1986 Michael Morgan, Ken Dalka and AT&T
 * All rights reserved.
 *
 * Based on "Rogue: Exploring the Dungeons of Doom"
 * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
 * All rights reserved.
 *
 * See the file LICENSE.TXT for full copyright and licensing information.
 */

#include "curses.h"
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#ifdef BSD
#include <sys/time.h>
#else
#include <time.h>
#endif
#include "mach_dep.h"
#include "network.h"
#include "rogue.h"
#ifdef PC7300
#include "sys/window.h"
#include <ctype.h>
extern struct uwdata wdata, oldwin;
extern char oldtext[WTXTNUM][WTXTLEN];
#endif

void open_records(void);
bool too_much(void);
bool author(void);
void chmsg(char *fmt, int arg);
#ifdef MAXPROCESSES
int loadav(void);
#endif
#ifdef MAXUSERS
int ucount(void);
#endif
bool holiday(void);

int
main(int argc, char *argv[], char *envp[])
{
    register char *env;
#ifdef PC7300
    int hardwindow;	/* Do we have a hardware window? */
#endif

    md_init();

    /*
     * get home and options from environment
     */

    strncpy(home, md_gethomedir(), LINELEN);

    /* Get default save file */
    strcpy(file_name, home);
    strcat(file_name, "arogue77.sav");

    /* Get default score file */
#ifdef SCOREFILE
    strncpy(score_file, SCOREFILE, LINELEN);
    score_file[LINELEN-1] = '\0';
#else
    strcpy(score_file, md_getroguedir());

    if (*score_file)
        strcat(score_file,"/");

    strcat(score_file,"arogue77.scr");
#endif

#ifdef SAVEDIR
    /* Check for common save location */
    if (argc >= 3 && strcmp(argv[1], "-n") == 0)
    {
        strncpy(whoami, argv[2], 79);
        whoami[79] = '\0';
        use_savedir = TRUE;
        if (LINELEN <= snprintf(file_name, LINELEN, "%s/%d-%s.ar7sav", SAVEDIR,
                     md_getuid(), whoami))
        {
            strcpy(file_name, "xrogue.sav");
            use_savedir = FALSE;
        }
    }
#endif

    if ((env = getenv("ROGUEOPTS")) != NULL)
	parse_opts(env);

    if (whoami[0] == '\0')
        strucpy(whoami, md_getusername(), strlen(md_getusername()));

    open_records();

    /*
     * check for print-score option
     */
    if (argc == 2 && strcmp(argv[1], "-s") == 0)
    {
	waswizard = TRUE;
	score(0, SCOREIT, 0);
	exit(0);
    }

#ifdef NUMNET
    /*
     * Check for a network update
     */
    if (argc == 2 && strcmp(argv[1], "-u") == 0) {
	unsigned long netread();
	int errcheck, errors = 0;
	unsigned long amount;
	short monster;

	/* Read in the amount and monster values to pass to score */
	amount = netread(&errcheck, sizeof(unsigned long), stdin);
	if (errcheck) errors++;

	monster = (short) netread(&errcheck, sizeof(short), stdin);
	if (errcheck) errors++;

	/* Now do the update if there were no errors */
	if (errors) exit(1);
	else {
	    score(amount, UPDATE, monster);
	    exit(0);
	}
    }
#endif

#ifdef WIZARD
    /*
     * Check to see if he is a wizard
     */
    if (argc >= 2 && argv[1][0] == '\0')
	if (strcmp(PASSWD, md_crypt(md_getpass("Wizard's password: "), "mT")) == 0)
	{
	    wizard = TRUE;
	    argv++;
	    argc--;
	}
#endif

    if (!wizard && !author() && !holiday()) {
	printf("Sorry, %s, but you can't play during working hours.\n", whoami);
	printf("Try again later.\n");
	exit(1);
    }
    if (!wizard && !author() && too_much()) {
	printf("Sorry, %s, but the system is too loaded now.\n", whoami);
	printf("Try again later.\n");
	exit(1);
    }

#if NICE
    if (!wizard)
        nice(19);	/* nice the max amount */
#endif

    if (use_savedir)
    {
        if (!restore(file_name, envp))
            exit(1);
    }
    else
    {
        md_normaluser();
    }
    if (argc == 2)
	if (!restore(argv[1], envp)) /* Note: restore will never return */
	    exit(1);
    dnum = (wizard && getenv("SEED") != NULL ?
	atoi(getenv("SEED")) :
	md_random_seed());
    if (wizard)
	printf("Hello %s, welcome to dungeon #%d\n", whoami, dnum);
    else
	printf("Hello %s, just a moment while I dig the dungeon...\n", whoami);
    fflush(stdout);
    seed = dnum;
    md_srand(seed);

#ifdef PC7300
    /* Store static window parameters */
    hardwindow = ioctl(0, WIOCGETD, &wdata);
    if (hardwindow >= 0) {	/* We have a hardware window */
	extern char **environ;

	/* Make sure our window is the right size */
	oldwin = wdata;
	if ((wdata.uw_height / wdata.uw_vs) < 23 ||
	    (wdata.uw_width / wdata.uw_hs) < 75) {
	    wdata.uw_width = 80 * wdata.uw_hs;
	    wdata.uw_height = 24 * wdata.uw_vs;
	    wdata.uw_x = 0;
	    wdata.uw_y = wdata.uw_vs;
	    wdata.uw_uflags = NBORDER;

	    /* Make the change */
	    if (ioctl(1, WIOCSETD, &wdata) >= 0 && environ) {
		char **eptr, *tptr, *nptr, *newenv, *lptr = 0, *cptr = 0;
		int i, nlines = -1, ncols = -1, nlindig = 0, ncoldig = 0;
		struct utdata labelbuf;

		/* Save and change window-associated text */
		for (i=0; i<WTXTNUM; i++) {
		    labelbuf.ut_num = i;
		    ioctl(1, WIOCGETTEXT, &labelbuf);
		    strncpy(oldtext[i], labelbuf.ut_text, WTXTLEN - 1);
		    if (*labelbuf.ut_text) {
			*labelbuf.ut_text = '\0';
			ioctl(1, WIOCSETTEXT, &labelbuf);
		    }
		}

		labelbuf.ut_num = WTXTLABEL;
		strcpy(labelbuf.ut_text, "Advanced Rogue");
		ioctl(1, WIOCSETTEXT, &labelbuf);

		/* We have to change the TERMCAP entry */
		eptr = environ;
		while (*eptr) {
		    if (strncmp(*eptr, "TERMCAP=", 8) == 0) break;
		    else eptr++;
		}

		/* We found a TERMCAP entry */
		if (*eptr) {
		    /* Search for li# and co# */
		    tptr = *eptr;
		    while (*tptr) {
			switch (*tptr) {
			    case 'l':
				if (nlines == -1 &&
				    strncmp(tptr, "li#", 3) == 0) {
				    tptr += 3;
				    lptr = tptr;
				    lines = atoi(tptr);
				    while (isdigit(*tptr)) {
					nlindig++;;
					tptr++;
				    }
				}
				else tptr++;
				break;
			    case 'c':
				if (ncols == -1 &&
				    strncmp(tptr, "co#", 3) == 0) {
				    tptr += 3;
				    cptr = tptr;
				    cols = atoi(tptr);
				    while (isdigit(*tptr)) {
					ncoldig++;
					tptr++;
				    }
				}
				else tptr++;
				break;
			    default:
				tptr++;
			}
		    }

		    /* Change the entry */
		    if (ncoldig != 2 || nlindig != 2) {
			int length;

			/* Add in difference in num lengths plus NULL */
			length = strlen(*eptr) - ncoldig - nlindig + 5;

			if (ncoldig == 0) length += 4; /* For :co# */
			if (nlindig == 0) length += 4; /* For :li# */

			newenv = malloc(length);
			tptr = *eptr;
			nptr = newenv;

			if (nlindig == 0 || ncoldig == 0) {
			    /* Copy up to the first : */
			    while (*tptr && *tptr != ':') *nptr++ = *tptr++;

			    /* Do we have to add a field? */
			    if (nlindig == 0) {
				strcpy(nptr, ":li#24");
				nptr += 6;
			    }
			    if (ncoldig == 0) {
				strcpy(nptr, ":co#80");
				nptr += 6;
			    }
			}
			while (*tptr) {
			    if (tptr == lptr) {
				strcpy(nptr, "24");
				nptr += 2;
				tptr += nlindig;
			    }
			    else if (tptr == cptr) {
				strcpy(nptr, "80");
				nptr += 2;
				tptr += ncoldig;
			    }
			    else *nptr++ = *tptr++;
			}

			*nptr = '\0';

			/* Replace the old one */
			free(*eptr);
			*eptr = newenv;
		    }
		    else {
			/* Just overwrite the old numbers */
			*lptr++ = '2';
			*lptr = '4';
			*cptr++ = '8';
			*cptr = '0';
		    }
		}
	    }
	}
    }
#endif
    init_things();			/* Set up probabilities of things */
    init_colors();			/* Set up colors of potions */
    init_stones();			/* Set up stone settings of rings */
    init_materials();			/* Set up materials of wands */
    initscr();				/* Start up cursor package */
    init_names();			/* Set up names of scrolls */
    init_misc();			/* Set up miscellaneous magic */
    init_foods();			/* set up the food table */

    cols = COLS;
    lines = LINES;
    if (cols > 85) cols = 85;
    if (lines > 24) lines = 24;
    if (lines < 23 || cols < 75) { /* give player a break if larger font used */
	printf("\nERROR: screen size too small for rogue\n");
	byebye(0);
    }

    /*
     * Now that we have cols and lines, we can update our window
     * structure for non-hardware windows.
     */
#ifdef PC7300
    if (hardwindow < 0) {
	wdata.uw_x = 0;
	wdata.uw_y = 0;
	wdata.uw_width = COLS;
	wdata.uw_height = LINES;
	wdata.uw_uflags = 0;
	wdata.uw_hs = 1;
	wdata.uw_vs = 1;
	wdata.uw_baseline = 0;
    }
#endif
    setup();
    /*
     * Set up windows
     */
    cw = newwin(lines, cols, 0, 0);
    mw = newwin(lines, cols, 0, 0);
    hw = newwin(lines, cols, 0, 0);
    msgw = newwin(4, cols, 0, 0);
    keypad(cw,TRUE);
    keypad(msgw,TRUE);

    init_player();			/* Roll up the rogue */
    waswizard = wizard;

#ifdef WIZARD
    /* A super wizard doesn't have to get equipped */
    if (wizard && strcmp(getenv("SUPER"),"YES") == 0) {
	level = 1;
	new_level(NORMLEV);
    }
    else
#endif
    new_level(STARTLEV);		/* Draw current level */
    /*
     * Start up daemons and fuses
     */
    start_daemon(doctor, &player, AFTER);
    fuse(swander, NULL, WANDERTIME, AFTER);
    if (player.t_ctype == C_MAGICIAN || player.t_ctype == C_RANGER)
	    fuse(spell_recovery, NULL, SPELLTIME, AFTER);
    if (player.t_ctype == C_DRUID || player.t_ctype == C_RANGER)
	    fuse(chant_recovery, NULL, SPELLTIME, AFTER);
    if (player.t_ctype == C_CLERIC || player.t_ctype == C_PALADIN)
	    fuse(prayer_recovery, NULL, SPELLTIME, AFTER);
    start_daemon(stomach, NULL, AFTER);
    if (player.t_ctype == C_THIEF	||
	player.t_ctype == C_ASSASIN	||
	player.t_ctype == C_MONK)
	    start_daemon(trap_look, NULL, AFTER);

    /* Does this character have any special knowledge? */
    switch (player.t_ctype) {
	case C_ASSASIN:
	    /* Assassins automatically recognize poison */
	    p_know[P_POISON] = TRUE;
    }

    /* Choose a quest item */
    quest_item = rnd(MAXRELIC);
    draw(cw);
    msg("You have been quested to retrieve the %s....",
	rel_magic[quest_item].mi_name);
    mpos = 0;
    playit();
}

/*
 * endit:
 *	Exit the program abnormally.
 */

void
endit(int sig)
{
    fatal("Ok, if you want to exit that badly, I'll have to allow it\n");
}

/*
 * fatal:
 *	Exit the program, printing a message.
 */

void
fatal(char *s)
{
    clear();
    move(lines-2, 0);
    printw("%s", s);
    draw(stdscr);
    endwin();
#ifdef PC7300
    endhardwin();
#endif
    printf("\n");	/* So the curser doesn't stop at the end of the line */
    exit(0);
}

/*
 * rnd:
 *	Pick a very random number.
 */
int
rnd(int range)
{
    return(range <= 0 ? 0 : md_rand() % range);
}

/*
 * roll:
 *	roll a number of dice
 */

int
roll(int number, int sides)
{
    register int dtotal = 0;

    while(number--)
	dtotal += rnd(sides)+1;
    return dtotal;
}
# ifdef SIGTSTP
/*
 * handle stop and start signals
 */
void
tstp(int sig)
{
    mvcur(0, cols - 1, lines - 1, 0);
    endwin();
    fflush(stdout);
    kill(0, SIGTSTP);
    signal(SIGTSTP, tstp);
    raw();
    noecho();
    keypad(cw,1);
    keypad(msgw,1);
    clearok(curscr, TRUE);
    touchwin(cw);
    draw(cw);
    md_flushinp();
}
# endif

void
setup(void)
{
#ifdef CHECKTIME
    int  checkout();

    if (!author()) {
	signal(SIGALRM, checkout);
	alarm(CHECKTIME * 60);
    }
#endif
/*
#ifndef DUMP
    signal(SIGILL, bugkill);
#ifdef SIGTRAP
    signal(SIGTRAP, bugkill);
#endif
#ifdef SIGIOT
    signal(SIGIOT, bugkill);
#endif
#ifdef SIGEMT
    signal(SIGEMT, bugkill);
#endif
    signal(SIGFPE, bugkill);
#ifdef SIGBUS
    signal(SIGBUS, bugkill);
#endif
    signal(SIGSEGV, bugkill);
#ifdef SIGSYS
    signal(SIGSYS, bugkill);
#endif
#ifdef SIGPIPE
    signal(SIGPIPE, bugkill);
#endif
#endif
*/
#ifdef SIGTSTP
    signal(SIGTSTP, tstp);
#endif

#ifdef SIGHUP
    signal(SIGHUP, auto_save);
#endif
    signal(SIGTERM, auto_save);
    signal(SIGINT, quit);
#ifdef SIGQUIT
    signal(SIGQUIT, endit);
#endif
    raw();				/* Cbreak mode */
    noecho();				/* Echo off */
    nonl();
}

void
reopen_score(void)
{
    if (scoreboard != NULL)
        fclose(scoreboard);
    scoreboard = fopen(score_file, "r+");
    if (scoreboard == NULL && errno == ENOENT)
        scoreboard = fopen(score_file, "w+");
}

void
open_records(void)
{
    if (scoreboard == NULL)
        reopen_score();
#ifdef LOGFILE
    if (logfile == NULL)
        logfile = fopen(LOGFILE, "a");
#endif
    return;
}

/*
 * playit:
 *	The main loop of the program.  Loop until the game is over,
 * refreshing things and looking at the proper times.
 */

void
playit(void)
{
    register char *opts;


    /*
     * parse environment declaration of options
     */
    if ((opts = getenv("ROGUEOPTS")) != NULL)
	parse_opts(opts);


    player.t_oldpos = hero;
    oldrp = roomin(&hero);
    after = TRUE;
    command();			/* Command execution */
    endit(0);
}

/*
 * see if the system is being used too much for this game
 */
bool
too_much(void)
{
#if MAXPROCESSES
	if (loadav() > MAXPROCESSES)
		return(TRUE);
#endif
#if MAXUSERS
	if (ucount() > MAXUSERS)
		return(TRUE);
#endif
	return(FALSE);
}

/*
 * author:
 *	See if a user is an author of the program
 */
bool
author(void)
{
	switch (md_getuid()) {
#if AUTHOR
		case AUTHOR:
#endif
		case 0: /* always OK for root to play */
			return TRUE;
		default:
			return FALSE;
	}
}


#if CHECKTIME
static int num_checks = 0;	/* times we've gone over in checkout() */

checkout()
{
	static char *msgs[] = {
	"The system is too loaded for games. Please leave in %d minutes",
	"Please save your game.  You have %d minutes",
	"This is your last chance. You had better leave in %d minutes",
	};
	int checktime;

	signal(SIGALRM, checkout);
	if (!holiday() && !author()) {
	    msg("Game time is over. Your game is being saved.\n\n");
	    auto_save();		/* NO RETURN */
	}
	if (too_much())	{
	    if (num_checks >= 3)
		fatal("You didn't listen, so now you are DEAD !!\n");
	    checktime = CHECKTIME / (num_checks + 1);
	    chmsg(msgs[num_checks++], checktime);
	    alarm(checktime * 60);
	}
	else {
	    if (num_checks) {
		chmsg("The load has dropped. You have a reprieve.");
		num_checks = 0;
	    }
	    alarm(CHECKTIME * 60);
	}
}

/*
 * checkout()'s version of msg.  If we are in the middle of a shell, do a
 * printf instead of a msg to avoid the refresh.
 */
void
chmsg(char *fmt, int arg)
{
	if (in_shell) {
		printf(fmt, arg);
		putchar('\n');
		fflush(stdout);
	}
	else
		msg(fmt, arg);
}
#endif

#ifdef MAXPROCESSES

#include <fcntl.h>

int
loadav(void)
{
	char *sarcmd = "sar -v | cut -c17-20 | tail -2";
	char *gettycmd = "grep getty /etc/inittab | wc -l";
	char sysbuffer[BUFSIZ];
	char tempfile[50];
	char inbuf[BUFSIZ];
	int fd, nprocess, ngetty;

	sprintf(tempfile, "/tmp/rg%d", getpid());
	sprintf(sysbuffer, "%s > %s", sarcmd, tempfile);
	if (system(sysbuffer) != 0) {
	    debug ("system() call failed");
	    return (MAXPROCESSES - 1);
	}
	if ((fd = open(tempfile, O_RDONLY)) == -1) {
	    debug ("open() call failed");
	    return (MAXPROCESSES - 1);
	}
	if (read(fd, inbuf, BUFSIZ) == -1) {
	    debug ("read() call failed");
	    return (MAXPROCESSES - 1);
	}
	close(fd);
	sscanf(inbuf, "%d", &nprocess);
	sprintf(sysbuffer, "%s > %s", gettycmd, tempfile);
	if (system(sysbuffer) != 0) {
	    debug ("system() call failed");
	    return (MAXPROCESSES - 1);
	}
	if ((fd = open(tempfile, O_RDONLY)) == -1) {
	    debug ("open() call failed");
	    return (MAXPROCESSES - 1);
	}
	if (read(fd, inbuf, BUFSIZ) == -1) {
	    debug ("read() call failed");
	    return (MAXPROCESSES - 1);
	}
	close(fd);
	sscanf(inbuf, "%d", &ngetty);
	unlink(tempfile);
	return(nprocess - ngetty);
}
#endif

#ifdef MAXUSERS
/*
 * ucount:
 *	Count the number of people on the system
 */
#include <sys/types.h>
#include <utmp.h>
struct utmp buf;

int
ucount(void)
{
	reg struct utmp *up;
	reg FILE *utmp;
	reg int count;

	if ((utmp = fopen(UTMP, "r")) == NULL)
	    return 0;

	up = &buf;
	count = 0;
	while (fread(up, 1, sizeof (*up), utmp) > 0)
#ifdef BSD
		if (buf.ut_line[0] == 't')	/* On a tty */
#else
		if (buf.ut_type == USER_PROCESS)
#endif
			count++;
	fclose(utmp);
	return count;
}
#endif

/*
 * holiday:
 *	Returns TRUE when it is a good time to play rogue
 */
bool
holiday(void)
{
#ifdef CHECKTIME
	long now;
	struct tm *localtime();
	reg struct tm *ntime;


	time(&now);			/* get the current time */
	ntime = localtime(&now);
	if(ntime->tm_wday == 0 || ntime->tm_wday == 6)
		return TRUE;		/* OK on Sat & Sun */
	if(ntime->tm_hour < 8 || ntime->tm_hour >= 18)
		return TRUE;		/* OK before 8AM & after 6PM */
	if(ntime->tm_yday <= 7 || ntime->tm_yday >= 350)
		return TRUE;		/* OK during Christmas */
	return FALSE;			/* All other times are bad */
#else
	return TRUE;
#endif
}