main.c revision 1.18
1/* $OpenBSD: main.c,v 1.18 2016/01/06 09:39:51 tb 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 97int 98main(int argc, 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 time_t 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 case 's': /* short */ 112 noheader = TRUE; 113 break; 114 115 case 'H': /* Header */ 116 headeronly = TRUE; 117 break; 118 119 case 'a': /* all users */ 120 activelist(); 121 cleanup(TRUE); 122 /* NOTREACHED */ 123 124 case 'p': /* purge old players */ 125 purgeoldplayers(); 126 cleanup(TRUE); 127 /* NOTREACHED */ 128 129 case 'S': /* set 'Wizard' */ 130 Wizard = !getuid(); 131 break; 132 133 case 'x': /* examine */ 134 examine = TRUE; 135 break; 136 137 case 'm': /* monsters */ 138 monstlist(); 139 cleanup(TRUE); 140 /* NOTREACHED */ 141 142 case 'b': /* scoreboard */ 143 scorelist(); 144 cleanup(TRUE); 145 /* NOTREACHED */ 146 } 147 148 if (!isatty(0)) /* don't let non-tty's play */ 149 cleanup(TRUE); 150 /* NOTREACHED */ 151 152 playinit(); /* set up to catch signals, init curses */ 153 154 if (examine) { 155 changestats(FALSE); 156 cleanup(TRUE); 157 /* NOTREACHED */ 158 } 159 if (!noheader) { 160 titlelist(); 161 purgeoldplayers(); /* clean up old characters */ 162 } 163 if (headeronly) 164 cleanup(TRUE); 165 /* NOTREACHED */ 166 167 do 168 /* get the player structure filled */ 169 { 170 Fileloc = -1L; 171 172 mvaddstr(22, 17, "Do you have a character to run [Q = Quit] ? "); 173 174 switch (getanswer("NYQ", FALSE)) { 175 case 'Y': 176 Fileloc = recallplayer(); 177 break; 178 179 case 'Q': 180 cleanup(TRUE); 181 /* NOTREACHED */ 182 183 default: 184 Fileloc = rollnewplayer(); 185 break; 186 } 187 clear(); 188 } 189 while (Fileloc < 0L); 190 191 if (Player.p_level > 5.0) 192 /* low level players have long timeout */ 193 Timeout = TRUE; 194 195 /* update some important player statistics */ 196 strlcpy(Player.p_login, Login, LOGIN_NAME_MAX); 197 time(&seconds); 198 Player.p_lastused = localtime(&seconds)->tm_yday; 199 Player.p_status = S_PLAYING; 200 writerecord(&Player, Fileloc); 201 202 Statptr = &Stattable[Player.p_type]; /* initialize pointer */ 203 204 altercoordinates(Player.p_x, Player.p_y, A_FORCED); /* set some flags */ 205 206 clear(); 207 208 for (;;) 209 /* loop forever, processing input */ 210 { 211 212 adjuststats(); /* cleanup stats */ 213 214 if (Throne && Player.p_crowns == 0 && Player.p_specialtype != SC_KING) 215 /* not allowed on throne -- move */ 216 { 217 mvaddstr(5, 0, "You're not allowed in the Lord's Chamber without a crown.\n"); 218 altercoordinates(0.0, 0.0, A_NEAR); 219 } 220 checktampered();/* check for energy voids, etc. */ 221 222 if (Player.p_status != S_CLOAKED 223 /* not cloaked */ 224 && (dtemp = fabs(Player.p_x)) == fabs(Player.p_y) 225 /* |x| = |y| */ 226 && !Throne) 227 /* not on throne */ 228 { 229 dtemp = sqrt(dtemp / 100.0); 230 if (floor(dtemp) == dtemp) 231 /* |x| / 100 == n*n; at a trading post */ 232 { 233 tradingpost(); 234 clear(); 235 } 236 } 237 checkbattle(); /* check for player to player battle */ 238 neatstuff(); /* gurus, medics, etc. */ 239 240 if (Player.p_status == S_CLOAKED) { 241 /* costs 3 mana per turn to be cloaked */ 242 if (Player.p_mana > 3.0) 243 Player.p_mana -= 3.0; 244 else 245 /* ran out of mana, uncloak */ 246 { 247 Player.p_status = S_PLAYING; 248 Changed = TRUE; 249 } 250 } 251 252 if (Player.p_status != S_PLAYING && Player.p_status != S_CLOAKED) 253 /* change status back to S_PLAYING */ 254 { 255 Player.p_status = S_PLAYING; 256 Changed = TRUE; 257 } 258 if (Changed) 259 /* update file only if important stuff has changed */ 260 { 261 writerecord(&Player, Fileloc); 262 Changed = FALSE; 263 continue; 264 } 265 readmessage(); /* read message, if any */ 266 267 displaystats(); /* print statistics */ 268 269 move(6, 0); 270 271 if (Throne) 272 /* maybe make king, print prompt, etc. */ 273 throneroom(); 274 275 /* print status line */ 276 addstr("1:Move 2:Players 3:Talk 4:Stats 5:Quit "); 277 if (Player.p_level >= MEL_CLOAK && Player.p_magiclvl >= ML_CLOAK) 278 addstr("6:Cloak "); 279 if (Player.p_level >= MEL_TELEPORT && Player.p_magiclvl >= ML_TELEPORT) 280 addstr("7:Teleport "); 281 if (Player.p_specialtype >= SC_COUNCIL || Wizard) 282 addstr("8:Intervene "); 283 284 procmain(); /* process input */ 285 } 286} 287/**/ 288/************************************************************************ 289/ 290/ FUNCTION NAME: initialstate() 291/ 292/ FUNCTION: initialize some important global variable 293/ 294/ AUTHOR: E. A. Estes, 12/4/85 295/ 296/ ARGUMENTS: none 297/ 298/ RETURN VALUE: none 299/ 300/ MODULES CALLED: fopen(), error(), getuid(), getlogin(), getpwuid() 301/ 302/ GLOBAL INPUTS: 303/ 304/ GLOBAL OUTPUTS: *Energyvoidfp, Echo, Marsh, *Login, Users, Beyond, 305/ Throne, Wizard, Changed, Okcount, Timeout, Windows, *Monstfp, *Messagefp, 306/ *Playersfp 307/ 308/ DESCRIPTION: 309/ Set global flags, and open files which remain open. 310/ 311*************************************************************************/ 312 313void 314initialstate(void) 315{ 316#ifdef TERMIOS 317 struct termios tty; 318#endif 319 320 Beyond = FALSE; 321 Marsh = FALSE; 322 Throne = FALSE; 323 Changed = FALSE; 324 Wizard = FALSE; 325 Timeout = FALSE; 326 Users = 0; 327 Windows = FALSE; 328 Echo = TRUE; 329 330 /* setup login name */ 331 if ((Login = getlogin()) == NULL) { 332 struct passwd *gpwd; 333 334 gpwd = getpwuid(getuid()); 335 if (gpwd != NULL) 336 Login = gpwd->pw_name; 337 else 338 errx(1, "Who are you?"); 339 } 340 341#ifdef TERMIOS 342 /* setup terminal keys */ 343 if (tcgetattr(0, &tty) == 0) { 344 Ch_Erase = tty.c_cc[VERASE]; 345 Ch_Kill = tty.c_cc[VKILL]; 346 } else { 347 Ch_Erase = CH_ERASE; 348 Ch_Kill = CH_KILL; 349 } 350#else 351 Ch_Erase = CH_ERASE; 352 Ch_Kill = CH_KILL; 353#endif 354 355 /* open some files */ 356 if ((Playersfp = fopen(_PATH_PEOPLE, "r+")) == NULL) 357 error(_PATH_PEOPLE); 358 /* NOTREACHED */ 359 360 if ((Monstfp = fopen(_PATH_MONST, "r+")) == NULL) 361 error(_PATH_MONST); 362 /* NOTREACHED */ 363 364 if ((Messagefp = fopen(_PATH_MESS, "r")) == NULL) 365 error(_PATH_MESS); 366 /* NOTREACHED */ 367 368 if ((Energyvoidfp = fopen(_PATH_VOID, "r+")) == NULL) 369 error(_PATH_VOID); 370 /* NOTREACHED */ 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 398long 399rollnewplayer(void) 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, 409"1:Magic User 2:Fighter 3:Elf 4:Dwarf 5:Halfling 6:Experimento "); 410 if (Wizard) { 411 addstr("7:Super ? "); 412 chartype = getanswer("1234567", FALSE); 413 } else { 414 addstr("? "); 415 chartype = getanswer("123456", FALSE); 416 } 417 418 do { 419 genchar(chartype); /* roll up a character */ 420 421 /* print out results */ 422 mvprintw(12, 14, 423 "Strength : %2.0f Quickness: %2.0f Mana : %2.0f\n", 424 Player.p_strength, Player.p_quickness, Player.p_mana); 425 mvprintw(13, 14, 426 "Energy Level: %2.0f Brains : %2.0f Magic Level: %2.0f\n", 427 Player.p_energy, Player.p_brains, Player.p_magiclvl); 428 429 if (Player.p_type == C_EXPER || Player.p_type == C_SUPER) 430 break; 431 432 mvaddstr(14, 14, "Type '1' to keep >"); 433 ch = getanswer(" ", TRUE); 434 } 435 while (ch != '1'); 436 437 if (Player.p_type == C_EXPER || Player.p_type == C_SUPER) 438 /* get coordinates for experimento */ 439 for (;;) { 440 mvaddstr(16, 0, "Enter the X Y coordinates of your experimento ? "); 441 getstring(Databuf, SZ_DATABUF); 442 sscanf(Databuf, "%lf %lf", &Player.p_x, &Player.p_y); 443 444 if (fabs(Player.p_x) > D_EXPER || fabs(Player.p_y) > D_EXPER) 445 mvaddstr(17, 0, "Invalid coordinates. Try again.\n"); 446 else 447 break; 448 } 449 450 for (;;) 451 /* name the new character */ 452 { 453 mvprintw(18, 0, 454 "Give your character a name [up to %d characters] ? ", SZ_NAME - 1); 455 getstring(Player.p_name, SZ_NAME); 456 truncstring(Player.p_name); /* remove trailing blanks */ 457 458 if (Player.p_name[0] == '\0') 459 /* no null names */ 460 mvaddstr(19, 0, "Invalid name."); 461 else 462 if (findname(Player.p_name, &Other) >= 0L) 463 /* cannot have duplicate names */ 464 mvaddstr(19, 0, "Name already in use."); 465 else 466 /* name is acceptable */ 467 break; 468 469 addstr(" Pick another.\n"); 470 } 471 472 /* get a password for character */ 473 Echo = FALSE; 474 475 do { 476 mvaddstr(20, 0, "Give your character a password [up to 8 characters] ? "); 477 getstring(Player.p_password, SZ_PASSWORD); 478 mvaddstr(21, 0, "One more time to verify ? "); 479 getstring(Databuf, SZ_PASSWORD); 480 } 481 while (strcmp(Player.p_password, Databuf) != 0); 482 483 Echo = TRUE; 484 485 return (allocrecord()); 486} 487/**/ 488/************************************************************************ 489/ 490/ FUNCTION NAME: procmain() 491/ 492/ FUNCTION: process input from player 493/ 494/ AUTHOR: E. A. Estes, 12/4/85 495/ 496/ ARGUMENTS: none 497/ 498/ RETURN VALUE: none 499/ 500/ MODULES CALLED: dotampered(), changestats(), inputoption(), allstatslist(), 501/ fopen(), wmove(), drandom(), sscanf(), fclose(), altercoordinates(), 502/ waddstr(), fprintf(), distance(), userlist(), leavegame(), encounter(), 503/ getstring(), wclrtobot() 504/ 505/ GLOBAL INPUTS: Circle, Illcmd[], Throne, Wizard, Player, *stdscr, 506/ Databuf[], Illmove[] 507/ 508/ GLOBAL OUTPUTS: Player, Changed 509/ 510/ DESCRIPTION: 511/ Process main menu options. 512/ 513*************************************************************************/ 514 515void 516procmain(void) 517{ 518 int ch; /* input */ 519 double x; /* desired new x coordinate */ 520 double y; /* desired new y coordinate */ 521 double temp; /* for temporary calculations */ 522 FILE *fp; /* for opening files */ 523 int loop; /* a loop counter */ 524 bool hasmoved = FALSE; /* set if player has moved */ 525 526 ch = inputoption(); 527 mvaddstr(4, 0, "\n\n"); /* clear status area */ 528 529 move(7, 0); 530 clrtobot(); /* clear data on bottom area of screen */ 531 532 if (Player.p_specialtype == SC_VALAR && (ch == '1' || ch == '7')) 533 /* valar cannot move */ 534 ch = ' '; 535 536 switch (ch) { 537 case 'K': /* move up/north */ 538 case 'N': 539 x = Player.p_x; 540 y = Player.p_y + MAXMOVE(); 541 hasmoved = TRUE; 542 break; 543 544 case 'J': /* move down/south */ 545 case 'S': 546 x = Player.p_x; 547 y = Player.p_y - MAXMOVE(); 548 hasmoved = TRUE; 549 break; 550 551 case 'L': /* move right/east */ 552 case 'E': 553 x = Player.p_x + MAXMOVE(); 554 y = Player.p_y; 555 hasmoved = TRUE; 556 break; 557 558 case 'H': /* move left/west */ 559 case 'W': 560 x = Player.p_x - MAXMOVE(); 561 y = Player.p_y; 562 hasmoved = TRUE; 563 break; 564 565 default: /* rest */ 566 Player.p_energy += (Player.p_maxenergy + Player.p_shield) / 15.0 567 + Player.p_level / 3.0 + 2.0; 568 Player.p_energy = 569 MIN(Player.p_energy, Player.p_maxenergy + Player.p_shield); 570 571 if (Player.p_status != S_CLOAKED) 572 /* cannot find mana if cloaked */ 573 { 574 Player.p_mana += (Circle + Player.p_level) / 4.0; 575 576 if (drandom() < 0.2 && Player.p_status == S_PLAYING && !Throne) 577 /* wandering monster */ 578 encounter(-1); 579 } 580 break; 581 582 case 'X': /* change/examine a character */ 583 changestats(TRUE); 584 break; 585 586 case '1': /* move */ 587 for (loop = 3; loop; --loop) { 588 mvaddstr(4, 0, "X Y Coordinates ? "); 589 getstring(Databuf, SZ_DATABUF); 590 591 if (sscanf(Databuf, "%lf %lf", &x, &y) != 2) 592 mvaddstr(5, 0, "Try again\n"); 593 else 594 if (distance(Player.p_x, x, Player.p_y, y) > MAXMOVE()) 595 ILLMOVE(); 596 else { 597 hasmoved = TRUE; 598 break; 599 } 600 } 601 break; 602 603 case '2': /* players */ 604 userlist(TRUE); 605 break; 606 607 case '3': /* message */ 608 mvaddstr(4, 0, "Message ? "); 609 getstring(Databuf, SZ_DATABUF); 610 /* we open the file for writing to erase any data which is 611 * already there */ 612 fp = fopen(_PATH_MESS, "w"); 613 if (Databuf[0] != '\0') 614 fprintf(fp, "%s: %s", Player.p_name, Databuf); 615 fclose(fp); 616 break; 617 618 case '4': /* stats */ 619 allstatslist(); 620 break; 621 622 case '5': /* good-bye */ 623 leavegame(); 624 /* NOTREACHED */ 625 626 case '6': /* cloak */ 627 if (Player.p_level < MEL_CLOAK || Player.p_magiclvl < ML_CLOAK) 628 ILLCMD(); 629 else 630 if (Player.p_status == S_CLOAKED) 631 Player.p_status = S_PLAYING; 632 else 633 if (Player.p_mana < MM_CLOAK) 634 mvaddstr(5, 0, "No mana left.\n"); 635 else { 636 Changed = TRUE; 637 Player.p_mana -= MM_CLOAK; 638 Player.p_status = S_CLOAKED; 639 } 640 break; 641 642 case '7': /* teleport */ 643 /* 644 * conditions for teleport 645 * - 20 per (level plus magic level) 646 * - OR council of the wise or valar or ex-valar 647 * - OR transport from throne 648 * transports from throne cost no mana 649 */ 650 if (Player.p_level < MEL_TELEPORT || Player.p_magiclvl < ML_TELEPORT) 651 ILLCMD(); 652 else 653 for (loop = 3; loop; --loop) { 654 mvaddstr(4, 0, "X Y Coordinates ? "); 655 getstring(Databuf, SZ_DATABUF); 656 657 if (sscanf(Databuf, "%lf %lf", &x, &y) == 2) { 658 temp = distance(Player.p_x, x, Player.p_y, y); 659 if (!Throne 660 /* can transport anywhere from throne */ 661 && Player.p_specialtype <= SC_COUNCIL 662 /* council, valar can transport 663 * anywhere */ 664 && temp > (Player.p_level + Player.p_magiclvl) * 20.0) 665 /* can only move 20 per exp. 666 * level + mag. level */ 667 ILLMOVE(); 668 else { 669 temp = (temp / 75.0 + 1.0) * 20.0; /* mana used */ 670 671 if (!Throne && temp > Player.p_mana) 672 mvaddstr(5, 0, "Not enough power for that distance.\n"); 673 else { 674 if (!Throne) 675 Player.p_mana -= temp; 676 hasmoved = TRUE; 677 break; 678 } 679 } 680 } 681 } 682 break; 683 684 case 'C': 685 case '9': /* monster */ 686 if (Throne) 687 /* no monsters while on throne */ 688 mvaddstr(5, 0, "No monsters in the chamber!\n"); 689 else 690 if (Player.p_specialtype != SC_VALAR) 691 /* the valar cannot call monsters */ 692 { 693 Player.p_sin += 1e-6; 694 encounter(-1); 695 } 696 break; 697 698 case '0': /* decree */ 699 if (Wizard || (Player.p_specialtype == SC_KING && Throne)) 700 /* kings must be on throne to decree */ 701 dotampered(); 702 else 703 ILLCMD(); 704 break; 705 706 case '8': /* intervention */ 707 if (Wizard || Player.p_specialtype >= SC_COUNCIL) 708 dotampered(); 709 else 710 ILLCMD(); 711 break; 712 } 713 714 if (hasmoved) 715 /* player has moved -- alter coordinates, and do random 716 * monster */ 717 { 718 altercoordinates(x, y, A_SPECIFIC); 719 720 if (drandom() < 0.2 && Player.p_status == S_PLAYING && !Throne) 721 encounter(-1); 722 } 723} 724/**/ 725/************************************************************************ 726/ 727/ FUNCTION NAME: titlelist() 728/ 729/ FUNCTION: print title page 730/ 731/ AUTHOR: E. A. Estes, 12/4/85 732/ 733/ ARGUMENTS: none 734/ 735/ RETURN VALUE: none 736/ 737/ MODULES CALLED: fread(), fseek(), fopen(), fgets(), wmove(), strlcpy(), 738/ fclose(), strlen(), waddstr(), snprintf(), wrefresh() 739/ 740/ GLOBAL INPUTS: Lines, Other, *stdscr, Databuf[], *Playersfp 741/ 742/ GLOBAL OUTPUTS: Lines 743/ 744/ DESCRIPTION: 745/ Print important information about game, players, etc. 746/ 747*************************************************************************/ 748 749void 750titlelist(void) 751{ 752 FILE *fp; /* used for opening various files */ 753 bool councilfound = FALSE; /* set if we find a member of the 754 * council */ 755 bool kingfound = FALSE; /* set if we find a king */ 756 double hiexp, nxtexp; /* used for finding the two highest players */ 757 double hilvl, nxtlvl; /* used for finding the two highest players */ 758 char hiname[21], nxtname[21]; /* used for finding the two 759 * highest players */ 760 761 nxtexp = 0; 762 mvaddstr(0, 14, 763 "W e l c o m e t o P h a n t a s i a (vers. 3.3.2)!"); 764 765 /* print message of the day */ 766 if ((fp = fopen(_PATH_MOTD, "r")) != NULL 767 && fgets(Databuf, SZ_DATABUF, fp) != NULL) { 768 mvaddstr(2, 40 - strlen(Databuf) / 2, Databuf); 769 fclose(fp); 770 } 771 /* search for king */ 772 fseek(Playersfp, 0L, SEEK_SET); 773 while (fread(&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) 774 if (Other.p_specialtype == SC_KING && 775 Other.p_status != S_NOTUSED) 776 /* found the king */ 777 { 778 snprintf(Databuf, sizeof Databuf, 779 "The present ruler is %s Level:%.0f", 780 Other.p_name, Other.p_level); 781 mvaddstr(4, 40 - strlen(Databuf) / 2, Databuf); 782 kingfound = TRUE; 783 break; 784 } 785 if (!kingfound) 786 mvaddstr(4, 24, "There is no ruler at this time."); 787 788 /* search for valar */ 789 fseek(Playersfp, 0L, SEEK_SET); 790 while (fread(&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) 791 if (Other.p_specialtype == SC_VALAR && Other.p_status != S_NOTUSED) 792 /* found the valar */ 793 { 794 snprintf(Databuf, sizeof Databuf, 795 "The Valar is %s Login: %s", Other.p_name, 796 Other.p_login); 797 mvaddstr(6, 40 - strlen(Databuf) / 2, Databuf); 798 break; 799 } 800 /* search for council of the wise */ 801 fseek(Playersfp, 0L, SEEK_SET); 802 Lines = 10; 803 while (fread(&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) 804 if (Other.p_specialtype == SC_COUNCIL && Other.p_status != S_NOTUSED) 805 /* found a member of the council */ 806 { 807 if (!councilfound) { 808 mvaddstr(8, 30, "Council of the Wise:"); 809 councilfound = TRUE; 810 } 811 /* This assumes a finite (<=5) number of C.O.W.: */ 812 snprintf(Databuf, sizeof Databuf, 813 "%s Login: %s", Other.p_name, Other.p_login); 814 mvaddstr(Lines++, 40 - strlen(Databuf) / 2, Databuf); 815 } 816 /* search for the two highest players */ 817 nxtname[0] = hiname[0] = '\0'; 818 hiexp = 0.0; 819 nxtlvl = hilvl = 0; 820 821 fseek(Playersfp, 0L, SEEK_SET); 822 while (fread(&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) 823 if (Other.p_experience > hiexp && Other.p_specialtype <= SC_KING && Other.p_status != S_NOTUSED) 824 /* highest found so far */ 825 { 826 nxtexp = hiexp; 827 hiexp = Other.p_experience; 828 nxtlvl = hilvl; 829 hilvl = Other.p_level; 830 strlcpy(nxtname, hiname, sizeof nxtname); 831 strlcpy(hiname, Other.p_name, sizeof hiname); 832 } else 833 if (Other.p_experience > nxtexp 834 && Other.p_specialtype <= SC_KING 835 && Other.p_status != S_NOTUSED) 836 /* next highest found so far */ 837 { 838 nxtexp = Other.p_experience; 839 nxtlvl = Other.p_level; 840 strlcpy(nxtname, Other.p_name, sizeof nxtname); 841 } 842 mvaddstr(15, 28, "Highest characters are:"); 843 snprintf(Databuf, sizeof Databuf, 844 "%s Level:%.0f and %s Level:%.0f", 845 hiname, hilvl, nxtname, nxtlvl); 846 mvaddstr(17, 40 - strlen(Databuf) / 2, Databuf); 847 848 /* print last to die */ 849 if ((fp = fopen(_PATH_LASTDEAD, "r")) != NULL 850 && fgets(Databuf, SZ_DATABUF, fp) != NULL) { 851 mvaddstr(19, 25, "The last character to die was:"); 852 mvaddstr(20, 40 - strlen(Databuf) / 2, Databuf); 853 fclose(fp); 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 883long 884recallplayer(void) 885{ 886 long loc = 0L; /* location in player file */ 887 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 /* prompt for password */ 902 mvaddstr(11, 0, "Password ? "); 903 getstring(Databuf, SZ_PASSWORD); 904 if (strcmp(Databuf, Player.p_password) == 0) 905 /* password good */ 906 { 907 Echo = TRUE; 908 909 if (Player.p_status != S_OFF) 910 /* player did not exit normally last 911 * 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 Player.p_status = S_HUNGUP; 922 writerecord(&Player, loc); 923 cleanup(TRUE); 924 /* NOTREACHED */ 925 } 926 death("Stupidity"); 927 /* NOTREACHED */ 928 } 929 return (loc); 930 } else 931 mvaddstr(12, 0, "No good.\n"); 932 } 933 934 Echo = TRUE; 935 } else 936 mvaddstr(11, 0, "Not found.\n"); 937 938 more(13); 939 return (-1L); 940} 941/**/ 942/************************************************************************ 943/ 944/ FUNCTION NAME: neatstuff() 945/ 946/ FUNCTION: do random stuff 947/ 948/ AUTHOR: E. A. Estes, 3/3/86 949/ 950/ ARGUMENTS: none 951/ 952/ RETURN VALUE: none 953/ 954/ MODULES CALLED: collecttaxes(), floor(), wmove(), drandom(), infloat(), 955/ waddstr(), mvprintw(), getanswer() 956/ 957/ GLOBAL INPUTS: Player, *stdscr, *Statptr 958/ 959/ GLOBAL OUTPUTS: Player 960/ 961/ DESCRIPTION: 962/ Handle gurus, medics, etc. 963/ 964*************************************************************************/ 965 966void 967neatstuff(void) 968{ 969 double temp; /* for temporary calculations */ 970 int ch; /* input */ 971 972 switch ((int) ROLL(0.0, 100.0)) { 973 case 1: 974 case 2: 975 if (Player.p_poison > 0.0) { 976 mvaddstr(4, 0, "You've found a medic! How much will you offer to be cured ? "); 977 temp = floor(infloat()); 978 if (temp < 0.0 || temp > Player.p_gold) 979 /* negative gold, or more than available */ 980 { 981 mvaddstr(6, 0, "He was not amused, and made you worse.\n"); 982 Player.p_poison += 1.0; 983 } else if (drandom() / 2.0 > (temp + 1.0) / MAX(Player.p_gold, 1)) 984 /* medic wants 1/2 of available gold */ 985 mvaddstr(5, 0, "Sorry, he wasn't interested.\n"); 986 else { 987 mvaddstr(5, 0, "He accepted."); 988 Player.p_poison = MAX(0.0, Player.p_poison - 1.0); 989 Player.p_gold -= temp; 990 } 991 } 992 break; 993 994 case 3: 995 mvaddstr(4, 0, "You've been caught raping and pillaging!\n"); 996 Player.p_experience += 4000.0; 997 Player.p_sin += 0.5; 998 break; 999 1000 case 4: 1001 temp = ROLL(10.0, 75.0); 1002 mvprintw(4, 0, "You've found %.0f gold pieces, want them ? ", temp); 1003 ch = getanswer("NY", FALSE); 1004 1005 if (ch == 'Y') 1006 collecttaxes(temp, 0.0); 1007 break; 1008 1009 case 5: 1010 if (Player.p_sin > 1.0) { 1011 mvaddstr(4, 0, "You've found a Holy Orb!\n"); 1012 Player.p_sin -= 0.25; 1013 } 1014 break; 1015 1016 case 6: 1017 if (Player.p_poison < 1.0) { 1018 mvaddstr(4, 0, "You've been hit with a plague!\n"); 1019 Player.p_poison += 1.0; 1020 } 1021 break; 1022 1023 case 7: 1024 mvaddstr(4, 0, "You've found some holy water.\n"); 1025 ++Player.p_holywater; 1026 break; 1027 1028 case 8: 1029 mvaddstr(4, 0, "You've met a Guru. . ."); 1030 if (drandom() * Player.p_sin > 1.0) 1031 addstr("You disgusted him with your sins!\n"); 1032 else if (Player.p_poison > 0.0) { 1033 addstr("He looked kindly upon you, and cured you.\n"); 1034 Player.p_poison = 0.0; 1035 } else { 1036 addstr("He rewarded you for your virtue.\n"); 1037 Player.p_mana += 50.0; 1038 Player.p_shield += 2.0; 1039 } 1040 break; 1041 1042 case 9: 1043 mvaddstr(4, 0, "You've found an amulet.\n"); 1044 ++Player.p_amulets; 1045 break; 1046 1047 case 10: 1048 if (Player.p_blindness) { 1049 mvaddstr(4, 0, "You've regained your sight!\n"); 1050 Player.p_blindness = FALSE; 1051 } 1052 break; 1053 1054 default: /* deal with poison */ 1055 if (Player.p_poison > 0.0) { 1056 temp = Player.p_poison * Statptr->c_weakness 1057 * Player.p_maxenergy / 600.0; 1058 if (Player.p_energy > Player.p_maxenergy / 10.0 1059 && temp + 5.0 < Player.p_energy) 1060 Player.p_energy -= temp; 1061 } 1062 break; 1063 } 1064} 1065/**/ 1066/************************************************************************ 1067/ 1068/ FUNCTION NAME: genchar() 1069/ 1070/ FUNCTION: generate a random character 1071/ 1072/ AUTHOR: E. A. Estes, 12/4/85 1073/ 1074/ ARGUMENTS: 1075/ int type - ASCII value of character type to generate 1076/ 1077/ RETURN VALUE: none 1078/ 1079/ MODULES CALLED: floor(), drandom() 1080/ 1081/ GLOBAL INPUTS: Wizard, Player, Stattable[] 1082/ 1083/ GLOBAL OUTPUTS: Player 1084/ 1085/ DESCRIPTION: 1086/ Use the lookup table for rolling stats. 1087/ 1088*************************************************************************/ 1089 1090void 1091genchar(int type) 1092{ 1093 int subscript; /* used for subscripting into Stattable */ 1094 struct charstats *statptr; /* for pointing into Stattable */ 1095 1096 subscript = type - '1'; 1097 1098 if (subscript < C_MAGIC || subscript > C_EXPER) 1099 if (subscript != C_SUPER || !Wizard) 1100 /* fighter is default */ 1101 subscript = C_FIGHTER; 1102 1103 statptr = &Stattable[subscript]; 1104 1105 Player.p_quickness = 1106 ROLL(statptr->c_quickness.base, statptr->c_quickness.interval); 1107 Player.p_strength = 1108 ROLL(statptr->c_strength.base, statptr->c_strength.interval); 1109 Player.p_mana = 1110 ROLL(statptr->c_mana.base, statptr->c_mana.interval); 1111 Player.p_maxenergy = 1112 Player.p_energy = 1113 ROLL(statptr->c_energy.base, statptr->c_energy.interval); 1114 Player.p_brains = 1115 ROLL(statptr->c_brains.base, statptr->c_brains.interval); 1116 Player.p_magiclvl = 1117 ROLL(statptr->c_magiclvl.base, statptr->c_magiclvl.interval); 1118 1119 Player.p_type = subscript; 1120 1121 if (Player.p_type == C_HALFLING) 1122 /* give halfling some experience */ 1123 Player.p_experience = ROLL(600.0, 200.0); 1124} 1125/**/ 1126/************************************************************************ 1127/ 1128/ FUNCTION NAME: playinit() 1129/ 1130/ FUNCTION: initialize for playing game 1131/ 1132/ AUTHOR: E. A. Estes, 12/4/85 1133/ 1134/ ARGUMENTS: none 1135/ 1136/ RETURN VALUE: none 1137/ 1138/ MODULES CALLED: signal(), wclear(), noecho(), cbreak(), initscr(), 1139/ wrefresh() 1140/ 1141/ GLOBAL INPUTS: *stdscr, ill_sig() 1142/ 1143/ GLOBAL OUTPUTS: Windows 1144/ 1145/ DESCRIPTION: 1146/ Catch a bunch of signals, and turn on curses stuff. 1147/ 1148*************************************************************************/ 1149 1150void 1151playinit() 1152{ 1153 initscr(); /* turn on curses */ 1154 noecho(); /* do not echo input */ 1155 cbreak(); /* do not process erase, kill */ 1156 clear(); 1157 refresh(); 1158 Windows = TRUE; /* mark the state */ 1159} 1160/**/ 1161/************************************************************************ 1162/ 1163/ FUNCTION NAME: cleanup() 1164/ 1165/ FUNCTION: close some files, and maybe exit 1166/ 1167/ AUTHOR: E. A. Estes, 12/4/85 1168/ 1169/ ARGUMENTS: 1170/ bool doexit - exit flag 1171/ 1172/ RETURN VALUE: none 1173/ 1174/ MODULES CALLED: exit(), wmove(), fclose(), endwin(), nocbreak(), wrefresh() 1175/ 1176/ GLOBAL INPUTS: *Energyvoidfp, LINES, *stdscr, Windows, *Monstfp, 1177/ *Messagefp, *Playersfp 1178/ 1179/ GLOBAL OUTPUTS: none 1180/ 1181/ DESCRIPTION: 1182/ Close all open files. If we are "in curses" terminate curses. 1183/ If 'doexit' is set, exit, otherwise return. 1184/ 1185*************************************************************************/ 1186 1187void 1188cleanup(int doexit) 1189{ 1190 if (Windows) { 1191 move(LINES - 2, 0); 1192 refresh(); 1193 nocbreak(); 1194 endwin(); 1195 } 1196 1197 if (Playersfp) { 1198 fclose(Playersfp); 1199 Playersfp = NULL; 1200 } 1201 if (Monstfp) { 1202 fclose(Monstfp); 1203 Monstfp = NULL; 1204 } 1205 if (Messagefp) { 1206 fclose(Messagefp); 1207 Messagefp = NULL; 1208 } 1209 if (Energyvoidfp) { 1210 fclose(Energyvoidfp); 1211 Energyvoidfp = NULL; 1212 } 1213 1214 if (doexit) 1215 exit(0); 1216} 1217