xref: /csrg-svn/games/hack/hack.main.c (revision 33150)
133149Sbostic /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
233149Sbostic /* hack.main.c - version 1.0.3 */
333149Sbostic 
433149Sbostic #include <stdio.h>
533149Sbostic #include <signal.h>
633149Sbostic #include "hack.h"
733149Sbostic 
833149Sbostic #ifdef QUEST
933149Sbostic #define	gamename	"quest"
1033149Sbostic #else
1133149Sbostic #define	gamename	"hack"
1233149Sbostic #endif QUEST
1333149Sbostic 
1433149Sbostic extern char *getlogin(), *getenv();
1533149Sbostic extern char plname[PL_NSIZ], pl_character[PL_CSIZ];
16*33150Sbostic extern struct permonst mons[CMNUM+2];
17*33150Sbostic extern char genocided[], fut_geno[];
1833149Sbostic 
1933149Sbostic int (*afternmv)();
2033149Sbostic int (*occupation)();
2133149Sbostic char *occtxt;			/* defined when occupation != NULL */
2233149Sbostic 
2333149Sbostic int done1();
2433149Sbostic int hangup();
2533149Sbostic 
2633149Sbostic int hackpid;				/* current pid */
2733149Sbostic int locknum;				/* max num of players */
2833149Sbostic #ifdef DEF_PAGER
2933149Sbostic char *catmore;				/* default pager */
3033149Sbostic #endif DEF_PAGER
3133149Sbostic char SAVEF[PL_NSIZ + 11] = "save/";	/* save/99999player */
3233149Sbostic char *hname;		/* name of the game (argv[0] of call) */
3333149Sbostic char obuf[BUFSIZ];	/* BUFSIZ is defined in stdio.h */
3433149Sbostic 
3533149Sbostic extern char *nomovemsg;
3633149Sbostic extern long wailmsg;
3733149Sbostic 
3833149Sbostic main(argc,argv)
3933149Sbostic int argc;
4033149Sbostic char *argv[];
4133149Sbostic {
4233149Sbostic 	register int fd;
4333149Sbostic #ifdef CHDIR
4433149Sbostic 	register char *dir;
4533149Sbostic #endif CHDIR
4633149Sbostic 
4733149Sbostic 	hname = argv[0];
4833149Sbostic 	hackpid = getpid();
4933149Sbostic 
5033149Sbostic #ifdef CHDIR			/* otherwise no chdir() */
5133149Sbostic 	/*
5233149Sbostic 	 * See if we must change directory to the playground.
5333149Sbostic 	 * (Perhaps hack runs suid and playground is inaccessible
5433149Sbostic 	 *  for the player.)
5533149Sbostic 	 * The environment variable HACKDIR is overridden by a
5633149Sbostic 	 *  -d command line option (must be the first option given)
5733149Sbostic 	 */
5833149Sbostic 
5933149Sbostic 	dir = getenv("HACKDIR");
6033149Sbostic 	if(argc > 1 && !strncmp(argv[1], "-d", 2)) {
6133149Sbostic 		argc--;
6233149Sbostic 		argv++;
6333149Sbostic 		dir = argv[0]+2;
6433149Sbostic 		if(*dir == '=' || *dir == ':') dir++;
6533149Sbostic 		if(!*dir && argc > 1) {
6633149Sbostic 			argc--;
6733149Sbostic 			argv++;
6833149Sbostic 			dir = argv[0];
6933149Sbostic 		}
7033149Sbostic 		if(!*dir)
7133149Sbostic 		    error("Flag -d must be followed by a directory name.");
7233149Sbostic 	}
7333149Sbostic #endif CHDIR
7433149Sbostic 
7533149Sbostic 	/*
7633149Sbostic 	 * Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS
7733149Sbostic 	 *			2. Use $USER or $LOGNAME	(if 1. fails)
7833149Sbostic 	 *			3. Use getlogin()		(if 2. fails)
7933149Sbostic 	 * The resulting name is overridden by command line options.
8033149Sbostic 	 * If everything fails, or if the resulting name is some generic
8133149Sbostic 	 * account like "games", "play", "player", "hack" then eventually
8233149Sbostic 	 * we'll ask him.
8333149Sbostic 	 * Note that we trust him here; it is possible to play under
8433149Sbostic 	 * somebody else's name.
8533149Sbostic 	 */
8633149Sbostic 	{ register char *s;
8733149Sbostic 
8833149Sbostic 	  initoptions();
8933149Sbostic 	  if(!*plname && (s = getenv("USER")))
9033149Sbostic 		(void) strncpy(plname, s, sizeof(plname)-1);
9133149Sbostic 	  if(!*plname && (s = getenv("LOGNAME")))
9233149Sbostic 		(void) strncpy(plname, s, sizeof(plname)-1);
9333149Sbostic 	  if(!*plname && (s = getlogin()))
9433149Sbostic 		(void) strncpy(plname, s, sizeof(plname)-1);
9533149Sbostic 	}
9633149Sbostic 
9733149Sbostic 	/*
9833149Sbostic 	 * Now we know the directory containing 'record' and
9933149Sbostic 	 * may do a prscore().
10033149Sbostic 	 */
10133149Sbostic 	if(argc > 1 && !strncmp(argv[1], "-s", 2)) {
10233149Sbostic #ifdef CHDIR
10333149Sbostic 		chdirx(dir,0);
10433149Sbostic #endif CHDIR
10533149Sbostic 		prscore(argc, argv);
10633149Sbostic 		exit(0);
10733149Sbostic 	}
10833149Sbostic 
10933149Sbostic 	/*
11033149Sbostic 	 * It seems he really wants to play.
11133149Sbostic 	 * Remember tty modes, to be restored on exit.
11233149Sbostic 	 */
11333149Sbostic 	gettty();
11433149Sbostic 	setbuf(stdout,obuf);
11533149Sbostic 	setrandom();
11633149Sbostic 	startup();
11733149Sbostic 	cls();
11833149Sbostic 	u.uhp = 1;	/* prevent RIP on early quits */
11933149Sbostic 	u.ux = FAR;	/* prevent nscr() */
12033149Sbostic 	(void) signal(SIGHUP, hangup);
12133149Sbostic 
12233149Sbostic 	/*
12333149Sbostic 	 * Find the creation date of this game,
12433149Sbostic 	 * so as to avoid restoring outdated savefiles.
12533149Sbostic 	 */
12633149Sbostic 	gethdate(hname);
12733149Sbostic 
12833149Sbostic 	/*
12933149Sbostic 	 * We cannot do chdir earlier, otherwise gethdate will fail.
13033149Sbostic 	 */
13133149Sbostic #ifdef CHDIR
13233149Sbostic 	chdirx(dir,1);
13333149Sbostic #endif CHDIR
13433149Sbostic 
13533149Sbostic 	/*
13633149Sbostic 	 * Process options.
13733149Sbostic 	 */
13833149Sbostic 	while(argc > 1 && argv[1][0] == '-'){
13933149Sbostic 		argv++;
14033149Sbostic 		argc--;
14133149Sbostic 		switch(argv[0][1]){
14233149Sbostic #ifdef WIZARD
14333149Sbostic 		case 'D':
14433149Sbostic /*			if(!strcmp(getlogin(), WIZARD)) */
14533149Sbostic 				wizard = TRUE;
14633149Sbostic /*			else
14733149Sbostic 				printf("Sorry.\n"); */
14833149Sbostic 			break;
14933149Sbostic #endif WIZARD
15033149Sbostic #ifdef NEWS
15133149Sbostic 		case 'n':
15233149Sbostic 			flags.nonews = TRUE;
15333149Sbostic 			break;
15433149Sbostic #endif NEWS
15533149Sbostic 		case 'u':
15633149Sbostic 			if(argv[0][2])
15733149Sbostic 			  (void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
15833149Sbostic 			else if(argc > 1) {
15933149Sbostic 			  argc--;
16033149Sbostic 			  argv++;
16133149Sbostic 			  (void) strncpy(plname, argv[0], sizeof(plname)-1);
16233149Sbostic 			} else
16333149Sbostic 				printf("Player name expected after -u\n");
16433149Sbostic 			break;
16533149Sbostic 		default:
16633149Sbostic 			/* allow -T for Tourist, etc. */
16733149Sbostic 			(void) strncpy(pl_character, argv[0]+1,
16833149Sbostic 				sizeof(pl_character)-1);
16933149Sbostic 
17033149Sbostic 			/* printf("Unknown option: %s\n", *argv); */
17133149Sbostic 		}
17233149Sbostic 	}
17333149Sbostic 
17433149Sbostic 	if(argc > 1)
17533149Sbostic 		locknum = atoi(argv[1]);
17633149Sbostic #ifdef MAX_NR_OF_PLAYERS
17733149Sbostic 	if(!locknum || locknum > MAX_NR_OF_PLAYERS)
17833149Sbostic 		locknum = MAX_NR_OF_PLAYERS;
17933149Sbostic #endif MAX_NR_OF_PLAYERS
18033149Sbostic #ifdef DEF_PAGER
18133149Sbostic 	if(!(catmore = getenv("HACKPAGER")) && !(catmore = getenv("PAGER")))
18233149Sbostic 		catmore = DEF_PAGER;
18333149Sbostic #endif DEF_PAGER
18433149Sbostic #ifdef MAIL
18533149Sbostic 	getmailstatus();
18633149Sbostic #endif MAIL
18733149Sbostic #ifdef WIZARD
18833149Sbostic 	if(wizard) (void) strcpy(plname, "wizard"); else
18933149Sbostic #endif WIZARD
19033149Sbostic 	if(!*plname || !strncmp(plname, "player", 4)
19133149Sbostic 		    || !strncmp(plname, "games", 4))
19233149Sbostic 		askname();
19333149Sbostic 	plnamesuffix();		/* strip suffix from name; calls askname() */
19433149Sbostic 				/* again if suffix was whole name */
19533149Sbostic 				/* accepts any suffix */
19633149Sbostic #ifdef WIZARD
19733149Sbostic 	if(!wizard) {
19833149Sbostic #endif WIZARD
19933149Sbostic 		/*
20033149Sbostic 		 * check for multiple games under the same name
20133149Sbostic 		 * (if !locknum) or check max nr of players (otherwise)
20233149Sbostic 		 */
20333149Sbostic 		(void) signal(SIGQUIT,SIG_IGN);
20433149Sbostic 		(void) signal(SIGINT,SIG_IGN);
20533149Sbostic 		if(!locknum)
20633149Sbostic 			(void) strcpy(lock,plname);
20733149Sbostic 		getlock();	/* sets lock if locknum != 0 */
20833149Sbostic #ifdef WIZARD
20933149Sbostic 	} else {
21033149Sbostic 		register char *sfoo;
21133149Sbostic 		(void) strcpy(lock,plname);
21233149Sbostic 		if(sfoo = getenv("MAGIC"))
21333149Sbostic 			while(*sfoo) {
21433149Sbostic 				switch(*sfoo++) {
21533149Sbostic 				case 'n': (void) srandom(*sfoo++);
21633149Sbostic 					break;
21733149Sbostic 				}
21833149Sbostic 			}
21933149Sbostic 		if(sfoo = getenv("GENOCIDED")){
22033149Sbostic 			if(*sfoo == '!'){
22133149Sbostic 				register struct permonst *pm = mons;
22233149Sbostic 				register char *gp = genocided;
22333149Sbostic 
22433149Sbostic 				while(pm < mons+CMNUM+2){
22533149Sbostic 					if(!index(sfoo, pm->mlet))
22633149Sbostic 						*gp++ = pm->mlet;
22733149Sbostic 					pm++;
22833149Sbostic 				}
22933149Sbostic 				*gp = 0;
23033149Sbostic 			} else
23133149Sbostic 				(void) strcpy(genocided, sfoo);
23233149Sbostic 			(void) strcpy(fut_geno, genocided);
23333149Sbostic 		}
23433149Sbostic 	}
23533149Sbostic #endif WIZARD
23633149Sbostic 	setftty();
23733149Sbostic 	(void) sprintf(SAVEF, "save/%d%s", getuid(), plname);
23833149Sbostic 	regularize(SAVEF+5);		/* avoid . or / in name */
23933149Sbostic 	if((fd = open(SAVEF,0)) >= 0 &&
24033149Sbostic 	   (uptodate(fd) || unlink(SAVEF) == 666)) {
24133149Sbostic 		(void) signal(SIGINT,done1);
24233149Sbostic 		pline("Restoring old save file...");
24333149Sbostic 		(void) fflush(stdout);
24433149Sbostic 		if(!dorecover(fd))
24533149Sbostic 			goto not_recovered;
24633149Sbostic 		pline("Hello %s, welcome to %s!", plname, gamename);
24733149Sbostic 		flags.move = 0;
24833149Sbostic 	} else {
24933149Sbostic not_recovered:
25033149Sbostic 		fobj = fcobj = invent = 0;
25133149Sbostic 		fmon = fallen_down = 0;
25233149Sbostic 		ftrap = 0;
25333149Sbostic 		fgold = 0;
25433149Sbostic 		flags.ident = 1;
25533149Sbostic 		init_objects();
25633149Sbostic 		u_init();
25733149Sbostic 
25833149Sbostic 		(void) signal(SIGINT,done1);
25933149Sbostic 		mklev();
26033149Sbostic 		u.ux = xupstair;
26133149Sbostic 		u.uy = yupstair;
26233149Sbostic 		(void) inshop();
26333149Sbostic 		setsee();
26433149Sbostic 		flags.botlx = 1;
26533149Sbostic 		makedog();
26633149Sbostic 		{ register struct monst *mtmp;
26733149Sbostic 		  if(mtmp = m_at(u.ux, u.uy)) mnexto(mtmp);	/* riv05!a3 */
26833149Sbostic 		}
26933149Sbostic 		seemons();
27033149Sbostic #ifdef NEWS
27133149Sbostic 		if(flags.nonews || !readnews())
27233149Sbostic 			/* after reading news we did docrt() already */
27333149Sbostic #endif NEWS
27433149Sbostic 			docrt();
27533149Sbostic 
27633149Sbostic 		/* give welcome message before pickup messages */
27733149Sbostic 		pline("Hello %s, welcome to %s!", plname, gamename);
27833149Sbostic 
27933149Sbostic 		pickup(1);
28033149Sbostic 		read_engr_at(u.ux,u.uy);
28133149Sbostic 		flags.move = 1;
28233149Sbostic 	}
28333149Sbostic 
28433149Sbostic 	flags.moonphase = phase_of_the_moon();
28533149Sbostic 	if(flags.moonphase == FULL_MOON) {
28633149Sbostic 		pline("You are lucky! Full moon tonight.");
28733149Sbostic 		u.uluck++;
28833149Sbostic 	} else if(flags.moonphase == NEW_MOON) {
28933149Sbostic 		pline("Be careful! New moon tonight.");
29033149Sbostic 	}
29133149Sbostic 
29233149Sbostic 	initrack();
29333149Sbostic 
29433149Sbostic 	for(;;) {
29533149Sbostic 		if(flags.move) {	/* actual time passed */
29633149Sbostic 
29733149Sbostic 			settrack();
29833149Sbostic 
29933149Sbostic 			if(moves%2 == 0 ||
30033149Sbostic 			  (!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) {
30133149Sbostic 				extern struct monst *makemon();
30233149Sbostic 				movemon();
30333149Sbostic 				if(!rn2(70))
30433149Sbostic 				    (void) makemon((struct permonst *)0, 0, 0);
30533149Sbostic 			}
30633149Sbostic 			if(Glib) glibr();
30733149Sbostic 			timeout();
30833149Sbostic 			++moves;
30933149Sbostic 			if(flags.time) flags.botl = 1;
31033149Sbostic 			if(u.uhp < 1) {
31133149Sbostic 				pline("You die...");
31233149Sbostic 				done("died");
31333149Sbostic 			}
31433149Sbostic 			if(u.uhp*10 < u.uhpmax && moves-wailmsg > 50){
31533149Sbostic 			    wailmsg = moves;
31633149Sbostic 			    if(u.uhp == 1)
31733149Sbostic 			    pline("You hear the wailing of the Banshee...");
31833149Sbostic 			    else
31933149Sbostic 			    pline("You hear the howling of the CwnAnnwn...");
32033149Sbostic 			}
32133149Sbostic 			if(u.uhp < u.uhpmax) {
32233149Sbostic 				if(u.ulevel > 9) {
32333149Sbostic 					if(Regeneration || !(moves%3)) {
32433149Sbostic 					    flags.botl = 1;
32533149Sbostic 					    u.uhp += rnd((int) u.ulevel-9);
32633149Sbostic 					    if(u.uhp > u.uhpmax)
32733149Sbostic 						u.uhp = u.uhpmax;
32833149Sbostic 					}
32933149Sbostic 				} else if(Regeneration ||
33033149Sbostic 					(!(moves%(22-u.ulevel*2)))) {
33133149Sbostic 					flags.botl = 1;
33233149Sbostic 					u.uhp++;
33333149Sbostic 				}
33433149Sbostic 			}
33533149Sbostic 			if(Teleportation && !rn2(85)) tele();
33633149Sbostic 			if(Searching && multi >= 0) (void) dosearch();
33733149Sbostic 			gethungry();
33833149Sbostic 			invault();
33933149Sbostic 			amulet();
34033149Sbostic 		}
34133149Sbostic 		if(multi < 0) {
34233149Sbostic 			if(!++multi){
34333149Sbostic 				pline(nomovemsg ? nomovemsg :
34433149Sbostic 					"You can move again.");
34533149Sbostic 				nomovemsg = 0;
34633149Sbostic 				if(afternmv) (*afternmv)();
34733149Sbostic 				afternmv = 0;
34833149Sbostic 			}
34933149Sbostic 		}
35033149Sbostic 
35133149Sbostic 		find_ac();
35233149Sbostic #ifndef QUEST
35333149Sbostic 		if(!flags.mv || Blind)
35433149Sbostic #endif QUEST
35533149Sbostic 		{
35633149Sbostic 			seeobjs();
35733149Sbostic 			seemons();
35833149Sbostic 			nscr();
35933149Sbostic 		}
36033149Sbostic 		if(flags.botl || flags.botlx) bot();
36133149Sbostic 
36233149Sbostic 		flags.move = 1;
36333149Sbostic 
36433149Sbostic 		if(multi >= 0 && occupation) {
36533149Sbostic 			if(monster_nearby())
36633149Sbostic 				stop_occupation();
36733149Sbostic 			else if ((*occupation)() == 0)
36833149Sbostic 				occupation = 0;
36933149Sbostic 			continue;
37033149Sbostic 		}
37133149Sbostic 
37233149Sbostic 		if(multi > 0) {
37333149Sbostic #ifdef QUEST
37433149Sbostic 			if(flags.run >= 4) finddir();
37533149Sbostic #endif QUEST
37633149Sbostic 			lookaround();
37733149Sbostic 			if(!multi) {	/* lookaround may clear multi */
37833149Sbostic 				flags.move = 0;
37933149Sbostic 				continue;
38033149Sbostic 			}
38133149Sbostic 			if(flags.mv) {
38233149Sbostic 				if(multi < COLNO && !--multi)
38333149Sbostic 					flags.mv = flags.run = 0;
38433149Sbostic 				domove();
38533149Sbostic 			} else {
38633149Sbostic 				--multi;
38733149Sbostic 				rhack(save_cm);
38833149Sbostic 			}
38933149Sbostic 		} else if(multi == 0) {
39033149Sbostic #ifdef MAIL
39133149Sbostic 			ckmailstatus();
39233149Sbostic #endif MAIL
39333149Sbostic 			rhack((char *) 0);
39433149Sbostic 		}
39533149Sbostic 		if(multi && multi%7 == 0)
39633149Sbostic 			(void) fflush(stdout);
39733149Sbostic 	}
39833149Sbostic }
39933149Sbostic 
40033149Sbostic glo(foo)
40133149Sbostic register foo;
40233149Sbostic {
40333149Sbostic 	/* construct the string  xlock.n  */
40433149Sbostic 	register char *tf;
40533149Sbostic 
40633149Sbostic 	tf = lock;
40733149Sbostic 	while(*tf && *tf != '.') tf++;
40833149Sbostic 	(void) sprintf(tf, ".%d", foo);
40933149Sbostic }
41033149Sbostic 
41133149Sbostic /*
41233149Sbostic  * plname is filled either by an option (-u Player  or  -uPlayer) or
41333149Sbostic  * explicitly (-w implies wizard) or by askname.
41433149Sbostic  * It may still contain a suffix denoting pl_character.
41533149Sbostic  */
41633149Sbostic askname(){
41733149Sbostic register int c,ct;
41833149Sbostic 	printf("\nWho are you? ");
41933149Sbostic 	(void) fflush(stdout);
42033149Sbostic 	ct = 0;
42133149Sbostic 	while((c = getchar()) != '\n'){
42233149Sbostic 		if(c == EOF) error("End of input\n");
42333149Sbostic 		/* some people get confused when their erase char is not ^H */
42433149Sbostic 		if(c == '\010') {
42533149Sbostic 			if(ct) ct--;
42633149Sbostic 			continue;
42733149Sbostic 		}
42833149Sbostic 		if(c != '-')
42933149Sbostic 		if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_';
43033149Sbostic 		if(ct < sizeof(plname)-1) plname[ct++] = c;
43133149Sbostic 	}
43233149Sbostic 	plname[ct] = 0;
43333149Sbostic 	if(ct == 0) askname();
43433149Sbostic }
43533149Sbostic 
43633149Sbostic /*VARARGS1*/
43733149Sbostic impossible(s,x1,x2)
43833149Sbostic register char *s;
43933149Sbostic {
44033149Sbostic 	pline(s,x1,x2);
44133149Sbostic 	pline("Program in disorder - perhaps you'd better Quit.");
44233149Sbostic }
44333149Sbostic 
44433149Sbostic #ifdef CHDIR
44533149Sbostic static
44633149Sbostic chdirx(dir, wr)
44733149Sbostic char *dir;
44833149Sbostic boolean wr;
44933149Sbostic {
45033149Sbostic 
45133149Sbostic #ifdef SECURE
45233149Sbostic 	if(dir					/* User specified directory? */
45333149Sbostic #ifdef HACKDIR
45433149Sbostic 	       && strcmp(dir, HACKDIR)		/* and not the default? */
45533149Sbostic #endif HACKDIR
45633149Sbostic 		) {
45733149Sbostic 		(void) setuid(getuid());		/* Ron Wessels */
45833149Sbostic 		(void) setgid(getgid());
45933149Sbostic 	}
46033149Sbostic #endif SECURE
46133149Sbostic 
46233149Sbostic #ifdef HACKDIR
46333149Sbostic 	if(dir == NULL)
46433149Sbostic 		dir = HACKDIR;
46533149Sbostic #endif HACKDIR
46633149Sbostic 
46733149Sbostic 	if(dir && chdir(dir) < 0) {
46833149Sbostic 		perror(dir);
46933149Sbostic 		error("Cannot chdir to %s.", dir);
47033149Sbostic 	}
47133149Sbostic 
47233149Sbostic 	/* warn the player if he cannot write the record file */
47333149Sbostic 	/* perhaps we should also test whether . is writable */
47433149Sbostic 	/* unfortunately the access systemcall is worthless */
47533149Sbostic 	if(wr) {
47633149Sbostic 	    register fd;
47733149Sbostic 
47833149Sbostic 	    if(dir == NULL)
47933149Sbostic 		dir = ".";
48033149Sbostic 	    if((fd = open(RECORD, 2)) < 0) {
48133149Sbostic 		printf("Warning: cannot write %s/%s", dir, RECORD);
48233149Sbostic 		getret();
48333149Sbostic 	    } else
48433149Sbostic 		(void) close(fd);
48533149Sbostic 	}
48633149Sbostic }
48733149Sbostic #endif CHDIR
48833149Sbostic 
48933149Sbostic stop_occupation()
49033149Sbostic {
49133149Sbostic 	if(occupation) {
49233149Sbostic 		pline("You stop %s.", occtxt);
49333149Sbostic 		occupation = 0;
49433149Sbostic 	}
49533149Sbostic }
496