1: /* 2: * Phantasia 3.2 -- Interterminal fantasy game 3: * 4: * Edward A. Estes 5: * AT&T Teletype Corp., September 4, 1984 6: */ 7: 8: /* 9: * This is the program which drives the whole mess. Hopefully, you will be 10: * able to wade throught the garbage if you have to fix something. 11: * several undocumented items exist. The program checks uid and sets the 12: * boolean flag 'su' (super user) if the person is allowed special powers. 13: * The 'su' may execute any of the valar/council options. Also, 14: * a 'vaporize' option exists to kill anybody at will. The 'su' can select 15: * character type 7, which starts out with the maximum possible in each 16: * category. (The resulting character is an experimento.) The 'su' may 17: * also change the stats of other characters with the -x option. 18: */ 19: 20: /* 21: * The program allocates as much file space as it needs to store characters, 22: * so the possibility exists for the character file to grow without bound. 23: * The file is purged upon normal entry to try to avoid that problem. 24: * A similar problem exists for energy voids. To alleviate the problem here, 25: * the void file is cleared with every new king. 26: */ 27: 28: /* 29: * The support functions are split between various files with no apparent 30: * order. Use of 'ctags' is recommended to find a particular function. 31: */ 32: 33: /* 34: * Put one line of text into the file 'motd' for announcements, etc. 35: */ 36: 37: /* 38: * If ENEMY is defined, a list of restricted login names is checked 39: * in the file 'enemy'. These names are listed, one per line, with 40: * no trailing blanks. 41: */ 42: 43: #include "phant.h" 44: 45: double strength, speed; 46: bool beyond, marsh, throne, valhala, changed, fghting, su, wmhl; 47: int fileloc, users; 48: jmp_buf fightenv, mainenv; 49: long secs; 50: /* 51: * worm hole map -- This table is carefully set up so that one can always 52: * return the way he/she came by inverting the initial path. 53: */ 54: struct worm_hole w_h[] = 55: { 56: 0,0,0,0, 35,22,2,0, 2,2,0,1, 59,34,64,0, 57: 54,47,0,60, 50,62,0,56, 19,31,25,0, 0,35,41,41, 58: 0,46,40,23, 24,0,29,30, 44,57,56,0, 0,44,39,40, 59: 61,41,0,42, 32,0,17,18, 57,0,63,64, 0,33,26,34, 60: 48,0,54,55, 28,23,22,13, 63,25,13,19, 34,6,18,20, 61: 27,26,19,21, 15,27,20,27, 1,28,34,17, 17,29,8,24, 62: 29,9,23,25, 18,30,24,6, 20,32,27,15, 21,20,21,26, 63: 22,17,46,29, 23,24,28,9, 25,38,9,31, 6,39,30,32, 64: 26,13,31,33, 15,40,32,35, 3,19,15,22, 7,1,33,36, 65: 37,37,35,37, 36,36,36,38, 30,42,37,39, 31,43,38,11, 66: 33,45,11,8, 12,48,7,7, 38,49,12,43, 39,51,42,44, 67: 11,10,43,45, 40,52,44,46, 8,53,45,28, 4,54,51,48, 68: 41,16,47,49, 42,55,48,50, 62,5,49,51, 43,56,50,47, 69: 45,58,53,53, 46,59,52,52, 47,4,55,16, 49,61,16,54, 70: 51,63,5,10, 10,14,59,58, 52,64,57,59, 53,3,58,57, 71: 60,60,4,61, 55,12,60,62, 5,50,61,63, 56,18,62,14, 72: 58,33,14,3 73: }; 74: 75: main(argc,argv) /* Phantasia main routine */ 76: int argc; 77: char *argv[]; 78: { 79: struct stats charac; 80: char aline[200], *login = NULL; 81: double x = 0.0, y = 0.0; 82: int ch, ch2; 83: reg int loop, temp; 84: FILE *fp; 85: bool shrt = FALSE, examine = FALSE, header = FALSE; 86: 87: if ((login = getlogin()) == NULL) 88: login = getpwuid(getuid())->pw_name; 89: #ifdef ENEMY 90: /* check hit list of restricted accounts */ 91: if ((fp = fopen(enemyfile, "r")) != NULL) 92: { 93: char enemy[20]; 94: 95: while (fscanf(fp, "%s", enemy) != EOF) 96: if (!strcmp(login,enemy)) 97: { 98: printf ("The Phantasia privileges for the account \"%s\" have been revoked.\n", login); 99: printf ("Mail comments to %s.\n", WIZARD); 100: exit (0); 101: } 102: fclose (fp); 103: } 104: #endif 105: setbuf(stdin, (char *) NULL); /* this may or may not be necessary */ 106: su = (getuid() == UID); 107: fghting = FALSE; 108: users = 0; 109: if (argc > 1 && (*++argv)[0] == '-') 110: switch ((*argv)[1]) 111: { 112: case 'h': /* help */ 113: printhelp(); 114: exit(0); 115: /*NOTREACHED*/ 116: case 's': /* short */ 117: shrt = TRUE; 118: break; 119: case 'x': /* examine */ 120: examine = TRUE; 121: break; 122: case 'H': /* Header */ 123: header = TRUE; 124: break; 125: case 'm': /* monsters */ 126: printmonster(); 127: exit(0); 128: /*NOTREACHED*/ 129: case 'a': /* all users */ 130: showusers(); 131: exit(0); 132: /*NOTREACHED*/ 133: case 'p': /* purge old players */ 134: purge(); 135: exit(0); 136: /*NOTREACHED*/ 137: } 138: if (!isatty(0)) /* don't let non-tty's play */ 139: exit(0); 140: init1(); /* set up for screen stuff */ 141: if (examine) 142: { 143: cstat(); 144: exit1(); 145: /*NOTREACHED*/ 146: } 147: if (!shrt) 148: { 149: titlestuff(); 150: purge(); /* clean up old characters */ 151: } 152: if (header) 153: { 154: exit1(); 155: /*NOTREACHED*/ 156: } 157: #ifdef OK_TO_PLAY 158: if (!ok_to_play()) 159: { 160: mvaddstr(23,27,"Sorry, you can't play now.\n"); 161: exit1(); 162: /*NOTREACHED*/ 163: } 164: #endif 165: mvaddstr(23,24,"Do you have a character to run? "); 166: ch = rgetch(); 167: if (toupper(ch) == 'Y') 168: fileloc = findchar(&charac); 169: else 170: { 171: initchar(&charac); 172: clear(); 173: mvaddstr(5,21,"Which type of character do you want:"); 174: mvaddstr(10,4,"1:Magic User 2:Fighter 3:Elf 4:Dwarf 5:Halfling 6:Experimento ? "); 175: ch = rgetch(); 176: do 177: { 178: genchar(&charac,ch); 179: mvprintw(15,14,"Strength: %2.0f Manna : %3.0f Quickness : %2d", 180: charac.str,charac.man,charac.quk); 181: mvprintw(16,14,"Brains : %2.0f Magic Level: %2.0f Energy Level: %2.0f", 182: charac.brn,charac.mag,charac.nrg); 183: if (charac.typ != 6) 184: { 185: mvaddstr(17,14,"Type '1' to keep >"); 186: ch2 = rgetch(); 187: } 188: else 189: break; 190: } 191: while (ch2 != '1'); 192: if (charac.typ == 6) 193: { 194: mvaddstr(19,0,"Enter the X Y coordinates of your experimento ? "); 195: getstring(aline,80); 196: sscanf(aline,"%F %F",&x,&y); 197: charac.x = (abs(x) > 1.2e+6) ? sgn(x)*1.2e+6 : floor(x); 198: charac.y = (abs(y) > 1.2e+6) ? sgn(y)*1.2e+6 : floor(y); 199: } 200: do 201: { 202: mvaddstr(20,0,"Give your character a name [up to 20 characters] ? "); 203: getstring(aline,80); 204: strncpy(charac.name,aline,20); 205: charac.name[20] = '\0'; 206: } 207: while (findname(charac.name)); 208: putchar('\n'); 209: fflush(stdout); 210: nocrmode(); 211: do 212: { 213: strcpy(charac.pswd,getpass("Give your character a password [up to 8 characters] ? ")); 214: putchar('\n'); 215: strcpy(aline,getpass("One more time to verify ? ")); 216: } 217: while (strcmp(charac.pswd,aline)); 218: fileloc = findspace(); 219: } 220: crmode(); 221: if (charac.status) 222: { 223: clear(); 224: addstr("Your character did not exit normally last time.\n"); 225: addstr("If you think you have good cause to have you character saved,\n"); 226: printw("you may quit and mail your reason to '%s'.\n",WIZARD); 227: addstr("Do you want to quit ? "); 228: ch = rgetch(); 229: if (toupper(ch) == 'Y') 230: { 231: charac.quk = -100; 232: leave(&charac); 233: /*NOTREACHED*/ 234: } 235: death(&charac); 236: } 237: charac.status = PLAYING; 238: strcpy(charac.login,login); 239: time(&secs); 240: charac.lastused = localtime(&secs)->tm_yday; 241: update(&charac,fileloc); 242: clear(); 243: #ifdef BSD41 244: sigset(SIGINT,interrupt); 245: #endif 246: #ifdef BSD42 247: signal(SIGINT,interrupt,-1); 248: #endif 249: #ifdef USG3 250: signal(SIGINT,interrupt); 251: #endif 252: #ifdef USG5 253: signal(SIGINT,interrupt); 254: #endif 255: 256: /* all set for now */ 257: 258: TOP: switch (setjmp(mainenv)) 259: { 260: case QUIT: 261: #ifdef BSD41 262: sigrelse(SIGINT); 263: #endif 264: #ifdef BSD42 265: signal(SIGINT,interrupt,-1); 266: #endif 267: #ifdef USG3 268: signal(SIGINT,interrupt); 269: #endif 270: #ifdef USG5 271: signal(SIGINT,interrupt); 272: #endif 273: leave(&charac); 274: /*NOTREACHED*/ 275: case DIE: 276: #ifdef BSD41 277: sigrelse(SIGINT); 278: #endif 279: #ifdef BSD42 280: signal(SIGINT,interrupt,-1); 281: #endif 282: #ifdef USG3 283: signal(SIGINT,interrupt); 284: #endif 285: #ifdef USG5 286: signal(SIGINT,interrupt); 287: #endif 288: death(&charac); 289: break; 290: } 291: #ifdef OK_TO_PLAY 292: if (!ok_to_play()) 293: { 294: mvaddstr(6,0,"Whoops! Can't play now.\n"); 295: leave(&charac); 296: /*NOTREACHED*/ 297: } 298: #endif 299: fghting = FALSE; 300: adjuststats(&charac); 301: if (throne && !charac.crn && (charac.typ < 10 || charac.typ > 20)) 302: { 303: mvaddstr(6,0,"You're not allowed in the Lord's Chamber without a crown.\n"); 304: changed = TRUE; 305: charac.x = charac.y = 10; 306: } 307: if (charac.status != CLOAKED && abs(charac.x) == abs(charac.y) 308: && floor(sqrt(fabs(charac.x/100.0))) == sqrt(fabs(charac.x/100.0)) && !throne) 309: { 310: trade(&charac); 311: clear(); 312: } 313: checktampered(&charac); 314: checkinterm(&charac); 315: if (charac.nrg < 0 || (charac.lvl >= 10000 && charac.typ != 99)) 316: death(&charac); 317: neatstuff(&charac); 318: if (changed) 319: { 320: update(&charac,fileloc); 321: changed = FALSE; 322: goto TOP; 323: } 324: move(5,0); 325: clrtoeol(); 326: fp = fopen(messfile,"r"); 327: if (fgets(aline,160,fp)) 328: addstr(aline); 329: fclose(fp); 330: printstats(&charac); 331: move(3,0); 332: clrtoeol(); 333: if (!wmhl) 334: { 335: if (throne) 336: kingstuff(&charac); 337: addstr("1:Move 2:Players 3:Talk 4:Stats 5:Quit "); 338: if (charac.lvl >= 5 && charac.mag >= 15) 339: addstr("6:Cloak "); 340: if (charac.lvl >= 10 && charac.mag >= 25) 341: addstr("7:Teleport "); 342: if (charac.typ > 20) 343: addstr("8:Intervention"); 344: ch = gch(charac.rng.type); 345: clrtoeol(); 346: move(6,0); 347: clrtobot(); 348: if (charac.typ == 99 && (ch == '1' || ch == '7')) 349: ch = ' '; 350: switch (ch2 = toupper(ch)) 351: { 352: case 'N': 353: charac.y += maxmove; 354: break; 355: case 'S': 356: charac.y -= maxmove; 357: break; 358: case 'E': 359: charac.x += maxmove; 360: break; 361: case 'W': 362: charac.x -= maxmove; 363: break; 364: default: /* rest */ 365: if (charac.status == CLOAKED) 366: if (charac.man > 3.0) 367: charac.man -= 3; 368: else 369: { 370: charac.status = PLAYING; 371: changed = TRUE; 372: } 373: else 374: { 375: charac.man += circ(charac.x,charac.y)/3+0.5; 376: charac.man += charac.lvl/5+0.5; 377: } 378: rndattack(); 379: break; 380: case '1': /* move */ 381: for (loop = 3; loop; --loop) 382: { 383: mvaddstr(5,0,"X Y Coordinates ? "); 384: getstring(aline,80); 385: if (sscanf(aline,"%F %F",&x,&y) < 2) 386: ; 387: else 388: if (hypot((double) charac.x - x, (double) charac.y - y) > maxmove) 389: illmove(); 390: else 391: { 392: charac.x = x; 393: charac.y = y; 394: break; 395: } 396: } 397: break; 398: case '2': /* players */ 399: printplayers(&charac); 400: break; 401: case '3': /* message */ 402: talk(charac.name); 403: break; 404: case '4': /* stats */ 405: showall(&charac); 406: break; 407: case '5': /* good-bye */ 408: leave(&charac); 409: /*NOTREACHED*/ 410: case '6': /* cloak */ 411: if (charac.lvl < 5 || charac.mag < 15) 412: illcmd(); 413: else if (charac.status == CLOAKED) 414: charac.status = PLAYING; 415: else if (charac.man < 35) 416: { 417: mvaddstr(6,0,"No power left.\n"); 418: refresh(); 419: } 420: else 421: { 422: changed = TRUE; 423: charac.man -= 35; 424: charac.status = CLOAKED; 425: } 426: break; 427: case '7': /* teleport */ 428: if (charac.lvl < 10 || charac.mag < 25) 429: illcmd(); 430: else 431: for (loop = 3; loop; --loop) 432: { 433: mvaddstr(5,0,"X Y Coordinates ? "); 434: getstring(aline,80); 435: if (sscanf(aline,"%F %F",&x,&y) == 2) 436: if ((temp = hypot(charac.x-x,charac.y-y)) 437: > (charac.lvl+charac.mag)*5+((charac.typ > 20) ? 1e+6 : 0) 438: && !throne) 439: illmove(); 440: else if ((temp = (temp/75+1)*20) > charac.man && !throne) 441: mvaddstr(6,0,"Not enough power for that distance.\n"); 442: else 443: { 444: charac.x = x; 445: charac.y = y; 446: if (!throne) 447: charac.man -= temp; 448: break; 449: } 450: } 451: break; 452: case '9': /* monster */ 453: if (throne) 454: mvaddstr(6,0,"No monsters in the chamber!\n"); 455: else if (charac.typ != 99) 456: { 457: charac.status = PLAYING; 458: changed = TRUE; 459: charac.sin += 1e-6; 460: fight(&charac,-1); 461: } 462: break; 463: case '0': /* decree */ 464: if (su || charac.typ > 10 && charac.typ < 20 && throne) 465: decree(&charac); 466: else 467: illcmd(); 468: break; 469: case '8': /* intervention */ 470: if (su || charac.typ > 20) 471: valarstuff(&charac); 472: else 473: illcmd(); 474: break; 475: case '\014': /* redo screen */ 476: clear(); 477: } 478: if (ch2 == 'E' || ch2 == 'W' || ch2 == 'N' || ch2 == 'S' 479: || ch2 == '1' || ch2 == '7') 480: { 481: checkmov(&charac); 482: rndattack(); 483: changed = TRUE; 484: } 485: } 486: else 487: { 488: addstr("F:Forward B:Back R:Right L:Left Q:Quit T:Talk P:Players S:Stats "); 489: ch = rgetch(); 490: move(6,0); 491: clrtobot(); 492: switch (toupper(ch)) 493: { 494: default: 495: if (charac.status == CLOAKED) 496: if (charac.man > 3.0) 497: charac.man -= 3; 498: else 499: { 500: charac.status = PLAYING; 501: changed = TRUE; 502: } 503: else 504: charac.man += charac.lvl/5+0.5; 505: break; 506: case 'F': 507: temp = (int) w_h[charac.wormhole].f; 508: goto CHKMOVE; 509: case 'B': 510: temp = (int) w_h[charac.wormhole].b; 511: goto CHKMOVE; 512: case 'R': 513: temp = (int) w_h[charac.wormhole].r; 514: goto CHKMOVE; 515: case 'L': 516: temp = (int) w_h[charac.wormhole].l; 517: goto CHKMOVE; 518: case 'Q': 519: leave(&charac); 520: /*NOTREACHED*/ 521: case 'T': 522: talk(charac.name); 523: break; 524: case 'P': 525: printplayers(&charac); 526: break; 527: case 'S': 528: showall(&charac); 529: break; 530: case '\014': /* redo screen */ 531: clear(); 532: } 533: goto TOP; 534: CHKMOVE: if (!temp) 535: { 536: charac.y = 0.0; 537: charac.x = pow(-1.0,(double) charac.wormhole) * charac.wormhole * 400 - 1.0; 538: charac.wormhole = 0; 539: changed = TRUE; 540: } 541: else 542: charac.wormhole = temp; 543: } 544: goto TOP; 545: } 546: 547: /* 548: * This function is provided to allow one to restrict access to the game. 549: * Tailor this routine as appropriate. 550: */ 551: 552: #ifdef OK_TO_PLAY 553: #include <sys/types.h> 554: #include <utmp.h> /* used for counting users on system */ 555: 556: bool ok_to_play() /* return FALSE if playing is not allowed at this time */ 557: { 558: #define MAXUSERS 8 /* max. number of people on system */ 559: reg struct tm *tp; 560: reg int numusers = 0; 561: FILE *fp; 562: long now; 563: struct utmp ubuf; 564: 565: if (su) 566: return (TRUE); 567: /* check time of day */ 568: time(&now); 569: if (((tp = localtime(&now))->tm_hour > 8 && tp->tm_hour < 12) /* 8-noon */ 570: || (tp->tm_hour > 13 && tp->tm_hour < 16)) /* 1-4 pm */ 571: return (FALSE); 572: /* check # of users */ 573: fp = fopen(_PATH_UTMP,"r"); 574: while (fread((char *) &ubuf,sizeof(ubuf),1,fp)) 575: #ifdef USG5 576: if (ubuf.ut_type == USER_PROCESS) 577: #else 578: if (*ubuf.ut_name) 579: #endif 580: ++numusers; 581: fclose(fp); 582: if (numusers > MAXUSERS) 583: return (FALSE); 584: return (TRUE); 585: } 586: #endif