view arogue5/main.c @ 264:778938a5c21d

UltraRogue: add location for character files. When using the -n option, UltraRogue will look for character files in a single location, similar to save files. The location is chosen by defining CHRDIR in getplay.c, at least until UltraRogue gets integrated with the build systems.
author John "Elwin" Edwards
date Sun, 19 Feb 2017 19:47:09 -0500
parents 3d4252fa2ed3
children d3968e9cb98d
line wrap: on
line source

/* 
 * Rogue
 *
 * Advanced Rogue
 * Copyright (C) 1984, 1985 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 <fcntl.h>
#include <sys/stat.h>
#include <limits.h>
#include <signal.h>
#include <time.h>
#include "mach_dep.h"
#include "network.h"
#include "rogue.h"

#ifdef CHECKTIME
static int num_checks;		/* times we've gone over in checkout() */
#endif

/*
 * fruits that you get at startup
 */
static char *funfruit[] = {
	"candleberry",	"caprifig",	"dewberry",	"elderberry",
	"gooseberry",	"guanabana",	"hagberry",	"ilama",
	"imbu",		"jaboticaba",	"jujube",	"litchi",	
	"mombin",	"pitanga",	"prickly pear", "rambutan",
	"sapodilla",	"soursop",	"sweetsop",	"whortleberry",
	"jellybean",	"apple",	"strawberry",	"blueberry",
	"peach",	"banana"
};
#define NFRUIT (sizeof(funfruit) / sizeof (char *))

void open_records(void);
bool holiday(void);

int
main(int argc, char *argv[], char *envp[])
{
    register char *env;
    char *roguedir;

    roguedir = md_getroguedir();
    md_init();

    /*
     * get home and options from environment
     */
    strncpy(home,md_gethomedir(),LINELEN);

#ifdef SAVEDIR
    if (argc >= 3 && !strcmp(argv[1], "-n")) {
        use_savedir = TRUE;
        strncpy(whoami, argv[2], LINELEN);
        whoami[LINELEN - 1] = '\0';
        if (snprintf(file_name, 256, "%s/%d-%s.ar5sav", SAVEDIR, 
                     md_getuid(), whoami) >= 256)
        {
            /* The name is too long */
            use_savedir = FALSE;
        }
    }
#endif
    /* Get default save file */
    if (!use_savedir) {
        strcpy(file_name, home);
        strcat(file_name, "arogue58.sav");
    }

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

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

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

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

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

    if (env == NULL || fruit[0] == '\0') {
	md_srand(md_random_seed());
	strcpy(fruit, funfruit[rnd(NFRUIT)]);
    }

    open_records();
    if (!use_savedir)
        md_normaluser();

    /*
     * 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) {
	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: "), "Si")) == 0)
	{
	    printf("Hail Mighty Wizard\n");
	    wizard = TRUE;
	    argv++;
	    argc--;
	}
#endif

#if MAXLOAD|MAXUSERS
    if (too_much() && !wizard && !author())
    {
	printf("Sorry, %s, but the system is too loaded now.\n", whoami);
	printf("Try again later.  Meanwhile, why not enjoy a%s %s?\n",
	    vowelstr(fruit), fruit);
	exit(1);
    }
#endif
    if (use_savedir)
    {
        /* Try to restore from file_name first. */
        if (!restore(file_name, envp))
            exit(1);
        /* If restore() returns true, there is no such saved game.
           So start a new one. */
    }
    else 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);

    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 */
    if (LINES < 24 || COLS < 80) {
	printf("\nERROR: screen size to small for rogue\n");
	byebye(-1);
    }

    if (!use_savedir) {
        if ((whoami == NULL) || (*whoami == '\0') || 
            (strcmp(whoami,"dosuser")==0))
        {
            echo();
            mvaddstr(23,2,"Rogue's Name? ");
            wgetnstr(stdscr,whoami,LINELEN);
            noecho();
        }

        if ((whoami == NULL) || (*whoami == '\0'))
            strcpy(whoami,"Rodney");
    }

    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,1);
    keypad(msgw,1);

    init_player();			/* Roll up the rogue */
    waswizard = wizard;
    new_level(NORMLEV);			/* Draw current level */
    /*
     * Start up daemons and fuses
     */
    start_daemon(doctor, &player, AFTER);
    fuse(swander, 0, WANDERTIME, AFTER);
    start_daemon(stomach, 0, AFTER);
    start_daemon(runners, 0, AFTER);
    if (player.t_ctype == C_THIEF)
	start_daemon(trap_look, 0, AFTER);

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

