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