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