void
open_records(void)
{
    if (scorefd == -1)
        md_reopen_score();
#ifdef LOGFILE
    if (logfile == NULL)
        logfile = fopen(LOGFILE, "a");
#endif
    return;
}

/*
 * endit:
 *	Exit the program abnormally.
 */
void
endit(int sig)
{
    NOOP(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();
    printf("\n");	/* So the cursor 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 a)
{
    mvcur(0, COLS - 1, LINES - 1, 0);
    endwin();
    fflush(stdout);
    kill(0, SIGTSTP);
    signal(SIGTSTP, tstp);
    raw();
    noecho();
    keypad(cw,1);
    clearok(curscr, TRUE);
    touchwin(cw);
    draw(cw);
    md_flushinp();
}
# endif

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

#ifndef DUMP
#ifdef SIGHUP
    signal(SIGHUP, auto_save);
#endif
    signal(SIGILL, bugkill);
#ifdef SIGTRAP
    signal(SIGTRAP, bugkill);
#endif
#ifdef SIGIOT
    signal(SIGIOT, bugkill);
#endif
#if 0
    signal(SIGEMT, bugkill);
    signal(SIGFPE, bugkill);
    signal(SIGBUS, bugkill);
    signal(SIGSEGV, bugkill);
    signal(SIGSYS, bugkill);
    signal(SIGPIPE, bugkill);
#endif
    signal(SIGTERM, auto_save);
#endif

    signal(SIGINT, quit);
#ifndef DUMP
#ifdef SIGQUIT
    signal(SIGQUIT, endit);
#endif
#endif
#ifdef SIGTSTP
    signal(SIGTSTP, tstp);
#endif
#ifdef CHECKTIME
    if (!author())
    {
	signal(SIGALRM, checkout);
	alarm(CHECKTIME * 60);
	num_checks = 0;
    }
#endif
    nonl();
    crmode();				/* Cbreak mode */
    noecho();				/* Echo off */
}

/*
 * 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;
    while (playing)
	command();			/* Command execution */
    endit(0);
}

#if MAXLOAD|MAXUSERS
/*
 * see if the system is being used too much for this game
 */
bool
too_much(void)
{
#ifdef MAXLOAD
	double avec[3];
#endif

#ifdef MAXLOAD
	loadav(avec);
	return (avec[2] > (MAXLOAD / 10.0));
#else
	return (ucount() > MAXUSERS);
#endif
}
#endif

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


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

void
checkout(void)
{
	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()) {
	    wclear(cw);
	    mvwaddstr(cw, LINES / 2, 0,
		"Game time is over. Your game is being saved.\n\n");
	    draw(cw);
	    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);
	}
}
#endif

#ifdef LOADAV

#include <nlist.h>

struct nlist avenrun =
{
	"_avenrun"
};

void
loadav(double *avg)
{
	reg int kmem;

	if ((kmem = open("/dev/kmem", 0)) < 0)
		goto bad;
	nlist(NAMELIST, &avenrun);
	if (avenrun.n_type == 0) {
bad:
		avg[0] = avg[1] = avg[2] = 0.0;
		return;
	}
	lseek(kmem, (long) avenrun.n_value, 0);
	read(kmem, avg, 3 * sizeof (double));
}
#endif

#ifdef UCOUNT
/*
 * 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)
		if (buf.ut_type == USER_PROCESS)
			count++;
	fclose(utmp);
	return count;
}
#endif

/*
 * holiday:
 *	Returns TRUE when it is a good time to play rogue
 */
bool
holiday(void)
{
	time_t 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 >= 17)
		return TRUE;		/* OK before 8AM & after 5PM */
	if(ntime->tm_yday <= 7 || ntime->tm_yday >= 350)
		return TRUE;		/* OK during Christmas */
#if 0 /* not for now */
	if (access("/usr/tmp/.ryes",0) == 0)
	    return TRUE;		/* if author permission */
#endif

	return FALSE;			/* All other times are bad */
}