nrelease - fix/improve livecd
[dragonfly.git] / games / hack / hack.main.c
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 }