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