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