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