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