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