hack.main.c revision 1.6
1/*
2 * Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985.
3 */
4
5#ifndef lint
6static char rcsid[] = "$NetBSD: hack.main.c,v 1.3 1995/03/23 08:30:35 cgd Exp $";
7#endif /* not lint */
8
9#include <stdio.h>
10#include <signal.h>
11#include "hack.h"
12
13#ifdef QUEST
14#define	gamename	"quest"
15#else
16#define	gamename	"hack"
17#endif
18
19extern char *getlogin(), *getenv();
20extern char plname[PL_NSIZ], pl_character[PL_CSIZ];
21extern struct permonst mons[CMNUM+2];
22extern char genocided[], fut_geno[];
23
24int (*afternmv)();
25int (*occupation)();
26char *occtxt;			/* defined when occupation != NULL */
27
28void done1();
29void hangup();
30
31int hackpid;				/* current pid */
32int locknum;				/* max num of players */
33#ifdef DEF_PAGER
34char *catmore;				/* default pager */
35#endif
36char SAVEF[PL_NSIZ + 11] = "save/";	/* save/99999player */
37char *hname;		/* name of the game (argv[0] of call) */
38char obuf[BUFSIZ];	/* BUFSIZ is defined in stdio.h */
39
40extern char *nomovemsg;
41extern long wailmsg;
42
43#ifdef CHDIR
44static void chdirx();
45#endif
46
47main(argc,argv)
48int argc;
49char *argv[];
50{
51	register int fd;
52#ifdef CHDIR
53	register char *dir;
54#endif
55
56	hname = argv[0];
57	hackpid = getpid();
58
59#ifdef CHDIR			/* otherwise no chdir() */
60	/*
61	 * See if we must change directory to the playground.
62	 * (Perhaps hack runs suid and playground is inaccessible
63	 *  for the player.)
64	 * The environment variable HACKDIR is overridden by a
65	 *  -d command line option (must be the first option given)
66	 */
67
68	dir = getenv("HACKDIR");
69	if(argc > 1 && !strncmp(argv[1], "-d", 2)) {
70		argc--;
71		argv++;
72		dir = argv[0]+2;
73		if(*dir == '=' || *dir == ':') dir++;
74		if(!*dir && argc > 1) {
75			argc--;
76			argv++;
77			dir = argv[0];
78		}
79		if(!*dir)
80		    error("Flag -d must be followed by a directory name.");
81	}
82#endif
83
84	/*
85	 * Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS
86	 *			2. Use $LOGNAME or $USER	(if 1. fails)
87	 *			3. Use getlogin()		(if 2. fails)
88	 * The resulting name is overridden by command line options.
89	 * If everything fails, or if the resulting name is some generic
90	 * account like "games", "play", "player", "hack" then eventually
91	 * we'll ask him.
92	 * Note that we trust him here; it is possible to play under
93	 * somebody else's name.
94	 */
95	{ register char *s;
96
97	  initoptions();
98	  if(!*plname && (s = getenv("LOGNAME")))
99		(void) strncpy(plname, s, sizeof(plname)-1);
100	  if(!*plname && (s = getenv("USER")))
101		(void) strncpy(plname, s, sizeof(plname)-1);
102	  if(!*plname && (s = getlogin()))
103		(void) strncpy(plname, s, sizeof(plname)-1);
104	  if(*plname)
105		plname[sizeof(plname)-1] = '\0';
106	}
107
108	/*
109	 * Now we know the directory containing 'record' and
110	 * may do a prscore().
111	 */
112	if(argc > 1 && !strncmp(argv[1], "-s", 2)) {
113#ifdef CHDIR
114		chdirx(dir,0);
115#endif
116		prscore(argc, argv);
117		exit(0);
118	}
119
120	/*
121	 * It seems he really wants to play.
122	 * Remember tty modes, to be restored on exit.
123	 */
124	gettty();
125	setbuf(stdout,obuf);
126	umask(007);
127	setrandom();
128	startup();
129	cls();
130	u.uhp = 1;	/* prevent RIP on early quits */
131	u.ux = FAR;	/* prevent nscr() */
132	(void) signal(SIGHUP, hangup);
133
134	/*
135	 * Find the creation date of this game,
136	 * so as to avoid restoring outdated savefiles.
137	 */
138	gethdate(hname);
139
140	/*
141	 * We cannot do chdir earlier, otherwise gethdate will fail.
142	 */
143#ifdef CHDIR
144	chdirx(dir,1);
145#endif
146
147	/*
148	 * Process options.
149	 */
150	while(argc > 1 && argv[1][0] == '-'){
151		argv++;
152		argc--;
153		switch(argv[0][1]){
154#ifdef WIZARD
155		case 'D':
156/*			if(!strcmp(getlogin(), WIZARD)) */
157				wizard = TRUE;
158/*			else
159				printf("Sorry.\n"); */
160			break;
161#endif
162#ifdef NEWS
163		case 'n':
164			flags.nonews = TRUE;
165			break;
166#endif
167		case 'u':
168			if(argv[0][2]) {
169			  (void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
170			  plname[sizeof(plname)-1] = '\0';
171			} else if(argc > 1) {
172			  argc--;
173			  argv++;
174			  (void) strncpy(plname, argv[0], sizeof(plname)-1);
175			  plname[sizeof(plname)-1] = '\0';
176			} else
177				printf("Player name expected after -u\n");
178			break;
179		default:
180			/* allow -T for Tourist, etc. */
181			(void) strncpy(pl_character, argv[0]+1,
182				sizeof(pl_character)-1);
183			plname[sizeof(pl_character)-1] = '\0';
184
185			/* printf("Unknown option: %s\n", *argv); */
186		}
187	}
188
189	if(argc > 1)
190		locknum = atoi(argv[1]);
191#ifdef MAX_NR_OF_PLAYERS
192	if(!locknum || locknum > MAX_NR_OF_PLAYERS)
193		locknum = MAX_NR_OF_PLAYERS;
194#endif
195#ifdef DEF_PAGER
196	if(!(catmore = getenv("HACKPAGER")) && !(catmore = getenv("PAGER")))
197		catmore = DEF_PAGER;
198#endif
199#ifdef MAIL
200	getmailstatus();
201#endif
202#ifdef WIZARD
203	if(wizard) (void) strcpy(plname, "wizard"); else
204#endif
205	if(!*plname || !strncmp(plname, "player", 4)
206		    || !strncmp(plname, "games", 4))
207		askname();
208	plnamesuffix();		/* strip suffix from name; calls askname() */
209				/* again if suffix was whole name */
210				/* accepts any suffix */
211#ifdef WIZARD
212	if(!wizard) {
213#endif
214		/*
215		 * check for multiple games under the same name
216		 * (if !locknum) or check max nr of players (otherwise)
217		 */
218		(void) signal(SIGQUIT,SIG_IGN);
219		(void) signal(SIGINT,SIG_IGN);
220		if(!locknum)
221			(void) strcpy(lock,plname);
222		getlock();	/* sets lock if locknum != 0 */
223#ifdef WIZARD
224	} else {
225		register char *sfoo;
226		(void) strcpy(lock,plname);
227		if(sfoo = getenv("MAGIC"))
228			while(*sfoo) {
229				switch(*sfoo++) {
230				case 'n': (void) srandom(*sfoo++);
231					break;
232				}
233			}
234		if(sfoo = getenv("GENOCIDED")){
235			if(*sfoo == '!'){
236				register struct permonst *pm = mons;
237				register char *gp = genocided;
238
239				while(pm < mons+CMNUM+2){
240					if(!strchr(sfoo, pm->mlet))
241						*gp++ = pm->mlet;
242					pm++;
243				}
244				*gp = 0;
245			} else
246				(void) strcpy(genocided, sfoo);
247			(void) strcpy(fut_geno, genocided);
248		}
249	}
250#endif
251	setftty();
252	(void) sprintf(SAVEF, "save/%d%s", getuid(), plname);
253	regularize(SAVEF+5);		/* avoid . or / in name */
254	if((fd = open(SAVEF, O_RDONLY)) >= 0 &&
255	   (uptodate(fd) || unlink(SAVEF) == 666)) {
256		(void) signal(SIGINT,done1);
257		pline("Restoring old save file...");
258		(void) fflush(stdout);
259		if(!dorecover(fd))
260			goto not_recovered;
261		pline("Hello %s, welcome to %s!", plname, gamename);
262		flags.move = 0;
263	} else {
264not_recovered:
265		fobj = fcobj = invent = 0;
266		fmon = fallen_down = 0;
267		ftrap = 0;
268		fgold = 0;
269		flags.ident = 1;
270		init_objects();
271		u_init();
272
273		(void) signal(SIGINT,done1);
274		mklev();
275		u.ux = xupstair;
276		u.uy = yupstair;
277		(void) inshop();
278		setsee();
279		flags.botlx = 1;
280		makedog();
281		{ register struct monst *mtmp;
282		  if(mtmp = m_at(u.ux, u.uy)) mnexto(mtmp);	/* riv05!a3 */
283		}
284		seemons();
285#ifdef NEWS
286		if(flags.nonews || !readnews())
287			/* after reading news we did docrt() already */
288#endif
289			docrt();
290
291		/* give welcome message before pickup messages */
292		pline("Hello %s, welcome to %s!", plname, gamename);
293
294		pickup(1);
295		read_engr_at(u.ux,u.uy);
296		flags.move = 1;
297	}
298
299	flags.moonphase = phase_of_the_moon();
300	if(flags.moonphase == FULL_MOON) {
301		pline("You are lucky! Full moon tonight.");
302		u.uluck++;
303	} else if(flags.moonphase == NEW_MOON) {
304		pline("Be careful! New moon tonight.");
305	}
306
307	initrack();
308
309	for(;;) {
310		if(flags.move) {	/* actual time passed */
311
312			settrack();
313
314			if(moves%2 == 0 ||
315			  (!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) {
316				extern struct monst *makemon();
317				movemon();
318				if(!rn2(70))
319				    (void) makemon((struct permonst *)0, 0, 0);
320			}
321			if(Glib) glibr();
322			timeout();
323			++moves;
324			if(flags.time) flags.botl = 1;
325			if(u.uhp < 1) {
326				pline("You die...");
327				done("died");
328			}
329			if(u.uhp*10 < u.uhpmax && moves-wailmsg > 50){
330			    wailmsg = moves;
331			    if(u.uhp == 1)
332			    pline("You hear the wailing of the Banshee...");
333			    else
334			    pline("You hear the howling of the CwnAnnwn...");
335			}
336			if(u.uhp < u.uhpmax) {
337				if(u.ulevel > 9) {
338					if(Regeneration || !(moves%3)) {
339					    flags.botl = 1;
340					    u.uhp += rnd((int) u.ulevel-9);
341					    if(u.uhp > u.uhpmax)
342						u.uhp = u.uhpmax;
343					}
344				} else if(Regeneration ||
345					(!(moves%(22-u.ulevel*2)))) {
346					flags.botl = 1;
347					u.uhp++;
348				}
349			}
350			if(Teleportation && !rn2(85)) tele();
351			if(Searching && multi >= 0) (void) dosearch();
352			gethungry();
353			invault();
354			amulet();
355		}
356		if(multi < 0) {
357			if(!++multi){
358				pline(nomovemsg ? nomovemsg :
359					"You can move again.");
360				nomovemsg = 0;
361				if(afternmv) (*afternmv)();
362				afternmv = 0;
363			}
364		}
365
366		find_ac();
367#ifndef QUEST
368		if(!flags.mv || Blind)
369#endif
370		{
371			seeobjs();
372			seemons();
373			nscr();
374		}
375		if(flags.botl || flags.botlx) bot();
376
377		flags.move = 1;
378
379		if(multi >= 0 && occupation) {
380			if(monster_nearby())
381				stop_occupation();
382			else if ((*occupation)() == 0)
383				occupation = 0;
384			continue;
385		}
386
387		if(multi > 0) {
388#ifdef QUEST
389			if(flags.run >= 4) finddir();
390#endif
391			lookaround();
392			if(!multi) {	/* lookaround may clear multi */
393				flags.move = 0;
394				continue;
395			}
396			if(flags.mv) {
397				if(multi < COLNO && !--multi)
398					flags.mv = flags.run = 0;
399				domove();
400			} else {
401				--multi;
402				rhack(save_cm);
403			}
404		} else if(multi == 0) {
405#ifdef MAIL
406			ckmailstatus();
407#endif
408			rhack((char *) 0);
409		}
410		if(multi && multi%7 == 0)
411			(void) fflush(stdout);
412	}
413}
414
415glo(foo)
416register foo;
417{
418	/* construct the string  xlock.n  */
419	register char *tf;
420
421	tf = lock;
422	while(*tf && *tf != '.') tf++;
423	(void) sprintf(tf, ".%d", foo);
424}
425
426/*
427 * plname is filled either by an option (-u Player  or  -uPlayer) or
428 * explicitly (-w implies wizard) or by askname.
429 * It may still contain a suffix denoting pl_character.
430 */
431askname(){
432register int c,ct;
433	printf("\nWho are you? ");
434	(void) fflush(stdout);
435	ct = 0;
436	while((c = getchar()) != '\n'){
437		if(c == EOF) error("End of input\n");
438		/* some people get confused when their erase char is not ^H */
439		if(c == '\010') {
440			if(ct) ct--;
441			continue;
442		}
443		if(c != '-')
444		if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_';
445		if(ct < sizeof(plname)-1) plname[ct++] = c;
446	}
447	plname[ct] = 0;
448	if(ct == 0) askname();
449}
450
451/*VARARGS1*/
452impossible(s,x1,x2)
453register char *s;
454{
455	pline(s,x1,x2);
456	pline("Program in disorder - perhaps you'd better Quit.");
457}
458
459#ifdef CHDIR
460static void
461chdirx(dir, wr)
462char *dir;
463boolean wr;
464{
465
466#ifdef SECURE
467	if(dir					/* User specified directory? */
468#ifdef HACKDIR
469	       && strcmp(dir, HACKDIR)		/* and not the default? */
470#endif
471		) {
472		/* revoke */
473		setegid(getgid());
474		setgid(getgid());
475	}
476#endif
477
478#ifdef HACKDIR
479	if(dir == NULL)
480		dir = HACKDIR;
481#endif
482
483	if(dir && chdir(dir) < 0) {
484		perror(dir);
485		error("Cannot chdir to %s.", dir);
486	}
487
488	/* warn the player if he cannot write the record file */
489	/* perhaps we should also test whether . is writable */
490	/* unfortunately the access systemcall is worthless */
491	if(wr) {
492	    register fd;
493
494	    if(dir == NULL)
495		dir = ".";
496	    if((fd = open(RECORD, O_RDWR)) < 0) {
497		printf("Warning: cannot write %s/%s", dir, RECORD);
498		getret();
499	    } else
500		(void) close(fd);
501	}
502}
503#endif
504
505stop_occupation()
506{
507	if(occupation) {
508		pline("You stop %s.", occtxt);
509		occupation = 0;
510	}
511}
512