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