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