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