1/* $OpenBSD: main.c,v 1.23 2024/05/21 05:00:47 jsg 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 105int 106main(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 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 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 394long 395rollnewplayer(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 511void 512procmain(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 744void 745titlelist(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 878long 879recallplayer(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 959void 960neatstuff(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 1083void 1084genchar(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 1135/ 1136/ GLOBAL OUTPUTS: Windows 1137/ 1138/ DESCRIPTION: 1139/ Catch a bunch of signals, and turn on curses stuff. 1140/ 1141*************************************************************************/ 1142 1143void 1144playinit(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 1180void 1181cleanup(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