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