xref: /dragonfly/games/hack/hack.main.c (revision 029e6489)
1 /*	$NetBSD: hack.main.c,v 1.17 2011/08/06 20:42:43 dholland Exp $	*/
2 
3 /*
4  * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
5  * Amsterdam
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met:
11  *
12  * - Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * - Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in the
17  * documentation and/or other materials provided with the distribution.
18  *
19  * - Neither the name of the Stichting Centrum voor Wiskunde en
20  * Informatica, nor the names of its contributors may be used to endorse or
21  * promote products derived from this software without specific prior
22  * written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
28  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 /*
38  * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
39  * All rights reserved.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. The name of the author may not be used to endorse or promote products
50  *    derived from this software without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
53  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
54  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
55  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
56  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
57  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
58  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
59  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
60  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
61  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62  */
63 
64 #include <signal.h>
65 #include <stdlib.h>
66 #include <unistd.h>
67 #include <fcntl.h>
68 #include "hack.h"
69 #include "extern.h"
70 
71 #ifdef QUEST
72 #define	gamename	"quest"
73 #else
74 #define	gamename	"hack"
75 #endif
76 
77 int             (*afternmv)(void);
78 int             (*occupation)(void);
79 const char           *occtxt;		/* defined when occupation != NULL */
80 
81 int             hackpid;	/* current pid */
82 int             locknum;	/* max num of players */
83 #ifdef DEF_PAGER
84 const char     *catmore;	/* default pager */
85 #endif
86 char            SAVEF[PL_NSIZ + 11] = "save/";	/* save/99999player */
87 char           *hname;		/* name of the game (argv[0] of call) */
88 
89 static char obuf[BUFSIZ];	/* BUFSIZ is defined in stdio.h */
90 
91 int main(int, char *[]);
92 static void chdirx(const char *, boolean);
93 
94 int
95 main(int argc, char *argv[])
96 {
97 	int             fd;
98 #ifdef CHDIR
99 	char           *dir;
100 #endif
101 
102 	/* Check for dirty tricks with closed fds 0, 1, 2 */
103 	fd = open("/dev/null", O_RDONLY);
104 	if (fd < 3)
105 		exit(1);
106 	close(fd);
107 
108 	hname = argv[0];
109 	hackpid = getpid();
110 
111 #ifdef CHDIR			/* otherwise no chdir() */
112 	/*
113 	 * See if we must change directory to the playground.
114 	 * (Perhaps hack runs suid and playground is inaccessible
115 	 *  for the player.)
116 	 * The environment variable HACKDIR is overridden by a
117 	 *  -d command line option (must be the first option given)
118 	 */
119 
120 	dir = getenv("HACKDIR");
121 	if (argc > 1 && !strncmp(argv[1], "-d", 2)) {
122 		argc--;
123 		argv++;
124 		dir = argv[0] + 2;
125 		if (*dir == '=' || *dir == ':')
126 			dir++;
127 		if (!*dir && argc > 1) {
128 			argc--;
129 			argv++;
130 			dir = argv[0];
131 		}
132 		if (!*dir)
133 			error("Flag -d must be followed by a directory name.");
134 	}
135 #endif
136 
137 	/*
138 	 * Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS
139 	 *			2. Use $USER or $LOGNAME	(if 1. fails)
140 	 *			3. Use getlogin()		(if 2. fails)
141 	 * The resulting name is overridden by command line options.
142 	 * If everything fails, or if the resulting name is some generic
143 	 * account like "games", "play", "player", "hack" then eventually
144 	 * we'll ask him.
145 	 * Note that we trust him here; it is possible to play under
146 	 * somebody else's name.
147 	 */
148 	{
149 		char           *s;
150 
151 		initoptions();
152 		if (!*plname && (s = getenv("USER")))
153 			(void) strncpy(plname, s, sizeof(plname) - 1);
154 		if (!*plname && (s = getenv("LOGNAME")))
155 			(void) strncpy(plname, s, sizeof(plname) - 1);
156 		if (!*plname && (s = getlogin()))
157 			(void) strncpy(plname, s, sizeof(plname) - 1);
158 	}
159 
160 	/*
161 	 * Now we know the directory containing 'record' and
162 	 * may do a prscore().
163 	 */
164 	if (argc > 1 && !strncmp(argv[1], "-s", 2)) {
165 #ifdef CHDIR
166 		chdirx(dir, 0);
167 #endif
168 		prscore(argc, argv);
169 		exit(0);
170 	}
171 	/*
172 	 * It seems he really wants to play.
173 	 * Remember tty modes, to be restored on exit.
174 	 */
175 	gettty();
176 	setbuf(stdout, obuf);
177 	setrandom();
178 	startup();
179 	cls();
180 	u.uhp = 1;		/* prevent RIP on early quits */
181 	u.ux = FAR;		/* prevent nscr() */
182 	(void) signal(SIGHUP, hang_up);
183 
184 	/*
185 	 * Find the creation date of this game,
186 	 * so as to avoid restoring outdated savefiles.
187 	 */
188 	gethdate(hname);
189 
190 	/*
191 	 * We cannot do chdir earlier, otherwise gethdate will fail.
192 	 */
193 #ifdef CHDIR
194 	chdirx(dir, 1);
195 #endif
196 
197 	/*
198 	 * Process options.
199 	 */
200 	while (argc > 1 && argv[1][0] == '-') {
201 		argv++;
202 		argc--;
203 		switch (argv[0][1]) {
204 #ifdef WIZARD
205 		case 'D':
206 			/* if(!strcmp(getlogin(), WIZARD)) */
207 			wizard = TRUE;
208 			/*
209 			 * else printf("Sorry.\n");
210 			 */
211 			break;
212 #endif
213 #ifdef NEWS
214 		case 'n':
215 			flags.nonews = TRUE;
216 			break;
217 #endif
218 		case 'u':
219 			if (argv[0][2])
220 				(void) strncpy(plname, argv[0] + 2, sizeof(plname) - 1);
221 			else if (argc > 1) {
222 				argc--;
223 				argv++;
224 				(void) strncpy(plname, argv[0], sizeof(plname) - 1);
225 			} else
226 				printf("Player name expected after -u\n");
227 			break;
228 		default:
229 			/* allow -T for Tourist, etc. */
230 			(void) strncpy(pl_character, argv[0] + 1,
231 				       sizeof(pl_character) - 1);
232 
233 			/* printf("Unknown option: %s\n", *argv); */
234 		}
235 	}
236 
237 	if (argc > 1)
238 		locknum = atoi(argv[1]);
239 #ifdef MAX_NR_OF_PLAYERS
240 	if (!locknum || locknum > MAX_NR_OF_PLAYERS)
241 		locknum = MAX_NR_OF_PLAYERS;
242 #endif
243 #ifdef DEF_PAGER
244 	if (((catmore = getenv("HACKPAGER")) == NULL &&
245 	    (catmore = getenv("PAGER")) == NULL) ||
246 	    catmore[0] == '\0')
247 		catmore = DEF_PAGER;
248 #endif
249 #ifdef MAIL
250 	getmailstatus();
251 #endif
252 #ifdef WIZARD
253 	if (wizard)
254 		(void) strcpy(plname, "wizard");
255 	else
256 #endif
257 		if (!*plname || !strncmp(plname, "player", 4)
258 		    || !strncmp(plname, "games", 4))
259 		askname();
260 	plnamesuffix();		/* strip suffix from name; calls askname() */
261 	/* again if suffix was whole name */
262 	/* accepts any suffix */
263 #ifdef WIZARD
264 	if (!wizard) {
265 #endif
266 		/*
267 		 * check for multiple games under the same name
268 		 * (if !locknum) or check max nr of players (otherwise)
269 		 */
270 		(void) signal(SIGQUIT, SIG_IGN);
271 		(void) signal(SIGINT, SIG_IGN);
272 		if (!locknum)
273 			(void) strcpy(lock, plname);
274 		getlock();	/* sets lock if locknum != 0 */
275 #ifdef WIZARD
276 	} else {
277 		char           *sfoo;
278 		(void) strcpy(lock, plname);
279 		if ((sfoo = getenv("MAGIC")) != NULL)
280 			while (*sfoo) {
281 				switch (*sfoo++) {
282 				case 'n':
283 					(void) srandom(*sfoo++);
284 					break;
285 				}
286 			}
287 		if ((sfoo = getenv("GENOCIDED")) != NULL) {
288 			if (*sfoo == '!') {
289 				const struct permonst *pm = mons;
290 				char           *gp = genocided;
291 
292 				while (pm < mons + CMNUM + 2) {
293 					if (!strchr(sfoo, pm->mlet))
294 						*gp++ = pm->mlet;
295 					pm++;
296 				}
297 				*gp = 0;
298 			} else
299 				(void) strlcpy(genocided, sfoo,
300 						sizeof(genocided));
301 			(void) strcpy(fut_geno, genocided);
302 		}
303 	}
304 #endif
305 	setftty();
306 	(void) snprintf(SAVEF, sizeof(SAVEF), "save/%d%s", getuid(), plname);
307 	regularize(SAVEF + 5);	/* avoid . or / in name */
308 	if ((fd = open(SAVEF, O_RDONLY)) >= 0 &&
309 	    (uptodate(fd) || unlink(SAVEF) == 666)) {
310 		(void) signal(SIGINT, done1);
311 		pline("Restoring old save file...");
312 		(void) fflush(stdout);
313 		if (!dorecover(fd))
314 			goto not_recovered;
315 		pline("Hello %s, welcome to %s!", plname, gamename);
316 		flags.move = 0;
317 	} else {
318 not_recovered:
319 		fobj = fcobj = invent = 0;
320 		fmon = fallen_down = 0;
321 		ftrap = 0;
322 		fgold = 0;
323 		flags.ident = 1;
324 		init_objects();
325 		u_init();
326 
327 		(void) signal(SIGINT, done1);
328 		mklev();
329 		u.ux = xupstair;
330 		u.uy = yupstair;
331 		(void) inshop();
332 		setsee();
333 		flags.botlx = 1;
334 		makedog();
335 		{
336 			struct monst   *mtmp;
337 			if ((mtmp = m_at(u.ux, u.uy)) != NULL)
338 				mnexto(mtmp);	/* riv05!a3 */
339 		}
340 		seemons();
341 #ifdef NEWS
342 		if (flags.nonews || !readnews())
343 			/* after reading news we did docrt() already */
344 #endif
345 			docrt();
346 
347 		/* give welcome message before pickup messages */
348 		pline("Hello %s, welcome to %s!", plname, gamename);
349 
350 		pickup(1);
351 		read_engr_at(u.ux, u.uy);
352 		flags.move = 1;
353 	}
354 
355 	flags.moonphase = phase_of_the_moon();
356 	if (flags.moonphase == FULL_MOON) {
357 		pline("You are lucky! Full moon tonight.");
358 		u.uluck++;
359 	} else if (flags.moonphase == NEW_MOON) {
360 		pline("Be careful! New moon tonight.");
361 	}
362 	initrack();
363 
364 	for (;;) {
365 		if (flags.move) {	/* actual time passed */
366 
367 			settrack();
368 
369 			if (moves % 2 == 0 ||
370 			    (!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) {
371 				movemon();
372 				if (!rn2(70))
373 					(void) makemon((struct permonst *) 0, 0, 0);
374 			}
375 			if (Glib)
376 				glibr();
377 			timeout();
378 			++moves;
379 			if (flags.time)
380 				flags.botl = 1;
381 			if (u.uhp < 1) {
382 				pline("You die...");
383 				done("died");
384 			}
385 			if (u.uhp * 10 < u.uhpmax && moves - wailmsg > 50) {
386 				wailmsg = moves;
387 				if (u.uhp == 1)
388 					pline("You hear the wailing of the Banshee...");
389 				else
390 					pline("You hear the howling of the CwnAnnwn...");
391 			}
392 			if (u.uhp < u.uhpmax) {
393 				if (u.ulevel > 9) {
394 					if (Regeneration || !(moves % 3)) {
395 						flags.botl = 1;
396 						u.uhp += rnd((int) u.ulevel - 9);
397 						if (u.uhp > u.uhpmax)
398 							u.uhp = u.uhpmax;
399 					}
400 				} else if (Regeneration ||
401 					 (!(moves % (22 - u.ulevel * 2)))) {
402 					flags.botl = 1;
403 					u.uhp++;
404 				}
405 			}
406 			if (Teleportation && !rn2(85))
407 				tele();
408 			if (Searching && multi >= 0)
409 				(void) dosearch();
410 			gethungry();
411 			invault();
412 			amulet();
413 		}
414 		if (multi < 0) {
415 			if (!++multi) {
416 				if (nomovemsg)
417 					pline("%s", nomovemsg);
418 				else
419 					pline("You can move again.");
420 				nomovemsg = 0;
421 				if (afternmv)
422 					(*afternmv) ();
423 				afternmv = 0;
424 			}
425 		}
426 		find_ac();
427 #ifndef QUEST
428 		if (!flags.mv || Blind)
429 #endif
430 		{
431 			seeobjs();
432 			seemons();
433 			nscr();
434 		}
435 		if (flags.botl || flags.botlx)
436 			bot();
437 
438 		flags.move = 1;
439 
440 		if (multi >= 0 && occupation) {
441 			if (monster_nearby())
442 				stop_occupation();
443 			else if ((*occupation) () == 0)
444 				occupation = 0;
445 			continue;
446 		}
447 		if (multi > 0) {
448 #ifdef QUEST
449 			if (flags.run >= 4)
450 				finddir();
451 #endif
452 			lookaround();
453 			if (!multi) {	/* lookaround may clear multi */
454 				flags.move = 0;
455 				continue;
456 			}
457 			if (flags.mv) {
458 				if (multi < COLNO && !--multi)
459 					flags.mv = flags.run = 0;
460 				domove();
461 			} else {
462 				--multi;
463 				rhack(save_cm);
464 			}
465 		} else if (multi == 0) {
466 #ifdef MAIL
467 			ckmailstatus();
468 #endif
469 			rhack(NULL);
470 		}
471 		if (multi && multi % 7 == 0)
472 			(void) fflush(stdout);
473 	}
474 }
475 
476 void
477 glo(int foo)
478 {
479 	/* construct the string  xlock.n  */
480 	size_t pos;
481 
482 	pos = 0;
483 	while (lock[pos] && lock[pos] != '.')
484 		pos++;
485 	(void) snprintf(lock + pos, sizeof(lock) - pos, ".%d", foo);
486 }
487 
488 /*
489  * plname is filled either by an option (-u Player  or  -uPlayer) or
490  * explicitly (-w implies wizard) or by askname.
491  * It may still contain a suffix denoting pl_character.
492  */
493 void
494 askname(void)
495 {
496 	int             c, ct;
497 	printf("\nWho are you? ");
498 	(void) fflush(stdout);
499 	ct = 0;
500 	while ((c = getchar()) != '\n') {
501 		if (c == EOF)
502 			error("End of input\n");
503 		/* some people get confused when their erase char is not ^H */
504 		if (c == '\010') {
505 			if (ct)
506 				ct--;
507 			continue;
508 		}
509 		if (c != '-')
510 			if (c < 'A' || (c > 'Z' && c < 'a') || c > 'z')
511 				c = '_';
512 		if (ct < (int)sizeof(plname) - 1)
513 			plname[ct++] = c;
514 	}
515 	plname[ct] = 0;
516 	if (ct == 0)
517 		askname();
518 }
519 
520 /* VARARGS1 */
521 void
522 impossible(const char *s, ...)
523 {
524 	va_list ap;
525 
526 	va_start(ap, s);
527 	vpline(s, ap);
528 	va_end(ap);
529 	pline("Program in disorder - perhaps you'd better Quit.");
530 }
531 
532 #ifdef CHDIR
533 static void
534 chdirx(const char *dir, boolean wr)
535 {
536 
537 #ifdef SECURE
538 	if (dir			/* User specified directory? */
539 #ifdef HACKDIR
540 	    && strcmp(dir, HACKDIR)	/* and not the default? */
541 #endif
542 		) {
543 		(void) setuid(getuid());	/* Ron Wessels */
544 		(void) setgid(getgid());
545 	}
546 #endif
547 
548 #ifdef HACKDIR
549 	if (dir == NULL)
550 		dir = HACKDIR;
551 #endif
552 
553 	if (dir && chdir(dir) < 0) {
554 		perror(dir);
555 		error("Cannot chdir to %s.", dir);
556 	}
557 	/* warn the player if he cannot write the record file */
558 	/* perhaps we should also test whether . is writable */
559 	/* unfortunately the access systemcall is worthless */
560 	if (wr) {
561 		int fd;
562 
563 		if (dir == NULL)
564 			dir = ".";
565 		if ((fd = open(RECORD, O_RDWR)) < 0) {
566 			printf("Warning: cannot write %s/%s", dir, RECORD);
567 			getret();
568 		} else
569 			(void) close(fd);
570 	}
571 }
572 #endif
573 
574 void
575 stop_occupation(void)
576 {
577 	if (occupation) {
578 		pline("You stop %s.", occtxt);
579 		occupation = 0;
580 	}
581 }
582