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