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