Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[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.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 }