133149Sbostic /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
233149Sbostic /* hack.main.c - version 1.0.3 */
333149Sbostic
433149Sbostic #include <stdio.h>
533149Sbostic #include <signal.h>
633149Sbostic #include "hack.h"
733149Sbostic
833149Sbostic #ifdef QUEST
933149Sbostic #define gamename "quest"
1033149Sbostic #else
1133149Sbostic #define gamename "hack"
12*47699Sbostic #endif
1333149Sbostic
1433149Sbostic extern char *getlogin(), *getenv();
1533149Sbostic extern char plname[PL_NSIZ], pl_character[PL_CSIZ];
1633150Sbostic extern struct permonst mons[CMNUM+2];
1733150Sbostic extern char genocided[], fut_geno[];
1833149Sbostic
1933149Sbostic int (*afternmv)();
2033149Sbostic int (*occupation)();
2133149Sbostic char *occtxt; /* defined when occupation != NULL */
2233149Sbostic
23*47699Sbostic void done1();
24*47699Sbostic void hangup();
2533149Sbostic
2633149Sbostic int hackpid; /* current pid */
2733149Sbostic int locknum; /* max num of players */
2833149Sbostic #ifdef DEF_PAGER
2933149Sbostic char *catmore; /* default pager */
30*47699Sbostic #endif
3133149Sbostic char SAVEF[PL_NSIZ + 11] = "save/"; /* save/99999player */
3233149Sbostic char *hname; /* name of the game (argv[0] of call) */
3333149Sbostic char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */
3433149Sbostic
3533149Sbostic extern char *nomovemsg;
3633149Sbostic extern long wailmsg;
3733149Sbostic
38*47699Sbostic #ifdef CHDIR
39*47699Sbostic static void chdirx();
40*47699Sbostic #endif
41*47699Sbostic
main(argc,argv)4233149Sbostic main(argc,argv)
4333149Sbostic int argc;
4433149Sbostic char *argv[];
4533149Sbostic {
4633149Sbostic register int fd;
4733149Sbostic #ifdef CHDIR
4833149Sbostic register char *dir;
49*47699Sbostic #endif
5033149Sbostic
5133149Sbostic hname = argv[0];
5233149Sbostic hackpid = getpid();
5333149Sbostic
5433149Sbostic #ifdef CHDIR /* otherwise no chdir() */
5533149Sbostic /*
5633149Sbostic * See if we must change directory to the playground.
5733149Sbostic * (Perhaps hack runs suid and playground is inaccessible
5833149Sbostic * for the player.)
5933149Sbostic * The environment variable HACKDIR is overridden by a
6033149Sbostic * -d command line option (must be the first option given)
6133149Sbostic */
6233149Sbostic
6333149Sbostic dir = getenv("HACKDIR");
6433149Sbostic if(argc > 1 && !strncmp(argv[1], "-d", 2)) {
6533149Sbostic argc--;
6633149Sbostic argv++;
6733149Sbostic dir = argv[0]+2;
6833149Sbostic if(*dir == '=' || *dir == ':') dir++;
6933149Sbostic if(!*dir && argc > 1) {
7033149Sbostic argc--;
7133149Sbostic argv++;
7233149Sbostic dir = argv[0];
7333149Sbostic }
7433149Sbostic if(!*dir)
7533149Sbostic error("Flag -d must be followed by a directory name.");
7633149Sbostic }
77*47699Sbostic #endif
7833149Sbostic
7933149Sbostic /*
8033149Sbostic * Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS
8133149Sbostic * 2. Use $USER or $LOGNAME (if 1. fails)
8233149Sbostic * 3. Use getlogin() (if 2. fails)
8333149Sbostic * The resulting name is overridden by command line options.
8433149Sbostic * If everything fails, or if the resulting name is some generic
8533149Sbostic * account like "games", "play", "player", "hack" then eventually
8633149Sbostic * we'll ask him.
8733149Sbostic * Note that we trust him here; it is possible to play under
8833149Sbostic * somebody else's name.
8933149Sbostic */
9033149Sbostic { register char *s;
9133149Sbostic
9233149Sbostic initoptions();
9333149Sbostic if(!*plname && (s = getenv("USER")))
9433149Sbostic (void) strncpy(plname, s, sizeof(plname)-1);
9533149Sbostic if(!*plname && (s = getenv("LOGNAME")))
9633149Sbostic (void) strncpy(plname, s, sizeof(plname)-1);
9733149Sbostic if(!*plname && (s = getlogin()))
9833149Sbostic (void) strncpy(plname, s, sizeof(plname)-1);
9933149Sbostic }
10033149Sbostic
10133149Sbostic /*
10233149Sbostic * Now we know the directory containing 'record' and
10333149Sbostic * may do a prscore().
10433149Sbostic */
10533149Sbostic if(argc > 1 && !strncmp(argv[1], "-s", 2)) {
10633149Sbostic #ifdef CHDIR
10733149Sbostic chdirx(dir,0);
108*47699Sbostic #endif
10933149Sbostic prscore(argc, argv);
11033149Sbostic exit(0);
11133149Sbostic }
11233149Sbostic
11333149Sbostic /*
11433149Sbostic * It seems he really wants to play.
11533149Sbostic * Remember tty modes, to be restored on exit.
11633149Sbostic */
11733149Sbostic gettty();
11833149Sbostic setbuf(stdout,obuf);
11933149Sbostic setrandom();
12033149Sbostic startup();
12133149Sbostic cls();
12233149Sbostic u.uhp = 1; /* prevent RIP on early quits */
12333149Sbostic u.ux = FAR; /* prevent nscr() */
12433149Sbostic (void) signal(SIGHUP, hangup);
12533149Sbostic
12633149Sbostic /*
12733149Sbostic * Find the creation date of this game,
12833149Sbostic * so as to avoid restoring outdated savefiles.
12933149Sbostic */
13033149Sbostic gethdate(hname);
13133149Sbostic
13233149Sbostic /*
13333149Sbostic * We cannot do chdir earlier, otherwise gethdate will fail.
13433149Sbostic */
13533149Sbostic #ifdef CHDIR
13633149Sbostic chdirx(dir,1);
137*47699Sbostic #endif
13833149Sbostic
13933149Sbostic /*
14033149Sbostic * Process options.
14133149Sbostic */
14233149Sbostic while(argc > 1 && argv[1][0] == '-'){
14333149Sbostic argv++;
14433149Sbostic argc--;
14533149Sbostic switch(argv[0][1]){
14633149Sbostic #ifdef WIZARD
14733149Sbostic case 'D':
14833149Sbostic /* if(!strcmp(getlogin(), WIZARD)) */
14933149Sbostic wizard = TRUE;
15033149Sbostic /* else
15133149Sbostic printf("Sorry.\n"); */
15233149Sbostic break;
153*47699Sbostic #endif
15433149Sbostic #ifdef NEWS
15533149Sbostic case 'n':
15633149Sbostic flags.nonews = TRUE;
15733149Sbostic break;
158*47699Sbostic #endif
15933149Sbostic case 'u':
16033149Sbostic if(argv[0][2])
16133149Sbostic (void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
16233149Sbostic else if(argc > 1) {
16333149Sbostic argc--;
16433149Sbostic argv++;
16533149Sbostic (void) strncpy(plname, argv[0], sizeof(plname)-1);
16633149Sbostic } else
16733149Sbostic printf("Player name expected after -u\n");
16833149Sbostic break;
16933149Sbostic default:
17033149Sbostic /* allow -T for Tourist, etc. */
17133149Sbostic (void) strncpy(pl_character, argv[0]+1,
17233149Sbostic sizeof(pl_character)-1);
17333149Sbostic
17433149Sbostic /* printf("Unknown option: %s\n", *argv); */
17533149Sbostic }
17633149Sbostic }
17733149Sbostic
17833149Sbostic if(argc > 1)
17933149Sbostic locknum = atoi(argv[1]);
18033149Sbostic #ifdef MAX_NR_OF_PLAYERS
18133149Sbostic if(!locknum || locknum > MAX_NR_OF_PLAYERS)
18233149Sbostic locknum = MAX_NR_OF_PLAYERS;
183*47699Sbostic #endif
18433149Sbostic #ifdef DEF_PAGER
18533149Sbostic if(!(catmore = getenv("HACKPAGER")) && !(catmore = getenv("PAGER")))
18633149Sbostic catmore = DEF_PAGER;
187*47699Sbostic #endif
18833149Sbostic #ifdef MAIL
18933149Sbostic getmailstatus();
190*47699Sbostic #endif
19133149Sbostic #ifdef WIZARD
19233149Sbostic if(wizard) (void) strcpy(plname, "wizard"); else
193*47699Sbostic #endif
19433149Sbostic if(!*plname || !strncmp(plname, "player", 4)
19533149Sbostic || !strncmp(plname, "games", 4))
19633149Sbostic askname();
19733149Sbostic plnamesuffix(); /* strip suffix from name; calls askname() */
19833149Sbostic /* again if suffix was whole name */
19933149Sbostic /* accepts any suffix */
20033149Sbostic #ifdef WIZARD
20133149Sbostic if(!wizard) {
202*47699Sbostic #endif
20333149Sbostic /*
20433149Sbostic * check for multiple games under the same name
20533149Sbostic * (if !locknum) or check max nr of players (otherwise)
20633149Sbostic */
20733149Sbostic (void) signal(SIGQUIT,SIG_IGN);
20833149Sbostic (void) signal(SIGINT,SIG_IGN);
20933149Sbostic if(!locknum)
21033149Sbostic (void) strcpy(lock,plname);
21133149Sbostic getlock(); /* sets lock if locknum != 0 */
21233149Sbostic #ifdef WIZARD
21333149Sbostic } else {
21433149Sbostic register char *sfoo;
21533149Sbostic (void) strcpy(lock,plname);
21633149Sbostic if(sfoo = getenv("MAGIC"))
21733149Sbostic while(*sfoo) {
21833149Sbostic switch(*sfoo++) {
21933149Sbostic case 'n': (void) srandom(*sfoo++);
22033149Sbostic break;
22133149Sbostic }
22233149Sbostic }
22333149Sbostic if(sfoo = getenv("GENOCIDED")){
22433149Sbostic if(*sfoo == '!'){
22533149Sbostic register struct permonst *pm = mons;
22633149Sbostic register char *gp = genocided;
22733149Sbostic
22833149Sbostic while(pm < mons+CMNUM+2){
22933149Sbostic if(!index(sfoo, pm->mlet))
23033149Sbostic *gp++ = pm->mlet;
23133149Sbostic pm++;
23233149Sbostic }
23333149Sbostic *gp = 0;
23433149Sbostic } else
23533149Sbostic (void) strcpy(genocided, sfoo);
23633149Sbostic (void) strcpy(fut_geno, genocided);
23733149Sbostic }
23833149Sbostic }
239*47699Sbostic #endif
24033149Sbostic setftty();
24133149Sbostic (void) sprintf(SAVEF, "save/%d%s", getuid(), plname);
24233149Sbostic regularize(SAVEF+5); /* avoid . or / in name */
24333149Sbostic if((fd = open(SAVEF,0)) >= 0 &&
24433149Sbostic (uptodate(fd) || unlink(SAVEF) == 666)) {
24533149Sbostic (void) signal(SIGINT,done1);
24633149Sbostic pline("Restoring old save file...");
24733149Sbostic (void) fflush(stdout);
24833149Sbostic if(!dorecover(fd))
24933149Sbostic goto not_recovered;
25033149Sbostic pline("Hello %s, welcome to %s!", plname, gamename);
25133149Sbostic flags.move = 0;
25233149Sbostic } else {
25333149Sbostic not_recovered:
25433149Sbostic fobj = fcobj = invent = 0;
25533149Sbostic fmon = fallen_down = 0;
25633149Sbostic ftrap = 0;
25733149Sbostic fgold = 0;
25833149Sbostic flags.ident = 1;
25933149Sbostic init_objects();
26033149Sbostic u_init();
26133149Sbostic
26233149Sbostic (void) signal(SIGINT,done1);
26333149Sbostic mklev();
26433149Sbostic u.ux = xupstair;
26533149Sbostic u.uy = yupstair;
26633149Sbostic (void) inshop();
26733149Sbostic setsee();
26833149Sbostic flags.botlx = 1;
26933149Sbostic makedog();
27033149Sbostic { register struct monst *mtmp;
27133149Sbostic if(mtmp = m_at(u.ux, u.uy)) mnexto(mtmp); /* riv05!a3 */
27233149Sbostic }
27333149Sbostic seemons();
27433149Sbostic #ifdef NEWS
27533149Sbostic if(flags.nonews || !readnews())
27633149Sbostic /* after reading news we did docrt() already */
277*47699Sbostic #endif
27833149Sbostic docrt();
27933149Sbostic
28033149Sbostic /* give welcome message before pickup messages */
28133149Sbostic pline("Hello %s, welcome to %s!", plname, gamename);
28233149Sbostic
28333149Sbostic pickup(1);
28433149Sbostic read_engr_at(u.ux,u.uy);
28533149Sbostic flags.move = 1;
28633149Sbostic }
28733149Sbostic
28833149Sbostic flags.moonphase = phase_of_the_moon();
28933149Sbostic if(flags.moonphase == FULL_MOON) {
29033149Sbostic pline("You are lucky! Full moon tonight.");
29133149Sbostic u.uluck++;
29233149Sbostic } else if(flags.moonphase == NEW_MOON) {
29333149Sbostic pline("Be careful! New moon tonight.");
29433149Sbostic }
29533149Sbostic
29633149Sbostic initrack();
29733149Sbostic
29833149Sbostic for(;;) {
29933149Sbostic if(flags.move) { /* actual time passed */
30033149Sbostic
30133149Sbostic settrack();
30233149Sbostic
30333149Sbostic if(moves%2 == 0 ||
30433149Sbostic (!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) {
30533149Sbostic extern struct monst *makemon();
30633149Sbostic movemon();
30733149Sbostic if(!rn2(70))
30833149Sbostic (void) makemon((struct permonst *)0, 0, 0);
30933149Sbostic }
31033149Sbostic if(Glib) glibr();
31133149Sbostic timeout();
31233149Sbostic ++moves;
31333149Sbostic if(flags.time) flags.botl = 1;
31433149Sbostic if(u.uhp < 1) {
31533149Sbostic pline("You die...");
31633149Sbostic done("died");
31733149Sbostic }
31833149Sbostic if(u.uhp*10 < u.uhpmax && moves-wailmsg > 50){
31933149Sbostic wailmsg = moves;
32033149Sbostic if(u.uhp == 1)
32133149Sbostic pline("You hear the wailing of the Banshee...");
32233149Sbostic else
32333149Sbostic pline("You hear the howling of the CwnAnnwn...");
32433149Sbostic }
32533149Sbostic if(u.uhp < u.uhpmax) {
32633149Sbostic if(u.ulevel > 9) {
32733149Sbostic if(Regeneration || !(moves%3)) {
32833149Sbostic flags.botl = 1;
32933149Sbostic u.uhp += rnd((int) u.ulevel-9);
33033149Sbostic if(u.uhp > u.uhpmax)
33133149Sbostic u.uhp = u.uhpmax;
33233149Sbostic }
33333149Sbostic } else if(Regeneration ||
33433149Sbostic (!(moves%(22-u.ulevel*2)))) {
33533149Sbostic flags.botl = 1;
33633149Sbostic u.uhp++;
33733149Sbostic }
33833149Sbostic }
33933149Sbostic if(Teleportation && !rn2(85)) tele();
34033149Sbostic if(Searching && multi >= 0) (void) dosearch();
34133149Sbostic gethungry();
34233149Sbostic invault();
34333149Sbostic amulet();
34433149Sbostic }
34533149Sbostic if(multi < 0) {
34633149Sbostic if(!++multi){
34733149Sbostic pline(nomovemsg ? nomovemsg :
34833149Sbostic "You can move again.");
34933149Sbostic nomovemsg = 0;
35033149Sbostic if(afternmv) (*afternmv)();
35133149Sbostic afternmv = 0;
35233149Sbostic }
35333149Sbostic }
35433149Sbostic
35533149Sbostic find_ac();
35633149Sbostic #ifndef QUEST
35733149Sbostic if(!flags.mv || Blind)
358*47699Sbostic #endif
35933149Sbostic {
36033149Sbostic seeobjs();
36133149Sbostic seemons();
36233149Sbostic nscr();
36333149Sbostic }
36433149Sbostic if(flags.botl || flags.botlx) bot();
36533149Sbostic
36633149Sbostic flags.move = 1;
36733149Sbostic
36833149Sbostic if(multi >= 0 && occupation) {
36933149Sbostic if(monster_nearby())
37033149Sbostic stop_occupation();
37133149Sbostic else if ((*occupation)() == 0)
37233149Sbostic occupation = 0;
37333149Sbostic continue;
37433149Sbostic }
37533149Sbostic
37633149Sbostic if(multi > 0) {
37733149Sbostic #ifdef QUEST
37833149Sbostic if(flags.run >= 4) finddir();
379*47699Sbostic #endif
38033149Sbostic lookaround();
38133149Sbostic if(!multi) { /* lookaround may clear multi */
38233149Sbostic flags.move = 0;
38333149Sbostic continue;
38433149Sbostic }
38533149Sbostic if(flags.mv) {
38633149Sbostic if(multi < COLNO && !--multi)
38733149Sbostic flags.mv = flags.run = 0;
38833149Sbostic domove();
38933149Sbostic } else {
39033149Sbostic --multi;
39133149Sbostic rhack(save_cm);
39233149Sbostic }
39333149Sbostic } else if(multi == 0) {
39433149Sbostic #ifdef MAIL
39533149Sbostic ckmailstatus();
396*47699Sbostic #endif
39733149Sbostic rhack((char *) 0);
39833149Sbostic }
39933149Sbostic if(multi && multi%7 == 0)
40033149Sbostic (void) fflush(stdout);
40133149Sbostic }
40233149Sbostic }
40333149Sbostic
glo(foo)40433149Sbostic glo(foo)
40533149Sbostic register foo;
40633149Sbostic {
40733149Sbostic /* construct the string xlock.n */
40833149Sbostic register char *tf;
40933149Sbostic
41033149Sbostic tf = lock;
41133149Sbostic while(*tf && *tf != '.') tf++;
41233149Sbostic (void) sprintf(tf, ".%d", foo);
41333149Sbostic }
41433149Sbostic
41533149Sbostic /*
41633149Sbostic * plname is filled either by an option (-u Player or -uPlayer) or
41733149Sbostic * explicitly (-w implies wizard) or by askname.
41833149Sbostic * It may still contain a suffix denoting pl_character.
41933149Sbostic */
askname()42033149Sbostic askname(){
42133149Sbostic register int c,ct;
42233149Sbostic printf("\nWho are you? ");
42333149Sbostic (void) fflush(stdout);
42433149Sbostic ct = 0;
42533149Sbostic while((c = getchar()) != '\n'){
42633149Sbostic if(c == EOF) error("End of input\n");
42733149Sbostic /* some people get confused when their erase char is not ^H */
42833149Sbostic if(c == '\010') {
42933149Sbostic if(ct) ct--;
43033149Sbostic continue;
43133149Sbostic }
43233149Sbostic if(c != '-')
43333149Sbostic if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_';
43433149Sbostic if(ct < sizeof(plname)-1) plname[ct++] = c;
43533149Sbostic }
43633149Sbostic plname[ct] = 0;
43733149Sbostic if(ct == 0) askname();
43833149Sbostic }
43933149Sbostic
44033149Sbostic /*VARARGS1*/
impossible(s,x1,x2)44133149Sbostic impossible(s,x1,x2)
44233149Sbostic register char *s;
44333149Sbostic {
44433149Sbostic pline(s,x1,x2);
44533149Sbostic pline("Program in disorder - perhaps you'd better Quit.");
44633149Sbostic }
44733149Sbostic
44833149Sbostic #ifdef CHDIR
449*47699Sbostic static void
chdirx(dir,wr)45033149Sbostic chdirx(dir, wr)
45133149Sbostic char *dir;
45233149Sbostic boolean wr;
45333149Sbostic {
45433149Sbostic
45533149Sbostic #ifdef SECURE
45633149Sbostic if(dir /* User specified directory? */
45733149Sbostic #ifdef HACKDIR
45833149Sbostic && strcmp(dir, HACKDIR) /* and not the default? */
459*47699Sbostic #endif
46033149Sbostic ) {
46133149Sbostic (void) setuid(getuid()); /* Ron Wessels */
46233149Sbostic (void) setgid(getgid());
46333149Sbostic }
464*47699Sbostic #endif
46533149Sbostic
46633149Sbostic #ifdef HACKDIR
46733149Sbostic if(dir == NULL)
46833149Sbostic dir = HACKDIR;
469*47699Sbostic #endif
47033149Sbostic
47133149Sbostic if(dir && chdir(dir) < 0) {
47233149Sbostic perror(dir);
47333149Sbostic error("Cannot chdir to %s.", dir);
47433149Sbostic }
47533149Sbostic
47633149Sbostic /* warn the player if he cannot write the record file */
47733149Sbostic /* perhaps we should also test whether . is writable */
47833149Sbostic /* unfortunately the access systemcall is worthless */
47933149Sbostic if(wr) {
48033149Sbostic register fd;
48133149Sbostic
48233149Sbostic if(dir == NULL)
48333149Sbostic dir = ".";
48433149Sbostic if((fd = open(RECORD, 2)) < 0) {
48533149Sbostic printf("Warning: cannot write %s/%s", dir, RECORD);
48633149Sbostic getret();
48733149Sbostic } else
48833149Sbostic (void) close(fd);
48933149Sbostic }
49033149Sbostic }
491*47699Sbostic #endif
49233149Sbostic
stop_occupation()49333149Sbostic stop_occupation()
49433149Sbostic {
49533149Sbostic if(occupation) {
49633149Sbostic pline("You stop %s.", occtxt);
49733149Sbostic occupation = 0;
49833149Sbostic }
49933149Sbostic }
500