1 /* $OpenBSD: main.c,v 1.22 2016/08/27 02:00:10 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 <curses.h> 32 #include <err.h> 33 #include <math.h> 34 #include <pwd.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #ifdef TERMIOS 38 #include <termios.h> 39 #endif 40 #include <time.h> 41 #include <unistd.h> 42 43 #include "macros.h" 44 #include "pathnames.h" 45 #include "phantdefs.h" 46 #include "phantglobs.h" 47 48 /* 49 * The program allocates as much file space as it needs to store characters, 50 * so the possibility exists for the character file to grow without bound. 51 * The file is purged upon normal entry to try to avoid that problem. 52 * A similar problem exists for energy voids. To alleviate the problem here, 53 * the void file is cleared with every new king, and a limit is placed 54 * on the size of the energy void file. 55 */ 56 57 /* 58 * Put one line of text into the file 'motd' for announcements, etc. 59 */ 60 61 /* 62 * The scoreboard file is updated when someone dies, and keeps track 63 * of the highest character to date for that login. 64 * Being purged from the character file does not cause the scoreboard 65 * to be updated. 66 */ 67 68 /* 69 * main.c Main routines for Phantasia 70 */ 71 72 /*************************************************************************** 73 / FUNCTION NAME: main() 74 / 75 / FUNCTION: initialize state, and call main process 76 / 77 / AUTHOR: E. A. Estes, 12/4/85 78 / 79 / ARGUMENTS: 80 / int argc - argument count 81 / char **argv - argument vector 82 / 83 / RETURN VALUE: none 84 / 85 / MODULES CALLED: monstlist(), checkenemy(), activelist(), 86 / throneroom(), checkbattle(), readmessage(), changestats(), writerecord(), 87 / tradingpost(), adjuststats(), recallplayer(), displaystats(), checktampered(), 88 / fabs(), rollnewplayer(), time(), exit(), sqrt(), floor(), wmove(), 89 / signal(), strlcat(), purgeoldplayers(), getuid(), isatty(), wclear(), 90 / strlcpy(), system(), altercoordinates(), cleanup(), waddstr(), procmain(), 91 / playinit(), leavegame(), localtime(), getanswer(), neatstuff(), initialstate(), 92 / scorelist(), titlelist() 93 / 94 / GLOBAL INPUTS: *Login, Throne, Wizard, Player, *stdscr, Changed, Databuf[], 95 / Fileloc, Stattable[] 96 / 97 / GLOBAL OUTPUTS: Wizard, Player, Changed, Fileloc, Timeout, *Statptr 98 / 99 / DESCRIPTION: 100 / Process arguments, initialize program, and loop forever processing 101 / player input. 102 / 103 ****************************************************************************/ 104 105 int 106 main(int argc, char **argv) 107 { 108 bool noheader = FALSE; /* set if don't want header */ 109 bool headeronly = FALSE; /* set if only want header */ 110 bool examine = FALSE; /* set if examine a character */ 111 time_t seconds; /* for time of day */ 112 double dtemp; /* for temporary calculations */ 113 114 initialstate(); /* init globals */ 115 116 /* process arguments */ 117 while (--argc && (*++argv)[0] == '-') 118 switch ((*argv)[1]) { 119 case 's': /* short */ 120 noheader = TRUE; 121 break; 122 123 case 'H': /* Header */ 124 headeronly = TRUE; 125 break; 126 127 case 'a': /* all users */ 128 activelist(); 129 cleanup(TRUE); 130 131 case 'p': /* purge old players */ 132 purgeoldplayers(); 133 cleanup(TRUE); 134 135 case 'S': /* set 'Wizard' */ 136 Wizard = !getuid(); 137 break; 138 139 case 'x': /* examine */ 140 examine = TRUE; 141 break; 142 143 case 'm': /* monsters */ 144 monstlist(); 145 cleanup(TRUE); 146 147 case 'b': /* scoreboard */ 148 scorelist(); 149 cleanup(TRUE); 150 } 151 152 if (!isatty(0)) /* don't let non-tty's play */ 153 cleanup(TRUE); 154 155 playinit(); /* set up to catch signals, init curses */ 156 157 if (examine) { 158 changestats(FALSE); 159 cleanup(TRUE); 160 } 161 if (!noheader) { 162 titlelist(); 163 purgeoldplayers(); /* clean up old characters */ 164 } 165 if (headeronly) 166 cleanup(TRUE); 167 168 do 169 /* get the player structure filled */ 170 { 171 Fileloc = -1L; 172 173 mvaddstr(22, 17, "Do you have a character to run [Q = Quit] ? "); 174 175 switch (getanswer("NYQ", FALSE)) { 176 case 'Y': 177 Fileloc = recallplayer(); 178 break; 179 180 case 'Q': 181 cleanup(TRUE); 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 313 void 314 initialstate(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 359 if ((Monstfp = fopen(_PATH_MONST, "r+")) == NULL) 360 error(_PATH_MONST); 361 362 if ((Messagefp = fopen(_PATH_MESS, "r")) == NULL) 363 error(_PATH_MESS); 364 365 if ((Energyvoidfp = fopen(_PATH_VOID, "r+")) == NULL) 366 error(_PATH_VOID); 367 } 368 /**/ 369 /************************************************************************ 370 / 371 / FUNCTION NAME: rollnewplayer() 372 / 373 / FUNCTION: roll up a new character 374 / 375 / AUTHOR: E. A. Estes, 12/4/85 376 / 377 / ARGUMENTS: none 378 / 379 / RETURN VALUE: none 380 / 381 / MODULES CALLED: initplayer(), allocrecord(), truncstring(), fabs(), wmove(), 382 / wclear(), sscanf(), strcmp(), genchar(), waddstr(), findname(), mvprintw(), 383 / getanswer(), getstring() 384 / 385 / GLOBAL INPUTS: Other, Wizard, Player, *stdscr, Databuf[] 386 / 387 / GLOBAL OUTPUTS: Echo 388 / 389 / DESCRIPTION: 390 / Prompt player, and roll up new character. 391 / 392 *************************************************************************/ 393 394 long 395 rollnewplayer(void) 396 { 397 int chartype; /* character type */ 398 int ch; /* input */ 399 400 initplayer(&Player); /* initialize player structure */ 401 402 clear(); 403 mvaddstr(4, 21, "Which type of character do you want:"); 404 mvaddstr(8, 4, 405 "1:Magic User 2:Fighter 3:Elf 4:Dwarf 5:Halfling 6:Experimento "); 406 if (Wizard) { 407 addstr("7:Super ? "); 408 chartype = getanswer("1234567", FALSE); 409 } else { 410 addstr("? "); 411 chartype = getanswer("123456", FALSE); 412 } 413 414 do { 415 genchar(chartype); /* roll up a character */ 416 417 /* print out results */ 418 mvprintw(12, 14, 419 "Strength : %2.0f Quickness: %2.0f Mana : %2.0f\n", 420 Player.p_strength, Player.p_quickness, Player.p_mana); 421 mvprintw(13, 14, 422 "Energy Level: %2.0f Brains : %2.0f Magic Level: %2.0f\n", 423 Player.p_energy, Player.p_brains, Player.p_magiclvl); 424 425 if (Player.p_type == C_EXPER || Player.p_type == C_SUPER) 426 break; 427 428 mvaddstr(14, 14, "Type '1' to keep >"); 429 ch = getanswer(" ", TRUE); 430 } 431 while (ch != '1'); 432 433 if (Player.p_type == C_EXPER || Player.p_type == C_SUPER) 434 /* get coordinates for experimento */ 435 for (;;) { 436 mvaddstr(16, 0, "Enter the X Y coordinates of your experimento ? "); 437 getstring(Databuf, SZ_DATABUF); 438 sscanf(Databuf, "%lf %lf", &Player.p_x, &Player.p_y); 439 440 if (fabs(Player.p_x) > D_EXPER || fabs(Player.p_y) > D_EXPER) 441 mvaddstr(17, 0, "Invalid coordinates. Try again.\n"); 442 else 443 break; 444 } 445 446 for (;;) 447 /* name the new character */ 448 { 449 mvprintw(18, 0, 450 "Give your character a name [up to %d characters] ? ", SZ_NAME - 1); 451 getstring(Player.p_name, SZ_NAME); 452 truncstring(Player.p_name); /* remove trailing blanks */ 453 454 if (Player.p_name[0] == '\0') 455 /* no null names */ 456 mvaddstr(19, 0, "Invalid name."); 457 else 458 if (findname(Player.p_name, &Other) >= 0L) 459 /* cannot have duplicate names */ 460 mvaddstr(19, 0, "Name already in use."); 461 else 462 /* name is acceptable */ 463 break; 464 465 addstr(" Pick another.\n"); 466 } 467 468 /* get a password for character */ 469 Echo = FALSE; 470 471 do { 472 mvaddstr(20, 0, "Give your character a password [up to 8 characters] ? "); 473 getstring(Player.p_password, SZ_PASSWORD); 474 mvaddstr(21, 0, "One more time to verify ? "); 475 getstring(Databuf, SZ_PASSWORD); 476 } 477 while (strcmp(Player.p_password, Databuf) != 0); 478 479 Echo = TRUE; 480 481 return (allocrecord()); 482 } 483 /**/ 484 /************************************************************************ 485 / 486 / FUNCTION NAME: procmain() 487 / 488 / FUNCTION: process input from player 489 / 490 / AUTHOR: E. A. Estes, 12/4/85 491 / 492 / ARGUMENTS: none 493 / 494 / RETURN VALUE: none 495 / 496 / MODULES CALLED: dotampered(), changestats(), inputoption(), allstatslist(), 497 / fopen(), wmove(), drandom(), sscanf(), fclose(), altercoordinates(), 498 / waddstr(), fprintf(), distance(), userlist(), leavegame(), encounter(), 499 / getstring(), wclrtobot() 500 / 501 / GLOBAL INPUTS: Circle, Illcmd[], Throne, Wizard, Player, *stdscr, 502 / Databuf[], Illmove[] 503 / 504 / GLOBAL OUTPUTS: Player, Changed 505 / 506 / DESCRIPTION: 507 / Process main menu options. 508 / 509 *************************************************************************/ 510 511 void 512 procmain(void) 513 { 514 int ch; /* input */ 515 double x; /* desired new x coordinate */ 516 double y; /* desired new y coordinate */ 517 double temp; /* for temporary calculations */ 518 FILE *fp; /* for opening files */ 519 int loop; /* a loop counter */ 520 bool hasmoved = FALSE; /* set if player has moved */ 521 522 ch = inputoption(); 523 mvaddstr(4, 0, "\n\n"); /* clear status area */ 524 525 move(7, 0); 526 clrtobot(); /* clear data on bottom area of screen */ 527 528 if (Player.p_specialtype == SC_VALAR && (ch == '1' || ch == '7')) 529 /* valar cannot move */ 530 ch = ' '; 531 532 switch (ch) { 533 case 'K': /* move up/north */ 534 case 'N': 535 x = Player.p_x; 536 y = Player.p_y + MAXMOVE(); 537 hasmoved = TRUE; 538 break; 539 540 case 'J': /* move down/south */ 541 case 'S': 542 x = Player.p_x; 543 y = Player.p_y - MAXMOVE(); 544 hasmoved = TRUE; 545 break; 546 547 case 'L': /* move right/east */ 548 case 'E': 549 x = Player.p_x + MAXMOVE(); 550 y = Player.p_y; 551 hasmoved = TRUE; 552 break; 553 554 case 'H': /* move left/west */ 555 case 'W': 556 x = Player.p_x - MAXMOVE(); 557 y = Player.p_y; 558 hasmoved = TRUE; 559 break; 560 561 default: /* rest */ 562 Player.p_energy += (Player.p_maxenergy + Player.p_shield) / 15.0 563 + Player.p_level / 3.0 + 2.0; 564 Player.p_energy = 565 MIN(Player.p_energy, Player.p_maxenergy + Player.p_shield); 566 567 if (Player.p_status != S_CLOAKED) 568 /* cannot find mana if cloaked */ 569 { 570 Player.p_mana += (Circle + Player.p_level) / 4.0; 571 572 if (drandom() < 0.2 && Player.p_status == S_PLAYING && !Throne) 573 /* wandering monster */ 574 encounter(-1); 575 } 576 break; 577 578 case 'X': /* change/examine a character */ 579 changestats(TRUE); 580 break; 581 582 case '1': /* move */ 583 for (loop = 3; loop; --loop) { 584 mvaddstr(4, 0, "X Y Coordinates ? "); 585 getstring(Databuf, SZ_DATABUF); 586 587 if (sscanf(Databuf, "%lf %lf", &x, &y) != 2) 588 mvaddstr(5, 0, "Try again\n"); 589 else 590 if (distance(Player.p_x, x, Player.p_y, y) > MAXMOVE()) 591 ILLMOVE(); 592 else { 593 hasmoved = TRUE; 594 break; 595 } 596 } 597 break; 598 599 case '2': /* players */ 600 userlist(TRUE); 601 break; 602 603 case '3': /* message */ 604 mvaddstr(4, 0, "Message ? "); 605 getstring(Databuf, SZ_DATABUF); 606 /* we open the file for writing to erase any data which is 607 * already there */ 608 fp = fopen(_PATH_MESS, "w"); 609 if (Databuf[0] != '\0') 610 fprintf(fp, "%s: %s", Player.p_name, Databuf); 611 fclose(fp); 612 break; 613 614 case '4': /* stats */ 615 allstatslist(); 616 break; 617 618 case '5': /* good-bye */ 619 leavegame(); 620 621 case '6': /* cloak */ 622 if (Player.p_level < MEL_CLOAK || Player.p_magiclvl < ML_CLOAK) 623 ILLCMD(); 624 else 625 if (Player.p_status == S_CLOAKED) 626 Player.p_status = S_PLAYING; 627 else 628 if (Player.p_mana < MM_CLOAK) 629 mvaddstr(5, 0, "No mana left.\n"); 630 else { 631 Changed = TRUE; 632 Player.p_mana -= MM_CLOAK; 633 Player.p_status = S_CLOAKED; 634 } 635 break; 636 637 case '7': /* teleport */ 638 /* 639 * conditions for teleport 640 * - 20 per (level plus magic level) 641 * - OR council of the wise or valar or ex-valar 642 * - OR transport from throne 643 * transports from throne cost no mana 644 */ 645 if (Player.p_level < MEL_TELEPORT || Player.p_magiclvl < ML_TELEPORT) 646 ILLCMD(); 647 else 648 for (loop = 3; loop; --loop) { 649 mvaddstr(4, 0, "X Y Coordinates ? "); 650 getstring(Databuf, SZ_DATABUF); 651 652 if (sscanf(Databuf, "%lf %lf", &x, &y) == 2) { 653 temp = distance(Player.p_x, x, Player.p_y, y); 654 if (!Throne 655 /* can transport anywhere from throne */ 656 && Player.p_specialtype <= SC_COUNCIL 657 /* council, valar can transport 658 * anywhere */ 659 && temp > (Player.p_level + Player.p_magiclvl) * 20.0) 660 /* can only move 20 per exp. 661 * level + mag. level */ 662 ILLMOVE(); 663 else { 664 temp = (temp / 75.0 + 1.0) * 20.0; /* mana used */ 665 666 if (!Throne && temp > Player.p_mana) 667 mvaddstr(5, 0, "Not enough power for that distance.\n"); 668 else { 669 if (!Throne) 670 Player.p_mana -= temp; 671 hasmoved = TRUE; 672 break; 673 } 674 } 675 } 676 } 677 break; 678 679 case 'C': 680 case '9': /* monster */ 681 if (Throne) 682 /* no monsters while on throne */ 683 mvaddstr(5, 0, "No monsters in the chamber!\n"); 684 else 685 if (Player.p_specialtype != SC_VALAR) 686 /* the valar cannot call monsters */ 687 { 688 Player.p_sin += 1e-6; 689 encounter(-1); 690 } 691 break; 692 693 case '0': /* decree */ 694 if (Wizard || (Player.p_specialtype == SC_KING && Throne)) 695 /* kings must be on throne to decree */ 696 dotampered(); 697 else 698 ILLCMD(); 699 break; 700 701 case '8': /* intervention */ 702 if (Wizard || Player.p_specialtype >= SC_COUNCIL) 703 dotampered(); 704 else 705 ILLCMD(); 706 break; 707 } 708 709 if (hasmoved) 710 /* player has moved -- alter coordinates, and do random 711 * monster */ 712 { 713 altercoordinates(x, y, A_SPECIFIC); 714 715 if (drandom() < 0.2 && Player.p_status == S_PLAYING && !Throne) 716 encounter(-1); 717 } 718 } 719 /**/ 720 /************************************************************************ 721 / 722 / FUNCTION NAME: titlelist() 723 / 724 / FUNCTION: print title page 725 / 726 / AUTHOR: E. A. Estes, 12/4/85 727 / 728 / ARGUMENTS: none 729 / 730 / RETURN VALUE: none 731 / 732 / MODULES CALLED: fread(), fseek(), fopen(), fgets(), wmove(), strlcpy(), 733 / fclose(), strlen(), waddstr(), snprintf(), wrefresh() 734 / 735 / GLOBAL INPUTS: Lines, Other, *stdscr, Databuf[], *Playersfp 736 / 737 / GLOBAL OUTPUTS: Lines 738 / 739 / DESCRIPTION: 740 / Print important information about game, players, etc. 741 / 742 *************************************************************************/ 743 744 void 745 titlelist(void) 746 { 747 FILE *fp; /* used for opening various files */ 748 bool councilfound = FALSE; /* set if we find a member of the 749 * council */ 750 bool kingfound = FALSE; /* set if we find a king */ 751 double hiexp, nxtexp; /* used for finding the two highest players */ 752 double hilvl, nxtlvl; /* used for finding the two highest players */ 753 char hiname[21], nxtname[21]; /* used for finding the two 754 * highest players */ 755 756 nxtexp = 0; 757 mvaddstr(0, 14, 758 "W e l c o m e t o P h a n t a s i a (vers. 3.3.2)!"); 759 760 /* print message of the day */ 761 if ((fp = fopen(_PATH_MOTD, "r")) != NULL 762 && fgets(Databuf, SZ_DATABUF, fp) != NULL) { 763 mvaddstr(2, 40 - strlen(Databuf) / 2, Databuf); 764 fclose(fp); 765 } 766 /* search for king */ 767 fseek(Playersfp, 0L, SEEK_SET); 768 while (fread(&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) 769 if (Other.p_specialtype == SC_KING && 770 Other.p_status != S_NOTUSED) 771 /* found the king */ 772 { 773 snprintf(Databuf, sizeof Databuf, 774 "The present ruler is %s Level:%.0f", 775 Other.p_name, Other.p_level); 776 mvaddstr(4, 40 - strlen(Databuf) / 2, Databuf); 777 kingfound = TRUE; 778 break; 779 } 780 if (!kingfound) 781 mvaddstr(4, 24, "There is no ruler at this time."); 782 783 /* search for valar */ 784 fseek(Playersfp, 0L, SEEK_SET); 785 while (fread(&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) 786 if (Other.p_specialtype == SC_VALAR && Other.p_status != S_NOTUSED) 787 /* found the valar */ 788 { 789 snprintf(Databuf, sizeof Databuf, 790 "The Valar is %s Login: %s", Other.p_name, 791 Other.p_login); 792 mvaddstr(6, 40 - strlen(Databuf) / 2, Databuf); 793 break; 794 } 795 /* search for council of the wise */ 796 fseek(Playersfp, 0L, SEEK_SET); 797 Lines = 10; 798 while (fread(&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) 799 if (Other.p_specialtype == SC_COUNCIL && Other.p_status != S_NOTUSED) 800 /* found a member of the council */ 801 { 802 if (!councilfound) { 803 mvaddstr(8, 30, "Council of the Wise:"); 804 councilfound = TRUE; 805 } 806 /* This assumes a finite (<=5) number of C.O.W.: */ 807 snprintf(Databuf, sizeof Databuf, 808 "%s Login: %s", Other.p_name, Other.p_login); 809 mvaddstr(Lines++, 40 - strlen(Databuf) / 2, Databuf); 810 } 811 /* search for the two highest players */ 812 nxtname[0] = hiname[0] = '\0'; 813 hiexp = 0.0; 814 nxtlvl = hilvl = 0; 815 816 fseek(Playersfp, 0L, SEEK_SET); 817 while (fread(&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) 818 if (Other.p_experience > hiexp && Other.p_specialtype <= SC_KING && Other.p_status != S_NOTUSED) 819 /* highest found so far */ 820 { 821 nxtexp = hiexp; 822 hiexp = Other.p_experience; 823 nxtlvl = hilvl; 824 hilvl = Other.p_level; 825 strlcpy(nxtname, hiname, sizeof nxtname); 826 strlcpy(hiname, Other.p_name, sizeof hiname); 827 } else 828 if (Other.p_experience > nxtexp 829 && Other.p_specialtype <= SC_KING 830 && Other.p_status != S_NOTUSED) 831 /* next highest found so far */ 832 { 833 nxtexp = Other.p_experience; 834 nxtlvl = Other.p_level; 835 strlcpy(nxtname, Other.p_name, sizeof nxtname); 836 } 837 mvaddstr(15, 28, "Highest characters are:"); 838 snprintf(Databuf, sizeof Databuf, 839 "%s Level:%.0f and %s Level:%.0f", 840 hiname, hilvl, nxtname, nxtlvl); 841 mvaddstr(17, 40 - strlen(Databuf) / 2, Databuf); 842 843 /* print last to die */ 844 if ((fp = fopen(_PATH_LASTDEAD, "r")) != NULL 845 && fgets(Databuf, SZ_DATABUF, fp) != NULL) { 846 mvaddstr(19, 25, "The last character to die was:"); 847 mvaddstr(20, 40 - strlen(Databuf) / 2, Databuf); 848 fclose(fp); 849 } 850 refresh(); 851 } 852 /**/ 853 /************************************************************************ 854 / 855 / FUNCTION NAME: recallplayer() 856 / 857 / FUNCTION: find a character on file 858 / 859 / AUTHOR: E. A. Estes, 12/4/85 860 / 861 / ARGUMENTS: none 862 / 863 / RETURN VALUE: none 864 / 865 / MODULES CALLED: writerecord(), truncstring(), more(), death(), wmove(), 866 / wclear(), strcmp(), printw(), cleanup(), waddstr(), findname(), mvprintw(), 867 / getanswer(), getstring() 868 / 869 / GLOBAL INPUTS: Player, *stdscr, Databuf[] 870 / 871 / GLOBAL OUTPUTS: Echo, Player 872 / 873 / DESCRIPTION: 874 / Search for a character of a certain name, and check password. 875 / 876 *************************************************************************/ 877 878 long 879 recallplayer(void) 880 { 881 long loc = 0L; /* location in player file */ 882 int loop; /* loop counter */ 883 int ch; /* input */ 884 885 clear(); 886 mvprintw(10, 0, "What was your character's name ? "); 887 getstring(Databuf, SZ_NAME); 888 truncstring(Databuf); 889 890 if ((loc = findname(Databuf, &Player)) >= 0L) 891 /* found character */ 892 { 893 Echo = FALSE; 894 895 for (loop = 0; loop < 2; ++loop) { 896 /* prompt for password */ 897 mvaddstr(11, 0, "Password ? "); 898 getstring(Databuf, SZ_PASSWORD); 899 if (strcmp(Databuf, Player.p_password) == 0) 900 /* password good */ 901 { 902 Echo = TRUE; 903 904 if (Player.p_status != S_OFF) 905 /* player did not exit normally last 906 * time */ 907 { 908 clear(); 909 addstr("Your character did not exit normally last time.\n"); 910 addstr("If you think you have good cause to have your character saved,\n"); 911 printw("you may quit and mail your reason to 'root'.\n"); 912 addstr("Otherwise, continuing spells certain death.\n"); 913 addstr("Do you want to quit ? "); 914 ch = getanswer("YN", FALSE); 915 if (ch == 'Y') { 916 Player.p_status = S_HUNGUP; 917 writerecord(&Player, loc); 918 cleanup(TRUE); 919 } 920 death("Stupidity"); 921 } 922 return (loc); 923 } else 924 mvaddstr(12, 0, "No good.\n"); 925 } 926 927 Echo = TRUE; 928 } else 929 mvaddstr(11, 0, "Not found.\n"); 930 931 more(13); 932 return (-1L); 933 } 934 /**/ 935 /************************************************************************ 936 / 937 / FUNCTION NAME: neatstuff() 938 / 939 / FUNCTION: do random stuff 940 / 941 / AUTHOR: E. A. Estes, 3/3/86 942 / 943 / ARGUMENTS: none 944 / 945 / RETURN VALUE: none 946 / 947 / MODULES CALLED: collecttaxes(), floor(), wmove(), drandom(), infloat(), 948 / waddstr(), mvprintw(), getanswer() 949 / 950 / GLOBAL INPUTS: Player, *stdscr, *Statptr 951 / 952 / GLOBAL OUTPUTS: Player 953 / 954 / DESCRIPTION: 955 / Handle gurus, medics, etc. 956 / 957 *************************************************************************/ 958 959 void 960 neatstuff(void) 961 { 962 double temp; /* for temporary calculations */ 963 int ch; /* input */ 964 965 switch ((int) ROLL(0.0, 100.0)) { 966 case 1: 967 case 2: 968 if (Player.p_poison > 0.0) { 969 mvaddstr(4, 0, "You've found a medic! How much will you offer to be cured ? "); 970 temp = floor(infloat()); 971 if (temp < 0.0 || temp > Player.p_gold) 972 /* negative gold, or more than available */ 973 { 974 mvaddstr(6, 0, "He was not amused, and made you worse.\n"); 975 Player.p_poison += 1.0; 976 } else if (drandom() / 2.0 > (temp + 1.0) / MAX(Player.p_gold, 1)) 977 /* medic wants 1/2 of available gold */ 978 mvaddstr(5, 0, "Sorry, he wasn't interested.\n"); 979 else { 980 mvaddstr(5, 0, "He accepted."); 981 Player.p_poison = MAX(0.0, Player.p_poison - 1.0); 982 Player.p_gold -= temp; 983 } 984 } 985 break; 986 987 case 3: 988 mvaddstr(4, 0, "You've been caught raping and pillaging!\n"); 989 Player.p_experience += 4000.0; 990 Player.p_sin += 0.5; 991 break; 992 993 case 4: 994 temp = ROLL(10.0, 75.0); 995 mvprintw(4, 0, "You've found %.0f gold pieces, want them ? ", temp); 996 ch = getanswer("NY", FALSE); 997 998 if (ch == 'Y') 999 collecttaxes(temp, 0.0); 1000 break; 1001 1002 case 5: 1003 if (Player.p_sin > 1.0) { 1004 mvaddstr(4, 0, "You've found a Holy Orb!\n"); 1005 Player.p_sin -= 0.25; 1006 } 1007 break; 1008 1009 case 6: 1010 if (Player.p_poison < 1.0) { 1011 mvaddstr(4, 0, "You've been hit with a plague!\n"); 1012 Player.p_poison += 1.0; 1013 } 1014 break; 1015 1016 case 7: 1017 mvaddstr(4, 0, "You've found some holy water.\n"); 1018 ++Player.p_holywater; 1019 break; 1020 1021 case 8: 1022 mvaddstr(4, 0, "You've met a Guru. . ."); 1023 if (drandom() * Player.p_sin > 1.0) 1024 addstr("You disgusted him with your sins!\n"); 1025 else if (Player.p_poison > 0.0) { 1026 addstr("He looked kindly upon you, and cured you.\n"); 1027 Player.p_poison = 0.0; 1028 } else { 1029 addstr("He rewarded you for your virtue.\n"); 1030 Player.p_mana += 50.0; 1031 Player.p_shield += 2.0; 1032 } 1033 break; 1034 1035 case 9: 1036 mvaddstr(4, 0, "You've found an amulet.\n"); 1037 ++Player.p_amulets; 1038 break; 1039 1040 case 10: 1041 if (Player.p_blindness) { 1042 mvaddstr(4, 0, "You've regained your sight!\n"); 1043 Player.p_blindness = FALSE; 1044 } 1045 break; 1046 1047 default: /* deal with poison */ 1048 if (Player.p_poison > 0.0) { 1049 temp = Player.p_poison * Statptr->c_weakness 1050 * Player.p_maxenergy / 600.0; 1051 if (Player.p_energy > Player.p_maxenergy / 10.0 1052 && temp + 5.0 < Player.p_energy) 1053 Player.p_energy -= temp; 1054 } 1055 break; 1056 } 1057 } 1058 /**/ 1059 /************************************************************************ 1060 / 1061 / FUNCTION NAME: genchar() 1062 / 1063 / FUNCTION: generate a random character 1064 / 1065 / AUTHOR: E. A. Estes, 12/4/85 1066 / 1067 / ARGUMENTS: 1068 / int type - ASCII value of character type to generate 1069 / 1070 / RETURN VALUE: none 1071 / 1072 / MODULES CALLED: floor(), drandom() 1073 / 1074 / GLOBAL INPUTS: Wizard, Player, Stattable[] 1075 / 1076 / GLOBAL OUTPUTS: Player 1077 / 1078 / DESCRIPTION: 1079 / Use the lookup table for rolling stats. 1080 / 1081 *************************************************************************/ 1082 1083 void 1084 genchar(int type) 1085 { 1086 int subscript; /* used for subscripting into Stattable */ 1087 struct charstats *statptr; /* for pointing into Stattable */ 1088 1089 subscript = type - '1'; 1090 1091 if (subscript < C_MAGIC || subscript > C_EXPER) 1092 if (subscript != C_SUPER || !Wizard) 1093 /* fighter is default */ 1094 subscript = C_FIGHTER; 1095 1096 statptr = &Stattable[subscript]; 1097 1098 Player.p_quickness = 1099 ROLL(statptr->c_quickness.base, statptr->c_quickness.interval); 1100 Player.p_strength = 1101 ROLL(statptr->c_strength.base, statptr->c_strength.interval); 1102 Player.p_mana = 1103 ROLL(statptr->c_mana.base, statptr->c_mana.interval); 1104 Player.p_maxenergy = 1105 Player.p_energy = 1106 ROLL(statptr->c_energy.base, statptr->c_energy.interval); 1107 Player.p_brains = 1108 ROLL(statptr->c_brains.base, statptr->c_brains.interval); 1109 Player.p_magiclvl = 1110 ROLL(statptr->c_magiclvl.base, statptr->c_magiclvl.interval); 1111 1112 Player.p_type = subscript; 1113 1114 if (Player.p_type == C_HALFLING) 1115 /* give halfling some experience */ 1116 Player.p_experience = ROLL(600.0, 200.0); 1117 } 1118 /**/ 1119 /************************************************************************ 1120 / 1121 / FUNCTION NAME: playinit() 1122 / 1123 / FUNCTION: initialize for playing game 1124 / 1125 / AUTHOR: E. A. Estes, 12/4/85 1126 / 1127 / ARGUMENTS: none 1128 / 1129 / RETURN VALUE: none 1130 / 1131 / MODULES CALLED: signal(), wclear(), noecho(), cbreak(), initscr(), 1132 / wrefresh() 1133 / 1134 / GLOBAL INPUTS: *stdscr, ill_sig() 1135 / 1136 / GLOBAL OUTPUTS: Windows 1137 / 1138 / DESCRIPTION: 1139 / Catch a bunch of signals, and turn on curses stuff. 1140 / 1141 *************************************************************************/ 1142 1143 void 1144 playinit(void) 1145 { 1146 initscr(); /* turn on curses */ 1147 noecho(); /* do not echo input */ 1148 cbreak(); /* do not process erase, kill */ 1149 clear(); 1150 refresh(); 1151 Windows = TRUE; /* mark the state */ 1152 } 1153 /**/ 1154 /************************************************************************ 1155 / 1156 / FUNCTION NAME: cleanup() 1157 / 1158 / FUNCTION: close some files, and maybe exit 1159 / 1160 / AUTHOR: E. A. Estes, 12/4/85 1161 / 1162 / ARGUMENTS: 1163 / bool doexit - exit flag 1164 / 1165 / RETURN VALUE: none 1166 / 1167 / MODULES CALLED: exit(), wmove(), fclose(), endwin(), nocbreak(), wrefresh() 1168 / 1169 / GLOBAL INPUTS: *Energyvoidfp, LINES, *stdscr, Windows, *Monstfp, 1170 / *Messagefp, *Playersfp 1171 / 1172 / GLOBAL OUTPUTS: none 1173 / 1174 / DESCRIPTION: 1175 / Close all open files. If we are "in curses" terminate curses. 1176 / If 'doexit' is set, exit, otherwise return. 1177 / 1178 *************************************************************************/ 1179 1180 void 1181 cleanup(int doexit) 1182 { 1183 if (Windows) { 1184 move(LINES - 2, 0); 1185 refresh(); 1186 nocbreak(); 1187 endwin(); 1188 } 1189 1190 if (Playersfp) { 1191 fclose(Playersfp); 1192 Playersfp = NULL; 1193 } 1194 if (Monstfp) { 1195 fclose(Monstfp); 1196 Monstfp = NULL; 1197 } 1198 if (Messagefp) { 1199 fclose(Messagefp); 1200 Messagefp = NULL; 1201 } 1202 if (Energyvoidfp) { 1203 fclose(Energyvoidfp); 1204 Energyvoidfp = NULL; 1205 } 1206 1207 if (doexit) 1208 exit(0); 1209 } 1210