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