|
|
1.1 ! root 1: /* ! 2: * Phantasia 3.3.2 -- Interterminal fantasy game ! 3: * ! 4: * Edward A. Estes ! 5: * AT&T, March 12, 1986 ! 6: */ ! 7: ! 8: /* DISCLAIMER: ! 9: * ! 10: * This game is distributed for free as is. It is not guaranteed to work ! 11: * in every conceivable environment. It is not even guaranteed to work ! 12: * in ANY environment. ! 13: * ! 14: * This game is distributed without notice of copyright, therefore it ! 15: * may be used in any manner the recipient sees fit. However, the ! 16: * author assumes no responsibility for maintaining or revising this ! 17: * game, in its original form, or any derivitives thereof. ! 18: * ! 19: * The author shall not be responsible for any loss, cost, or damage, ! 20: * including consequential damage, caused by reliance on this material. ! 21: * ! 22: * The author makes no warranties, express or implied, including warranties ! 23: * of merchantability or fitness for a particular purpose or use. ! 24: * ! 25: * AT&T is in no way connected with this game. ! 26: */ ! 27: ! 28: #include <sys/types.h> ! 29: #include <pwd.h> ! 30: ! 31: /* ! 32: * The program allocates as much file space as it needs to store characters, ! 33: * so the possibility exists for the character file to grow without bound. ! 34: * The file is purged upon normal entry to try to avoid that problem. ! 35: * A similar problem exists for energy voids. To alleviate the problem here, ! 36: * the void file is cleared with every new king, and a limit is placed ! 37: * on the size of the energy void file. ! 38: */ ! 39: ! 40: /* ! 41: * Put one line of text into the file 'motd' for announcements, etc. ! 42: */ ! 43: ! 44: /* ! 45: * The scoreboard file is updated when someone dies, and keeps track ! 46: * of the highest character to date for that login. ! 47: * Being purged from the character file does not cause the scoreboard ! 48: * to be updated. ! 49: */ ! 50: ! 51: /* ! 52: * All source files are set up for 'vi' with shiftwidth=4, tabstop=8. ! 53: */ ! 54: ! 55: /**/ ! 56: ! 57: /* ! 58: * main.c Main routines for Phantasia ! 59: */ ! 60: ! 61: #include "include.h" ! 62: ! 63: /*************************************************************************** ! 64: / FUNCTION NAME: main() ! 65: / ! 66: / FUNCTION: initialize state, and call main process ! 67: / ! 68: / AUTHOR: E. A. Estes, 12/4/85 ! 69: / ! 70: / ARGUMENTS: ! 71: / int argc - argument count ! 72: / char **argv - argument vector ! 73: / ! 74: / RETURN VALUE: none ! 75: / ! 76: / MODULES CALLED: monstlist(), checkenemy(), activelist(), ! 77: / throneroom(), checkbattle(), readmessage(), changestats(), writerecord(), ! 78: / tradingpost(), adjuststats(), recallplayer(), displaystats(), checktampered(), ! 79: / fabs(), rollnewplayer(), time(), exit(), sqrt(), floor(), wmove(), ! 80: / signal(), strcat(), purgeoldplayers(), getuid(), isatty(), wclear(), ! 81: / strcpy(), system(), altercoordinates(), cleanup(), waddstr(), procmain(), ! 82: / playinit(), leavegame(), localtime(), getanswer(), neatstuff(), initialstate(), ! 83: / scorelist(), titlelist() ! 84: / ! 85: / GLOBAL INPUTS: *Login, Throne, Wizard, Player, *stdscr, Changed, Databuf[], ! 86: / Fileloc, Stattable[] ! 87: / ! 88: / GLOBAL OUTPUTS: Wizard, Player, Changed, Fileloc, Timeout, *Statptr ! 89: / ! 90: / DESCRIPTION: ! 91: / Process arguments, initialize program, and loop forever processing ! 92: / player input. ! 93: / ! 94: /***************************************************************************/ ! 95: ! 96: main(argc, argv) ! 97: int argc; ! 98: char **argv; ! 99: { ! 100: bool noheader = FALSE; /* set if don't want header */ ! 101: bool headeronly = FALSE; /* set if only want header */ ! 102: bool examine = FALSE; /* set if examine a character */ ! 103: long seconds; /* for time of day */ ! 104: double dtemp; /* for temporary calculations */ ! 105: ! 106: initialstate(); /* init globals */ ! 107: ! 108: /* process arguments */ ! 109: while (--argc && (*++argv)[0] == '-') ! 110: switch ((*argv)[1]) ! 111: { ! 112: case 's': /* short */ ! 113: noheader = TRUE; ! 114: break; ! 115: ! 116: case 'H': /* Header */ ! 117: headeronly = TRUE; ! 118: break; ! 119: ! 120: case 'a': /* all users */ ! 121: activelist(); ! 122: cleanup(TRUE); ! 123: /*NOTREACHED*/ ! 124: ! 125: case 'p': /* purge old players */ ! 126: purgeoldplayers(); ! 127: cleanup(TRUE); ! 128: /*NOTREACHED*/ ! 129: ! 130: case 'S': /* set 'Wizard' */ ! 131: Wizard = !getuid(); ! 132: break; ! 133: ! 134: case 'x': /* examine */ ! 135: examine = TRUE; ! 136: break; ! 137: ! 138: case 'm': /* monsters */ ! 139: monstlist(); ! 140: cleanup(TRUE); ! 141: /*NOTREACHED*/ ! 142: ! 143: case 'b': /* scoreboard */ ! 144: scorelist(); ! 145: cleanup(TRUE); ! 146: /*NOTREACHED*/ ! 147: } ! 148: ! 149: if (!isatty(0)) /* don't let non-tty's play */ ! 150: cleanup(TRUE); ! 151: /*NOTREACHED*/ ! 152: ! 153: playinit(); /* set up to catch signals, init curses */ ! 154: ! 155: if (examine) ! 156: { ! 157: changestats(FALSE); ! 158: cleanup(TRUE); ! 159: /*NOTREACHED*/ ! 160: } ! 161: ! 162: if (!noheader) ! 163: { ! 164: titlelist(); ! 165: purgeoldplayers(); /* clean up old characters */ ! 166: } ! 167: ! 168: if (headeronly) ! 169: cleanup(TRUE); ! 170: /*NOTREACHED*/ ! 171: ! 172: do ! 173: /* get the player structure filled */ ! 174: { ! 175: Fileloc = -1L; ! 176: ! 177: mvaddstr(22, 17, "Do you have a character to run [Q = Quit] ? "); ! 178: ! 179: switch (getanswer("NYQ", FALSE)) ! 180: { ! 181: case 'Y': ! 182: Fileloc = recallplayer(); ! 183: break; ! 184: ! 185: case 'Q': ! 186: cleanup(TRUE); ! 187: /*NOTREACHED*/ ! 188: ! 189: default: ! 190: Fileloc = rollnewplayer(); ! 191: break; ! 192: } ! 193: clear(); ! 194: } ! 195: while (Fileloc < 0L); ! 196: ! 197: if (Player.p_level > 5.0) ! 198: /* low level players have long timeout */ ! 199: Timeout = TRUE; ! 200: ! 201: /* update some important player statistics */ ! 202: strcpy(Player.p_login, Login); ! 203: time(&seconds); ! 204: Player.p_lastused = localtime(&seconds)->tm_yday; ! 205: Player.p_status = S_PLAYING; ! 206: writerecord(&Player, Fileloc); ! 207: ! 208: Statptr = &Stattable[Player.p_type]; /* initialize pointer */ ! 209: ! 210: /* catch interrupts */ ! 211: #ifdef BSD41 ! 212: sigset(SIGINT, interrupt); ! 213: #endif ! 214: #ifdef BSD42 ! 215: signal(SIGINT, interrupt); ! 216: #endif ! 217: #ifdef SYS3 ! 218: signal(SIGINT, interrupt); ! 219: #endif ! 220: #ifdef SYS5 ! 221: signal(SIGINT, interrupt); ! 222: #endif ! 223: ! 224: altercoordinates(Player.p_x, Player.p_y, A_FORCED); /* set some flags */ ! 225: ! 226: clear(); ! 227: ! 228: for (;;) ! 229: /* loop forever, processing input */ ! 230: { ! 231: ! 232: adjuststats(); /* cleanup stats */ ! 233: ! 234: if (Throne && Player.p_crowns == 0 && Player.p_specialtype != SC_KING) ! 235: /* not allowed on throne -- move */ ! 236: { ! 237: mvaddstr(5,0,"You're not allowed in the Lord's Chamber without a crown.\n"); ! 238: altercoordinates(0.0, 0.0, A_NEAR); ! 239: } ! 240: ! 241: checktampered(); /* check for energy voids, etc. */ ! 242: ! 243: if (Player.p_status != S_CLOAKED ! 244: /* not cloaked */ ! 245: && (dtemp = fabs(Player.p_x)) == fabs(Player.p_y) ! 246: /* |x| = |y| */ ! 247: && !Throne) ! 248: /* not on throne */ ! 249: { ! 250: dtemp = sqrt(dtemp / 100.0); ! 251: if (floor(dtemp) == dtemp) ! 252: /* |x| / 100 == n*n; at a trading post */ ! 253: { ! 254: tradingpost(); ! 255: clear(); ! 256: } ! 257: } ! 258: ! 259: checkbattle(); /* check for player to player battle */ ! 260: neatstuff(); /* gurus, medics, etc. */ ! 261: ! 262: if (Player.p_status == S_CLOAKED) ! 263: /* costs 3 mana per turn to be cloaked */ ! 264: if (Player.p_mana > 3.0) ! 265: Player.p_mana -= 3.0; ! 266: else ! 267: /* ran out of mana, uncloak */ ! 268: { ! 269: Player.p_status = S_PLAYING; ! 270: Changed = TRUE; ! 271: } ! 272: ! 273: if (Player.p_status != S_PLAYING && Player.p_status != S_CLOAKED) ! 274: /* change status back to S_PLAYING */ ! 275: { ! 276: Player.p_status = S_PLAYING; ! 277: Changed = TRUE; ! 278: } ! 279: ! 280: if (Changed) ! 281: /* update file only if important stuff has changed */ ! 282: { ! 283: writerecord(&Player, Fileloc); ! 284: Changed = FALSE; ! 285: continue; ! 286: } ! 287: ! 288: readmessage(); /* read message, if any */ ! 289: ! 290: displaystats(); /* print statistics */ ! 291: ! 292: move(6, 0); ! 293: ! 294: if (Throne) ! 295: /* maybe make king, print prompt, etc. */ ! 296: throneroom(); ! 297: ! 298: /* print status line */ ! 299: addstr("1:Move 2:Players 3:Talk 4:Stats 5:Quit "); ! 300: if (Player.p_level >= MEL_CLOAK && Player.p_magiclvl >= ML_CLOAK) ! 301: addstr("6:Cloak "); ! 302: if (Player.p_level >= MEL_TELEPORT && Player.p_magiclvl >= ML_TELEPORT) ! 303: addstr("7:Teleport "); ! 304: if (Player.p_specialtype >= SC_COUNCIL || Wizard) ! 305: addstr("8:Intervene "); ! 306: ! 307: procmain(); /* process input */ ! 308: } ! 309: } ! 310: /**/ ! 311: /************************************************************************ ! 312: / ! 313: / FUNCTION NAME: initialstate() ! 314: / ! 315: / FUNCTION: initialize some important global variable ! 316: / ! 317: / AUTHOR: E. A. Estes, 12/4/85 ! 318: / ! 319: / ARGUMENTS: none ! 320: / ! 321: / RETURN VALUE: none ! 322: / ! 323: / MODULES CALLED: time(), fopen(), srandom(), error(), getuid(), getlogin(), ! 324: / getpwuid() ! 325: / ! 326: / GLOBAL INPUTS: ! 327: / ! 328: / GLOBAL OUTPUTS: *Energyvoidfp, Echo, Marsh, *Login, Users, Beyond, ! 329: / Throne, Wizard, Changed, Okcount, Timeout, Windows, *Monstfp, *Messagefp, ! 330: / *Playersfp ! 331: / ! 332: / DESCRIPTION: ! 333: / Set global flags, and open files which remain open. ! 334: / ! 335: /************************************************************************/ ! 336: ! 337: initialstate() ! 338: { ! 339: Beyond = FALSE; ! 340: Marsh = FALSE; ! 341: Throne = FALSE; ! 342: Changed = FALSE; ! 343: Wizard = FALSE; ! 344: Timeout = FALSE; ! 345: Users = 0; ! 346: Windows = FALSE; ! 347: Echo = TRUE; ! 348: ! 349: /* setup login name */ ! 350: if ((Login = getlogin()) == NULL) ! 351: Login = getpwuid(getuid())->pw_name; ! 352: ! 353: /* open some files */ ! 354: if ((Playersfp = fopen(_PATH_PEOPLE, "r+")) == NULL) ! 355: error(_PATH_PEOPLE); ! 356: /*NOTREACHED*/ ! 357: ! 358: if ((Monstfp = fopen(_PATH_MONST, "r+")) == NULL) ! 359: error(_PATH_MONST); ! 360: /*NOTREACHED*/ ! 361: ! 362: if ((Messagefp = fopen(_PATH_MESS, "r")) == NULL) ! 363: error(_PATH_MESS); ! 364: /*NOTREACHED*/ ! 365: ! 366: if ((Energyvoidfp = fopen(_PATH_VOID, "r+")) == NULL) ! 367: error(_PATH_VOID); ! 368: /*NOTREACHED*/ ! 369: ! 370: srandom((unsigned) time((long *) NULL)); /* prime random numbers */ ! 371: } ! 372: /**/ ! 373: /************************************************************************ ! 374: / ! 375: / FUNCTION NAME: rollnewplayer() ! 376: / ! 377: / FUNCTION: roll up a new character ! 378: / ! 379: / AUTHOR: E. A. Estes, 12/4/85 ! 380: / ! 381: / ARGUMENTS: none ! 382: / ! 383: / RETURN VALUE: none ! 384: / ! 385: / MODULES CALLED: initplayer(), allocrecord(), truncstring(), fabs(), wmove(), ! 386: / wclear(), sscanf(), strcmp(), genchar(), waddstr(), findname(), mvprintw(), ! 387: / getanswer(), getstring() ! 388: / ! 389: / GLOBAL INPUTS: Other, Wizard, Player, *stdscr, Databuf[] ! 390: / ! 391: / GLOBAL OUTPUTS: Echo ! 392: / ! 393: / DESCRIPTION: ! 394: / Prompt player, and roll up new character. ! 395: / ! 396: /************************************************************************/ ! 397: ! 398: long ! 399: rollnewplayer() ! 400: { ! 401: int chartype; /* character type */ ! 402: int ch; /* input */ ! 403: ! 404: initplayer(&Player); /* initialize player structure */ ! 405: ! 406: clear(); ! 407: mvaddstr(4, 21, "Which type of character do you want:"); ! 408: mvaddstr(8, 4, "1:Magic User 2:Fighter 3:Elf 4:Dwarf 5:Halfling 6:Experimento "); ! 409: if (Wizard) { ! 410: addstr("7:Super ? "); ! 411: chartype = getanswer("1234567", FALSE); ! 412: } ! 413: else { ! 414: addstr("? "); ! 415: chartype = getanswer("123456", FALSE); ! 416: } ! 417: ! 418: do ! 419: { ! 420: genchar(chartype); /* roll up a character */ ! 421: ! 422: /* print out results */ ! 423: mvprintw(12, 14, ! 424: "Strength : %2.0f Quickness: %2.0f Mana : %2.0f\n", ! 425: Player.p_strength, Player.p_quickness, Player.p_mana); ! 426: mvprintw(13, 14, ! 427: "Energy Level: %2.0f Brains : %2.0f Magic Level: %2.0f\n", ! 428: Player.p_energy, Player.p_brains, Player.p_magiclvl); ! 429: ! 430: if (Player.p_type == C_EXPER || Player.p_type == C_SUPER) ! 431: break; ! 432: ! 433: mvaddstr(14, 14, "Type '1' to keep >"); ! 434: ch = getanswer(" ", TRUE); ! 435: } ! 436: while (ch != '1'); ! 437: ! 438: if (Player.p_type == C_EXPER || Player.p_type == C_SUPER) ! 439: /* get coordinates for experimento */ ! 440: for (;;) ! 441: { ! 442: mvaddstr(16, 0, "Enter the X Y coordinates of your experimento ? "); ! 443: getstring(Databuf, SZ_DATABUF); ! 444: sscanf(Databuf, "%lf %lf", &Player.p_x, &Player.p_y); ! 445: ! 446: if (fabs(Player.p_x) > D_EXPER || fabs(Player.p_y) > D_EXPER) ! 447: mvaddstr(17, 0, "Invalid coordinates. Try again.\n"); ! 448: else ! 449: break; ! 450: } ! 451: ! 452: for (;;) ! 453: /* name the new character */ ! 454: { ! 455: mvprintw(18, 0, ! 456: "Give your character a name [up to %d characters] ? ", SZ_NAME - 1); ! 457: getstring(Player.p_name, SZ_NAME); ! 458: truncstring(Player.p_name); /* remove trailing blanks */ ! 459: ! 460: if (Player.p_name[0] == '\0') ! 461: /* no null names */ ! 462: mvaddstr(19, 0, "Invalid name."); ! 463: else if (findname(Player.p_name, &Other) >= 0L) ! 464: /* cannot have duplicate names */ ! 465: mvaddstr(19, 0, "Name already in use."); ! 466: else ! 467: /* name is acceptable */ ! 468: break; ! 469: ! 470: addstr(" Pick another.\n"); ! 471: } ! 472: ! 473: /* get a password for character */ ! 474: Echo = FALSE; ! 475: ! 476: do ! 477: { ! 478: mvaddstr(20, 0, "Give your character a password [up to 8 characters] ? "); ! 479: getstring(Player.p_password, SZ_PASSWORD); ! 480: mvaddstr(21, 0, "One more time to verify ? "); ! 481: getstring(Databuf, SZ_PASSWORD); ! 482: } ! 483: while (strcmp(Player.p_password, Databuf) != 0); ! 484: ! 485: Echo = TRUE; ! 486: ! 487: return(allocrecord()); ! 488: } ! 489: /**/ ! 490: /************************************************************************ ! 491: / ! 492: / FUNCTION NAME: procmain() ! 493: / ! 494: / FUNCTION: process input from player ! 495: / ! 496: / AUTHOR: E. A. Estes, 12/4/85 ! 497: / ! 498: / ARGUMENTS: none ! 499: / ! 500: / RETURN VALUE: none ! 501: / ! 502: / MODULES CALLED: dotampered(), changestats(), inputoption(), allstatslist(), ! 503: / fopen(), wmove(), drandom(), sscanf(), fclose(), altercoordinates(), ! 504: / waddstr(), fprintf(), distance(), userlist(), leavegame(), encounter(), ! 505: / getstring(), wclrtobot() ! 506: / ! 507: / GLOBAL INPUTS: Circle, Illcmd[], Throne, Wizard, Player, *stdscr, ! 508: / Databuf[], Illmove[] ! 509: / ! 510: / GLOBAL OUTPUTS: Player, Changed ! 511: / ! 512: / DESCRIPTION: ! 513: / Process main menu options. ! 514: / ! 515: /************************************************************************/ ! 516: ! 517: procmain() ! 518: { ! 519: int ch; /* input */ ! 520: double x; /* desired new x coordinate */ ! 521: double y; /* desired new y coordinate */ ! 522: double temp; /* for temporary calculations */ ! 523: FILE *fp; /* for opening files */ ! 524: register int loop; /* a loop counter */ ! 525: bool hasmoved = FALSE; /* set if player has moved */ ! 526: ! 527: ch = inputoption(); ! 528: mvaddstr(4, 0, "\n\n"); /* clear status area */ ! 529: ! 530: move(7, 0); ! 531: clrtobot(); /* clear data on bottom area of screen */ ! 532: ! 533: if (Player.p_specialtype == SC_VALAR && (ch == '1' || ch == '7')) ! 534: /* valar cannot move */ ! 535: ch = ' '; ! 536: ! 537: switch (ch) ! 538: { ! 539: case 'K': /* move up/north */ ! 540: case 'N': ! 541: x = Player.p_x; ! 542: y = Player.p_y + MAXMOVE(); ! 543: hasmoved = TRUE; ! 544: break; ! 545: ! 546: case 'J': /* move down/south */ ! 547: case 'S': ! 548: x = Player.p_x; ! 549: y = Player.p_y - MAXMOVE(); ! 550: hasmoved = TRUE; ! 551: break; ! 552: ! 553: case 'L': /* move right/east */ ! 554: case 'E': ! 555: x = Player.p_x + MAXMOVE(); ! 556: y = Player.p_y; ! 557: hasmoved = TRUE; ! 558: break; ! 559: ! 560: case 'H': /* move left/west */ ! 561: case 'W': ! 562: x = Player.p_x - MAXMOVE(); ! 563: y = Player.p_y; ! 564: hasmoved = TRUE; ! 565: break; ! 566: ! 567: default: /* rest */ ! 568: Player.p_energy += (Player.p_maxenergy + Player.p_shield) / 15.0 ! 569: + Player.p_level / 3.0 + 2.0; ! 570: Player.p_energy = ! 571: MIN(Player.p_energy, Player.p_maxenergy + Player.p_shield); ! 572: ! 573: if (Player.p_status != S_CLOAKED) ! 574: /* cannot find mana if cloaked */ ! 575: { ! 576: Player.p_mana += (Circle + Player.p_level) / 4.0; ! 577: ! 578: if (drandom() < 0.2 && Player.p_status == S_PLAYING && !Throne) ! 579: /* wandering monster */ ! 580: encounter(-1); ! 581: } ! 582: break; ! 583: ! 584: case 'X': /* change/examine a character */ ! 585: changestats(TRUE); ! 586: break; ! 587: ! 588: case '1': /* move */ ! 589: for (loop = 3; loop; --loop) ! 590: { ! 591: mvaddstr(4, 0, "X Y Coordinates ? "); ! 592: getstring(Databuf, SZ_DATABUF); ! 593: ! 594: if (sscanf(Databuf, "%lf %lf", &x, &y) != 2) ! 595: mvaddstr(5, 0, "Try again\n"); ! 596: else if (distance(Player.p_x, x, Player.p_y, y) > MAXMOVE()) ! 597: ILLMOVE(); ! 598: else ! 599: { ! 600: hasmoved = TRUE; ! 601: break; ! 602: } ! 603: } ! 604: break; ! 605: ! 606: case '2': /* players */ ! 607: userlist(TRUE); ! 608: break; ! 609: ! 610: case '3': /* message */ ! 611: mvaddstr(4, 0, "Message ? "); ! 612: getstring(Databuf, SZ_DATABUF); ! 613: /* we open the file for writing to erase any data which is already there */ ! 614: fp = fopen(_PATH_MESS, "w"); ! 615: if (Databuf[0] != '\0') ! 616: fprintf(fp, "%s: %s", Player.p_name, Databuf); ! 617: fclose(fp); ! 618: break; ! 619: ! 620: case '4': /* stats */ ! 621: allstatslist(); ! 622: break; ! 623: ! 624: case '5': /* good-bye */ ! 625: leavegame(); ! 626: /*NOTREACHED*/ ! 627: ! 628: case '6': /* cloak */ ! 629: if (Player.p_level < MEL_CLOAK || Player.p_magiclvl < ML_CLOAK) ! 630: ILLCMD(); ! 631: else if (Player.p_status == S_CLOAKED) ! 632: Player.p_status = S_PLAYING; ! 633: else if (Player.p_mana < MM_CLOAK) ! 634: mvaddstr(5, 0, "No mana left.\n"); ! 635: else ! 636: { ! 637: Changed = TRUE; ! 638: Player.p_mana -= MM_CLOAK; ! 639: Player.p_status = S_CLOAKED; ! 640: } ! 641: break; ! 642: ! 643: case '7': /* teleport */ ! 644: /* ! 645: * conditions for teleport ! 646: * - 20 per (level plus magic level) ! 647: * - OR council of the wise or valar or ex-valar ! 648: * - OR transport from throne ! 649: * transports from throne cost no mana ! 650: */ ! 651: if (Player.p_level < MEL_TELEPORT || Player.p_magiclvl < ML_TELEPORT) ! 652: ILLCMD(); ! 653: else ! 654: for (loop = 3; loop; --loop) ! 655: { ! 656: mvaddstr(4, 0, "X Y Coordinates ? "); ! 657: getstring(Databuf, SZ_DATABUF); ! 658: ! 659: if (sscanf(Databuf, "%lf %lf", &x, &y) == 2) ! 660: { ! 661: temp = distance(Player.p_x, x, Player.p_y, y); ! 662: if (!Throne ! 663: /* can transport anywhere from throne */ ! 664: && Player.p_specialtype <= SC_COUNCIL ! 665: /* council, valar can transport anywhere */ ! 666: && temp > (Player.p_level + Player.p_magiclvl) * 20.0) ! 667: /* can only move 20 per exp. level + mag. level */ ! 668: ILLMOVE(); ! 669: else ! 670: { ! 671: temp = (temp / 75.0 + 1.0) * 20.0; /* mana used */ ! 672: ! 673: if (!Throne && temp > Player.p_mana) ! 674: mvaddstr(5, 0, "Not enough power for that distance.\n"); ! 675: else ! 676: { ! 677: if (!Throne) ! 678: Player.p_mana -= temp; ! 679: hasmoved = TRUE; ! 680: break; ! 681: } ! 682: } ! 683: } ! 684: } ! 685: break; ! 686: ! 687: case 'C': ! 688: case '9': /* monster */ ! 689: if (Throne) ! 690: /* no monsters while on throne */ ! 691: mvaddstr(5, 0, "No monsters in the chamber!\n"); ! 692: else if (Player.p_specialtype != SC_VALAR) ! 693: /* the valar cannot call monsters */ ! 694: { ! 695: Player.p_sin += 1e-6; ! 696: encounter(-1); ! 697: } ! 698: break; ! 699: ! 700: case '0': /* decree */ ! 701: if (Wizard || Player.p_specialtype == SC_KING && Throne) ! 702: /* kings must be on throne to decree */ ! 703: dotampered(); ! 704: else ! 705: ILLCMD(); ! 706: break; ! 707: ! 708: case '8': /* intervention */ ! 709: if (Wizard || Player.p_specialtype >= SC_COUNCIL) ! 710: dotampered(); ! 711: else ! 712: ILLCMD(); ! 713: break; ! 714: } ! 715: ! 716: if (hasmoved) ! 717: /* player has moved -- alter coordinates, and do random monster */ ! 718: { ! 719: altercoordinates(x, y, A_SPECIFIC); ! 720: ! 721: if (drandom() < 0.2 && Player.p_status == S_PLAYING && !Throne) ! 722: encounter(-1); ! 723: } ! 724: } ! 725: /**/ ! 726: /************************************************************************ ! 727: / ! 728: / FUNCTION NAME: titlelist() ! 729: / ! 730: / FUNCTION: print title page ! 731: / ! 732: / AUTHOR: E. A. Estes, 12/4/85 ! 733: / ! 734: / ARGUMENTS: none ! 735: / ! 736: / RETURN VALUE: none ! 737: / ! 738: / MODULES CALLED: fread(), fseek(), fopen(), fgets(), wmove(), strcpy(), ! 739: / fclose(), strlen(), waddstr(), sprintf(), wrefresh() ! 740: / ! 741: / GLOBAL INPUTS: Lines, Other, *stdscr, Databuf[], *Playersfp ! 742: / ! 743: / GLOBAL OUTPUTS: Lines ! 744: / ! 745: / DESCRIPTION: ! 746: / Print important information about game, players, etc. ! 747: / ! 748: /************************************************************************/ ! 749: ! 750: titlelist() ! 751: { ! 752: register FILE *fp; /* used for opening various files */ ! 753: bool councilfound = FALSE; /* set if we find a member of the council */ ! 754: bool kingfound = FALSE; /* set if we find a king */ ! 755: double hiexp, nxtexp; /* used for finding the two highest players */ ! 756: double hilvl, nxtlvl; /* used for finding the two highest players */ ! 757: char hiname[21], nxtname[21];/* used for finding the two highest players */ ! 758: ! 759: mvaddstr(0, 14, "W e l c o m e t o P h a n t a s i a (vers. 3.3.2)!"); ! 760: ! 761: /* print message of the day */ ! 762: if ((fp = fopen(_PATH_MOTD, "r")) != NULL ! 763: && fgets(Databuf, SZ_DATABUF, fp) != NULL) ! 764: { ! 765: mvaddstr(2, 40 - strlen(Databuf) / 2, Databuf); ! 766: fclose(fp); ! 767: } ! 768: ! 769: /* search for king */ ! 770: fseek(Playersfp, 0L, 0); ! 771: while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) ! 772: if (Other.p_specialtype == SC_KING && Other.p_status != S_NOTUSED) ! 773: /* found the king */ ! 774: { ! 775: sprintf(Databuf, "The present ruler is %s Level:%.0f", ! 776: Other.p_name, Other.p_level); ! 777: mvaddstr(4, 40 - strlen(Databuf) / 2, Databuf); ! 778: kingfound = TRUE; ! 779: break; ! 780: } ! 781: ! 782: if (!kingfound) ! 783: mvaddstr(4, 24, "There is no ruler at this time."); ! 784: ! 785: /* search for valar */ ! 786: fseek(Playersfp, 0L, 0); ! 787: while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) ! 788: if (Other.p_specialtype == SC_VALAR && Other.p_status != S_NOTUSED) ! 789: /* found the valar */ ! 790: { ! 791: sprintf(Databuf, "The Valar is %s Login: %s", Other.p_name, Other.p_login); ! 792: mvaddstr(6, 40 - strlen(Databuf) / 2 , Databuf); ! 793: break; ! 794: } ! 795: ! 796: /* search for council of the wise */ ! 797: fseek(Playersfp, 0L, 0); ! 798: Lines = 10; ! 799: while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) ! 800: if (Other.p_specialtype == SC_COUNCIL && Other.p_status != S_NOTUSED) ! 801: /* found a member of the council */ ! 802: { ! 803: if (!councilfound) ! 804: { ! 805: mvaddstr(8, 30, "Council of the Wise:"); ! 806: councilfound = TRUE; ! 807: } ! 808: ! 809: /* This assumes a finite (<=5) number of C.O.W.: */ ! 810: sprintf(Databuf, "%s Login: %s", Other.p_name, Other.p_login); ! 811: mvaddstr(Lines++, 40 - strlen(Databuf) / 2, Databuf); ! 812: } ! 813: ! 814: /* search for the two highest players */ ! 815: nxtname[0] = hiname[0] = '\0'; ! 816: hiexp = 0.0; ! 817: nxtlvl = hilvl = 0; ! 818: ! 819: fseek(Playersfp, 0L, 0); ! 820: while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) ! 821: if (Other.p_experience > hiexp && Other.p_specialtype <= SC_KING && Other.p_status != S_NOTUSED) ! 822: /* highest found so far */ ! 823: { ! 824: nxtexp = hiexp; ! 825: hiexp = Other.p_experience; ! 826: nxtlvl = hilvl; ! 827: hilvl = Other.p_level; ! 828: strcpy(nxtname, hiname); ! 829: strcpy(hiname, Other.p_name); ! 830: } ! 831: else if (Other.p_experience > nxtexp ! 832: && Other.p_specialtype <= SC_KING ! 833: && Other.p_status != S_NOTUSED) ! 834: /* next highest found so far */ ! 835: { ! 836: nxtexp = Other.p_experience; ! 837: nxtlvl = Other.p_level; ! 838: strcpy(nxtname, Other.p_name); ! 839: } ! 840: ! 841: mvaddstr(15, 28, "Highest characters are:"); ! 842: sprintf(Databuf, "%s Level:%.0f and %s Level:%.0f", ! 843: hiname, hilvl, nxtname, nxtlvl); ! 844: mvaddstr(17, 40 - strlen(Databuf) / 2, Databuf); ! 845: ! 846: /* print last to die */ ! 847: if ((fp = fopen(_PATH_LASTDEAD,"r")) != NULL ! 848: && fgets(Databuf, SZ_DATABUF, fp) != NULL) ! 849: { ! 850: mvaddstr(19, 25, "The last character to die was:"); ! 851: mvaddstr(20, 40 - strlen(Databuf) / 2,Databuf); ! 852: fclose(fp); ! 853: } ! 854: ! 855: refresh(); ! 856: } ! 857: /**/ ! 858: /************************************************************************ ! 859: / ! 860: / FUNCTION NAME: recallplayer() ! 861: / ! 862: / FUNCTION: find a character on file ! 863: / ! 864: / AUTHOR: E. A. Estes, 12/4/85 ! 865: / ! 866: / ARGUMENTS: none ! 867: / ! 868: / RETURN VALUE: none ! 869: / ! 870: / MODULES CALLED: writerecord(), truncstring(), more(), death(), wmove(), ! 871: / wclear(), strcmp(), printw(), cleanup(), waddstr(), findname(), mvprintw(), ! 872: / getanswer(), getstring() ! 873: / ! 874: / GLOBAL INPUTS: Player, *stdscr, Databuf[] ! 875: / ! 876: / GLOBAL OUTPUTS: Echo, Player ! 877: / ! 878: / DESCRIPTION: ! 879: / Search for a character of a certain name, and check password. ! 880: / ! 881: /************************************************************************/ ! 882: ! 883: long ! 884: recallplayer() ! 885: { ! 886: long loc = 0L; /* location in player file */ ! 887: register int loop; /* loop counter */ ! 888: int ch; /* input */ ! 889: ! 890: clear(); ! 891: mvprintw(10, 0, "What was your character's name ? "); ! 892: getstring(Databuf, SZ_NAME); ! 893: truncstring(Databuf); ! 894: ! 895: if ((loc = findname(Databuf, &Player)) >= 0L) ! 896: /* found character */ ! 897: { ! 898: Echo = FALSE; ! 899: ! 900: for (loop = 0; loop < 2; ++loop) ! 901: { ! 902: /* prompt for password */ ! 903: mvaddstr(11, 0, "Password ? "); ! 904: getstring(Databuf, SZ_PASSWORD); ! 905: if (strcmp(Databuf, Player.p_password) == 0) ! 906: /* password good */ ! 907: { ! 908: Echo = TRUE; ! 909: ! 910: if (Player.p_status != S_OFF) ! 911: /* player did not exit normally last time */ ! 912: { ! 913: clear(); ! 914: addstr("Your character did not exit normally last time.\n"); ! 915: addstr("If you think you have good cause to have your character saved,\n"); ! 916: printw("you may quit and mail your reason to 'root'.\n"); ! 917: addstr("Otherwise, continuing spells certain death.\n"); ! 918: addstr("Do you want to quit ? "); ! 919: ch = getanswer("YN", FALSE); ! 920: if (ch == 'Y') ! 921: { ! 922: Player.p_status = S_HUNGUP; ! 923: writerecord(&Player, loc); ! 924: cleanup(TRUE); ! 925: /*NOTREACHED*/ ! 926: } ! 927: death("Stupidity"); ! 928: /*NOTREACHED*/ ! 929: } ! 930: return(loc); ! 931: } ! 932: else ! 933: mvaddstr(12, 0, "No good.\n"); ! 934: } ! 935: ! 936: Echo = TRUE; ! 937: } ! 938: else ! 939: mvaddstr(11, 0, "Not found.\n"); ! 940: ! 941: more(13); ! 942: return(-1L); ! 943: } ! 944: /**/ ! 945: /************************************************************************ ! 946: / ! 947: / FUNCTION NAME: neatstuff() ! 948: / ! 949: / FUNCTION: do random stuff ! 950: / ! 951: / AUTHOR: E. A. Estes, 3/3/86 ! 952: / ! 953: / ARGUMENTS: none ! 954: / ! 955: / RETURN VALUE: none ! 956: / ! 957: / MODULES CALLED: collecttaxes(), floor(), wmove(), drandom(), infloat(), ! 958: / waddstr(), mvprintw(), getanswer() ! 959: / ! 960: / GLOBAL INPUTS: Player, *stdscr, *Statptr ! 961: / ! 962: / GLOBAL OUTPUTS: Player ! 963: / ! 964: / DESCRIPTION: ! 965: / Handle gurus, medics, etc. ! 966: / ! 967: /************************************************************************/ ! 968: ! 969: neatstuff() ! 970: { ! 971: double temp; /* for temporary calculations */ ! 972: int ch; /* input */ ! 973: ! 974: switch ((int) ROLL(0.0, 100.0)) ! 975: { ! 976: case 1: ! 977: case 2: ! 978: if (Player.p_poison > 0.0) ! 979: { ! 980: mvaddstr(4, 0, "You've found a medic! How much will you offer to be cured ? "); ! 981: temp = floor(infloat()); ! 982: if (temp < 0.0 || temp > Player.p_gold) ! 983: /* negative gold, or more than available */ ! 984: { ! 985: mvaddstr(6, 0, "He was not amused, and made you worse.\n"); ! 986: Player.p_poison += 1.0; ! 987: } ! 988: else if (drandom() / 2.0 > (temp + 1.0) / MAX(Player.p_gold, 1)) ! 989: /* medic wants 1/2 of available gold */ ! 990: mvaddstr(5, 0, "Sorry, he wasn't interested.\n"); ! 991: else ! 992: { ! 993: mvaddstr(5, 0, "He accepted."); ! 994: Player.p_poison = MAX(0.0, Player.p_poison - 1.0); ! 995: Player.p_gold -= temp; ! 996: } ! 997: } ! 998: break; ! 999: ! 1000: case 3: ! 1001: mvaddstr(4, 0, "You've been caught raping and pillaging!\n"); ! 1002: Player.p_experience += 4000.0; ! 1003: Player.p_sin += 0.5; ! 1004: break; ! 1005: ! 1006: case 4: ! 1007: temp = ROLL(10.0, 75.0); ! 1008: mvprintw(4, 0, "You've found %.0f gold pieces, want them ? ", temp); ! 1009: ch = getanswer("NY", FALSE); ! 1010: ! 1011: if (ch == 'Y') ! 1012: collecttaxes(temp, 0.0); ! 1013: break; ! 1014: ! 1015: case 5: ! 1016: if (Player.p_sin > 1.0) ! 1017: { ! 1018: mvaddstr(4, 0, "You've found a Holy Orb!\n"); ! 1019: Player.p_sin -= 0.25; ! 1020: } ! 1021: break; ! 1022: ! 1023: case 6: ! 1024: if (Player.p_poison < 1.0) ! 1025: { ! 1026: mvaddstr(4, 0, "You've been hit with a plague!\n"); ! 1027: Player.p_poison += 1.0; ! 1028: } ! 1029: break; ! 1030: ! 1031: case 7: ! 1032: mvaddstr(4, 0, "You've found some holy water.\n"); ! 1033: ++Player.p_holywater; ! 1034: break; ! 1035: ! 1036: case 8: ! 1037: mvaddstr(4, 0, "You've met a Guru. . ."); ! 1038: if (drandom() * Player.p_sin > 1.0) ! 1039: addstr("You disgusted him with your sins!\n"); ! 1040: else if (Player.p_poison > 0.0) ! 1041: { ! 1042: addstr("He looked kindly upon you, and cured you.\n"); ! 1043: Player.p_poison = 0.0; ! 1044: } ! 1045: else ! 1046: { ! 1047: addstr("He rewarded you for your virtue.\n"); ! 1048: Player.p_mana += 50.0; ! 1049: Player.p_shield += 2.0; ! 1050: } ! 1051: break; ! 1052: ! 1053: case 9: ! 1054: mvaddstr(4, 0, "You've found an amulet.\n"); ! 1055: ++Player.p_amulets; ! 1056: break; ! 1057: ! 1058: case 10: ! 1059: if (Player.p_blindness) ! 1060: { ! 1061: mvaddstr(4, 0, "You've regained your sight!\n"); ! 1062: Player.p_blindness = FALSE; ! 1063: } ! 1064: break; ! 1065: ! 1066: default: /* deal with poison */ ! 1067: if (Player.p_poison > 0.0) ! 1068: { ! 1069: temp = Player.p_poison * Statptr->c_weakness ! 1070: * Player.p_maxenergy / 600.0; ! 1071: if (Player.p_energy > Player.p_maxenergy / 10.0 ! 1072: && temp + 5.0 < Player.p_energy) ! 1073: Player.p_energy -= temp; ! 1074: } ! 1075: break; ! 1076: } ! 1077: } ! 1078: /**/ ! 1079: /************************************************************************ ! 1080: / ! 1081: / FUNCTION NAME: genchar() ! 1082: / ! 1083: / FUNCTION: generate a random character ! 1084: / ! 1085: / AUTHOR: E. A. Estes, 12/4/85 ! 1086: / ! 1087: / ARGUMENTS: ! 1088: / int type - ASCII value of character type to generate ! 1089: / ! 1090: / RETURN VALUE: none ! 1091: / ! 1092: / MODULES CALLED: floor(), drandom() ! 1093: / ! 1094: / GLOBAL INPUTS: Wizard, Player, Stattable[] ! 1095: / ! 1096: / GLOBAL OUTPUTS: Player ! 1097: / ! 1098: / DESCRIPTION: ! 1099: / Use the lookup table for rolling stats. ! 1100: / ! 1101: /************************************************************************/ ! 1102: ! 1103: genchar(type) ! 1104: int type; ! 1105: { ! 1106: register int subscript; /* used for subscripting into Stattable */ ! 1107: register struct charstats *statptr;/* for pointing into Stattable */ ! 1108: ! 1109: subscript = type - '1'; ! 1110: ! 1111: if (subscript < C_MAGIC || subscript > C_EXPER) ! 1112: if (subscript != C_SUPER || !Wizard) ! 1113: /* fighter is default */ ! 1114: subscript = C_FIGHTER; ! 1115: ! 1116: statptr = &Stattable[subscript]; ! 1117: ! 1118: Player.p_quickness = ! 1119: ROLL(statptr->c_quickness.base, statptr->c_quickness.interval); ! 1120: Player.p_strength = ! 1121: ROLL(statptr->c_strength.base, statptr->c_strength.interval); ! 1122: Player.p_mana = ! 1123: ROLL(statptr->c_mana.base, statptr->c_mana.interval); ! 1124: Player.p_maxenergy = ! 1125: Player.p_energy = ! 1126: ROLL(statptr->c_energy.base, statptr->c_energy.interval); ! 1127: Player.p_brains = ! 1128: ROLL(statptr->c_brains.base, statptr->c_brains.interval); ! 1129: Player.p_magiclvl = ! 1130: ROLL(statptr->c_magiclvl.base, statptr->c_magiclvl.interval); ! 1131: ! 1132: Player.p_type = subscript; ! 1133: ! 1134: if (Player.p_type == C_HALFLING) ! 1135: /* give halfling some experience */ ! 1136: Player.p_experience = ROLL(600.0, 200.0); ! 1137: } ! 1138: /**/ ! 1139: /************************************************************************ ! 1140: / ! 1141: / FUNCTION NAME: playinit() ! 1142: / ! 1143: / FUNCTION: initialize for playing game ! 1144: / ! 1145: / AUTHOR: E. A. Estes, 12/4/85 ! 1146: / ! 1147: / ARGUMENTS: none ! 1148: / ! 1149: / RETURN VALUE: none ! 1150: / ! 1151: / MODULES CALLED: signal(), wclear(), noecho(), crmode(), initscr(), ! 1152: / wrefresh() ! 1153: / ! 1154: / GLOBAL INPUTS: *stdscr, ill_sig() ! 1155: / ! 1156: / GLOBAL OUTPUTS: Windows ! 1157: / ! 1158: / DESCRIPTION: ! 1159: / Catch a bunch of signals, and turn on curses stuff. ! 1160: / ! 1161: /************************************************************************/ ! 1162: ! 1163: playinit() ! 1164: { ! 1165: /* catch/ingnore signals */ ! 1166: ! 1167: #ifdef BSD41 ! 1168: sigignore(SIGQUIT); ! 1169: sigignore(SIGALRM); ! 1170: sigignore(SIGTERM); ! 1171: sigignore(SIGTSTP); ! 1172: sigignore(SIGTTIN); ! 1173: sigignore(SIGTTOU); ! 1174: sighold(SIGINT); ! 1175: sigset(SIGHUP, ill_sig); ! 1176: sigset(SIGTRAP, ill_sig); ! 1177: sigset(SIGIOT, ill_sig); ! 1178: sigset(SIGEMT, ill_sig); ! 1179: sigset(SIGFPE, ill_sig); ! 1180: sigset(SIGBUS, ill_sig); ! 1181: sigset(SIGSEGV, ill_sig); ! 1182: sigset(SIGSYS, ill_sig); ! 1183: sigset(SIGPIPE, ill_sig); ! 1184: #endif ! 1185: #ifdef BSD42 ! 1186: signal(SIGQUIT, ill_sig); ! 1187: signal(SIGALRM, SIG_IGN); ! 1188: signal(SIGTERM, SIG_IGN); ! 1189: signal(SIGTSTP, SIG_IGN); ! 1190: signal(SIGTTIN, SIG_IGN); ! 1191: signal(SIGTTOU, SIG_IGN); ! 1192: signal(SIGINT, ill_sig); ! 1193: signal(SIGHUP, SIG_DFL); ! 1194: signal(SIGTRAP, ill_sig); ! 1195: signal(SIGIOT, ill_sig); ! 1196: signal(SIGEMT, ill_sig); ! 1197: signal(SIGFPE, ill_sig); ! 1198: signal(SIGBUS, ill_sig); ! 1199: signal(SIGSEGV, ill_sig); ! 1200: signal(SIGSYS, ill_sig); ! 1201: signal(SIGPIPE, ill_sig); ! 1202: #endif ! 1203: #ifdef SYS3 ! 1204: signal(SIGINT, SIG_IGN); ! 1205: signal(SIGQUIT, SIG_IGN); ! 1206: signal(SIGTERM, SIG_IGN); ! 1207: signal(SIGALRM, SIG_IGN); ! 1208: signal(SIGHUP, ill_sig); ! 1209: signal(SIGTRAP, ill_sig); ! 1210: signal(SIGIOT, ill_sig); ! 1211: signal(SIGEMT, ill_sig); ! 1212: signal(SIGFPE, ill_sig); ! 1213: signal(SIGBUS, ill_sig); ! 1214: signal(SIGSEGV, ill_sig); ! 1215: signal(SIGSYS, ill_sig); ! 1216: signal(SIGPIPE, ill_sig); ! 1217: #endif ! 1218: #ifdef SYS5 ! 1219: signal(SIGINT, SIG_IGN); ! 1220: signal(SIGQUIT, SIG_IGN); ! 1221: signal(SIGTERM, SIG_IGN); ! 1222: signal(SIGALRM, SIG_IGN); ! 1223: signal(SIGHUP, ill_sig); ! 1224: signal(SIGTRAP, ill_sig); ! 1225: signal(SIGIOT, ill_sig); ! 1226: signal(SIGEMT, ill_sig); ! 1227: signal(SIGFPE, ill_sig); ! 1228: signal(SIGBUS, ill_sig); ! 1229: signal(SIGSEGV, ill_sig); ! 1230: signal(SIGSYS, ill_sig); ! 1231: signal(SIGPIPE, ill_sig); ! 1232: #endif ! 1233: ! 1234: initscr(); /* turn on curses */ ! 1235: noecho(); /* do not echo input */ ! 1236: crmode(); /* do not process erase, kill */ ! 1237: clear(); ! 1238: refresh(); ! 1239: Windows = TRUE; /* mark the state */ ! 1240: } ! 1241: ! 1242: /**/ ! 1243: /************************************************************************ ! 1244: / ! 1245: / FUNCTION NAME: cleanup() ! 1246: / ! 1247: / FUNCTION: close some files, and maybe exit ! 1248: / ! 1249: / AUTHOR: E. A. Estes, 12/4/85 ! 1250: / ! 1251: / ARGUMENTS: ! 1252: / bool doexit - exit flag ! 1253: / ! 1254: / RETURN VALUE: none ! 1255: / ! 1256: / MODULES CALLED: exit(), wmove(), fclose(), endwin(), nocrmode(), wrefresh() ! 1257: / ! 1258: / GLOBAL INPUTS: *Energyvoidfp, LINES, *stdscr, Windows, *Monstfp, ! 1259: / *Messagefp, *Playersfp ! 1260: / ! 1261: / GLOBAL OUTPUTS: none ! 1262: / ! 1263: / DESCRIPTION: ! 1264: / Close all open files. If we are "in curses" terminate curses. ! 1265: / If 'doexit' is set, exit, otherwise return. ! 1266: / ! 1267: /************************************************************************/ ! 1268: ! 1269: cleanup(doexit) ! 1270: bool doexit; ! 1271: { ! 1272: if (Windows) ! 1273: { ! 1274: move(LINES - 2, 0); ! 1275: refresh(); ! 1276: nocrmode(); ! 1277: endwin(); ! 1278: } ! 1279: ! 1280: fclose(Playersfp); ! 1281: fclose(Monstfp); ! 1282: fclose(Messagefp); ! 1283: fclose(Energyvoidfp); ! 1284: ! 1285: if (doexit) ! 1286: exit(0); ! 1287: /*NOTREACHED*/ ! 1288: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.