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