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